溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Docker容器中Dockerfile配置文件的介紹

發(fā)布時間:2020-05-29 12:42:08 來源:億速云 閱讀:1727 作者:Leah 欄目:云計算

本篇文章展示了Docker容器中Dockerfile配置文件的介紹,文章主要介紹Docker執(zhí)行Dockerfile的流程、Dockerfile的結(jié)構(gòu)、Dockerfile常用指令以及鏡像緩存,有需要的小伙伴可以參考。

Dockerfile簡介

Dockerfile 是Docker中用于定義鏡像自動化構(gòu)建流程的配置文件。在Dockerfile中,包含了構(gòu)建鏡像過程中需要執(zhí)行的命令和其他操作。通過Dockerfile可以更加清晰,明確的給定Docker鏡像的制作過程,由于僅是簡單,小體積的文件,在網(wǎng)絡(luò)等介質(zhì)中傳遞的速度快,能夠更快的實現(xiàn)容器遷移和集群部署。
Dockerfile是一個文本文件,其內(nèi)包含了一條條的指令,每一條指令構(gòu)建一層,因此每一條指令的內(nèi)容,就是描述該層應(yīng)當(dāng)如何構(gòu)建。

相對于提交容器修改在進行鏡像遷移的方式相比,使用Dockerfile有很多優(yōu)勢:

  • Dockerfile的體積遠小于鏡像包,更容易進行快速遷移和部署。
  • 環(huán)境構(gòu)建流程記錄在Dockerfile中,能夠直觀的看到鏡像構(gòu)建的順序和邏輯。
  • 使用Dockerfile構(gòu)建鏡像能夠更輕松的實現(xiàn)自動部署等自動化流程。
  • 在修改環(huán)境搭建細節(jié)時,修改Dockerfile文件更加簡單。

實際開發(fā)使用中很少會選擇容器提交這種方法來構(gòu)建鏡像,而是幾乎采用Dockerfile來制作鏡像。

Docker執(zhí)行Dockerfile的大致流程:

  • (1)docker會從Dockerfile文件頭FROM指定的基礎(chǔ)鏡像運行一個容器
  • (2)然后執(zhí)行一條指令,對容器作出修改。
  • (3)接著執(zhí)行類似于docker commit的操作,創(chuàng)建一個新的鏡像層。
  • (4)在基于剛創(chuàng)建的鏡像運行一個新的容器。
  • (4)執(zhí)行dockerfile中的下一條指令,直到所有指令都執(zhí)行完畢。

    docker會刪除中間層創(chuàng)建的容器,但不會刪除中間層鏡像,所以可以使用docker run運行一個中間層容器,從而查看每一步構(gòu)建后的鏡像狀態(tài),,這樣就可以進行調(diào)試。

Dockerfile 基本結(jié)構(gòu)

Dockerfile 又一行行命令語句組成,并且支持以#開頭的注釋行。
Dockerfile 分為四部分:基礎(chǔ)鏡像信息,維護者信息鏡像操作指令,容器啟動時執(zhí)行指令。
例如,以下為一個完整的Dockerfile:

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..

# Base image to use, this must be set as the first line
1,第一行必須指定,基礎(chǔ)鏡像信息
FROM ubuntu

# Maintainer: docker_user <docker_user at email.com> (@docker_user)
2,維護者信息
MAINTAINER docker_user docker_user@email.com

# Commands to update the image
3,鏡像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# Commands when creating a new container
4,容器啟動執(zhí)行指令
CMD /usr/sbin/nginx

其中,已開始必須要指明所基于的鏡像名稱,接下來一般會說明維護者信息;后面則是鏡像操作指令,例如RUN指令。每執(zhí)行一條RUN指令,鏡像添加新的一層,并提交;最后是CMD指令,來指明運行容器時的操作命令。

Dockerfile 常用指令

以下常見的dockerfile指令,基本包含常用的90%功能。

常用指令目錄:

1,F(xiàn)ROM--指定基礎(chǔ)鏡像
2,MAINTAINER--指定維護者信息
3,RUN--運行指定的命令
4,CMD--容器啟動時執(zhí)行的命令
5, EXPOSE--聲明容器的服務(wù)端口
6,ENV--設(shè)置環(huán)境變量
7,ARG--構(gòu)建參數(shù)
8,COPY--復(fù)制文件或目錄
9,ADD--更高級的復(fù)制文件/目錄
10,ENTRYPOINT--入口點
11,ENTRYPOINT與CMD指令結(jié)合使用
12,VOLUME--定義匿名卷
13,WORKDIR--指定工作目錄
14,USER--指定當(dāng)前用戶
15,ONBUILD--為鏡像添加觸發(fā)器

