Skip to content

Docker 容器化

前言

「在我的機器上能跑」是開發者最經典的藉口,Docker 讓這個藉口徹底消失。 容器化技術將應用及其所有依賴打包成一個標準化的單元,確保在任何環境中都能一致執行。它是現代軟體交付的基石。

這篇文章會帶你學什麼?

學完這章後,你將獲得:

  • 核心概念:理解映像、容器、倉庫三大核心概念
  • 架構對比:明白容器和虛擬機的本質區別
  • 實作能力:掌握 Dockerfile 撰寫和常用指令
  • 編排基礎:學會用 Docker Compose 管理多服務應用
  • 最佳實踐:了解映像最佳化、安全加固等生產級實踐
章節內容核心概念
第 1 章為什麼需要容器環境一致性、資源效率、標準化交付
第 2 章核心概念映像、容器、倉庫、Dockerfile
第 3 章Docker 生命週期撰寫、建置、推送、執行、管理
第 4 章Docker Compose多服務編排、網路、資料卷
第 5 章最佳實踐映像最佳化、安全、多階段建置

1. 為什麼需要容器?

在容器出現之前,部署一個應用需要在伺服器上手動安裝執行環境、設定環境變數、處理依賴衝突。不同環境(開發、測試、正式)之間的差異是 bug 的溫床。

Virtual Machines vs Containers
Switch between both virtualization models to compare their architecture
App A / App B / App C
App A + Bins/Libs
App B + Bins/Libs
App C + Bins/Libs
Docker Engine
Host OS
Physical hardware
Startup speedSeconds
Resource usageShared host OS kernel (MB scale)
IsolationProcess-level isolation
DensityHundreds of containers per host
Image sizeMB scale

容器解決了什麼問題?

問題傳統方式容器方式
環境不一致「我本地能跑」打包所有依賴,到處一致
依賴衝突App A 需要 Node 14,App B 需要 Node 18每個容器獨立環境
資源浪費每個 VM 一個完整 OS共享核心,MB 級開銷
部署慢手動安裝設定docker run 一條指令
擴容難新建 VM、裝環境、部署秒級啟動新容器

容器的本質

容器不是輕量級虛擬機。它的本質是被隔離的程序。Linux 核心透過兩個機制實現容器:

  • Namespace:隔離程序的視野(PID、網路、檔案系統等)
  • Cgroups:限制程序的資源使用(CPU、記憶體、IO)

容器裡的程序和宿主機上的普通程序沒有本質區別,只是被「關在了一個看不到外面的房間裡」。


2. 核心概念

Docker 的世界圍繞三個核心概念:映像(Image)、容器(Container)、倉庫(Registry)。

概念類比說明
映像(Image)類別 / 模板唯讀的應用模板,包含程式碼、執行環境、函式庫、設定
容器(Container)實例 / 物件映像的執行實例,可讀寫,有獨立的生命週期
倉庫(Registry)應用商店儲存和分發映像的服務(Docker Hub、ACR、ECR)
Dockerfile配方 / 藍圖定義如何建置映像的文字檔案
資料卷(Volume)外接硬碟持久化資料,容器刪除後資料不遺失

映像的分層結構

Docker 映像由多個唯讀層(Layer)疊加而成,每條 Dockerfile 指令建立一層:

┌─────────────────────────┐
│  CMD ["node", "app.js"] │  ← 啟動命令層
├─────────────────────────┤
│  COPY . /app            │  ← 應用程式碼層(經常變)
├─────────────────────────┤
│  RUN npm install        │  ← 依賴安裝層(偶爾變)
├─────────────────────────┤
│  FROM node:18-alpine    │  ← 基礎映像層(很少變)
└─────────────────────────┘

為什麼分層很重要?

Docker 會快取每一層。如果某一層沒有變化,建置時會直接複用快取。所以 Dockerfile 中應該把變化頻率低的指令放在前面(如安裝依賴),變化頻率高的放在後面(如複製程式碼)。這樣大部分建置都能命中快取,速度快很多。


