Dockerfile 基礎:建構 Docker 映像檔的核心指令
概述
Dockerfile 是一個包含一系列指令的文字檔案,Docker 引擎會讀取這些指令,並自動建構出 Docker 映像檔 (Image)。它定義了映像檔的內容、運行環境以及啟動時執行的指令。理解 Dockerfile 是掌握 Docker 容器化技術的關鍵一步。
Dockerfile 核心指令詳解
1. 基礎指令
FROM:指定基礎映像檔。Dockerfile 的第一條指令必須是FROM。- 語法:
FROM <image>[:<tag>] - 範例:
FROM ubuntu:18.04(使用 Ubuntu 18.04 作為基礎映像檔) - 注意:如果未指定
tag,預設為latest。建議明確指定版本,以確保建構的可重複性。
- 語法:
MAINTAINER:指定映像檔的維護者資訊。- 語法:
MAINTAINER <name> [email] - 範例:
MAINTAINER Bill.Lin <zzx123bill@gmail.com> - 注意:此指令已過時,建議使用
LABEL指令來替代,例如LABEL maintainer="Bill.Lin <zzx123bill@gmail.com>"。
- 語法:
2. 執行指令
RUN:在建構映像檔時執行命令。每個RUN指令都會在映像檔中建立一個新的層 (layer)。- 語法:
RUN <command>(shell 模式,預設使用/bin/sh -c執行)RUN ["executable", "param1", "param2"](exec 模式,直接執行可執行檔)
- 範例 (shell 模式):
1 2RUN apt-get update -y \ && apt-get install nginx -y- 注意:使用
&&將多個命令串聯起來,可以減少映像檔層數,優化映像檔大小。
- 注意:使用
- 範例 (exec 模式):
1 2RUN ["./test.php", "dev", "offline"] # 等同於在容器內執行 `./test.php dev offline`
- 語法:
CMD:設定容器啟動時預設執行的命令。如果docker run命令後帶有參數,CMD的命令會被覆蓋。一個 Dockerfile 中只能有一個CMD,多個CMD只有最後一個生效。- 語法:
CMD ["executable", "param1", "param2"](exec 模式,推薦)CMD ["param1", "param2"](作為ENTRYPOINT的預設參數)CMD command param1 param2(shell 模式)
- 範例:
CMD ["nginx", "-g", "daemon off;"](啟動 Nginx 並保持在前台運行) - 注意:
CMD會被docker run <image> <command>中的<command>覆蓋。
- 語法:
ENTRYPOINT:設定容器啟動時執行的命令。ENTRYPOINT不會被docker run後的參數覆蓋,而是將這些參數作為ENTRYPOINT命令的參數。- 語法:
ENTRYPOINT ["executable", "param1", "param2"](exec 模式,推薦) - 範例:
ENTRYPOINT ["docker-entrypoint.sh"] CMD與ENTRYPOINT組合使用: 當ENTRYPOINT和CMD都使用 exec 模式時,CMD的內容會作為ENTRYPOINT的參數。這種組合常用於設定固定的啟動命令,同時允許使用者透過1 2 3 4FROM nginx ENTRYPOINT ["nginx", "-c"] CMD ["/etc/nginx/nginx.conf"] # 容器啟動時實際執行:nginx -c /etc/nginx/nginx.confCMD或docker run參數來提供預設或自訂的參數。
- 語法:
3. 環境設定
WORKDIR:設定工作目錄。後續的RUN,CMD,ENTRYPOINT,COPY,ADD指令都會在這個目錄下執行。- 語法:
WORKDIR /path/to/workdir - 範例:
1 2WORKDIR /app COPY . /app # 將建構上下文中的檔案複製到容器的 /app 目錄
- 語法:
USER:指定運行容器時的用戶或用戶組。- 語法:
USER <user>[:<group>] - 範例:
USER nobody(以非 root 用戶運行,提高安全性) - 注意:預設為
root。在生產環境中,建議使用非 root 用戶運行應用程式。
- 語法:
VOLUME:建立一個掛載點,將容器內的路徑標記為外部掛載點,用於持久化資料或共享資料。- 語法:
VOLUME ["/data"] - 範例:
VOLUME /var/lib/mysql(將 MySQL 資料目錄標記為儲存卷)
- 語法:
ARG:定義建構時的變數。這些變數只在docker build過程中有效,不會保留在最終的映像檔中。- 語法:
ARG <name>[=<default value>] - 範例:
1 2ARG VERSION=1.0 FROM myapp:${VERSION} - 使用:
docker build --build-arg VERSION=2.0 .
- 語法:
ENV:設定環境變數。這些變數會保留在最終的映像檔中,並在容器運行時可用。- 語法:
ENV <key>=<value> ... - 範例:
1 2ENV MY_PATH /usr/local WORKDIR $MY_PATH # WORKDIR 可以使用 ENV 定義的變數
- 語法:
EXPOSE:聲明容器運行時監聽的埠。這只是一個文件說明,並不會實際發布埠。要發布埠,需要在docker run或docker-compose.yml中使用-p或ports。- 語法:
EXPOSE <port> [<port>...] - 範例:
EXPOSE 8080(聲明容器會監聽 8080 埠)
- 語法:
4. 檔案操作
COPY:將建構上下文中的檔案或目錄複製到映像檔中。- 語法:
COPY <src>... <dest> - 範例:
COPY ./target/mms.war /app/mms.war(將主機target目錄下的mms.war複製到容器的/app目錄) - 注意:
COPY只能複製本地建構上下文中的檔案。
- 語法:
ADD:與COPY類似,但ADD具有額外的功能:- 如果
<src>是一個壓縮檔 (如.tar,.gz,.zip),它會自動解壓縮到<dest>。 - 如果
<src>是一個 URL,它會從該 URL 下載檔案。 - 語法:
ADD <src>... <dest> - 範例:
ADD https://example.com/app.tar.gz /app/ - 注意:由於
ADD的自動解壓縮和 URL 下載功能可能導致不確定性,通常建議優先使用COPY,除非您明確需要ADD的特殊功能。
- 如果
Docker Build 流程
使用 docker build 命令來建構 Docker 映像檔。
- 語法:
docker build [OPTIONS] PATH | URL | - - 範例:
docker build -t my-app:1.0 .-t my-app:1.0:為映像檔指定名稱和標籤。.:指定建構上下文的路徑。Docker 會將此路徑下的所有檔案發送到 Docker Daemon,作為建構過程的上下文。
範例:Java Spring Boot 應用程式的多階段建構
多階段建構 (Multi-stage Builds) 允許您在一個 Dockerfile 中使用多個 FROM 指令。每個 FROM 指令都可以使用不同的基礎映像檔,並且每個階段都可以獨立地建構。最終的映像檔只包含您需要的最終產物,大大減少了映像檔的大小。
| |
這個範例展示了如何使用多階段建構來:
- 在第一個階段 (
build) 中編譯 Java 應用程式,生成一個 JAR 檔案。 - 在第二個階段中,只複製最終的 JAR 檔案到一個更小的 JRE 基礎映像檔中。
這樣可以避免將編譯工具、原始碼等不必要的內容包含在最終的運行映像檔中,從而顯著減小映像檔大小,提高安全性和部署效率。
透過熟練掌握 Dockerfile 的各項指令和最佳實踐,您可以更有效地建構和管理您的 Docker 映像檔。
進階最佳實踐
.dockerignore 減少 build context
| |
沒有 .dockerignore 時,docker build 會把整個目錄(含 node_modules、.git)送到 Docker daemon,大幅增加 build 時間。
Layer Cache 最佳化排序
依賴的 layer 改變頻率越低,放越前面:
| |
Distroless / Scratch 映像(最小化)
| |
Distroless 映像:
- 無 shell(無法
docker exec sh)→ 安全性高 - 映像大小大幅減少(約 50-80MB vs JDK 映像 500MB+)
- 適合生產環境;開發環境仍用完整 JDK 映像
留言討論