Dockerfile 構建鏡像常用指令
Dockerfile 是一個文本文件,其內包含了一條條的指定(Instruction),每一條指令構建一層,因此每一條指定的內容,就是描述該層應當如何構建。
通過使用build 命令,根據Dockerfile的描述來構建鏡像
通過源代碼的方式
通過標准輸入流的方式
通過源代碼的路徑:
Dockerfile需要放置在項目的跟目錄位置
在構建的時候,Dockerfile client會把整個context打包發送到Docker Server端,然后由server端負責build鏡像,在構建成功后,會刪除context目錄
docker build -t {鏡像名字} {項目的路徑可以是相對路徑}
通過標准輸入流:
通過標准輸入流的方式獲取Dockerfile的內容
client不會打包上傳context目錄,因此對於一些ADD、COPY等涉及host本地文件復制的操作不能夠支持
docker build -t {鏡像名字} - < Dockerfile路徑
已nginx 示例:
[root@server ~]# mkdir myNginx #創建一個存放Dockerfile的目錄 [root@server ~]# cd myNginx [root@server myNginx]# vim Dockerfile #編輯Dockerfile FROM nginx RUN echo '<h1>Hello, My Dockerfile Nginx!</h1>' > /usr/share/nginx/html/index.html
這個Dockerfile很簡單,就兩行。涉及到兩條指令,FROM 和RUN。
FROM 指定基礎鏡像
所謂的定制鏡像,必須是以一個鏡像為基礎,在其上進行定制。一個 Dockerfile 中的 FROM 是必備的指定,並且必須是第一條指令。
FROM {bash鏡像}
RUN 執行命令
RUN 指令是用來執行命令行命令的,一個Dockerfile可以包含多個 RUN,按照定義順序執行。
RUN 支持兩種運行格式:
- shell 格式:RUN <cmd>,這個會當做/bin/sh -c “cmd” 運行,就像直接在命令行中輸入的命令一樣。比如上面的:
RUN echo '<h1>Hello, My Dockerfile Nginx!</h1>' > /usr/share/nginx/html/index.html
-
exec 格式:RUN ["可執行文件", "參數1", "參數2", ...],Docker 把它當做json的順序來解析,必須使用雙引號,且可執行文件必須是完整路徑。
RUN 就像Shell腳本一樣可以執行命令,但是也不建議一個命令對應一個RUN,像下面這樣的寫法
FROM centos RUN yum install -y gcc make cmake RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" RUN mkdir -p /usr/src/redis RUN tar xf redis.tar.gz -C /usr/src/redis --stript-components=1 RUN cd /usr/src/redis RUN make RUN make install
因為Dockerfile 中的每一個指令都會建立一層,RUN 也不例外,每一個RUN 的行為,就和我們手工創建景象的過程一樣,新建一層,上面的這種寫法創建了7層,這樣會產生非常臃腫、非常多層的鏡像,不僅僅增加了構建部署的時間,也容易出錯。上面的Dockerfile正確寫法應該如下:
FROM centos RUN yum install -y gcc make cmake \ && wget -O redis.tar.gz "http://download.redis.io/releases/redi s-3.2.5.tar.gz" \ && mkdir -p /usr/src/redis \ && tar xf redis.tar.gz -C /usr/src/redis --stript-components=1 \ && cd /usr/src/redis \ && make \ && make install
首先,之前所有的命令只有一個目的,就是編譯、安裝redis 可執行文件。因此沒有必要建立很多層,這只是一層的事情。因此 只需要使用一個RUN 指令,使用&& 將各個所需命令串聯起來。將前面的7層簡化為1層。
COPY 復制文件
格式:
- COPY <源路徑> ... <目標路徑>
- COPY ["<源路徑1>", ... "<目標路徑>"]
和 RUN 指令一樣,也有兩種格式,一種類似於命令行,一種類似於函數調用。
COPY 指令將從構建上下文目錄中 <源路徑> 的文件/目錄復制到新的一層的鏡像內的 <目標路徑> 位置。例如:
[root@server ~]# mkdir myCentos #創建一個文件夾 [root@server ~]# cd myCentos/ [root@server myCentos]# echo "hello world" >> file.txt #准備測試文件 [root@server myCentos]# mkdir dir #准備測試目錄 [root@server myCentos]# touch dir/test{1..5}.txt #准備測試目錄里面的文件 [root@server myCentos]# vim Dockerfile #編輯Dockerfile FROM centos COPY file.txt /mydir/ COPY dir /mydir/ [root@server myCentos]# ls #查看宿主主機的當前的測試文件 dir Dockerfile file.txt [root@server myCentos]# ls dir/ #查看宿主機的當前的測試文件 test1.txt test2.txt test3.txt test4.txt test5.txt
[root@server myCentos]# docker build -t mycentos:v1 . #構建一個叫mycentos,標簽為v1的鏡像 Sending build context to Docker daemon 6.144kB Step 1/3 : FROM centos ---> 1e1148e4cc2c Step 2/3 : COPY file.txt /mydir/ ---> 16250be2483b Step 3/3 : COPY dir /mydir/dir ---> 2296f62ff0b3 Successfully built 2296f62ff0b3 Successfully tagged mycentos:v1 [root@server myCentos]# docker run -it --rm --name myC mycentos:v1 /bin/bash #啟動一個容器,使用上面創建的鏡像,進去並執行shell命令 [root@cc87d839493f /]# ls mydir/ #查看容器內的mydir文件夾 dir file.txt [root@cc87d839493f /]# ls mydir/dir/ #查看容器內mydir文件夾下面的dir文件夾的文件 test1.txt test2.txt test3.txt test4.txt test5.txt
從上面示例可以看出, <目標路徑> 可以是容器內的相對路徑,如果該路徑不存在,會自動在復制文件前創建缺失目錄,且<目標路徑>也可以是相對路徑(工作目錄可以用WORKDIR 指令來指定)
如果是拷貝目錄, 那么在 目標地址 也必須寫上 拷貝的目錄的名稱 。否則是將該目錄下的所文件拷貝到目標路徑
還需注意一點,使用COPY指令,源文件的各種元數據都會保留,比如讀、寫、執行權限、文件變更時間等。這個特性對於鏡像定制很有用。特別是構建相關的文件都在使用Git進行管理的時候。
ADD 更高級的復制文件
ADD 指令和COPY 的格式和性質基本一致。但是在 COPY 基礎上增加了一些功能。
比如 <源路徑> 可能是一個 URL,這種情況下,會自動去下載這個鏈接的文件到 <目標路徑>里,下載完成的文件權限自動設置為 600,如果不是想要的權限,那么可以通過 RUN 進行權限調整。
如果 <源路徑> 為一個 tar 壓縮文件的話,或者壓縮格式為 gzip,bzip以及xz 的情況下,ADD 指令會自動解壓縮這個壓縮文件到 <目標路徑>去。
示例
[root@server ~]# mkdir myCentos1 && cd myCentos1 #創建構建編寫Dockerfile的目錄並進入 [root@server myCentos1]# wget http://nginx.org/download/nginx-1.14.2.tar.gz #方便測試,先准備一個nginx1.14-2版本的 [root@server myCentos1]# vim Dockerfile #編寫Dockerfile FROM centos ADD http://nginx.org/download/nginx-1.12.2.tar.gz /nginx/ ADD nginx-1.14.2.tar.gz /nginx/ [root@server myCentos1]# ls #編寫完成查看當前目錄下的文件 Dockerfile nginx-1.14.2.tar.gz [root@server myCentos1]# docker build -t mycentos:v2 . #開始構建一個名字叫mycentos:v2的鏡像 Sending build context to Docker daemon 1.018MB Step 1/3 : FROM centos ---> 1e1148e4cc2c Step 2/3 : ADD http://nginx.org/download/nginx-1.12.2.tar.gz /nginx/ Downloading [==================================================>] 981.7kB/981.7kB ---> a1c9717e9a03 Step 3/3 : ADD nginx-1.14.2.tar.gz /nginx/ ---> 068c9e89ae0e Successfully built 068c9e89ae0e Successfully tagged mycentos:v2 [root@server myCentos1]# docker run -it --rm --name myC mycentos:v2 /bin/bash #進入容器,並執行/bin/bash命令 [root@197da52295fa /]# ls /nginx/ #查看nginx目錄 nginx-1.12.2.tar.gz nginx-1.14.2
從上面示例可以看出,ADD指令,如果我們跟的是一個URL的文件,那么會自動下載並放到 <目標路徑>,如果跟的是一個壓縮文件,那么會自動解壓到 <目標路徑>
注意:如果我們只是單純的想復制一個文件或者一個壓縮包,那么建議還是使用COPY,如果需要像上面這樣用到下載,或解壓縮,采用ADD
CMD 容器啟動命令
容器不是虛擬機,容器就是進程,既然是進程,那么在啟動容器的時候,需要指定所運行的程序及參數。CMD 指令就是用於指定容器默認的主進程啟動命令的。
CMD 指令有三種格式:
- shell 格式: CMD <命令>
- exec 格式:CMD ["可執行文件","參數1","參數2", ...]
- 參數列表格式:CMD ["參數1","參數2",...] , 這個時候CMD作為 ENTRYPOINT的參數。
在指令格式上,一般推薦使用exec 格式,這類格式在解析時會被解析為JSON數組,因此一定要使用雙引號 “” ,而不要使用單引號。
如果使用shell 格式的話,實際的命令會被包裝為 sh -c 的參數的形式進行執行。比如:
CMD echo $HOME
在實際執行中,會將其變更為:
CMD ["sh", "-c", "echo $HOME"]
在運行時可以指定新的命令來替代鏡像設置中的這個默認命令,比如,centos鏡像默認的 CMD 是 /bin/bash, 如果我們直接 docker run -it centos 的時候,會直接進入 bash。我們也可以在運行時指定別的命令,如 docker run -it centos cat /etc/passwd 。這就是用 cat /etc/passwd命令替換了默認的 /bin/bash 命令了。

