Kubernetes集群运维经验总结

描述

Kubernetes集群运维的10个坑与规避方法:血泪教训总结

作为一名在K8s运维战壕里摸爬滚打3年的工程师,我踩过的坑能绕地球一圈。今天把这些"学费"分享给大家,希望能帮你们少走弯路。

前言:为什么要写这篇文章?

去年双11凌晨2点,我们的K8s集群突然雪崩,200+个Pod全部重启,用户投诉电话打爆了运营的手机。事后复盘发现,这完全是一个可以避免的低级错误。那一刻我意识到,运维不仅是技术活,更是经验活。

本文总结了我和团队在K8s生产环境中遇到的10个最常见且最致命的坑,每个坑都配有真实案例、详细分析和可执行的解决方案。

坑位1:资源配置不当导致的"雪花效应"

真实案例

 

# 错误配置示例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-service
spec:
  replicas: 10
  template:
    spec:
      containers:
      - name: web
        image: nginx:latest
        # 没有设置资源限制!

 

后果: 某个Pod内存泄漏,疯狂占用节点资源,导致整个节点上的其他Pod被驱逐,引发连锁反应。

规避方法

1. 强制设置资源限制

 

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

 

2. 使用LimitRange自动注入

 

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limit-range
spec:
  limits:
  - default:
      cpu: "500m"
      memory: "512Mi"
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    type: Container

 

运维心得: 生产环境必须设置资源限制,这是铁律!建议用Prometheus监控资源使用趋势,动态调整配置。

坑位2:存储卷挂载的"消失魔术"

真实案例

一次升级后,我们发现数据库Pod的数据全部丢失。原因是PVC配置错误,挂载了错误的存储类。

 

# 危险配置
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  storageClassName: "standard"  # 默认存储类,不持久化!
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

 

规避方法

1. 明确指定存储类

 

spec:
  storageClassName: "ssd-retain"  # 明确指定持久化存储类

 

2. 设置PV回收策略

 

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  persistentVolumeReclaimPolicy: Retain  # 保护数据
  capacity:
    storage: 20Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce

 

3. 备份验证脚本

 

#!/bin/bash
# daily-backup-check.sh
kubectl get pvc -A -o wide | grep -v "Bound" && echo "警告:存在未绑定的PVC!"
kubectl get pv | grep "Released" && echo "警告:存在已释放的PV,可能数据丢失!"

 

坑位3:镜像管理的"薛定谔状态"

真实案例

 

# 坑爹配置
containers:
- name: app
  image: myapp:latest  # latest标签,部署时不确定版本
  imagePullPolicy: Always  # 每次都拉取,网络故障时无法启动

 

生产环境中,某次网络抖动导致镜像拉取失败,整个服务无法启动,影响了2小时。

规避方法

1. 使用具体版本标签

 

containers:
- name: app
  image: myapp:v1.2.3-20231120  # 明确版本号
  imagePullPolicy: IfNotPresent

 

2. 建立镜像仓库高可用方案

 

# 配置多个镜像仓库
apiVersion: v1
kind: Secret
metadata:
  name: regcred-backup
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: 
---
spec:
  template:
    spec:
      imagePullSecrets:
      - name: regcred-primary
      - name: regcred-backup

 

3. 镜像预热脚本

 

#!/bin/bash
# image-preload.sh
NODES=$(kubectl get nodes -o name)
IMAGE_LIST="app:v1.2.3 nginx:1.20 redis:6.2"

for node in $NODES; do
    for image in $IMAGE_LIST; do
        echo "预加载镜像 $image 到节点 $node"
        kubectl debug $node -it --image=$image -- /bin/true
    done
done

 

坑位4:网络策略配置的"黑洞现象"

真实案例

开启了网络策略后,服务间无法通信,排查了一整夜才发现是NetworkPolicy配置错误。

 

# 过度严格的网络策略
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
  # 没有配置任何允许规则,所有流量被阻断!

 

规避方法