1,F(xiàn)ROM--指定基礎(chǔ)鏡像
第一條指令必須為FROM指令。
如果不以任何鏡像為基礎(chǔ),那么寫法如下,同時意味著接下來所寫的指令將作為鏡像的第一層開始:
FROM nginx
FROM 指令支持三種格式:

FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>

三種寫法,第二種和第三種是可選項,如果沒有選擇,那么默認為latest。

在Dockerfile中可以多次出現(xiàn)FROM指令,當(dāng)FROM第二次或者之后出現(xiàn)時,表示在此刻構(gòu)建時,要將當(dāng)前指出鏡像的內(nèi)容合并到此刻構(gòu)建鏡像的內(nèi)容。

ARG是唯一可以在FROM之前執(zhí)行的參數(shù):

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

2,MAINTAINER--指定維護者信息
格式為:MAINTAINER <name>,指定維護者信息(鏡像維護者姓名或郵箱),可選項。
例如:
MAINTAINER docker_user docker_user@email.com
3,RUN--運行指定的命令
RUN有兩種格式:

RUN <command>
RUN ["executable", "param1", "param2"]

前者將在shell終端運行命令,即/bin/sh -c; 后者則使用exec執(zhí)行。指定使用其它終端可以通過第二種方式實現(xiàn),例如使用bash終端: RUN ["/bin/bash", "-c", "echo hello"]。

注意:多行命令不要寫多個RUN,原因是Dockerfile中每一個指令都會建立一層,多少個RUN就構(gòu)建了多少層鏡像,會造成鏡像的臃腫、多層,不僅僅增加了構(gòu)件部署的時間,還容易出錯。一般一個RUN指令后邊可以跟多個要執(zhí)行的命令(使用&&符即可),當(dāng)命令較長時可以使用"\"來換行。
例子如下:

RUN yum -y install gcc* pcre-devel openssl-devel zlib-devel unzip make vim net-tools elinks tree \
 && groupadd nginx \
 &&  useradd  nginx -g nginx  -s /sbin/nologin 

4,CMD--容器啟動時執(zhí)行的命令
CMD支持三種格式:

CMD ["executable","param1","param2"] 使用 exec 執(zhí)行,推薦方式;
CMD command param1 param2 在 /bin/sh 中執(zhí)行,提供給需要交互的應(yīng)用;
CMD ["param1","param2"] 提供給 ENTRYPOINT 的默認參數(shù);

指定啟動容器時執(zhí)行的命令,每個dockerfile只能有一條CMD命令。如果指令了多條命令,只有最后一條會被執(zhí)行。

如果用戶啟動容器時候指定了運行的命令,則會覆蓋掉CMD指定的命令。

補充細節(jié):如果不是使用shell(/bin/sh)這種方式,”[]“中括號這里邊包括參數(shù)的一定要用雙引號,千萬不能寫成單引號。原因是參數(shù)傳遞后,docker解析的是一個JSON array
例如:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
CMD echo hello world
CMD echo hello china
[root@sqm-docker01 dfs]# docker run --rm test:v1   #只有最后一條CMD命令會被執(zhí)行
hello china    
[root@sqm-docker01 dfs]# docker run --rm test:v1 echo "hello friend"  #在啟動容器時指定運行命令,會將其覆蓋
hello friend

采用exec格式,那么上面的例子改為:

FROM centos:latest
CMD ["/bin/bash","-c","echo hello world"]
CMD ["/bin/bash","-c","echo hello china"]

還有一種情況下CMD會結(jié)合ENTRYPOINT指令使用,后面會講到。

5, EXPOSE--聲明容器的服務(wù)端口
格式為 EXPOSE <端口1> [<端口2>...] 。

