阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈

在這篇文章中,我們將討論更進階的Dockerfile指令。這些指令可用於建立更進階的 Docker 映像。

例如,我們可以使用VOLUME指令將主機的檔案系統綁定到 Docker 容器。這將使我們能夠將 Docker 容器產生和使用的資料保存到本機。

我們將在這篇文章中介紹以下指令:

  1. ENV指令

  2. ARG指令

  3. WORKDIR指令

  4. COPY指令

  5. ADD指令

  6. USER指令

  7. VOLUME指令

  8. EXPOSE指令

  9. HEALTHCHECK指令

  10. ONBUILD指令

ENV 指令

Dockerfile中的ENV指令可用來設定環境變數。環境變數用於設定環境變數。環境變數是鍵值對,為容器內執行的應用程式和進程提供資訊。它們可以透過在執行時提供動態值來影響程式和腳本的行為。

環境變數依照以下格式定義為鍵值對:

ENV <key> <value>

例如,我們可以使用ENV指令來設定路徑,如下所示:

ENV PATH $PATH:/usr/local/app/bin/

我們可以在同一行中設定多個環境變數,並用空格分隔。然而,在這種形式中,應該用等於 ( = ) 符號分隔:

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

下面,我們設定兩個配置好的環境變數。 PATH環境變數的值配置為$PATH:/usr/local/app/binVERSION環境變數的值配置為1.0.0

ENV PATH=$PATH:/usr/local/app/bin/ VERSION=1.0.0

一旦在Dockerfile中使用ENV指令設定了環境變數,則該變數將在所有後續 Docker 映像層中可用。該變數甚至可以在從此 Docker 映像啟動的 Docker 容器中使用。

ARG 指令

Dockerfile 中的ARG指令用於定義使用者可以在建置時使用docker build指令傳遞給建構器的變數。這些變數的行為與環境變數類似,可以在整個 Dockerfile 中使用,但不會保留在最終映像中,除非使用ENV指令明確宣告。

ARG指令有以下格式:

ARG <varname>

我們也可以加入多個ARG指令,如下:

ARG USER
ARG VERSION

這些參數還可以具有在 Dockerfile 本身中指定的可選預設值。如果使用者在建置過程中沒有提供任何值,Docker 將使用ARG指令中定義的預設值:

ARG USER=TestUser
ARG VERSION=1.0.0

ENV變數不同, ARG變數無法從正在執行的容器存取。它們僅在建置過程中可用。

在 Dockerfile 中使用 ENV 和 ARG 指令

我們將建立一個使用 ubuntu 作為父映像的Dockerfile ,但我們將能夠在建置時變更 ubuntu 版本。我們還將指定環境名稱和應用程式目錄作為 Docker 映像的環境變數。

使用mkdir指令建立一個名為env-arg-example的新目錄:

mkdir env-arg-example

使用cd指令導航新建立的env-arg-example目錄:

cd env-arg-example

現在,讓我們建立一個新的 Dockerfile。我將使用 VS Code,但您可以隨意使用任何您覺得舒適的編輯器:

code Dockerfile

將以下內容新增至Dockerfile 。然後儲存並退出:

ARG TAG=latest
FROM ubuntu:$TAG
LABEL [email protected]
ENV ENVIRONMENT=dev APP_DIR=/usr/local/app/bin
CMD ["env"]

Dockerfile先定義一個參數TAG ,預設值為latest 。然後,它使用此參數在FROM指令中指定基本映像,從而選擇標記為latest Ubuntu 映像。

LABEL指令將元資料加入影像中,指示維護者的電子郵件地址。接下來, ENV指令設定兩個環境變數:值為devENVIRONMENT和指向/usr/local/app/bin APP_DIR 。在容器內執行的應用程式可以使用這些變數來根據環境和目錄路徑調整行為。

最後, CMD指令指定從映像啟動容器時要執行的命令,在本例中,它執行env來顯示容器內設定的所有環境變數。


現在讓我們建立 Docker 映像:

docker image build -t env-arg --build-arg TAG=23.10 .

輸出應類似以下內容:

