Dockerfile最佳實踐(一)

在「Docker部署您的第一個應用程序」一篇中,咱們已經使用了Dockerfile來構建鏡像,這一篇將補充Dockerfile常用指令。html

Dockerfile最佳实践(一)


Docker能夠經過讀取Dockerfile中的指令來生成鏡像。Dockerfile是一個文本文件,用戶對鏡像操做的全部指令均可以寫在Dockerfile文件中,最後使用docker build來構建鏡像。linux

在「Docker部署您的第一個應用程序」中,咱們使用了命令「docker image build -t bulletinboard:1.0 . 」,docker image build命令經過讀取Dockerfile和指定的上下文來構建鏡像,命令結尾有一個「." 點,這個點就是構建鏡像的上下文。git

上下文是遞歸進行處理的。所以,即包括該上下文下的全部子目錄。github

鏡像的構建是由Docker守護進程(Docker daemon)完成的,而不是由CLI。構建過程首先要作的是將整個上下文(遞歸地)發送給守護進程。在大多數狀況下,最好從一個空目錄做爲上下文開始,並將Dockerfile保存在該目錄中。只添加生成Dockerfile所需的文件。docker

注意:千萬不要使用"/"根做爲上下文,例如以下命令,由於它將會將宿主機"/"根目錄下全部文件傳輸到Docker的守護進程,能夠在開發環境嘗試執行以下命令進行驗證。apache

# docker image build /ubuntu

若要在構建的上下文中將配置文件或包構建到鏡像中,可在Dockerfile中使用COPY指令。若要提升構建性能,可經過在上下文目錄中添加.dockerignore文件來排除文件和目錄。一般Dockerfile,位於上下文的根目錄中,在docker build中使用-f標誌能夠指定文件系統中任何地方的docker file,使用-t標誌能夠指定構建鏡像的倉庫以及tag標籤,例如:vim

# cat >/tmp/centos <<EOF
FROM centos:latest
MAINTAINER firefly@demo.com
EOF
# docker image build -f /tmp/centos -t centos:v0.1 .
# docker images

REPOSITORY TAG IMAGE ID CREATED SIZEcentos

centos v0.1 7eab7b4cc6ea 38 seconds ago 220MB緩存

您也能夠指定構建鏡像的多個倉庫以及tag,例如:

# docker image build -f /tmp/centos -t t01/centos:v0.1 -t t02/centos:v0.2 .
# docker images

REPOSITORY TAG IMAGE ID CREATED SIZE

centos v0.1 7eab7b4cc6ea 3 minutes ago 220MB

t01/centos v0.1 7eab7b4cc6ea 3 minutes ago 220MB

t02/centos v0.2 7eab7b4cc6ea 3 minutes ago 220MB

注意:當前所指的倉庫和tag均位於當前宿主機,在其餘宿主機上,您是沒法獲取這些鏡像的(除非您推送到您的docker hub帳戶下或其餘方式),後續將會講到docker的私有倉庫registry或Harobor來遠程分享咱們作好的鏡像。

使用Dockerfile構建鏡像步驟總結以下:

一、爲鏡像建立一個目錄,如bulletin-board-app

二、進入bulletin-board-app目錄,在該目錄下建立並完成Dockerfile文件編寫

三、鏡像所須要的文件或代碼都拷貝到bulletin-board-app目錄

四、若是bulletin-board-app目錄下有文件是構建時不須要的,則能夠建立並編寫.dockerignore文件來忽略不須要的文件

五、在bulletin-board-app目錄下執行docker image build命令,並指定上下文位置爲".",如命令」docker image build -t test/bulletinboard .「

Docker守護進程在執行Dockerfile中的指令以前,會先對Dockerfile執行初步驗證,若是語法不正確,則返回相關錯誤,若是是參數錯誤,例如目標目錄不存在則不會檢查,直到執行到該指令時拋出錯誤。

Docker守護進程逐個執行Dockerfile中的指令,必要時將每條指令的結果提交給新鏡像,最後輸出新鏡像的ID。Docker守護進程將自動清理您發送的上下文。

注意,每一個指令都是獨立運行的,所以上一指命的執行不會對下一個指令產生任何影響。

只要有可能,Docker將會重用中間鏡像(緩存),以顯著加快Docker的構建過程。而且在控制檯會輸出Using cache消息。

