应用部署与滚动更新
本文介绍了Kubernetes中实现零停机发布的滚动更新策略。通过一个Python Flask应用实战案例,详细讲解了Deployment控制器如何管理应用版本更新,包括ReplicaSet版本控制、滚动更新参数配置(maxSurge/maxUnavailable)以及多阶段构建优化镜像。文章提供了完整的项目结构、Flask应用代码示例、Dockerfile配置和Kubernetes资源定义,演示
目录
『宝藏代码胶囊开张啦!』—— 我的 CodeCapsule 来咯!✨写代码不再头疼!我的新站点 CodeCapsule 主打一个 “白菜价”+“量身定制”!无论是卡脖子的毕设/课设/文献复现,需要灵光一现的算法改进,还是想给项目加个“外挂”,这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网
应用部署与滚动更新:Kubernetes 零停机发布实战
一、引言:从传统部署到云原生发布的演进
在传统的应用部署中,发布新版本往往意味着停机维护——停止旧版本、部署新版本、重启服务,整个过程需要数分钟甚至更久。对于追求高可用的现代应用,停机就意味着用户体验下降、业务收入损失。而容器化与容器编排技术的出现,彻底改变了这一局面。
滚动更新(Rolling Update) 是 Kubernetes 提供的核心发布策略,它允许你逐步用新版本替换旧版本,在整个更新过程中始终保持应用在线,实现零停机发布。同时,Kubernetes 还提供了自动回滚、版本历史管理等机制,让发布变得安全可控。
然而,仅仅知道 kubectl set image 是不够的。如何配置更新策略以平衡速度和稳定性?如何监控更新过程?当更新失败时如何快速回滚?蓝绿部署、金丝雀发布与滚动更新的关系是什么?本文将从一个 Python Flask 应用 的实战出发,深入讲解 Kubernetes 中 Deployment 的滚动更新机制,并运用多阶段构建优化镜像体积,帮助你掌握生产级应用发布的完整流程。
二、核心概念:Deployment 与滚动更新机制
2.1 Deployment:声明式应用管理
Deployment 是 Kubernetes 中管理无状态应用的核心控制器。它通过 ReplicaSet 确保指定数量的 Pod 副本始终运行,并支持声明式的更新策略。当你想更新应用版本时,只需修改 Deployment 的 Pod 模板(如镜像标签),Deployment 会自动触发滚动更新。
2.2 ReplicaSet:版本快照
每次 Deployment 的 Pod 模板发生变化,都会创建一个新的 ReplicaSet,并保留旧的 ReplicaSet。这不仅实现了版本追踪,还让回滚变得简单——只需将 Deployment 的 spec 指回旧的 ReplicaSet。
2.3 滚动更新策略参数
滚动更新的行为由 strategy.rollingUpdate 下的两个参数控制:
- maxSurge:更新过程中,可以超出期望副本数的最大 Pod 数量。可以是绝对数(如 2)或百分比(如 25%)。默认 25%。
- maxUnavailable:更新过程中,可以不可用的最大 Pod 数量。同样可以是绝对数或百分比。默认 25%。
这两个参数共同决定了更新速度和可用性之间的平衡。例如,对于 10 个副本的应用,maxSurge=25% 允许最多额外创建 2 个新 Pod,maxUnavailable=25% 允许最多 2 个旧 Pod 提前终止。
2.4 更新状态与历史记录
kubectl rollout status 可以监控更新进度,kubectl rollout history 查看版本历史。每次更新都会记录 Revision,方便回滚。
三、实战演练:滚动更新 Python Flask 应用
本节将创建一个简单的 Flask 应用,通过两个版本(v1 和 v2)演示滚动更新的完整流程。
3.1 项目结构
rolling-update-demo/
├── app-v1/
│ ├── app.py # v1 版本 Flask 应用
│ ├── requirements.txt
│ ├── Dockerfile # 多阶段构建
│ └── .dockerignore
├── app-v2/ # v2 版本(仅修改返回消息)
│ ├── app.py
│ ├── requirements.txt
│ ├── Dockerfile
│ └── .dockerignore
├── k8s/
│ ├── namespace.yaml # 命名空间
│ ├── deployment.yaml # Deployment 定义
│ └── service.yaml # Service 暴露应用
└── README.md
3.2 Flask 应用代码
app-v1/app.py:
import os
import socket
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
hostname = socket.gethostname()
return jsonify({
'version': 'v1',
'message': 'Hello from v1',
'hostname': hostname
})
@app.route('/health')
def health():
return jsonify({'status': 'healthy'})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
app-v2/app.py(仅修改版本号):
import os
import socket
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
hostname = socket.gethostname()
return jsonify({
'version': 'v2',
'message': 'Hello from v2',
'hostname': hostname
})
@app.route('/health')
def health():
return jsonify({'status': 'healthy'})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
app-v1/requirements.txt 和 app-v2/requirements.txt 相同:
flask==2.3.3
gunicorn==21.2.0
3.3 多阶段构建 Dockerfile
app-v1/Dockerfile(v2 相同):
# 构建阶段
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# 运行阶段
FROM python:3.11-slim
# 创建非root用户
RUN addgroup --system --gid 1001 appgroup && \
adduser --system --uid 1001 --gid 1001 --no-create-home appuser
WORKDIR /app
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY . .
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 5000
HEALTHCHECK CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')" || exit 1
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"]
app-v1/.dockerignore:
__pycache__
*.pyc
.env
.git
README.md
Dockerfile
.dockerignore
3.4 构建并推送镜像
# 构建 v1 和 v2 镜像
docker build -t your-registry/rolling-demo:v1 ./app-v1
docker build -t your-registry/rolling-demo:v2 ./app-v2
# 推送到镜像仓库
docker push your-registry/rolling-demo:v1
docker push your-registry/rolling-demo:v2
3.5 Kubernetes 资源配置
k8s/namespace.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: rolling-demo
k8s/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: rolling-demo
namespace: rolling-demo
spec:
replicas: 4
selector:
matchLabels:
app: rolling-demo
# 滚动更新策略
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多额外创建1个新Pod
maxUnavailable: 0 # 不允许旧Pod不可用(保证零停机)
template:
metadata:
labels:
app: rolling-demo
spec:
containers:
- name: app
image: your-registry/rolling-demo:v1
ports:
- containerPort: 5000
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
readinessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 15
periodSeconds: 10
k8s/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: rolling-demo-service
namespace: rolling-demo
spec:
selector:
app: rolling-demo
ports:
- port: 80
targetPort: 5000
type: NodePort
3.6 部署 v1 版本
# 创建命名空间
kubectl apply -f k8s/namespace.yaml
# 部署 v1
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
# 查看状态
kubectl get pods -n rolling-demo -w
kubectl get svc -n rolling-demo
# 获取 NodePort
NODE_PORT=$(kubectl get svc rolling-demo-service -n rolling-demo -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}')
# 测试访问
curl http://${NODE_IP}:${NODE_PORT}/
3.7 执行滚动更新到 v2
在一个终端持续请求,观察是否中断:
while true; do curl -s http://${NODE_IP}:${NODE_PORT}/ | grep version; sleep 0.5; done
在另一个终端触发更新:
# 修改镜像版本为 v2
kubectl set image deployment/rolling-demo app=your-registry/rolling-demo:v2 -n rolling-demo
# 监控更新状态
kubectl rollout status deployment/rolling-demo -n rolling-demo
# 查看 ReplicaSet 历史
kubectl rollout history deployment/rolling-demo -n rolling-demo
观察持续请求的终端,你会看到版本从 v1 逐渐变成 v2,但整个过程没有出现请求失败(除极短暂的重试外,通常零停机)。
3.8 回滚到 v1
如果新版本有问题,可以快速回滚:
# 回滚到上一个版本
kubectl rollout undo deployment/rolling-demo -n rolling-demo
# 回滚到指定版本(通过 history 查看 revision)
kubectl rollout undo deployment/rolling-demo --to-revision=1 -n rolling-demo
四、进阶优化:滚动更新最佳实践
4.1 合理设置 maxSurge 和 maxUnavailable
| 场景 | maxSurge | maxUnavailable | 说明 |
|---|---|---|---|
| 零停机生产发布 | 1 (或 25%) | 0 | 确保任何时候都有足够的 Pod 处理流量 |
| 快速更新(可容忍少量中断) | 25% | 25% | 平衡速度与可用性 |
| 资源紧张环境 | 0 | 1 | 避免额外资源占用,但会有短暂容量下降 |
4.2 结合就绪探针(Readiness Probe)
滚动更新的核心是等待新 Pod 就绪后才终止旧 Pod。因此必须为容器配置正确的 readinessProbe,确保新 Pod 真正能够接收流量后才开始替换。
4.3 暂停与恢复更新
对于复杂更新(如需要手工验证的批次),可以暂停更新:
kubectl rollout pause deployment/rolling-demo -n rolling-demo
# 手工验证部分新 Pod
kubectl rollout resume deployment/rolling-demo -n rolling-demo
4.4 金丝雀发布(Canary Release)
滚动更新是自动的渐进式发布。但如果你想更精细地控制流量比例(如只让 10% 的流量流向新版本),可以结合 Service 和多个 Deployment 实现金丝雀发布:
或者使用 Service Mesh(如 Istio)实现更精细的流量控制。
4.5 蓝绿部署(Blue-Green Deployment)
蓝绿部署是另一种发布策略:同时运行两套环境(蓝和绿),通过切换 Service 的 selector 实现瞬间切换。Kubernetes 中可以简单实现:
# 部署绿色版本
kubectl apply -f deployment-green.yaml
# 待绿色版本就绪后,修改 Service 的 selector 指向绿色
kubectl patch service rolling-demo-service -p '{"spec":{"selector":{"app":"rolling-demo","version":"v2"}}}'
4.6 多阶段构建收益
与之前系列一致,多阶段构建将镜像体积从 400MB+ 优化至 82MB,加速拉取和启动,从而缩短滚动更新总时长。
| 构建方式 | 基础镜像 | 最终镜像大小 |
|---|---|---|
| 单阶段 | python:3.11 | 912 MB |
| 单阶段 slim | python:3.11-slim | 412 MB |
| 多阶段 | python:3.11-slim | 82 MB |
五、效果验证
5.1 零停机验证
在滚动更新过程中持续请求,可以看到响应中的 version 字段从 v1 逐渐变为 v2,但从未出现连接错误(如 curl: (52) Empty reply from server)。
5.2 滚动速度控制
观察 Pod 的变化:
kubectl get pods -n rolling-demo -w
你会看到新 Pod 逐个创建并变为 Running,旧 Pod 逐个 Terminating,新旧 Pod 会短暂共存。
5.3 回滚验证
模拟新版本故障(例如故意让 v2 的 /health 返回 500),然后观察滚动更新是否会卡住,并测试回滚功能。
六、完整代码
6.1 app-v1/app.py
(见上文)
6.2 app-v1/requirements.txt
flask==2.3.3
gunicorn==21.2.0
6.3 app-v1/Dockerfile
(见上文)
6.4 app-v1/.dockerignore
(见上文)
6.5 app-v2/ 对应文件(略)
6.6 k8s/namespace.yaml
(见上文)
6.7 k8s/deployment.yaml
(见上文)
6.8 k8s/service.yaml
(见上文)
七、总结与最佳实践清单
通过本文的实战,我们掌握了 Kubernetes 中 Deployment 的滚动更新机制,并通过一个 Python Flask 应用演示了零停机发布的全过程。以下是 应用部署与滚动更新的最佳实践清单:
- ✅ 配置就绪探针:确保新 Pod 真正就绪后才接收流量。
- ✅ 合理设置滚动更新参数:生产环境推荐
maxSurge=25%、maxUnavailable=0或maxSurge=1、maxUnavailable=0。 - ✅ 监控更新过程:使用
kubectl rollout status和事件监控。 - ✅ 保留版本历史:不要手动删除旧的 ReplicaSet,以便快速回滚。
- ✅ 回滚准备:确保回滚操作已经演练,关键应用可考虑蓝绿部署。
- ✅ 结合金丝雀发布:对于重大变更,先让少量流量验证。
- ✅ 镜像优化:多阶段构建减小体积,加速拉取和启动。
- ✅ 资源限制:设置 requests 和 limits,保证更新过程集群稳定。
滚动更新是 Kubernetes 保障服务连续性的基石。掌握这一技能后,你可以自信地将应用的发布流程自动化,实现快速迭代与稳定运行的完美平衡。
更多推荐



所有评论(0)