GitOps 工作流:ArgoCD 实践

GitOps 核心原则 GitOps 是一种现代化的持续交付方法论,由 Weaveworks 在 2017 年提出。它将 Git 作为基础设施和应用配置的唯一可信源(Single Source of Truth),通过声明式方式实现持续部署。 根据 ArgoCD 官方文档,GitOps 遵循四大核心原则: 1. 声明式系统 基础设施和应用配置以声明式描述(YAML/Helm/Kustomize)存储在 Git 中: # Git 仓库结构示例 infra-repo/ ├── apps/ │ ├── frontend/ │ │ ├── deployment.yaml │ │ ├── service.yaml │ │ └── configmap.yaml │ └── backend/ │ ├── deployment.yaml │ └── service.yaml ├── helm/ │ └── values-production.yaml └── kustomize/ ├── base/ └── overlays/ ├── staging/ └── production/ 声明式描述的核心价值:配置即文档,Git 历史即审计日志。任何环境变更都可追溯、可回滚。 2. 版本控制 所有变更通过 Git 提交记录,天然具备:...

July 22, 2024 · 6 分钟 · 1125 字 · 徐保金

容量规划与弹性扩容实践

概述 容量规划是 SRE 的核心职责之一。Google SRE Book 将容量规划视为"前瞻性工作",强调基于数据预测而非凭经验猜测。一个没有容量规划的团队,要么在高峰期被流量打垮,要么在低谷期浪费大量资源成本。 从指标采集、数据建模、Kubernetes 弹性扩容配置、陷阱规避四个层面,详细梳理容量规划的工程实践。 关于容量规划的系统方法论,可参考 Google SRE Book - Capacity Planning 中关于容量规划与级联故障的讨论。 一、容量规划的核心思想:基于数据而非猜测 容量规划的三个层次 当前容量评估:系统现在能扛多少?水位是多少? 容量趋势预测:按当前增长趋势,什么时候需要扩容? 弹性伸缩策略:面对突发流量,如何自动应对? 容量规划的前提:可观测性 没有度量就没有管理。容量规划的基础是完善的监控体系,需要持续采集以下指标: 指标类别 具体指标 采集工具 CPU 使用率、负载(1m/5m/15m) node_exporter / cAdvisor 内存 使用量、可用量、OOM 次数 node_exporter / cAdvisor 网络 入站/出站带宽、连接数、丢包率 node_exporter 磁盘 I/O IOPS、读写延迟、队列深度 node_exporter 应用层 QPS、延迟分布、错误率 Prometheus / 自定义指标 中间件 连接池使用率、队列长度、缓存命中率 Exporter / 自定义指标 容量水位定义 不是所有指标都同等重要。需要定义关键资源的容量水位: # 容量水位定义示例 capacity_thresholds: cpu: warning: 60% # 60% 开始关注 critical: 80% # 80% 需要扩容 limit: 90% # 90% 紧急扩容 memory: warning: 70% critical: 85% limit: 95% disk_io: warning: 60% critical: 80% limit: 90% connection: warning: 60% # 连接池使用率 critical: 80% limit: 90% 关键原则:水位线不是拍脑袋定的,而是基于压测数据和历史故障分析得出的。如果你的应用在 CPU 85% 时开始出现延迟劣化,那么 warning 就应该设在 70% 以下。...

June 27, 2024 · 6 分钟 · 1180 字 · 徐保金

容器运行时:containerd 与 CRI 深入解析

