實戰部署:將應用部署到 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'
本日總結
在本章中,你學到了:
- ✅ 雲端 K8s 服務比較:GKE、DOKS、LKE 的優劣分析
- ✅ 建立雲端叢集:使用 DigitalOcean 建立生產級 K8s 叢集
- ✅ PersistentVolume:確保資料庫資料不遺失
- ✅ K8s Secret:安全地管理密碼與 API Key
- ✅ 三層式架構部署:資料庫、後端 API、前端的完整部署流程
- ✅ Ingress 與 TLS:設定自訂網域與自動 HTTPS
- ✅ 實用除錯技巧:describe、logs、exec、events
下一章,我們將學習如何監控你的 K8s 叢集,並設定自動伸縮策略!