Skip to content

Latest commit

 

History

History
184 lines (140 loc) · 11.7 KB

File metadata and controls

184 lines (140 loc) · 11.7 KB

运维手册

面向已部署(见 DEPLOYMENT.md)后日常运维:监控日志、扩缩容、防护治理、压测、优雅停机、排障、容灾。

1. 监控与日志

可观测栈:deploy/docker/observability/(Prometheus 9090 / Grafana 3000 / Loki 3100 / Promtail),host 网络模式。

cd deploy/docker/observability && docker compose up -d prometheus grafana loki promtail
  • 指标:Grafana → JVM·HTTP 概览面板;Prometheus http://localhost:9090 看 targets 全 up。
  • 日志:Grafana → Explore → Loki 数据源,查 {namespace="cloud-platform"}(k8s)或 {job="cloud-platform"}(docker,Promtail 采宿主 /tmp/*.log)。
  • 链路:Zipkin http://localhost:9411,按 serviceName / traceId 看调用链。

K8s 内可观测性(21-observability.yaml)经 Service DNS 抓取,并为 k6 开 Prometheus Remote Write;访问用 kubectl -n cloud-platform port-forward svc/grafana 3000:3000。

落盘规则

日志经 logback FileAppender 直接落宿主目录(/home/zw/cloud-logs/<name>.log,容器内 /tmp/logs),不依赖 stdout 管道(JVM stdout 块缓冲会丢日志)。镜像模式由 LOGGING_FILE_NAME 注入路径。

2. 扩缩容

  • 控制台:WEB_CONSOLE.md「状态」页 POST /api/scale(等价 scale.sh <x> <N>)。
  • kubectl:kubectl -n cloud-platform scale deploy/<svc> --replicas=<N>。
  • Headlamp:Workloads → Deployments → Scale(控制台独立,不碰副本)。
  • HPA:默认不部署(避免与手动 scale 冲突);需启用启动集群加 --with-governance(CPU 70% 自动伸缩,min2/max4)。

3. 防护与治理(运行时操作)

设计与实现原理见 ARCHITECTURE §4;本节仅运行时操作。

3.1 网关限流(Actuator 端点,免重启)

限流参数来自运行时可变 bean,可即时改写,无需重启网关、不重建镜像。

curl http://localhost:8080/actuator/ratelimit                       # 查当前
curl -X POST http://localhost:8080/actuator/ratelimit -H 'Content-Type: application/json' -d '{"enabled":false}'   # 放开(压测/演练)
curl -X POST http://localhost:8080/actuator/ratelimit -H 'Content-Type: application/json' -d '{"enabled":true}'    # 恢复
curl -X POST http://localhost:8080/actuator/ratelimit -H 'Content-Type: application/json' -d '{"replenishRate":200,"burstCapacity":400}'  # 仅调阈值
  • 约束:burstCapacity ≥ replenishRate(端点兜底:若传入小于 replenishRate 自动取等,不挂网关)。
  • 端点与 Nacos 写同一 bean;正式限流参数以 Nacos 配置为准,端点临时改动会被后续 Nacos 推送覆盖。勿注入 GATEWAY_RATELIMIT_* 环境变量(会覆盖 Nacos)。
  • Nacos 放开:控制台把 cloud-gateway.yaml 的 gateway.ratelimit.enabled 改 false 并发布,无需重启。

3.2 与压测集成(自动放开 + 恢复)

压测脚本跑 k6 前自动 POST /actuator/ratelimit {"enabled":false},trap EXIT 保证跑完(含异常)自动恢复 true;镜像/进程模式同 HTTP 端点,无需区分形态、无需重启网关。

3.3 网关熔断(Resilience4j)

user-service / order-service 路由挂路由级熔断(fallbackUri: forward:/fallback):被路由实例全部不可用时网关直接 fallback,避免雪崩。下游恢复注册(Nacos 约 30s)后自动半开→闭合。与限流串行(先限流后熔断)。

3.4 网关鉴权(JWT)

AuthGlobalFilter(HS256),默认 gateway.security.enabled=false 关闭以保持演示可用。

  • 生产启用:java -jar cloud-gateway.jar --gateway.security.enabled=true --gateway.security.secret=<强密钥>(生产应写入 Nacos);建议认证服务签发、网关只验签(RS256)。

3.5 Sentinel(预留)

依赖已引入、Dashboard 随可观测栈一键拉起(8858)。落地需:业务方法加 @SentinelResource + 各服务 yml 配 spring.cloud.sentinel.transport.dashboard=127.0.0.1:8858。

4. 压测(k6)

k6 以官方镜像 grafana/k6:0.52.0 容器化(无需宿主装 k6)。三种形态共用 test/loadtest/script/(唯一真源),启动逻辑共用 test/loadtest/loadtest-common.sh,仅部署动作不同:

形态 启动 BASE_URL(寻址)
proc 宿主 k6 直接读 http://localhost:8080(同机 loopback,dev/demo)
docker test/loadtest/docker/start-loadtest-docker.sh http://host.docker.internal:8080(独立加压机打宿主)
k8s test/loadtest/k8s/start-loadtest-k8s.sh http://cloud-gateway:8080(集群内 Service DNS,生产姿态)

前提:①业务服务已起(网关 :8080 可访问)②可观测栈已起(Prometheus 开 Remote Write Receiver)。

bash test/loadtest/docker/start-loadtest-docker.sh order      # 下单全链路
bash test/loadtest/docker/start-loadtest-docker.sh user      # 用户查询
bash test/loadtest/docker/start-loadtest-docker.sh mixed     # 混合 80% 读 / 20% 写
VUS=200 DURATION=120s bash test/loadtest/docker/start-loadtest-docker.sh mixed   # 梯度加压

参数(constant-arrival-rate,RATE 直接决定吞吐):RATE(默认 200) / VUS(100) / MAXVUS(1000) / DURATION(120s) / BASE_URL 经 env 覆盖。

观察:Grafana → k6 Load Test 面板(VU/QPS/p99/错误率);k6 指标走 Remote Write 推送,空闲时面板空属正常。

容量压测:梯度加压 + 推导限流(闭环)

部署 → 梯度压测取拐点 → 按实测容量留安全余量设限流 → 重压验证。

脚本 test/loadtest/run-loadtest.sh <service> <replicas...> [--scenario S] [--rates R...] 只测量、不扩容:<service> 为任意业务 Deployment 名(纯参数),读实际副本校验与声明一致后才压测。每档期望副本先 kubectl scale 设好。

kubectl -n cloud-platform scale deploy/user-service --replicas=3
bash test/loadtest/run-loadtest.sh user-service 3            # 单档
# 全量 1-4 副本
for svc in user-service order-service; do
  for r in 1 2 3 4; do
    kubectl -n cloud-platform scale deploy/$svc --replicas=$r
    kubectl -n cloud-platform rollout status deploy/$svc
    bash test/loadtest/run-loadtest.sh $svc $r
  done
done

结果落 test/loadtest/results/(INDEX.md 汇总 + 每档 *.log)。

判读:对副本数 r,failed%<0.01 且 p95 在 SLO 内(user<300ms / order<500ms)的最大 RATE 即可持续容量 C(r)。

由 C(r) 推导限流:

  1. 拐点 = C(r)。
  2. 扩展效率 eff = C(2r)/C(r):≈2 扩副本线性有效、限流随副本等比放大;<<2 瓶颈在下游,限流直接钉死 C(r)。
  3. SCG per-IP(Redis 令牌桶):replenishRate ≈ C(r)/N_clients × 安全系数(N_clients 估算 10~50,下限保底 ≥20/s)。套用 POST /actuator/ratelimit {"replenishRate":50,"burstCapacity":100} 或 Nacos gateway.ratelimit.* 热推。
  4. edge 全局(当前未设):Σ C(r) ≤ edge_global ≤ gateway_tier_capacity,edge 守总量、SCG 守单体。
  5. 运行阈值取上限 70%~80%,套用后重跑压测 enabled:true 确认 failed%<1%、p95 达标,闭环。

注意:压测务必 enabled:false,否则单 k6 IP 被 SCG 钉死 100/s,测不出真实容量。

5. 优雅停机与启停顺序

层 配置 值
应用 server.shutdown graceful
应用 spring.lifecycle.timeout-per-shutdown-phase 30s
stop-services.sh docker compose down -t 35s(比应用多 5s 余量)
stop-docker.sh 关闭顺序 边缘 → 业务 → 可观测 → 中间件
stop-docker.sh DRAIN_WAIT 3s(断边缘后等在途排空)
  • 数据卷默认保留:down 不带 -v,命名卷全部保留;彻底重置用 bash deploy/docker/reset-docker.sh(破坏性,CLEAN_IMAGES=1 可删业务镜像)。
  • 配置在源码 application.yml,改后须重建(build-images.sh 或 mvn package);Spring Boot 默认 30s 优雅窗口已生效。
  • K8s 侧:terminationGracePeriodSeconds ≥ 30s,滚动发布配 preStop 平滑下线;已加 startupProbe(宽限 300s)避免冷启动误杀。

6. 排障速查

日常查看(k8s)

kubectl -n cloud-platform get pods -o wide
kubectl -n cloud-platform get events --sort-by=.lastTimestamp   # 排障首选
kubectl -n cloud-platform logs deploy/<svc> --tail=100 -f
kubectl -n cloud-platform logs <pod> --previous                 # 看上次崩溃(CrashLoop 必看)
kubectl -n cloud-platform describe pod <pod>                    # 事件/镜像/拉取失败

故障速查表

现象 根因 处理
全集群 ContainerCreating 节点缺 pause 沙箱镜像 ctr import 补 pause 后重建 Pod
UnknownHostException: nacos / CrashLoopBackOff CoreDNS 缺失致集群 DNS 死 ctr import coredns 后 kubectl -n kube-system rollout restart deploy/coredns
ImagePullBackOff 镜像未导入节点 / 版本号与清单不一致 按 DEPLOYMENT §2 导入;核对 VERSION 与清单硬编码版本
k3d 导入 content digest not found 却假成功 k3d 5.9.0 导出 tar 损坏(已知 bug) 用 ctr import 回退通道(docker save → cp → ctr import)
离线 ContainerCreating / DNS 死 k3d 节点是独立 containerd,不读宿主 daemon.json 加速 离线须预先把 pause/coredns/中间件镜像导入节点

镜像离线导入(k3d 节点 containerd 命名空间 k8s.io):

k3d image import cloud/user-service:1.0.0 -c cloud-platform
# 回退通道
NODE=k3d-cloud-platform-server-0
docker save cloud/user-service:1.0.0 -o /tmp/user.tar && docker cp /tmp/user.tar $NODE:/tmp/ && docker exec $NODE ctr -n k8s.io images import /tmp/user.tar

发布与重启(k8s)

kubectl -n cloud-platform rollout restart deploy/user-service
kubectl -n cloud-platform set image deploy/order-service order-service=cloud/order-service:1.1.0
kubectl -n cloud-platform rollout status deploy/cloud-gateway --timeout=180s
kubectl -n cloud-platform rollout undo deploy/user-service

启停与重建(k3d)

bash deploy/kubernetes/stop-k3d.sh                 # 软停止:容器暂停,状态全留
k3d cluster start cloud-platform                   # 随时恢复,Pod 原样回来
DELETE=1 bash deploy/kubernetes/stop-k3d.sh        # 硬删除:销毁集群、释放 30080(无 PVC,数据丢)
BUILD_MODE=skip bash deploy/kubernetes/start-k3d.sh   # 复用本地镜像、跳过构建,快速恢复

仅更新业务代码不要删集群:重新导入镜像 + rollout restart 即可。web-console 是独立容器,改它只需重启自身,与集群生命周期无关。

7. 容灾(DR)

按「堵数据丢失(P0) → 去入口单点(P1) → 中间件高可用(P2) → 跨区多活(P3)」分层。

P0 已落地(数据不丢,已实测)

  • 中间件持久化卷:MySQL(mysql-data)、Redis(redis-data + appendonly yes AOF)、Kafka(kafka-data)、Zookeeper(zk-data) 均挂命名卷;容器重建不再丢数据。
  • MySQL 定时备份:deploy/docker/middleware/backup-mysql.sh 复用 cloud-mysql 做 mysqldump 压缩落 ./backups/、保留 7 天,可接入 crontab。
  • 代价:首次重建 redis/zk/kafka 清空演示数据(缓存/历史消息),业务自动回源/重连,可接受。

P1/P2/P3(计划,未实施)

  • P1 去入口单点:Gateway 改多实例 + Nginx upstream 挂多网关 health_check;Nginx 单点靠 keepalived / 云 LB 做 VIP 漂移。
  • P2 中间件高可用:Nacos 集群、MySQL 主从、Redis 哨兵、Kafka 多 broker(min.insync.replicas≥2);生产建议云托管 / K8s Operator。
  • P3 跨区多活:多可用区 K8s + podAntiAffinity 打散、数据跨区复制、定期恢复演练,定义 RTO/RPO。