Sparkle CodesSparkle
项目 / 运维

发版出故障之后:回滚、监控和堡垒机各自解决什么问题

x
xpx
Apr 20, 2025
Editorial Insight
#cicd#docker-compose#grafana#jumpserver#prometheus#rollback

发版出问题时,现场通常不是只有一个动作。

一边要判断要不要把代码切回旧版本,一边要看监控确认影响范围,必要时还得有人登录机器查日志、重启服务、修配置。回滚、监控、堡垒机这几个词经常一起出现,但它们解决的其实不是同一个问题。

把这几件事放到同一个场景里看,会更容易理解:回滚解决“先把业务恢复回来”,监控解决“我们到底出了什么问题”,堡垒机解决“谁可以进生产环境,以及进去之后做了什么”。

代码回滚:先看发布方式

代码能不能快速回滚,首先取决于你平时是怎么发布的。

用 Docker Compose 发版时,回滚本质上是切回旧镜像

如果服务是通过 Docker Compose 部署的,最关键的前提不是 Compose 本身,而是镜像版本管理。

每次发版都应该生成一个可追溯的镜像标签,例如:

  • myapp:v1.0:上一个稳定版本
  • myapp:v1.1:刚发布的新版本

这时回滚的动作其实很直接:把 docker-compose.yml 里的镜像标签改回旧版本,然后重新拉起服务。

YAML
services:
  app:
    image: myapp:v1.0
    ports:
      - "8080:8080"
BASH
# 原来是 myapp:v1.1,改回 myapp:v1.0
docker compose up -d

这里发生的事情不是“手动覆盖代码”,而是 Compose 按新的镜像配置重建对应容器。如果旧镜像已经在机器上,整个过程通常很快;如果还需要重新拉取镜像,耗时则取决于镜像仓库和网络。

这种回滚方式的好处是边界很清楚:你回滚的是一个已经打包完成的运行时产物,而不是一堆散落在服务器上的文件。

没有容器化时,回滚依赖发版前的备份

如果线上还是“传压缩包、解压、启动进程”这一套,回滚就不能靠镜像标签,而要靠发版前是否保留了完整的旧版本目录。

一个常见做法是,发布新版本前先把旧目录改名留存:

BASH
mv app app_bak_20231024
tar -xzf app_new.tar.gz -C /srv/
mv /srv/app_new /srv/app

一旦新版本启动失败,或者虽然能启动但出现明显故障,回滚步骤通常是:

  1. 停掉当前进程
  2. 移除有问题的新目录
  3. 把备份目录改回原名
  4. 重新启动服务
BASH
systemctl stop app
rm -rf /srv/app
mv /srv/app_bak_20231024 /srv/app
systemctl start app

这类回滚能不能成功,关键不在“手速快不快”,而在发布前有没有守住一个基本原则:先备份,再发布。

当然,它也有明显限制。只要发布过程里混入了在线改配置、手工传文件、目录外写入临时资源之类的动作,回滚就不一定是一次简单的目录替换。

数据库不是同一种“回滚”

代码切回旧版本,不等于数据库也能一起回去。两者的风险级别不一样。

如果这次发布带了数据库变更,尤其是 DDL,那么回滚策略通常要单独设计。比较稳妥的处理方式,一般会围绕这几件事展开:

  1. 变更前先做快照备份或导出备份
  2. 小范围改动准备对应的 Rollback SQL
  3. 真正出现大面积损坏时,依赖最近一次全量备份和增量日志做恢复

这里最需要避免的是把“代码回滚”想得过于乐观。代码可以很快切回旧版本,但数据库一旦做了不可逆操作,恢复成本通常高得多。所以很多团队会把数据库变更放到更严格的审核流程里,尽量避免在线上直接做不可逆调整。

监控数据是怎么拿到的

回滚只能先止血,接下来还得回答另一个问题:问题到底发生在哪里。

这时候监控系统才真正开始发挥作用。

