實戰部署:將應用部署到 Kubernetes

到目前為止,我們已經學會了如何使用 Docker 容器化應用、使用 Docker Compose 管理多服務、以及在本機使用 kind 建立 K8s 測試叢集。

現在,讓我們進入真正的重頭戲——將應用部署到雲端 Kubernetes 叢集上

選擇雲端 K8s 服務

市面上有許多雲端服務商提供 Kubernetes 叢集管理服務(Managed K8s),以下是最常見的幾種:

| 服務商 | 服務名稱 | 價格 | 適合對象 | |--------|---------|------|---------| | Google Cloud | GKE (Google Kubernetes Engine) | 每個叢集 $0.10/小時 | 中大型專案 | | DigitalOcean | DOKS (DigitalOcean K8s) | 無管理費,只付節點費 | 個人開發者、接案 🏆 | | Linode / Akamai | LKE | 無管理費,只付節點費 | 個人開發者 | | Azure | AKS | 無管理費,只付節點費 | 企業專案 | | AWS | EKS | 每個叢集 $0.10/小時 | 企業專案 |

💡 個人開發者首選:DigitalOcean K8s 對於接案開發者或個人創業者,DigitalOcean 的 K8s 服務 (DOKS) 是最具成本效益的選擇。管理費完全免費,你只需要支付節點(伺服器)的費用。最小的節點方案(2 核心、2 GB 記憶體)每月只需 $12 美元,非常適合個人專案。

實戰目標

我們將把前一章的三層式應用(PostgreSQL + FastAPI + Next.js)部署到雲端 K8s 上,並設定自訂網域與 TLS 憑證(HTTPS)。

完整部署架構

使用者
    │
    ▼
