Docker的Dockerfile
參考資料:B站狂神教程
https://www.bilibili.com/video/BV1og4y1q7M4?p=26
Dockerfile是什麼
Dockerfile是一個構建Docker映象的命令引數指令碼。
構建映象的步驟:
- 編寫一個Dockerfile檔案
- docker build 構建映象
- docker run 執行映象
- docker push 釋出映象
Dockerfile的構建過程
構建Dockerfile需要了解很多能寫在Dockerfile裡的命令:
https://docs.docker.com/engine/reference/builder/#from
這是官方文件,裡面有部分命令,不知道全不全,肯定是夠用了。
還有這張圖片,很有意思:
每個指令都會建立提交一個新的映象層,並提交。最後跑起來的時候再加一層可寫層:
Dockerfile的指令
還是這個文件:https://docs.docker.com/engine/reference/builder/#from
看一下命令大概的分類:
從左到右依次是構建的命令,連線工作目錄等的命令和啟動執行的命令。常用命令就是上面那張鐵鏽紅色的圖。
FROM # 基礎映象,一切從這裡開始構建 MAINTAINER # 映象是誰寫的,姓名+郵箱 RUN # 映象構建的時候需要執行的命令 ADD # 步驟,比如要構建tomcat映象,這裡就放tomcat壓縮包 WORKDIR # 映象的工作目錄 VOLUME # 掛載目錄 EXPOSE # 暴露埠配置 CMD # 指定這個容器啟動的時候要執行的命令 # 只有最後一個會生效,而且可被替代 # 比如自己構建centos,指定/bin/bash來直接進入命令列互動模式 ENTRYPOINT # 指定這個容器啟動的時候要執行的命令,可以追加命令 ONBUILD # 觸發指令。當構建一個被繼承的Dockerfile,這時候就會執行ONBUILD的指令 COPY # 類似ADD,將我們的檔案拷貝到映象中 ENV # 構建的時候設定環境變數
區分一下CMD和ENTRYPOINT。比如我們在Dockerfile裡CMD ls -a
,然後run的時候在後面跟上命令ls -l
,就會替換掉-a,變成-l。但如果ENTRYPOINT ls -a
,run的時候-l,就會變成ls -a -l
。
CMD與ENTRYPOOINT的區別
實踐一下CMD,寫一個最簡單的dockerfile:
FROM centos
CMD ["ls","-a"]
用CMD加命令可以通過中括號來構建,就跟上面程式碼一樣。把它build起來:
root@KitDevVps:/home/dockerfiles# docker build -f cmd-dockerfile -t cmdtest .
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM centos
---> 831691599b88
Step 2/2 : CMD ["ls","-a"]
---> Running in 21e24114cf28
Removing intermediate container 21e24114cf28
---> 9ca8e41f3bf4
Successfully built 9ca8e41f3bf4
Successfully tagged cmdtest:latest
直接run這個映象:
root@KitDevVps:/home/dockerfiles# docker run 9ca8e41f3bf4
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
發現它直接就執行了ls -a
這個命令。我們知道CMD是無法追加命令的,只能替換,我們run一下這個容器,然後加上-l
這個option:
root@KitDevVps:/home/dockerfiles# docker run 9ca8e41f3bf4 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
發現報了一個Error。這是因為-l
直接替換了整個CMD,連ls
都替換掉了,由本來的ls -a
變成了-l
。沒有-l
這種命令,所以報錯。我們現在想執行ls -al
命令,就只能再run出容器時直接加上這個命令,以替換原來的ls -a
:
root@KitDevVps:/home/dockerfiles# docker run 9ca8e41f3bf4 ls -al
total 56
drwxr-xr-x 1 root root 4096 Jul 5 02:33 .
drwxr-xr-x 1 root root 4096 Jul 5 02:33 ..
-rwxr-xr-x 1 root root 0 Jul 5 02:33 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 5 02:33 dev
drwxr-xr-x 1 root root 4096 Jul 5 02:33 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Jun 11 02:35 lost+found
drwxr-xr-x 2 root root 4096 May 11 2019 media
drwxr-xr-x 2 root root 4096 May 11 2019 mnt
drwxr-xr-x 2 root root 4096 May 11 2019 opt
dr-xr-xr-x 104 root root 0 Jul 5 02:33 proc
dr-xr-x--- 2 root root 4096 Jun 11 02:35 root
drwxr-xr-x 11 root root 4096 Jun 11 02:35 run
lrwxrwxrwx 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 May 11 2019 srv
dr-xr-xr-x 13 root root 0 Jul 5 02:33 sys
drwxrwxrwt 7 root root 4096 Jun 11 02:35 tmp
drwxr-xr-x 12 root root 4096 Jun 11 02:35 usr
drwxr-xr-x 20 root root 4096 Jun 11 02:35 var
再實踐一下ENTRYPOINT,寫一個dockerfile,依然用中括號寫命令:
FROM centos
ENTRYPOINT ["ls","-a"]
build起映象來:
root@KitDevVps:/home/dockerfiles# docker build -f entrypoint-dockerfile -t entrypointtest .
Sending build context to Docker daemon 4.096kB
Step 1/2 : FROM centos
---> 831691599b88
Step 2/2 : ENTRYPOINT ["ls","-a"]
---> Running in e4fdc1c58ec7
Removing intermediate container e4fdc1c58ec7
---> 52b32cc2192c
Successfully built 52b32cc2192c
Successfully tagged entrypointtest:latest
run一下:
root@KitDevVps:/home/dockerfiles# docker run 52b32cc2192c
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
加個option-l
:
root@KitDevVps:/home/dockerfiles# docker run 52b32cc2192c -l
total 56
drwxr-xr-x 1 root root 4096 Jul 5 02:38 .
drwxr-xr-x 1 root root 4096 Jul 5 02:38 ..
-rwxr-xr-x 1 root root 0 Jul 5 02:38 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 5 02:38 dev
drwxr-xr-x 1 root root 4096 Jul 5 02:38 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Jun 11 02:35 lost+found
drwxr-xr-x 2 root root 4096 May 11 2019 media
drwxr-xr-x 2 root root 4096 May 11 2019 mnt
drwxr-xr-x 2 root root 4096 May 11 2019 opt
dr-xr-xr-x 107 root root 0 Jul 5 02:38 proc
dr-xr-x--- 2 root root 4096 Jun 11 02:35 root
drwxr-xr-x 11 root root 4096 Jun 11 02:35 run
lrwxrwxrwx 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 May 11 2019 srv
dr-xr-xr-x 13 root root 0 Jul 5 02:33 sys
drwxrwxrwt 7 root root 4096 Jun 11 02:35 tmp
drwxr-xr-x 12 root root 4096 Jun 11 02:35 usr
drwxr-xr-x 20 root root 4096 Jun 11 02:35 var
可以成功執行。這個時候如果直接寫ls -al
:
root@KitDevVps:/home/dockerfiles# docker run 52b32cc2192c ls -al
ls: cannot access 'ls': No such file or directory
注意,如果dockerfile檔案直接命名為"Dockerfile",build的時候就不需要加-f
來指定對應的dockerfile檔案了,它可以自動尋找到。
實踐一個dockerfile
看一下centos7的dockerfile:
首先,幾乎所有映象都是FROM scratch,然後配置需要的軟體和配置來進行構建。
然後ADD上centos的壓縮包。
看一下ubuntu的dockerfile:
我們注意到用docker映象run起來的ubuntu沒有vim和ifconfig命令:
root@KitDevVps:~# docker run -d -it --name ubuntu01 ubuntu
daec7691f786dffa97ae5f757212da17da2c7591aeaa7a5c215217d0904f3089
root@KitDevVps:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
daec7691f786 ubuntu "/bin/bash" 4 seconds ago Up 3 seconds ubuntu01
1cdd55fd90c5 nginx "/docker-entrypoint.…" 36 hours ago Up 32 hours 0.0.0.0:80->80/tcp nginx1
root@KitDevVps:~# docker attach ubuntu01
root@daec7691f786:/# vim
bash: vim: command not found
root@daec7691f786:/# ifconfig
bash: ifconfig: command not found
我們自己構建一個ubuntu:
編寫dockerfile檔案
FROM centos
MAINTAINER Kit<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y net-tools
EXPOSE 8888
CMD echo $MYPATH
CMD echo "build succeed"
CMD /bin/bash
其中環境變數ENV是鍵值對的形式,MYPATH是key,/usr/local是值。
WORKDIR是用$號引用了環境變數。
使用RUN來update,然後安裝了vim和net-tools。實際上執行三條命令不需要寫三個RUN,這樣會生成三層,太浪費,可以像上面ubuntu的Dockerfile那樣,用\
和&&
等符號連線起來,要換行就用上\
,不換行可以一直用&&
來拼接,只寫一個RUN。
最後一行CMD /bin/bash
是執行起來之後啟動bash命令列。
注意,有一個常用的ADD
指令這裡沒有用到,ADD是新增壓縮檔案的,可以自動解壓,壓縮檔案包名之後可以跟一個地址,選擇解壓的位置。這個位置當然是容器內的目錄。看上面ubuntu映象的ADD
後面的壓縮包名後加了個/
,表示解壓在根目錄。
通過dockerfile構建映象
使用docker build
命令。-f後面跟dockerfile名字,-t後面跟自己給映象取的名字,也可以加上tag。最後的.是映象生成的位置:
root@KitDevVps:/home/dockerfiles# docker build -f centos-dockerfile -t myubuntu:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/11 : FROM centos
---> 831691599b88
Step 2/11 : MAINTAINER Kit<[email protected]>
---> Running in f1f196999014
Removing intermediate container f1f196999014
---> 67670905b570
Step 3/11 : ENV MYPATH /usr/local
---> Running in 017f52d55537
Removing intermediate container 017f52d55537
---> 82b77bd524e0
Step 4/11 : WORKDIR $MYPATH
---> Running in a50c62fe9048
Removing intermediate container a50c62fe9048
---> 292ddb55bacb
Step 5/11 : RUN apt-get update
---> Running in 3d6d1e0ef4ce
/bin/sh: apt-get: command not found
The command '/bin/sh -c apt-get update' returned a non-zero code: 127
很尷尬,映象竟然不能執行apt-get,不過一部分的過程可以看到。我就不再搞了,基本用不到生成ubuntu映象這種需求,不折騰了。dockerfile裡的命令是沒有錯的。
我們修改一下dockerfile,將apt指令改為普通的echo:
FROM centos
MAINTAINER Kit<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN echo "step 1"
RUN echo "step 2"
RUN echo "step 3"
CMD echo $MYPATH
CMD echo "build succeed"
CMD /bin/bash
build映象
build一下:
root@KitDevVps:/home/dockerfiles# docker build -f centos-dockerfile -t myubuntu:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/10 : FROM centos
---> 831691599b88
Step 2/10 : MAINTAINER Kit<[email protected]>
---> Running in 1dedfc7bd7cd
Removing intermediate container 1dedfc7bd7cd
---> b810f07548a3
Step 3/10 : ENV MYPATH /usr/local
---> Running in 5f8ce5a07590
Removing intermediate container 5f8ce5a07590
---> 578eab12c981
Step 4/10 : WORKDIR $MYPATH
---> Running in b371dc06fd20
Removing intermediate container b371dc06fd20
---> 7abf094e042e
Step 5/10 : RUN echo "step 1"
---> Running in 0c65382a6491
step 1
Removing intermediate container 0c65382a6491
---> 04bf696c460e
Step 6/10 : RUN echo "step 2"
---> Running in 1276c444e36c
step 2
Removing intermediate container 1276c444e36c
---> d005e474c20c
Step 7/10 : RUN echo "step 3"
---> Running in 02de174de709
step 3
Removing intermediate container 02de174de709
---> 8f7f565d2061
Step 8/10 : CMD echo $MYPATH
---> Running in 028888e7f8c8
Removing intermediate container 028888e7f8c8
---> 7535a914d720
Step 9/10 : CMD echo "build succeed"
---> Running in b8eb3004dc32
Removing intermediate container b8eb3004dc32
---> f416fc8cb156
Step 10/10 : CMD /bin/bash
---> Running in b531285dbe9d
Removing intermediate container b531285dbe9d
---> dc3664ede0fc
Successfully built dc3664ede0fc
Successfully tagged myubuntu:1.0
這次成功了,其中輸出的三次step123都輸出了。
可以通過docker history 映象id
來檢視這個映象是怎麼一步一步構建起來的。看一下我們新構建的映象:
root@KitDevVps:/home/dockerfiles# docker history dc3664ede0fc
IMAGE CREATED CREATED BY SIZE COMMENT
dc3664ede0fc 2 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
f416fc8cb156 2 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
7535a914d720 2 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
8f7f565d2061 2 minutes ago /bin/sh -c echo "step 3" 0B
d005e474c20c 2 minutes ago /bin/sh -c echo "step 2" 0B
04bf696c460e 2 minutes ago /bin/sh -c echo "step 1" 0B
7abf094e042e 2 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
578eab12c981 2 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
b810f07548a3 2 minutes ago /bin/sh -c #(nop) MAINTAINER Kit<xxxxxx@xx.… 0B
831691599b88 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:84700c11fcc969ac0… 215MB
比較全面的Dockerfile展示
上面寫的Dockerfile都很兒戲,下面是狂神視訊教程裡寫的一段tomcat的Dockerfile:
我不搞JAVA,不太懂,但是這一段Dockerfile裡很全面。
- 先是FROM,基於centos映象來構建。
- MAINTAINER,構建者的資訊。
- COPY了一個readme檔案進去,並指定了目錄,可以寫一些基本的使用文件在裡面。
- ADD了兩個必須的壓縮包,並且指定了解壓位置。
- RUN了一條命令,安裝了vim。
- ENV設定一個環境變數,取名MYPATH。
- WORKDIR設定當前的工作目錄,直接使用上面設定的環境變數MYPATH。如果用
exec
進入映象,預設會在這個目錄下。 - 連用幾個ENV設定了一些必須的環境變數,都是JAVA和tomcat的,我看不懂。.NET Core的官方示例Dockerfile中也沒有ENV,我推測ENV可以用來設定資料庫連線字串等。
- EXPOSE暴露8080埠。
- CMD指定執行的指令,這裡應該是啟動tomcat的指令。