概述 Kubernetes v1.24 正式移除了 dockershim,这意味着 Docker Engine 不再作为 K8s 的容器运行时。取而代之的是符合 CRI(Container Runtime Interface)规范的运行时,其中 containerd 是事实上的标准选择。 很多运维人员对 containerd 的认知停留在"Docker 的替代品",但实际上 containerd 的架构、镜像管理、命令行工具都与 Docker 有显著差异。本文深入理解 CRI 规范、containerd 架构、日常运维和从 Docker 迁移的实践。 本文基于 containerd v2.0 和 Kubernetes v1.30。参考 containerd 官方文档 CRI 规范 什么是 CRI CRI(Container Runtime Interface)是 K8s 定义的容器运行时接口规范。K8s 不再直接调用容器运行时,而是通过 gRPC 调用符合 CRI 规范的运行时: kubelet → CRI gRPC → 容器运行时(containerd/CRI-O)→ 容器 CRI 接口定义 CRI 定义了两个核心服务: 服务 功能 关键方法 RuntimeService 容器和 Pod 生命周期 RunPodSandbox, CreateContainer, StartContainer, StopContainer ImageService 镜像管理 ListImages, PullImage, RemoveImage, ImageStatus // CRI 接口简化定义 service RuntimeService { rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse); rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse); rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse); rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse); rpc StartContainer(StartContainerRequest) returns (StartContainerResponse); rpc StopContainer(StopContainerRequest) returns (StopContainerResponse); rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse); rpc ListContainers(ListContainersRequest) returns (ListContainersResponse); rpc ContainerStats(ContainerStatsRequest) returns (ContainerStatsResponse); rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse); } service ImageService { rpc ListImages(ListImagesRequest) returns (ListImagesResponse); rpc PullImage(PullImageRequest) returns (PullImageResponse); rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse); rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse); } 为什么移除 dockershim Docker Engine 不原生支持 CRI,K8s 通过 dockershim 适配层调用 Docker。这带来几个问题:...

June 14, 2024 · 8 分钟 · 1675 字 · 徐保金

Runbook 编写指南:让运维知识可复现

概述 凌晨三点被告警叫醒,面对一个不熟悉的服务,你能多快恢复?如果你需要翻聊天记录、问同事、翻代码才能弄清楚怎么处理,那说明你的团队缺一样东西——Runbook。 Runbook 是 SRE 体系中最基础但也最容易被忽视的工程实践。它是连接"告警"和"行动"的桥梁——告警告诉你"出问题了",Runbook 告诉你"该怎么办"。一个好的 Runbook 能让任何有基础技术能力的工程师在 On-Call 时有效响应告警,而不依赖特定人员的"脑内知识"。 从 Runbook 的作用、结构设计、自动化、版本管理、质量评审到实战模板,详细梳理如何编写和维护高质量的 Runbook。 关于 Runbook 的好的实践,可参考 Google SRE Workbook - Operational Overload 和 PagerDuty - Runbook Guide。 一、Runbook 的作用与价值 什么是 Runbook Runbook 是一份标准化的操作手册,描述了如何诊断和处理特定的运维场景。它的核心特征是: 任何有基础技术能力的工程师,按照 Runbook 的步骤操作,都能正确处理对应的告警或运维任务——而不需要依赖特定人员的主观经验。 没有 Runbook 的代价 没有 Runbook 的故障响应: 告警触发 → On-Call 工程师看到 "Redis 内存使用率 90%" → 不知道该怎么办 → 打电话问张三(张三写过这个服务) → 张三不在线 → 打电话问李四 → 李四说 "可能是某个 key 太大了,你看看" → 工程师花 20 分钟找大 key → 最终处理了,但用了 40 分钟 有 Runbook 的故障响应: 告警触发 → On-Call 工程师看到 "Redis 内存使用率 90%" → 告警中附带 Runbook 链接 → 打开 Runbook,按步骤操作 → Step 1: 执行 redis-cli --bigkeys 扫描大 key → Step 2: 发现 user_session:xxx 占用 500MB → Step 3: 执行 DEL user_session:xxx → 10 分钟恢复 Runbook 的价值 维度 价值 降低 MTTR 按步骤操作比从零排查快得多 降低 On-Call 压力 有文档可依,不需要"什么都知道" 知识沉淀 把个人经验转化为组织知识 新人友好 新人 On-Call 不再需要"跟班学习"数周 驱动自动化 Runbook 中的步骤是自动化的候选对象 Runbook 与其他文档的区别 文档类型 目的 读者 场景 Runbook 处理特定告警/故障的操作步骤 On-Call 工程师 告警触发时 Architecture Doc 描述系统架构和设计决策 所有工程师 理解系统设计 API Doc 描述 API 接口规范 开发者 对接 API Postmortem 分析故障根因和改进项 团队 故障复盘 Playbook 更广泛的工作流程指南 团队 执行复杂任务 二、Runbook 的结构设计 标准结构 一个好的 Runbook 应该包含以下部分:...

