Docker 映象構建之 Dockerfile

贾维斯Echo發表於2024-03-20

在 Docker 中構建映象最常用的方式,就是使用 Dockerfile。Dockerfile 是一個用來構建映象的文字檔案,文字內容包含了一條條構建映象所需的指令和說明。官方文件:https://docs.docker.com/engine/reference/builder/

目錄
  • 一、Dockerfile 基本介紹
    • 1.1 什麼是 Dockerfile
    • 1.2 Dockerfile 主體內容
    • 1.3 構建Dockerfile步驟
    • 1.4 理解構建上下文(Build Context)
    • 1.5 Build Cache
    • 1.6 Dockfile 檔案的注意事項
    • 1.7 dockerfile的保留字指令
  • 二、Dockerfile 的保留字指令詳解
    • 2.1 FROM
    • 2.2 MAINTAINER
    • 2.3 LABEL
    • 2.4 RUN
    • 2.5 EXPOSE
    • 2.6 WORKDIR
    • 2.7 ENV
    • 2.8 ADD
    • 2.9 COPY
    • 2.10 VOLUME
    • 2.11 CMD (這個指令需放在最後)
    • 2.12 ONBUILD
  • 三、構建映象
    • 3.1 構建映象
    • 3.2 .dockerignore 實踐
  • 四、Dockerfile 實踐
    • 4.1 使用 centos7 作為基礎映象部署 nginx 服務
    • 4.2 在容器中編譯安裝 nginx 服務
    • 4.3 構建以 Centos 為依賴映象並安裝 Django 的服務
    • 4.4 構建以 python 為依賴映象並安裝 Django 服務
    • 4.5 使用 NGINX 代理 Django
  • 五、Docker 映象管理
    • 5.1 docker save
    • 5.2 docker load
    • 5.3 docker tag
    • 5.4 docker push
    • 5.5 docker pull
  • 六、映象倉庫
    • 6.1 Docker hub
    • 6.2 建立私有映象倉庫
  • 七、檢視映象構建歷史
  • 八、多段構建 ( Multi-stage build)
  • 九、多程序的容器映象
    • 9.1 選擇適當的 init 程序
    • 9.2 Tini 開源專案
  • 十、Dockerfile 最佳實踐

一、Dockerfile 基本介紹

1.1 什麼是 Dockerfile

  • Dockerfile 是用來構建 Docker 映象的構建檔案, 是由一系列的命令和引數構成的指令碼
  • 透過指令的方式構建映象

1.2 Dockerfile 主體內容

Dockerfile 主體內容分為四部分:基礎映象資訊、 維護者資訊、 映象操作指令和容器啟動時執行指令。

1.3 構建Dockerfile步驟

  1. 編寫 Dockerfile 檔案
  2. docker build 構建映象
  3. docker run 建立容器

1.4 理解構建上下文(Build Context)

  1. 當執行 docker build 命令時,當前工作目錄被稱為構建上下文。

  2. docker build 預設查詢當前目錄的 Dockerfile 作為構建輸入,也可以透過 -f 指定 Dockerfile

docker build -f ./ Dockerfile
  1. docker build 執行時,首先會把構建上下文傳輸給 docker daemon,把沒用的檔案包含在構建上下文時,會導致傳輸時間長,構建需要的資源多,構建出的映象大等問題。這種情況可以透過.dockerignore檔案從編譯上下文排除某些檔案。
  2. 因此需要確保構建上下文清晰,比如建立一個專門的目錄放置 Dockerfile,並在目錄中執行 docker build。

1.5 Build Cache

構建容器映象時,Docker 依次讀取 Dockerfile 中的指令,並按順序依次執行構建指令。
Docker 讀取指令後,會先判斷快取中是否有可用的已存映象,只有已存映象不存在時才會重新構建。

  • 通常 Docker 簡單判斷 Dockerfile 中的指令與映象。
  • 針對 ADDCOPY 指令,Docker 判斷該映象層每一個檔案的內容並生成一個 checksum,與現存映象比較時,Docker 比較的是二者的 checksum。
  • 其他指令,比如 RUN apt-get -y update,Docker 簡單比較與現存映象中的指令字串是否一致。
  • 當某一層 cache 失效以後,所有所有層級的 cache 均一併失效,後續指令都重新構建映象。