1. 渐进式网络策略部署

 

# 第一步:只监控,不阻断
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-netpol
  annotations:
    net.example.com/policy-mode: "monitor"  # 先监控模式
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api
    ports:
    - protocol: TCP
      port: 80

 

2. 网络策略测试工具

 

#!/bin/bash
# netpol-test.sh
echo "测试网络连通性..."
kubectl run test-pod --image=nicolaka/netshoot --rm -it -- /bin/bash
# 在Pod内测试:
# nc -zv  

 

运维技巧: 使用Calico或Cilium的可视化工具,图形化查看网络策略效果。

坑位5:探针配置不合理导致的"误杀"

真实案例

 

# 激进的探针配置
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 5    # 启动延迟太短
  periodSeconds: 5          # 检查间隔太短
  failureThreshold: 1       # 失败一次就重启
  timeoutSeconds: 1         # 超时时间太短

 

结果:应用启动需要30秒,但探针5秒后就开始检查,导致Pod不断重启。

规避方法

1. 合理配置探针参数

 

# 温和的探针配置
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 60   # 给足启动时间
  periodSeconds: 30         # 适中的检查间隔
  failureThreshold: 3       # 多次失败才重启
  timeoutSeconds: 10        # 合理的超时时间
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
  failureThreshold: 3

 

2. 探针测试脚本

 

#!/bin/bash
# probe-test.sh
POD_NAME=$1
echo "测试Pod探针响应时间..."
kubectl exec $POD_NAME -- time wget -qO- localhost:8080/health
kubectl exec $POD_NAME -- time wget -qO- localhost:8080/ready

 

坑位6:滚动更新策略的"服务中断"

真实案例

 

# 危险的更新策略
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 50%   # 一半Pod同时更新
      maxSurge: 0           # 不允许超出副本数

 

结果:更新期间服务能力直接腰斩,用户体验极差。

规避方法

1. 保守的滚动更新策略

 

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%   # 最多四分之一不可用
      maxSurge: 25%         # 允许临时超出副本数
  minReadySeconds: 30       # 新Pod稳定30秒后才继续

 

2. 部署前的容量评估

 

#!/bin/bash
# capacity-check.sh
DEPLOYMENT=$1
CURRENT_REPLICAS=$(kubectl get deployment $DEPLOYMENT -o jsonpath='{.spec.replicas}')
MAX_UNAVAILABLE=$(kubectl get deployment $DEPLOYMENT -o jsonpath='{.spec.strategy.rollingUpdate.maxUnavailable}')
echo "当前副本数: $CURRENT_REPLICAS"
echo "最大不可用: $MAX_UNAVAILABLE"
echo "更新期间最少可用Pod数: $((CURRENT_REPLICAS - MAX_UNAVAILABLE))"

 

坑位7:日志收集的"磁盘炸弹"

真实案例

某个应用产生大量DEBUG日志,没有配置日志轮转,最终把节点磁盘写满,导致整个节点不可用。

规避方法