EXPOSE 指令是聲明運行時容器提供服務(wù)端口,這只是一個聲明,在運行時并不會因為這個聲明應(yīng)用就會開啟這個端口的服務(wù)。
在Dockerfile 中寫入這樣的聲明有兩個好處,一個是幫助鏡像使用者理解這個鏡像服務(wù)的守護端口,以方便配置映射;另一個用戶則是運行時使用隨機端口映射時,也就是docker run -P 時,會自動隨機映射EXPOSE的端口。
要將 EXPOSE 和在運行時使用 -p <宿主端口>:<容器端口> 區(qū)分開來。-p,是映射宿主端口和容器端口,換句話說,就是將容器的對應(yīng)端口服務(wù)公開給外界訪問,而 EXPOSE 僅僅是聲明容器打算使用什么端口而已,并不會自動在宿主進行端口映射。
例如:
EXPOSE 80 443

6,ENV--設(shè)置環(huán)境變量
格式有兩種:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

兩者的區(qū)別是第一種是一次設(shè)置一個,第二種是一次設(shè)置多個。

ENV這個指令就是指定一個環(huán)境變量,會被后續(xù)RUN指定使用,并在容器運行時保持。

ENV VERSION=1.0 DEBUG=on \
    NAME="Happy Feet"

這個例子中演示了如何換行,以及對含有空格的值用雙引號括起來的辦法,這和shell下的行為是一致的。
舉例如下:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM busybox:latest
ENV var1=haha var2=hehe
RUN echo $var1 > a.txt && echo $var2 > b.txt
執(zhí)行dockerfile后,進行容器查看文件:
[root@sqm-docker01 dfs]# docker run --rm test:v2 /bin/sh
/ # cat a.txt 
haha
/ # cat b.txt 
hehe
#并且定義的變量會在容器運行時保持:
/ # echo $var1
haha
/ # echo $var2
hehe

7,ARG--構(gòu)建參數(shù)

格式:
ARG <name>[=<default value>]

構(gòu)建參數(shù)和ENV的效果一樣,都是設(shè)置環(huán)境變量。所不同的是,ARG所設(shè)置的構(gòu)建環(huán)境的環(huán)境變量,在將來容器運行時是不會存在的這些環(huán)境變量的。但是不要因此就使用ARG保存密碼之類的信息,因為docker history還是可以看到所有值的。

#用法一:在執(zhí)行docker build構(gòu)建時傳遞參數(shù)

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ARG user
ARG password
RUN echo $user > a.txt \
    && echo $password >> a.txt
#傳遞兩個參數(shù)時,則在每個參數(shù)中添加--build-arg。
[root@sqm-docker01 dfs]# docker build --build-arg user=sqm --build-arg password=123.com  -t arg:v1 .
#運行容器,參數(shù)傳遞成功
[root@sqm-docker01 dfs]# docker run -it --rm arg:v1 /bin/bash
root@4809b0c54f8d:/# cat a.txt 
sqm
123.com
#與ENV不同,在容器中是不會存在這些變量的
root@9383b7d3d21e:/# echo $user

root@9383b7d3d21e:/# echo $password

root@9383b7d3d21e:/# 

注意:如果指定了該參數(shù),但Dockerfile中未使用,構(gòu)建過程中會輸出警告。

#用法二:在dockerfile中設(shè)定一個默認值,如果ARG指令具有默認值,并且在構(gòu)建時未傳遞任何參數(shù),那么構(gòu)建其將使用該默認值

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ARG user=zhangsan
ARG password=123456
RUN echo $user $password > a.txt
#構(gòu)建鏡像且不傳遞任何參數(shù):
[root@sqm-docker01 dfs]# docker build -t arg:v2 .
#運行容器,參數(shù)傳遞成功
[root@sqm-docker01 dfs]# docker run -it --rm arg:v2 /bin/bash
root@b52fa70086de:/# cat a.txt 
zhangsan 123456

#多階段構(gòu)建中每個階段都必須包含arg指令:

FROM busybox
ARG user
RUN ./run/setup $user

FROM busybox
ARG user
RUN ./run/other $user

#ENV變量會覆蓋同名的ARG變量:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ARG user=zhangsan
ENV user lisi
RUN echo $user > a.txt
[root@sqm-docker01 dfs]# docker build -t arg:v3 .
[root@sqm-docker01 dfs]# docker run -it --rm arg:v3 /bin/bash
root@a2aefd05efee:/# cat a.txt 
lisi

#ARG和ENV指令結(jié)合使用:(一般這種用法,不常用)

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
ARG var
ENV user=${var}
RUN echo $user > a.txt
[root@sqm-docker01 dfs]# docker build --build-arg var=zhangsan -t arg:v4 .
[root@sqm-docker01 dfs]# docker run -it --rm arg:v4 /bin/bash
[root@26bf8c139a3f /]# cat a.txt 
zhangsan