演示示例:

# cat >Dockerfile <<EOF
FROM alpine:3.2
MAINTAINER firefly@demo.com
RUN apk update && apk add socat && rm -r /var/cache/
CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh
EOF

第一次構建

# docker build -t demo/demo:v0.1 .

Sending build context to Docker daemon 2.048kB

Step 1/4 : FROM alpine:3.2

3.2: Pulling from library/alpine

95f5ecd24e43: Pull complete

Digest: sha256:ddac200f3ebc9902fb8cfcd599f41feb2151f1118929da21bcef57dc276975f9

Status: Downloaded newer image for alpine:3.2

---> 98f5f2d17bd1

Step 2/4 : MAINTAINER firefly@demo.com

---> Running in fa3786732ad5

Removing intermediate container fa3786732ad5

---> 6f5007fa547d

Step 3/4 : RUN apk update && apk add socat && rm -r /var/cache/

---> Running in b157222691fb

fetch http://dl-cdn.alpinelinux.org/alpine/v3.2/main/x86_64/APKINDEX.tar.gz

v3.2.3-474-g10ee65f [http://dl-cdn.alpinelinux.org/alpine/v3.2/main]

OK: 5294 distinct packages available

(1/4) Installing ncurses-terminfo-base (5.9)

(2/4) Installing ncurses-libs (5.9)

(3/4) Installing readline (6.3.008)

(4/4) Installing socat (1.7.3.0)

Executing busybox-1.23.2.trigger

OK: 7 MiB in 19 packages

Removing intermediate container b157222691fb

---> 58c5258280f7

Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh

---> Running in ca843dd16f02

Removing intermediate container ca843dd16f02

---> 7bf06f4ab80b

Successfully built 7bf06f4ab80b

Successfully tagged demo/demo:v0.1

第二次構建

# docker build -t demo/demo:v0.2 .

Sending build context to Docker daemon 2.048kB

Step 1/4 : FROM alpine:3.2

---> 98f5f2d17bd1

Step 2/4 : MAINTAINER firefly@demo.com

---> Using cache

---> 6f5007fa547d

Step 3/4 : RUN apk update && apk add socat && rm -r /var/cache/

---> Using cache

---> 58c5258280f7

Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh

---> Using cache

---> 7bf06f4ab80b

Successfully built 7bf06f4ab80b

Successfully tagged demo/demo:v0.2

生成緩存僅用於具備本地父鏈的鏡像。這也意味着這些緩存鏡像均是由以前的構建生成的,或者整個鏡像鏈是用docker load方式加載的。若是須要指定鏡像緩存,可使用--cache from選項。使用--cache from指定的鏡像不須要有父鏈,能夠從其餘倉庫中拉取。

完成構建後,就能夠考慮將存儲在本地倉庫的鏡像推送到遠端倉庫(例如:Harobor)

BuildKit

從18.09版開始,Docker支持一個新的構建工具buildkit,moby/buildkit項目(https://github.com/moby/buildkit)。與現有的實現工具相比,BuildKit提供了許多特性:

一、檢測並跳過執行未使用的構建階段

二、並行化獨立構建階段

三、構建過程當中只增量地傳輸上下文中更改的文件

四、檢測並跳過在上下文中傳輸未使用的文件

五、許多新特性在外部Dockerfile實現

六、避免與API的其他部分(中間鏡像和容器)產生反作用

七、自動修剪並設置生成緩存的優先級

要使用BuildKit,須要在調用docker build命令以前在CLI上設置環境變量DOCKER_BUILDKIT=1。

要了解可用於基於BuildKit的構建的實驗Dockerfile語法,請參閱BuildKit文檔(https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md)。

Dockerfile指令與語法

Dockerfile中的指令不區分大小寫。可是,按慣例是要大寫,以便更容易地將它們與參數區分開來。

Docker按順序運行Dockerfile中的指令。Dockerfile必須以「FROM」指令開頭。固然,FROM指令以前能夠有註釋和全局參數。FROM指令指定了父鏡像。FROM前面只能有一個或多個ARG指令,這些指令聲明Dockerfile的FROM行中使用的參數。

Docker將以#開頭的行視爲註釋。

一、FROM 鏡像:標籤

指定新鏡像是基於哪一個(基礎)鏡像建立,每個鏡像的建立都須要一條FROM指令,例如:

FROM centos:latest

二、MAINTAINER 名字/郵箱

維護人信息,例如:

MAINTAINER firefly@demo.com

三、ADD 源文件 新鏡像目錄

將源文件複製到新建立鏡像中,源文件要與Dockerfile同屬一個目錄,ADD指令會自動解壓tar、tgz包,例如:

ADD example.tgz /data

四、COPY 源文件 目標目錄

將源文複製到新建像中,源文件要與Dockerfile所屬同一個目錄,同ADD相似,例如:

COPY sources.list /etc/apt

五、ENV 關鍵字 值

設置變量或環境變量,例如:

ENV foo /var/www/html

上述表示變量foo的值是/var/www/html

六、RUN 命令

基於現有鏡像執行命令,並提交到新鏡像上,一般在安裝軟件包時使用RUN,例如:

RUN yum -y install sysstat

七、WORKDIR 目錄

指定工做目錄,經過WORKDIR設置工做目錄後,Dockerfile中其後的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都會在該目錄下執行,後續登陸基於該鏡像的容器缺省路徑就是WORKDIR。

八、EXPOSE 端口號

指定Docker容器從該鏡像運行時所開啓的端口,例如:

EXPOSE 80

九、VOLUME 掛載點

Docker容器從該鏡像運行時會設置一個掛載點,例如:

VOLUME /data

十、CMD["要運行的程序","參數1","參數2"]

容器啓動時要運行的命令或腳本,Dockerfile只能有一條CMD命令,若是有多條,則執行最後一條,另外執行docker run 命令時若使用了/bin/bash,則會覆蓋CMD。例如:

CMD ["/bin/bash","/root/start.sh"]

示例演示

一、建立Dockerfile

# mdkir demo
# cd demo
# cat > Dockerfile <<EOF
#My first image
FROM ubuntu:latest
MAINTAINER firefly@demo.com
ENV foo /var/www/html
WORKDIR ${foo}
ADD code.tgz $foo
COPY sources.list /etc/apt
COPY start.sh /root/
RUN chmod 755 /root/start.sh
RUN mkdir /data
VOLUME /data
RUN apt-get -y update && apt-get -y install sysstat lsof net-tools procps vim bash
RUN apt-get -y install apache2
RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN date
COPY ports.conf /etc/apache2/ports.conf
ADD example.tgz /data
EXPOSE 80
CMD ["/bin/bash","/root/start.sh"]
EOF

上述Dockerfile中用到的相關腳本配置以下:

# cat >sources.list <<EOF
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
EOF
# cat >ports.conf <<EOF
ServerName localhost
Listen 80
<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
EOF
# cat >start.sh <<EOF
#!/bin/bash
apache2ctl start
bash
EOF

二、經過Dockerfile構建鏡像

# docker image build -t test/httpd:v0.1 .
# docker images

REPOSITORY TAG IMAGE ID CREATED SIZE

test/httpd v0.1 9a9a2b7dd312 2 minutes ago 165MB

httpd latest 2ae34abc2ed0 3 weeks ago 165MB

三、基於鏡像運行容器

# docker container run -idt -p 80 --name test_httpd01 test/httpd:v0.1

6e7a40ec63b618bf043b45d334c289df782f02e19617dc0686c3be41a582e047

注意:在建立容器時不要加/bin/bash,否則會覆蓋CMD致使apache服務不啓動。

四、查看容器狀態並確認端口處於監聽狀態

# docker ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

a919ee33ae0e test/httpd:v0.1 "/bin/bash /root/sta…" 3 minutes ago Up 3 minutes 0.0.0.0:32787->80/tcp test_httpd01

# docker exec -it test_httpd01 netstat -antp

Active Internet connections (servers and established)

Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name

tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 18/apache2

五、測試

# curl http://127.0.0.1:32787

hello

六、刪除容器和鏡像,容器的生命週期結束

# docker stop test_httpd01
# docker rm test_httpd01
# docker rmi $(docker images |grep "test/httpd" |awk '{print $3}')


總結對於有必定Linux基礎的童鞋,編寫Dockerfile是比較簡單的,但仍然須要注意一些細節,好比ADD、COPY指令差別,RUN與CMD指令差別等。

相關文章
相關標籤/搜索