0%

Dockerfile 撰寫教學

歡迎來到 Docker 的進階玩法——撰寫 Dockerfile!在前兩篇文章中,我們聊過 Docker 的核心架構(咖啡機的運作原理)和常用指令(如何點單與沖咖啡)。這次,我們要更進一步,自己動手調配「咖啡配方」,也就是撰寫 Dockerfile,讓你能打造專屬的 Docker Image。一樣會沿用「咖啡機」的比喻,若還沒閱讀過前文的讀者,可以先至上篇閱讀前篇閱讀 看看。準備好了嗎?讓我們開始調配吧!

什麼是 Dockerfile?

Dockerfile 就像是咖啡店裡的「沖泡配方單」。它告訴 Docker 如何從一堆原料(Base Image、程式碼、依賴)一步步沖出一杯完美的咖啡(Container)。有了 Dockerfile,你可以:

  • 定義咖啡機的基礎環境(例如用哪款咖啡機)。
  • 加入自己的原料(程式碼、設定檔)。
  • 設定沖泡步驟(安裝依賴、啟動應用)。

最終,這個配方會被打包成一個 Image,就可以隨時能用 docker run 沖出來。

基本結構:咖啡配方的組成

假設你要做一杯「Node.js 拿鐵」,以下是撰寫 Dockerfile 的基本指令:

FROM:選一台咖啡機

指定 Dockerfile 的基礎映像檔(Base Image),作為構建的起點。所有的後續指令都基於這個映像檔執行。

語法FROM <image>[:<tag>] [AS <name>]

  • <image>:映像檔名稱,例如 nodeubuntu
  • <tag>:版本標籤,例如 18latest(不指定會預設為 latest)。
  • AS <name>:可選,用於多階段構建時命名這個階段。
1
FROM node:18

就像選擇一台「Node.js 品牌的咖啡機」,版本是 18,裡面已經裝好了基本的沖泡工具(Node.js 環境)。

注意

  • 每個 Dockerfile 必須以 FROM 開始(多階段構建除外)。
  • 可以從 Docker Hub 或 Private 倉庫拉取映像檔。
  • 選擇適當

WORKDIR:設定沖泡檯面

設定工作目錄,將本機的檔案或目錄複製到容器內的指定路徑,所有後續操作都在這裡進行。

1
WORKDIR /app

就像在咖啡店裡選一個乾淨的檯面,準備開始調配。

COPY:加入原料

把本地的檔案(程式碼、設定檔)複製到容器內的工作目錄。

語法COPY <src> <dest>

  • <src>:來源路徑(本機檔案)。
  • <dest>:目標路徑(容器內部)。
1
COPY . .

把你的咖啡豆(app.js)、糖(package.json)搬到檯面上,準備開始調配。

注意

  • 如果 <dest> 不存在,會自動創建。
  • 建議搭配 .dockerignore(類似 .gitignore),排除不必要的檔案(例如 node_modules),避免浪費空間。

ADD:拿食材(進階版)

類似 COPY,但支援自動解壓縮和從 URL 下載文件。

語法ADD <src> <dest>

  • :主機上的文件、目錄或 URL。
  • :容器內的目標路徑。
1
ADD https://example.com/file.tar.gz /app/ 

注意

由於功能較多,ADD 可能導致意外行為,建議僅在需要解壓縮或下載時使用。
一般拷貝本地文件時,優先選擇 COPY 以保持簡單明確。

ENV:設定沖泡參數

設置容器內的環境變數。

語法ENV <key>=<value>
:環境變數的名稱。
:環境變數的值。

1
ENV APP_PORT=8080

就像設定咖啡機的「水溫」或「沖泡時間」,這裡設定 APP_PORT=8080 作為應用程式的運行端口。

注意

環境變數在 Dockerfile 中是持久的,會影響後續指令和容器行為。
避免在 ENV 中存儲敏感資訊(如密碼),以確保安全性。

RUN:準備沖泡環境

執行指令,通常用來安裝依賴,並將結果保存到映像檔中。

語法

  • Shell 格式:RUN <command>(例如 RUN npm install)。
  • Exec 格式:RUN ["executable", "param1", "param2"](例如 RUN ["apt-get", "update"])。
1
RUN npm install

像是把咖啡豆磨碎、加入濾紙、清洗咖啡機,這些都是準備沖泡的步驟。這些動作在打包配方(Image)時完成,而不是端上桌時才做。

注意

  • RUNdocker build 時執行,每次執行都會生成一個新的映像層(Layer)。
  • 盡量將多個命令用 && 合併(例如 RUN apt-get update && apt-get install -y curl),減少層數,節省空間。
  • 與後面的 CMD 不同,RUN 不影響容器運行時的行為。

CMD:按下開始鍵

指定容器啟動時要執行的命令。每個 Dockerfile 只能有一個 CMD,如果有多個,只有最後一個有效。

語法

  • Exec 格式(推薦):CMD ["executable", "param1", "param2"]
  • Shell 格式:CMD command param1 param2
  • 參數格式:CMD ["param1", "param2"](搭配 ENTRYPOINT 使用)。
1
CMD ["node", "app.js"]

指定容器啟動時要執行的命令。
就像告訴咖啡機:「用這些原料沖一杯咖啡吧!」

注意

  • CMDdocker run 時執行,且可以被命令列參數覆蓋(例如 docker run my-image bash 會取代 CMD)。
  • 如果沒指定 CMD,容器可能無法運行(除非有 ENTRYPOINT)。
  • RUN 的區別:RUN 是構建時跑,CMD 是啟動時跑。