[+] Building 34.9s (6/6) FINISHED                                                                                                            docker:default
 => [internal] load .dockerignore                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                        0.0s
 => [internal] load build definition from Dockerfile                                                                                                   0.0s
 => => transferring dockerfile: 189B                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/ubuntu:23.10                                                                                        3.3s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                                                          0.0s
 => [1/1] FROM docker.io/library/ubuntu:23.10@sha256:fd7fe639db24c4e005643921beea92bc449aac4f4d40d60cd9ad9ab6456aec01                                 31.6s
 => => resolve docker.io/library/ubuntu:23.10@sha256:fd7fe639db24c4e005643921beea92bc449aac4f4d40d60cd9ad9ab6456aec01                                  0.0s
 => => sha256:fd7fe639db24c4e005643921beea92bc449aac4f4d40d60cd9ad9ab6456aec01 1.13kB / 1.13kB                                                         0.0s
 => => sha256:c57e8a329cd805f341ed7ee7fcc010761b29b9b8771b02a4f74fc794f1d7eac5 424B / 424B                                                             0.0s
 => => sha256:77081d4f1e7217ffd2b55df73979d33fd493ad941b3c1f67f1e2364b9ee7672f 2.30kB / 2.30kB                                                         0.0s
 => => sha256:cd0bff360addc3363f9442a3e0b72ff44a74ccc0120d0fc49dfe793035242642 27.23MB / 27.23MB                                                      30.3s
 => => extracting sha256:cd0bff360addc3363f9442a3e0b72ff44a74ccc0120d0fc49dfe793035242642                                                              1.1s
 => exporting to image                                                                                                                                 0.0s
 => => exporting layers                                                                                                                                0.0s
 => => writing image sha256:86b2f4c440c71f37c3f29f5dd5fe79beac30f5a6ce878bd14dc17f439bd2377d                                                           0.0s
 => => naming to docker.io/library/env-arg                                                                                                             0.0s

現在,執行docker container run指令,從我們在上一個步驟中建置的 Docker 映像啟動一個新容器:

docker container run env-arg

輸出應該類似以下內容:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d6020a144f39
ENVIRONMENT=dev
APP_DIR=/usr/local/app/bin
HOME=/root

WORKDIR 指令

Dockerfile 中的WORKDIR指令用於為 Dockerfile 中後續的任何指令設定目前工作目錄。此指令有助於定義ADDCMDCOPYENTRYPOINTRUN等指令在容器內的執行位置。

WORKDIR指令具有以下格式:

WORKDIR /path/to/workdir

如果映像中不存在指定的目錄,Docker 將在建置過程中建立它。此外, WORKDIR指令有效地結合了類別 Unix 系統中mkdircd指令的功能。如果目錄不存在,它將建立該目錄並將目前目錄變更為指定路徑。

我們可以在一個 Dockerfile 中包含多個WORKDIR指令。如果後續WORKDIR指令使用相對路徑,它們將相對於最後一個WORKDIR設定。

例如:

WORKDIR /one
WORKDIR two
WORKDIR three
WORKDIR drink

WORKDIR /one將設定/one作為初始工作目錄。然後WORKINGDIR two會將目錄更改為/one/twoWORKDIR three進一步將其更改為one/two/three 。最後WORKDIR drink會將其更改為最終形式one/two/three/drink

複製指令

建置 Docker 映像時,通常會將本機開發環境中的檔案包含到映像本身中。這些檔案的範圍可以從應用程式原始碼到設定檔以及應用程式在容器內正常運作所需的其他資源。 Dockerfile 中的COPY指令可以實現此目的,它允許我們指定本機檔案系統中的哪些檔案或目錄應複製到正在建置的映像中。

COPY命令的語法如下所示:

COPY <source> <destination>

<source>指定本機檔案系統上相對於建置上下文的檔案或目錄的路徑。 <destination>指定在 Docker 映像檔系統中應將檔案或目錄複製到的路徑。

在以下範例中,我們使用COPY指令將index.html檔案從本機檔案系統複製到 Docker 映像的/var/www/html/目錄:

COPY index.html /var/www/html/index.html

我們也可以使用通配符來複製與給定模式相符的所有檔案。下面,我們將目前目錄中所有副檔名為.html檔案複製到 Docker 映像的/var/www/html/目錄:

COPY *.html /var/www/html/

當在 Dockerfile 中使用COPY指令將檔案從本機檔案系統傳輸到 Docker 映像時,我們也可以指定--chown標誌。該標誌允許我們設定 Docker 映像中複製檔案的使用者和群組所有權。

COPY --chown=myuser:mygroup *.html /var/www/html/

在此範例中, --chown=myuser:mygroup指定從本機目錄複製到 Docker 映像中的/var/www/html/所有.html檔案應歸myuser (使用者)和mygroup (群組)所有。

ADD指令

Dockerfiles 中的ADD指令的功能與COPY指令類似,但具有附加功能。

ADD <source> <destination>

<source>指定本機檔案系統上的檔案或目錄的路徑或URL或遠端 URL。 <destination>再次指定在 Docker 映像檔系統中應將檔案或目錄複製到的路徑。

在下面的範例中,我們將使用ADD從本機檔案系統複製檔案:

ADD index.html /var/www/html/index.html

在此範例中,Docker 會將index.html檔案從本機檔案系統(相對於 Docker 建置上下文)複製到 Docker 映像內的/var/www/html/index.html