前台執行和后台執行的問題: Docker 不是虛擬機,容器中的應用都應該以前台執行,而不是像虛擬機、物理機里面那樣,用 start / systemctl 去啟動后台服務,容器內沒有后台服務的概念。 比如:如果我們將CMD 寫成這樣: CMD service nginx start 然后會發現容器執行后就立刻退出了,甚至在容器內使用systemctl 命令結果卻發現根本執行不了。這就是因為沒有搞明白前台、后台的概念,沒有區分容器和虛擬機的差異,依舊在以傳統虛擬機的角度去理解容器 對於容器而言,其啟動程序就是容器應用進程,容器就是為了主進程而存在的,主進程退出,容器就失去了存在的意義,從而退出,其它輔助進程不是它需要關心的東西。 而使用 service nginx start 命令,則是希望 start來已后台守護進程形式啟動nginx服務。而 CMD service nginx start 會被理解為 CMD ["sh", "-c", "service nginx start"],因此主進程實際上是sh, 那么當service nginx start 命令結束后,sh 也就結束了, sh 作為主進程退出了,自然就會令容器退出。 正確的做法是直接執行 nginx 可執行文件,並且要求以前台形式。如: CMD ["nginx", "-g"," daemon off;"]
ENTRYPOINT 入口點
ENTRYPOINT 的格式和 RUN 指令格式一樣,分為 exec 格式 和shell格式。
ENTRYPOINT 的目的和 CMD 一樣,都是在指定容器啟動程序及參數。ENTRYPOINT 在運行時也可以替代,不過比CMD要繁瑣,需要通過 docker run 的參數 --entrypoint 來指定。
當指定了 ENTRYPOINT 后,CMD 的含義就發生了改變,不再是直接運行其命令,而是將 CMD 的內容作為參數傳給 ENTRYPOINT 指令,換句話說實際執行時,將變為:
ENTRYPOINT "<CMD>"
那么問題來了? 有了CMD ,為什么還要有ENTRYPOINT呢? 這種 ENTRYPOINT "<CMD>" 有什么好處呢?
場景一:讓鏡像變成命令一樣使用
假設我們需要一個得知自己當前公網IP的鏡像,那么可以使用CMD來實現:
[root@server myCentos2]# cat Dockerfile #查看Dockerfile文件內容 FROM centos CMD ["curl", "-s", "https://ip.cn"] [root@server myCentos2]# docker build -t mycentos:v3 . #創建一個鏡像 [root@server myCentos2]# docker run mycentos:v3 #啟動一個容器 當前 IP: 116.24.66.227 來自: 廣東省深圳市 電信
這樣看起來是可以當做命令來使用,不過命令一般有參數,CMD 中實質的命令是 curl, 如果希望顯示HTTP頭信息,就需要加上 -i 參數。
[root@server myCentos2]# docker run mycentos:v3 -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 根本不是命令,所以找不到。
那么如果我們希望加入 -i 參數,我們就必須重新完整的輸入這個命令:
[root@server myCentos2]# docker run mycentos:v3 curl -i https://ip.cn
顯然這不是好的解決辦法,而是用 ENTRYPOINT 就可以解決這個問題:
[root@server myCentos2]# cat Dockerfile #查看修改后的Dockerfile FROM centos ENTRYPOINT ["curl", "-s", "https://ip.cn"] [root@server myCentos2]# docker build -t mycentos:v4 . #再次生成一個新的鏡像 [root@server myCentos2]# docker run mycentos:v4 #測試不加參數啟動鏡像 當前 IP: 116.24.66.227 來自: 廣東省深圳市 電信 [root@server myCentos2]# docker run mycentos:v4 -i #測試加上參數啟動鏡像 HTTP/1.1 200 OK Date: Wed, 09 Jan 2019 13:23:09 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Set-Cookie: __cfduid=d67dbccf782da9ec6788bf87a78993a701547040189; expires=Thu, 09-Jan-20 13:23:09 GMT; path=/; domain=.ip.cn; HttpOnly Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" Server: cloudflare CF-RAY: 49673780decb961f-SJC 當前 IP: 116.24.66.227 來自: 廣東省深圳市 電信
從上面可以看出,不加-i 參數可以運行成功, 加上-i參數也可以運行成功,且達到了curl -i 的效果。這是因為當存在 ENTRYPOINT 后,CMD 的內容將會作為參數傳遞給 ENTRYPOINT ,而這里 -i 就是新的CMD,因此會作為參數傳遞給curl,從而達到了我們預期的效果。
場景二:應用運行前的准備工作
啟動容器就是啟動主進程,但有些時候,啟動主進程前,需要一些准備工作。比如 mysql 類的數據庫,可能需要一些數據庫配置、初始化的工作,這些工作要在最終的mysql 服務器運行之前解決。此外,可能希望避免使用root 用戶去啟動服務器, 從而提高安全性,而在啟動還需要以 root 身份執行一些必要的准備工作,最后切換到服務用戶啟動服務等。或者除了服務外,其它命令依舊可以使用 root 身份執行,方便調試等。
這些准備工作是和容器 CMD 無關的,無論CMD 是什么,都需要事先進行一個預處理的工作。這種情況下,可以寫一個腳本,然后放入 ENTRYPOINT 中去執行,而這個腳本會接到的參數 (也就是CMD)作為指令,在腳本最后執行。比如官方的redis 中就是這樣做的:
FROM alpine:3.4 ... RUN addgroup -S redis && adduser -S -G redis redis ... ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 6379 CMD ["redis-server"]
可以看到其中為了 redis 服務創建了redis 用戶,並在最后指定了 ENTRYPOINT 為 docker-entrypoint.sh 腳本。
#!/bin/bash ... # allow the container to be started with `--user` if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then chown -R redis . exec su-exec redis "$0" "$@" fi exec "$@"
該腳本的內容就是根據 CMD 的內容來判斷,如果是 redis-server 的話,則切換到 redis用戶身份啟動服務,否則依舊使用 root 身份執行。比如:
# docker run -it redis id uid=0(root) gid=0(root) groups=0(root)
ENV 設置環境變量
格式:
- ENV <key><value>
- ENV<key1>=<value1> <key2>=<value2>
這個指令很簡單,就是設置環境變量,無論是后面的其它指令,如RUN,還是運行時的應用,都可以直接使用這里定義的環境變量。
[root@server myCentos3]# cat Dockerfile #查看編輯好的Dockerfile文件內容 FROM centos ENV name1 xiaobai ENV name2=zhazha name3=cainiao [root@server myCentos3]# docker build -t mycentos:v5 . #構建鏡像 [root@server myCentos3]# docker run --rm -it --name test mycentos:v5 #啟動一個容器,並以交互式啟動 [root@b55929b2c63f /]# echo $name1 #進入容器后調用環境變量name1 xiaobai [root@b55929b2c63f /]# echo $name2 $name3 #調用環境變量name2,name3 zhazha cainiao
定義了環境變量,可以在后續的指令中直接使用環境變量
FROM centos ENV URL="https://nginx.org/download/" ADD $URLnginx.1.12.2.tar.gz /usr/local/
ARG 構建參數
格式:
- ARG <參數名>[=<默認值>]
構建參數和 ENV的效果一樣,都是設置環境變量。所不同的是,ARG 所設置的構建環境和環境變量,在將來容器運行時是不會存在這些環境變量的。但是不要因此就使用 ARG 保存密碼之類的信息,因為 docker history 還是可以看到所有值的。
Dockerfile 中的ARG指令是定義參數名稱,以及定義其默認值。該默認值可以在構建命令 docker build 中用 --build-arg <參數名>=<值> 來覆蓋。
VOLUME 定義匿名卷
格式:
- VOLUME ["<路徑1>","<路徑2>"...]
- VOLUME <路徑>
容器運行時應該盡量保持容器存儲層不會發生寫操作,對於數據庫類需要保存動態數據的應用,其數據文件應該保存在數據卷中,為了防止運行時忘記將動態文件所保存目錄掛載為卷,在Dockerfile中,可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應用也可以正常運行,不會向容器存儲層寫入大量數據。
[root@server myCentos4]# cat Dockerfile #編寫Dockerfile文件,內容如下 FROM nginx VOLUME /usr/share/nginx/html/ [root@server myCentos4]# docker build -t mynginx:v1 . #創建一個mynginx:v1的鏡像 [root@server myCentos4]# docker run -it --rm --name nginx mynginx:v1 /bin/bash #利用mynginx:v1鏡像啟動一個名字叫做nginx的容器 root@eaa3ffb6dda7:/# ls /usr/share/nginx/html/ #查看nginx網站目錄的文件 50x.html index.html # 此時打開第二個終端,第一個終端不要關閉 [root@server ~]# docker inspect nginx #在第二個終端查看啟動的nginx 容器掛載信息,會發現在宿主機上面自動掛載到了一個目錄 "Mounts": [ ... "Source": "/var/lib/docker/volumes/47794aa178f1aa829d8e778f319a6bb678349926a8107f459fcb10d3b00ff8b5/_data", ... ], [root@server ~]# ls /var/lib/docker/volumes/47794aa178f1aa829d8e778f319a6bb678349926a8107f459fcb10d3b00ff8b5/_data #查看掛載的目錄,和容器中的目錄一樣,有同樣的文件 50x.html index.html [root@server ~]# echo "<h1>hello docker</h1>" >> /var/lib/docker/volumes/47794aa178f1aa829d8e778f319a6bb678349926a8107f459fcb10d3b00ff8b5/_data/test.html #在該目錄添加文件 # 此時回到nginx容器啟動的第一個終端 root@eaa3ffb6dda7:/# ls /usr/share/nginx/html/ #再次查看該目錄會發現也有上面新添加的文件 50x.html index.html test.html root@eaa3ffb6dda7:/# cat /usr/share/nginx/html/test.html #並且查看文件內容 <h1>hello docker</h1>
從上面可以看出,實際Dockerfile中只做了一件事,那就是將nginx網站目錄掛載到了一個宿主機的一個匿名目錄,且通過 docker inspect可以找到該目錄,這樣就實現了動態的更改網站數據目錄的內容了。從而提高了靈活性。
同樣還可以在運行容器的時候指定掛載:
[root@server myCentos4]# docker run -it --rm -v mydata:/usr/share/nginx/html/ --name nginx mynginx:v1 /bin/bash #指定掛載的目錄啟動容器 root@f5c496cc9846:/# # 打開第二個終端查看掛載目錄 [root@server ~]# docker inspect nginx #查看掛載信息 "Mounts": [ ... "Source": "/var/lib/docker/volumes/mydata/_data", "Destination": "/usr/share/nginx/html", ... ],
這樣 使用了 mydata 這個命令卷掛載到了/usr/share/nginx/html 這個位置,替代了Dockerfile 中定義的匿名卷的掛載配置
EXPOSE 聲明端口
格式:
- EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是聲明運行容器時提供的服務端口,這只是一個聲明,在運行時並不會因為這個聲明就會開啟這個端口的服務。在Dockerfile中寫入這樣的聲明有兩個好處,一個是幫助使用鏡像者理解這個鏡像服務的守護端口,以方便配置映射;另一個用處則是在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射EXPOSE的端口。
WORKDIR 指定工作目錄
格式:
- WORKDIR <工作目錄路徑>
使用 WORKDIR 指令可以來制定工作目錄(或者稱為當前目錄),以后各層的當前目錄就被改為指定的目錄,如該目錄不存在,則會自動創建。
可以為RUN、CMD、ENTRYPOINT指令配置工作目錄,可以使用多個WORKDIR指令,后續參數如果是相對路徑,則會基於之前的命令指定的路徑。如:WORKDIR /home WORKDIR test。最終路徑為/home/test。path路徑也可以是環境變量,比如有環境變量HOME=/home, WORKDIR $HOME/test也就是/home/test
[root@server myCentos5]# cat Dockerfile #查看編輯好的Dockerfile文件 FROM centos WORKDIR /home/testdir [root@server myCentos5]# docker build -t mycentos:v6 . #創建一個名字叫做mycentos:v6的鏡像 [root@server myCentos5]# docker run --rm -it mycentos:v6 #使用上面創建的鏡像啟動一個容器 [root@4f7ae938d471 testdir]# pwd #進入容器后查看當前工作目錄 /home/testdir
USER 指定當前用戶
格式:
- USER <用戶名>
USER 和WORKDIR 相似,都是改變環境狀態並影響以后的層。WORKDIR是改變工作目錄,USER 則是指定運行容器時的用戶名或UID,后續的RUN、CMD、ENTRYPOINT 也會已該用戶身份去運行命令。
當然,USER 只是能夠切換到指定用戶而已,這個用戶必須事先建立好,否則無法切換。
RUN groupadd -r redis && useradd -r -g redis redis USER redis RUN ["redis-server"]
HEALTHCHECK 健康檢查
格式:
- HEALTHCHECK [選項] CMD <命令>:設置檢查容器健康的命令
- HEALTHCHECK NONE:如果基礎鏡像有健康檢查指令,使用這行可以屏蔽掉其健康檢查指令
HEALTHCHECK 指令是告訴Docker 應該如何進行判斷容器的狀態是否正常
當在一個鏡像指定了 HEALTHCHECK 指令后,用其啟動容器,初始狀態會為 starting,在HEALTHCHECK 指令檢查成功后變為healthy, 如果連續一定次數失敗,則變為 unhealthy。
HEALTHCHECK 支持下列選項:
- --interval=<間隔>:兩次健康檢查的間隔,默認為30秒;
- --timeout=<時長>:健康檢查命令運行超時時間,如果超過這個時間,本次健康檢查就被視為失敗,默認30秒;
- --retries=<次數>:當連續失敗指定次數后,則將容器狀態視為unhealthy,默認3次。
和CMD, ENTRYPOINT 一樣,HEALTHCHECK 只可以出現一次,如果寫了多個,只有最后一個生效。
在HEALTHCHECK [選項] CMD 后面的命令,格式和 ENTRYPOINT 一樣,分為 shell 格式,和 exec 格式。命令的返回值決定了該次健康檢查的成功與 否:0:成功; 1 :失敗; 2 :保留,不要使用這個值。
假設我們有個鏡像是個最簡單的 Web 服務,我們希望增加健康檢查來判斷其 Web 服務是否在正常工作,我們可以用 curl 來幫助判斷,其 Dockerfile 的 HEALTHCHECK可以這樣寫:
[root@server myNginx1]# cat Dockerfile FROM nginx RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fs http://localhost/ || exit 1 [root@server myNginx1]# docker build -t mynginx:v2 . [root@server myNginx1]# docker run -d --name web -p 80:80 mynginx:v2
這里設置了每5秒檢查一次(這里為了測試所以間隔非常短,實際應該相對較長),如果健康檢查命令超過3秒沒響應就視為失敗,並且使用 curl -fs http://localhost/ || exit 1 作為健康檢查命令。
當運行改鏡像啟動的容器后,可以通過 docker ps 看到最初的狀態為(health:starting):
[root@server myNginx1]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fb9c72f7a6de mynginx:v2 "nginx -g 'daemon of…" 6 seconds ago Up 4 seconds (health: starting) 0.0.0.0:80->80/tcp web
再等待幾秒鍾后,再次 docker ps,就會看到健康檢查狀態變成了 (healthy):
[root@server myNginx1]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fb9c72f7a6de mynginx:v2 "nginx -g 'daemon of…" 18 seconds ago Up 17 seconds (healthy) 0.0.0.0:80->80/tcp web
如果健康檢查連續失敗超過了重試次數,狀態就會變為 (unhealthy)。