跳转到内容

更新网站内容

业务场景

云咖啡公司推出了新品咖啡,需要更新官网内容,展示新品信息。作为运维工程师,你需要在不影响用户访问的情况下完成网站更新。

需求:

  • 更新网站内容,添加新品咖啡信息
  • 确保更新过程中服务不中断
  • 配置健康检查,确保服务可用性
  • 设置资源限制,防止资源耗尽

学习目标

完成本课程后,你将掌握:

  • Deployment 的滚动更新机制
  • 如何实现零停机部署
  • 健康检查(LivenessProbe 和 ReadinessProbe)
  • 资源限制(requests 和 limits)
  • 版本回滚操作

前置准备

1. 确认上一课程的环境

bash
# 检查命名空间是否存在
kubectl get namespace cloud-cafe

# 检查 Deployment 是否存在
kubectl get deployment -n cloud-cafe

# 检查 Service 是否存在
kubectl get svc -n cloud-cafe

如果资源不存在,请先完成 01-云咖啡官网部署.md 课程。

2. 获取当前访问地址

bash
# 获取 NodePort 端口号
NODE_PORT=$(kubectl get svc cloud-cafe-nodeport -n cloud-cafe -o jsonpath='{.spec.ports[0].nodePort}')

# 获取节点 IP
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')

echo "当前访问地址: http://$NODE_IP:$NODE_PORT"

实战步骤

Step 1: 理解滚动更新机制

概念: Deployment 的滚动更新会逐步替换旧版本的 Pod,确保在更新过程中始终有可用的 Pod 在运行。

让我们先查看当前的 Deployment 状态:

bash
# 查看 Deployment 详情
kubectl describe deployment cloud-cafe-web -n cloud-cafe

# 查看 ReplicaSet(每个版本对应一个 ReplicaSet)
kubectl get rs -n cloud-cafe

# 查看 Pod
kubectl get pods -n cloud-cafe

观察要点:

  • 当前有一个 ReplicaSet
  • Pod 的标签包含 pod-template-hash,用于标识版本
  • Deployment 的 REVISION 字段显示当前版本号

Step 2: 准备新版本的网站内容

云咖啡公司推出了新品"焦糖玛奇朵",我们需要更新网站内容:

📌 ConfigMap 更新策略

生产环境中有两种更新 ConfigMap 的策略:

  1. 直接更新同名 ConfigMap - 更新配置后,重新加载 Pod(可能需要重启)
  2. 创建新的 ConfigMap 并更新 Deployment 引用 - 通过滚动更新实现零停机切换

本课程使用第二种策略,通过更新 Deployment 的 ConfigMap 引用来触发滚动更新。

首先,创建新的 index.html 文件(新品展示页面):

点击查看 index.html 内容(新品展示页面)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>云咖啡公司 - 新品上市</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      padding: 50px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
    }
    h1 { font-size: 48px; margin-bottom: 20px; }
    p { font-size: 24px; margin-bottom: 30px; }
    .coffee { font-size: 80px; margin: 30px 0; }
    .new-product {
      background: rgba(255, 255, 255, 0.2);
      border-radius: 15px;
      padding: 30px;
      margin: 30px auto;
      max-width: 600px;
    }
    .badge {
      background: #ff6b6b;
      color: white;
      padding: 5px 15px;
      border-radius: 20px;
      font-size: 14px;
      display: inline-block;
      margin-bottom: 15px;
    }
  </style>
</head>
<body>
  <div class="coffee">☕</div>
  <h1>欢迎来到云咖啡公司</h1>
  <p>用心制作每一杯咖啡</p>
  <div class="new-product">
    <span class="badge">新品上市</span>
    <h2>🍯 焦糖玛奇朵</h2>
    <p>香浓焦糖与绵密奶泡的完美融合</p>
    <p>限时特惠:¥28</p>
  </div>
  <p>我们的咖啡豆来自世界各地,经过精心烘焙</p>
  <p>为您提供最优质的咖啡体验</p>
</body>
</html>