ENTRYPOINT:咖啡機的預設模式

設定容器啟動時的預設執行程式,類似 CMD,但更像一個固定的「入口點」。它不會被 docker run 的參數輕易覆蓋。

語法

  • Exec 格式(推薦):ENTRYPOINT ["executable", "param1", "param2"]
  • Shell 格式:ENTRYPOINT command param1 param2
1
2
ENTRYPOINT ["node"]
CMD ["app.js"]

容器啟動時固定執行 node,而 app.js 是預設參數,可以被 docker run 覆蓋(例如 docker run my-image index.js 變成 node index.js)。

就像設定咖啡機的「預設模式」為「沖泡模式」(node),而 CMD 是你給的配方(app.js)。你可以臨時換配方,但模式不會變。

注意

  • ENTRYPOINTCMD 搭配使用時,CMD 提供預設參數,ENTRYPOINT 提供執行程式。
  • 如果只有 ENTRYPOINT,沒有 CMD,容器啟動時不會有預設行為,除非在 docker run 提供參數。
  • Shell 格式的 ENTRYPOINT 會忽略 CMDdocker run 的參數,建議用 Exec 格式。

EXPOSE:標示咖啡出口

告訴 Docker 這個容器預計使用哪些端口,並作為文件提示。它不會自動映射端口,實際映射仍需靠 docker run -p

語法EXPOSE <port>[/<protocol>]

  • <port>:端口號。
  • <protocol>:通訊協定(預設為 tcp,可選 udp)。
1
EXPOSE 3000

表示容器內的應用會使用 3000 端口。

就像在咖啡機上貼個標籤:「咖啡從 3000 號出口出來哦!」但你還是得自己把杯子(本機端口)拿到出口(-p 3000:3000)接咖啡。

注意事項

  • EXPOSE 只是元資料,不會自動開放端口。
  • 查看映像的暴露端口:docker inspect <image>
  • 多個端口可以用多行,例如:
    1
    2
    EXPOSE 80
    EXPOSE 443

實戰演練:打造 Node.js 拿鐵

讓我們實際寫一個簡單的 Node.js 應用,並用 Dockerfile 打包。

準備程式碼

創建一個資料夾(例如 coffee-app),裡面放以下檔案:

1
2
3
4
5
// app.js
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Enjoy your Docker Coffee!'));
app.listen(3000, () => console.log('Coffee brewing on port 3000'));
1
2
3
4
5
6
7
8
// package.json
{
"name": "coffee-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2"
}
}

撰寫 Dockerfile

在同一個資料夾裡創建 Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 選一台 Node.js 18 的咖啡機
FROM node:18

# 設定沖泡檯面
WORKDIR /app

# 加入原料(程式碼)
COPY . .

# 準備環境(安裝依賴)
RUN npm install

# 按下開始鍵(啟動應用)
CMD ["node", "app.js"]

打包成 Image

打開終端機,進入 coffee-app 資料夾,執行:

1
docker build -t my-coffee .
  • 比喻:就像把配方單交給店長(Docker Daemon),說:「幫我把這份配方打包好!」
  • 結果:生成一個名叫 my-coffee 的 Image。

4:沖一杯咖啡

1
docker run -d -p 3000:3000 my-coffee
  • 比喻:按下咖啡機的開始鍵,把咖啡送到 3000 號桌。
  • 結果:瀏覽 http://localhost:3000,會看到「Enjoy your Docker Coffee!」。

進階技巧:讓咖啡更好喝

多階段構建:減肥版咖啡

如果你的 Image 太大,可以用多階段構建。例如:

1
2
3
4
5
6
7
8
9
10
11
# 第一階段:準備原料
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm install

# 第二階段:只保留必要部分
FROM node:18-slim
WORKDIR /app
COPY --from=builder /app .
CMD ["node", "app.js"]
  • 比喻:就像先把咖啡豆磨好(第一階段),然後只用輕巧的咖啡機沖泡(第二階段),減少浪費。

ENV:加入秘密調味料

1
2
ENV PORT=3000
CMD ["node", "app.js"]

設定環境變數。
像是偷偷加點糖漿,讓咖啡更有風味。

EXPOSE:標示咖啡出口

1
EXPOSE 3000

告訴別人這個容器會用哪個端口(只是提示,不強制)。
就像在咖啡機上貼個標籤:「咖啡從 3000 號出口出來哦!」

常見問題與注意事項

  1. 檔案大小太大?

    • 檢查是否複製了不必要的檔案。用 .dockerignore 排除(例如 node_modules)。
    • 比喻:別把整個原料倉庫搬進咖啡機,只帶需要的就好。
  2. CMD vs RUN?

    • RUN 是構建時執行(磨豆子),CMD 是啟動時執行(沖咖啡)。
    • 比喻:準備原料和端上桌是兩回事。
  3. 忘了加 WORKDIR?

    • 指令會在根目錄(/)執行,可能導致混亂。
    • 比喻:沒選檯面,原料會散落一地。

撰寫 Dockerfile 就像調配咖啡配方:從選咖啡機(FROM)、準備原料(COPY)、設定步驟(RUN),到最後按下開始鍵(CMD)。學會這些,你就能打造屬於自己的 Docker Image,隨時沖出一杯穩定的「程式咖啡」!