理解 Prometheus 这类主机监控,先得有一个很具体的概念:生产环境里的机器除了公网出口,通常还处在同一个内网里,机器之间可以通过内网地址通信。

对外部访问来说,业务机不会把 SSH 直接暴露到公网;但对内网里的程序来说,监控机、业务机之间的指标采集是可以走内网的。

一个常见的部署方式

在稍微正式一点的环境里,Prometheus 和 Grafana 更常见的做法是单独放在监控机上,而不是和业务进程混跑在同一台生产机里。这样做的目的很直接:避免监控系统本身和线上业务抢资源,也让职责边界更清楚。

指标采集通常分两步。

第一步:业务机上运行 Exporter

每台业务机上会运行一个很小的采集程序,例如 Node Exporter。它负责读取这台机器的 CPU、内存、磁盘等指标,并把这些数据暴露成一个 HTTP 接口。

在很多默认配置里,这个接口会监听在 9100 端口。

第二步:Prometheus 定时去拉

Prometheus 自己不登录机器,也不通过堡垒机转发。它只是在监控机上维护一份目标列表,然后定时去这些业务机的内网地址拉取指标。

一个最小的配置大致是这样:

YAML
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 抓监控是不是也得走堡垒机?

通常不是这样。

人去排障,要经过堡垒机

人访问生产环境,核心问题是身份校验、权限控制和审计。

一个典型路径是:

  1. 运维或开发先登录堡垒机
  2. 堡垒机根据账号权限,只展示可访问的目标机器
  3. 人再通过堡垒机进入对应业务机
  4. 登录后的操作被记录下来,用于审计和回放

这条路径关注的是“谁进去了、能进哪些机器、进去之后做了什么”。

程序发版,通常走自动化链路

CI/CD 则解决的是另一件事:怎么把一个确定版本的产物稳定地送到目标环境。

一个常见流程是:

  1. 代码推到 GitLab
  2. Jenkins 拉取代码并构建产物
  3. 通过脚本、Ansible 或容器编排系统,把新版本发布到目标机器或集群

这条链路本质上是机器和机器之间的自动化协作。它关注的是版本、步骤、可重复性和权限边界,而不是人工登录后的操作审计。

也正因为如此,“人走堡垒机,程序走流水线” 往往是两条并行的线:前者服务于排障和管控,后者服务于持续发布。

业务机、跳板机、堡垒机分别解决什么问题

把这几个角色拆开看,会更清楚。

业务机

业务机就是实际承载线上服务的生产服务器,上面跑着 Nginx、应用进程、数据库或其他核心组件。它承载真实流量和真实数据,所以一般不会把 SSH 直接暴露在公网。

跳板机

跳板机解决的是最基础的网络隔离问题。外部人员不能直接 SSH 到业务机,而是先登录到一台位于同一内网的中间机器,再从这台机器进入目标服务器。

它的重点是“隔离”。

堡垒机

堡垒机是在“跳板”这个思路上继续往前走一步。除了转发访问,它还会补上权限控制和安全审计这两件事。

草稿里提到的这些能力,基本都属于堡垒机常见的职责范围:

  • 细粒度授权:不同角色看到不同机器
  • 高危命令拦截:对特定操作做限制
  • 会话审计:记录登录后的操作过程

如果只是少量机器、少量人员,普通跳板机可能已经够用;一旦机器和人员规模上来,只靠“大家共用一台中间机”就很难回答审计和追责问题,这时堡垒机的价值才会真正显出来。

这几件事为什么总是一起出现

回过头看,回滚、监控、堡垒机之所以总在同一个场景里出现,是因为它们刚好覆盖了线上故障处理的三个不同阶段:

  • 先恢复:用回滚把业务拉回稳定版本
  • 再定位:用监控判断影响范围和异常时间点
  • 最后介入:必要时通过堡垒机进入机器做人工排查

它们看起来都和“运维”有关,但本质上分别在回答三个不同的问题:怎么恢复、怎么看清、谁能进去处理。

BACK TO BLOG
The End of Interaction