8,COPY--復(fù)制文件或目錄‘’
格式:

COPY [--chown=<user>:<group>] <源路徑>... <目標(biāo)路徑>
COPY [--chown=<user>:<group>] ["<源路徑1>",... "<目標(biāo)路徑>"]

和RUN指令一樣,也有兩種格式,前者在shell終端運行; 后者則使用exec執(zhí)行。

<源路徑>為宿主機的上的路徑,可以是多個,甚至可以是通配符,其通配符規(guī)則要滿足 Go 的 filepath.Match 規(guī)則,如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

<目標(biāo)路徑>是容器中的路徑,可以是絕對路徑,也可以是相對于工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定)。目標(biāo)路徑不需要事先創(chuàng)建,如果目錄不存在會在復(fù)制文件前先行創(chuàng)建缺失目錄。

此外,還需要注意一點,使用 COPY 指令,源文件的各種元數(shù)據(jù)都會保留。比如讀、寫、執(zhí)行權(quán)限、文件變更時間等。可以使用如下命令指定:

COPY [--chown=:] ...
COPY [--chown=:] ["",... ""] (包含空格的路徑需要這種形式)

–chown功能僅在用于構(gòu)建Linux容器的Dockerfiles上受支持
例如:
COPY --chown=nginx:nginx  files* /somedir/

舉例:

