Dockerfile最佳實踐(二)鞏固篇

在「Dockerfile最佳實踐(一)」中,咱們已經瞭解到Dockerfile中經常使用指令的使用,並給出了演示示例,這一篇將再補充和鞏固Dockerfile中的經常使用知識點。git

Dockerfile context(上下文)github

在執行docker image build時,CLI首先會告知這次構建將向Docker守護進程發送生成上下文的大小,例如。docker

# cat > Dockerfile <<EOF
FROM centos:7
EOF
# docker image build -t demo .

Sending build context to Docker daemon 2.048kBshell

前面咱們已經講到鏡像的構建是由Docker守護進程(Docker daemon)完成的,那麼上述執行的"docker build -t demo . "實際上是經歷了兩步:ubuntu

首先,Docker CLI將上下文下包含的全部文件(遞歸地)發送給Docker守護進程Docker daemon。centos

而後,Docker Demon會收到Dcoker CLI發送的內容,經過讀取Dockerfile裏面的指令進行鏡像的分層構建。bash

注意:如果使用"/"根做爲上下文,則可能會致使主機異常重啓(在AWS上驗證是會致使主機自動重啓),由於它將會將宿主機"/"根目錄下全部文件傳輸到Docker的守護進程。ide

COPY指令ui

COPY指令有2種書寫格式spa

COPY [--chown=user:group] 本地源文件 容器目標目錄

COPY [--chown=user:group] ["本地源文件", "容器目標目錄"]

默認全部拷貝到容器的文件屬主(UID)和屬組(GID)都是 0(root用戶),除非可選參數--chown指定用戶名、組名或UID/GID。--chown容許是username和groupname字符串,或者使用UID和GID。

--chown 特性僅支持用於構建Linux容器的Dockerfiles,在Windows容器上無效。這是由於Linux和Windows的用戶和組的概念是有差別而且不能相互轉換的,所以使用/etc/passwd和/etc/group將用戶和組名稱轉換爲UID或GID此功也僅適用於基於Linux操做系統的容器。

COPY指令的本地源文件支持使用模糊匹配和正則匹配。

COPY指令中源文件的路徑是以上下文(context)做爲起始點,而不是宿主機上某個絕對路徑下的文件。也所以推薦用戶在構建鏡像時使用"."做爲上下文,而且事先將Dockerfile中所需文件拷貝到指定的上下文路徑下。

注意:使用COPY指令拷貝的源文件是多個文件(不是一個文件)時,則目標目錄必須以"/"結尾,如「/test/",不能寫成「/test」。

演示示例1

# mkdir demo2
# cd demo2
# mkdir dir{a..z}
# touch arr{a..z}.txt
# cat >Dockerfile <<EOF
FROM centos:latest
MAINTAINER firefly@demo.com
#RUN yum -y install coreutils procps-ng bash
RUN useradd -d /home/test -m test -s /bin/bash
RUN mkdir /test
WORKDIR /test
COPY dir* /test/
COPY arr*.txt /home/test/
COPY --chown=test:test arr*.txt /test/
EOF
# docker image build -t demo:v0.1 .
# docker run -idt --name demo01 demo:v0.1 /bin/bash
# docker exec -it demo01 ls -l /test/

RUN指令

RUN指令有2種書寫格式

RUN 命令         #RUN指令後面所接的命令是在shell中運行的,在Linux上就比如執行了 /bin/sh -c command (例如在終端執行:/bin/sh -c ls),在Windows上就比如是執行 cmd /S /C command(例如按win+r鍵在「運行」窗口執行: cmd /S /C mstsc)

RUN ["命令", "參數1", "參數2"]             #以exec的方式運行

RUN指令執行的任何命令都發生在當前鏡像之上的一個新層中(即一箇中間容器intermediate container),命令執行結束後會將結果提交到新的鏡像(如同docker commit),並刪除中間容器,因此咱們在執行RUN指令時會常常看到相似以下日誌。

Step 9/19 : RUN chmod 755 /root/start.sh