June 11, 2024 · 10 分钟 · 1945 字 · 徐保金

Prometheus 服务发现机制详解

概述 在 Prometheus 的监控体系中,服务发现(Service Discovery,简称 SD)是连接"被监控目标"与"采集引擎"的桥梁。当你的基础设施从几台虚拟机扩展到数百个 Kubernetes Pod、跨可用区云主机、Consul 注册节点时,手动维护 static_configs 就变成了一场灾难——每次扩容、缩容、迁移都要改配置、重启 Prometheus,告警还会因为 target 不可达而误报。 Prometheus 原生支持十余种服务发现机制,能够在目标变更后自动感知,无需重启。我将从静态配置出发,逐步深入 file_sd、kubernetes_sd、consul_sd、dns_sd、ec2_sd 等主流方案,并详细讲解 relabel_configs 标签管理这一核心能力,最后给出多集群监控的落地实践。 参考来源:Prometheus 官方文档 — Configuration 一、为什么需要服务发现 1.1 静态配置的局限 先看一个最简单的静态配置: scrape_configs: - job_name: 'node' static_configs: - targets: - '192.168.1.10:9100' - '192.168.1.11:9100' - '192.168.1.12:9100' labels: env: 'production' region: 'beijing' 这在服务器数量固定时没什么问题。但考虑以下场景: 场景 静态配置的痛点 Kubernetes Pod 扩缩容 Pod IP 每次重建都变,手动改配置不现实 云厂商 Auto Scaling 弹性伸缩后新实例无法被监控,存在盲区 蓝绿部署 / 金丝雀发布 新版本实例需要自动加入监控 多机房迁移 IP 段变更,需要批量修改配置 容器化微服务 实例数量随时变化,生命周期短 1.2 服务发现的核心价值 ┌─────────────┐ ┌───────────────────┐ ┌──────────────┐ │ 服务注册中心 │ ← 感知 → │ Prometheus SD │ ← 采集 → │ Target 实例 │ │ (Consul/K8s) │ │ (自动更新 target) │ │ (Exporter) │ └─────────────┘ └───────────────────┘ └──────────────┘ │ ▼ ┌───────────────────┐ │ relabel_configs │ │ (标签过滤/改写) │ └───────────────────┘ 服务发现机制让 Prometheus 从"被动配置"变为"主动感知":...

June 6, 2024 · 7 分钟 · 1466 字 · 徐保金

Python 日志分析脚本实战

概述 日志是系统运行时留下的最真实记录。当线上出问题时,日志是第一现场;当需要洞察系统行为时,日志是最丰富的数据源。但日志分析不是 grep 几个关键词那么简单——面对每天几十 GB 的日志,你需要高效的解析、灵活的聚合、智能的异常检测和清晰的可视化。本文用 Python 从零搭建一套完整的日志分析工具链。 参考来源:Python re 模块文档、pandas 文档 一、日志解析基础 1.1 正则表达式解析 日志解析的核心是把非结构化文本变成结构化数据。正则表达式是最基础也最灵活的工具: import re from datetime import datetime from typing import NamedTuple, Optional class LogEntry(NamedTuple): timestamp: datetime level: str message: str source: Optional[str] = None # 通用应用日志格式:2026-07-10 14:32:01 [ERROR] [app.payment] Payment failed: order=12345 APP_LOG_PATTERN = re.compile( r'(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+' r'\[(?P<level>DEBUG|INFO|WARN|WARNING|ERROR|FATAL)\]\s+' r'(?:\[(?P<source>[\w.]+)\]\s+)?' r'(?P<message>.+)' ) def parse_app_log(line: str) -> Optional[LogEntry]: """解析应用日志行""" match = APP_LOG_PATTERN.match(line.strip()) if not match: return None return LogEntry( timestamp=datetime....

June 5, 2024 · 14 分钟 · 2980 字 · 徐保金

Linux 进程调度器:CFS 原理与调优

概述 进程调度器是操作系统内核的核心组件,它决定了哪个进程在哪个 CPU 上运行、运行多长时间。Linux 自 2.6.23 起采用 CFS(Completely Fair Scheduler)作为默认调度器,经过多年演进,在 6.6 内核中又引入了 EEVDF(Earliest Eligible Virtual Deadline First)替代 CFS。本文深入理解 CFS 的工作原理、nice/cgroup CPU 控制、实时调度、CPU 亲和性等核心主题,并分享生产环境的调优经验。 CFS 调度器原理 设计哲学 CFS 的核心目标是"完全公平"——每个进程按照其权重比例获得 CPU 时间。与传统调度器基于时间片不同,CFS 使用"虚拟运行时间"(vruntime)来跟踪每个进程的 CPU 消耗: vruntime = 实际运行时间 × (NICE_0_LOAD / 进程权重) nice 值为 0 的进程权重为 1024(NICE_0_LOAD) nice 值越低,权重越大,vruntime 增长越慢,获得更多 CPU 时间 CFS 始终选择 vruntime 最小的进程运行 红黑树与调度队列 CFS 使用红黑树维护运行队列,按 vruntime 排序: [vruntime=50] / \ [vruntime=20] [vruntime=80] / \ [vruntime=10] [vruntime=35] 最左节点(vruntime 最小)是下一个被调度的进程 进程运行后 vruntime 增加,被重新插入红黑树 查找、插入、删除均为 O(log N) 调度周期与最小粒度 # 调度周期(目标延迟):所有进程在这段时间内至少运行一次 $ sysctl kernel....

May 29, 2024 · 7 分钟 · 1330 字 · 徐保金

告警自动化处理:从告警风暴到自愈系统的工程实践

概述 凌晨三点,手机震动。你从被窝爬起来,打开电脑,SSH 上去,发现某个服务 CPU 飙升。Kill 进程,重启服务,12 分钟搞定——但你彻底清醒了。四点半又来一条告警:磁盘使用率超 85%。又爬起来,du -sh 定位,删掉过期日志,15 分钟。 这是无数运维工程师的日常。监控做了,告警配了,脚本也写了——但最后一步还是人在跑。而且偏偏在凌晨。 根据 Google SRE Book 的数据,一个典型的 SRE 团队每天接收 50-100 条告警,其中 80% 是噪音,超过 60% 的告警是重复处理过的已知问题。 告警自动化的目标不是消灭告警,而是把人的判断和操作转化为系统的自动响应。我将从告警降噪、分级路由、Runbook 自动化、自愈平台架构、AI 辅助治理五个维度,详细梳理如何构建告警自动化处理体系。 告警现状:为什么需要自动化 告警风暴的根源 告警风暴通常不是监控配置不足,而是配置泛滥的产物。以下是生产环境中最常见的告警问题模式: 问题模式 典型表现 根因 告警泛滥 每天 100+ 条告警,80% 无需人工介入 静态阈值过敏感,缺少聚合和去重 告警疲劳 工程师忽略告警通知,真正故障被淹没 信号噪声比太低,缺少优先级分级 重复告警 同一问题触发多条告警,不同监控视角 缺少告警关联和聚合机制 响应延迟 从告警到人工处理平均 15-30 分钟 缺少自动化响应,依赖人工介入 重复劳动 超过 60% 的告警处理流程完全相同 没有将已知操作沉淀为自动化 Runbook 告警生命周期的五个阶段 一个成熟的告警自动化系统应该覆盖告警的完整生命周期: ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 1. 产生 │────▶│ 2. 降噪 │────▶│ 3....

May 28, 2024 · 23 分钟 · 4850 字 · 徐保金

故障响应框架:SEV 分级与 escalation 流程

