[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article:1xXEO46a:guest":3,"article:1xXEO46a:related":32},{"id":4,"created_at":5,"updated_at":6,"code":7,"author_id":8,"title":9,"slug":10,"content":11,"content_html":12,"summary":13,"category_slug":14,"visibility":15,"is_pinned":16,"is_recommended":16,"is_approved":17,"view_count":18,"like_count":19,"favorite_count":19,"comment_count":19,"hot_score":20,"recommended_score":19,"status":21,"publish_at":6,"author":22,"is_liked":16,"is_favorite":16},1188,"2026-10-20T20:39:07.024+08:00","2026-10-20T20:40:27.733+08:00","1xXEO46a",11,"Kubernetes 入门实战：从 Pod 到 Deployment 的完整指南","kubernetes-入门实战：从-pod-到-deployment-的完整指南","# Kubernetes 入门实战：从 Pod 到 Deployment 的完整指南\n\nKubernetes 已成为容器编排的事实标准。但\"部署一个 Nginx\"和\"真正用好 K8s\"之间，隔着对工作负载、网络、存储与声明式理念的系统理解。本文从最小概念出发，逐步构建起可用的集群应用。\n\n## 一、为什么需要 Kubernetes\n\n单机 Docker 解决的问题是\"环境一致性\"，K8s 解决的问题是**规模化编排**：\n\n- 自动调度：容器该跑在哪台机器、内存不够如何驱逐；\n- 自动伸缩：流量大了自动加副本，小了自动缩；\n- 自动恢复：容器挂了自动重启，节点挂了自动迁移；\n- 服务发现与负载均衡：天然集成 DNS 与反向代理。\n\n核心思想是**声明式**：你描述\"想要的最终状态\"（Deployment 期望 3 个副本），控制器持续调谐，让现实逼近期望。\n\n## 二、核心对象：从 Pod 到工作负载\n\n### 2.1 Pod：最小调度单元\n\nPod 是一个或多个容器的组合，共享网络命名空间与存储卷。Pod 本身是\"一次性\"的——直接创建 Pod 无法享受自动恢复，所以几乎从不直接使用 Pod，而是通过工作负载控制器管理。\n\n### 2.2 Deployment：无状态应用的标配\n\n```yaml\n# deployment.yaml\napiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: api-server\n  labels:\n    app: api\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: api\n  template:\n    metadata:\n      labels:\n        app: api\n    spec:\n      containers:\n        - name: api\n          image: registry.example.com\u002Fapi:v1.2.0\n          ports:\n            - containerPort: 8080\n          readinessProbe:\n            httpGet:\n              path: \u002Fhealthz\n              port: 8080\n            initialDelaySeconds: 5\n            periodSeconds: 10\n          resources:\n            requests:\n              cpu: 250m\n              memory: 256Mi\n            limits:\n              cpu: 500m\n              memory: 512Mi\n```\n\n**关键配置解读**：\n\n- `replicas: 3`：期望副本数，滚动更新\u002F故障恢复都以它为基准；\n- **readinessProbe（就绪探针）**：就绪前不接入流量，滚动更新零中断；\n- **resources.requests\u002Flimits**：requests 用于调度决策，limits 用于资源上限——不设 limits 的容器可能耗尽节点内存。\n\n### 2.3 滚动更新与回滚\n\n```bash\nkubectl set image deployment\u002Fapi-server api=registry.example.com\u002Fapi:v1.3.0\nkubectl rollout status deployment\u002Fapi-server\nkubectl rollout undo deployment\u002Fapi-server   # 一键回滚到上一版本\n```\n\n## 三、Service 与网络模型\n\n### 3.1 Service：稳定的访问入口\n\nPod 的 IP 是漂移的，Service 提供稳定的虚拟 IP 与 DNS 名称：\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: api-svc\nspec:\n  selector:\n    app: api\n  ports:\n    - port: 80\n      targetPort: 8080\n```\n\n集群内其他服务可通过 `api-svc:80` 访问，DNS 自动解析到后端的 Pod 集合。\n\n### 3.2 Ingress：对外暴露的统一网关\n\n```yaml\napiVersion: networking.k8s.io\u002Fv1\nkind: Ingress\nmetadata:\n  name: api-ingress\nspec:\n  rules:\n    - host: api.example.com\n      http:\n        paths:\n          - path: \u002Fapi\n            pathType: Prefix\n            backend:\n              service:\n                name: api-svc\n                port:\n                  number: 80\n```\n\nIngress 控制器（Nginx Ingress \u002F Traefik）负责把域名+路径路由到对应 Service，统一处理 TLS 与限流。\n\n## 四、ConfigMap 与 Secret：配置分离\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: app-config\ndata:\n  LOG_LEVEL: \"info\"\n  MAX_CONN: \"100\"\n\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: db-secret\ntype: Opaque\nstringData:\n  DB_PASSWORD: \"s3cr3t\"\n```\n\n```yaml\n# 在 Deployment 中引用\nenvFrom:\n  - configMapRef: { name: app-config }\nenv:\n  - name: DB_PASSWORD\n    valueFrom:\n      secretKeyRef: { name: db-secret, key: DB_PASSWORD }\n```\n\n**原则**：镜像不可变，配置可注入。同样的镜像配合不同 ConfigMap 即可跑在不同环境。\n\n## 五、存储：从 emptyDir 到 PVC\n\n| 存储类型 | 生命周期 | 适用 |\n|----------|----------|------|\n| emptyDir | 随 Pod 生灭 | 临时缓存、日志中转 |\n| hostPath | 随节点 | 单节点调试 |\n| PersistentVolumeClaim | 独立于 Pod | 数据库等持久化数据 |\n\n```yaml\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: mysql-data\nspec:\n  accessModes: [\"ReadWriteOnce\"]\n  resources:\n    requests:\n      storage: 20Gi\n```\n\n## 六、常用运维命令速查\n\n```bash\nkubectl get pods -o wide                # 查看 Pod 与所在节点\nkubectl logs -f pod\u002Fapi-server-xxx      # 跟踪日志\nkubectl exec -it pod\u002Fapi-server-xxx -- sh   # 进入容器\nkubectl describe pod api-server-xxx     # 排查事件的黄金命令\nkubectl get events --sort-by=.lastTimestamp   # 集群事件\nkubectl top nodes                       # 节点资源使用率\n```\n\n**排查套路**：Pod 起不来先看 `describe` 的事件；网络不通先确认 Service 的 selector 是否匹配 Pod 标签；镜像拉取失败先检查 imagePullPolicy 与仓库凭证。\n\n## 结语\n\nKubernetes 的入门门槛不在命令，而在**心智模型**：把一切当作\"声明期望 + 控制器调谐\"。理解了 Pod 的生命周期、Deployment 的滚动策略、Service 的稳定入口这三角，你就能安全地把应用跑上集群。建议从本地 minikube 起步，把每个对象都亲手 apply 一遍，再逐步引入探针、资源限制与 Ingress——规模化的可靠性，正是从这些细节中长出来的。\n","\u003Ch1>Kubernetes 入门实战：从 Pod 到 Deployment 的完整指南\u003C\u002Fh1>\n\u003Cp>Kubernetes 已成为容器编排的事实标准。但&quot;部署一个 Nginx&quot;和&quot;真正用好 K8s&quot;之间，隔着对工作负载、网络、存储与声明式理念的系统理解。本文从最小概念出发，逐步构建起可用的集群应用。\u003C\u002Fp>\n\u003Ch2>一、为什么需要 Kubernetes\u003C\u002Fh2>\n\u003Cp>单机 Docker 解决的问题是&quot;环境一致性&quot;，K8s 解决的问题是\u003Cstrong>规模化编排\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>自动调度：容器该跑在哪台机器、内存不够如何驱逐；\u003C\u002Fli>\n\u003Cli>自动伸缩：流量大了自动加副本，小了自动缩；\u003C\u002Fli>\n\u003Cli>自动恢复：容器挂了自动重启，节点挂了自动迁移；\u003C\u002Fli>\n\u003Cli>服务发现与负载均衡：天然集成 DNS 与反向代理。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>核心思想是\u003Cstrong>声明式\u003C\u002Fstrong>：你描述&quot;想要的最终状态&quot;（Deployment 期望 3 个副本），控制器持续调谐，让现实逼近期望。\u003C\u002Fp>\n\u003Ch2>二、核心对象：从 Pod 到工作负载\u003C\u002Fh2>\n\u003Ch3>2.1 Pod：最小调度单元\u003C\u002Fh3>\n\u003Cp>Pod 是一个或多个容器的组合，共享网络命名空间与存储卷。Pod 本身是&quot;一次性&quot;的——直接创建 Pod 无法享受自动恢复，所以几乎从不直接使用 Pod，而是通过工作负载控制器管理。\u003C\u002Fp>\n\u003Ch3>2.2 Deployment：无状态应用的标配\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yaml\"># deployment.yaml\napiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: api-server\n  labels:\n    app: api\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: api\n  template:\n    metadata:\n      labels:\n        app: api\n    spec:\n      containers:\n        - name: api\n          image: registry.example.com\u002Fapi:v1.2.0\n          ports:\n            - containerPort: 8080\n          readinessProbe:\n            httpGet:\n              path: \u002Fhealthz\n              port: 8080\n            initialDelaySeconds: 5\n            periodSeconds: 10\n          resources:\n            requests:\n              cpu: 250m\n              memory: 256Mi\n            limits:\n              cpu: 500m\n              memory: 512Mi\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>关键配置解读\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>replicas: 3\u003C\u002Fcode>：期望副本数，滚动更新\u002F故障恢复都以它为基准；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>readinessProbe（就绪探针）\u003C\u002Fstrong>：就绪前不接入流量，滚动更新零中断；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>resources.requests\u002Flimits\u003C\u002Fstrong>：requests 用于调度决策，limits 用于资源上限——不设 limits 的容器可能耗尽节点内存。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>2.3 滚动更新与回滚\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">kubectl set image deployment\u002Fapi-server api=registry.example.com\u002Fapi:v1.3.0\nkubectl rollout status deployment\u002Fapi-server\nkubectl rollout undo deployment\u002Fapi-server   # 一键回滚到上一版本\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>三、Service 与网络模型\u003C\u002Fh2>\n\u003Ch3>3.1 Service：稳定的访问入口\u003C\u002Fh3>\n\u003Cp>Pod 的 IP 是漂移的，Service 提供稳定的虚拟 IP 与 DNS 名称：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yaml\">apiVersion: v1\nkind: Service\nmetadata:\n  name: api-svc\nspec:\n  selector:\n    app: api\n  ports:\n    - port: 80\n      targetPort: 8080\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>集群内其他服务可通过 \u003Ccode>api-svc:80\u003C\u002Fcode> 访问，DNS 自动解析到后端的 Pod 集合。\u003C\u002Fp>\n\u003Ch3>3.2 Ingress：对外暴露的统一网关\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yaml\">apiVersion: networking.k8s.io\u002Fv1\nkind: Ingress\nmetadata:\n  name: api-ingress\nspec:\n  rules:\n    - host: api.example.com\n      http:\n        paths:\n          - path: \u002Fapi\n            pathType: Prefix\n            backend:\n              service:\n                name: api-svc\n                port:\n                  number: 80\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Ingress 控制器（Nginx Ingress \u002F Traefik）负责把域名+路径路由到对应 Service，统一处理 TLS 与限流。\u003C\u002Fp>\n\u003Ch2>四、ConfigMap 与 Secret：配置分离\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-yaml\">apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: app-config\ndata:\n  LOG_LEVEL: &quot;info&quot;\n  MAX_CONN: &quot;100&quot;\n\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: db-secret\ntype: Opaque\nstringData:\n  DB_PASSWORD: &quot;s3cr3t&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-yaml\"># 在 Deployment 中引用\nenvFrom:\n  - configMapRef: { name: app-config }\nenv:\n  - name: DB_PASSWORD\n    valueFrom:\n      secretKeyRef: { name: db-secret, key: DB_PASSWORD }\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>原则\u003C\u002Fstrong>：镜像不可变，配置可注入。同样的镜像配合不同 ConfigMap 即可跑在不同环境。\u003C\u002Fp>\n\u003Ch2>五、存储：从 emptyDir 到 PVC\u003C\u002Fh2>\n\u003Cp>| 存储类型 | 生命周期 | 适用 |\n|----------|----------|------|\n| emptyDir | 随 Pod 生灭 | 临时缓存、日志中转 |\n| hostPath | 随节点 | 单节点调试 |\n| PersistentVolumeClaim | 独立于 Pod | 数据库等持久化数据 |\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yaml\">apiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: mysql-data\nspec:\n  accessModes: [&quot;ReadWriteOnce&quot;]\n  resources:\n    requests:\n      storage: 20Gi\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>六、常用运维命令速查\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\">kubectl get pods -o wide                # 查看 Pod 与所在节点\nkubectl logs -f pod\u002Fapi-server-xxx      # 跟踪日志\nkubectl exec -it pod\u002Fapi-server-xxx -- sh   # 进入容器\nkubectl describe pod api-server-xxx     # 排查事件的黄金命令\nkubectl get events --sort-by=.lastTimestamp   # 集群事件\nkubectl top nodes                       # 节点资源使用率\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>排查套路\u003C\u002Fstrong>：Pod 起不来先看 \u003Ccode>describe\u003C\u002Fcode> 的事件；网络不通先确认 Service 的 selector 是否匹配 Pod 标签；镜像拉取失败先检查 imagePullPolicy 与仓库凭证。\u003C\u002Fp>\n\u003Ch2>结语\u003C\u002Fh2>\n\u003Cp>Kubernetes 的入门门槛不在命令，而在\u003Cstrong>心智模型\u003C\u002Fstrong>：把一切当作&quot;声明期望 + 控制器调谐&quot;。理解了 Pod 的生命周期、Deployment 的滚动策略、Service 的稳定入口这三角，你就能安全地把应用跑上集群。建议从本地 minikube 起步，把每个对象都亲手 apply 一遍，再逐步引入探针、资源限制与 Ingress——规模化的可靠性，正是从这些细节中长出来的。\u003C\u002Fp>\n","从 Pod、Deployment、Service 到 Ingress 与存储卷，梳理 Kubernetes 核心对象与调度原理，给出应用容器化上云的完整指南。","运维","public",false,true,2,0,39834.288175842776,"published",{"id":8,"created_at":23,"updated_at":24,"code":25,"username":26,"display_name":27,"role":28,"avatar_url":29,"is_active":17,"is_banned":16,"follower_count":30,"following_count":19,"last_login_at":31},"2024-09-12T11:58:24.365+08:00","2026-10-20T20:39:06.889+08:00","kbXvOKMJ","1726113504301","打工人","user","\u002Fcuts\u002F1726370661979da4b1661617b527c821952001.jpg",1,"2026-10-20T20:39:06+08:00",[33,53,65],{"id":34,"created_at":35,"updated_at":36,"code":37,"author_id":38,"title":39,"slug":40,"content":41,"content_html":42,"summary":43,"category_slug":14,"visibility":15,"is_pinned":16,"is_recommended":16,"is_approved":17,"view_count":38,"like_count":19,"favorite_count":19,"comment_count":19,"hot_score":44,"recommended_score":19,"status":21,"publish_at":36,"author":45,"is_liked":16,"is_favorite":16},1177,"2026-10-20T18:08:49.796+08:00","2026-10-20T18:09:29.045+08:00","M9Xd7n8J",3,"Linux 服务器性能排查实战：从 CPU、内存到磁盘的完整方法论","linux-服务器性能排查实战：从-cpu、内存到磁盘的完整方法论","# Linux 服务器性能排查实战：从 CPU、内存到磁盘的完整方法论\n\n线上服务出问题时，最怕的不是故障本身，而是无从下手。本文沉淀一套可复用的 Linux 性能排查方法论：从现象定位到根因分析，每一步都有对应的工具和判断标准。\n\n## 一、总览：先看整体，再查细节\n\n登录服务器后别急着敲命令，先回答三个问题：**负载高不高？谁在消耗？瓶颈在哪？**\n\n```bash\nuptime                       # 负载均值 load average: 0.12, 0.25, 0.30\nfree -h                      # 内存与 swap 使用\ndf -h                        # 磁盘空间\ndmesg -T | tail -20          # 内核日志，OOM \u002F IO 错误都在这里\n```\n\n`load average` 的三个数字分别代表 1\u002F5\u002F15 分钟平均负载。**判断标准：负载接近 CPU 核数视为满载**。例如 8 核机器 load=8 已饱和，load=16 说明有进程在排队等待 CPU。\n\n## 二、CPU 排查：谁吃掉了我的 CPU？\n\n### 2.1 实时观测\n\n```bash\ntop                          # 按 P 按 CPU 排序，按 M 按内存排序\nhtop                         # 更友好的交互界面\nmpstat -P ALL 1              # 每核占用率，判断是否单核打满\n```\n\n### 2.2 定位到线程\n\ntop 里看到的进程 PID，进一步定位是哪个线程在烧 CPU：\n\n```bash\ntop -H -p \u003CPID>              # 查看进程内所有线程的 CPU\n```\n\n### 2.3 抓取线程栈\n\n```bash\n# 记下 CPU 最高的线程 TID，转为 16 进制\nprintf \"0x%x\\n\" \u003CTID>\n# 抓取该线程的调用栈（Java 用 jstack，C\u002FGo 用 gdb 或 perf）\nperf top -p \u003CPID>            # 实时采样函数热点\n```\n\n## 三、内存排查：从 RSS 到 OOM\n\n```bash\nfree -h                      # total \u002F used \u002F available\nvmstat 1 5                   # si\u002Fso 大于 0 说明在持续换页，内存告急\nps aux --sort=-rss | head    # 按物理内存占用排序找大户\n```\n\n### 3.1 OOM Killer 的蛛丝马迹\n\n```bash\ndmesg -T | grep -i \"out of memory\"    # 内核 OOM 记录\njournalctl -k -f                      # systemd 环境下看内核日志\n```\n\n被 OOM 杀的进程会留下完整记录：进程名、占用内存、触发时刻。**常见根因**：Java 堆设置超过容器限制、Go 程序缓存无上限、Redis 未设置 maxmemory。\n\n### 3.2 排查泄漏的工具\n\n```bash\n# Go 程序\ngo tool pprof http:\u002F\u002Flocalhost:6060\u002Fdebug\u002Fpprof\u002Fheap\n# Java 程序\njmap -heap \u003CPID>\njstat -gc \u003CPID> 1000 10\n# 通用：观察 RSS 是否随流量单调增长\n```\n\n## 四、磁盘排查：IO 打满与空间告急\n\n### 4.1 空间问题\n\n```bash\ndf -h                          # 各分区使用率\ndu -sh \u002F* 2>\u002Fdev\u002Fnull | sort -rh | head -10   # 逐层定位大目录\nfind \u002Fvar\u002Flog -size +100M -ls  # 找出巨型日志\n```\n\n### 4.2 IO 性能问题\n\n```bash\niostat -x 1                     # %util 接近 100% 说明磁盘饱和\niotop                           # 哪个进程在读写\ndstat -d                        # 读写吞吐\n```\n\n**IO 饱和典型场景**：MySQL 无索引全表扫描、日志写入无缓冲、ES 频繁 merge。定位到进程后结合应用日志分析。\n\n## 五、网络排查：延迟与连接数\n\n```bash\nss -s                          # 连接数统计\nss -t state established | wc -l  # 当前 ESTABLISHED 连接数\nnetstat -i                     # 网卡丢包率\nsar -n DEV 1                   # 网卡吞吐与错误\n```\n\n连接数异常高的排查思路：检查是否有连接泄漏（未释放的连接池）、是否有短连接风暴、是否被攻击（半连接 SYN Flood 用 `ss -t state syn-recv` 观察）。\n\n## 六、实战案例：一次 CPU 飙高的完整排障\n\n**现象**：告警 8 核机器 load 飙到 20，服务响应延迟变大。\n\n**排查步骤**：\n\n```bash\n# 1. 确认负载与 CPU\nuptime                         # load 20\nmpstat -P ALL 1                # 8 个核全部 100%\n\n# 2. 找 CPU 大户\ntop -o %CPU -n 1 | head        # 发现 java PID 12345 占 780%\n\n# 3. 定位线程与栈\ntop -H -p 12345 | head         # 线程 22333 最热\nprintf \"0x%x\\n\" 22333          # 0x573d\njstack 12345 | grep -A 20 0x573d   # 栈指向 JSON 序列化热点\n\n# 4. 确认根因\n# 日志显示请求量翻倍，且序列化逻辑存在重复 String 拼接\n```\n\n**结论**：流量高峰 + 热点代码路径上的低效序列化，双重叠加导致 CPU 饱和。优化序列化实现后负载恢复正常。\n\n## 七、排查工具箱速查\n\n| 场景 | 首选工具 | 辅助工具 |\n|------|----------|----------|\n| 负载高 | uptime \u002F top | mpstat \u002F htop |\n| CPU 占用 | top -H | perf top \u002F pidstat |\n| 内存不足 | free \u002F vmstat | dmesg(OOM) \u002F ps rss |\n| 磁盘空间 | df \u002F du | find |\n| 磁盘 IO | iostat -x | iotop \u002F dstat |\n| 网络 | ss \u002F sar -n | netstat \u002F tcpdump |\n| 应用栈 | jstack \u002F gdb \u002F pprof | perf \u002F flamegraph |\n\n## 结语\n\n性能排查是\"现象 → 数据 → 定位 → 验证\"的循环，不是玄学。**先把工具链备齐，再按 CPU → 内存 → 磁盘 → 网络 → 应用栈的顺序逐层收敛**，大多数问题都能在十分钟内找到突破口。记住：观察要全面，动手要克制，每一步结论都要有数据支撑。\n","\u003Ch1>Linux 服务器性能排查实战：从 CPU、内存到磁盘的完整方法论\u003C\u002Fh1>\n\u003Cp>线上服务出问题时，最怕的不是故障本身，而是无从下手。本文沉淀一套可复用的 Linux 性能排查方法论：从现象定位到根因分析，每一步都有对应的工具和判断标准。\u003C\u002Fp>\n\u003Ch2>一、总览：先看整体，再查细节\u003C\u002Fh2>\n\u003Cp>登录服务器后别急着敲命令，先回答三个问题：\u003Cstrong>负载高不高？谁在消耗？瓶颈在哪？\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">uptime                       # 负载均值 load average: 0.12, 0.25, 0.30\nfree -h                      # 内存与 swap 使用\ndf -h                        # 磁盘空间\ndmesg -T | tail -20          # 内核日志，OOM \u002F IO 错误都在这里\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>load average\u003C\u002Fcode> 的三个数字分别代表 1\u002F5\u002F15 分钟平均负载。\u003Cstrong>判断标准：负载接近 CPU 核数视为满载\u003C\u002Fstrong>。例如 8 核机器 load=8 已饱和，load=16 说明有进程在排队等待 CPU。\u003C\u002Fp>\n\u003Ch2>二、CPU 排查：谁吃掉了我的 CPU？\u003C\u002Fh2>\n\u003Ch3>2.1 实时观测\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">top                          # 按 P 按 CPU 排序，按 M 按内存排序\nhtop                         # 更友好的交互界面\nmpstat -P ALL 1              # 每核占用率，判断是否单核打满\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>2.2 定位到线程\u003C\u002Fh3>\n\u003Cp>top 里看到的进程 PID，进一步定位是哪个线程在烧 CPU：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">top -H -p &lt;PID&gt;              # 查看进程内所有线程的 CPU\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>2.3 抓取线程栈\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 记下 CPU 最高的线程 TID，转为 16 进制\nprintf &quot;0x%x\\n&quot; &lt;TID&gt;\n# 抓取该线程的调用栈（Java 用 jstack，C\u002FGo 用 gdb 或 perf）\nperf top -p &lt;PID&gt;            # 实时采样函数热点\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>三、内存排查：从 RSS 到 OOM\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\">free -h                      # total \u002F used \u002F available\nvmstat 1 5                   # si\u002Fso 大于 0 说明在持续换页，内存告急\nps aux --sort=-rss | head    # 按物理内存占用排序找大户\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>3.1 OOM Killer 的蛛丝马迹\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">dmesg -T | grep -i &quot;out of memory&quot;    # 内核 OOM 记录\njournalctl -k -f                      # systemd 环境下看内核日志\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>被 OOM 杀的进程会留下完整记录：进程名、占用内存、触发时刻。\u003Cstrong>常见根因\u003C\u002Fstrong>：Java 堆设置超过容器限制、Go 程序缓存无上限、Redis 未设置 maxmemory。\u003C\u002Fp>\n\u003Ch3>3.2 排查泄漏的工具\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\"># Go 程序\ngo tool pprof http:\u002F\u002Flocalhost:6060\u002Fdebug\u002Fpprof\u002Fheap\n# Java 程序\njmap -heap &lt;PID&gt;\njstat -gc &lt;PID&gt; 1000 10\n# 通用：观察 RSS 是否随流量单调增长\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>四、磁盘排查：IO 打满与空间告急\u003C\u002Fh2>\n\u003Ch3>4.1 空间问题\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">df -h                          # 各分区使用率\ndu -sh \u002F* 2&gt;\u002Fdev\u002Fnull | sort -rh | head -10   # 逐层定位大目录\nfind \u002Fvar\u002Flog -size +100M -ls  # 找出巨型日志\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.2 IO 性能问题\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">iostat -x 1                     # %util 接近 100% 说明磁盘饱和\niotop                           # 哪个进程在读写\ndstat -d                        # 读写吞吐\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>IO 饱和典型场景\u003C\u002Fstrong>：MySQL 无索引全表扫描、日志写入无缓冲、ES 频繁 merge。定位到进程后结合应用日志分析。\u003C\u002Fp>\n\u003Ch2>五、网络排查：延迟与连接数\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\">ss -s                          # 连接数统计\nss -t state established | wc -l  # 当前 ESTABLISHED 连接数\nnetstat -i                     # 网卡丢包率\nsar -n DEV 1                   # 网卡吞吐与错误\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>连接数异常高的排查思路：检查是否有连接泄漏（未释放的连接池）、是否有短连接风暴、是否被攻击（半连接 SYN Flood 用 \u003Ccode>ss -t state syn-recv\u003C\u002Fcode> 观察）。\u003C\u002Fp>\n\u003Ch2>六、实战案例：一次 CPU 飙高的完整排障\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>现象\u003C\u002Fstrong>：告警 8 核机器 load 飙到 20，服务响应延迟变大。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>排查步骤\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 1. 确认负载与 CPU\nuptime                         # load 20\nmpstat -P ALL 1                # 8 个核全部 100%\n\n# 2. 找 CPU 大户\ntop -o %CPU -n 1 | head        # 发现 java PID 12345 占 780%\n\n# 3. 定位线程与栈\ntop -H -p 12345 | head         # 线程 22333 最热\nprintf &quot;0x%x\\n&quot; 22333          # 0x573d\njstack 12345 | grep -A 20 0x573d   # 栈指向 JSON 序列化热点\n\n# 4. 确认根因\n# 日志显示请求量翻倍，且序列化逻辑存在重复 String 拼接\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>结论\u003C\u002Fstrong>：流量高峰 + 热点代码路径上的低效序列化，双重叠加导致 CPU 饱和。优化序列化实现后负载恢复正常。\u003C\u002Fp>\n\u003Ch2>七、排查工具箱速查\u003C\u002Fh2>\n\u003Cp>| 场景 | 首选工具 | 辅助工具 |\n|------|----------|----------|\n| 负载高 | uptime \u002F top | mpstat \u002F htop |\n| CPU 占用 | top -H | perf top \u002F pidstat |\n| 内存不足 | free \u002F vmstat | dmesg(OOM) \u002F ps rss |\n| 磁盘空间 | df \u002F du | find |\n| 磁盘 IO | iostat -x | iotop \u002F dstat |\n| 网络 | ss \u002F sar -n | netstat \u002F tcpdump |\n| 应用栈 | jstack \u002F gdb \u002F pprof | perf \u002F flamegraph |\u003C\u002Fp>\n\u003Ch2>结语\u003C\u002Fh2>\n\u003Cp>性能排查是&quot;现象 → 数据 → 定位 → 验证&quot;的循环，不是玄学。\u003Cstrong>先把工具链备齐，再按 CPU → 内存 → 磁盘 → 网络 → 应用栈的顺序逐层收敛\u003C\u002Fstrong>，大多数问题都能在十分钟内找到突破口。记住：观察要全面，动手要克制，每一步结论都要有数据支撑。\u003C\u002Fp>\n","沉淀一套可复用的 Linux 性能排查方法论：从 CPU、内存、磁盘、网络到应用栈逐层收敛，每个场景配备工具链与判断标准，并附完整实战排障案例。",39834.3367644271,{"id":38,"created_at":46,"updated_at":47,"code":48,"username":49,"display_name":50,"role":28,"avatar_url":51,"is_active":17,"is_banned":16,"follower_count":30,"following_count":19,"last_login_at":52},"2023-12-05T17:38:27.845+08:00","2026-10-21T00:01:41.406+08:00","r2Xr2XOR","1701769107783","小卒子","\u002Fcuts\u002F17021221850442010-06-29_233540.jpg","2026-10-21T00:01:41+08:00",{"id":54,"created_at":55,"updated_at":56,"code":57,"author_id":8,"title":58,"slug":59,"content":60,"content_html":61,"summary":62,"category_slug":14,"visibility":15,"is_pinned":16,"is_recommended":16,"is_approved":17,"view_count":18,"like_count":19,"favorite_count":19,"comment_count":19,"hot_score":63,"recommended_score":19,"status":21,"publish_at":56,"author":64,"is_liked":16,"is_favorite":16},1190,"2026-10-20T20:39:07.177+08:00","2026-10-20T20:40:25.406+08:00","yj6yR789","CI\u002FCD 流水线实战：从代码提交到生产部署的自动化之路","ci\u002Fcd-流水线实战：从代码提交到生产部署的自动化之路","# CI\u002FCD 流水线实战：从代码提交到生产部署的自动化之路\n\n持续集成（CI）与持续部署（CD）的核心价值，是把\"提交代码 → 构建 → 测试 → 部署\"这条链路从手工操作变为自动化流水线。本文以 GitHub Actions 为例，完整设计一条生产可用的 CI\u002FCD 流水线，并沉淀各阶段的工程经验。\n\n## 一、流水线的四阶段模型\n\n一条健康的流水线由四个阶段组成，每个阶段都有明确的产出与门禁：\n\n```\n提交 → ① 静态检查 → ② 单元测试 → ③ 构建产物 → ④ 部署发布\n        lint\u002Fvet     test\u002Frace    镜像\u002F二进制     staging → prod\n```\n\n**门禁原则**：上一阶段失败立即终止，绝不让劣质代码流向下一阶段。\n\n## 二、阶段一：静态检查\n\n```yaml\nname: CI\non:\n  push:\n    branches: [main]\n  pull_request:\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-go@v5\n        with:\n          go-version: \"1.24\"\n      - run: go vet .\u002F...\n      - run: gofmt -l .\n      - run: go run honnef.co\u002Fgo\u002Ftools\u002Fcmd\u002Fstaticcheck@latest .\u002F...\n```\n\n静态检查的目的是**在运行前拦截低级错误**：未使用的变量、可疑的代码路径、风格不一致。前端项目对应 `npm run lint` 与 `npm run typecheck`。\n\n## 三、阶段二：单元测试\n\n```yaml\n  test:\n    runs-on: ubuntu-latest\n    needs: lint            # 依赖上一阶段\n    services:\n      mysql:\n        image: mysql:8.0\n        env:\n          MYSQL_ROOT_PASSWORD: test\n        ports: [\"3306:3306\"]\n        options: >-\n          --health-cmd \"mysqladmin ping\" --health-interval 5s\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - run: go test -race -coverprofile=coverage.out .\u002F...\n      - uses: actions\u002Fupload-artifact@v4\n        with:\n          name: coverage\n          path: coverage.out\n```\n\n**工程要点**：\n- `-race` 检测数据竞争，这是并发代码的必修课；\n- 用 `services` 启动真实 MySQL\u002FRedis 跑集成测试，避免\"测试过了生产挂了\"；\n- 覆盖率产物上传，配合 Codecov 等工具追踪趋势。\n\n## 四、阶段三：构建产物\n\n```yaml\n  build:\n    runs-on: ubuntu-latest\n    needs: test\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: docker\u002Fsetup-buildx-action@v3\n      - uses: docker\u002Flogin-action@v3\n        with:\n          registry: registry.example.com\n          username: ${{ secrets.REGISTRY_USER }}\n          password: ${{ secrets.REGISTRY_PASSWORD }}\n      - uses: docker\u002Fbuild-push-action@v6\n        with:\n          push: true\n          tags: |\n            registry.example.com\u002Fapp\u002Fapi:${{ github.sha }}\n            registry.example.com\u002Fapp\u002Fapi:latest\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n```\n\n**镜像标签策略**：用 commit SHA 打不可变标签（可精确定位回滚点），`latest` 仅作快捷引用，生产部署永远引用 SHA 标签。\n\n## 五、阶段四：部署发布\n\n### 5.1 分环境审批门禁\n\n```yaml\n  deploy-staging:\n    runs-on: ubuntu-latest\n    needs: build\n    environment: staging\n    steps:\n      - run: kubectl set image deployment\u002Fapi api=registry.example.com\u002Fapp\u002Fapi:${{ github.sha }}\n\n  deploy-production:\n    runs-on: ubuntu-latest\n    needs: deploy-staging\n    environment:\n      name: production\n      url: https:\u002F\u002Fwww.example.com\n    steps:\n      - run: kubectl set image deployment\u002Fapi api=registry.example.com\u002Fapp\u002Fapi:${{ github.sha }}\n```\n\nGitHub 的 `environment` 机制天然支持\"staging 自动部署 + production 需人工批准\"，既快又稳。\n\n### 5.2 部署后的验证\n\n```yaml\n      - name: 健康检查\n        run: |\n          kubectl rollout status deployment\u002Fapi --timeout=120s\n          curl -sf http:\u002F\u002Fapi.example.com\u002Fhealthz\n      - name: 失败自动回滚\n        if: failure()\n        run: kubectl rollout undo deployment\u002Fapi\n```\n\n**金丝雀意识**：流量敏感的发布建议逐步放量（先 10% 再 100%），用 Argo Rollouts 或 服务网格实现。\n\n## 六、流水线的十个工程经验\n\n1. **流水线即代码**：所有配置进仓库，评审与版本化随代码走；\n2. **密钥绝不进 YAML**：全部用 CI 平台的 secrets 引用；\n3. **幂等部署**：同一 SHA 部署两次结果一致，杜绝\"再点一次\"的忐忑；\n4. **构建与部署分离**：制品先落仓库再部署，构建失败不产生部署；\n5. **缓存要聪明**：依赖缓存（Go mod \u002F npm cache）命中率直接决定构建耗时；\n6. **测试要真实**：单测覆盖逻辑，集成测试连接真实中间件；\n7. **流水线要快**：超过 10 分钟的 CI 会让团队下意识绕开它；\n8. **失败要可读**：日志分级输出，失败步骤定位到具体命令；\n9. **发布窗口意识**：生产发布挂红灯、禁发时间等业务规则写进门禁；\n10. **度量流水线本身**：构建时长、测试通过率、部署频次，都是团队健康的体温计。\n\n## 结语\n\nCI\u002FCD 的价值不在\"自动\"二字，而在**让每次变更都经过同样的质量关卡**。从静态检查到测试，从构建到带门禁的部署，流水线把\"靠人把关\"升级为\"靠机制把关\"。搭建流水线不难，难的是让团队真正信任它、依赖它。当发布从\"心惊胆战的手工操作\"变成\"一键触发的标准流程\"，你就获得了持续交付的真正自由。\n","\u003Ch1>CI\u002FCD 流水线实战：从代码提交到生产部署的自动化之路\u003C\u002Fh1>\n\u003Cp>持续集成（CI）与持续部署（CD）的核心价值，是把&quot;提交代码 → 构建 → 测试 → 部署&quot;这条链路从手工操作变为自动化流水线。本文以 GitHub Actions 为例，完整设计一条生产可用的 CI\u002FCD 流水线，并沉淀各阶段的工程经验。\u003C\u002Fp>\n\u003Ch2>一、流水线的四阶段模型\u003C\u002Fh2>\n\u003Cp>一条健康的流水线由四个阶段组成，每个阶段都有明确的产出与门禁：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>提交 → ① 静态检查 → ② 单元测试 → ③ 构建产物 → ④ 部署发布\n        lint\u002Fvet     test\u002Frace    镜像\u002F二进制     staging → prod\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>门禁原则\u003C\u002Fstrong>：上一阶段失败立即终止，绝不让劣质代码流向下一阶段。\u003C\u002Fp>\n\u003Ch2>二、阶段一：静态检查\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-yaml\">name: CI\non:\n  push:\n    branches: [main]\n  pull_request:\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-go@v5\n        with:\n          go-version: &quot;1.24&quot;\n      - run: go vet .\u002F...\n      - run: gofmt -l .\n      - run: go run honnef.co\u002Fgo\u002Ftools\u002Fcmd\u002Fstaticcheck@latest .\u002F...\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>静态检查的目的是\u003Cstrong>在运行前拦截低级错误\u003C\u002Fstrong>：未使用的变量、可疑的代码路径、风格不一致。前端项目对应 \u003Ccode>npm run lint\u003C\u002Fcode> 与 \u003Ccode>npm run typecheck\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Ch2>三、阶段二：单元测试\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-yaml\">  test:\n    runs-on: ubuntu-latest\n    needs: lint            # 依赖上一阶段\n    services:\n      mysql:\n        image: mysql:8.0\n        env:\n          MYSQL_ROOT_PASSWORD: test\n        ports: [&quot;3306:3306&quot;]\n        options: &gt;-\n          --health-cmd &quot;mysqladmin ping&quot; --health-interval 5s\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - run: go test -race -coverprofile=coverage.out .\u002F...\n      - uses: actions\u002Fupload-artifact@v4\n        with:\n          name: coverage\n          path: coverage.out\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>工程要点\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>-race\u003C\u002Fcode> 检测数据竞争，这是并发代码的必修课；\u003C\u002Fli>\n\u003Cli>用 \u003Ccode>services\u003C\u002Fcode> 启动真实 MySQL\u002FRedis 跑集成测试，避免&quot;测试过了生产挂了&quot;；\u003C\u002Fli>\n\u003Cli>覆盖率产物上传，配合 Codecov 等工具追踪趋势。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>四、阶段三：构建产物\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-yaml\">  build:\n    runs-on: ubuntu-latest\n    needs: test\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: docker\u002Fsetup-buildx-action@v3\n      - uses: docker\u002Flogin-action@v3\n        with:\n          registry: registry.example.com\n          username: ${{ secrets.REGISTRY_USER }}\n          password: ${{ secrets.REGISTRY_PASSWORD }}\n      - uses: docker\u002Fbuild-push-action@v6\n        with:\n          push: true\n          tags: |\n            registry.example.com\u002Fapp\u002Fapi:${{ github.sha }}\n            registry.example.com\u002Fapp\u002Fapi:latest\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>镜像标签策略\u003C\u002Fstrong>：用 commit SHA 打不可变标签（可精确定位回滚点），\u003Ccode>latest\u003C\u002Fcode> 仅作快捷引用，生产部署永远引用 SHA 标签。\u003C\u002Fp>\n\u003Ch2>五、阶段四：部署发布\u003C\u002Fh2>\n\u003Ch3>5.1 分环境审批门禁\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yaml\">  deploy-staging:\n    runs-on: ubuntu-latest\n    needs: build\n    environment: staging\n    steps:\n      - run: kubectl set image deployment\u002Fapi api=registry.example.com\u002Fapp\u002Fapi:${{ github.sha }}\n\n  deploy-production:\n    runs-on: ubuntu-latest\n    needs: deploy-staging\n    environment:\n      name: production\n      url: https:\u002F\u002Fwww.example.com\n    steps:\n      - run: kubectl set image deployment\u002Fapi api=registry.example.com\u002Fapp\u002Fapi:${{ github.sha }}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>GitHub 的 \u003Ccode>environment\u003C\u002Fcode> 机制天然支持&quot;staging 自动部署 + production 需人工批准&quot;，既快又稳。\u003C\u002Fp>\n\u003Ch3>5.2 部署后的验证\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yaml\">      - name: 健康检查\n        run: |\n          kubectl rollout status deployment\u002Fapi --timeout=120s\n          curl -sf http:\u002F\u002Fapi.example.com\u002Fhealthz\n      - name: 失败自动回滚\n        if: failure()\n        run: kubectl rollout undo deployment\u002Fapi\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>金丝雀意识\u003C\u002Fstrong>：流量敏感的发布建议逐步放量（先 10% 再 100%），用 Argo Rollouts 或 服务网格实现。\u003C\u002Fp>\n\u003Ch2>六、流水线的十个工程经验\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\u003Cstrong>流水线即代码\u003C\u002Fstrong>：所有配置进仓库，评审与版本化随代码走；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>密钥绝不进 YAML\u003C\u002Fstrong>：全部用 CI 平台的 secrets 引用；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>幂等部署\u003C\u002Fstrong>：同一 SHA 部署两次结果一致，杜绝&quot;再点一次&quot;的忐忑；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>构建与部署分离\u003C\u002Fstrong>：制品先落仓库再部署，构建失败不产生部署；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>缓存要聪明\u003C\u002Fstrong>：依赖缓存（Go mod \u002F npm cache）命中率直接决定构建耗时；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>测试要真实\u003C\u002Fstrong>：单测覆盖逻辑，集成测试连接真实中间件；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>流水线要快\u003C\u002Fstrong>：超过 10 分钟的 CI 会让团队下意识绕开它；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>失败要可读\u003C\u002Fstrong>：日志分级输出，失败步骤定位到具体命令；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>发布窗口意识\u003C\u002Fstrong>：生产发布挂红灯、禁发时间等业务规则写进门禁；\u003C\u002Fli>\n\u003Cli>\u003Cstrong>度量流水线本身\u003C\u002Fstrong>：构建时长、测试通过率、部署频次，都是团队健康的体温计。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>结语\u003C\u002Fh2>\n\u003Cp>CI\u002FCD 的价值不在&quot;自动&quot;二字，而在\u003Cstrong>让每次变更都经过同样的质量关卡\u003C\u002Fstrong>。从静态检查到测试，从构建到带门禁的部署，流水线把&quot;靠人把关&quot;升级为&quot;靠机制把关&quot;。搭建流水线不难，难的是让团队真正信任它、依赖它。当发布从&quot;心惊胆战的手工操作&quot;变成&quot;一键触发的标准流程&quot;，你就获得了持续交付的真正自由。\u003C\u002Fp>\n","从 Git 分支模型、流水线设计到镜像构建与灰度发布，落地从代码提交到生产部署的自动化链路。",39834.28813139833,{"id":8,"created_at":23,"updated_at":24,"code":25,"username":26,"display_name":27,"role":28,"avatar_url":29,"is_active":17,"is_banned":16,"follower_count":30,"following_count":19,"last_login_at":31},{"id":66,"created_at":67,"updated_at":68,"code":69,"author_id":8,"title":70,"slug":71,"content":72,"content_html":73,"summary":74,"category_slug":14,"visibility":15,"is_pinned":16,"is_recommended":16,"is_approved":17,"view_count":18,"like_count":19,"favorite_count":19,"comment_count":19,"hot_score":75,"recommended_score":19,"status":21,"publish_at":68,"author":76,"is_liked":16,"is_favorite":16},1187,"2026-10-20T20:39:06.963+08:00","2026-10-20T20:40:05.899+08:00","AkKWly62","Prometheus 监控实战：从指标采集到告警体系的完整搭建","prometheus-监控实战：从指标采集到告警体系的完整搭建","# Prometheus 监控实战：从指标采集到告警体系的完整搭建\n\n监控是运维的\"眼睛\"。在 Prometheus 之前，监控体系往往靠 Zabbix 等轮询式工具，指标零散、告警滞后。Prometheus 以\"拉取式采集 + 多维标签 + PromQL\"重新定义了监控标准，成为云原生时代的监控事实标准。本文从指标模型讲起，完整搭建一套可用的监控告警体系。\n\n## 一、核心概念：指标模型与拉取架构\n\nPrometheus 的核心是**指标（Metric）**，格式为 `指标名{标签=值} 数值`：\n\n```\nhttp_requests_total{method=\"GET\", status=\"200\"} 1024\n```\n\n- **指标名**描述测量对象，**标签**描述维度，**样本值**为当前数值；\n- 同一指标不同标签组合即不同时间序列（time series）。\n\n架构上 Prometheus **主动拉取**（scrape）目标暴露的 `\u002Fmetrics` 端点，而非等目标推送。这意味着：\n- 目标需要暴露 HTTP 端点（Exporter 就是干这个的）；\n- 每个目标配置抓取间隔与超时；\n- 无目标上报导致的历史数据可通过 `--storage.tsdb.retention.time` 控制保留时长。\n\n## 二、指标体系搭建\n\n### 2.1 采集层：Exporter 生态\n\n| 采集对象 | Exporter | 端点示例 |\n|----------|----------|----------|\n| 服务器基础指标 | node_exporter | `node_cpu_seconds_total` |\n| MySQL | mysqld_exporter | `mysql_global_status_threads_connected` |\n| Redis | redis_exporter | `redis_connected_clients` |\n| Nginx | nginx_exporter | `nginx_http_requests_total` |\n| 应用业务指标 | 自研暴露 | `http_requests_total` |\n\n以 node_exporter 为例：\n\n```bash\n.\u002Fnode_exporter --web.listen-address=\":9100\" &\ncurl localhost:9100\u002Fmetrics | head\n```\n\n### 2.2 配置抓取任务\n\n```yaml\n# prometheus.yml\nglobal:\n  scrape_interval: 15s\n  evaluation_interval: 15s\n\nscrape_configs:\n  - job_name: \"node\"\n    static_configs:\n      - targets: [\"10.0.1.11:9100\", \"10.0.1.12:9100\"]\n  - job_name: \"mysql\"\n    static_configs:\n      - targets: [\"10.0.1.11:9104\"]\n  - job_name: \"api\"\n    metrics_path: \"\u002Fmetrics\"\n    static_configs:\n      - targets: [\"10.0.1.21:8080\"]\n```\n\n## 三、PromQL：监控查询语言\n\n### 3.1 基础查询\n\n```promql\nnode_cpu_seconds_total                                    # 原始时间序列\nrate(node_cpu_seconds_total{mode=\"idle\"}[5m])            # 5分钟每秒平均速率\nsum(rate(node_cpu_seconds_total{mode!=\"idle\"}[5m])) by (instance)  # 按实例聚合CPU使用率\n```\n\n### 3.2 常用聚合操作符\n\n- `sum \u002F avg \u002F max \u002F min`：按维度聚合；\n- `by \u002F without`：按标签分组；\n- `rate \u002F irate`：速率计算（irate 更适合高频突变指标）；\n- `increase()`：增量。\n\n```promql\n# 每台机器的 CPU 使用率\n(1 - avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) by (instance)) * 100\n\n# 每秒 HTTP 请求数按状态码分布\nsum(rate(http_requests_total[5m])) by (status)\n```\n\n## 四、告警体系：Alertmanager\n\n### 4.1 告警规则\n\n```yaml\n# rules.yml\ngroups:\n  - name: host\n    rules:\n      - alert: HostHighCpu\n        expr: (1 - avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) by (instance)) * 100 > 85\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          summary: \"{{ $labels.instance }} CPU 使用率超过 85%\"\n          description: \"当前 {{ $value }}%\"\n```\n\n### 4.2 Alertmanager 通知渠道\n\n```yaml\nroute:\n  group_by: [\"alertname\"]\n  group_wait: 30s\n  group_interval: 5m\n  repeat_interval: 4h\n  receiver: \"webhook\"\n\nreceivers:\n  - name: \"webhook\"\n    webhook_configs:\n      - url: \"http:\u002F\u002Fnotify-service:8080\u002Falert\"\n```\n\n通知渠道支持邮件、企业微信、钉钉、Webhook，统一接入便于与工单系统联动。\n\n## 五、Grafana 可视化\n\n```bash\ndocker run -d -p 3000:3000 grafana\u002Fgrafana\n```\n\n在 Grafana 中添加 Prometheus 数据源后，可以直接导入现成的 Dashboard JSON（如 node_exporter 全家桶模板），或手工配置面板。**告警与可视化是两回事**：Grafana 面板用于\"看\"，Alertmanager 用于\"响\"。\n\n## 六、体系化的三大告警维度\n\n| 维度 | 示例指标 | 告警阈值思路 |\n|------|----------|--------------|\n| 资源 | CPU\u002F内存\u002F磁盘\u002F带宽 | 持续 85%+ 超过 10 分钟 |\n| 业务 | QPS、错误率、P99 延迟 | 错误率 > 1%，P99 超基线 2 倍 |\n| 可用性 | 抓取失败、up 指标 | 任何 target 连续 3 次抓取失败 |\n\n```promql\n# 目标探活告警\nup == 0\n```\n\n## 结语\n\nPrometheus 体系的落地路径很清晰：**Exporter 采集 → 指标建模 → PromQL 查询 → Alertmanager 告警 → Grafana 可视化**。真正让监控产生价值的不是工具本身，而是\"业务可观测\"的设计——为每个关键路径定义 SLO 指标，把告警从\"机器报警\"升级为\"用户体验报警\"。从今天起，用 Prometheus 替你把每个深夜的告警变成可定位、可响应、可复盘的标准流程。\n","\u003Ch1>Prometheus 监控实战：从指标采集到告警体系的完整搭建\u003C\u002Fh1>\n\u003Cp>监控是运维的&quot;眼睛&quot;。在 Prometheus 之前，监控体系往往靠 Zabbix 等轮询式工具，指标零散、告警滞后。Prometheus 以&quot;拉取式采集 + 多维标签 + PromQL&quot;重新定义了监控标准，成为云原生时代的监控事实标准。本文从指标模型讲起，完整搭建一套可用的监控告警体系。\u003C\u002Fp>\n\u003Ch2>一、核心概念：指标模型与拉取架构\u003C\u002Fh2>\n\u003Cp>Prometheus 的核心是\u003Cstrong>指标（Metric）\u003C\u002Fstrong>，格式为 \u003Ccode>指标名{标签=值} 数值\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>http_requests_total{method=&quot;GET&quot;, status=&quot;200&quot;} 1024\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cul>\n\u003Cli>\u003Cstrong>指标名\u003C\u002Fstrong>描述测量对象，\u003Cstrong>标签\u003C\u002Fstrong>描述维度，\u003Cstrong>样本值\u003C\u002Fstrong>为当前数值；\u003C\u002Fli>\n\u003Cli>同一指标不同标签组合即不同时间序列（time series）。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>架构上 Prometheus \u003Cstrong>主动拉取\u003C\u002Fstrong>（scrape）目标暴露的 \u003Ccode>\u002Fmetrics\u003C\u002Fcode> 端点，而非等目标推送。这意味着：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>目标需要暴露 HTTP 端点（Exporter 就是干这个的）；\u003C\u002Fli>\n\u003Cli>每个目标配置抓取间隔与超时；\u003C\u002Fli>\n\u003Cli>无目标上报导致的历史数据可通过 \u003Ccode>--storage.tsdb.retention.time\u003C\u002Fcode> 控制保留时长。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>二、指标体系搭建\u003C\u002Fh2>\n\u003Ch3>2.1 采集层：Exporter 生态\u003C\u002Fh3>\n\u003Cp>| 采集对象 | Exporter | 端点示例 |\n|----------|----------|----------|\n| 服务器基础指标 | node_exporter | \u003Ccode>node_cpu_seconds_total\u003C\u002Fcode> |\n| MySQL | mysqld_exporter | \u003Ccode>mysql_global_status_threads_connected\u003C\u002Fcode> |\n| Redis | redis_exporter | \u003Ccode>redis_connected_clients\u003C\u002Fcode> |\n| Nginx | nginx_exporter | \u003Ccode>nginx_http_requests_total\u003C\u002Fcode> |\n| 应用业务指标 | 自研暴露 | \u003Ccode>http_requests_total\u003C\u002Fcode> |\u003C\u002Fp>\n\u003Cp>以 node_exporter 为例：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">.\u002Fnode_exporter --web.listen-address=&quot;:9100&quot; &amp;\ncurl localhost:9100\u002Fmetrics | head\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>2.2 配置抓取任务\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yaml\"># prometheus.yml\nglobal:\n  scrape_interval: 15s\n  evaluation_interval: 15s\n\nscrape_configs:\n  - job_name: &quot;node&quot;\n    static_configs:\n      - targets: [&quot;10.0.1.11:9100&quot;, &quot;10.0.1.12:9100&quot;]\n  - job_name: &quot;mysql&quot;\n    static_configs:\n      - targets: [&quot;10.0.1.11:9104&quot;]\n  - job_name: &quot;api&quot;\n    metrics_path: &quot;\u002Fmetrics&quot;\n    static_configs:\n      - targets: [&quot;10.0.1.21:8080&quot;]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>三、PromQL：监控查询语言\u003C\u002Fh2>\n\u003Ch3>3.1 基础查询\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-promql\">node_cpu_seconds_total                                    # 原始时间序列\nrate(node_cpu_seconds_total{mode=&quot;idle&quot;}[5m])            # 5分钟每秒平均速率\nsum(rate(node_cpu_seconds_total{mode!=&quot;idle&quot;}[5m])) by (instance)  # 按实例聚合CPU使用率\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>3.2 常用聚合操作符\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Ccode>sum \u002F avg \u002F max \u002F min\u003C\u002Fcode>：按维度聚合；\u003C\u002Fli>\n\u003Cli>\u003Ccode>by \u002F without\u003C\u002Fcode>：按标签分组；\u003C\u002Fli>\n\u003Cli>\u003Ccode>rate \u002F irate\u003C\u002Fcode>：速率计算（irate 更适合高频突变指标）；\u003C\u002Fli>\n\u003Cli>\u003Ccode>increase()\u003C\u002Fcode>：增量。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cpre>\u003Ccode class=\"language-promql\"># 每台机器的 CPU 使用率\n(1 - avg(rate(node_cpu_seconds_total{mode=&quot;idle&quot;}[5m])) by (instance)) * 100\n\n# 每秒 HTTP 请求数按状态码分布\nsum(rate(http_requests_total[5m])) by (status)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>四、告警体系：Alertmanager\u003C\u002Fh2>\n\u003Ch3>4.1 告警规则\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yaml\"># rules.yml\ngroups:\n  - name: host\n    rules:\n      - alert: HostHighCpu\n        expr: (1 - avg(rate(node_cpu_seconds_total{mode=&quot;idle&quot;}[5m])) by (instance)) * 100 &gt; 85\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          summary: &quot;{{ $labels.instance }} CPU 使用率超过 85%&quot;\n          description: &quot;当前 {{ $value }}%&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.2 Alertmanager 通知渠道\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yaml\">route:\n  group_by: [&quot;alertname&quot;]\n  group_wait: 30s\n  group_interval: 5m\n  repeat_interval: 4h\n  receiver: &quot;webhook&quot;\n\nreceivers:\n  - name: &quot;webhook&quot;\n    webhook_configs:\n      - url: &quot;http:\u002F\u002Fnotify-service:8080\u002Falert&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>通知渠道支持邮件、企业微信、钉钉、Webhook，统一接入便于与工单系统联动。\u003C\u002Fp>\n\u003Ch2>五、Grafana 可视化\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\">docker run -d -p 3000:3000 grafana\u002Fgrafana\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在 Grafana 中添加 Prometheus 数据源后，可以直接导入现成的 Dashboard JSON（如 node_exporter 全家桶模板），或手工配置面板。\u003Cstrong>告警与可视化是两回事\u003C\u002Fstrong>：Grafana 面板用于&quot;看&quot;，Alertmanager 用于&quot;响&quot;。\u003C\u002Fp>\n\u003Ch2>六、体系化的三大告警维度\u003C\u002Fh2>\n\u003Cp>| 维度 | 示例指标 | 告警阈值思路 |\n|------|----------|--------------|\n| 资源 | CPU\u002F内存\u002F磁盘\u002F带宽 | 持续 85%+ 超过 10 分钟 |\n| 业务 | QPS、错误率、P99 延迟 | 错误率 &gt; 1%，P99 超基线 2 倍 |\n| 可用性 | 抓取失败、up 指标 | 任何 target 连续 3 次抓取失败 |\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-promql\"># 目标探活告警\nup == 0\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>结语\u003C\u002Fh2>\n\u003Cp>Prometheus 体系的落地路径很清晰：\u003Cstrong>Exporter 采集 → 指标建模 → PromQL 查询 → Alertmanager 告警 → Grafana 可视化\u003C\u002Fstrong>。真正让监控产生价值的不是工具本身，而是&quot;业务可观测&quot;的设计——为每个关键路径定义 SLO 指标，把告警从&quot;机器报警&quot;升级为&quot;用户体验报警&quot;。从今天起，用 Prometheus 替你把每个深夜的告警变成可定位、可响应、可复盘的标准流程。\u003C\u002Fp>\n","从指标模型、Exporter 接入、PromQL 查询到告警规则与高可用联邦，给出生产级 Prometheus 监控体系的完整搭建路径。",39834.28768695388,{"id":8,"created_at":23,"updated_at":24,"code":25,"username":26,"display_name":27,"role":28,"avatar_url":29,"is_active":17,"is_banned":16,"follower_count":30,"following_count":19,"last_login_at":31}]