[root@sqm-docker01 dfs]# echo "hello world" > index.html
[root@sqm-docker01 dfs]# echo aaaaaa > a.txt
[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
COPY ./index.html /usr/share/nginx/html/
COPY ./a.txt /test/
[root@sqm-docker01 dfs]# docker run --rm  nginx:v1  /bin/bash
root@8a1ee4925b43:/# cat /usr/share/nginx/html/index.html 
hello world
#即使目標(biāo)目錄不存在,會自己事先創(chuàng)建
root@8a1ee4925b43:/# cat /test/a.txt   
aaaaaa

##拷貝整個目錄:
格式:COPY src WORKDIR/src
例如:

[root@sqm-docker01 dfs]# cat test/a.txt 
addddddddddddaaaaa
[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
COPY ./test  /usr/share/nginx/test/  
#必須要在目標(biāo)路徑下指定要拷貝的目錄,不然只拷貝源目錄中的文件,而不拷貝目錄

#進入容器查看整個目錄及其目錄下的文件
[root@sqm-docker01 dfs]# docker run --rm nginx:v2 /bin/bash
root@4eae96cbe364:/# cd /usr/share/nginx/
root@4eae96cbe364:/usr/share/nginx# ls
html  test
root@4eae96cbe364:/usr/share/nginx# cat test/a.txt 
addddddddddddaaaaa

9,ADD--更高級的復(fù)制文件/目錄
格式為:

ADD [--chown=<user>:<group>] <源路徑>... <目標(biāo)路徑>
ADD [--chown=<user>:<group>] ["<源路徑1>",... "<目標(biāo)路徑>"]

ADD指令和COPY 的格式和性質(zhì)基本一致。但是在 COPY`基礎(chǔ)上增加了一些功能:可以是一個URL(通過URL下載下來自動設(shè)置權(quán)限600);還可以是一個tar文件(自動解壓為目錄)。

舉例:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
ADD nginx-1.8.0.tar.gz  /usr/src
[root@sqm-docker01 dfs]# docker run --rm  nginx:v3 /bin/bash
[root@0c8d6789aa4c /]# cd /usr/src/
[root@0c8d6789aa4c src]# ls   
debug  kernels  nginx-1.8.0     #自動解壓tar包
[root@0c8d6789aa4c src]# ls nginx-1.8.0/
CHANGES  CHANGES.ru  LICENSE  README  auto  conf  configure  contrib  html  man  src

#通過url下載鏈接文件放到目標(biāo)路徑下:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
ADD http://nginx.org/download/nginx-1.9.0.tar.gz /

[root@sqm-docker01 dfs]# docker run --rm  nginx:v4 /bin/bash
[root@b9d978e3a333 /]# ls -lh nginx-1.9.0.tar.gz 
-rw------- 1 root root 835K Apr 28  2015 nginx-1.9.0.tar.gz
#默認權(quán)限600

#與COPY的區(qū)別:
用于與COPY類似,不同的是COPY的只能是本地文件/目錄,如果src是歸檔文件(tar,zip,tgz,xz等),使用ADD命令文件會被自動解壓到dest。

因此在COPY和ADD指令中選擇的時候,可以遵循這樣的原則,所有的文件復(fù)制均使用COPY,僅在需要自動解壓縮的場合使用 ADD。

10,ENTRYPOINT--入口點
兩種格式:

ENTRYPOINT ["executable", "param1", "param2"](exec格式)
ENTRYPOINT command param1 param2(shell中執(zhí)行)。

ENTRYPOINT 的 目的和CMD一樣,都是在指定容器啟動程序及參數(shù)。ENTRYPOINT 在運行時也可以替代,不過比CMD要略顯繁瑣,需要通過docekr run的參數(shù) --entrypoint 來指定。

#與CMD比較說明:
1)相同點:只能寫一條,如果寫多條,那么只有最后一條生效。
容器啟動時才運行,運行時機相同。
2)不同點: ENTRYPOINT不會被運行的命令覆蓋,而CMD則會被覆蓋。
例子:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
ENTRYPOINT echo hello world
ENTRYPOINT echo hello china
#與CMD一樣,只有最后一條指令生效
[root@sqm-docker01 dfs]# docker run --rm  en:v1 
hello china
#不同的是不會被運行時的命令覆蓋
[root@sqm-docker01 dfs]# docker run ---rm  en:v1 echo "test"
hello china
#除非使用--entrypoint參數(shù)
[root@sqm-docker01 dfs]# docker run --rm --entrypoint hostname  en:v1    
fa667d019ce5
#這里使用hostname命令將hello china覆蓋了

11,ENTRYPOINT與CMD指令結(jié)合使用
#如果我們在Dockerfile中同時寫了ENTRYPOINT和CMD,那么CMD指定的內(nèi)容就會作為ENTRYPOINT的參數(shù)。
舉例如下:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
ENTRYPOINT ["/usr/bin/ping","baidu.com","-c"]
CMD ["4"]
#運行一個容器:
[root@sqm-docker01 dfs]# docker run --rm en:v2
PING baidu.com (39.156.69.79): 56 data bytes
64 bytes from 39.156.69.79: seq=0 ttl=127 time=72.931 ms
64 bytes from 39.156.69.79: seq=1 ttl=127 time=62.366 ms
64 bytes from 39.156.69.79: seq=2 ttl=127 time=58.875 ms
64 bytes from 39.156.69.79: seq=3 ttl=127 time=50.662 ms

--- baidu.com ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 50.662/61.208/72.931 ms

此時容器中運行的命令為:ping baidu.com -c 4。

#ENTRYPOINT中的參數(shù)始終會被使用,而CMD的額外參數(shù)可以在容器啟動時動態(tài)替換掉,例子如下:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
ENTRYPOINT ["/usr/bin/ping","baidu.com","-c"]
CMD ["4"]
[root@sqm-docker01 dfs]# docker run --rm en:v2 2
PING baidu.com (39.156.69.79) 56(84) bytes of data.
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=127 time=80.4 ms
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=127 time=61.5 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 61.532/70.945/80.358/9.413 ms

此時容器運行的命為:ping baidu.com -c 2。

#這兩種指令結(jié)合起來使用,有什么好處呢?我們通過以場景來理解:(例子和上邊例子大同小異)

//假設(shè)我們需要一個得知自己當(dāng)前公網(wǎng)IP的鏡像,可以那么先用CMD來實現(xiàn):

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
RUN yum  -y install curl
CMD ["/usr/bin/curl","-s","https://ip.cn"]
#構(gòu)建鏡像并運行容器:
[root@sqm-docker01 dfs]# docker build -t myip:v1 .
[root@sqm-docker01 dfs]# docker run --rm myip:v1 
{"ip": "117.136.60.216", "country": "江西省", "city": "移動"}

看起來我們是可以把它當(dāng)作命令使用了,不過命令總有參數(shù),如果我們希望顯示HTTP頭信息,就需要加上-i參數(shù)。那么我們可以直接加-i參數(shù)給docker run myip么?

[root@sqm-docker01 dfs]# docker run --rm myip:v1 -i
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"-i\": executable file not found in $PATH": unknown.

我們可以看到替換文件找不到的報錯,executable file not found,之前我們說過,跟在上面名字后面的是command,運行時會替換為CMD的值。因此這里的-i替換了原來的CMD,而不是添加在原來的curl -s https://ip.cn 后面。-i根本不是命令,所以自然找不到。

//為了有很好的解決這個問題,我們將CMD和ENTRYPOINT結(jié)合使用:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
RUN yum  -y install curl
ENTRYPOINT ["/usr/bin/curl","-s","https://ip.cn"]
CMD ["-i"]
#構(gòu)建并運行容器:
[root@sqm-docker01 dfs]# docker build -t myip:v2 .
[root@sqm-docker01 dfs]# docker run --rm myip:v2 
HTTP/2 200 
date: Wed, 19 Feb 2020 07:10:48 GMT
content-type: application/json; charset=UTF-8
set-cookie: __cfduid=d1903fe7e93a885c6ae4890572cda42161582096248; expires=Fri, 20-Mar-20 07:10:48 GMT; path=/; domain=.ip.cn; HttpOnly; SameSite=Lax
cf-cache-status: DYNAMIC
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
alt-svc: h4-25=":443"; ma=86400, h4-24=":443"; ma=86400, h4-23=":443"; ma=86400
server: cloudflare
cf-ray: 56766c505d6fb22e-HKG

{"ip": "117.136.60.216", "country": "江西省", "city": "移動"}

可以看到成功了,這是因為當(dāng)存在ENTRYPOINT后,CMD的內(nèi)容將會傳遞給ENTRYPOINT使用,從而達到了我們預(yù)期的效果。

##總結(jié)幾條規(guī)律:

  • 如果 ENTRYPOINT 使用了 shell 模式,CMD 指令會被忽略。
  • 如果 ENTRYPOINT 使用了 exec 模式,CMD 指定的內(nèi)容被追加為 ENTRYPOINT 指定命令的參數(shù)。
  • 如果 ENTRYPOINT 使用了 exec 模式,CMD 也應(yīng)該使用 exec 模式

12,VOLUME--定義匿名卷
格式為:

VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>

在Dockerfile中,我們可以預(yù)先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應(yīng)用也可以正常運行,不會向容器存儲層寫入大量數(shù)據(jù)。

VOLUME /data

這里的/data 目錄就會在運行時自動掛載為匿名卷,任何向/data 中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態(tài)化。當(dāng)然,運行容器時可以覆蓋這個掛載設(shè)置。

docker run -d -v mydata:/data xxxx

在上邊命令中,就使用了mydata 這個命名卷掛載到了/data 這個位置,替代了Dockerfile 中定義的匿名卷的掛載配置。

#注意:從該指令的支持的格式就可以知道,VOLUME只支持docker manager volume的掛載方式,而不支持bind mount的方式。

docker manager volume:不需要指定源文件,只需要指定mount point。把容器里面的目錄映射到了本地(宿主機)。

舉例如下:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
VOLUME /usr/share/nginx/html/
#構(gòu)建并運行容器:
[root@sqm-docker01 dfs]# docker build -t volume:v1 .
[root@sqm-docker01 dfs]# docker run -itd --name volume volume:v1 
5d963fb3b51ae9ddcc3b55382e289e0447235676f8893900a440f5f9500f035e

我們通過docker inspect查看通過該dockerfile創(chuàng)建的大量生成的容器,可以看到掛載點的信息:

[root@sqm-docker01 dfs]# docker inspect volume 
        "Mounts": [
            {
                "Type": "volume",
                "Name": "190c5a22df09462a9f5fd54209b8bc5ad06fc9382f9c8b9c665c734e4bcf95e0",
                "Source": "/var/lib/docker/volumes/190c5a22df09462a9f5fd54209b8bc5ad06fc9382f9c8b9c665c734e4bcf95e0/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

從上面的信息可以看出默認掛載到本地的源路徑為“/var/lib/docker/volumes/190c5a22df09462a9f5fd54209b8bc5ad06fc9382f9c8b9c665c734e4bcf95e0/_data” ,而目標(biāo)路徑(容器內(nèi)的路徑)為自定義的"/usr/share/nginx/html"。

13,WORKDIR--指定工作目錄
格式為:

WORKDIR <工作目錄路徑>

設(shè)置工作目錄,對RUN,CMD,ENTRYPOINT,COPY,ADD生效。如果目錄不存在,則會幫你創(chuàng)建,也可以設(shè)置多次,如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
#則最終的路徑為/a/b/c。

##WORKDIR也可以解析環(huán)境變量,如:

ENV DIRPATH /usr/src
WORKDIR $DIRPATH
RUN pwd
#pwd的執(zhí)行結(jié)果是/usr/src。

注意:千萬不要把dockerfile等同于shell腳本來書寫,例如:

RUN cd /app
RUN echo "hello" > world.txt

將這個dockerfile 進行構(gòu)建鏡像運行后,會發(fā)現(xiàn)找不到/app/world.txt 文件,或者其內(nèi)容不是 hello。原因其實很簡單,在dockerfile中這兩行RUN命令的執(zhí)行環(huán)境不同,是兩個完全不同的容器。這就是對 Dockerfile 構(gòu)建分層存儲的概念不了解所導(dǎo)致的錯誤。
如果需要改變以后各層的工作目錄的位置,那么應(yīng)該使用 WORKDIR 指令。

14,USER--指定當(dāng)前用戶

格式: USER <用戶名>[:<用戶組>]

USER指令和WORKDIR相似,都是改變環(huán)境狀態(tài)并影響以后的層。
WORKDIR是改變工作目錄,USER是改變之后層的執(zhí)行RUN,CMD,以及ENTRYPOINT 這類命令的身份。
當(dāng)然,和WORKDIR一樣,USER只是幫助你切換到指定用戶而已,這個用戶必須是事先建立好的,否則無法切換:

RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]

如果以root執(zhí)行腳本,在執(zhí)行期間希望改變身份,比如希望以某個已經(jīng)建立好的用戶來運行某個服務(wù)進程,不要使用su或者sudo,這些都需要比較麻煩的配置,而且在TTY缺失的環(huán)境下經(jīng)常出錯。一般使用 gosu。
舉例如下:

# 建立 redis 用戶,并使用 gosu 換另一個用戶執(zhí)行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下載 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true
# 設(shè)置 CMD,并以另外的用戶(redis)執(zhí)行
CMD [ "exec", "gosu", "redis", "redis-server" ]

15,ONBUILD--為鏡像添加觸發(fā)器

格式:ONBUILD <其它指令>

ONBUILD 是一個特殊的指令,它后面跟的是其他指令,比如RUN,COPY等,而這些指令,在當(dāng)前鏡像構(gòu)建時并不會被執(zhí)行。
只有當(dāng)以當(dāng)前鏡像為基礎(chǔ)鏡像,去構(gòu)建下一級鏡像的時候才會被執(zhí)行。
Dockerfile中的其他指令都是為了定制當(dāng)前鏡像而準(zhǔn)備的,唯有ONBUILD 是為了幫助別人定制自己而準(zhǔn)備的。
舉例如下:
//編寫一個Dockerfile文件,內(nèi)容如下:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ONBUILD COPY ./index.html /   #拷貝文件到容器內(nèi)的 / 下

//利用上面的dockerfile文件構(gòu)建鏡像:
[root@sqm-docker01 dfs]# docker build -t image1 .

//利用image1鏡像創(chuàng)建容器:

[root@sqm-docker01 dfs]# docker run --rm -it image1 /bin/bash
root@db8a068d9f30:/# ls    
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

我們發(fā)現(xiàn)以image1鏡像運行的容器/目錄下并沒有index.html文件,這說明ONBUILD指定的指令并不會在自己的鏡像構(gòu)建中執(zhí)行。

//再編寫一個新的Dockerfile文件,內(nèi)容如下:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM image1  #使用的基礎(chǔ)鏡像是上面構(gòu)建的鏡像image1
CMD echo hello world

//利用上面的dockerfile文件構(gòu)建鏡像:

[root@sqm-docker01 dfs]# docker build -t image2 .
Sending build context to Docker daemon  102.6MB
Step 1/2 : FROM image1
# Executing 1 build trigger
 ---> 796e32308d29
Step 2/2 : CMD echo hello world
 ---> Running in 5c16f913f5e9
Removing intermediate container 5c16f913f5e9
 ---> 4c0a45374727
Successfully built 4c0a45374727
Successfully tagged image2:latest

//利用image2鏡像創(chuàng)建容器:

[root@sqm-docker01 dfs]# docker run -it --rm image2 /bin/bash
root@3e3c1d0fe3f6:/# ls
bin   dev  home        lib    media  opt   root  sbin  sys  usr
boot  etc  index.html  lib64  mnt    proc  run   srv   tmp  var
root@3e3c1d0fe3f6:/# cat index.html 
hello world

我們發(fā)現(xiàn)以image2鏡像運行的容器根目錄下有index.html文件,說明觸發(fā)器執(zhí)行了 COPY ./index.html / 指令。

鏡像緩存特性:

1)Docker 會緩存已有鏡像的鏡像層,構(gòu)建新鏡像時,如果某鏡像層已經(jīng)存在,就直接使用,無需重新創(chuàng)建,如下所示:
//創(chuàng)建一個新的dockerfile

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
RUN yum -y install vim
//構(gòu)建一個鏡像
[root@sqm-docker01 dfs]# docker build -t a:v1 .

#往剛剛創(chuàng)建的dockerfile中添加一點新內(nèi)容(在鏡像中添加一條COPY指令):

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
RUN yum -y install vim
COPY testfile /

#以同一個dockefile重新構(gòu)建鏡像:

[root@sqm-docker01 dfs]# touch testfile
[root@sqm-docker01 dfs]# docker build -t a:v2 .
Sending build context to Docker daemon  102.6MB
Step 1/3 : FROM centos
 ---> 0f3e07c0138f
Step 2/3 : RUN yum -y install vim
 ---> Using cache
 ---> 6467d4675159
Step 3/3 : COPY testfile /
 ---> 6b92fe05882f
Successfully built 6b92fe05882f
Successfully tagged a:v2

(1)確保testfile文件已存在
(2)Using cache,重點在這里:之前已經(jīng)運行過相同的RUN指令,這次直接使用緩存中的鏡像層6467d4675159。
(3)執(zhí)行COPY指令。
其過程是啟動臨時容器,復(fù)制testfile,提交新的鏡像層6b92fe05882f,刪除臨時容器。

如果我們希望在構(gòu)建鏡像時不使用緩存,可以在docker build命令中加上--no-cache參數(shù)。

2)Dockerfile中每一個指令都會創(chuàng)建一個鏡像層,上層是依賴于下層的。無論什么時候,只要某一層發(fā)生變化,其上面所有層的緩存都會失效。
也就是說,如果我們改變 Dockerfile 指令的執(zhí)行順序,或者修改或添加指令,都會使緩存失效。
舉例說明,比如交換上面RUN和COPY指令的順序:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
COPY testfile /
RUN yum -y install vim

雖然在邏輯上這種改動對鏡像的內(nèi)容沒有影響,但由于分層的結(jié)構(gòu)特性,Docker 必須重建受影響的鏡像層。

//重新構(gòu)建鏡像:

[root@sqm-docker01 dfs]# docker build -t a:v3 .
Sending build context to Docker daemon  102.6MB
Step 1/3 : FROM centos
 ---> 0f3e07c0138f
Step 2/3 : COPY testfile /
 ---> 97e725434a6b
Step 3/3 : RUN yum -y install vim
 ---> Running in 5f30ff393047
......
Removing intermediate container 5f30ff393047
 ---> 90ceae8b3638
Successfully built 90ceae8b3638
Successfully tagged a:v3

從上面的輸出可以看到生成了新的鏡像層97e725434a6b,緩存已經(jīng)失效。

除了構(gòu)建時使用緩存,Docker 在下載鏡像時也會使用。例如我們下載 httpd 鏡像:

[root@sqm-docker01 dfs]#docker pull httpd
Using default tag: latest
latest: Pulling from library/httpd
f17d81b4b692: Already exists 
06fe09255c64: Already exists 
0baf8127507d: Already exists 
07b9730387a3: Already exists 
6dbdee9d6fa5: Already exists 
Digest: sha256:76954e59f23aa9845ed81146ef3cad4a78f5eb3daab9625874ebca0e416367e2
Status: Image is up to date for httpd:latest

docker pull 命令輸出顯示第一層(base 鏡像)已經(jīng)存在,不需要下載。

簡單了解鏡像分層概念
Dockerfile中每行代碼都會產(chǎn)生一個新的分層,一個鏡像不能超過127層,每個層都會產(chǎn)生一個單獨的id。
在已經(jīng)存在image中的層,都是只讀的。當(dāng)一個image被運行成為一個container的時候,這個層是可讀可寫的,需要操作鏡像層中的文件時,都是通過容器層復(fù)制一份,然后在進行操作,這個機制是copy on write(寫時復(fù)制)。

以上就是Docker容器的配置文件Dockerfile的詳細介紹了,看完之后是否有所收獲呢?如果想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI