首頁 > 軟體

Docker 映象構建保姆級入門範例教學

2022-09-06 18:06:08

一、概述

  • Dockerfile 是一個用來構建映象的文字檔案,文字內容包含了一條條構建映象所需的指令和說明。

官方檔案:

Dockerfile 範例:

二、Dockerfile 結構

Dockerfile 結構主要分為四部分:

  • 基礎映象資訊
  • 維護者資訊
  • 映象操作指令
  • 容器啟動時執行指令 (CMD/ENTRYPOINT)
  • 【溫馨提示】Dockerfile 每行支援一條指令,每條指令可攜帶多個引數(支援&&),支援使用以“#“號開頭的註釋(jason 檔案不支援#註釋),但是也非必須滿足上面的四點。

三、常用 Dockerfile 操作指令

  • ARG—— 定義建立映象過程中使用的變數 ,唯一一個可以在 FROM 之前定義 。
  • FROM——基於某個映象, FROM前面只能有一個或多個ARG指令 。
  • MAINTAINER(已棄用) —— 映象維護者姓名或郵箱地址 。
  • VOLUME —— 指定容器掛載點到宿主機自動生成的目錄或其他容器
  • RUN——執行映象裡的命令,跟在 liunx 執行命令一樣,只需要在前面加上 RUN 關鍵詞就行。
  • COPY——複製本地(宿主機)上的檔案到映象。
  • ADD——複製並解壓(宿主機)上的壓縮檔案到映象。
  • ENV——設定環境變數。
  • WORKDIR —— 為 RUN、CMD、ENTRYPOINT、COPY 和 ADD 設定工作目錄,就是切換目錄 。
  • USER —— 為 RUN、CMD、和 ENTRYPOINT 執行命令指定執行使用者。
  • EXPOSE —— 宣告容器的伺服器埠(僅僅是宣告) 。
  • CMD—— 容器啟動後執行的命令 ,多個 CMD 只會執行最後一個,跟 ENTRYPOINT 的區別是,CMD 可以作為 ENTRYPOINT 的引數,且會被 yaml 檔案裡的 command 覆蓋。
  • ENTRYPOINT—— 容器啟動後執行的命令 ,多個只會執行最後一個。
  • HEALTHCHECH —— 健康檢查 。
  • ONBUILD——它後面跟的是其它指令,比如 RUN, COPY 等,而這些指令,在當前映象構建時並不會被執行。只有當以當前映象為基礎映象,去構建下一級映象的時候才會被執行。
  • LABEL——LABEL 指令用來給映象新增一些後設資料(metadata),以鍵值對的形式 ,替換 MAINTAINER。

1)映象構建(docker build)

docker build -t text:v1 . --no-cache
# 要在構建後將映像標記到多個儲存庫中,請在執行命令-t時新增多個引數
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
### 引數解釋
# -t:指定映象名稱
# . :當前目錄Dockerfile
# -f:指定Dockerfile路徑
#  --no-cache:不快取

2)執行容器測試(docker run)

# 非互動式執行
docker run centos:7.4.1708 /bin/echo "Hello world"
### 互動式執行
# -t: 在新容器內指定一個偽終端或終端。
#-i: 允許你對容器內的標準輸入 (STDIN) 進行互動。
# 會登入到docker環境中,互動式
docker run -it centos:7.4.1708 /bin/bash
# -d:後臺執行,加了 -d 引數預設不會進入容器
docker run -itd centos:7.4.1708 /bin/bash
### 進入容器
# 在使用 -d 引數時,容器啟動後會進入後臺。此時想要進入容器,可以通過以下指令進入:
#docker exec -it :推薦大家使用 docker exec -it 命令,因為此命令會退出容器終端,但不會導致容器的停止。
#docker attach:容器退出,會導致容器的停止。
docker exec -it  b2c0235dc53 /bin/bash
docker attach  b2c0235dc53

3)ARG

  • 構建引數,與 ENV 作用一致。不過作用域不一樣。ARG 設定的環境變數僅對 Dockerfile 內有效,也就是說只有 docker build 的過程中有效,構建好的映象內不存在此環境變數。唯一一個可以在 FROM 之前定義 。構建命令 docker build 中可以用 --build-arg <引數名>=<值> 來覆蓋。

語法格式:

ARG <引數名>[=<預設值>]

範例:

# 在FROM之前定義ARG,只在 FROM 中生效
ARG VERSION=laster
FROM centos:${VERSION}
# 在FROM之後使用,得重新定義,不需要賦值
ARG VERSION
RUN echo $VERSION >/tmp/image_version

4)FROM

  • 客製化的映象都是基於 FROM 的映象 ,【必選項】

語法格式:

