IBM Kubernetes 上的 Strangler Fig:在不影响生产的情况下现代化单体应用
I’m happy to help translate the article, but I don’t have the full text of the post. Could you please paste the article’s content here (excluding the source line you already provided)? Once I have the text, I’ll translate it into Simplified Chinese while preserving the formatting, markdown, and any code blocks or URLs.
在本指南结束时,您将能够:
- 将现有的单体应用容器化
- 将其部署到 IBM Cloud Kubernetes Service
- 将其放置在 Ingress 后面
- 部署新的“边缘”服务
- 使用基于路径的路由逐步切换流量
- 保持回滚简单安全
先决条件
| 项目 | 详情 |
|---|---|
| IBM Cloud 账户 | – |
| 现有 IKS 集群 | – |
| 本地工具 | ibmcloud, kubectl, docker |
| 登录 | bash<br>ibmcloud login -a https://cloud.ibm.com<br>ibmcloud target -r <region> -g <resource-group><br> |
1. 设置一个干净的命名空间
kubectl create namespace monolith-demo
kubectl config set-context --current --namespace=monolith-demo
kubectl get ns
Goal: No behavior change – just package the monolith. (目标:不改变行为——仅对单体进行打包。)
2. 将单体容器化
Dockerfile(Node.js 单体)
# ---- Build stage ----
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# ---- Runtime stage ----
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app /app
EXPOSE 8080
CMD ["npm","start"]
添加最小健康端点(如果尚未拥有)
// Example endpoints
app.get("/health", (req, res) => res.status(200).send("ok"));
app.get("/ready", (req, res) => res.status(200).send("ready"));
构建镜像
docker build -t monolith:1.0.0 .
推送到 IBM Cloud 容器注册表
# Log in (one‑time)
ibmcloud cr login
ibmcloud cr namespace-add <your-namespace>
# Tag & push
docker tag monolith:1.0.0 <registry>/<namespace>/monolith:1.0.0
docker push <registry>/<namespace>/monolith:1.0.0
# Verify
ibmcloud cr images | grep monolith
3. 部署单体
3.1 部署清单 (deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: monolith
spec:
replicas: 2
selector:
matchLabels:
app: monolith
template:
metadata:
labels:
app: monolith
spec:
containers:
- name: monolith
image: <registry>/<namespace>/monolith:1.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
kubectl apply -f deployment.yaml
kubectl rollout status deploy/monolith
kubectl get pods -l app=monolith
3.2 Service 清单 (service.yaml)
apiVersion: v1
kind: Service
metadata:
name: monolith-svc
spec:
selector:
app: monolith
ports:
- name: http
port: 80
targetPort: 8080
type: ClusterIP
kubectl apply -f service.yaml
kubectl get svc monolith-svc
快速本地测试
kubectl port-forward svc/monolith-svc 8080:80
curl -i http://localhost:8080/health
4. 通过 Ingress 暴露(路由控制平面)
Ingress 清单 (ingress.yaml)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: <your-host>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: monolith-svc
port:
number: 80
kubectl apply -f ingress.yaml
kubectl get ingress app-ingress -o wide
此时: 100 % 的流量仍然进入单体应用。
5. 选择一个低风险的首个切片进行“绞杀”
好的首选候选:/api/auth/*。
在本演练中,我们将提取 auth API。
最小示例端点(添加到单体)
app.get("/api/auth/ping", (req, res) => {
res.json({ service: "auth-service", status: "pong" });
});
6. 构建新的 Auth 服务
Dockerfile (Dockerfile)
FROM node:20-alpine
WORKDIR /app
COPY . .
EXPOSE 8081
CMD ["node","server.js"]
构建并推送
docker build -t auth-service:1.0.0 .
docker tag auth-service:1.0.0 <registry>/<namespace>/auth-service:1.0.0
docker push <registry>/<namespace>/auth-service:1.0.0
7. 部署 Auth 服务
7.1 部署清单 (auth-deploy.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
spec:
replicas: 2
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
spec:
containers:
- name: auth-service
image: <registry>/<namespace>/auth-service:1.0.0
ports:
- containerPort: 8081
readinessProbe:
httpGet:
path: /ready
port: 8081
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8081
initialDelaySeconds: 15
periodSeconds: 10
kubectl apply -f auth-deploy.yaml
kubectl rollout status deploy/auth-service
kubectl get pods -l app=auth-service
7.2 服务清单 (auth-svc.yaml)
apiVersion: v1
kind: Service
metadata:
name: auth-svc
spec:
selector:
app: auth-service
ports:
- name: http
port: 80
targetPort: 8081
type: ClusterIP
kubectl apply -f auth-svc.yaml
kubectl get svc auth-svc
8. 更新 Ingress 以将 /api/auth/* 路由到新服务
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: <your-host>
http:
paths:
# New route for auth
- path: /api/auth
pathType: Prefix
backend:
service:
name: auth-svc
port:
number: 80
# Fallback to monolith for everything else
- path: /
pathType: Prefix
backend:
service:
name: monolith-svc
port:
number: 80
kubectl apply -f ingress.yaml
kubectl get ingress app-ingress -o wide
现在,对 /api/auth/* 的流量由新的 auth‑service 提供服务,而所有其他请求仍然会命中 monolith。
9. 渐进式发布与回滚
-
验证新端点
curl -i https://<your-host>/api/auth/ping -
增加流量(如果使用流量拆分控制器)或直接监控新服务的指标和日志。
-
回滚(如有需要)
# Remove the auth path from the Ingress kubectl edit ingress app-ingress # delete the /api/auth block # Or delete the auth deployment/service kubectl delete -f auth-deploy.yaml kubectl delete -f auth-svc.yaml
因为单体应用在 Ingress 后保持不变,你可以随时立即恢复。
10. 清理(当你准备好时)
kubectl delete -f ingress.yaml
kubectl delete -f service.yaml
kubectl delete -f deployment.yaml
kubectl delete namespace monolith-demo
🎉 您刚刚在真实的 IBM Cloud Kubernetes 环境中应用了 Strangler Fig 模式!
继续使用相同的方法提取更多功能切片(例如 /api/orders/*、/api/payments/*),直至可以安全地退役单体应用。
保持回滚简单且快速
选项 A – 路由回单体
编辑 Ingress 并移除 /api/auth 路径(或将其指向 monolith-svc),然后重新应用:
kubectl apply -f ingress.yaml
选项 B – 撤销部署滚动更新
kubectl rollout undo deploy/auth-service
过程
- 稳定首个提取的能力
- 选择下一个受限域
- 将其构建为独立服务
- 部署它
- 使用 Ingress 进行路由
- 在每一步保持回滚可用
随着时间的推移
- 单体系统逐渐缩小。
- 现代化变成日常工作,而不是“一次性大迁移”。
- 没有停机时间。
Strangler Fig 模式 能够奏效,因为它尊重现实——你可以在不删除过去的情况下进行现代化。
如果你现在仍在使用单体系统,这种方法可以让你在不破坏已有功能的前提下前进。