---> Running in a2e6b9ce0940

Removing intermediate container a2e6b9ce0940

---> 6b03e9b0ce70

新提交鏡像將用於Dockerfile中的下一個指令。分層運行指令和提交符合Docker的核心概念,在Docker中,提交開銷很小,咱們能夠從鏡像歷史記錄的任何一點來建立容器,就像源代碼管理同樣。

CMD指令

CMD指令有3種書寫格式

CMD 命令              #以shell方式運行

CMD ["可執行程序","參數1","參數2"]                 #exec方式,推薦運行方式

CMD ["參數1", "參數2"]            #用於給ENTRYPOINT指令提供默認參數

Dockerfile中只能有一條CMD指令。若是出現多條,則只有最後一條指令生效。

注意: CMD指令會被 docker run 後的參數所覆蓋。例如:

docker run -idt --name demo01 demo:v0.1 /bin/bash        # "/bin/bash"將會覆蓋CMD指令

ENTRYPOINT指令

ENTRYPOINT指令有2種書寫格式

ENTRYPOINT [「命令」,「參數1」,「參數2」]                     #執行效果同docker exec

ENTRYPOINT 命令 參數1 參數2                     #執行效果同shell命令

ENTRYPOINT指令不會被 docker run 後的參數所覆蓋,而是附加在ENTRYPOINT指令以後。並且CMD指令中的參數會傳遞給ENTRYPOINT。

同CMD指令同樣,Dockerfile中也只能有一條ENTRYPOINT指令,而且如果多條則最後一條生效。

CMD與ENTRYPOINT指令存在的目的就是在容器啓動時就運行必要的應用程序。

演示示例2

驗證CMD指令

# mkdir demo2
# cd demo2
# cat >Dockerfile <<EOF
FROM ubuntu
RUN apt-get -y update && apt-get install -y iputils-ping
CMD ["ping", "114.114.114.114"]
EOF

構建鏡像

# docker image build -t demo02:v0.1 .

根據上述鏡像建立容器demo02,而且不指定相似"/bin/bash" 命令

# docker run -idt --name demo02 demo02:v0.1

查看容器demo02日誌輸出

# docker logs demo02

PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.

64 bytes from 114.114.114.114: icmp_seq=1 ttl=61 time=36.7 ms

64 bytes from 114.114.114.114: icmp_seq=2 ttl=61 time=36.5 ms

登陸到容器,並執行top命令查看,如圖1.1所示。

# docker exec -it demo02 /bin/bash

clip_image001

圖1.1


若是,咱們在建立容器demo02_2時指定運行參數 "/bin/bash",如

# docker run -idt --name demo02_2 demo02:v0.1 /bin/bash


查看容器demo02_2運行狀態

# docker ps -a |grep demo02_2

9b76b8af295d    demo02:v0.1      "/bin/bash"              7 seconds ago                   Up 7 seconds                    demo02_2

登陸到容器,使用top命令查看容器進程,能夠看到CMD指令被docker run 後的"/bin/bash"參數覆蓋了,如圖1.2所示。

# docker exec -it demo02_2 /bin/bash


clip_image001[5]

圖1.2

演示示例3

驗證ENTRYPOINT指令

# mkdir demo3
# cd demo3
# cat >Dockerfile <<EOF
FROM ubuntu
RUN apt-get -y update && apt-get install -y iputils-ping
ENTRYPOINT ["ping", "114.114.114.114"]
EOF
# docker image build -t demo03:v0.1 .

注意,建立容器時不指定相似"/bin/bash"

# docker run -idt --name demo03 demo03:v0.1

查看容器日誌

# docker logs demo03

PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.

64 bytes from 114.114.114.114: icmp_seq=1 ttl=61 time=36.6 ms

64 bytes from 114.114.114.114: icmp_seq=2 ttl=78 time=36.5 ms

登陸到容器,並執行top命令查看,如圖1.3所示。

# docker exec -it demo03 /bin/bash


clip_image001[7]