[Ingress]  ─── 網域 myapp.com,自動 HTTPS (Let's Encrypt)
    │
    ▼
[Service: frontend]  (LoadBalancer / ClusterIP)
    │
    ▼
[Pod: frontend]  ─── Next.js 前端 (3 個副本)
    │
    ▼
[Service: backend]  (ClusterIP,僅叢集內部可存取)
    │
    ▼
[Pod: backend]  ─── FastAPI 後端 (3 個副本,自動伸縮)
    │
    ▼
[Service: postgres]  (ClusterIP)
    │
    ▼
[Pod: postgres]  ─── PostgreSQL 資料庫 (1 個副本,使用 PersistentVolume)

第一步:建立雲端 K8s 叢集

我們以 DigitalOcean 為例(因為它最簡單且最便宜):

使用 DigitalOcean CLI 建立叢集

# 安裝 doctl (DigitalOcean CLI)
brew install doctl

# 認證
doctl auth init

# 建立 K8s 叢集(3 個節點,最小規格)
doctl kubernetes cluster create my-cluster \
  --region sgp1 \
  --size s-2vcpu-2gb \
  --count 3 \
  --version latest

過程大約需要 5-10 分鐘。完成後,doctl 會自動更新你的 kubeconfig,讓 kubectl 可以直接連線到這個新叢集。

驗證連線

# 確認 kubectl 已切換到新的叢集
kubectl config current-context

# 檢視節點
kubectl get nodes

# 輸出範例
NAME                   STATUS   ROLES    AGE   VERSION
pool-abcde-12345       Ready    <none>   5m    v1.28.3
pool-abcde-67890       Ready    <none>   5m    v1.28.3
pool-abcde-11111       Ready    <none>   5m    v1.28.3

第二步:部署資料庫

在正式環境中,我們需要確保資料庫的資料不會因為 Pod 重啟而消失。因此我們需要使用 PersistentVolume 來儲存資料:

# postgres-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          env:
            - name: POSTGRES_USER
              value: "myapp"
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
            - name: POSTGRES_DB
              value: "myapp"
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              cpu: "250m"
              memory: "512Mi"
            limits:
              cpu: "500m"
              memory: "1Gi"
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432
  type: ClusterIP

建立資料庫密碼 Secret

在 Kubernetes 中,機敏資料(如密碼、API Key)應該用 Secret 來管理,而不是直接寫在 YAML 中:

# 建立 Secret(密碼為 myapp-secret-password)
kubectl create secret generic db-secret \
  --from-literal=password=myapp-secret-password

第三步:部署後端 API

後端 API 需要連線到資料庫。K8s 會透過 Service 名稱 http://postgres:5432 來連線:

# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: your-registry/backend:latest
          ports:
            - containerPort: 8000
          env:
            - name: DATABASE_URL
              value: "postgresql://myapp:myapp-secret-password@postgres:5432/myapp"
          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 10
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  selector:
    app: backend
  ports:
    - port: 8000
      targetPort: 8000
  type: ClusterIP

第四步:部署前端

前端需要能夠從瀏覽器存取,所以使用 LoadBalancer 類型的 Service:

# frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: frontend
          image: your-registry/frontend:latest
          ports:
            - containerPort: 3000
          env:
            - name: NEXT_PUBLIC_API_URL
              value: "http://backend:8000"
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 3000
  type: LoadBalancer

一次部署所有資源

# 部署資料庫
kubectl apply -f postgres-pvc.yaml

# 部署後端
kubectl apply -f backend-deployment.yaml

# 部署前端
kubectl apply -f frontend-deployment.yaml

# 查看所有資源
kubectl get pods,svc,deployment

第五步:設定 Ingress 與 TLS

LoadBalancer 只能處理單一服務,但我們有多個服務(前端、後端管理介面等)。這時候我們需要使用 Ingress 來根據域名或路徑進行路由。

安裝 Ingress Controller

不同的雲端服務商有不同的 Ingress Controller。我們使用最普及的 nginx-ingress

# 使用 Helm 安裝 (Helm 是 K8s 的套件管理工具)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install nginx-ingress ingress-nginx/ingress-nginx

設定 Ingress 規則

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - myapp.com
      secretName: myapp-tls
  rules:
    - host: myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: backend
                port:
                  number: 8000

自動 HTTPS 憑證 (cert-manager)

為了讓網站擁有 HTTPS,我們可以使用 cert-manager 來自動申請 Let's Encrypt 憑證:

# 安裝 cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

# 建立 ClusterIssuer
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            class: nginx
EOF

設定完成後,當你建立 Ingress 並加上 cert-manager.io/cluster-issuer: "letsencrypt-prod" 註解時,cert-manager 會自動向 Let's Encrypt 申請憑證並儲存在 Secret 中。

實用 kubectl 除錯指令

部署過程中難免會遇到問題,以下是常用除錯指令:

# 查看 Pod 詳細狀態(包含錯誤訊息)
kubectl describe pod <pod-name>

# 查看 Pod 即時日誌
kubectl logs -f <pod-name>

# 查看特定標籤的所有 Pod 日誌
kubectl logs -f -l app=backend

# 進入 Pod 內部除錯
kubectl exec -it <pod-name> -- sh

# 查看 Service 的 Endpoints(是否正確連接到 Pod)
kubectl get endpoints <service-name>

# 查看叢集事件(了解 K8s 內部發生了什麼)
kubectl get events --sort-by='.lastTimestamp'

本日總結

在本章中,你學到了:

  1. 雲端 K8s 服務比較:GKE、DOKS、LKE 的優劣分析
  2. 建立雲端叢集:使用 DigitalOcean 建立生產級 K8s 叢集
  3. PersistentVolume:確保資料庫資料不遺失
  4. K8s Secret:安全地管理密碼與 API Key
  5. 三層式架構部署:資料庫、後端 API、前端的完整部署流程
  6. Ingress 與 TLS:設定自訂網域與自動 HTTPS
  7. 實用除錯技巧:describe、logs、exec、events

下一章,我們將學習如何監控你的 K8s 叢集,並設定自動伸縮策略!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!