Docker鏡像構建


一、簡介

  在構建容器化應用時,相當重要的步驟莫過於鏡像制作,本文將介紹鏡像制作方法以及鏡像制作的建議。通常鏡像的制作有兩種方式:
  • 使用現有的容器使用docker commit 生成鏡像
  • 使用Dockerfile進行鏡像構建
    采用docker commit 生成的鏡像實際上是容器內的文件系統進行修改在進行提交,而運行的容器實際上是在鏡像的文件系統頂層添加了一層讀寫層,所都的修改都是基於這一層,當生成鏡像時會將這一層數據保存,所以每次使用commit提交鏡像時候都會比原來多一層,這樣會使得鏡像越來越大並且不易維護。同時,對於鏡像使用者來說完全不透明,使用者不清楚該鏡像怎么樣構建的,是否安全等,這種方式及其不推薦。
    而使用Dockerfile構建鏡像,對於使用者來說完全透明,構建鏡像的每一個步驟都在Dockerfile文件中描述的清清楚楚,同時當需要對鏡像修改時候,只需修改Dockerfile文件中的指令,維護鏡像只需要維護一個Dockerfile,這也是鏡像構建的最佳方式。當然,要使用Dockerfile就必須明白Dockerfile的語法和各個指令,以下將作詳細介紹。
 

二、Dockerfile介紹

 
Dockerfile實際上就是一個文本文件,只不過這里的文件內容被Docker Deamon識別從而進行鏡像構建。
使用Dockerfile步驟:
  1.編寫Dockerfile文件,用於描述鏡像生成的步驟
  2.使用docker build -t name:tag 命令構建鏡像
 

語法規則

1.#號代表注解。
2.Dockerfile每一行都是以某個指令(約定大寫字母)開始,后面可加參數構成完整指令,用於描述鏡像構建步驟。
3.指令從上倒下依次執行
4.Dockerfile的第一個指令一定是FROM指令,用於指定基礎鏡像
5.Dockerfile還可以使用.dockerignore文件來忽略在制作鏡像時候需要忽略的文件或者目錄,列如使用COPY指令時候忽略某些文件或者目錄。
6.所有指令參數為數組時,最好使用雙引號
 

環境變量引用

1.若要在Dockerfile中引環境變量則使用$variable_name或${variable_name}
2.當變量為空或者變量值未設置可以使用${variable_name:-value}來指定變量的默認值
 

docker build命令

docker build 命令用於基於Dockerfile構建鏡像,使用語法:
docker build [OPTIONS] PATH | URL | -
其中PATH代表含有Dockfile的目錄,當然也可以是URL中含有Dockerfile
常用選項:
  • -t, --tag list  指定生成鏡像標簽,格式為name:tag
  • -f, --file string  單獨指定Dockerfile文件位置
  • --build-arg list  設置構建時的變量
  • --no-cache  構建鏡像時候不使用緩存
 

快速開始