然后,更新 ConfigMap:

bash
# 更新 ConfigMap(直接更新同名 ConfigMap)
kubectl create configmap cloud-cafe-html \
  --from-file=index.html=index.html \
  -n cloud-cafe \
  --dry-run=client -o yaml | kubectl apply -f -

# 或者先删除再创建
# kubectl delete configmap cloud-cafe-html -n cloud-cafe
# kubectl create configmap cloud-cafe-html --from-file=index.html=index.html -n cloud-cafe

# 查看更新后的 ConfigMap
kubectl get configmap cloud-cafe-html -n cloud-cafe -o yaml

Step 3: 执行滚动更新

现在我们使用新的 ConfigMap 更新 Deployment。

📌 关于 --dry-run=client 参数的解释

--dry-run=client 表示客户端干运行,只在本地验证配置,不实际发送到集群执行。

作用

  • 验证 YAML 配置是否正确
  • 预览将要创建/修改的资源
  • 避免误操作导致生产事故

两种模式

模式说明使用场景
--dry-run=client只在客户端验证快速检查配置语法
--dry-run=server发送到 API Server 验证验证资源是否符合集群策略

然后,更新 Deployment 触发滚动更新。由于 ConfigMap 更新后不会自动触发 Pod 重启,我们需要通过更新 Deployment 来触发滚动更新:

点击查看更新后的 deployment.yaml 内容
yaml
# 云咖啡官网 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloud-cafe-web
  namespace: cloud-cafe
  labels:
    app: cloud-cafe-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: cloud-cafe-web
  template:
    metadata:
      labels:
        app: cloud-cafe-web
      # 添加 annotation 来触发滚动更新
      annotations:
        configmap-version: "2026-02-26-v1"
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html-content
          mountPath: /usr/share/nginx/html
      volumes:
      - name: html-content
        configMap:
          name: cloud-cafe-html
          items:
          - key: index.html
            path: index.html
bash
# 方式一:使用独立的 YAML 文件(推荐)
kubectl apply -f deployment.yaml

# 方式二:使用 --dry-run 先验证
kubectl apply -f deployment.yaml --dry-run=client
kubectl apply -f deployment.yaml

# 方式三:通过更新 annotation 触发滚动更新
kubectl patch deployment cloud-cafe-web -n cloud-cafe -p \
  '{"spec":{"template":{"metadata":{"annotations":{"configmap-version":"'$(date +%s)'"}}}}}'

📌 为什么需要更新 Deployment 来触发滚动更新?

ConfigMap 更新后,已经运行的 Pod 不会自动获取新的配置。有两种方式让 Pod 使用新的 ConfigMap:

  1. 直接更新 Deployment - 修改 annotation 或配置,触发滚动更新
  2. 等待 Pod 重启 - 新创建的 Pod 会使用新的 ConfigMap

生产环境建议

  • 使用 kubectl rollout restart deployment/<name> 触发滚动更新
  • 或在 Pod 模板中添加版本 annotation,更新 annotation 触发更新
  • 或使用配置重载工具(如 Reloader)自动检测 ConfigMap 变化

观察滚动更新过程:

bash
# 在另一个终端窗口中实时查看 Pod 状态
kubectl get pods -n cloud-cafe -w

# 查看滚动更新状态
kubectl rollout status deployment/cloud-cafe-web -n cloud-cafe

# 查看 Deployment 的更新历史
kubectl rollout history deployment/cloud-cafe-web -n cloud-cafe

观察要点:

  • 旧的 Pod 逐个被终止
  • 新的 Pod 逐个被创建
  • 在更新过程中,始终有可用的 Pod
  • ReplicaSet 会创建新的版本

Step 4: 验证更新结果

bash
# 查看新的 Pod
kubectl get pods -n cloud-cafe

# 查看新的 ReplicaSet
kubectl get rs -n cloud-cafe

# 访问网站,查看新内容
curl http://$NODE_IP:$NODE_PORT

预期输出: 应该看到包含"焦糖玛奇朵"新品信息的新页面

