Dockerfile構建鏡像

1、docker鏡像構建簡介

   在構建容器化應用時,至關重要的步驟莫過於鏡像製做,本文將介紹鏡像製做方法以及鏡像製做的建議。一般鏡像的製做有兩種方式:
使用現有的容器使用docker commit 生成鏡像使用Dockerfile進行鏡像構建
''''採用docker commit 生成的鏡像其實是容器內的文件系統進行修改在進行提交,而運行的容器其實是在鏡像的文件系統頂層添加了一層讀寫層,所都的修改都是基於這一層,當生成鏡像時會將這一層數據保存,因此每次使用commit提交鏡像時候都會比原來多一層,這樣會使得鏡像愈來愈大而且不易維護。同時,對於鏡像使用者來講徹底不透明,使用者不清楚該鏡像怎麼樣構建的,是否安全等,這種方式及其不推薦。
''''而使用Dockerfile構建鏡像,對於使用者來講徹底透明,構建鏡像的每個步驟都在Dockerfile文件中描述的清清楚楚,同時當須要對鏡像修改時候,只需修改Dockerfile文件中的指令,維護鏡像只須要維護一個Dockerfile,這也是鏡像構建的最佳方式。固然,要使用Dockerfile就必須明白Dockerfile的語法和各個指令,如下將做詳細介紹。
''''Docker以從上到下的順序運行Dockerfile的指令。爲了指定基本映像,第一條指令必須是FROM。一個聲明以#字符開頭則被視爲註釋。能夠在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。html

2、Dockerfile介紹

''''Dockerfile實際上就是一個文本文件,只不過這裏的文件內容被Docker Deamon識別從而進行鏡像構建。
''''在構建docker鏡像的時候,實際上將當前目錄移動到了一個虛擬目錄當中,全部的操做路徑都是以虛擬路徑爲準。linux

使用Dockerfile步驟:
  1.編寫Dockerfile文件,用於描述鏡像生成的步驟
  2.使用docker build -t name:tag 命令構建鏡像nginx

格式:
docker build [參數] [dockerfile的路徑]git

docker build參數:
-c : 指定使用CPU大小
-f : 指定dockerfile路徑
-t : 指定構建後的鏡像名稱github

2.1,語法規則
  1. #號表明註解。
  2. Dockerfile每一行都是以某個指令(約定大寫字母)開始,後面可加參數構成完整指令,用於描述鏡像構建步驟。
  3. 指令從上倒下依次執行
  4. Dockerfile的第一個指令必定是FROM指令,用於指定基礎鏡像
  5. Dockerfile還可使用.dockerignore文件來忽略在製做鏡像時候須要忽略的文件或者目錄,列如使用COPY指令時候忽略某些文件或者目錄。
  6. 全部指令參數爲數組時,最好使用雙引號
2.2,環境變量引用

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

2.3,docker build命令

docker build 命令用於基於Dockerfile構建鏡像,使用語法:
docker build [OPTIONS] PATH | URL | -
其中PATH表明含有Dockfile的目錄,固然也能夠是URL中含有Dockerfile
經常使用選項:docker

-t, --tag list        # 指定生成鏡像標籤,格式爲name:tag
-f, --file string     # 單獨指定Dockerfile文件位置
--build-arg list      # 設置構建時的變量
--no-cache            # 構建鏡像時候不使用緩存
2.4,快速開始

構建一個簡單的nginx鏡像:shell

# 1.建立一個目錄用於存放DockerFile
[root@docker ~]# mkdir docker_project
[root@docker ~]# cd docker_project/
[root@docker ~/docker_project]# 

# 2.編輯Dockerfile文件,若是文件名稱不是Dockerfile須要用-f指定名稱
[root@docker ~/docker_project]# vim Dockerfile
# 指定基礎鏡像爲centos:7.9.2009
FROM centos:7.9.2009

# 3.構建鏡像
[root@docker ~/docker_project]# docker build -t centos7:v1 .
Sending build context to Docker daemon  2.048kB
Step 1/1 : FROM centos:7.9.2009
 ---> 8652b9f0cb4c
Successfully built 8652b9f0cb4c
Successfully tagged centos7:v1


# 4.利用製做的鏡像啓動容器,並查看是否運行成功
[root@docker ~/docker_project]# docker run -d -it --name my_centos71 centos7:v1
dd95eab722bf53fbcafbfad420b87eb5acd7cbdccfdff320a6709faca3e2bd37
[root@docker ~/docker_project]# docker ps
CONTAINER ID   IMAGE        COMMAND       CREATED         STATUS         PORTS     NAMES
dd95eab722bf   centos7:v1   "/bin/bash"   3 seconds ago   Up 2 seconds             my_centos71

三,Dockerfile指令詳解

3.1 FROM指令(必須)

''''FROM指令是最重要且必須爲Dockerfile中的第一個非注視指令,用於爲構建的鏡像指定基礎鏡像。後續指令運行環境基於該基礎鏡像,構建鏡像時候默認會先從主機上尋找鏡像,若不存在時則從Docker HUB上拉取鏡像。指定構建鏡像的基礎鏡像(有且只能有一個基礎鏡像)vim

語法 :
FROM <repository> 
FROM <repository>[:<tag>] 
FROM <repository>@<digest>
FROM [基礎鏡像]:[鏡像版本號]

解釋:
repository:      # 鏡像倉庫
tag:             # 鏡像標籤,省略就是latest
digest:          # 鏡像哈希碼
3.2 LABEL指令

''''LABEL用於爲鏡像提供元數據信息,其數據格式爲key=value。windows

語法 :
LABEL <key>=<value> <key>=<value> <key>=<value> ...

示例:
  LABEL version="1.0" description="這是一個Web服務器" by="IT筆錄"

''''使用LABEL指定元數據時,一條LABEL指定能夠指定一或多條元數據,指定多條元數據時不一樣元數據之間經過空格分隔。推薦將全部的元數據經過一條LABEL指令指定,以避免生成過多的中間鏡像。

3.3 MAINTAINER (棄用)指令

''''用於提供鏡像提供者的信息,能夠在Docker任何位置。該語法可能廢棄,推薦使用LABEL

語法:
MAINTAINER <message> 

解釋:
message:能夠是任意文本信息

示例:
MAINTAINER "wd <xxx@163.com>"
3.4 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必須是一個目錄,且必須以/結尾
3.5 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:# 複製到容器中的文件所屬用戶組

示例:
ADD hom* /mydir/        
ADD hom?.txt /mydir/

注意事項:

  1. 當src是URL時,若是dest不以/結尾,則src指定的文件將被下載而且被建立爲dest,若是dest以/結尾,則src指定下載的文件會保存在dest目錄下。
  2. 當src是一個本地目錄的一個tar壓縮格式文件,其在容器中會被展開爲目錄,類型與tar -x命令,經過URL下載的tar文件則不會被解壓。
  3. 若是指定來多個src或者src中使用了通配符,則dest必須是一個目錄,且必須以/結尾,多個文件一同被複制在dest目錄下
3.6 WORKDIR指令

''''用於爲Dockerfile中的各個指定設置工做目錄,可使用屢次,當使用相對路徑時目錄是基於前一個WORKDIR指令

語法 :
WORKDIR dirpath

示例:
WORKDIR /usr/local
3.7 ENV指令

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

語法:
ENV <key> <value>
ENV <key>=<value> ...

示例:
ENV myName="John Doe」 \
    myDog=Rex \
    myCat=fluffy
ENV myCat fluffy

注意事項:

  1. 第一種格式中key以後的全部值會被做爲value,所以一次只能設置一個變量
  2. 第二種格式可一次性設置多個變量,每一個變量爲一個key=value的鍵值對,若是value種包含空格,能夠用反斜線(\)轉義,也能夠經過對value加引號進行標識,此外反斜線也可用於續行,多個變量時候建議使用。
3.8 RUN指令

''''用於在build過程當中運行的程序,能夠是任何指令,能夠指定多個RUN,RUN指令建立的中間鏡像會被緩存,並會在下次構建中使用。若是不想使用這些緩存鏡像,能夠在構建時指定--no-cache參數,如:docker build --no-cache

bash -c選項說明

首先有個atest shell腳本,裏面的內容爲

echo $0
echo $1
echo $2

# 執行bash -c 「./atest hello world」他的輸出以下:
./atest
hello
world

注意事項:

  1. -c 第一個字符串必定要是命令路徑,不能是文件名,若是把./atest前面的./去掉,那麼就會報找不到命令
  2. 命令文件必需要有可執行權限,即./atest 的必須就有x屬性
兩種語法:
# shell 格式默認linux採用/bin/sh -c,windows採用cmd /S /C
RUN <command>  [linux命令]

# exec可執行程序格式 
RUN ["executable", "param1", "param2」]

示例:
RUN yum install -y nginx
RUN ["/bin/bash", "-c", "echo hello"]
3.9 EXPOSE指令

''''用於爲容器暴露端口到外部,用於實現通信,相似於docker run的-p選項

