cover

Kubernetes 入門:管理整個港口的系統

如果 Docker 是一個集裝箱,Kubernetes 就是管理整個港口的系統。

Container RuntimeDocker Compose 實戰模板 裡,我們用 Docker Compose 把多個容器組合在一起,一台機器上就能跑起完整的服務。這對小團隊、少量服務的場景完全夠用。但當你開始面對這些問題——「服務掛了能不能自動重啟?」「流量變大怎麼自動擴展?」「怎麼在不中斷服務的情況下更新版本?」「多台機器怎麼協調?」——Docker Compose 就力不從心了。Kubernetes(簡稱 K8s)就是來解決這些問題的。


為什麼需要 K8s?

單機 Docker(即使搭配 Compose)在生產環境會遇到幾個根本性的瓶頸:

flowchart LR
    subgraph SingleHost["單機 Docker 的瓶頸"]
        P1["手動 Scaling<br/>流量大了只能手動加容器"]
        P2["No Self-healing<br/>容器掛了要人工介入"]
        P3["No Rolling Update<br/>更新必須停機"]
        P4["單點故障<br/>Host 掛了全部服務掛"]
    end

    subgraph K8sSolves["K8s 解決方案"]
        S1["Horizontal Pod Autoscaler<br/>根據 CPU/Memory 自動擴縮"]
        S2["自動重啟 + 自動遷移<br/>Pod 掛了自動在其他 Node 重建"]
        S3["Rolling Update<br/>逐步替換 Pod,零停機"]
        S4["多 Node 叢集<br/>任一 Node 掛了,服務自動遷移"]
    end

    P1 --> S1
    P2 --> S2
    P3 --> S3
    P4 --> S4

    style SingleHost fill:#fce4ec,stroke:#c62828
    style K8sSolves fill:#e8f5e9,stroke:#388e3c

Docker Compose 的 restart: unless-stopped 只能在同一台機器上重啟容器。如果那台機器本身掛了呢?如果你需要根據流量自動從 2 個實例擴展到 10 個呢?如果你要在不中斷任何一個請求的情況下從 v1.2 更新到 v1.3 呢?這些都超出了 Docker Compose 的設計範圍。


核心概念

K8s 的概念可以從外到內、從大到小來理解:

flowchart TD
    subgraph Cluster["Cluster(叢集)"]
        subgraph CP["Control Plane"]
            API[API Server]
            Sched[Scheduler]
            CM[Controller Manager]
            Etcd[(etcd)]
        end

        subgraph Node1["Node 1 (Worker)"]
            subgraph NS1["Namespace: production"]
                subgraph Deploy1["Deployment: api"]
                    Pod1A["Pod (api-v2-abc)"]
                    Pod1B["Pod (api-v2-def)"]
                end
                Svc1["Service: api-svc<br/>ClusterIP"]
            end
            Kubelet1[kubelet]
        end

        subgraph Node2["Node 2 (Worker)"]
            subgraph NS2["Namespace: production"]
                subgraph Deploy2["Deployment: worker"]
                    Pod2A["Pod (worker-xyz)"]
                end
            end
            Kubelet2[kubelet]
        end

        Ingress["Ingress Controller<br/>外部流量入口"] --> Svc1
        Svc1 --> Pod1A
        Svc1 --> Pod1B
    end

    API --> Kubelet1
    API --> Kubelet2

    style CP fill:#e3f2fd,stroke:#1976d2
    style NS1 fill:#fff3e0,stroke:#f57c00
    style NS2 fill:#fff3e0,stroke:#f57c00
    style Deploy1 fill:#e8f5e9,stroke:#388e3c
    style Deploy2 fill:#e8f5e9,stroke:#388e3c