测试服务连续性:

bash
# 在更新过程中持续访问服务,验证服务不中断
for i in {1..20}; do
  curl -s http://$NODE_IP:$NODE_PORT | grep -o "云咖啡公司" && echo " - 请求 $i 成功"
  sleep 0.5
done

Step 5: 配置健康检查

概念: 健康检查用于确保容器正常运行。Kubernetes 提供了三种探针:

  • LivenessProbe: 检查容器是否存活,如果失败则重启容器
  • ReadinessProbe: 检查容器是否准备好接收流量,如果失败则从 Service 中移除
  • StartupProbe: 检查容器是否启动成功(用于启动慢的应用)

让我们为 Deployment 添加健康检查。

首先,编辑 deployment.yaml 文件,添加健康检查配置:

bash
# 编辑 deployment.yaml 文件
# 使用你喜欢的编辑器:vim、nano、或直接在IDE中编辑
vim deployment.yaml

deployment.yaml 完整内容(包含健康检查配置):

点击查看 deployment.yaml 内容
yaml
# 云咖啡官网 Deployment
# 本次修改:添加健康检查配置(LivenessProbe 和 ReadinessProbe)
# 修改位置:在 containers[0] 下添加探针配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloud-cafe-web
  namespace: cloud-cafe
  labels:
    app: cloud-cafe-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: cloud-cafe-web
  template:
    metadata:
      labels:
        app: cloud-cafe-web
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        # [新增开始] 健康检查配置
        # LivenessProbe:检查容器是否存活,失败则重启容器
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10    # 容器启动后10秒开始检查,给应用启动时间
          periodSeconds: 10          # 每10秒检查一次
          timeoutSeconds: 5          # 超时时间5秒
          failureThreshold: 3        # 连续失败3次才认为不健康,避免偶发波动
        # ReadinessProbe:检查容器是否就绪,失败则从Service移除
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5     # 容器启动后5秒开始检查
          periodSeconds: 5           # 每5秒检查一次
          timeoutSeconds: 3          # 超时时间3秒
          failureThreshold: 3        # 连续失败3次才认为不就绪
        # [新增结束]
        volumeMounts:
        - name: html-content
          mountPath: /usr/share/nginx/html
      volumes:
      - name: html-content
        configMap:
          name: cloud-cafe-html
          items:
          - key: index.html
            path: index.html

保存文件后,应用配置:

bash
# 应用更新后的 Deployment
kubectl apply -f deployment.yaml

# 等待更新完成
kubectl rollout status deployment/cloud-cafe-web -n cloud-cafe

# 查看 Pod 详情,确认健康检查已配置
kubectl describe pod -l app=cloud-cafe-web -n cloud-cafe | grep -A 10 "Liveness\|Readiness"

观察要点:

  • Pod 的 Events 中应该有健康检查的日志
  • Pod 的状态应该是 Running 且 Ready

测试健康检查:

bash
# 查看健康检查事件
kubectl get events -n cloud-cafe --sort-by='.lastTimestamp'

# 模拟容器故障(进入 Pod 并停止 nginx)

> 📌 **关于 `--sort-by` 参数的解释**
>
> `--sort-by` 用于按指定字段对输出结果进行排序。
>
> **语法**`--sort-by='.字段路径'`
>
> **常用示例**
> ```bash
> # 按时间戳排序事件(最新的在最后)
> kubectl get events --sort-by='.lastTimestamp'
>
> # 按创建时间排序 Pod
> kubectl get pods --sort-by='.metadata.creationTimestamp'
>
> # 按重启次数排序(查看异常 Pod)
> kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
> ```
>
> **提示**:配合 `-o jsonpath` 可以查看可用的字段路径