語法:
POSE <port> [<port>/<protocol>...]

解釋:
port:      # 端口
protocol:  # 協議,能夠是udp或tcp,默認tcp

示例: 
EXPOSE 8080
EXPOSE 8080/udp 8088/tcp
3.10 CMD 指令

''''用於爲在鏡像啓動時爲容器候提供的默認命令,該指令能夠有多個,可是隻有最後一個生效。

語法 :
# shell格式,含有shell環境
CMD command param1 param2  

# 可執行程序格式
CMD ["executable","param1","param2」]  

# 第三種用於爲ENTRYPOINT提供默認參數
CMD ["param1","param2」]

注意:

  1. 在第一種格式中command 一般是一個shell命令,且默認以/bin/sh -c來運行它,這意味着此進程在容器的的PID不爲1,不能接受unix信號,所以使用docker stop 命令中止容器時,此進程接受不到SIGTERM信號。
  2. 第二種格式是可執行程序運行方式,不會以"/bin/sh -c」來發起,無shell環境,全部 shell變量不能引用,可是能夠用"/bin/bash -c」做爲啓動命令達到第一種格式效果
  3. 第三種格式須要結合ENTRYPOINT使用,做用是爲其提供默認參數
3.11 ENTRYPOINT 指令

''''相似於CMD功能,用於爲啓動容器指定默認啓動命令,與CMD不一樣的是ENTRYPOINT命令不會隨着docker run 後使用的命令覆蓋而會把命令做爲參數,除非docker run 參數中指定了—entrypoint

語法 :
ENTRYPOINT <command>
ENTRYPOINT ["<executable>", "<param1>", "<param2>"]

注意事項:

  1. 與CMD相似,第一種方式默認會以/bin/sh -c 啓動,而第二種則不會,也就意味着沒有shell環境
  2. 一般ENTRPOINT用於使用ENTRPOINT腳本啓動
  3. 當CMD與ENTRYPOINT同時存在時,CMD的參數爲ENTRYPOINT提供

示例:

["nginx","-g","daemon off"]
3.12 USER 指令

''''用於指定構建鏡像時RUN、CMD、ENTRYPOINT等指令使用的用戶或UID,默認狀況容器運行身份爲ROOT

語法 :
USER <user>[:<group>] 
USER <UID>[:<GID>]

示例: 
USER nginx

注意事項:

  1. 指定的USER或者GROUP必須在容器中存在,不然指令會運行失敗
3.13 SHELL 指令

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

語法:
SHELL ["executable", "parameters"]

示例:
# 等價於 RUN echo hello
SHELL ["echo", 「hello"]
3.14 ARG 指令

''''該指令用於在build過程當中提供參數,而在命令行使用--build-arg = 來傳遞參數值,這樣可使用參數進行構建鏡像。

# 語法:
ARG <name>[=<default value>]

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

# 構建鏡像:
[root@docker ~]# 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=xm
 ---> 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@docker ~]# 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
3.15 ONBULD

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

語法:
ONBUILD [INSTRUCTION]

解釋:
INSTRUCTION:      # 指令能夠是RUN 、COPY等

注意事項:

  1. ONBUILD不會觸發FROM指令。
  2. 在鏡像標籤中應明確指出onbuild關鍵字,以標記使用其基礎鏡像會觸發其餘指令
3.16 VOLUME

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

語法:
VOLUME <path>
VOLUME ["path"]

解釋:
path:表明容器中的目錄,與docker run 不一樣,
Dockerfile中不能指定宿主機目錄,默認使用docker管理的掛載點

示例:
VOLUME ["/var/log/「]
VOLUME /myvol

4、使用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就有了構建產物,不用在安裝編譯環境,鏡像會很縮小。

4.1 命名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 .
4.2 使用本地stage

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

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

五,構建鏡像建議

  1. 基礎鏡像儘可能選擇比體積較小的鏡像,如每一個官方發行的alpine鏡像。雖然這版本鏡像比較小,可是與之帶來的是利用該類鏡像運行的容器中排錯的命令不多;

  2. 使用RUN指令時候,儘可能把多個RUN指令合併爲一個,一般作法是使用&&符號;

  3. 經過multi-stage方法減小一些沒必要要使用的環境來減少鏡像;

  4. 安裝完成軟件同時刪除一些不須要的文件或目錄;

相關文章
相關標籤/搜索