1. 配置日志轮转

 

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluent.conf: |
    
      @type tail
      path /var/log/containers/*.log
      pos_file /var/log/fluentd-containers.log.pos
      tag kubernetes.*
      read_from_head true
      
        @type json
        time_format %Y-%m-%dT%H:%M:%S.%NZ
      
    
    
    # 日志过滤,减少存储压力
    
      @type grep
      
        key log
        pattern /DEBUG|TRACE/
      
    

 

2. 磁盘使用监控

 

#!/bin/bash
# disk-monitor.sh
THRESHOLD=85
NODES=$(kubectl get nodes -o name)

for node in $NODES; do
    USAGE=$(kubectl top node $node --no-headers | awk '{print $5}' | tr -d '%')
    if [ "$USAGE" -gt "$THRESHOLD" ]; then
        echo "警告:节点 $node 磁盘使用率 ${USAGE}%,超过阈值!"
        # 发送告警...
    fi
done

 

坑位8:RBAC权限的"特权升级"

真实案例

为了图方便,给应用Pod配置了cluster-admin权限,结果被安全部门发现,差点引发安全事故。

 

# 危险配置
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-app-binding
subjects:
- kind: ServiceAccount
  name: my-app
  namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin  # 过高的权限!

 

规避方法

1. 最小权限原则

 

# 创建最小权限角色
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]

 

2. 权限审计脚本

 

#!/bin/bash
# rbac-audit.sh
echo "检查危险的ClusterRoleBinding..."
kubectl get clusterrolebinding -o yaml | grep -A 5 -B 5 "cluster-admin"

echo "检查ServiceAccount权限..."
kubectl get rolebinding,clusterrolebinding --all-namespaces -o wide

 

坑位9:节点维护的"单点故障"

真实案例

某天需要重启一个节点进行内核升级,直接执行了重启,结果发现该节点上运行着数据库的Master Pod,导致数据库短暂不可用。

规避方法

1. 优雅的节点维护流程

 

#!/bin/bash
# node-maintenance.sh
NODE_NAME=$1

echo "1. 检查节点上的关键Pod..."
kubectl get pods --all-namespaces --field-selector spec.nodeName=$NODE_NAME -o wide

echo "2. 标记节点不可调度..."
kubectl cordon $NODE_NAME

echo "3. 等待用户确认..."
read -p "确认要驱逐Pod吗?(y/N) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
    echo "4. 驱逐Pod..."
    kubectl drain $NODE_NAME --ignore-daemonsets --delete-emptydir-data --grace-period=300
fi

echo "5. 节点已准备好维护"

 

2. Pod反亲和性配置

 

# 确保关键应用分散在不同节点
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - database
        topologyKey: kubernetes.io/hostname

 

坑位10:监控告警的"狼来了"

真实案例

配置了过于敏感的告警规则,每天收到几百条告警,最后大家都麻木了,真正的故障反而被忽略。

 

# 过于敏感的告警规则
- alert: HighCPUUsage
  expr: cpu_usage > 50%  # 阈值过低
  for: 1m                # 持续时间太短
  labels:
    severity: critical   # 级别过高

 

规避方法

1. 合理的告警分级

 

# Prometheus告警规则
groups:
- name: kubernetes-apps
  rules:
  - alert: PodCrashLooping
    expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 重启频繁"
      
  - alert: PodNotReady
    expr: kube_pod_status_ready{condition="false"} == 1
    for: 10m
    labels:
      severity: critical
    annotations:
      summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 长时间未就绪"

 

2. 告警降噪脚本

 

#!/bin/bash
# alert-dedup.sh
# 合并相似告警,减少噪音
kubectl get events --sort-by='.lastTimestamp' | 
grep -E "Warning|Error" | 
awk '{print $4, $5, $6}' | 
sort | uniq -c | sort -nr

 

运维最佳实践总结

经过这些血泪教训,我总结了几条K8s运维的黄金法则:

预防为主

• 资源限制是必须的:宁可保守,不可激进

• 探针配置要合理:给应用足够的启动和响应时间

• 权限最小化原则:能用Role就不用ClusterRole

监控先行

• 全面监控:节点、Pod、网络、存储都要覆盖

• 合理告警:减少噪音,突出重点

• 定期巡检:自动化检查集群健康状态

故障演练

• 混沌工程:主动制造故障,测试系统韧性

• 备份验证:定期测试备份恢复流程

• 应急预案:制定详细的故障处理流程

文档化

• 操作记录:每次变更都要有记录

• 知识沉淀:把踩坑经验形成文档

• 团队培训:定期分享最佳实践

写在最后

K8s运维是一个持续学习的过程,每个坑都是成长的机会。希望这篇文章能帮到正在K8s路上摸索的你。如果你也有类似的踩坑经历,欢迎在评论区分享,让我们一起成长!

记住:在生产环境中,没有小问题,只有大事故。 每一个细节都可能决定系统的稳定性。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分