構建一個簡單的nginx鏡像:
1.創建一個目錄用於存放DockerFile
mkdir /opt/demo -p
cd /opt/demo/
2.編輯Dockerfile文件,如果文件名稱不是Dockerfile需要用-f指定名稱。
FROM centos:latest  #指定基礎鏡像為centos
LABEL Author=“wd”   #指明作者
RUN  yum install -y yum epel-release && yum install -y nginx && echo "${HOSTNAME}-nginx server" > /usr/share/nginx/html/index.html  #運行命令安裝Nginx
CMD  [ "/usr/sbin/nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf”] #啟動容器運行的命令
3.構建鏡像
[root@app51 demo]# docker build -t nginx:v1 ./
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM centos:latest
---> 1e1148e4cc2c
Step 2/4 : LABEL Author="wd"
---> Using cache
---> 8eb3ffcb8ba3
Step 3/4 : RUN  yum install -y yum epel-release && yum install -y nginx && echo "${HOSTNAME}-nginx server" > /usr/share/nginx/html/index.html
---> Using cache
---> ac91999a716e
Step 4/4 : CMD  [ "/usr/sbin/nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"]
---> Running in 323afd4ac89d
Removing intermediate container 323afd4ac89d
---> 6403c553fd04
Successfully built 6403c553fd04
Successfully tagged nginx:v1
4.利用制作的鏡像啟動容器,並查看是否運行成功.
[root@app51 demo]# docker run -d --name nginx-demo-c1 -p 8088:80 nginx:v1             
08812b7def62c9ad7879dfa4182bc28a20f524e2dbc5eb6e4fe63d2b67be3cc9
[root@app51 demo]# curl http://127.0.0.1:8088 
60e5de135132-nginx server  #訪問成功
[root@app51 demo]#
以上的Dockerfile中的每一行是一個指令,用於描述鏡像生成的步驟,以下將介紹這些指令用法。
 

三、指令詳解 

FROM

  FROM指令是最重要且必須為Dockerfile中的第一個非注視指令,用於為構建的鏡像指定基礎鏡像。后續指令運行環境基於該基礎鏡像,構建鏡像時候默認會先從主機上尋找鏡像,若不存在時則從Docker HUB上拉取鏡像。
語法 :
FROM <repository> 
FROM <repository>[:<tag>] 
FROM <repository>@<digest>

解釋:
repository:鏡像倉庫
tag:鏡像標簽,省略就是latest
digest:鏡像哈希碼
示例: 
FROM centos:latest

LABEL

  LABEL用於為鏡像提供元數據信息,其數據格式為key=value。
語法 :
LABEL <key>=<value> <key>=<value> <key>=<value> ...

示例:

LABEL "com.example.vendor"="ACME Incorporated”
LABEL maintainer="SvenDowideit@home.org.au"

MAINTAINER (deprecated)

  用於提供鏡像提供者的信息,可以在Docker任何位置。該語法可能廢棄,推薦使用LABEL
語法:
MAINTAINER <message>

解釋:
message:可以是任意文本信息
示例: 
MAINTAINER "wd <xxx@163.com>"

COPY

  用於主機中的文件或者復制到鏡像中

語法:

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

解釋:
src:源文件或者目錄,支持通配符。如果src是目錄,src目錄自己不會被復制,復制的是目錄中的文件
dest:容器中文件系統目錄,如果目錄不存在自動創建創建。
user:復制到容器中的文件所屬用戶
group:復制到容器中的文件所屬用戶組
注意事項:
  1. 如果復制的src或dest中存在空格字符需使用第二種加雙引號方式
  2. src必須是 build的上下文目錄(Dockerfile同級目錄或子目錄),不能是父目錄或者絕對路徑
  3. 如果指定來多個src或者src中使用了通配符,則dest必須是一個目錄,且必須以/結尾
示例: 
COPY hom* /mydir/        #拷貝以hom開頭的的所有文件
COPY hom?.txt /mydir/    #?代表占位符,可以拷貝

ADD

  ADD指令類似於COPY,但是ADD比COPY更強大,支持TAR文件和URL路徑

語法:

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

解釋
src:源文件或者目錄,支持通配符。如果src是目錄,src目錄自己不會被復制,復制的是目錄中的文件
dest:容器中文件系統目錄,如果目錄不存在自動創建創建。
user:復制到容器中的文件所屬用戶
group:復制到容器中的文件所屬用戶組
注意事項:
  1. 當src是URL時,如果dest不以/結尾,則src指定的文件將被下載並且被創建為dest,如果dest以/結尾,則src指定下載的文件會保存在dest目錄下。
  2. 當src是一個本地目錄的一個tar壓縮格式文件,其在容器中會被展開為目錄,類型與tar -x命令,通過URL下載的tar文件則不會被解壓。
  3. 如果指定來多個src或者src中使用了通配符,則dest必須是一個目錄,且必須以/結尾,多個文件一同被復制在dest目錄下
示例:
ADD hom* /mydir/        
ADD hom?.txt /mydir/  

WORKDIR

  用於為Dockerfile中的各個指定設置工作目錄,可以使用多次,當使用相對路徑時目錄是基於前一個WORKDIR指令。
語法 :
WORKDIR dirpath

示例:

WORKDIR /usr/local

ENV

  用於為鏡像定義所需的環境變量,並可被Dockfile中位於其以后的指令所調用,如ADD、COPY、RUN等調用格式為$variable_name或者${variable_name},此外在啟動容器時候這些變量也是存在的。

語法:

ENV <key> <value>
ENV <key>=<value> ...

注意:
  1. 第一種格式中key之后的所有值會被作為value,因此一次只能設置一個變量
  2. 第二種格式可一次性設置多個變量,每個變量為一個key=value的鍵值對,如果value種包含空格,可以用反斜線(\)轉義,也可以通過對value加引號進行標識,此外反斜線也可用於續行,多個變量時候建議使用。 

示例:

ENV myName="John Doe” \
    myDog=Rex \
    myCat=fluffy
ENV myCat fluffy

RUN

  用於在build過程中運行的程序,可以是任何指令,可以指定多個RUN

語法:

RUN <command>  #shell 格式默認linux采用/bin/sh -c,windows采用cmd /S /C
RUN ["executable", "param1", "param2”]  #可執行程序格式

示例:

RUN yum install -y nginx
RUN ["/bin/bash", "-c", "echo hello"]

EXPOSE

  用於為容器暴露端口到外部,用於實現通訊,類似於docker run的-p選項

語法:

EXPOSE <port> [<port>/<protocol>...]

解釋:
port:端口
protocol:協議,可以是udp或tcp,默認tcp
示例: 
EXPOSE 8080
EXPOSE 8080/udp 8088/tcp

VOLUME

  用於在image中創建一個掛載目錄,以掛載宿主機上的目錄

語法:

VOLUME <path>
VOLUME ["path"]

解釋:
path:代表容器中的目錄,與docker run 不同,Dockerfile中不能指定宿主機目錄,默認使用docker管理的掛載點
示例: 
VOLUME ["/var/log/“]
VOLUME /myvol

CMD

  用於為在鏡像啟動為容時候提供的默認命令,該指定可以有多個,但是只有最后一個生效。
語法 :
CMD command param1 param2   #shell格式,含有shell環境
CMD ["executable","param1","param2”]  #可執行程序格式
CMD ["param1","param2”] #第三種用於為ENTRYPOINT提供默認參數

注意:

  1. 在第一種格式中command 通常是一個shell命令,且默認以/bin/sh -c來運行它,這意味着此進程在容器的的PID不為1,不能接受unix信號,因此使用docker stop <container>命令停止容器時,此進程接受不到SIGTERM信號。
  2. 第二種格式是可執行程序運行方式,不會以"/bin/sh -c”來發起,無shell環境,所有shell變量不能引用,但是可以用"/bin/bash -c”作為啟動命令達到第一種格式效果
  3. 第三種格式需要結合ENTRYPOINT使用,作用是為其提供默認參數 

ENTRYPOINT

  類似於CMD功能,用於為啟動容器指定默認啟動命令,與CMD不同的是ENTRYPOINT命令不會隨着docker run 后使用的命令覆蓋而會把命令作為參數,除非docker run 參數中指定了—entrypoint
語法 :
ENTRYPOINT <command>
ENTRYPOINT ["<executable>", "<param1>", "<param2>"]

注意事項:
  • 與CMD類似,第一種方式默認會以/bin/sh -c 啟動,而第二種則不會,也就意味着沒有shell環境
  • 通常ENTRPOINT用於使用ENTRPOINT腳本啟動
  • 當CMD與ENTRYPOINT同時存在時,CMD的參數為ENTRYPOINT提供 

示例:

[“nginx”,"-g","daemon off"]

USER

  用於指定構建鏡像時RUN、CMD、ENTRYPOINT等指令使用的用戶或UID,默認情況容器運行身份為ROOT
語法 :
USER <user>[:<group>] 
USER <UID>[:<GID>]

注意事項:
  1. 指定的USER或者GROUP必須在容器中存在,否則指令會運行失敗
示例: 
USER nginx

STOPSIGNAL

  該指令用於設置容器停止時向容器內進程發送的信號,列如 9 、SIGKILL、SIGTERM。

語法:

STOPSIGNAL signal

示例:

STOPSIGNAL SIGKILL

注意事項:
  1. 向容器發送信號只能被PID=1的進程所接收,當PID=1進程不是應用進程時候,應用進程收不到終止信號。 

HEALTHCHECK

  該指令在1.12版本中添加,用於對容器中的應用進行健康檢查,不做檢查使用NONE。當對容器做了健康檢查時候,檢查值為0表示成功,非0表示不健康。 
語法:
HEALTHCHECK [OPTIONS] CMD command

其中OPTIONS有如下選項:

  • --interval=DURATION 檢查間隔(默認: 30s)
  • --timeout=DURATION 超時時間(默認t: 30s)
  • --start-period=DURATION 等待檢查的時間,默認0s代表一啟動就檢查 (默認: 0s)
  • --retries=N (default: 3)  重試次數 

示例:

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

SHELL

  將可執行程序運行為shell環境,默認以/bin/sh -c運行

語法:

SHELL ["executable", "parameters"]

示例:

SHELL ["echo", “hello"] #等價於 RUN echo hello

ARG

  該指令用於在build過程中提供參數,而在命令行使用--build-arg <varname>=<value>來傳遞參數值,這樣可以使用參數進行構建鏡像。 
語法:
ARG <name>[=<default value>]

示例:
Dockerfile
FROM nginx
ARG CONF="/tmp/nginx.conf"
LABEL Author=wd
RUN  touch "${CONF}"

構建鏡像:

[root@app51 ~]# docker build  --build-arg CONF='/etc/test.conf' -t nginx:v15.2 ./     
Sending build context to Docker daemon  225.6MB
Step 1/4 : FROM nginx
 ---> f09fe80eb0e7
Step 2/4 : ARG CONF="/tmp/nginx.conf"
 ---> Using cache
 ---> ac081589c644
Step 3/4 : LABEL Author=wd
 ---> Using cache
 ---> 53b9b0ba4460
Step 4/4 : RUN  touch "${CONF}"
 ---> Running in 50debe96f876
Removing intermediate container 50debe96f876
 ---> d8680a2433bc
Successfully built d8680a2433bc
Successfully tagged nginx:v15.2

運行容器查看:

[root@app51 ~]# docker run --rm nginx:v15.2 ls /etc/test.conf -l
-rw-r--r-- 1 root root 0 Feb 27 11:18 /etc/test.conf

ONBULD

  用於在Dockerfile中定義一個觸發器,當制作出來的鏡像被別人用於基礎鏡像時候自動觸發。

語法:

ONBUILD [INSTRUCTION]

解釋:
INSTRUCTION:指令可以是RUN 、COPY等
注意事項:
  1. ONBUILD不會觸發FROM指令。
  2. 在鏡像標簽中應明確指出onbuild關鍵字,以標記使用其基礎鏡像會觸發其他指令 

示例:

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

 

四、使用multi-stage

  在構建鏡像過程中,我們可能只需要某些鏡像的產物,比如在運行一個go程序需要先go程序包編譯后才運行,如果在一個鏡像里面完成,先要經過安裝編譯環境,程序編譯完再安裝運行環境,最后運行程序,這樣的鏡像體積往往比較大,不利於我們使用。而真正我們需要的鏡像是只有程序包和運行環境,編譯環境的構建在運行容器時候是不需要的,所以Docker提供了一種解決方案就是multi-stage(多階段構建)。
    Docker允許多個鏡像的構建可以使用同一個Dockerfile,每個鏡像構建過程可以稱之為一個stage,簡單理解就是一個FROM指令到下一個FROM指令,而每個stage可使用上一個stage過程的產物或環境(其實還支持其他鏡像的),這樣一來,最終所得鏡像體積相對較小。不僅如此多階段構建同樣可以很方便地將多個彼此依賴的項目通過一個Dockerfile就可輕松構建出期望的容器鏡像,而不用擔心鏡像太大、項目環境依賴等問題。
    通過上述介紹,我們可以在第一個stage將go程序編譯得到編譯后程序包,然后在第二個stage中直接拷貝編譯好的go程序包到運行環境中,最后的鏡像中就只有程序包和運行環境。以下作為示例: 
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]  

在以上Dockerfile中存在兩個FROM指令,也就是兩個stage,第一個stage用於構建產物,而在第二個stage中使用COPY --from=0 意思將第一個stage中的/go/src/github.com/alexellis/href-counter/app拷貝到.目錄,第二個stage僅僅相當於執行copy就有了構建產物,不用在安裝編譯環境,鏡像會很縮小。 
 

命名stage

  默認情況下,stage未命名,可以通過整數來引用它們,第一個stage表示0,第二個表1以此類推。 但是,當有多個stage時候,這樣會顯得麻煩,Docker提供AS 語法可以為stage命名:

FROM golang:1.7.3 as builder

然后在另一個stage中使用:

COPY --from=builder /go/src/github.com/alexellis/href-counter/app .

使用本地stage

  除了可以使用Dockerfile中的stage外,構建鏡像時候還可以直接使用本地已存在的環境和產物,例如:

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

構建鏡像建議

  1. 基礎鏡像盡量選擇比體積較小的鏡像,如每個官方發行的alpine鏡像。雖然這版本鏡像比較小,但是與之帶來的是利用該類鏡像運行的容器中排錯的命令很少;
  2. 使用RUN指令時候,盡量把多個RUN指令合並為一個,通常做法是使用&&符號;
  3. 通過multi-stage方法減少一些不必要使用的環境來減小鏡像;
  4. 安裝完成軟件同時刪除一些不需要的文件或目錄; 

ref:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM