代码示例:
-
容器与云原生——Docker 官方镜像最佳实践,K8s StatefulSet + Sentinel 高可用
(接 152276042 篇结尾,直接落地) -
把“官方镜像”用到刀刃上
1.1 优先用 Docker Hub 的“/official-image”命名空间
- 官方镜像构建脚本公开在 https://github.com/docker-library,CI 在官方 Jenkins 跑,每日做 apt/apk 安全更新,版本号与上游严格对应。
- 非官方镜像常把“latest”当“最省事”,结果 2024-08 爆发的 libssl3 漏洞 70 % 的“latest”三天后才重建。
1.2 锁死 digest,拒绝漂移
docker pull redis:7.2.4-bookworm@sha256:6a5f1c5d19…
把 digest 写进 Dockerfile / Helm values,CI 做一次不可变推送,后续任何节点拉到的都是同一层 blob,符合“可重复构建”合规基线。
1.3 最小化攻击面
- 官方镜像默认带
apt或apk,线上不需要。用多阶段构建把运行时裁剪成distroless或alpine,Go 业务镜像可从 120 MB 压到 18 MB。 - 官方 Redis 镜像默认打开保护模式,注释掉
bind 0.0.0.0即可;但自己再打镜像时,记得把protected-mode yes写回配置,防止误删。
1.4 用官方健康检查脚本
官方 MySQL 镜像自带/healthcheck.sh,返回值 0 才认为主库可用;不要自己写mysqladmin ping,版本差异会导致 false positive。
-
Sentinel 在容器里的“三座大山”
2.1 网络标识漂移
Sentinel 靠<ip:port>识别主节点,Pod 重建后 IP 必变,导致“主观下线”永远投票不过半。
2.2 配置持久化
Sentinel 把sentinel monitor mymaster …写进自身配置文件,容器一旦重启,写层丢失,又回到“刚出厂”状态。
2.3 脑裂阈值
K8s 滚动升级时,旧 Pod 与新 Pod 并存 30 s,Sentinel 可能同时看到 5 个 Sentinel 与 3 个 Sentinel 两种视图,触发两次 failover,出现双主。 -
用 StatefulSet 一揽子解决
3.1 给每个 Sentinel 一个“身份证”
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-sentinel
spec:
serviceName: sentinel
replicas: 3
podManagementPolicy: OrderedReady
template:
metadata:
labels:
app: sentinel
spec:
containers:
- name: sentinel
image: redis:7.2.4-bookworm@sha256:6a5f1c5d19…
command:
- redis-sentinel
- /etc/sentinel.conf
- --sentinel
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
- 每个 Pod 固定主机名
redis-sentinel-0.sentinel.default.svc.cluster.local,Sentinel 启动时把该主机名写进sentinel announce-ip,Sentinel 之间用 DNS 域名互相发现,IP 漂移不再影响。 volumeClaimTemplates保证/data/sentinel.conf落在 PV 里,Pod 重建后配置不丢。
3.2 初始化脚本一次性写 monitor
在initContainer里用sentinelSet命令只在<ordinal>==0时执行:
redis-cli -p 26379 sentinel monitor mymaster redis-0.redis.default 6379 2
其余 Pod 启动后通过 SENTINEL sentinels mymaster 自动学习,无需再手动添加。
3.3 防止并发的 failover
调高 down-after-milliseconds 到 10000 ms,配合 min-replicas-max-lag 5;
K8s 滚动升级前,先通过 kubectl annotate sts/redis-sentinel upgrade=freeze 触发 PreStop 钩子,让 Sentinel 主动 SENTINEL failover mymaster 交出领导权,再删 Pod,保证任何时刻只有 1 个主库。
- Redis 主从也用 StatefulSet
4.1 一主两从
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 3
template:
spec:
containers:
- name: redis
image: redis:7.2.4-bookworm@sha256:6a5f1c5d19…
command: ["/usr/local/bin/redis-start.sh"]
redis-start.sh 逻辑:
- 若
hostname后缀为-0,执行redis-server /etc/redis.conf --port 6379; - 否则先
redis-cli -h redis-0.redis ping,成功则执行replicaof redis-0.redis 6379。
4.2 用podManagementPolicy: Parallel缩短启动时间,但结合minReadySeconds: 10防止并发全量同步打爆网卡。
- 统一暴露:Headless Service + ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
clusterIP: None
ports:
- port: 6379
name: redis
selector:
app: redis
---
apiVersion: v1
kind: Service
metadata:
name: redis-read
spec:
ports:
- port: 6379
name: redis
selector:
app: redis
- 业务写操作用
redis-0.redis:6379直连主库; - 读操作用
redis-read:6379走 ClusterIP 自动负载到任意副本,降低主库压力。
- 监控:把 Sentinel 事件打进 Prometheus
Sentinel 支持sentinel notification-script:
#!/bin/bash
echo "sentinel_failing $@" | curl --data-binary @- http://pushgateway:9091/metrics/job/redis/sentinel/$HOSTNAME
Prometheus 端记录 sentinel_failing{master="mymaster"},再配 Alertmanager 5 分钟内 >=2 次即告警。
- 灾难演练:一键“杀”主库
kubectl exec redis-0 -- kill -9 1
观察:
- Sentinel 10 s 完成投票;
redis-1提升为主;- 业务写操作报错 ❤️ s(连接池重试);
- Prometheus 显示
redis_master_failover_seconds_bucketP99 12 s。
- 镜像供应链加固(2025 新规)
- 官方镜像已默认开启 SBOM,CI 里用
docker buildx imagetools inspect redis:7.2.4-bookworm --format "{{ json .SBOM }}"导出,上传至公司合规平台; - 用
cosign做镜像签名,公钥托管在 K8scosign-pubConfigMap, admission webhook 拒绝任何未签名镜像。
- 小结
Docker 官方镜像是“第一信任源”,但容器生命周期短、IP 易变,与 Redis Sentinel 的“有状态”天然冲突。用 StatefulSet 固定网络标识 + PV 持久化配置,再辅以启动顺序、failover 冻结、监控告警三板斧,可在 100 % 容器化环境中把 RPO 压到 30 s 以内,RTO 压到 60 s 以内,满足金融级“云原生高可用”硬要求。
更多技术文章见公众号: 大城市小农民
1万+

被折叠的 条评论
为什么被折叠?