3. Docker 生命週期

從撰寫 Dockerfile 到容器執行,Docker 的運作流程是一條清晰的管線。

Docker Lifecycle
Click each stage to inspect the details
📝
Write a Dockerfile
🔨
Build the image
☁️
Push to registry
▶️
Run the container
⚙️
Manage containers
Write a Dockerfile
A Dockerfile is the recipe for building an image. It starts from a base image and defines how to assemble the application environment step by step. Each instruction creates an image layer that Docker can cache for later builds.
Common commands
FROM node:18-alpineChoose the base image
WORKDIR /appSet the working directory
COPY package*.json ./Copy dependency files for cache reuse
RUN npm installInstall dependencies
COPY . .Copy application code
EXPOSE 3000Declare the port
CMD ["node", "server.js"]Start command

Dockerfile 常用指令速查

指令作用範例
FROM指定基礎映像FROM node:18-alpine
WORKDIR設定工作目錄WORKDIR /app
COPY複製檔案到映像COPY package.json ./
RUN建置時執行命令RUN npm install
ENV設定環境變數ENV NODE_ENV=production
EXPOSE宣告連接埠(僅文件作用)EXPOSE 3000
CMD容器啟動命令CMD ["node", "app.js"]
ENTRYPOINT容器入口點(不易被覆蓋)ENTRYPOINT ["nginx"]

4. Docker Compose:多服務編排

真實專案通常不止一個容器。一個 Web 應用可能需要:應用伺服器 + 資料庫 + Redis + Nginx。Docker Compose 用一個 YAML 檔案定義和管理多個容器。

docker-compose.yml 範例

yaml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=secret

  redis:
    image: redis:7-alpine

volumes:
  db-data:

Compose 核心概念

概念說明範例
services定義各個容器服務app、db、redis
volumes持久化資料卷db-data 儲存資料庫檔案
networks自訂網路(預設自動建立)服務間透過服務名稱互相存取
depends_on啟動順序依賴app 依賴 db 和 redis
environment環境變數資料庫密碼、連線位址

服務發現

在 Docker Compose 中,服務名稱就是主機名稱。app 容器可以直接用 db:5432 存取資料庫,用 redis:6379 存取 Redis,不需要知道 IP 位址。這是 Docker 內建 DNS 的功勞。


5. 最佳實踐

5.1 多階段建置(Multi-stage Build)

多階段建置是最佳化映像大小的利器。建置階段安裝所有工具和依賴,最終階段只保留執行時需要的檔案。

dockerfile
# 建置階段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 執行階段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

5.2 映像最佳化清單

最佳化項目做法效果
選擇小基礎映像alpine 而非 ubuntu映像從 ~200MB 降到 ~50MB
合併 RUN 指令多個命令用 && 連接減少映像層數
使用 .dockerignore排除 node_modules、.git 等加速建置,減小上下文
多階段建置分離建置和執行環境最終映像不含建置工具
固定版本號node:18.17-alpine 而非 node:latest建置可重現

5.3 安全實踐

實踐說明
不用 root 執行USER node 指定非 root 使用者
掃描漏洞docker scout 或 Trivy 掃描映像
最小權限只安裝必要的套件,不安裝除錯工具
不硬編碼金鑰用環境變數或 Docker Secrets
定期更新基礎映像及時修復安全漏洞

總結

Docker 容器化是現代軟體交付的基礎設施,理解它對於任何開發者都至關重要。

回顧本章的關鍵要點:

  1. 容器 vs 虛擬機:容器共享宿主核心,更輕量、更快,但隔離性略弱於 VM
  2. 核心三件套:映像(模板)、容器(實例)、倉庫(分發)
  3. Dockerfile:分層建置,利用快取,變化少的指令放前面
  4. Docker Compose:用 YAML 定義多服務應用,服務名稱即主機名稱
  5. 生產實踐:多階段建置減小映像、alpine 基礎映像、非 root 執行

延伸閱讀