Chuyển đến nội dung chính

第 2 課:容器和 Docker — 應用程式打包平台

容器與虛擬機器、Docker 架構、Dockerfile 最佳實務、多階段建置、映像安全掃描和基本容器網路。

🏗️ 建築 — 第 2 課 第 2 課:容器和 Docker — 封閉平台 申請包

雲端原生微服務架構

第 1 部分:雲端原生基礎

亞洲開發網

第 2 課:容器和 Docker — 應用程式打包平台

簡介

容器是雲端原生的基礎。微服務架構中的每個服務都被打包到容器映像中,以確保開發、登台和生產之間的一致性。本文深入探討容器、Docker 和基本的最佳實務。


1. 容器與虛擬機

1.1 虛擬機

┌──────────────────────────────────────┐
│           Host Hardware              │
├──────────────────────────────────────┤
│           Host OS (Linux)            │
├──────────────────────────────────────┤
│           Hypervisor (KVM/ESXi)      │
├────────────┬────────────┬────────────┤
│  Guest OS  │  Guest OS  │  Guest OS  │
│  (Ubuntu)  │  (CentOS)  │  (Debian)  │
├────────────┼────────────┼────────────┤
│  Libs/Bins │  Libs/Bins │  Libs/Bins │
├────────────┼────────────┼────────────┤
│   App A    │   App B    │   App C    │
└────────────┴────────────┴────────────┘
  • 每個虛擬機器運行單獨的作業系統(單獨的核心)
  • 資源消耗:500MB - 2GB RAM 僅用於作業系統
  • 啟動:30 秒 - 幾分鐘
  • 隔離:強(硬體等級)

1.2 容器

┌──────────────────────────────────────┐
│           Host Hardware              │
├──────────────────────────────────────┤
│           Host OS (Linux Kernel)     │
├──────────────────────────────────────┤
│        Container Runtime (containerd)│
├────────────┬────────────┬────────────┤
│  Libs/Bins │  Libs/Bins │  Libs/Bins │
├────────────┼────────────┼────────────┤
│   App A    │   App B    │   App C    │
└────────────┴────────────┴────────────┘
  • 所有容器共享主機作業系統的核心
  • Light:容器鏡像 5MB - 200MB
  • 啟動: < 1 秒
  • Isolation: Process-level (namespaces + cgroups)

1.3 比較

標準虛擬機器貨櫃
Startup time30s - 5min< 1s
Image sizeGBMB
RAM overhead500MB+ per VM~0 overhead
IsolationStrong (hypervisor)Process-level (namespace)
Density10-20 VMs per host100-1000 containers per host
PortabilityModerateExcellent

2. Docker Architecture

2.1 Core Components

┌─────────────────────────────────────────────────┐
│                Docker Client (CLI)               │
│  docker build, docker run, docker push           │
└───────────────────────┬─────────────────────────┘
                        │ REST API
┌───────────────────────▼─────────────────────────┐
│                Docker Daemon (dockerd)            │
│                                                   │
│  ┌──────────┐  ┌──────────────┐  ┌────────────┐ │
│  │  Images   │  │  Containers  │  │  Volumes   │ │
│  └──────────┘  └──────────────┘  └────────────┘ │
│  ┌──────────┐  ┌──────────────┐                  │
│  │ Networks  │  │  containerd  │                  │
│  └──────────┘  └──────┬───────┘                  │
└──────────────────────┬──────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────┐
│              Container Runtime (runc)             │
│         Linux Kernel: namespaces + cgroups        │
└─────────────────────────────────────────────────┘

2.2 Linux Kernel Features

容器基於 Linux 核心的 2 個核心功能建構:

命名空間 — 隔離:

  • pid — 進程隔離(容器只能看到自己的進程)
  • net — 網路隔離(每個容器都有自己的網路堆疊)
  • mnt — 掛載自己的檔案系統
  • uts — 私有主機名
  • ipc — 單獨的進程間通信
  • user — 使用者/群組 ID 單獨映射

Cgroups — 資源限制:

  • CPU (millicores)
  • Memory (bytes)
  • Disk I/O
  • Network bandwidth

3. Dockerfile Best Practices

3.1 Multi-stage Build

多階段建置透過分離建置環境和運行時來幫助建立緊湊的映像:

# === Stage 1: Build ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build

# === Stage 2: Runtime ===
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
CMD ["node", "dist/main.js"]