FROM [--platform=&lt;platform&gt;] &lt;image&gt; [AS &lt;name&gt;]
FROM [--platform=&lt;platform&gt;] &lt;image&gt;[:&lt;tag&gt;] [AS &lt;name&gt;]
FROM [--platform=&lt;platform&gt;] &lt;image&gt;[@&lt;digest&gt;] [AS &lt;name&gt;]
  • 如果參照多平臺影象,可選--platform標誌可用於指定影象的平臺。FROM例如,linux/amd64、 linux/arm64或windows/amd64。預設情況下,使用構建請求的目標平臺。全域性構建引數可用於此標誌的值,例如允許您將階段強制為原生構建平臺 ( --platform=$BUILDPLATFORM),並使用它交叉編譯到階段內的目標平臺。

範例:

ARG VERSION=latest
FROM busybox:$VERSION
# FROM --platform="linux/amd64" busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

5)MAINTAINER(已棄用)

  • 映象維護者資訊

語法格式:

MAINTAINER <name>

範例:

LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"

6)VOLUME

  • 定義匿名資料卷。在啟動容器時忘記掛載資料卷,會自動掛載到匿名卷。

作用:

  • 避免重要的資料,因容器重啟而丟失,這是非常致命的。
  • 避免容器不斷變大。
  • 在啟動容器 docker run 的時候,我們可以通過 -v 引數修改掛載點。

語法格式:

# 後面路徑是容器內的路徑,對應宿主機的目錄是隨機的
VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>

範例:

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

7)RUN

  • 用於執行後面跟著的命令列命令。

語法格式:

  • RUN (shell形式,命令在 shell 中執行,預設/bin/sh -c在 Linux 或cmd /S /CWindows 上)
  • RUN ["executable", "param1", "param2"](執行形式)

範例:

# 以下三種寫法等價
RUN /bin/bash -c 'source $HOME/.bashrc; 
echo $HOME'
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "source $HOME/.bashrc; echo $HOME"]

8)COPY

  • 拷貝(宿主機)檔案或目錄到容器中,跟 ADD 類似,但不具備自動下載或解壓的功能 。所有新檔案和目錄都使用 0 的 UID 和 GID 建立,除非可選--chown標誌指定給定的使用者名稱、組名或 UID/GID 組合以請求複製內容的特定所有權。

語法格式:

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

範例:

# 新增所有以「hom」開頭的檔案:
COPY hom* /mydir/
# ?替換為任何單個字元,例如「home.txt」。
COPY hom?.txt /mydir/
# 使用相對路徑,並將「test.txt」新增到<WORKDIR>/relativeDir/:
COPY test.txt relativeDir/
# 使用絕對路徑,並將「test.txt」新增到/absoluteDir/
COPY test.txt /absoluteDir/
# 修改檔案許可權
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/

9)ADD

  • 拷貝檔案或目錄到容器中,如果是 URL 或壓縮包便會自動下載或自動解壓 。

ADD 指令和 COPY 的使用格類似(同樣需求下,官方推薦使用 COPY)。功能也類似,不同之處如下:

  • ADD 的優點:在執行 <原始檔> 為 tar 壓縮檔案的話,壓縮格式為 gzip, bzip2 以及 xz 的情況下,會自動複製並解壓到 <目標路徑>。
  • ADD 的缺點:在不解壓的前提下,無法複製 tar 壓縮檔案。會令映象構建快取失效,從而可能會令映象構建變得比較緩慢。具體是否使用,可以根據是否需要自動解壓來決定。

語法格式:

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

範例:

# 萬用字元
ADD hom* /mydir/
# 相對路徑,拷貝到WORKDIR目錄下relativeDir/
ADD test.txt relativeDir/
# 絕對路徑
ADD test.txt /absoluteDir/
# 更改許可權
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/

ADD 和 COPY 的區別和使用場景:

  • ADD 支援新增遠端 url 和自動提取壓縮格式的檔案,COPY 只允許從本機中複製檔案
  • COPY 支援從其他構建階段中複製原始檔(--from)
  • 根據官方 Dockerfile 最佳實踐,除非真的需要從遠端 url 新增檔案或自動提取壓縮檔案才用 ADD,其他情況一律使用 COPY

10)ENV

  • 設定環境變數,定義了環境變數,那麼在後續的指令中,就可以使用這個環境變數。

語法格式:

ENV <key1>=<value1> <key2>=<value2>...
# 省略"="此語法不允許在單個ENV指令中設定多個環境變數,並且可能會造成混淆。
ENV <key> <value>

範例:

ENV JAVA_HOME=/usr/local/jdk
ENV MY_NAME="John Doe" MY_DOG=Rex The Dog 
   MY_CAT=fluffy
