前端应用K8s部署全流程
这是模块 05 最实用的一篇:从 0 把一个前端项目跑到 K8s 集群上,配齐 HTTPS、自动扩缩容、滚动更新。
1. 准备工作
假设:
- 已有 K8s 集群(阿里 ACK / AWS EKS / 本地 minikube / kind)
- kubectl 已配置
- 镜像仓库可用(ghcr.io / ACR / Harbor)
- 已有域名和证书
2. 项目结构
my-frontend/
├── src/
├── Dockerfile
├── nginx.conf
├── k8s/
│ ├── namespace.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ └── configmap.yaml
└── .github/
└── workflows/
└── deploy.yaml
3. Dockerfile(SPA)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost/health || exit 1
nginx.conf:
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|woff2?|png|jpg|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location = /health {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
}
4. K8s 资源清单
4.1 Namespace
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: frontend
4.2 Deployment
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-frontend
namespace: frontend
labels:
app: my-frontend
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: my-frontend
template:
metadata:
labels:
app: my-frontend
spec:
containers:
- name: web
image: ghcr.io/myorg/my-frontend:v1.0.0
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 15
periodSeconds: 20
securityContext:
runAsNonRoot: true
runAsUser: 101 # nginx 用户
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: my-frontend
关键点:
maxUnavailable: 0保证滚动更新不中断resources必须设,否则调度不确定、HPA 不工作readinessProbe通过才接流量topologySpreadConstraints分散到不同节点
4.3 Service
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-frontend
namespace: frontend
spec:
selector:
app: my-frontend
ports:
- port: 80
targetPort: 80
type: ClusterIP
4.4 Ingress
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-frontend
namespace: frontend
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: my-frontend-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-frontend
port:
number: 80
需要集群装 Ingress Controller(nginx-ingress 或 traefik)和 cert-manager。
4.5 HPA
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-frontend
namespace: frontend
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-frontend
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 100
periodSeconds: 30
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
5. 部署命令
# 首次部署
kubectl apply -f k8s/
# 更新镜像
kubectl set image deployment/my-frontend web=ghcr.io/myorg/my-frontend:v1.1.0 -n frontend
# 看滚动状态
kubectl rollout status deployment/my-frontend -n frontend
# 回滚
kubectl rollout undo deployment/my-frontend -n frontend
# 回滚到特定版本
kubectl rollout history deployment/my-frontend -n frontend
kubectl rollout undo deployment/my-frontend --to-revision=3 -n frontend
6. CI/CD 自动部署
# .github/workflows/deploy.yaml
name: Deploy
on:
push:
tags: ['v*']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: $}} github.actor }}
password: $}} secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/$}} github.repository }}:$}} github.ref_name }}
build-args: |
NEXT_PUBLIC_API_URL=$}} vars.API_URL }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Deploy to K8s
uses: azure/k8s-set-context@v4
with:
kubeconfig: $}} secrets.KUBECONFIG }}
- name: Update image
run: |
kubectl set image deployment/my-frontend \
web=ghcr.io/$}} github.repository }}:$}} github.ref_name }} \
-n frontend
kubectl rollout status deployment/my-frontend -n frontend --timeout=300s
7. 排障
# Pod 起不来
kubectl describe pod <pod> -n frontend
# 看 Events 段:拉镜像失败、探针失败、资源不足
# 看日志
kubectl logs <pod> -n frontend
kubectl logs <pod> -n frontend --previous # 上次崩的日志
# 进容器
kubectl exec -it <pod> -n frontend -- sh
# 网络不通
kubectl get svc,ingress -n frontend
kubectl run debug --rm -it --image=alpine -- sh
# 容器内 wget http://my-frontend.frontend/health
# 端口转发
kubectl port-forward svc/my-frontend 8080:80 -n frontend
# 浏览器打开 localhost:8080
8. 生产运维关键操作
# 手动扩缩
kubectl scale deployment/my-frontend --replicas=10 -n frontend
# 重启(不改镜像,用于读新 ConfigMap)
kubectl rollout restart deployment/my-frontend -n frontend
# 暂停/继续 rollout(金丝雀手动控制)
kubectl rollout pause deployment/my-frontend -n frontend
kubectl rollout resume deployment/my-frontend -n frontend
# 看资源使用
kubectl top pods -n frontend
kubectl top nodes
9. 常见反模式
- 不设 resources:Pod 被随意调度 + HPA 不工作
- 不设 readinessProbe:Pod 起来但应用还没 ready 就接流量 → 5xx
- 只 1 个 replica:节点挂了服务中断
maxUnavailable: 1+ 2 副本:更新时 50% 不可用- 镜像用
latest:kubectl set image 无效(tag 没变不重拉)。用具体 tag +imagePullPolicy: Always - Secret 明文存 git:用 sealed-secrets / external-secrets-operator
- 不做 topologySpread:所有 Pod 在同一节点,节点挂全挂
- HPA minReplicas=1:缩容到 1 后无容灾
10. 延伸阅读
- Kubernetes 官方教程
- Kubernetes Patterns — 设计模式
- cert-manager 文档
- Ingress-Nginx Controller
- 模块 06 CI/CD 中的 K8s 部署策略