1.6 Dockfile 檔案的注意事項

  • Docker 以從上到下的順序執行 Dockerfile 的指令。為了指定基本映像,第一條指令必須是 FROM。
  • 每條保留字指令都必須是大寫字母, 並且後面要跟隨至少一個引數
  • 指令按照從上到下的順序執行
  • 每條指令可用 # 新增註釋
  • 每條指令都會建立一個新映象層, 並對映象進行提交

1.7 dockerfile的保留字指令

  • 主要保留指令:
    1. FROM
    2. RUN
    3. ADD
    4. COPY
    5. WORKDIR
    6. CMD

一般用以上保留字指令就可以完成容器想要的功能,所有欄位如下。

指令 含義
FROM 指定基礎映象,必須為第一個命令
MAINTAINER 維護者資訊
RUN 構建映象docker build時執行的命令
ADD 將本地檔案新增到容器中,tar 型別檔案會自動解壓(網路壓縮資源不會被解壓)
COPY 功能類似ADD,但是是不會自動解壓檔案,也不能訪問網路資源
CMD 在docker run時會執行的命令,如果存在多個則僅最後一個生效。
LABEL 用於為映象新增後設資料
ENV 設定環境變數
EXPOSE 指定於外界互動的埠
VOLUME 用於指定持久化目錄
WORKDIR 工作目錄,類似於cd命令
ARG 用於指定傳遞給構建執行時的變數
ONBUILD 用於設定映象觸發器

二、Dockerfile 的保留字指令詳解

2.1 FROM

  • 基礎(依賴)映象, 就是當前要建立的映象是基於那個映象
  • 基本語法如下:
FROM <image> 
FROM <image>:<tag> 
FROM <image>@<digest> 
示例: 
	FROM mysql:5.6 
# 注: tag 或 digest 是可選的,如果不使用這兩個值時,會使用 latest 版本的基礎映象

如果不以任何映象為基礎,那麼寫法為:FROM scratch。官方說明:scratch 映象是一個空映象,可以用於構建 busybox 等超小映象,可以說是真正的從零開始構建屬於自己的映象。

2.2 MAINTAINER

  • 指明映象維護者及其聯絡方式(一般是郵箱地址)。官方說明已過時,推薦使用 LABEL。
格式:
	MAINTAINER <name>
示例:
    MAINTAINER Jasper Xu MAINTAINER sorex@163.com
    MAINTAINER Jasper Xu <sorex@163.com>

2.3 LABEL

  • 語法:LABEL <key>=<value> <key>=<value> <key>=<value> ...
  • 功能是為映象指定標籤或為映象新增後設資料。也可以使用 LABEL 來指定映象作者。
#示例:後設資料
LABEL version="1.0" description="這是一個 Web 伺服器" by="IT 筆錄" 

# 注:使用 "LABEL" 指定後設資料時,一條 "LABEL" 指定可以指定一或多條後設資料,指定多條後設資料時不同後設資料之間透過空格分隔。推薦將所有的後設資料透過一條 "LABEL" 指令指定,以免生成過多的中間映象LABEL 
示例二 
maintainer="http://blog.taoxiaoxin.club/"

2.4 RUN

  • 容器構建時需要執行的命令
# RUN 用於在映象容器中執行命令,一個Dockerfile檔案內可以有多個RUN其有以下兩種命令執行方式: 
shell 執行 
	格式:RUN <command> 
exec 執行 
	格式:RUN ["executable", "param1", "param2"] 
	示例:
		 RUN ["executable", "param1", "param2"] 
		 RUN apk update RUN ["/etc/execfile", "arg1", "arg1"] 
注: RUN 指令建立的中間映象會被快取,並會在下次構建中使用。如果不想使用這些快取映象,可以在構建 時指定--no-cache 引數,如:docker build --no-cache

2.5 EXPOSE

  • 暴露容器執行時的監聽埠給外部,可以指定埠是監聽 TCP 還是 UDP,如果未指定協議,則預設為 TCP。
# 格式:
EXPOSE <port> [<port>...]
# 示例:
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp

# 注:"EXPOSE" 並不會讓容器的埠訪問到主機。如果想使得容器與宿主機的埠有對映關係,要使其可訪問,需要在" docker run" 執行容器時透過"-p" 來發布這些埠,或透過"-P" 引數來發布" EXPOSE" 匯出的所有埠,

2.6 WORKDIR

  • 指定建立容器後, 終端預設處在的工作目錄, 也就是落腳點,為 RUN、CMD、ENTRYPOINT 以及 COPY 和 AND 設定工作目錄。
# 格式:
WORKDIR /path/to/workdir
# 示例:
WORKDIR /a (這時工作目錄為/a)
WORKDIR b (這時工作目錄為/a/b)
WORKDIR c (這時工作目錄為/a/b/c)

# 注:透過 "WORKDIR" 設定工作目錄後,"Dockerfile" 中其後的命令 RUN、CMD、ENTRYPOINT、ADD、COPY等命令都會在該目錄下執行。在使用 "docker run" 執行容器時,可以透過"-w" 引數覆蓋構建時所設定的工作目錄

2.7 ENV

  • 用來在構建映象過程中設定環境變數
# 格式:
ENV <key> <value> 
#<key>之後的所有內容均會被視為其<value>的組成部分,因此,一次只能設定一個變數
ENV <key>=<value> ... 
#可以設定多個變數,每個變數為一個"<key>=<value>"的鍵值對,如果<key>中包含空格,可以使用\來進行轉義,也可以透過""來進行標示;另外,反斜線也可以用於續行
# 示例:
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat=fluffy

2.8 ADD

  • 將宿主機目錄下的檔案複製到映象裡面 (會自動解壓 tar 壓縮包),src 可以是一個本地檔案或者是一個本地壓縮檔案,壓縮檔案會自動解壓。還可以是一個 url,如果把 src 寫成一個 url,那麼 ADD 就類似於 wget 命令,然後自動下載和解壓。
# 格式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]  # 用於支援包含空格的路徑
# 示例:
ADD hom* /mydir/            # 新增所有以"hom"開頭的檔案
ADD hom?.txt /mydir/        # ? 替代一個單字元,例如:"home.txt"
ADD test relativeDir/       # 新增 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/      # 新增 "test" 到 /absoluteDir/

2.9 COPY

  • 類似 ADD, 複製本地檔案到映象中 (不會自動解壓)
指令:COPY
功能描述:複製檔案到映象中。
語法:COPY < src>… < dest>|[“< src>”,… “< dest>”]
提示:指令邏輯和 ADD 十分相似,同樣 Docker Daemon 會從編譯目錄尋找檔案或目錄,dest 為映象中的絕對路徑或者相對於 WORKDIR 的路徑。

2.10 VOLUME

  • 用於目錄掛載
# 格式:
VOLUME ["/path/to/dir"]
# 示例:
VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]

# 注:一個卷可以存在於一個或多個容器的指定目錄,該目錄可以繞過聯合檔案系統,並具有以下功能:
1. 卷可以容器間共享和重用
2. 容器並不一定要和其它容器共享卷
3. 修改卷後會立即生效
4. 對卷的修改不會對映象產生影響
5. 卷會一直存在,直到沒有任何容器在使用它

2.11 CMD (這個指令需放在最後)

  • 指定容器啟動時要執行的命令
# 格式:
CMD ["executable","param1","param2"] (執行可執行檔案,優先)
CMD ["param1","param2"] (設定了 ENTRYPOINT,則直接呼叫 ENTRYPOINT 新增引數)
CMD command param1 param2 (執行 shell 內部命令)
# 示例:
CMD echo "This is a test." | wc -w
CMD ["/usr/bin/wc","--help"]

# 注: "CMD" 不同於 "RUN","CMD" 用於指定在容器啟動時所要執行的命令,而 "RUN" 用於指定映象構建時所要執行的命令

2.12 ONBUILD

  • 用於設定映象觸發器
# 格式:
ONBUILD [INSTRUCTION]
# 示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

# 注:當所構建的映象被用做其它映象的基礎映象,該映象中的觸發器將會被觸發

三、構建映象

3.1 構建映象

Dockerfile 檔案編寫好以後,真正構建映象時需要透過 docker build 命令。

docker build 命令用於使用 Dockerfile 建立映象。

# 使用當前目錄的 Dockerfile 建立映象
docker build -t mycentos:7 .
# 透過 -f Dockerfile 檔案的位置建立映象
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 .
  • -f:指定要使用的 Dockerfile 路徑;
  • --tag, -t:映象的名字及標籤,可以在一次構建中為一個映象設定多個標籤。

3.2 .dockerignore 實踐

docker build 執行時,首先會把構建上下文傳輸給 docker daemon,把沒用的檔案包含在構建上下文時,會導致傳輸時間長,構建需要的資源多,構建出的映象大等問題。這種情況可以透過.dockerignore檔案從編譯上下文排除某些檔案。

舉個例子,下面我們試著到一個包含檔案很多的目錄執行下面的命令,會感受到差異。

首先建立一個名為 my_project 的目錄,並在其中新增一些無用的檔案和子目錄:

mkdir my_project
cd my_project

# 建立無用檔案
touch file1.txt
touch file2.txt
touch file3.txt

# 建立無用子目錄,並在其中新增檔案
mkdir dir1
mkdir dir2
touch dir1/file4.txt
touch dir2/file5.txt

接下來,在 my_project 目錄中建立一個 Dockerfile,內容如下:

FROM alpine:3.14.2

WORKDIR /app

COPY . .

CMD ["ls", "-l"]

接著,在當前目錄下的 my_project 目錄透過 -f 引數可以告訴 Docker 使用指定路徑下的 Dockerfile 檔案進行構建

docker build -f ./Dockerfile .

可以看到,構建當前映象是非常快的,當前目錄檔案檔案內容很小,因此構建過程比較快。

但是當我們直接切換到根目錄,接下來我們來試試看。

docker build ~/workspace/my_project/ 

image-20240320222503890

你可以直觀的感受到這個過程是很慢的,這是因為根目錄作為構建上下文傳輸給 Docker daemon,包括所有的無用檔案和子目錄。如果目錄中的檔案和子目錄很多,這個過程會變得非常耗時,尤其是在網路速度較慢的情況下。所以一般打包docker映象請切換到dockerfile當前目錄下

當然,有時候我們為了讓打包的容器映象更加輕量一點,我們可以建立一個 .dockerignore 檔案,告訴 Docker 在構建時忽略某些檔案或目錄。

my_project 目錄中建立一個名為 .dockerignore 的檔案,並新增以下內容:

file*.txt
dir*/

這樣,我們告訴 Docker 在構建時忽略所有以 file 開頭的 .txt 檔案和所有的 dir 開頭的子目錄。然後重新執行構建命令:

docker build -t my_image .

這次構建過程將忽略無用檔案和子目錄,從而加快構建時間並減小生成的映象大小。

四、Dockerfile 實踐

4.1 使用 centos7 作為基礎映象部署 nginx 服務

  • 先建立一個 nginx.repo 檔案
cat > nginx.repo <<EOF
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF
  • 編寫 Dockerfile 檔案
cat > Dockerfile <<EOF
# 指定基礎映象(依賴映象)
FROM centos:7

# 執行一個命令
RUN yum install -y yum-utils

# 將本地檔案新增到容器中
ADD nginx.repo /etc/yum.repos.d/nginx.repo

# 更新YUM快取
RUN yum makecache

# 安裝nginx
RUN yum install -y nginx

# 制定容器啟動預設執行的命令
CMD nginx -g 'daemon off;'
EOF
  • 構建映象
docker build -t install/nginx:v1 .
  • 檢視剛剛構建的映象, 然後例項容器
[root@shawn ~]# docker images
[root@shawn ~]# docker run -dit install/nginx:v1 sh
  • 檢視剛剛例項出的容器, 並進入到容器中
[root@shawn ~]# docker exec -it 94f8e35f3357  bash
  • 檢測 nginx 是否部署成功
[root@shawn ~]# crul 127.0.0.1  # 出現 html 程式碼說明部署成功

4.2 在容器中編譯安裝 nginx 服務

  • 編輯 Dockerfile 檔案
[root@shawn ~]# vim Dockerfile
'''檔案內容
# 指定基礎映象(依賴映象)
FROM centos:7

# 執行命令
RUN yum install yum-utils wget zlib zlib-devel pcre pcre-devel make gcc gcc-c++
RUN cd /opt && wget http://nginx.org/download/nginx-1.18.0.tar.gz && tar -xvf nginx.1.18.0/ && cd nginx-1.18.0/ && ./configure && make && make install

# 指定進入容器的預設工作目錄
WORKDIR /usr/local/nginx/sbin

# 指定容器啟動預設執行的命令
CMD ./nginx -g 'daemon off;'
'''
  • 構建映象
[root@shawn ~]# docker build -t yuan/install/nginx:v2 .
  • 檢視是否構建成功,並例項出容器
[root@shawn ~]# docker images
[root@shawn ~]# docker run -dit --name yuan_nginx yuan/install/nginx:v2 sh
  • 檢視容器是否啟動成功, 並測試 nginx
[root@shawn ~]# docker exec yuan_nginx crul 127.0.0.1  # 出現 html 程式碼說明部署成功

4.3 構建以 Centos 為依賴映象並安裝 Django 的服務

  • 首先構建一個Dockerfile檔案
[root@shawn ~]#vim Dockerfile
# 指定基礎映象
FROM centos:7
# 執行命令
RUN yum makecache && yum update -y && yum install -y python3 && pip3 install django
# 複製本地檔案到容器
COPY shawn /root/
# 指定進入到容器的工作目錄
WORKDIR /root/
# 指定向外暴露的埠
EXPOSE 8080
# 執行命令
CMD cd ./shawn && python3 manage.py runserver 0.0.0.0:8080
  • 檔案 shawn 的構建
在宿主機上安裝 Django
django-admin startproject shawn  #建立一個 "Shawn" 專案
cd ./shawn  #進入目錄
django-admin startapp application  #開始專案
cd ./shawn
vim setting.cong  #修改配置檔案"*"代理
cd .. #退出
  • 構建映象
[root@shawn ~]#docker build -t test333:v1 .
  • 檢視並使用映象例項化出容器
[root@shawn ~]#docker images
[root@shawn ~]#docker run -dit --name test001 -p 9999:8080 test333:v1 sh
  • 檢視剛開啟的容器,並進入容器啟動 Django 服務
[root@shawn ~]#docker exec -it test001 bash
[root@80f1315c030c ~]# python3 manage.py runserver 0.0.0.0:8080
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
December 04, 2020 - 06:50:19
Django version 3.1.4, using settings 'lingxiu.settings'
Starting development server at http://0.0.0.0:8080/
Quit the server with CONTROL-C.
  • 使用瀏覽器驗證一下

image-20201204180450328

4.4 構建以 python 為依賴映象並安裝 Django 服務

  • 編輯 Dockerfile 檔案
[root@shawn ~]# vim Dockerfile
'''檔案內容
# 指定依賴映象
FROM python:3.6

# 設定作者
MAINTAINER Shawn

# 執行命令
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip3 install django==2.2.2

# 複製檔案
COPY app /root/

# 設定工作目錄
WORKDIR /root/

# 執行命令
CMD cd ./app && python3 manage.py runserver 0.0.0.0:7777
  • 檔案 app 的構建
在宿主機上安裝 Django
django-admin startproject app  #建立一個 "app" 專案
cd ./app  #進入目錄
django-admin startapp application  #開始專案
cd ./app
vim setting.cong  #修改配置檔案"*"代理
cd .. #退出
  • 構建映象
[root@shawn ~]#docker build -t jjjj .
  • 檢視並使用映象例項化出容器
[root@shawn ~]#docker images
[root@shawn ~]#docker run -dit --name jjjjtest -p 4444:7777 jjjj:latest sh
  • 檢視剛開啟的容器,並進入容器啟動 Django 服務
[root@shawn ~]#docker exec -it jjjtest bash
root@b85f93fcc114:~# python3 manage.py runserver 0.0.0.0:7777
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
December 04, 2020 - 10:17:51
Django version 2.2.2, using settings 'app.settings'
Starting development server at http://0.0.0.0:7777/
Quit the server with CONTROL-C.
  • 使用瀏覽器檢驗一下

4.5 使用 NGINX 代理 Django

  • 先構建一個 Django 服務, 步驟與上一個例子相同
  • 改變了一下向外暴露的埠
🎅編寫 "Dockerfile" 檔案
[root@shawn DjangoDocker]#vim Dockerfile 
'''檔案內容
# 指定依賴映象
FROM pyhton:3.6

# 安裝 Django
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip3 install django==2.2.2

# COPY 檔案
COPY app /root/

# 指定工作目錄
WORKDIR /root/

# 執行命令
CMD cd ./app && python3 manage.py runserver 0.0.0.0:8080
'''
[root@shawn DjangoDocker]#ls
app  Dockerfile  # 這兩個檔案, "app" 在上一個例子中有構建

🎅構建映象,並檢視
[root@shawn DjangoDocker]#docker build -t python_django:v6 .
[root@shawn DjangoDocker]#docker images

🎅例項出容器,並檢視
[root@shawn DjangoDocker]#docker run -dit --name p_d_test1 -p 8888:8080 python_django:v6 sh
6906ff9e3ec0f9d583eb27890d82c79deff4358a43e5f1ec768a702547d020bf
[root@shawn DjangoDocker]#docker ps

🎅進到容器裡面,開啟服務,再測試
[root@shawn DjangoDocker]#docker exec -it p_d_test1 bash
root@6906ff9e3ec0:~# python3 manage.py runserver 0.0.0.0:8080
[root@shawn DjangoDocker]#curl 127.0.0.1:8888
  • 然後來編寫 nginx 服務以及代理配置
🎅編寫 "nginx.repo" 檔案
[root@shawn NginxDocker]#vim nginx.repo
'''檔案內容(官網可複製)
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
'''

🎅編寫 "default.conf" 檔案(代理"Django"配置)
[root@shawn NginxDocker]#vim default.conf
'''檔案內容
server {
        listen 80;
        server_name www.py16zxl.com;

        location / {
        # 這裡填的是 Django 服務的訪問地址與埠(對映埠)
        proxy_pass http://192.168.13.234:8888/;
        index index.html index.htm index.jsp;
        }
}
'''

🎅編寫 "Dockerfile" 檔案
[root@shawn NginxDocker]#vim Dockerfile
'''檔案內容
# 指定依賴進行
FROM centos:7

# 指定作者
MAINTAINER shawn

# 安裝依賴
RUN yum install -y yum-utils gcc gcc-c++ pcre pcre-devel zlib zlib-devel make wget

## 原始碼安裝 nginx1.18.0
# RUN wget http://nginx.org/download/nginx-1.18.0.tar.gz && tar -xvf nginx-1.18.0.tar.gz && cd nginx.1.18.0 && ./configure --prefix="/usr/local/nginx-1.18.0" && make && make install

# 複製 NGINX 配置檔案
COPY nginx.repo /etc/yum.repos.d/

# 更新 yum 軟體包索引
RUN yum makecache fast

# yum 安裝 nginx
RUN yum install -y nginx

# 指定向外暴露的埠
EXPOSE 8000

# 複製 nginx 預設配置檔案
COPY default.conf /etc/nginx/conf.d/