# 此語法不允許在單個ENV指令中設定多個環境變數,並且可能會造成混淆。
ENV JAVA_HOME /usr/local/jdk

11)WORKDIR

  • 指定工作目錄。用 WORKDIR 指定的工作目錄,會在構建映象的每一層中都存在。(WORKDIR 指定的工作目錄,必須是提前建立好的)。

語法格式:

WORKDIR <工作目錄路徑>

範例:

FROM busybox
ENV FOO=/bar
WORKDIR ${FOO}   # WORKDIR /bar

12)USER

  • 用於指定執行後續命令的使用者和使用者組,這邊只是切換後續命令執行的使用者(使用者和使用者組必須提前已經存在)。

語法格式:

USER <使用者名稱>[:<使用者組>]
USER <UID>[:<GID>]

範例:

FROM busybox
RUN groupadd --system --gid=9999 admin && useradd --system --home-dir /home/admin --uid=9999 --gid=admin admin
USER admin:admin
# USER 9999:9999

13)EXPOSE

  • 暴露埠 ,僅僅只是宣告埠。

作用:

  • 幫助映象使用者理解這個映象服務的守護埠,以方便設定對映。
  • 在執行時使用隨機埠對映時,也就是 docker run -P 時,會自動隨機對映 EXPOSE 的埠。

語法格式:

# 預設情況下,EXPOSE假定 TCP。
EXPOSE <port> [<port>/<protocol>...]

範例:

EXPOSE 80/TCP 443/TCP
EXPOSE 80 443
EXPOSE 80/tcp
EXPOSE 80/udp

14)CMD

  • 類似於 RUN 指令,用於執行程式,但二者執行的時間點不同:CMD 在構建映象時不會執行,在容器執行 時執行。

語法格式:

CMD <shell 命令>
CMD ["<可執行檔案或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...]  # 該寫法是為 ENTRYPOINT 指令指定的程式提供預設引數
  • 推薦使用第二種格式,執行過程比較明確。第一種格式實際上在執行的過程中也會自動轉換成第二種格式執行,並且預設可執行檔案是 sh。

範例:

CMD cat /etc/profile
CMD ["/bin/sh","-c","/etc/profile"]
  • 注意:如果 Dockerfile 中如果存在多個 CMD 指令,僅最後一個生效。

15)ENTRYPOINT

  • 類似於 CMD 指令,但其不會被 docker run 的命令列引數指定的指令所覆蓋,而且這些命令列引數會被當作引數送給 ENTRYPOINT 指令指定的程式。但是, 如果執行 docker run 時使用了 --entrypoint 選項,將覆蓋 ENTRYPOINT 指令指定的程式。在 k8s 中 command 也會覆蓋 ENTRYPOINT 指令指定的程式

語法格式:

# exec形式,這是首選形式:
ENTRYPOINT ["executable", "param1", "param2"]
# 外殼形式:
ENTRYPOINT command param1 param2

範例:

FROM ubuntu
ENTRYPOINT ["top", "-b"]
# CMD作為ENTRYPOINT引數
CMD ["-c"]
# 與下面的等價
ENTRYPOINT ["top", "-b -c"]
ENTRYPOINT  top -b -c
  • 注意:如果 Dockerfile 中如果存在多個 ENTRYPOINT 指令,僅最後一個生效。

16)HEALTHCHECK

  • 用於指定某個程式或者指令來監控 docker 容器服務的執行狀態。

語法格式:

HEALTHCHECK [OPTIONS] CMD command(通過在容器內執行命令檢查容器執行狀況)
HEALTHCHECK NONE(禁用從基礎映像繼承的任何執行狀況檢查)

選項CMD有:

  • --interval=DURATION(預設30s:):間隔,頻率
  • --timeout=DURATION(預設30s:):超時時間
  • --start-period=DURATION(預設0s:):為需要時間引導的容器提供初始化時間, 在此期間探測失敗將不計入最大重試次數。
  • --retries=N(預設3:):重試次數

命令的exit status指示容器的執行狀況。可能的值為:

  • 0:健康狀態,容器健康且已準備完成。
  • 1:不健康狀態,容器工作不正常。
  • 2:保留,不要使用此退出程式碼。

範例:

FROM nginx
MAINTAINER Securitit
HEALTHCHECK --interval=5s --timeout=3s 
 CMD curl -f http://localhost/ || exit 1
CMD ["usr/sbin/nginx", "-g", "daemon off;"]

17)ONBUILD

  • ONBUILD 是一個特殊的指令,它後面跟的是其它指令,比如 RUN, COPY 等,而這些指令,在當前映象構建時並不會被執行。只有當以當前映象為基礎映象,去構建下一級映象的時候才會被執行。

