1. 程式人生 > 實用技巧 >Docker的Dockerfile

Docker的Dockerfile

參考資料:B站狂神教程
https://www.bilibili.com/video/BV1og4y1q7M4?p=26

Dockerfile是什麼

Dockerfile是一個構建Docker映象的命令引數指令碼。

構建映象的步驟:

  1. 編寫一個Dockerfile檔案
  2. docker build 構建映象
  3. docker run 執行映象
  4. 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裡很全面。

  1. 先是FROM,基於centos映象來構建。
  2. MAINTAINER,構建者的資訊。
  3. COPY了一個readme檔案進去,並指定了目錄,可以寫一些基本的使用文件在裡面。
  4. ADD了兩個必須的壓縮包,並且指定了解壓位置。
  5. RUN了一條命令,安裝了vim。
  6. ENV設定一個環境變數,取名MYPATH。
  7. WORKDIR設定當前的工作目錄,直接使用上面設定的環境變數MYPATH。如果用exec進入映象,預設會在這個目錄下。
  8. 連用幾個ENV設定了一些必須的環境變數,都是JAVA和tomcat的,我看不懂。.NET Core的官方示例Dockerfile中也沒有ENV,我推測ENV可以用來設定資料庫連線字串等。
  9. EXPOSE暴露8080埠。
  10. CMD指定執行的指令,這裡應該是啟動tomcat的指令。