回滚是一种运维操作。通常发生在部署过程中发现问题,需要将目标环境恢复到部署前的状态。
在我看来,回滚有两种模式。其中一种是一步步执行反向操作,我称之为反向操作模式。
基于反向操作的回滚模式
可能是由于过去手工运维的思维方式的惯性,我发现不少人只知道这一种模式。
比如使用手工部署Nginx的配置的操作如下:
- 1. SSH登录到目标服务器
- 2. 进入到存放Nginx的
/etc/nginx/sites-enabled/
目录 - 3. 编辑目标配置文件
vim example.443.conf
- 4. 增加一个location配置
- 5. reload nginx使配置生效
在反向操作模式的回滚方案中,我们应该该如何回滚呢?回滚步骤1,2,3,5步骤与部署时一致。第4步,需要操作人员找到该配置,并删除。
这时,操作人员在操作时就可能会出错,而且出错了,你可能很难觉察到。因为他是手工操作的。
那么,有人就想,以上步骤能自动回滚就好了。
可是,该如何自动回滚呢?我们需要在部署时就设计好相应的自动化回滚脚本。当需要时,就触发其自动回滚。
然而这个回滚方案的方案是无法通用的,而且增加了运维成本。因为普通的运维人员对于一个Nginx的配置的变更,是非常不愿意写回滚的脚本的,而且,他本人也不一定能写出正确的、可靠的自动化回滚脚本。
那么,有人就会想了,我能否实现自动生成回滚代码的平台?
答案是可以的。你必须预先定义每一个步骤动作,比如在平台上将Nginx配置的修改作为一个动作定义。然后再定义它的反操作。
如果你是实现过类似平台项目,你会知道,这工作量是无穷无尽的。因为运维的操作是无穷无尽的。
你可以说平台可以提供自定义步骤动作的能力,那么你同样会遇到“他本人也不一定能写出正确的、可靠的自动化回滚脚本”的问题。而且,既然是平台了,定义操作的责任就应该是平台的责任。
所以,这是一个业界的难题。
在面试过程中,面试官通常假设我也会遇到同样的难题。然而,我根本不会遇到这个问题。
基于版本的回滚模式
我在解释这个模式时,很多人无法理解。
还是以部署Nginx配置为案例。但是通过Ansible来实现自动化部署。假设已经存在以下部署脚本:
- hosts: prod-nginx
gather_facts: yes
become: true
vars_files:
# Nginx的配置
- common_vars/nginx.yaml
roles:
# Nginx的部署逻辑,是声明式的、幂等的。
- ansible-role-nginx
以上代码含义大概是:部署Nginx到prod-nginx主机列表上,并使用common_vars/nginx.yaml文件中的配置。
nginx.yaml的配置如下:
nginx_vhosts:
- listen: "80"
server_name: "*.example.com"
return: "301 https://{{example_domain}}$request_uri"
filename: "example.80.conf"
我们对以上代码版本化(提前到Git中,并通过自动化构建),得到版本号:v1.0.1。
现在我们需要像“反向操作的回滚模式”中的案例那样修改线上的配置时,我们的做法是在nginx.yaml配置增加相应的配置,最终效果如下:
nginx_vhosts:
- listen: "80"
server_name: "*.example.com"
return: "301 https://{{example_domain}}$request_uri"
filename: "example.80.conf"
- listen: "80"
server_name: "*.abc.com"
return: "301 https://{{example_domain}}$request_uri"
filename: "abc.80.conf"
然后将代码push到代码库中,经过构建,我们得到版本号:v1.1.0。
部署时,使用v1.1.0的代码部署即可。
基于版本的回滚模式,回滚就很简单了。就是再执行一次v1.0.1版本(v1.1.0的上一个版本)的代码就可以了。
基于这种模式实现平台化,也非常简单。平台不需要关心具体运行的内容,就要选择上一个正确的版本的代码执行,就完成了回滚。不会遇到“基于反向操作的回滚模式”的实现过程遇到的各种问题。
也因为这种简单化,平台实现部署的标准化,也非常简单。
但是,基于版本的回滚模式是有前提的,你的部署代码的执行必须是幂等的、声明式的。幂等的,指的是同一份代码,运行多次,得到的结果是一样的。
小结
基于版本的回滚模式准确来说,也是部署。也就是它使用老版本部署代替传统的“回滚”。