圖1.3


建立容器demo05時指定運行參數 」127.0.0.1」

# docker run -idt --name demo05 demo03:v0.1 127.0.0.1

進入容器demo05並執行top查看,確認docker run 後的參數127.0.0.1是傳遞給了ENTRYPOINT指令,如圖1.4所示。

clip_image001[9]

圖1.4


演示示例4

ENTRYPOINT指令和CMD指令結合使用

咱們將可能會調整的參數寫到CMD指令。而後在docker run 裏指定參數,這樣CMD指令後的參數就會被覆蓋掉而ENTRYPOINT裏的不被覆蓋。

# cat >Dockerfile <<EOF
FROM ubuntu
RUN apt-get -y update && apt-get install -y iputils-ping
ENTRYPOINT ["ping", "114.114.114.114"]
CMD ["-a"]
EOF
# docker image build -t demo03:v0.2 .

建立容器demo03_2時指定運行參數爲"-c 3"

# docker run -idt --name demo03_2 demo03:v0.2 -c 3

查看容器運行日誌,確認是ping -c 3次後結束,即CMD指令後的參數被覆蓋爲CMD ["-c 3"]

# docker logs demo03_2

PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.

64 bytes from 114.114.114.114: icmp_seq=1 ttl=64 time=36.7 ms

64 bytes from 114.114.114.114: icmp_seq=2 ttl=59 time=36.5 ms

64 bytes from 114.114.114.114: icmp_seq=3 ttl=62 time=36.4 ms

--- 114.114.114.114 ping statistics ---

3 packets transmitted, 3 received, 0% packet loss, time 2003ms

rtt min/avg/max/mdev = 36.495/36.613/36.789/0.254 ms


Dockerfile使用管道符或標準輸入方式來構建鏡像

示例:

echo -e 'FROM busybox\nRUN echo "hello world"' | docker build -

或者

docker build -<<EOF
FROM busybox
RUN echo "hello world"
EOF

上述2個示例是等價的

docker build [OPTIONS] - 中的"-"是鏈接符,用於獲取路徑的位置,並指示Docker從stdin讀取構建上下文。

如下示例咱們嘗試使用COPY或ADD指令

# mkdir example1
# cd example1
# touch somefile.txt
docker build -t myimage:v0.1 -<<EOF
FROM busybox
COPY somefile.txt .
RUN cat /somefile.txt
EOF

會提示以下錯誤,由於上述並無指定從本地來構建上下文

Sending build context to Docker daemon 2.048kB

Step 1/3 : FROM busybox

---> b534869c81f0

Step 2/3 : COPY somefile.txt .

COPY failed: stat /var/lib/docker/tmp/docker-builder461920604/somefile.txt: no such file or directory

但使用如下語法就可使用本地文件系統上的文件,而且使用stdin中的Dockerfile來構建鏡像。語法使用-f(或--file)選項指定要使用的Dockerfile,使用連字符(-)做爲文件名指示Docker從stdin讀取Dockerfile

docker build [OPTIONS] -f- PATH

如下示例咱們嘗試使用COPY或ADD指令

# mkdir example2
# cd example2
# touch somefile.txt
# docker build -t myimage:v0.2 -f- . <<EOF
FROM busybox
COPY somefile.txt .
RUN cat /somefile.txt
EOF


從遠端倉庫接取代碼而後再從標準輸入讀取Dockerfile構建鏡像

docker build [OPTIONS] -f- PATH

若是要從不包含Dockerfile的git倉庫中來構建鏡像,或者是使用自定義Dockerfile構建鏡像,則此語法很是有用。

# docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF
FROM busybox
COPY hello.c .
EOF

注意:上述示例執行成功前提是已安裝好git客戶端

總結

本節示例較多,但均是很是簡單的示例,說話孰能生巧,需勤加練習。另Docker官方建議,爲下降複雜性、依賴性、以及鏡像大小和構建時間,要儘可能避免安裝額外的或沒必要要的包。

相關文章
相關標籤/搜索