在構建容器化應用時,至關重要的步驟莫過於鏡像製做,本文將介紹鏡像製做方法以及鏡像製做的建議。一般鏡像的製做有兩種方式:
使用現有的容器使用docker commit 生成鏡像使用Dockerfile進行鏡像構建
''''
採用docker commit 生成的鏡像其實是容器內的文件系統進行修改在進行提交,而運行的容器其實是在鏡像的文件系統頂層添加了一層讀寫層,所都的修改都是基於這一層,當生成鏡像時會將這一層數據保存,因此每次使用commit提交鏡像時候都會比原來多一層,這樣會使得鏡像愈來愈大而且不易維護。同時,對於鏡像使用者來講徹底不透明,使用者不清楚該鏡像怎麼樣構建的,是否安全等,這種方式及其不推薦。
''''
而使用Dockerfile構建鏡像,對於使用者來講徹底透明,構建鏡像的每個步驟都在Dockerfile文件中描述的清清楚楚,同時當須要對鏡像修改時候,只需修改Dockerfile文件中的指令,維護鏡像只須要維護一個Dockerfile,這也是鏡像構建的最佳方式。固然,要使用Dockerfile就必須明白Dockerfile的語法和各個指令,如下將做詳細介紹。
''''
Docker以從上到下的順序運行Dockerfile的指令。爲了指定基本映像,第一條指令必須是FROM。一個聲明以#字符開頭則被視爲註釋。能夠在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。html
''''
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
#
號表明註解。1.若要在Dockerfile中引環境變量則使用$variable_name或${variable_name}
2.當變量爲空或者變量值未設置可使用${variable_name:-value}來指定變量的默認值golang
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 # 構建鏡像時候不使用緩存
構建一個簡單的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
''''
FROM指令是最重要且必須爲Dockerfile中的第一個非注視指令,用於爲構建的鏡像指定基礎鏡像。後續指令運行環境基於該基礎鏡像,構建鏡像時候默認會先從主機上尋找鏡像,若不存在時則從Docker HUB上拉取鏡像。指定構建鏡像的基礎鏡像(有且只能有一個基礎鏡像)vim
語法 : FROM <repository> FROM <repository>[:<tag>] FROM <repository>@<digest> FROM [基礎鏡像]:[鏡像版本號] 解釋: repository: # 鏡像倉庫 tag: # 鏡像標籤,省略就是latest digest: # 鏡像哈希碼
''''
LABEL用於爲鏡像提供元數據信息,其數據格式爲key=value。windows
語法 : LABEL <key>=<value> <key>=<value> <key>=<value> ... 示例: LABEL version="1.0" description="這是一個Web服務器" by="IT筆錄"
''''
使用LABEL指定元數據時,一條LABEL指定能夠指定一或多條元數據,指定多條元數據時不一樣元數據之間經過空格分隔。推薦將全部的元數據經過一條LABEL指令指定,以避免生成過多的中間鏡像。
''''
用於提供鏡像提供者的信息,能夠在Docker任何位置。該語法可能廢棄,推薦使用LABEL
語法: MAINTAINER <message> 解釋: message:能夠是任意文本信息 示例: MAINTAINER "wd <xxx@163.com>"
''''
用於主機中的文件或者複製到鏡像中
語法: COPY [--chown=<user>:<group>] <src>... <dest> COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] 解釋: src: # 源文件或者目錄,支持通配符。若是src是目錄,src目錄本身不會被複制,複製的是目錄中的文件 dest: # 容器中文件系統目錄,若是目錄不存在自動建立建立。 user: # 複製到容器中的文件所屬用戶 group:# 複製到容器中的文件所屬用戶組
注意事項:
''''
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/
注意事項:
''''
用於爲Dockerfile中的各個指定設置工做目錄,可使用屢次,當使用相對路徑時目錄是基於前一個WORKDIR指令
語法 : WORKDIR dirpath 示例: WORKDIR /usr/local
''''
用於爲鏡像定義所需的環境變量,並可被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
注意事項:
''''
用於在build過程當中運行的程序,能夠是任何指令,能夠指定多個RUN,RUN指令建立的中間鏡像會被緩存,並會在下次構建中使用。若是不想使用這些緩存鏡像,能夠在構建時指定--no-cache參數,如:docker build --no-cache
首先有個atest shell腳本,裏面的內容爲
echo $0 echo $1 echo $2 # 執行bash -c 「./atest hello world」他的輸出以下: ./atest hello world
注意事項:
兩種語法: # 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"]
''''
用於爲容器暴露端口到外部,用於實現通信,相似於docker run的-p選項
語法: POSE <port> [<port>/<protocol>...] 解釋: port: # 端口 protocol: # 協議,能夠是udp或tcp,默認tcp 示例: EXPOSE 8080 EXPOSE 8080/udp 8088/tcp
''''
用於爲在鏡像啓動時爲容器候提供的默認命令,該指令能夠有多個,可是隻有最後一個生效。
語法 : # shell格式,含有shell環境 CMD command param1 param2 # 可執行程序格式 CMD ["executable","param1","param2」] # 第三種用於爲ENTRYPOINT提供默認參數 CMD ["param1","param2」]
注意:
''''
相似於CMD功能,用於爲啓動容器指定默認啓動命令,與CMD不一樣的是ENTRYPOINT命令不會隨着docker run 後使用的命令覆蓋而會把命令做爲參數,除非docker run 參數中指定了—entrypoint
語法 : ENTRYPOINT <command> ENTRYPOINT ["<executable>", "<param1>", "<param2>"]
注意事項:
示例:
["nginx","-g","daemon off"]
''''
用於指定構建鏡像時RUN、CMD、ENTRYPOINT等指令使用的用戶或UID,默認狀況容器運行身份爲ROOT
語法 : USER <user>[:<group>] USER <UID>[:<GID>] 示例: USER nginx
注意事項:
''''
將可執行程序運行爲shell環境,默認以/bin/sh -c運行
語法: SHELL ["executable", "parameters"] 示例: # 等價於 RUN echo hello SHELL ["echo", 「hello"]
''''
該指令用於在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
''''
用於在Dockerfile中定義一個觸發器,當製做出來的鏡像被別人用於基礎鏡像時候自動觸發。
語法: ONBUILD [INSTRUCTION] 解釋: INSTRUCTION: # 指令能夠是RUN 、COPY等
注意事項:
''''
用於在image中建立一個掛載目錄,以掛載宿主機上的目錄
語法: VOLUME <path> VOLUME ["path"] 解釋: path:表明容器中的目錄,與docker run 不一樣, Dockerfile中不能指定宿主機目錄,默認使用docker管理的掛載點 示例: VOLUME ["/var/log/「] VOLUME /myvol
''''
在構建鏡像過程當中,咱們可能只須要某些鏡像的產物,好比在運行一個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表示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 .
''''
除了可使用Dockerfile中的stage外,構建鏡像時候還能夠直接使用本地已存在的環境和產物,例如:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
基礎鏡像儘可能選擇比體積較小的鏡像,如每一個官方發行的alpine鏡像。雖然這版本鏡像比較小,可是與之帶來的是利用該類鏡像運行的容器中排錯的命令不多;
使用RUN指令時候,儘可能把多個RUN指令合併爲一個,一般作法是使用&&符號;
經過multi-stage方法減小一些沒必要要使用的環境來減少鏡像;
安裝完成軟件同時刪除一些不須要的文件或目錄;