語法格式:

ONBUILD <其它指令>

範例:

FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]

18)LABEL

  • LABEL 指令用來給映象新增一些後設資料(metadata),以鍵值對的形式。用來替代 MAINTAINER。

語法格式:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

範例:比如我們可以新增映象的作者

LABEL org.opencontainers.image.authors="runoob"

四、ARG 和 ENV 的區別

  • ARG 定義的變數只會存在於映象構建過程,啟動容器後並不保留這些變數
  • ENV 定義的變數在啟動容器後仍然保留

五、CMD,ENTRYPOINT,command,args 場景測試

當用戶同時在 kubernetes 中的 yaml 檔案中寫了command和args的時候,預設是會覆蓋DockerFile中的命令列和引數,完整的情況分類如下:

1)command 和 args 不存在場景測試

  • 如果 command 和 args 都沒有寫,那麼用DockerFile預設的設定。
Dockerfile
FROM centos
COPY test.sh /
RUN chmod +x /test.sh
### ENTRYPOINT將作為的子命令啟動/bin/sh -c,它不會傳遞引數,要傳遞引數只能這樣傳參
# ENTRYPOINT ["/bin/sh","-c","/test.sh ENTRYPOINT"]
ENTRYPOINT ["/test.sh","ENTRYPOINT"]
CMD ["CMD"]

/tmp/test.sh

#!/bin/bash
echo $*

構建

docker build -t test1:v1 -f Dockerfile .

yaml 編排

cat << EOF > test1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: test
spec:
 replicas: 1
 selector:
   matchLabels:
     app: test
 template:
   metadata:
     labels:
       app: test
   spec:
     nodeName: local-168-182-110
     containers:
     - name: test
       image: test:v1
       #command: ['/bin/sh','-c','/test.sh']
       #args: ['args']
EOF

執行

kubectl apply -f test.yaml

2)command 存在,但 args 存在場景測試

  • 如果 command 寫了,但 args 沒有寫,那麼 Docker 預設的設定會被忽略而且僅僅執行.yaml檔案的 command(不帶任何引數的)。
cat << EOF > test2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: test2
spec:
 replicas: 1
 selector:
   matchLabels:
     app: test2
 template:
   metadata:
     labels:
       app: test2
   spec:
     nodeName: local-168-182-110
     containers:
     - name: test2
       image: test:v1
       # ['/bin/sh','-c','/test.sh command','hello'],加了'/bin/sh','-c',也是不能外部傳參,不會輸出hello,只能通過這樣傳參,['/bin/sh','-c','/test.sh command'];CMD裡面的引數會被忽略
       command: ['/test.sh']
       # command帶引數
       # command: ['/test.sh','command']
       #args: ['args']
EOF

3)command 不存在,但 args 存在場景測試

  • 如果 command 沒寫,但 args 寫了,那麼 Docker 預設設定的 ENTRYPOINT 的命令列會被執行,但是呼叫的引數是.yaml中的 args,CMD 的引數會被覆蓋,但是 ENTRYPOINT 自帶的引數還是會執行的。
cat << EOF > test3.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: test3
spec:
 replicas: 1
 selector:
   matchLabels:
     app: test3
 template:
   metadata:
     labels:
       app: test3
   spec:
     nodeName: local-168-182-110
     containers:
     - name: test3
       image: test:v1
       # ['/bin/sh','-c','/test.sh command','hello'],加了'/bin/sh','-c',也是不能外部傳參,不會輸出hello,只能通過這樣傳參,['/bin/sh','-c','/test.sh command'];CMD裡面的引數會被忽略
       # command: ['/test.sh']
       # command帶引數
       # command: ['/test.sh','command']
       args: ['args']
EOF

4)command 和 args 都存在場景測試

  • 如果如果 command 和 args 都寫了,那麼 Docker 預設的設定被忽略,使用.yaml的設定。
cat << EOF > test4.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: test4
spec:
 replicas: 1
 selector:
   matchLabels:
     app: test4
 template:
   metadata:
     labels:
       app: test4
   spec:
     nodeName: local-168-182-110
     containers:
     - name: test4
       image: test:v1
       # ['/bin/sh','-c','/test.sh command','hello'],加了'/bin/sh','-c',也是不能外部傳參,不會輸出hello,只能通過這樣傳參,['/bin/sh','-c','/test.sh command'];CMD裡面的引數會被忽略
       # command: ['/test.sh']
       # command帶引數,command和args都會帶上
       command: ['/test.sh','command']
       args: ['args']
EOF

映象構建 Dockerfile 的介紹就到這裡了!

到此這篇關於Docker 映象構建保姆級入門實戰指南的文章就介紹到這了,更多相關Docker 映象構建內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com