etcd / OOM / 数据库备份 / CI深入 / 自动化运维 · Q41–Q50
# 查看 etcd 集群健康状态 ETCDCTL_API=3 etcdctl \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ endpoint health # 查看集群成员 etcdctl member list # 备份快照(生产必做) etcdctl snapshot save /backup/etcd-$(date +%F).db # 查看快照状态 etcdctl snapshot status /backup/etcd-2025-01-01.db
# 第一步:确认是 OOMKilled kubectl describe pod <pod-name> # 看 Last State → Reason: OOMKilled # 看 Exit Code: 137(128 + 9,被 SIGKILL) # 第二步:看历史日志(容器重启前) kubectl logs <pod-name> --previous # 第三步:看内存使用趋势(Grafana / kubectl top) kubectl top pod <pod-name> --containers # 第四步:宿主机层面确认 dmesg | grep -i "oom\|killed" journalctl -k | grep -i oom
resources:
requests:
memory: "256Mi" # 正常运行所需,通过监控 P90 用量确定
cpu: "250m" # 0.25 核,1000m = 1核
limits:
memory: "512Mi" # requests 的 1.5~2 倍,留弹性空间
cpu: "500m" # CPU limit 可以设高,反正只限速不 Kill
| 方式 | 工具 | 优点 | 缺点 |
|---|---|---|---|
| 逻辑备份 | mysqldump | 跨版本兼容,可读 | 大库慢,恢复慢 |
| 物理备份 | xtrabackup | 快速,支持热备和增量 | 需相同 MySQL 版本 |
| binlog 备份 | mysqlbinlog | 实现增量 + 时间点恢复 | 需配合全量使用 |
#!/bin/bash # 全量热备,不锁表,生产推荐 DATE=$(date +%F) BACKUP_DIR="/backup/mysql/$DATE" xtrabackup --backup \ --user=backup_user \ --password=<pwd> \ --target-dir=$BACKUP_DIR # prepare 阶段(使备份可用) xtrabackup --prepare --target-dir=$BACKUP_DIR # 上传到对象存储 ossutil cp -r $BACKUP_DIR oss://your-bucket/mysql/
# 场景:误删数据,需要恢复到 10:30 之前 # 1. 恢复最近的全量备份 xtrabackup --copy-back --target-dir=/backup/mysql/2025-01-01 chown -R mysql:mysql /var/lib/mysql # 2. 用 binlog 回放到指定时间点(不包含误操作) mysqlbinlog \ --start-datetime="2025-01-01 00:00:00" \ --stop-datetime="2025-01-01 10:29:59" \ /var/lib/mysql/mysql-bin.* | mysql -u root -p # 关键:binlog 必须提前开启 # my.cnf: log_bin = /var/lib/mysql/mysql-bin
livenessProbe:
httpGet:
path: /healthz # 接口返回 2xx 即存活
port: 8080
initialDelaySeconds: 30 # 容器启动后等30秒再开始探测
periodSeconds: 10 # 每10秒探测一次
failureThreshold: 3 # 连续失败3次才重启
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 2 # 失败2次就摘流量
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 允许最多 30*10=300秒 启动时间
periodSeconds: 10
| 维度 | Sentinel 哨兵 | Cluster 集群 |
|---|---|---|
| 数据分布 | 全量复制,每节点全量数据 | 分片,数据分散在各节点 |
| 容量扩展 | 不能水平扩容 | 支持在线扩缩容 |
| 故障转移 | 哨兵投票选主 | 集群内部 Gossip 协议 |
| 最小节点数 | 1主+1从+3哨兵 | 3主+3从(最少6个) |
| 适用场景 | 数据量不大,需高可用 | 数据量大,需水平扩展 |
CRC16(key) % 16384 计算归属槽,路由到对应节点# 查看集群状态 redis-cli -c -h 127.0.0.1 -p 7001 cluster info redis-cli -c cluster nodes # 查看各节点槽分布 # 模拟客户端(-c 开启自动重定向) redis-cli -c -h 127.0.0.1 -p 7001 > set foo bar # 自动 MOVED 到正确节点 # 在线迁移槽(扩容时用) redis-cli --cluster reshard 127.0.0.1:7001
{} 哈希标签强制同槽:key:{user:1}:name 和 key:{user:1}:age 在同一槽。
多个项目 Jenkinsfile 有大量重复代码(构建镜像、推 Harbor、kubectl 部署逻辑相同),修改一处要改所有项目。共享库把公共逻辑提取为函数库,各项目直接调用。
# Git 仓库结构(jenkins-shared-library) ├── vars/ │ ├── buildImage.groovy # 全局函数:构建推送镜像 │ ├── deployK8s.groovy # 全局函数:部署到 K8s │ └── notify.groovy # 全局函数:钉钉通知 └── src/ # 类库(复杂逻辑)
// vars/buildImage.groovy
def call(String imageName, String tag) {
sh """
docker build -t harbor.xx.com/${imageName}:${tag} .
docker push harbor.xx.com/${imageName}:${tag}
docker rmi harbor.xx.com/${imageName}:${tag}
"""
}
// vars/deployK8s.groovy
def call(String namespace, String deployment, String image) {
sh "kubectl set image deployment/${deployment} \
app=${image} -n ${namespace} --record"
sh "kubectl rollout status deployment/${deployment} -n ${namespace}"
}
// 引用共享库(在 Jenkins 全局配置里注册 Git 地址)
@Library('jenkins-shared-library') _
pipeline {
agent any
stages {
stage('构建镜像') {
steps { buildImage('myapp', env.BUILD_NUMBER) }
}
stage('部署') {
steps {
deployK8s('production', 'myapp',
"harbor.xx.com/myapp:${env.BUILD_NUMBER}")
}
}
}
post { failure { notify('构建失败') } }
}
| 维度 | Zabbix | Prometheus |
|---|---|---|
| 架构 | Push(Agent 主动上报) | Pull(主动拉取 exporter) |
| 数据存储 | MySQL / PostgreSQL | 本地 TSDB(时序数据库) |
| 查询语言 | 无,GUI 操作为主 | PromQL,表达力强 |
| K8s 支持 | 差,需额外配置 | 原生支持,ServiceMonitor |
| 告警 | 内置告警,GUI 配置 | AlertManager,YAML 配置 |
| 适合场景 | 传统物理机/VM 监控 | 云原生/K8s/微服务监控 |
# Zabbix Agent 配置(被监控端) # /etc/zabbix/zabbix_agentd.conf Server=192.168.1.100 # Zabbix Server IP ServerActive=192.168.1.100 # 主动模式 Hostname=web-server-01 # 在 Zabbix UI 里对应的主机名 # 自定义监控项(UserParameter) UserParameter=nginx.conn,ss -ant | grep ESTABLISHED | wc -l
# 场景:/data 目录满了,新增一块磁盘 /dev/sdb 扩容 # 第1步:初始化新磁盘为 PV pvcreate /dev/sdb pvdisplay # 查看 PV 列表 # 第2步:将 PV 加入 VG(假设 VG 名为 datavg) vgextend datavg /dev/sdb vgdisplay datavg # 确认 VG 空间增加 # 第3步:扩展 LV(+100G 或扩到最大) lvextend -L +100G /dev/datavg/datalv # 或扩到 VG 全部剩余空间 lvextend -l +100%FREE /dev/datavg/datalv # 第4步:在线扩容文件系统(不卸载) # ext4 文件系统 resize2fs /dev/datavg/datalv # xfs 文件系统(CentOS 7+ 默认) xfs_growfs /data # 验证 df -h /data
lvextend 只扩了 LV,文件系统还没扩。最后必须执行 resize2fs(ext4)或 xfs_growfs(xfs),两者缺一不可。漏掉这步是最常见的操作失误。
主动在系统中注入故障(网络延迟、Pod 宕机、磁盘满、CPU 满),验证系统的容错能力和告警响应是否符合预期,在可控条件下暴露隐患,而不是等线上真实故障时才发现。
# 1. 随机删除 Pod(验证 Deployment 自愈能力)
kubectl delete pod -l app=myapp -n production --grace-period=0
# 2. 模拟节点 NotReady(验证 Pod 漂移)
# 在某个 worker 上停止 kubelet
systemctl stop kubelet
# 观察:Pod 是否在 5分钟后漂移到其他节点
# 3. 模拟 OOMKill(验证 limits 和告警)
kubectl exec -it <pod> -- stress --vm 1 --vm-bytes 600M
# 4. 网络注入(用 tc 模拟延迟)
tc qdisc add dev eth0 root netem delay 200ms 50ms
# 恢复
tc qdisc del dev eth0 root
# 5. 使用 Chaos Mesh(K8s 原生混沌工具)
kubectl apply -f - <<EOF
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: pod-kill-test
spec:
action: pod-kill
selector:
namespaces: [staging] # 只在预发环境测!
labelSelectors:
app: myapp
scheduler:
cron: "@every 5m"
EOF
# === 代码/配置 === □ 代码已 Code Review,通过 SonarQube 静态扫描 □ 镜像已构建并在测试环境验证通过 □ ConfigMap / 环境变量变更已确认 □ 数据库变更脚本已准备(DDL 变更需提前评估锁表风险) # === 资源 === □ 节点资源充足(kubectl top nodes,预留 20% 余量) □ PVC 空间充足(df -h,磁盘使用率 < 80%) □ 镜像已推送到 Harbor 并扫描无高危漏洞 # === 流量 === □ 选低峰时段(凌晨或周末) □ 分批灰度上线(先 1 个 Pod,观察5分钟再全量) □ Nginx / Ingress 限流规则已确认 # === 应急 === □ 回滚命令已准备(kubectl rollout undo) □ 告警通道畅通(钉钉/企微 webhook 已测试) □ 相关人员已通知并在线待命