概述 故障不可避免,但故障响应的质量决定了影响范围和持续时间。一个成熟的故障响应框架能够在混乱中建立秩序——让正确的人在做正确的事,让信息流向该去的地方,让恢复速度尽可能快。 很多团队在故障发生时面临的真实场景是:告警轰炸、群消息刷屏、不知道谁在负责、重复排查同一个问题、对外信息不一致、故障恢复后说不清楚做了什么。这些问题的根因不是技术能力不足,而是缺乏结构化的响应框架。 从故障分级、响应角色分工、escalation 路径设计、通信模板、时间线记录到恢复策略,详细梳理如何构建一个可执行的故障响应框架。 关于故障响应的系统方法论,可参考 Google SRE Book - Managing Incidents 和 Atlassian Incident Management Handbook。 一、故障分级标准 为什么需要分级 没有分级的故障管理等于没有管理。如果所有故障都按最高优先级处理,结果就是没有真正的最高优先级。故障分级的本质是资源调度优先级——在有限的人力下,让最严重的故障优先获得资源。 SEV 分级标准 采用业界通行的 SEV1-SEV4 四级分类: 级别 定义 影响范围 响应时效 升级条件 示例 SEV1 生产服务完全不可用或核心功能失效 全量或大量用户受影响,营收直接损失 立即响应,<5min 15min 无进展自动升级 支付服务宕机、数据库不可用、核心 API 全部 5xx SEV2 核心功能严重降级 部分用户受影响,业务功能受损 <15min 30min 无进展自动升级 支付成功率下降 20%、P99 延迟劣化 5 倍 SEV3 非核心功能降级或潜在风险 少量用户受影响或无直接影响 <30min(工作时间) 无需自动升级 某非核心服务异常、磁盘水位 80% SEV4 优化建议或已知问题 无用户影响 下一工作日 无需升级 告警阈值优化、文档补充 分级判定要素 故障分级需要考虑多个维度,不是单一指标决定的: # 故障分级判定矩阵 severity_matrix: dimensions: - user_impact: # 用户影响 none: 0 minimal: 1 # <1% 用户受影响 moderate: 2 # 1-10% 用户受影响 significant: 3 # 10-50% 用户受影响 severe: 4 # >50% 用户受影响 - business_impact: # 业务影响 none: 0 low: 1 # 非核心功能,无营收影响 medium: 2 # 核心功能降级,营收轻微影响 high: 3 # 核心功能失效,营收明显损失 critical: 4 # 全站不可用,营收严重损失 - duration: # 持续时间(已持续或预计) unknown: 3 # 未知持续时间按高风险处理 brief: 1 # <5 分钟 short: 2 # 5-30 分钟 medium: 3 # 30 分钟 - 2 小时 long: 4 # >2 小时 # 最高维度决定 SEV 级别 rule: "max(user_impact, business_impact) + duration_bonus" # duration_bonus: 如果持续时间 > 预期,SEV 上调一级 自动分级 理想状态下,故障级别应该能通过告警规则自动确定:...

May 23, 2024 · 9 分钟 · 1796 字 · 徐保金

分布式链路追踪:Jaeger 落地实践

为什么需要分布式追踪 微服务架构下,一个用户请求往往要穿越多个服务。当某个接口耗时从 200ms 飙升到 2s 时,日志散落在 N 台机器上,你很难判断瓶颈在哪一层——是网关转发慢、下游 DB 查询慢、还是某个服务间调用排队? 分布式追踪(Distributed Tracing)解决的就是这个问题:它为每个请求生成一个全局唯一的 Trace ID,在服务间透传,最终在 UI 上画出一条完整的调用链路树,让每一段耗时一目了然。 Jaeger(发音类似"耶格")由 Uber 开源,现为 CNCF 毕业项目。本文基于 Jaeger v1.60+ 和 OpenTelemetry SDK 进行实践。官方文档 分布式追踪核心概念 Trace 一个 Trace 代表一次完整的分布式请求链路,由唯一的 128-bit Trace ID 标识。它是一棵由多个 Span 组成的树形结构: Trace (TraceID: a1b2c3...) ├── Span: HTTP GET /api/orders [gateway] │ ├── Span: RPC GetUser [user-service] │ │ └── Span: SELECT * FROM users [mysql] │ └── Span: RPC GetOrderList [order-service] │ └── Span: Redis GET [redis] Span Span 是追踪的最小单元,记录一次操作的开始和结束。关键字段:...

May 16, 2024 · 6 分钟 · 1074 字 · 徐保金