Joseph Chen
「『在我電腦可以跑』是工程師最讓人頭痛的一句話。Docker 解決的就是這個問題 — 把整個執行環境打包起來一起出貨,讓它在哪裡都能跑。」
— 容器化技術的核心價值:環境一致性(Environment Parity)。
為什麼需要 Docker?
你在本機開發的 Python 3.11 應用程式,部署到伺服器後發現對方是 Python 3.8,某個套件不支援,整個炸掉。這就是「環境不一致」問題。
開發環境
macOS + Python 3.11 + 套件 A v2.0
測試環境
Ubuntu + Python 3.9 + 套件 A v1.8
生產環境
CentOS + Python 3.8 + 套件 A v1.5
VM vs Container:架構比較
Docker Container 和虛擬機(VM)都能提供隔離環境,但底層架構完全不同:
Virtual Machine
- 每個 VM 含完整 OS,體積 GB 級
- 啟動需 1–2 分鐘
- 隔離性最強(不同 Kernel)
Docker Container
- 共用 Host Kernel,體積 MB 級
- 啟動只需秒級(甚至毫秒)
- Process-level 隔離(namespace + cgroup)
| 比較項目 | VM | Container |
|---|---|---|
| 體積 | GB 級(含完整 OS) | MB 級(只含應用層) |
| 啟動時間 | 1–2 分鐘 | 秒級甚至更快 |
| 隔離程度 | 強(獨立 Kernel) | 中(共用 Kernel) |
| 資源消耗 | 高 | 低 |
| 可攜性 | 依賴 Hypervisor | 任何有 Docker 的機器都能跑 |
| 使用場景 | 需要不同 OS、強隔離 | 微服務、CI/CD、快速部署 |
三個核心概念:Dockerfile → Image → Container
Docker 的世界只有三個主角,搞懂他們的關係就懂了 80%:
建置與執行流程
Dockerfile
食譜(文字檔)
→
docker build
Image
快照(唯讀)
→
docker run
Container
執行中的實例
Dockerfile
食譜一個純文字的指令檔,告訴 Docker「怎麼建這個 Image」。FROM 哪個 base image、要 RUN 哪些指令安裝套件、要 COPY 哪些檔案進去。
Image
快照(唯讀)docker build 執行 Dockerfile 後產生的唯讀快照,由多個 layer 堆疊而成。可以 push 到 Docker Hub 分享給別人。
Container
執行中的實例docker run 從 Image 啟動的執行實例,有自己的 process、network、filesystem。同一個 Image 可以同時啟動多個 Container。
常用指令速查表
| 指令 | 作用 | 常用參數 |
|---|---|---|
| docker build -t <name> . | 從 Dockerfile 建 Image | -t 指定名稱:tag,. = 當前目錄 |
| docker run <image> | 從 Image 啟動 Container | -d 背景執行,-p 8080:80 port 對應,-v 掛載 volume |
| docker ps | 列出執行中的 Container | -a 顯示全部(含已停止) |
| docker stop <id> | 停止 Container | docker stop $(docker ps -q) 停全部 |
| docker rm <id> | 刪除 Container | -f 強制刪除執行中的 |
| docker images | 列出本機所有 Image | |
| docker rmi <image> | 刪除 Image | -f 強制刪除 |
| docker exec -it <id> sh | 進入執行中的 Container | -it = interactive + tty |
| docker logs <id> | 查看 Container 輸出日誌 | -f 追蹤即時輸出 |
| docker pull <image> | 從 Registry 下載 Image | docker pull node:18-alpine |
| docker push <image> | 推 Image 到 Registry | 需先 docker login |
Dockerfile 基礎語法
Dockerfile 的每一行指令都會建立一個 layer。以下是最常用的關鍵字:
| 指令 | 說明 |
|---|---|
| FROM | 指定 base image,每個 Dockerfile 都必須有。alpine 版本體積更小。 |
| WORKDIR | 設定後續指令的工作目錄,不存在時自動建立。 |
| COPY | 複製本機檔案/目錄到容器內。 |
| ADD | 和 COPY 類似,但支援 URL 和自動解壓 tar,一般優先用 COPY。 |
| RUN | 建置階段執行的 shell 指令,每個 RUN 建立一個 layer。 |
| ENV | 設定環境變數,Container 執行時也會存在。 |
| EXPOSE | 宣告 port(僅文件用途,需要 -p 才真正對應到主機)。 |
| CMD | 容器啟動的預設指令,可被 docker run 後面的參數覆蓋。 |
| ENTRYPOINT | 容器入口點,不易被覆蓋,常和 CMD 搭配使用。 |
docker-compose:多服務一起管理
實際專案往往有前端、後端、資料庫三個服務。docker-compose 讓你用一個 YAML 檔宣告所有服務,一行指令全部啟動。
Volume vs Bind Mount:資料持久化
Container 本身是無狀態的,刪除後資料就消失。要讓資料持久化,需要掛載外部儲存:
Named Volume
- 由 Docker 管理,可跨平台
- 性能較佳
- 用 docker volume ls 管理
Bind Mount
- 本機路徑直接對應
- 開發時即時同步
- 跨平台路徑問題需注意
面試常考題
以下是 Docker / DevOps 相關面試中最常出現的概念題,每題附標準答案要點。
Q1. Image 和 Container 的差異?
Image 是唯讀的靜態快照(食譜),由 Dockerfile build 產生,存在 registry 可以分享。Container 是 Image 的執行實例(料理),有自己的 process、network、可寫 filesystem layer。同一個 Image 可以同時 run 多個 Container,彼此獨立。
Q2. CMD 和 ENTRYPOINT 的差異?
CMD 是容器的預設指令,可以被 docker run 後面傳入的參數完全覆蓋。ENTRYPOINT 是固定的入口點,傳入的參數會附加在後面而不是替換。常見搭配:ENTRYPOINT ["python"] + CMD ["app.py"],這樣可以 docker run myapp other.py 來執行不同的腳本。
Q3. Docker layer 是什麼?為何要把 COPY package.json 放在 COPY . . 前?
Dockerfile 每一個指令(FROM/RUN/COPY)都建立一個 layer,Docker 會 cache 每層。如果某層輸入沒變,直接用快取跳過。package.json 改動頻率遠低於原始碼,把它先 COPY 並 RUN npm install,只要 package.json 沒變就直接使用 cache,大幅加速 CI/CD 的 build 時間。
Q4. Container 之間如何通訊?
使用 docker-compose 時,同一個 compose file 的 Container 預設在同一個 network,可以直接用 service name 當 hostname(例如後端連 db:5432)。若是手動啟動,需要 docker network create 建立自訂 network,再用 --network 參數指定。
Q5. docker-compose 和 Kubernetes 的差異?
docker-compose 適合本機開發和小型部署,管理單台機器上的多個 Container,設定簡單。Kubernetes(K8s)是生產級的容器編排系統,管理多台機器(cluster)、自動 scaling、rolling update、自我修復(Pod 掛了自動重啟)、服務發現、負載平衡。學習曲線差距很大。
Q6. 什麼是 multi-stage build?為什麼用它?
Multi-stage build 是在同一個 Dockerfile 裡用多個 FROM 分成不同階段。例如先用 node:18 build React app(含所有 devDependencies),再用 nginx:alpine 只複製 build 產物,最終 Image 不含任何 Node.js 環境。效果:把數百 MB 的 build 環境 Image 縮到幾十 MB 的精簡 Image,減少攻擊面積與部署時間。
這篇學到什麼
上一篇
EP.12 — Git
版本控制的底層邏輯
下一篇
Coming Soon...
敬請期待下一篇