結果:映像建置 ~800MB → 映像運行時 ~150MB

3.2 Layer Caching

Docker 快取每一層。安排Dockerfile來優化緩存:

# ✅ Tốt: Dependencies ít thay đổi → cache được layer này
COPY package*.json ./
RUN npm ci
# Source code thay đổi thường xuyên → chỉ invalidate từ đây
COPY . .
RUN npm run build

# ❌ Xấu: Mỗi lần thay đổi code → rebuild tất cả
COPY . .
RUN npm ci && npm run build

3.3 Security Best Practices

# 1. Dùng specific version, KHÔNG dùng :latest
FROM node:20.11-alpine3.19

# 2. Chạy với non-root user
RUN addgroup -S app && adduser -S app -G app
USER app

# 3. Không copy file nhạy cảm
# .dockerignore:
# .env
# .git
# node_modules
# *.secret

# 4. Scan image trước khi push
# trivy image myapp:1.0.0

3.4 Java / Go / Python Examples

Java (Spring Boot):

FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY gradle/ gradle/
COPY gradlew build.gradle.kts settings.gradle.kts ./
RUN ./gradlew dependencies --no-daemon
COPY src/ src/
RUN ./gradlew bootJar --no-daemon

FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S app && adduser -S app -G app
COPY --from=build /app/build/libs/*.jar /app/app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

Go:

FROM golang:1.22-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server

FROM scratch
COPY --from=build /server /server
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]

一起去 scratch base image → image size ~10-15MB.


4. Container Networking

4.1 Docker Network Types

Bridge (default)     Host              None         Overlay (Swarm/K8s)
┌────────────────┐  ┌──────────┐  ┌──────────┐  ┌──────────────────┐
│ Container A    │  │Container │  │Container │  │ Host 1    Host 2 │
│ 172.17.0.2     │  │shares    │  │no network│  │ ┌─────┐  ┌─────┐│
│ Container B    │  │host's    │  │isolated  │  │ │ C-A │  │ C-B ││
│ 172.17.0.3     │  │network   │  │          │  │ └──┬──┘  └──┬──┘│
│      │         │  │stack     │  │          │  │    └────┬────┘   │
│ docker0 bridge │  │          │  │          │  │   VXLAN overlay  │
└────────────────┘  └──────────┘  └──────────┘  └──────────────────┘

4.2 Docker Compose Networking

services:
  order-service:
    build: ./order-service
    ports:
      - "8080:8080"
    networks:
      - backend
    depends_on:
      postgres:
        condition: service_healthy

  payment-service:
    build: ./payment-service
    ports:
      - "8081:8080"
    networks:
      - backend

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: orders
      POSTGRES_USER: app
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 3s
      retries: 5

networks:
  backend:
    driver: bridge

volumes:
  pgdata:

secrets:
  db_password:
    file: ./secrets/db_password.txt

在同一網路中,服務透過服務名稱相互呼叫:

order-service → http://payment-service:8080/api/pay

5. Image Registry

5.1 帶有註冊表的 CI/CD 管道

Developer → Git Push → CI Pipeline:
  1. docker build -t registry.example.com/order-service:v1.2.3
  2. trivy image registry.example.com/order-service:v1.2.3
  3. docker push registry.example.com/order-service:v1.2.3
  4. Update K8s manifest → ArgoCD sync

Registry Options:
├── Docker Hub (public)
├── Harbor (self-hosted, recommended)
├── AWS ECR
├── Google Artifact Registry
└── GitHub Container Registry (ghcr.io)

5.2 Image Tagging Strategy

# ✅ Semantic versioning
registry.example.com/order-service:1.2.3
registry.example.com/order-service:1.2.3-alpine

# ✅ Git commit hash (immutable)
registry.example.com/order-service:abc123f

# ❌ KHÔNG dùng :latest trong production
registry.example.com/order-service:latest

6. 總結

ConceptKey Point
容器與虛擬機器容器更輕、啟動更快、密度更高
多階段建置分離建置與執行時間以減少映像大小
層緩存整理Dockerfile優化緩存
非 root 使用者始終使用非 root 使用者執行容器
影像掃描推播前掃描,屏蔽危險圖片
網路同一網路中的容器透過服務名稱互相呼叫

下一篇文章:Kubernetes 架構 — Kubernetes 如何編排數百個容器、自動擴展、自我修復和管理整個生命週期。