# 容器起來執行的命令
CMD /usr/local/nginx-1.18.0/sbin/nginx -g 'daemon off;' 
'''
# 當前需要的檔案
[root@shawn NginxDocker]#ls
default.conf  Dockerfile  nginx.repo

# 開始構建映象,並檢視
[root@shawn NginxDocker]#docker build -t nginx_d:v7 .
[root@shawn NginxDocker]#docker images

# 例項化出容器,並檢視
[root@shawn NginxDocker]#docker run -dit --name nginx_d -p 80:80 nginx_d:v7 sh
[root@shawn NginxDocker]#docker ps

# 進入容器,開啟 "nginx" 服務,並驗證
[root@shawn NginxDocker]#docker exec -it nginx_d bash
[root@51f54c1d5abb /]#nginx
[root@shawn NginxDocker]#curl 127.0.0.1:80
# 發現透過訪問 nginx 也可以進入 Django 頁面

五、Docker 映象管理

5.1 docker save

docker save 命令用於將一個或多個映象儲存到歸檔檔案中。這個命令常用於將映象遷移到其他地方,或者在沒有 Docker registry 的環境中分享映象。

5.2 docker load

docker load 命令用於從一個歸檔檔案中載入映象。

舉例:

docker load -i ubuntu.tar

這個例子將從當前目錄下的 ubuntu.tar 檔案中載入映象。

5.3 docker tag

docker tag 命令用於為本地映象建立一個新的標籤。這個命令常用於準備將映象推送到 Docker registry 之前,為映象設定一個合適的版本號或者名稱空間。

舉個例子:

docker tag ubuntu:latest myusername/ubuntu:latest

這個例子將 ubuntu:latest 映象打上一個新的標籤 myusername/ubuntu:latest。

5.4 docker push

docker push 命令用於將本地的映象上傳到 Docker registry。
例子:

docker push myusername/ubuntu:latest

這個例子將 myusername/ubuntu:latest 映象推送到 Docker Hub 中的 myusername 名稱空間下。

5.5 docker pull

docker pull 命令用於從 Docker registry 拉取或者更新指定的映象。
例子:

docker pull ubuntu:latest

這個例子將從 Docker Hub 拉取最新的 Ubuntu 映象到本地。
使用這些命令時,請確保您已經登入到 Docker registry(使用 docker login 命令),並且有足夠的許可權進行 pushpull操作。

六、映象倉庫

6.1 Docker hub

  • https://hub.docker.com/

6.2 建立私有映象倉庫

sudo docker run -d -p 5000:5000 registry

七、檢視映象構建歷史

docker history 映象名稱:標籤|ID
docker history mycentos:7

八、多段構建 ( Multi-stage build)

  • 有效減少映象層級的方式
# 使用 golang:1.16-alpine 作為基礎映象
FROM golang:1.16-alpine AS build

# 安裝git,用於獲取依賴
RUN apk add --no-cache git

# 安裝dep工具,用於依賴管理
RUN go get github.com/golang/dep/cmd/dep

# 將專案依賴的Gopkg.lock和Gopkg.toml複製到容器中
COPY Gopkg.lock Gopkg.toml /go/src/project/

# 設定工作目錄
WORKDIR /go/src/project/

# 使用dep安裝專案依賴,僅使用-vendor-only模式
RUN dep ensure -vendor-only

# 複製整個專案到容器中
COPY . /go/src/project/

# 構建專案,輸出可執行檔案到/bin/project
RUN go build -o /bin/project
# 使用scratch作為基礎映象,減小映象體積
FROM scratch

# 將可執行檔案從第一個階段的構建中複製到scratch映象中
COPY --from=build /bin/project /bin/project

# 定義容器啟動時的入口點
ENTRYPOINT ["/bin/project"]

九、多程序的容器映象

9.1 選擇適當的 init 程序

  • 需要捕獲 SIGTERM 訊號並完成子程序的優雅終止
  • 負責清理退出的子程序以避免殭屍程序

9.2 Tini 開源專案

Tini 是一個用於容器的微小但有效的 init。它的作用是生成一個單一的子程序(通常用於容器),並等待它退出,同時清理殭屍程序並進行訊號轉發。使用 Tini 有幾個好處:防止軟體意外建立殭屍程序,保證預設訊號處理程式正常工作,並且可以完全透明地使用。在 Docker 中使用 Tini 時,如果版本是1.13或更高,則無需額外安裝,只需透過 docker run 命令加上 --init 引數即可。另外,還可以使用預構建的 Docker 映象或按照指南在其他平臺上安裝 Tini

開源專案地址:https://github.com/krallin/tini

十、Dockerfile 最佳實踐

  • 不要安裝安裝無效軟體包,適當使用 .dockerignore 檔案忽略不需要包含在映象中的檔案和目錄。

  • 應簡化映象中同時執行的程序數。理想狀況下,每個映象應該只有一個程序。當無法避免同一映象執行多程序時,應選擇合理的初始化程序 (init process)。

  • 最小化層級數。

    1. 最新的 Docker 只有 RUNCOPYADD 建立新層,其他指令建立臨時層,不會增加映象大小。比如 EXPOSE 指令就不會生成新層。
    2. 多條 RUN 命令可透過連線符連線成一條指令集以減少層數。
    3. 透過多段構建減少映象層數。
  • 把多行引數按字母排序,可以減少可能出現的重複引數,並且提高可讀性。

  • 編寫 Dockerfile 的時候,應該把變更頻率低的編譯指令優先構建以便放在映象底層以有效利用構建快取。

  • 複製檔案時,每個檔案應獨立複製,這確保某個檔案變更時,隻影響該檔案對應的快取。

終極目標:易管理、少漏洞、映象小、層級少、利用快取

相關文章