```bash
kubectl exec -it $(kubectl get pod -l app=cloud-cafe-web -n cloud-cafe -o jsonpath='{.items[0].metadata.name}') -n cloud-cafe -- /bin/bash
# 在 Pod 内执行: nginx -s stop

# 退出 Pod,观察 Pod 是否被重启
exit
kubectl get pods -n cloud-cafe -w

观察要点:

  • LivenessProbe 检测到容器失败
  • Pod 被自动重启
  • 重启次数增加

Step 6: 设置资源限制

概念: 资源限制用于控制容器的资源使用,防止单个容器耗尽节点资源。

  • requests: 容器需要的最小资源量(用于调度)
  • limits: 容器可以使用的最大资源量(用于限制)

继续在 deployment.yaml 文件中添加资源限制配置:

bash
# 编辑 deployment.yaml 文件
vim deployment.yaml

deployment.yaml 完整内容(在Step 5基础上添加资源限制):

点击查看 deployment.yaml 内容
yaml
# 云咖啡官网 Deployment
# 本次修改:添加资源限制(requests 和 limits)
# 修改位置:在 containers[0] 下添加 resources 配置
# 前置依赖:需要先完成 Step 5 的健康检查配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloud-cafe-web
  namespace: cloud-cafe
  labels:
    app: cloud-cafe-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: cloud-cafe-web
  template:
    metadata:
      labels:
        app: cloud-cafe-web
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        # [新增开始] 资源限制配置
        # requests:调度时保证的最小资源(用于节点筛选)
        # limits:容器能使用的最大资源(硬性限制)
        resources:
          requests:
            memory: "64Mi"    # 请求64MB内存,确保节点有足够资源
            cpu: "250m"       # 请求0.25核CPU(1000m = 1核)
          limits:
            memory: "128Mi"   # 最多使用128MB内存,超出会被OOMKilled
            cpu: "500m"       # 最多使用0.5核CPU,超出会被限制
        # [新增结束]
        # [Step 5已添加] 健康检查配置
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10    # 容器启动后10秒开始检查
          periodSeconds: 10          # 每10秒检查一次
          timeoutSeconds: 5          # 超时时间5秒
          failureThreshold: 3        # 连续失败3次才认为不健康
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5     # 容器启动后5秒开始检查
          periodSeconds: 5           # 每5秒检查一次
          timeoutSeconds: 3          # 超时时间3秒
          failureThreshold: 3        # 连续失败3次才认为不就绪
        volumeMounts:
        - name: html-content
          mountPath: /usr/share/nginx/html
      volumes:
      - name: html-content
        configMap:
          name: cloud-cafe-html
          items:
          - key: index.html
            path: index.html

保存文件后,应用配置:

bash
# 应用更新后的 Deployment
kubectl apply -f deployment.yaml

# 等待更新完成
kubectl rollout status deployment/cloud-cafe-web -n cloud-cafe

# 查看 Pod 的资源使用情况
kubectl top pods -n cloud-cafe

观察要点:

  • Pod 的 QoS 类别(Burstable、Guaranteed、BestEffort)
  • 实际资源使用情况

查看 QoS 类别:

bash
# 查看 Pod 的 QoS 类别
kubectl get pod -l app=cloud-cafe-web -n cloud-cafe -o jsonpath='{.items[0].status.qosClass}'

# 解释 QoS 类别:
# - Guaranteed: 同时设置了 requests 和 limits,且两者相等
# - Burstable: 设置了 requests,但 requests < limits
# - BestEffort: 没有设置 requests 和 limits

Step 7: 版本回滚

场景: 假设新版本出现了问题,我们需要快速回滚到旧版本。

bash
# 查看更新历史
kubectl rollout history deployment/cloud-cafe-web -n cloud-cafe

# 查看特定版本的详细信息
kubectl rollout history deployment/cloud-cafe-web -n cloud-cafe --revision=2

# 回滚到上一个版本
kubectl rollout undo deployment/cloud-cafe-web -n cloud-cafe

# 等待回滚完成
kubectl rollout status deployment/cloud-cafe-web -n cloud-cafe

# 验证回滚结果
curl http://$NODE_IP:$NODE_PORT

预期输出: 应该看到旧版本的页面(没有"焦糖玛奇朵"新品信息)

回滚到指定版本:

bash
# 再次更新到新版本
kubectl set image deployment/cloud-cafe-web nginx=nginx:latest -n cloud-cafe
kubectl rollout status deployment/cloud-cafe-web -n cloud-cafe

# 回滚到指定版本(例如版本 2)
kubectl rollout undo deployment/cloud-cafe-web -n cloud-cafe --to-revision=2

# 等待回滚完成
kubectl rollout status deployment/cloud-cafe-web -n cloud-cafe

验证和测试

1. 检查所有资源状态

bash
# 查看 Deployment
kubectl get deployment -n cloud-cafe

# 查看 Pod
kubectl get pods -n cloud-cafe

# 查看 Service
kubectl get svc -n cloud-cafe

# 查看 ConfigMap
kubectl get configmap -n cloud-cafe

2. 测试滚动更新的连续性

bash
# 在一个终端中持续访问服务
while true; do
  curl -s http://$NODE_IP:$NODE_PORT | grep -o "云咖啡公司" && echo " - $(date)"
  sleep 1
done

在另一个终端中执行更新:

bash
# 更新 Deployment
kubectl set image deployment/cloud-cafe-web nginx=nginx:1.21 -n cloud-cafe

# 观察第一个终端的输出,确认服务不中断

3. 测试健康检查

bash
# 查看健康检查事件
kubectl get events -n cloud-cafe --sort-by='.lastTimestamp' | grep -i "probe"

# 查看最近的 Pod 事件
kubectl describe pod -l app=cloud-cafe-web -n cloud-cafe | tail -20

4. 测试资源限制

bash
# 查看 Pod 的资源使用情况
kubectl top pods -n cloud-cafe

# 查看 Pod 的资源限制配置
kubectl get pod -l app=cloud-cafe-web -n cloud-cafe -o jsonpath='{.items[0].spec.containers[0].resources}'

📝 总结和思考

本课程学到的知识点

  1. 滚动更新: 逐步替换旧版本 Pod,确保服务连续性
  2. 健康检查: LivenessProbe 和 ReadinessProbe 确保服务可用性
  3. 资源限制: requests 和 limits 控制资源使用
  4. 版本回滚: 快速回退到之前的版本
  5. 更新历史: 查看和管理 Deployment 的版本历史

关键概念

  • 零停机部署: 通过滚动更新实现服务不中断
  • 自愈能力: 健康检查自动重启失败的容器
  • 资源隔离: 资源限制防止单个容器影响其他容器
  • 版本管理: Deployment 自动管理多个版本

思考题

  1. 滚动更新过程中,如果新版本的 Pod 启动失败,会发生什么?
  2. LivenessProbe 和 ReadinessProbe 有什么区别?分别在什么场景下使用?
  3. 如果只设置 limits 不设置 requests,会发生什么?
  4. 如何控制滚动更新的速度?(提示:maxSurge 和 maxUnavailable)

最佳实践

  1. 始终配置健康检查: 确保服务可用性
  2. 合理设置资源限制: 防止资源耗尽
  3. 测试回滚流程: 确保能够快速回退
  4. 监控更新过程: 及时发现问题

下一步

本课程学习了如何安全地更新应用,并配置健康检查和资源限制。

下一课程将学习如何添加数据库,实现数据持久化。

下一课程: 03-订单系统部署.md


清理环境

如果你想清理本课程创建的资源:

bash
# 删除 Deployment(会删除相关的 Pod 和 ReplicaSet)
kubectl delete deployment cloud-cafe-web -n cloud-cafe

# 删除 Service
kubectl delete svc cloud-cafe-svc cloud-cafe-nodeport -n cloud-cafe

# 删除 ConfigMap
kubectl delete configmap cloud-cafe-html -n cloud-cafe

# 删除命名空间
kubectl delete namespace cloud-cafe

提示: 如果你要继续学习下一个课程,建议保留这些资源,因为下一个课程会在此基础上进行。

评论区

专业的Linux技术学习平台,从入门到精通的完整学习路径