核心元件解說:

  • Cluster:整個 K8s 環境,由一組 Node 組成。
  • Node:叢集中的一台機器(虛擬機或實體機)。分為 Control Plane(管理層)和 Worker Node(工作層)。
  • Pod:K8s 最小的部署單位。一個 Pod 裡通常跑一個容器(少數情況會有 sidecar 容器)。Pod 是短暫的,隨時可能被銷毀和重建,不要把它當成 VM 來用。
  • Deployment:宣告「我要跑幾個 Pod、用什麼映像檔、怎麼更新」。Deployment 管理 Pod 的生命週期,處理 rolling update 和 rollback。
  • Service:為一組 Pod 提供穩定的網路入口。Pod 的 IP 會變,但 Service 的 ClusterIP 和 DNS 名稱不會變。
  • Ingress:管理外部 HTTP/HTTPS 流量如何路由到內部 Service。類似 Nginx 反向代理,但由 K8s 統一管理。
  • ConfigMap / Secret:存放設定檔和敏感資料,注入到 Pod 裡。對應 Secrets & Config 的概念。
  • Namespace:邏輯隔離。把不同環境(dev / staging / production)或不同團隊的資源分開。

從 Docker Compose 到 K8s 的對照表

如果你已經熟悉 Docker Compose,這張表幫助你快速對應 K8s 的概念:

Docker ComposeKubernetes用途
docker-compose.ymlDeployment + Service定義服務和網路
services.app.imageDeployment.spec.containers.image指定容器映像
ports: "8080:80"Service(NodePort / LoadBalancer)暴露端口
volumesPersistentVolumeClaim(PVC)持久存儲
environmentConfigMap / Secret環境變數
depends_on無直接對應(用 initContainers)啟動順序
restart: unless-stoppedPod restartPolicy + Deployment自動重啟
deploy.replicasDeployment.spec.replicas副本數量
networksNetworkPolicy網路隔離
logging由 Node 層級的 log agent 處理日誌收集

最關鍵的心智模型轉變:Docker Compose 是「命令式」——你告訴 Docker 要跑什麼、怎麼跑。K8s 是「宣告式」——你描述你想要的狀態(desired state),K8s 自己想辦法達成並持續維持。


基本 K8s 清單範本

以下用一個簡單的 Web API 為例,展示最常用的 K8s 資源清單。

Deployment

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: production
  labels:
    app: api
    version: v1.2.3
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 更新時最多多出 1 個 Pod
      maxUnavailable: 0   # 更新時不允許有 Pod 不可用
  template:
    metadata:
      labels:
        app: api
        version: v1.2.3
    spec:
      containers:
        - name: api
          image: registry.example.com/myapp/api:v1.2.3
          ports:
            - containerPort: 8000
          envFrom:
            - configMapRef:
                name: api-config
            - secretRef:
                name: api-secrets
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10

注意 readinessProbelivenessProbe 的差別:readiness 決定 Pod 是否接收流量(不 ready 就從 Service 移除),liveness 決定 Pod 是否存活(不 live 就重啟)。這比 Docker 的單一 health check 更細緻。

Service

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: api-svc
  namespace: production
spec:
  selector:
    app: api
  ports:
    - protocol: TCP
      port: 80          # Service 對外的 port
      targetPort: 8000  # 轉發到 Pod 的 port
  type: ClusterIP       # 只在叢集內部可存取

Service 透過 selector 找到所有 label 為 app: api 的 Pod,自動做負載均衡。Pod 被替換了,Service 自動更新後端清單。

Ingress

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls-cert
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 80

Ingress 處理 TLS 終結和路由,對應 Reverse Proxy + TLS 的角色。搭配 cert-manager 可以自動管理 Let’s Encrypt 憑證(參考 Secrets 管理與憑證生命週期)。

ConfigMap

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
  namespace: production
data:
  APP_ENV: production
  LOG_LEVEL: info
  DATABASE_HOST: postgres-svc
  REDIS_HOST: redis-svc

敏感資料(密碼、API Key)用 Secret,非敏感設定用 ConfigMap。修改 ConfigMap 後,需要重啟 Pod 才能吃到新值(除非應用程式有 watch config 的機制)。


K8s 生態工具

K8s 本身只是核心平台,真正發揮威力需要搭配生態工具:

flowchart TD
    subgraph Core["K8s Core"]
        K8s[Kubernetes API]
    end

    subgraph PackageMgmt["套件管理"]
        Helm["Helm<br/>K8s 的 apt/yum<br/>用 Chart 打包部署"]
        Kustomize["Kustomize<br/>用 overlay 管理多環境"]
    end

    subgraph GitOps["GitOps 部署"]
        Argo["ArgoCD<br/>Git repo 自動同步到叢集"]
        Flux["Flux<br/>另一個 GitOps 方案"]
    end

    subgraph GUI["GUI 管理"]
        Lens["Lens<br/>桌面端 K8s IDE"]
        K9s["k9s<br/>終端機 UI"]
        Dashboard["K8s Dashboard<br/>官方 Web UI"]
    end

    subgraph Monitoring["監控"]
        PromOp["Prometheus Operator<br/>一鍵部署監控 Stack"]
    end

    K8s --> PackageMgmt
    K8s --> GitOps
    K8s --> GUI
    K8s --> Monitoring

    style Core fill:#e3f2fd,stroke:#1976d2
    style PackageMgmt fill:#e8f5e9,stroke:#388e3c
    style GitOps fill:#fff3e0,stroke:#f57c00
    style GUI fill:#fce4ec,stroke:#c62828
    style Monitoring fill:#f3e5f5,stroke:#7b1fa2
  • Helm:K8s 的套件管理工具。把一組相關的 K8s 清單(Deployment、Service、ConfigMap 等)打包成 Chart,可以用一行命令安裝。社群有大量現成的 Chart(PostgreSQL、Redis、Prometheus、Grafana),不需要自己寫 YAML。Helm 也支援 values.yaml 做參數化,同一套 Chart 可以部署到不同環境。
  • ArgoCD:GitOps 工具。把 K8s 清單放在 Git repo 裡,ArgoCD 監聽 repo 變更,自動同步到叢集。「Git 就是唯一的事實來源(single source of truth)」。搭配 CDIaC 的理念,所有變更都有版本紀錄和 code review。
  • Lens / k9s:K8s 的管理工具。Lens 是桌面 GUI,k9s 是終端機 UI,都能查看 Pod 狀態、即時日誌、exec 進容器、管理資源。比 kubectl 直接打指令效率高很多,特別是在排查問題時。

什麼時候該上 K8s?什麼時候不該?

這是最重要的一節。K8s 是強大的工具,但它的複雜度也是真實的。不要因為「大家都在用」就上 K8s。

flowchart TD
    Start["你的場景"] --> Q1{"服務數量?"}
    Q1 -->|"1-5 個"| Q2{"需要 auto-scaling?"}
    Q1 -->|"10+ 個"| K8s["考慮 K8s"]

    Q2 -->|"不需要"| Q3{"需要 HA?"}
    Q2 -->|"需要"| K8s

    Q3 -->|"不需要"| Compose["Docker Compose<br/>+ Portainer 就夠了"]
    Q3 -->|"需要"| Q4{"團隊有 K8s 經驗?"}

    Q4 -->|"有"| K8s
    Q4 -->|"沒有"| Managed["用 Managed K8s<br/>GKE / EKS / AKS"]

    style Compose fill:#e8f5e9,stroke:#388e3c
    style K8s fill:#e3f2fd,stroke:#1976d2
    style Managed fill:#fff3e0,stroke:#f57c00

適合 Docker Compose + Portainer 的場景:

  • 小團隊(1-5 人),少量服務(1-5 個)
  • 流量穩定,不需要自動擴展
  • 單機或少量機器就能扛住
  • 沒有專職的 DevOps/SRE
  • 個人專案、Side project

適合考慮 K8s 的場景:

  • 微服務架構,10 個以上的服務
  • 需要根據流量自動擴縮(auto-scaling)
  • 需要高可用(HA),任何單點故障都不能影響服務
  • 需要零停機部署(rolling update / canary / blue-green)
  • 有專職的 DevOps/SRE 或願意投入學習成本
  • 多團隊共用叢集,需要資源隔離和配額管理

折衷方案:

  • 如果需要 K8s 但團隊沒有經驗,用雲端的 Managed K8s(GKE、EKS、AKS)。Control Plane 由雲端廠商管理,你只管 Worker Node 和應用部署。省去了維護 etcd、API Server 等核心元件的負擔。
  • Docker Swarm 是另一個選項——比 K8s 簡單很多,支援多機部署和基本的 service discovery。但社群活躍度和生態遠不如 K8s,不建議在新專案中採用。

一個誠實的建議:如果你需要花 30 分鐘以上的時間解釋為什麼需要 K8s,那你大概不需要 K8s。Docker Compose + 良好的 CI/CD + 監控,已經能搞定大部分中小型團隊的需求。K8s 的價值在規模化之後才真正顯現。


延伸閱讀