在下面的範例中,我們將使用ADD從遠端 URL 複製檔案:

ADD http://example.com/test-data.csv /tmp/test-data.csv

COPY不同, ADD指令允許指定 URL(在本例中http://example.com/test-data.csv )作為<source>參數。 Docker 將從 URL 下載檔案並將其複製到 Docker 映像內的/tmp/test-data.csv

ADD指令不僅從本機檔案系統複製檔案或從 URL 下載文件,還包括從某些類型的壓縮檔案中自動提取的功能。當<source>是壓縮檔案(例如.tar.tar.gz.tgz.bz2.tbz2.txz.zip )時,Docker 會自動將其內容提取到 Docker 映像中的<destination>中檔案系統。

例如:

ADD myapp.tar.gz /opt/myapp/

在上面的範例中, myapp.tar.gz是一個壓縮存檔文件,Docker 會自動將myapp.tar.gz的內容提取到 Docker 映像內的/opt/myapp/中。

最佳實務:在 Dockerfile 中複製與新增

編寫 Dockerfile 時,在COPYADD指令之間進行選擇對於保持映像建置過程中的清晰度、安全性和可靠性至關重要。

清晰度和意圖

COPY很簡單,明確指出本機檔案系統中的檔案或目錄正在複製到 Docker 映像中。這種清晰度有助於理解 Dockerfile 的用途,並且隨著時間的推移更容易維護。

另一方面, ADD引入了額外的功能,例如從 URL 下載檔案和自動提取壓縮檔案。雖然這些功能在某些場景下可能很方便,但它們也可能掩蓋了簡單複製檔案的初衷。如果不仔細管理,缺乏透明度可能會導致意外行為或安全風險。

安全性和可預測性

使用COPY可避免與從任意 URL 下載檔案相關的潛在風險,從而增強安全性。 Docker 映像應使用受控、經過驗證的來源來建置,以防止包含意外或惡意內容。將檔案下載與建置流程分開並使用COPY可確保 Docker 建置環境保持安全且可預測。

Docker 哲學的一致性

Docker 鼓勵建立輕量級、高效且可預測的容器化應用程式。 COPY透過促進簡單性並降低鏡像建造過程中意外副作用的風險,很好地符合了這一理念。

在 Dockerfile 中使用 WORKDIR、COPY 和 ADD 指令

在此範例中,我們將把自訂 HTML 檔案部署到 Apache Web 伺服器。我們將使用 Ubuntu 作為基礎映像並在其上安裝 Apache。然後,我們將自訂的index.html檔案複製到 Docker 映像並下載 Docker 標誌。

使用mkdir指令建立一個名為workdir-copy-add-example的新目錄:

mkdir workdir-copy-add-example

導航到新建立的workdir-copy-add-example目錄:

cd .\workdir-copy-add-example\

workdir-copy-add-example目錄中,建立一個名為index.html檔案。該檔案將在建置期間複製到 Docker 映像。我將使用 VS Code,但您可以隨意使用您覺得更舒適的任何編輯器:

code index.html

將以下內容新增至 index.html 文件,儲存並關閉編輯器:

<html>
    <body>
        <h1>
            Welcome to Docker!
        </h1>
        <img src="logo.png" height="350" width="500"/>
    </body>
</html>

這段 HTML 程式碼建立了一個基本網頁,用一個大標題「歡迎來到 Docker!」來迎接訪客。在標題下方,它包含使用帶有 source 屬性 ( src="logo.png" ) 的<img>標記顯示的圖像,指示它應該獲取並顯示名為logo.png的圖像檔案。影像的大小為高度 350 像素、寬度 500 像素( height="350"width="500" )。


現在,在此目錄中建立一個Dockerfile

code Dockerfile

Dockerfile檔案中加入以下內容,儲存並退出:

FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 -y
WORKDIR /var/www/html/
COPY index.html .
ADD https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg ./logo.png
CMD ["ls"]

該 Dockerfile 首先指定FROM ubuntu:latest ,表示它將建置在最新的可用 Ubuntu 基礎映像上。隨後的RUN apt-get update && apt-get upgrade指令更新升級容器內的軟體包清單。接下來, apt-get install apache2 -y使用套件管理器安裝 Apache Web 伺服器。 WORKDIR /var/www/html/指令將工作目錄設定為/var/www/html/ ,這是 Apache 中提供 Web 內容的常用位置。

在此目錄中, COPY index.html .將本機index.html檔案從主機複製到容器中。此外, ADD https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg ./logo.png從 URL 檢索 SVG 映像檔並將其本機儲存為logo.png相同目錄。

最後, CMD ["ls"]指定容器啟動時,將執行ls指令,顯示/var/www/html/中的檔案和目錄清單。


現在,使用workdir-copy-add標籤建立 Docker 映像:

docker build -t workdir-copy-add .

您應該看到以下輸出:

[+] Building 4.0s (13/13) FINISHED                                                                       docker:default
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 290B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                   3.6s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                      0.0s
 => [1/6] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362  0.0s
 => => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362  0.0s
 => [internal] load build context                                                                                  0.0s
 => => transferring context: 32B                                                                                   0.0s
 => https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg                     0.3s
 => CACHED [2/6] RUN apt-get update && apt-get upgrade                                                             0.0s
 => CACHED [3/6] RUN apt-get install apache2 -y                                                                    0.0s
 => CACHED [4/6] WORKDIR /var/www/html/                                                                            0.0s
 => CACHED [5/6] COPY index.html .                                                                                 0.0s
 => CACHED [6/6] ADD https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg .  0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:646864d79dc576f862a980ef6ddab550ef9801790d2c91967c3c9596cf85b81a                       0.0s
 => => naming to docker.io/library/workdir-copy-add                                                                0.0s

執行docker container run指令從我們之前建置的 Docker 映像啟動一個新容器:

docker run workdir-copy-add

我們可以看到, index.htmllogo.png都在/var/www/html/目錄:

index.html
logo.png

使用者指令

在 Docker 中,預設情況下,容器以 root 使用者執行,該使用者在容器環境中擁有廣泛的權限。為了降低安全風險,Docker 允許我們使用Dockerfile中的USER指令指定非 root 使用者。該指令設定容器的預設用戶,而 Dockerfile 中指定的所有後續命令(例如RUNCMDENTRYPOINT )都會在此用戶的上下文下執行。

實作USER指令被認為是 Docker 安全性的最佳實踐,符合最小權限原則。它確保容器以其功能所需的最小權限執行,從而增強整體系統安全性並減少攻擊面。

USER指令採用以下格式:

USER <user>

除了使用者名稱之外,我們還可以指定可選的群組名稱來執行 Docker 容器:

USER <user>:<group>

您需要確保<user><group>值是有效的使用者名稱和群組名稱。否則,Docker 守護程式在嘗試執行容器時會拋出錯誤。

在 Dockerfile 中使用 USER 指令

在此範例中,我們將使用Dockerfile中的USER指令來設定預設使用者。我們將安裝 Apache Web 伺服器並將使用者變更為www-data 。最後,我們將執行whoami命令透過列印使用者名稱來驗證目前使用者。

建立一個名為user-example的新目錄

mkdir user-example

導航到新建立的user-example directory

cd .\user-example\

user-example目錄中建立一個新的Dockerfile

code Dockerfile

將以下內容新增至您的Dockerfile中,儲存並關閉編輯器:

FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 -y
USER www-data
CMD ["whoami"]

此 Dockerfile 從最新的 Ubuntu 基礎映像啟動,並在安裝 Apache Web 伺服器 ( apache2 ) 之前更新系統軟體包。它透過切換到通常用於 Web 伺服器的www-data使用者來增強安全性,以最大限度地減少潛在的漏洞。 CMD ["whoami"]指令確保容器啟動時顯示目前使用者 ( www-data ),示範適合在 Docker 環境中託管 Web 應用程式的安全性設定。


建置 Docker 映像:

docker build -t user .

您應該看到以下輸出:

[+] Building 5.0s (8/8) FINISHED                                                                                                             docker:default
 => [internal] load build definition from Dockerfile                                                                                                   0.0s
 => => transferring dockerfile: 157B                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                      0.1s
 => => transferring context: 2B                                                                                                                        0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                                                       4.8s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                                                          0.0s
 => [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30                                 0.0s
 => => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30                                 0.0s
 => CACHED [2/3] RUN apt-get update && apt-get upgrade                                                                                                 0.0s
 => CACHED [3/3] RUN apt-get install apache2 -y                                                                                                        0.0s
 => exporting to image                                                                                                                                 0.0s
 => => exporting layers                                                                                                                                0.0s
 => => writing image sha256:ce1de597471f741f4fcae898215cfcb0d847aacf7c201690c5d4e95289476768                                                           0.0s
 => => naming to docker.io/library/user                                                                                                                0.0s

現在,執行docker container run指令,從我們在上一個步驟中建置的 Docker 映像啟動一個新容器:

docker container run user

輸出應將www-data顯示為與 Docker 容器關聯的目前使用者:

www-data

體積指令

在 Docker 中,容器旨在以可移植且輕量級的方式封裝應用程式及其相依性。但是,預設情況下,在 Docker 容器的檔案系統中產生或修改的任何資料都是短暫的,這意味著它僅在容器執行時期間存在。當容器被刪除或替換時,這些資料就會遺失,這給需要持久性儲存的應用程式(例如資料庫或檔案儲存系統)帶來了挑戰。

為了應對這項挑戰,Docker 引入了卷的概念。卷提供了一種獨立於容器生命週期來保存資料的方法。它們充當 Docker 容器和主機之間的橋樑,確保即使容器停止、刪除或替換,儲存在磁碟區中的資料仍然存在。這使得磁碟區對於需要跨容器執行個體維護狀態資訊(例如儲存資料庫、設定檔或應用程式日誌)的應用程式至關重要。

當您使用VOLUME指令在 Dockerfile 中定義磁碟區時,Docker 會在容器的檔案系統中建立託管目錄。此目錄用作卷的安裝點。至關重要的是,Docker 還在主機上建立了相應的目錄,用於儲存磁碟區的實際資料。此對應可確保容器內對磁碟區內的檔案所做的任何變更都會立即與主機上的對應目錄同步,反之亦然。

Docker 中的磁碟區支援多種類型,包括命名磁碟區和主機安裝磁碟區。命名卷由 Docker 建立和管理,為磁碟區生命週期和儲存管理提供更多控制和靈活性。另一方面,主機安裝的磁碟區可讓您直接將主機檔案系統中的目錄安裝到容器中,從而提供對主機資源的直接存取。

VOLUME指令通常採用 JSON 陣列作為參數:

VOLUME ["path/to/volume"]

或者,我們可以指定具有多個路徑的純字串:

VOLUME /path/to/volume1 /path/to/volume2

我們可以使用docker container inspect <container>指令來查看容器中可用的磁碟區。 docker 容器檢查命令的輸出 JSON 將列印類似以下內容的磁碟區資訊:

[
   {
      "CreatedAt":"2024-06-21T22:52:52+03:00",
      "Driver":"local",
      "Labels":null,
      "Mountpoint":"/var/lib/docker/volumes/f46f82ea6310d0db3a13897a0c3ab45e659ff3255eaeead680b48bca37cc0166/_data",
      "Name":"f46f82ea6310d0db3a13897a0c3ab45e659ff3255eaeead680b48bca37cc0166",
      "Options":null,
      "Scope":"local"
   }
]

在 Dockerfile 中使用 VOLUME 指令

在此範例中,我們將設定一個 Docker 容器來執行 Apache Web 伺服器。但是,我們不希望在 Docker 容器發生故障時遺失 Apache 日誌檔案。作為解決方案,我們將透過將 Apache 日誌路徑安裝到底層 Docker 主機來持久保存日誌檔案。

建立一個名為volume-example的新目錄

mkdir volume-example

導航到新建立的volume-example目錄

cd volume-example

volume-example目錄中建立一個新的Dockerfile

code Dockerfile

將以下內容新增至Dockerfile ,儲存並退出

FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 -y
VOLUME ["/var/log/apache2"]

此 Dockerfile 首先使用最新版本的 Ubuntu 作為基礎映像,並透過執行apt-get updateapt-get upgrade更新所有已安裝的軟體套件來確保它是最新的。然後,它使用apt-get install apache2 -y安裝 Apache HTTP Server ( apache2 )。 VOLUME ["/var/log/apache2"]指令在/var/log/apache2定義一個 Docker 卷,這是 Apache 通常儲存其日誌檔案的位置。


現在,讓我們建立 Docker 映像:

docker build -t volume .

輸出應如下所示:

[+] Building 3.6s (8/8) FINISHED                                                                                                             docker:default
 => [internal] load .dockerignore                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                        0.0s
 => [internal] load build definition from Dockerfile                                                                                                   0.0s
 => => transferring dockerfile: 155B                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                                                       3.5s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                                                          0.0s
 => [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30                                 0.0s
 => => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30                                 0.0s
 => CACHED [2/3] RUN apt-get update && apt-get upgrade                                                                                                 0.0s
 => CACHED [3/3] RUN apt-get install apache2 -y                                                                                                        0.0s
 => exporting to image                                                                                                                                 0.0s
 => => exporting layers                                                                                                                                0.0s
 => => writing image sha256:9c7a81e379553444e0b4f3bbf45bdd17880aea251db8f8b75669e13964b9c30f                                                           0.0s
 => => naming to docker.io/library/volume  

執行docker container run命令以從先前建置的映像啟動一個新容器。請注意,您還需要使用--interactive--tty標誌來開啟互動式 bash 會話,以便您可以從容器的 bash shell 執行命令。此外,您需要使用--name標誌將容器名稱定義為volume-container

docker container run --interactive --tty --name volume-container volume /bin/bash

您的 bash shell 將如下開啟:

root@8aa0f5fb8a6d:/#

導覽至/var/log/apache2目錄

root@8aa0f5fb8a6d:/# cd /var/log/apache2/

這將產生以下輸出:

root@8aa0f5fb8a6d:/var/log/apache2#

現在,列出目錄中的可用文件

root@8aa0f5fb8a6d:/var/log/apache2# ls -l

輸出應如下所示

total 0
-rw-r----- 1 root adm 0 Jun 20 13:42 access.log
-rw-r----- 1 root adm 0 Jun 20 13:42 error.log
-rw-r----- 1 root adm 0 Jun 20 13:42 other_vhosts_access.log

這些是 Apache 在執行進程時建立的日誌檔案。檢查此磁碟區的主機掛載後,應該可以使用相同的檔案。


退出容器以檢查主機檔案系統

root@8aa0f5fb8a6d:/var/log/apache2# exit

檢查volume-container以查看安裝訊息

docker container inspect volume-container

Mounts鍵下,您將能夠看到與安裝相關的訊息

"Mounts":[
    {
         "Type":"volume",
         "Name":"50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8",
         "Source":"/var/lib/docker/volumes/50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8/_data",
         "Destination":"/var/log/apache2",
         "Driver":"local",
         "Mode":"",
         "RW":true,
         "Propagation":""
    }
]

使用docker volume inspect <volume_name>區。您可以在上一個輸出的Name欄位中找到<volume_name>

docker volume inspect 50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8

您應該得到類似以下內容的輸出

[{
   "CreatedAt":"2024-06-21T11:02:32Z",
   "Driver":"local",
   "Labels":{
      "com.docker.volume.anonymous":""
   },
   "Mountpoint":"/var/lib/docker/volumes/50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8/_data",
   "Name":"50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8",
   "Options":null,
   "Scope":"local"
}]

列出主機檔案路徑中可用的檔案。主機檔案路徑可以透過先前輸出的Mountpoint欄位來辨識

ls -l /var/lib/docker/volumes/50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8/_data

EXPOSE 指令

Docker 中的EXPOSE指令用於向 Docker 指示容器將在其執行時間監聽特定連接埠。此聲明主要提供訊息,實際上並不將連接埠發佈到主機系統或預設從容器外部存取它們。相反,它記錄了哪些連接埠旨在用於 Docker 環境中的容器間通訊或網路服務。

EXPOSE指令支援 TCP 和 UDP 協議,允許靈活地公開連接埠以滿足各種網路要求。該指令是容器執行時使用的-p-P選項的前身,用於將這些公開的端口實際映射到主機上的端口,從而在需要時啟用外部存取。

EXPOSE指令具有以下格式:

EXPOSE <port>

健康檢查指令

健康檢查是旨在評估容器運作健康狀況的重要機制。它提供了一種驗證 Docker 容器中執行的應用程式是否正常運作的方法。如果沒有指定的健康檢查,Docker 缺乏自主判斷容器健康狀態的能力。這在可靠性和正常運作時間至關重要的生產環境中變得尤為重要。

Docker 中的HEALTHCHECK指令允許開發人員定義自訂執行狀況檢查(通常以命令或腳本的形式),定期檢查容器的狀態並報告其執行狀況。此指令可確保主動監控並協助 Docker 編排工具根據運作狀況做出有關容器生命週期管理的明智決策。

Dockerfile中只能有一個HEALTHCHECK指令。如果有多個HEALTHCHECK指令,則只有最後一個指令生效。

例如,我們可以使用下列指令來確保容器可以接收http://localhost/端點上的流量:

HEALTHCHECK CMD curl -f http://localhost/ || exit 1

上述指令末的退出程式碼用於指定容器的健康狀態, 01是該欄位的有效值。 0表示健康容器, 1表示不健康容器。

在 Docker 中使用HEALTHCHECK指令時,可以設定基本指令以外的其他參數來自訂執行狀況檢查的執行方式:

  • --interval :指定執行健康檢查的頻率,預設間隔為 30 秒。

  • --timeout :定義執行狀況檢查指令成功完成所允許的最長時間。如果在此時間內沒有收到成功的回應,則健康檢查將被標記為失敗。預設超時也設定為 30 秒。

  • --start-period :指定 Docker 開始執行第一次執行狀況檢查之前的初始延遲。此參數允許容器在執行狀況檢查開始之前有一段時間進行初始化,預設啟動時間為 0 秒。

  • --retries :定義 Docker 認為容器不健康之前允許的連續失敗的健康檢查次數。預設情況下,Docker 最多允許重試 3 次。

在以下範例中,透過提供自訂值來覆寫HEALTHCHECK的預設值:

HEALTHCHECK \
    --interval=1m \
    --timeout=2s \
    --start-period=2m \
    --retries=3 \
    CMD curl -f http://localhost/ || exit 1

在 Dockerfile 中使用 EXPOSE 和 HEALTHCHECK 指令

我們將對 Apache Web 伺服器進行 docker 化,以便從 Web 瀏覽器存取 Apache 主頁。此外,我們將配置執行狀況檢查以確定 Apache Web 伺服器的運作狀況。

建立一個名為expose-heathcheck-example的新目錄

mkdir expose-healthcheck-example

導航到新建立的expose-healthcheck-example目錄

cd .\expose-healthcheck-example\

建立Dockerfile並新增以下內容

FROM ubuntu:latest

RUN apt-get update && apt-get upgrade

RUN apt-get install apache2 curl -y

HEALTHCHECK CMD curl -f http://localhost/ || exit 1

EXPOSE 80

ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]

該 Dockerfile 首先拉取最新的 Ubuntu 基礎映像並更新它。然後它使用apt-get安裝 Apache Web 伺服器和curl。 HEALTHCHECK指令設定為執行執行狀況檢查指令 ( curl -f http://localhost/ || exit 1 ),根據本機主機連線確保容器的運作狀況。暴露80埠以允許外部存取Apache。最後,容器被配置為使用ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]在前台模式下執行 Apache,確保它作為主程序保持活動和回應。此設定允許在 Docker 環境中託管可透過連接埠 80 存取的 Web 伺服器。


建構影像

docker image build -t expose-healthcheck-example .

您應該得到類似以下的輸出:

[+] Building 29.0s (8/8) FINISHED                                                                                                            docker:default
 => [internal] load build definition from Dockerfile                                                                                                   0.0s
 => => transferring dockerfile: 244B                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                        0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                                                       3.4s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                                                          0.0s
 => [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30                                 0.0s
 => => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30                                 0.0s
 => CACHED [2/3] RUN apt-get update && apt-get upgrade                                                                                                 0.0s
 => [3/3] RUN apt-get install apache2 curl -y                                                                                                         24.8s
 => exporting to image                                                                                                                                 0.6s
 => => exporting layers                                                                                                                                0.6s
 => => writing image sha256:3323e865b3888a4e45852c6a8c163cb820739735716f8783a0d126b43d810f1e                                                           0.0s
 => => naming to docker.io/library/expose-healthcheck-example                                                                                          0.0s

執行docker container run指令啟動一個新的容器。您將使用-p標誌將主機的連接埠80重定向到容器的連接埠8080 。此外,您將使用--name標誌將容器名稱指定為expose-healthcheck-container ,並使用-d標誌以分離模式執行容器

docker container run -p 8080:80 --name expose-healthcheck-container -d expose-healthcheck-example

使用docker container list指令列出正在執行的容器

docker container list

在產出中,您將看到expose-healthcheck-containerSTATUS是健康的

CONTAINER ID   IMAGE                        COMMAND                  CREATED              STATUS                        PORTS                            NAMES
3ff16b11275c   expose-healthcheck-example   "apache2ctl -D FOREG…"   About a minute ago   Up About a minute (healthy)   80/tcp, 0.0.0.0:8080->8080/tcp   expose-healthcheck-container

現在,您應該能夠查看 Apache 主頁。從瀏覽器導航到http://127.0.0.1:8080端點

圖片描述

ONBUILD 指令

Dockerfiles 中的 ONBUILD 指令有助於建立可重複使用的基礎映像,用於後續映像建置。它允許開發人員定義僅當另一個 Docker 映像使用當前映像作為基礎時才會觸發的指令。例如,您可以建立一個 Docker 映像,其中包含執行應用程式所需的所有必要先決條件和配置。

透過在此「先決條件」映像中套用 ONBUILD 指令,可以延遲特定指令,直到該映像檔在另一個 Dockerfile 中用作父映像。這些延遲指令不會在目前 Dockerfile 的建置過程中執行,而是在建置子映像時繼承並執行。這種方法簡化了設定環境的過程,並確保公共依賴項和配置在從基礎映像派生的多個專案或應用程式中一致地應用。

ONBUILD指令採用以下格式

ONBUILD <instruction>

舉個例子,假設我們在自訂基礎映像的Dockerfile中有以下ONBUILD指令

ONBUILD ENTRYPOINT ["echo", "Running an ONBUILD Directive"]

如果我們從自訂基礎映像建立 Docker 容器,則不會列印Running an ONBUILD Directive值,但如果我們將其用作另一個 Docker 映像的基礎,則會列印該值。

在 Dockerfile 中使用 ONBUILD 指令

在此範例中,我們將使用 Apache Web 伺服器建立父映像,並使用ONBUILD指令複製 HTML 檔案。

建立一個名為onbuild-parent-example的新目錄

mkdir onbuild-parent-example

導航到新建立的onbuild-parent-example目錄:

cd .\onbuild-parent-example\

新建一個Dockerfile並新增以下內容

FROM ubuntu:latest

RUN apt-get update && apt-get upgrade

RUN apt-get install apache2 -y

ONBUILD COPY *.html /var/www/html

EXPOSE 80

ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]

此 Dockerfile 首先使用最新的 Ubuntu 基礎映像。它更新和升級系統軟體包,然後安裝 Apache Web 伺服器。 ONBUILD 指令指定從此 Dockerfile 建置的任何子映像都會自動將所有 HTML 檔案從建置上下文複製到容器內的/var/www/html目錄。連接埠 80 被公開以允許傳入流量到達 Apache 伺服器。最後, ENTRYPOINT命令將容器配置為在前台模式下執行 Apache,確保它作為主進程保持活動和回應。此設定使容器能夠透過連接埠 80 上的 Apache 提供 Web 內容。


現在,建立 Docker 映像:

docker image build -t onbuild-parent-example .

輸出應如下所示:

[+] Building 3.5s (8/8) FINISHED                                                                         docker:default
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 221B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.1s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                   3.3s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                      0.0s
 => [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362  0.0s
 => => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362  0.0s
 => CACHED [2/3] RUN apt-get update && apt-get upgrade                                                             0.0s
 => CACHED [3/3] RUN apt-get install apache2 -y                                                                    0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:4a6360882fb65415cdd7326392de35c2336a8599c2c4b8b7a1e4d962d81df7e4                       0.0s
 => => naming to docker.io/library/onbuild-parent-example                                                          0.0s

執行docker container run指令,從上一個步驟建置的 Docker 映像中啟動一個新容器:

docker container run -p 8080:80 --name onbuild-parent-container -d onbuild-parent-example

如果您導覽至http://127.0.0.1:8080/端點,您應該會看到預設的 Apache 主頁

圖片描述


移除容器,使其不會幹擾端口

docker container stop onbuild-parent-container
docker container rm onbuild-parent-container

現在,讓我們使用onbuild-parent-container作為基礎映像建立另一個 Docker 映像,以部署自訂 HTML 主頁。為此,我們建立一個名為onbuild-child-example的新目錄

cd ..
mkdir onbuild-child-example

建立一個新的 html 頁面,包含以下內容

<html>

    <body>

        <h1>Demonstrating Docker ONBUILD Directive</h1>

    </body>

</html>

在同一目錄下建立Dockerfile

FROM onbuild-parent-example

Dockerfile有一個指令。這將使用FROM指令來利用我們先前建立的作為基礎映像的onbuild-parent-example Docker 映像。


現在,建置 docker 映像

docker image build -t onbuild-child-example .

輸出應該類似以下內容

[+] Building 0.3s (7/7) FINISHED                                                                         docker:default
 => [internal] load .dockerignore                                                                                  0.1s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load build definition from Dockerfile                                                               0.1s
 => => transferring dockerfile: 64B                                                                                0.0s
 => [internal] load metadata for docker.io/library/onbuild-parent-example:latest                                   0.0s
 => [internal] load build context                                                                                  0.1s
 => => transferring context: 134B                                                                                  0.0s
 => [1/1] FROM docker.io/library/onbuild-parent-example                                                            0.1s
 => [2/1] COPY *.html /var/www/html                                                                                0.0s
 => exporting to image                                                                                             0.1s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:9fb3629a292e2536300724db933eb59a6fb918f9d01e46a01aff18fe1ad6fe69                       0.0s
 => => naming to docker.io/library/onbuild-child-example                                                           0.0s

執行docker container run指令,用我們剛剛建置的映像啟動一個新的容器

docker container run -p 8080:80 --name onbuild-child-container -d onbuild-child-example

如果您導航到http://127.0.0.1:8080/端點,現在應該可以查看我們的自訂index.html頁面。

圖片描述

概括

在這篇文章中,我們將重點放在建立 Docker 映像。我們討論了更進階的 Dockerfile 指令,包括ENVARGWORKDIRCOPYADDUSERVOLUMEEXPOSEHEALTHCHECKONBUILD指令。

在接下來的幾篇文章中,我們將討論什麼是 Docker 註冊表、私人和公共 Docker 註冊表以及如何將映像發佈到 Docker 註冊表。


繫好安全帶,毛茛!這個碼頭之旅即將變得更加瘋狂。如需參考,請查看本系列的第一篇文章。這是您了解所有具體細節的一站式商店。


原文出處:https://dev.to/kalkwst/advanced-dockerfile-directives-193f


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