发版出问题时,现场通常不是只有一个动作。
一边要判断要不要把代码切回旧版本,一边要看监控确认影响范围,必要时还得有人登录机器查日志、重启服务、修配置。回滚、监控、堡垒机这几个词经常一起出现,但它们解决的其实不是同一个问题。
把这几件事放到同一个场景里看,会更容易理解:回滚解决“先把业务恢复回来”,监控解决“我们到底出了什么问题”,堡垒机解决“谁可以进生产环境,以及进去之后做了什么”。
代码回滚:先看发布方式
代码能不能快速回滚,首先取决于你平时是怎么发布的。
用 Docker Compose 发版时,回滚本质上是切回旧镜像
如果服务是通过 Docker Compose 部署的,最关键的前提不是 Compose 本身,而是镜像版本管理。
每次发版都应该生成一个可追溯的镜像标签,例如:
myapp:v1.0:上一个稳定版本myapp:v1.1:刚发布的新版本
这时回滚的动作其实很直接:把 docker-compose.yml 里的镜像标签改回旧版本,然后重新拉起服务。
services:
app:
image: myapp:v1.0
ports:
- "8080:8080"
# 原来是 myapp:v1.1,改回 myapp:v1.0
docker compose up -d
这里发生的事情不是“手动覆盖代码”,而是 Compose 按新的镜像配置重建对应容器。如果旧镜像已经在机器上,整个过程通常很快;如果还需要重新拉取镜像,耗时则取决于镜像仓库和网络。
这种回滚方式的好处是边界很清楚:你回滚的是一个已经打包完成的运行时产物,而不是一堆散落在服务器上的文件。
没有容器化时,回滚依赖发版前的备份
如果线上还是“传压缩包、解压、启动进程”这一套,回滚就不能靠镜像标签,而要靠发版前是否保留了完整的旧版本目录。
一个常见做法是,发布新版本前先把旧目录改名留存:
mv app app_bak_20231024
tar -xzf app_new.tar.gz -C /srv/
mv /srv/app_new /srv/app
一旦新版本启动失败,或者虽然能启动但出现明显故障,回滚步骤通常是:
- 停掉当前进程
- 移除有问题的新目录
- 把备份目录改回原名
- 重新启动服务
systemctl stop app
rm -rf /srv/app
mv /srv/app_bak_20231024 /srv/app
systemctl start app
这类回滚能不能成功,关键不在“手速快不快”,而在发布前有没有守住一个基本原则:先备份,再发布。
当然,它也有明显限制。只要发布过程里混入了在线改配置、手工传文件、目录外写入临时资源之类的动作,回滚就不一定是一次简单的目录替换。
数据库不是同一种“回滚”
代码切回旧版本,不等于数据库也能一起回去。两者的风险级别不一样。
如果这次发布带了数据库变更,尤其是 DDL,那么回滚策略通常要单独设计。比较稳妥的处理方式,一般会围绕这几件事展开:
- 变更前先做快照备份或导出备份
- 小范围改动准备对应的 Rollback SQL
- 真正出现大面积损坏时,依赖最近一次全量备份和增量日志做恢复
这里最需要避免的是把“代码回滚”想得过于乐观。代码可以很快切回旧版本,但数据库一旦做了不可逆操作,恢复成本通常高得多。所以很多团队会把数据库变更放到更严格的审核流程里,尽量避免在线上直接做不可逆调整。
监控数据是怎么拿到的
回滚只能先止血,接下来还得回答另一个问题:问题到底发生在哪里。
这时候监控系统才真正开始发挥作用。
理解 Prometheus 这类主机监控,先得有一个很具体的概念:生产环境里的机器除了公网出口,通常还处在同一个内网里,机器之间可以通过内网地址通信。
对外部访问来说,业务机不会把 SSH 直接暴露到公网;但对内网里的程序来说,监控机、业务机之间的指标采集是可以走内网的。
一个常见的部署方式
在稍微正式一点的环境里,Prometheus 和 Grafana 更常见的做法是单独放在监控机上,而不是和业务进程混跑在同一台生产机里。这样做的目的很直接:避免监控系统本身和线上业务抢资源,也让职责边界更清楚。
指标采集通常分两步。
第一步:业务机上运行 Exporter
每台业务机上会运行一个很小的采集程序,例如 Node Exporter。它负责读取这台机器的 CPU、内存、磁盘等指标,并把这些数据暴露成一个 HTTP 接口。
在很多默认配置里,这个接口会监听在 9100 端口。
第二步:Prometheus 定时去拉
Prometheus 自己不登录机器,也不通过堡垒机转发。它只是在监控机上维护一份目标列表,然后定时去这些业务机的内网地址拉取指标。
一个最小的配置大致是这样:
scrape_configs:
- job_name: "node"
static_configs:
- targets:
- "10.0.0.12:9100"
- "10.0.0.13:9100"
Prometheus 拿到数据后,Grafana 再去查询 Prometheus,把这些时间序列画成图表。对运维或开发来说,真正日常接触最多的往往不是 Prometheus 本身,而是 Grafana 面板。
人的访问路径和程序的发布路径,不是一回事
很多刚接触线上环境的人会把这几件事混在一起:既然生产机不能直接登录,那 Jenkins 发版的时候是不是也要先经过堡垒机?Prometheus 抓监控是不是也得走堡垒机?
通常不是这样。
人去排障,要经过堡垒机
人访问生产环境,核心问题是身份校验、权限控制和审计。
一个典型路径是:
- 运维或开发先登录堡垒机
- 堡垒机根据账号权限,只展示可访问的目标机器
- 人再通过堡垒机进入对应业务机
- 登录后的操作被记录下来,用于审计和回放
这条路径关注的是“谁进去了、能进哪些机器、进去之后做了什么”。
程序发版,通常走自动化链路
CI/CD 则解决的是另一件事:怎么把一个确定版本的产物稳定地送到目标环境。
一个常见流程是:
- 代码推到 GitLab
- Jenkins 拉取代码并构建产物
- 通过脚本、Ansible 或容器编排系统,把新版本发布到目标机器或集群
这条链路本质上是机器和机器之间的自动化协作。它关注的是版本、步骤、可重复性和权限边界,而不是人工登录后的操作审计。
也正因为如此,“人走堡垒机,程序走流水线” 往往是两条并行的线:前者服务于排障和管控,后者服务于持续发布。
业务机、跳板机、堡垒机分别解决什么问题
把这几个角色拆开看,会更清楚。
业务机
业务机就是实际承载线上服务的生产服务器,上面跑着 Nginx、应用进程、数据库或其他核心组件。它承载真实流量和真实数据,所以一般不会把 SSH 直接暴露在公网。
跳板机
跳板机解决的是最基础的网络隔离问题。外部人员不能直接 SSH 到业务机,而是先登录到一台位于同一内网的中间机器,再从这台机器进入目标服务器。
它的重点是“隔离”。
堡垒机
堡垒机是在“跳板”这个思路上继续往前走一步。除了转发访问,它还会补上权限控制和安全审计这两件事。
草稿里提到的这些能力,基本都属于堡垒机常见的职责范围:
- 细粒度授权:不同角色看到不同机器
- 高危命令拦截:对特定操作做限制
- 会话审计:记录登录后的操作过程
如果只是少量机器、少量人员,普通跳板机可能已经够用;一旦机器和人员规模上来,只靠“大家共用一台中间机”就很难回答审计和追责问题,这时堡垒机的价值才会真正显出来。
这几件事为什么总是一起出现
回过头看,回滚、监控、堡垒机之所以总在同一个场景里出现,是因为它们刚好覆盖了线上故障处理的三个不同阶段:
- 先恢复:用回滚把业务拉回稳定版本
- 再定位:用监控判断影响范围和异常时间点
- 最后介入:必要时通过堡垒机进入机器做人工排查
它们看起来都和“运维”有关,但本质上分别在回答三个不同的问题:怎么恢复、怎么看清、谁能进去处理。