DevOps 从零到生产级掌握
DevOps 不是“会用 Docker 命令”或“会点 Jenkins 发布按钮”。商业项目真正需要的是:代码能稳定构建,制品能追踪,配置能隔离,发布能回滚,服务能观测,故障能定位,权限和密钥不乱放。
一句话建立主线:
DevOps 是把开发、测试、构建、制品、部署、运行、监控、告警、回滚、复盘连接成闭环,让软件可以稳定、可重复、可审计地交付到生产环境。
学习目标
学完这一页,你要能做到:
- 说清楚代码从提交到用户访问的全过程。
- 区分源码、构建产物、镜像、容器、Pod、服务入口。
- 解释 Docker 镜像分层、容器隔离、端口映射、数据卷和网络。
- 解释 Nginx 反向代理、负载均衡、限流、HTTPS 和静态资源缓存。
- 解释 Jenkins Pipeline 如何拉代码、测试、打包、构建镜像、部署和回滚。
- 解释 Kubernetes 中 Pod、Deployment、ReplicaSet、Service、Ingress、ConfigMap、Secret、Probe、Volume 的关系。
- 能写最小 Dockerfile、docker-compose、Jenkinsfile、K8s Deployment/Service YAML。
- 能排查“服务访问不了、发布失败、Pod 起不来、接口变慢、磁盘打满、配置不生效、健康检查失败”。
- 能把 DevOps 用到订单、支付、采集、搜索、网关、后台管理等商业系统。
如果要按“零基础能学会、面试能讲清、生产能排查”的标准验收,请配合阅读 DevOps 从零到精通验收清单。本页负责串主线,验收清单负责把 Docker、Nginx、Jenkins、Kubernetes、发布回滚和排障拆成可检查的学习目标。
全链路流程
flowchart TD
A["开发提交代码"] --> B["Git 仓库"]
B --> C["CI 触发流水线"]
C --> D["编译和单元测试"]
D --> E["打包产物<br/>jar 或 dist"]
E --> F["构建 Docker 镜像"]
F --> G["推送镜像仓库"]
G --> H["部署到 Kubernetes"]
H --> I["Pod 启动和健康检查"]
I --> J["Service 提供集群内访问"]
J --> K["Ingress 或 Nginx 暴露入口"]
K --> L["用户访问服务"]
L --> M["日志、指标、链路追踪"]
M --> N["告警、回滚、复盘"]这条链路里,任何一层出问题,表现都可能是“访问不了”。所以排查不能只盯应用日志,要先确定请求走到了哪一层。
第一层:从源码到构建产物
源码不能直接发布到生产。生产运行的应该是可重复构建出来的产物,例如:
| 项目类型 | 构建命令 | 产物 |
|---|---|---|
| Spring Boot | mvn clean package | app.jar |
| Vue/VitePress | npm run build | dist 静态文件 |
| Go 服务 | go build | 二进制文件 |
| Python 服务 | 构建镜像或打包依赖 | 镜像或 wheel |
为什么不能手动复制代码到服务器:
- 服务器环境不可控,同一份代码在不同机器表现可能不同。
- 依赖版本可能漂移。
- 无法审计这次发布来自哪个提交。
- 出问题时不知道该回滚到哪个版本。
最小构建脚本:
#!/usr/bin/env bash
set -e
git rev-parse --short HEAD
mvn clean test package
ls -lh target/*.jarset -e 的作用是前一步失败后立即停止。如果不停止,测试失败也继续发布,生产风险会非常高。
第二层:Docker 镜像和容器
镜像是只读模板,容器是镜像运行起来后的进程环境。
flowchart TD
A["Dockerfile"] --> B["docker build"]
B --> C["镜像 image"]
C --> D["docker run"]
D --> E["容器 container"]镜像为什么有分层
Dockerfile 每条关键指令通常形成一层。相同层可以复用缓存,减少构建时间和传输体积。
FROM eclipse-temurin:8-jre
WORKDIR /app
COPY target/app.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]这里用 JDK8 运行时是因为很多企业项目仍以 JDK8 为主。学习 DevOps 时不要只看最新版本,生产中常见的是 JDK8、JDK11、JDK17 混用。
容器不等于虚拟机
容器本质上仍是宿主机上的进程,只是通过 namespace 做隔离,通过 cgroup 做资源限制,通过镜像层提供文件系统。
| 概念 | 解决什么问题 | 不理解会怎样 |
|---|---|---|
| namespace | 隔离进程、网络、挂载点 | 误以为容器是完整虚拟机 |
| cgroup | 限制 CPU、内存 | 容器 OOM 不知道为什么 |
| image layer | 复用文件系统层 | 镜像越打越大 |
| volume | 持久化数据 | 容器删了数据也没了 |
| bridge network | 容器间通信 | 服务连不上依赖 |
最小运行 Demo
docker build -t order-api:1.0.0 .
docker run -d --name order-api -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
order-api:1.0.0
docker logs -f order-api
curl http://127.0.0.1:8080/actuator/health常见坑:
| 现象 | 常见原因 | 排查命令 |
|---|---|---|
| 容器启动后马上退出 | 入口命令错误、应用启动失败 | docker logs 容器名 |
| 本机访问不到 | 端口没有 -p 映射 | docker ps |
| 配置没生效 | 环境变量名或 profile 错 | docker inspect |
| 容器内连不上外部 DB | 网络、防火墙、地址写 localhost | docker exec 进入容器测试 |
| 磁盘越来越大 | 日志无限写、镜像没清理 | docker system df |
第三层:Docker Compose
Compose 适合本地开发、测试环境或单机部署,把多个容器组合起来。
flowchart TD
A["Compose项目"] --> B["order-api"]
A --> C["mysql"]
A --> D["redis"]
B --> E["服务名DNS访问mysql和redis"]
C --> F["Secret提供初始化密码"]
C --> G["Named Volume保存数据"]
C --> H["Healthcheck表达启动状态"]完整配置不应使用明文root密码,也不应默认把MySQL、Redis端口暴露到所有宿主机接口。请完成 Compose商业栈Demo 与 MySQL容器化Demo。depends_on 和健康检查只改善初始顺序,生产代码仍要有连接超时、退避重试和连接池恢复。
第四层:Nginx 入口层
Nginx 常见职责:
- 反向代理:把外部请求转发给后端服务。
- 负载均衡:多个后端实例分摊流量。
- 静态资源:直接返回前端文件、图片、下载文件。
- HTTPS 终止:处理 TLS 证书。
- 限流:保护后端不被突发流量打垮。
- 缓存:缓存低频变化内容。
flowchart TD
A["用户请求"] --> B["Nginx"]
B --> C{"路径判断"}
C -- "/api" --> D["后端服务集群"]
C -- "/static" --> E["静态资源目录"]
C -- "HTTPS" --> F["证书和 TLS"]最小反向代理:
upstream order_api {
server 10.0.0.11:8080;
server 10.0.0.12:8080;
}
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://order_api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}如果不设置 X-Forwarded-For,后端看到的可能只有 Nginx IP,审计、风控、限流都会受影响。
Nginx 排查顺序
| 现象 | 排查点 |
|---|---|
| 404 | location 是否匹配、静态目录是否存在 |
| 502 | 后端服务不可达、端口不通、upstream 错 |
| 504 | 后端超时、网关超时太短、服务太慢 |
| 客户端真实 IP 丢失 | 请求头没有透传 |
| 上传大文件失败 | client_max_body_size 太小 |
| 接口被打爆 | 没有限流、没有连接数限制 |
第五层:Jenkins Pipeline
Jenkins 不只是“点按钮发布”,核心是把发布过程变成可审计的流水线。
flowchart TD
A["拉代码"] --> B["编译"]
B --> C["测试"]
C --> D["构建镜像"]
D --> E["推送镜像"]
E --> F["部署"]
F --> G["健康检查"]
G --> H{"是否成功"}
H -- "成功" --> I["通知"]
H -- "失败" --> J["回滚或停止发布"]最小 Jenkinsfile:
pipeline {
agent any
environment {
APP_NAME = 'order-api'
REGISTRY = 'registry.example.com/backend'
IMAGE_TAG = "${env.BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Test and Package') {
steps {
sh 'mvn clean test package'
}
}
stage('Build Image') {
steps {
sh 'docker build -t $REGISTRY/$APP_NAME:$IMAGE_TAG .'
}
}
stage('Push Image') {
steps {
sh 'docker push $REGISTRY/$APP_NAME:$IMAGE_TAG'
}
}
stage('Deploy') {
steps {
sh 'kubectl set image deployment/order-api order-api=$REGISTRY/$APP_NAME:$IMAGE_TAG'
sh 'kubectl rollout status deployment/order-api --timeout=120s'
}
}
}
}生产注意:
- 凭据必须放 Jenkins Credentials,不要写在 Jenkinsfile。
- 镜像标签不要只用
latest,要用版本号、构建号或 Git commit。 - 发布后必须做健康检查。
- 失败要能停止或回滚。
- 流水线日志不能打印密码、token、私钥。
第六层:Kubernetes 核心对象
K8s 用来管理容器集群。它解决的是:多实例、自动拉起、滚动更新、服务发现、配置管理、扩缩容和故障恢复。
flowchart TD
A["Deployment"] --> B["ReplicaSet"]
B --> C["Pod 1"]
B --> D["Pod 2"]
B --> E["Pod 3"]
F["Service"] --> C
F --> D
F --> E
G["Ingress"] --> F
H["ConfigMap 和 Secret"] --> C| 对象 | 作用 | 初学者要记住 |
|---|---|---|
| Pod | 最小调度单位 | 容器运行在 Pod 里 |
| Deployment | 管理副本和发布 | 一般不要直接手写裸 Pod |
| ReplicaSet | 维持副本数 | 通常由 Deployment 管 |
| Service | 稳定访问入口 | Pod IP 会变,Service 名稳定 |
| Ingress | 集群外入口 | 通常配合 Nginx Ingress |
| ConfigMap | 普通配置 | 不放密码 |
| Secret | 敏感配置 | 仍要结合权限和加密 |
| Probe | 健康检查 | 决定是否接流量或重启 |
| Volume | 持久化数据 | Pod 重建后数据不丢 |
最小部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-api
spec:
replicas: 3
selector:
matchLabels:
app: order-api
template:
metadata:
labels:
app: order-api
spec:
containers:
- name: order-api
image: registry.example.com/backend/order-api:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: prod
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: order-api
spec:
selector:
app: order-api
ports:
- port: 80
targetPort: 8080为什么要 readiness 和 liveness 分开:
| 探针 | 表示什么 | 失败后果 |
|---|---|---|
| readiness | 是否准备好接流量 | Service 暂时不转发给它 |
| liveness | 进程是否活着 | K8s 重启容器 |
如果把慢启动服务的 liveness 配太激进,应用还没启动完就被反复杀掉,会进入 CrashLoopBackOff。
第七层:灰度发布和回滚
生产发布不能只追求“快”,还要能控制风险。
flowchart TD
A["新版本镜像"] --> B["少量实例或少量流量"]
B --> C["观察错误率和延迟"]
C --> D{"指标正常"}
D -- "是" --> E["扩大流量"]
D -- "否" --> F["回滚旧版本"]K8s 回滚命令:
kubectl rollout history deployment/order-api
kubectl rollout undo deployment/order-api
kubectl rollout status deployment/order-api回滚依赖一个前提:旧镜像还在,配置兼容,数据库变更可回退或向前兼容。很多事故不是代码不能回滚,而是数据库表结构已经不可逆。
第八层:可观测性
线上系统至少要有三类证据:
| 类型 | 回答的问题 | 例子 |
|---|---|---|
| 日志 | 某次请求具体发生了什么 | 异常堆栈、业务参数、traceId |
| 指标 | 系统整体是否健康 | QPS、P99、错误率、CPU、内存 |
| 链路追踪 | 请求经过哪些服务 | 网关 -> 订单 -> 库存 -> 支付 |
flowchart TD
A["用户请求"] --> B["网关生成 traceId"]
B --> C["订单服务"]
C --> D["库存服务"]
C --> E["支付服务"]
C --> F["写日志和指标"]
F --> G["日志平台和监控系统"]没有可观测性会怎样:
- 用户说慢,但不知道哪个接口慢。
- 接口报错,但没有请求上下文。
- 发布后错误率升高,但没有自动告警。
- 多服务调用失败,只能靠猜。
商业项目场景
订单系统发布
订单系统通常涉及订单、库存、支付、MQ 和数据库。发布时要重点关注:
- 数据库变更要先兼容旧代码。
- 消息格式要兼容旧消费者。
- 支付回调接口不能随意中断。
- 发布后观察错误率、支付成功率、订单状态卡住数量。
- 回滚时不能让订单状态机出现逆向流转。
医疗数据采集平台
采集平台通常有设备接入、数据清洗、异步入库、文件归档和查询服务。DevOps 重点:
- 采集网关要滚动发布,避免大量设备同时断连。
- 原始报文和清洗结果要有日志和归档。
- 消息队列堆积要告警。
- 数据库连接池、磁盘、对象存储要监控。
- 配置变更要可审计,避免采集规则误改。
生产排查总流程
flowchart TD
A["线上故障"] --> B{"用户表现"}
B -- "访问不了" --> C["查 DNS、Nginx、Ingress、Service"]
B -- "接口报错" --> D["查应用日志和依赖状态"]
B -- "接口变慢" --> E["查 P99、线程池、DB、Redis、MQ"]
B -- "发布失败" --> F["查流水线、镜像、权限、K8s 事件"]
B -- "Pod 起不来" --> G["查 describe、logs、探针、资源"]
C --> H["确认请求到达哪一层"]
D --> I["根据 traceId 找完整链路"]
E --> J["定位瓶颈层而不是盲目扩容"]
F --> K["停止发布或回滚"]常用命令:
kubectl get pod -n prod
kubectl describe pod order-api-xxx -n prod
kubectl logs order-api-xxx -n prod --tail=200
kubectl get events -n prod --sort-by=.metadata.creationTimestamp
kubectl rollout status deployment/order-api -n prod
kubectl top pod -n prod面试标准回答
DevOps 是什么
DevOps 是把开发、测试、构建、部署、运行、监控和反馈连接起来的工程实践。它的目标不是单纯自动发布,而是让软件交付可重复、可追踪、可回滚、可观测,减少环境差异和人工操作风险。Docker 和虚拟机有什么区别
虚拟机虚拟完整操作系统,隔离强但启动慢、资源重;Docker 容器共享宿主机内核,通过 namespace 和 cgroup 隔离进程、网络和资源,启动快、镜像轻,适合应用交付和弹性部署。但容器不是完整虚拟机,数据持久化、网络和资源限制需要单独设计。Kubernetes 为什么需要 Service
Pod 是动态的,重建后 IP 会变化。Service 提供稳定的访问入口,通过标签选择一组 Pod,并把流量转发到健康 Pod。没有 Service,调用方需要感知 Pod IP 变化,服务发现和负载均衡都会很困难。发布失败怎么处理
先停止继续放量,确认失败发生在构建、镜像推送、部署、启动、健康检查还是流量入口。再看流水线日志、镜像标签、K8s events、Pod logs 和探针状态。若已经影响生产,优先回滚到上一稳定版本,同时保留现场证据再分析根因。关联知识点
本章小结
DevOps 的核心不是堆工具,而是把软件交付变成闭环:源码可构建,产物可追踪,镜像可复现,配置可隔离,发布可回滚,服务可观测,故障可定位。小白学习时要先把“代码怎么变成线上服务”这条链路走通,再逐个深入 Docker、Nginx、Jenkins 和 Kubernetes。
