如何使用蓝绿部署安全地发布软件

来自菜鸟教程
跳转至:导航、​搜索

介绍

现代开发实践通常区分部署和发布软件。 部署是将新代码放到服务器上的步骤。 发布是新代码开始接收生产流量的步骤。

蓝绿部署是一种部署和发布软件的策略。 它依赖于维护两个独立的生产能力环境,为了便于讨论,它们分别称为蓝色和绿色。 在本指南中,我们将讨论如何在 DigitalOcean 上使用蓝绿部署来改进将用户过渡到软件新版本的过程。

先决条件

为了完成本指南,您需要在允许您在主机之间移动 IP 地址的环境中部署两台 Ubuntu 20.04 服务器。 在 DigitalOcean 上,Floating IPs 可以提供此功能。 这些服务器将代表两个并行环境,它们交替用于登台和生产。 您可以随意调用这些服务器,但在本指南中,我们将它们称为“蓝色”和“绿色”。

在这些服务器中的每一个上,您都应该有一个为管理功能配置了 sudo 的非 root 用户。 您可以按照我们的 Ubuntu 20.04 初始服务器设置指南 配置这些用户。

什么是蓝绿部署?

蓝绿部署背后的基本概念是维护 这篇 Martin Fowler 的这篇文章 流行的技术,即维护两组环境,每组环境都能够在生产中为您的应用程序提供服务。 这两个环境应该几乎相同。 按照惯例,这些被称为蓝色和绿色环境。

这些环境中只有一个处于活动状态并在任何时候接收生产流量。 在这些环境(Web 服务器或负载平衡器)的 Web 端点前面,路由器或其他流量引导机制将所有生产流量推送到当前活动的环境。

计划新版本时,会将其部署到非活动环境中。 对于蓝绿部署,非活动环境充当最终的暂存环境。 它非常紧密地反映了生产环境,可用于在决定实时推送更改之前进行最终测试。

一旦您在内部测试了您的部署并对其健壮性充满信心,您就可以通过调整路由机制来快速发布新版本。 基本上,您在流量引导层翻转开关,以便所有生产流量开始转移到您的新软件版本。 以前活动的环境变为非活动环境,您以前的登台环境成为您的新生产环境。

此时,您之前的软件版本处于非活动状态,但仍可访问。 如果您的新活动部署遇到任何严重问题,您可以通过再次修改路由机制来恢复到以前的版本。

第 1 步 - 创建本地应用程序

为了演示这个一般概念,我们将设置两个服务器环境。 每个都将安装一个 Web 服务器。 请记住,在此示例中,Web 服务器代表整个应用程序堆栈,其中可能包括负载平衡器、多个 Web 服务器以及后端中的分布式或复制数据库。 我们在本指南中使用 Web 服务器,因为它代表了可以演示此发布模式的最小环境。

我们将开始在本地计算机上开发一个“应用程序”。 实际上,这只是一个 index.html 页面,我们可以部署到我们的服务器上。 我们将在我们的每台服务器上配置一个 git post receive hook,以便我们可以通过发出 git push 来进行部署。 我们将把应用程序的初始版本部署到我们的两台服务器上。

在本指南中,我们将使用 DigitalOcean 浮动 IP 地址 作为我们的路由机制。 浮动 IP 提供了一种将流量从一台服务器转移到另一台服务器的机制。 我们将创建一个浮动 IP 并将其指向我们的绿色服务器,以将其设置为我们的初始生产机器。

然后我们将修改我们的应用程序并将其部署到我们的蓝色服务器。 此时仍将通过未更改的绿色服务器提供生产流量。 然后我们可以测试蓝色服务器以确保我们的部署成功并且没有错误。 当我们准备好后,我们可以通过将 Floating IP 地址重新分配给蓝色服务器来将 Floating IP 移动到新版本的代码中。

我们将从创建我们的“应用程序”开始。 如上所述,这实际上只是我们的 Web 服务器可以显示的索引页面。 它允许我们演示应用程序的不同“版本”,而无需实际开发的开销。

在您的本地系统(或另一个 Droplet)上,使用您平台的首选方法安装 git。 如果您的本地机器运行的是 Ubuntu,您可以通过键入以下命令进行安装:

sudo apt update
sudo apt install git

在 Mac、Windows 或其他 Linux 环境中,如果尚未安装 git ,则应按照其文档 安装。

我们需要设置一些配置设置才能提交到 git 存储库。 我们将通过键入以下内容来设置我们的姓名和电子邮件地址:

git config --global user.name "Your Name"
git config --global user.email "username@email.com"

使用我们的配置集,我们可以为我们的新应用程序创建一个目录并进入它:

mkdir ~/sample_app
cd ~/sample_app

通过键入以下内容在我们的应用程序目录中初始化一个 git 存储库:

git init

现在,使用 nano 或您喜欢的文本编辑器创建代表我们应用程序的 index.html 文件:

nano index.html

在内部,我们将只指定应用程序的版本号。 这样,我们可以知道每个服务器上我们的应用程序的哪个版本:

~/sample_app/index.html

App v1

完成后保存并关闭文件。 如果您使用 nano,请按 Ctrl+X,然后在出现提示时按 Y 并回车。

最后,我们可以将 index.html 文件添加到 git 暂存区,然后通过键入以下内容进行提交:

git add .
git commit -m "initializing repository with version 1"

提交文件后,我们将暂时停止在本地机器上的应用程序开发,并专注于设置我们的蓝色和绿色 Web 服务器。

第 2 步 – 配置蓝色和绿色 Web 服务器

接下来,我们将使用功能性 Web 服务器设置我们的绿色和蓝色环境。 我们将在本指南中使用 Apache。 使用您的 sudo 用户登录您的服务器以开始使用。

注意:本节的步骤需要在蓝绿服务器上完成。


我们可以用 apt 安装 Apache。 通过键入以下内容更新本地包索引并安装 Web 服务器软件:

sudo apt update
sudo apt install apache2

这应该在您的两个 Web 服务器上安装并启动 Apache。

接下来,我们应该创建并配置一个“部署”用户。 该用户将有权访问 Apache 的 Web 根目录,并将拥有我们将应用程序推送到的裸 git 存储库。

通过键入以下内容创建 deploy 用户:

sudo adduser --disabled-password deploy

这将创建一个禁用密码验证的新用户。

我们将赋予这个新用户对 Apache 的默认 Web 根目录的所有权。 它位于 /var/www/html。 通过键入以下内容更改此目录的所有权:

sudo chown -R deploy:deploy /var/www/html

这就是我们部署所需的全部内容,它仅依赖于将文件移动到 Web 根目录中。

注意: 如果您偏离本指南并且您的部署步骤需要 root 权限,您需要配置无密码 sudo 权限以使用 [X209X ] 帐户。

这可以通过在 /etc/sudoers.d 目录中创建一个新的 sudoers 文件来完成:

sudo visudo -f /etc/sudoers.d/90-deployment

在此文件中,您可以添加在部署期间需要运行的命令。 这些可以这样指定:

/etc/sudoers.d/90-deployment

deploy ALL=(ALL) NOPASSWD: first_deployment_command, second_deployment_command, ...

完成后保存并关闭文件。 这应该允许 deploy 用户在没有密码的情况下正确执行所需的命令。


第 3 步 - 在绿色和蓝色 Web 服务器上设置 Git 部署

现在我们已经安装了 Apache 并且配置了一个用户来执行部署,我们可以配置一个裸 git 存储库来将我们的应用程序推送到。 然后我们可以设置一个 post-receive 钩子,当我们将其推送到我们的服务器时,它将自动部署我们主分支的最新版本。

注意:本节的步骤需要在蓝绿服务器上完成。


首先在两台服务器上安装 git

sudo apt install git

接下来,我们需要以我们的 deploy 用户身份登录。 我们可以通过输入 sudo 来做到这一点:

sudo su - deploy

在我们的 deploy 用户的主目录中,为我们的示例应用程序创建一个目录,就像我们在本地计算机上所做的那样。 创建后进入目录:

mkdir ~/sample_app
cd ~/sample_app

我们将在这个目录中初始化一个 git 存储库,就像我们在本地系统上所做的那样。 但是,在我们的服务器上,我们将包含 --bare 选项。 这将创建一个没有工作目录的 git 存储库。 相反,通常隐藏在 .git 目录中的内容将被放置到主文件夹中:

git init --bare

接下来我们将设置一个 post-receive 挂钩。 这只是一个脚本,它将在 git push 发生后部署我们的更改。 您可以通过阅读 本指南 了解有关此部署策略的更多信息。 我们应该把这个脚本放在我们 repo 的 hooks 目录中。 通过键入以下内容创建并打开文件:

nano hooks/post-receive

在里面,粘贴以下部署脚本。 这基本上与上面链接的文章中概述的脚本相同。 我们使用 GIT_DIR 变量来指示我们在服务器上的 git 存储库,WORK_TREE 变量来指定我们的 Apache 文档根,并且 HOSTNAME 来获取我们的进度消息的服务器主机名。 此脚本会将 main 分支中的所有更改部署到 web 目录。 下面的脚本不需要更改:

/home/deploy/sample_app/hooks/post-receive

#!/bin/bash

GIT_DIR=/home/deploy/sample_app
WORK_TREE=/var/www/html
HOSTNAME=$(hostname)

while read oldrev newrev ref
do
    if [[ $ref =~ .*/main$ ]];
    then
        echo "Main ref received.  Deploying main branch to $HOSTNAME..."
        git --work-tree=$WORK_TREE --git-dir=$GIT_DIR checkout -f
        echo "Git hooks deploy complete."
    else
        echo "Ref $ref successfully received.  Doing nothing: only the main branch may be deployed on this server."
    fi
done

如果您偏离本指南并需要更复杂的部署步骤,请将它们添加到上述脚本中的 then 子句中。 确保本节中需要提升权限的任何步骤都使用 sudo 命令。 此外,请确保将此处使用 sudo 的所有命令添加到最后一节底部指定的 sudoers 文件中。

完成后保存并关闭文件。

修改 post-receive 钩子的权限,使 git 可以在适当的时候执行:

chmod +x hooks/post-receive

第 4 步 – 在蓝色和绿色服务器上配置 SSH 密钥访问

接下来,我们将配置 SSH 密钥,以便 git 可以将更改推送到我们的 Web 服务器,而无需提示输入密码。

在您的 本地或开发计算机 上,通过键入以下命令检查您是否已经配置了 SSH 密钥:

cat ~/.ssh/id_rsa.pub

如果您已经有可用的 SSH 密钥对,您应该会看到如下所示的内容:

Outputssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDilFdzkgBcSKdh6tx5pLf+HH6Pv7z7jRZ7cSo6lQvecWOOgGl/wHCVZWx1ULvrF7VgJpgugLwxYsFh3E39sm1+7zeAlRxhFrbWvATwpAEwh5m0+48LTmvXCnJ8/om+GfmAwplmzGk/DNs5trVeagG62Css0rypdoNuLrVdCVKUXGXbO6KnpOsBqoM2HvZKtQ8j1gx+1UUnvK9LYes+ZzC2XZZeBh2dGABe7HNnd8+6e1f2ZjPEKAEV2fPJGAGaAQOnzSKJkUt/B9PdKFbCjnnG1sT0kQoxMRIAiqfR7wa7PUQCM5Orm5S92OTNcnRr8bWVjN18bWCyXkpxxWbIvVU/ user@devel

如果命令正确执行,请复制完整显示的文本。 我们将在下一节中使用它。 您现在可以安全地跳过那里。

如果您在本地计算机上 not 有 SSH 密钥,您可能会看到如下所示的错误:

Outputcat: /home/user/.ssh/id_rsa.pub: No such file or directory

如果是这种情况,您可以通过键入以下内容来创建新的公钥和私钥对:

ssh-keygen

在所有提示中按 Enter 以接受默认值。 创建密钥后,重新键入 cat 命令以显示新的公钥:

cat ~/.ssh/id_rsa.pub

这次应该正确执行。 复制显示的行以在下一部分中使用。

回到您的绿色和蓝色服务器,我们将授权我们在本地或开发机器上的帐户连接到我们的 deploy 用户。

作为您的 deploy 用户,创建一个 ~/.ssh 目录。 在里面,打开一个名为 authorized_keys 的文件:

mkdir ~/.ssh
nano ~/.ssh/authorized_keys

在此文件中,粘贴您从本地计算机复制的输出:

~/.ssh/authorized_keys

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDilFdzkgBcSKdh6tx5pLf+HH6Pv7z7jRZ7cSo6lQvecWOOgGl/wHCVZWx1ULvrF7VgJpgugLwxYsFh3E39sm1+7zeAlRxhFrbWvATwpAEwh5m0+48LTmvXCnJ8/om+GfmAwplmzGk/DNs5trVeagG62Css0rypdoNuLrVdCVKUXGXbO6KnpOsBqoM2HvZKtQ8j1gx+1UUnvK9LYes+ZzC2XZZeBh2dGABe7HNnd8+6e1f2ZjPEKAEV2fPJGAGaAQOnzSKJkUt/B9PdKFbCjnnG1sT0kQoxMRIAiqfR7wa7PUQCM5Orm5S92OTNcnRr8bWVjN18bWCyXkpxxWbIvVU/ user@devel

完成后保存并关闭文件。

接下来,锁定权限,以便 SSH 可以使用您创建的文件:

chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

第 5 步 – 在本地开发机器上配置 Git Remote

现在我们已经为我们的 Web 服务器配置了 SSH 密钥访问权限,并且在每台服务器上都设置了我们的应用程序目录,我们可以将蓝色和绿色服务器作为远程服务器添加到本地 git 应用程序存储库中。

在您的本地机器上,移回您的应用程序目录:

cd ~/sample_app

添加远程引用,以便 git 可以将更改推送到您的绿色和蓝色 Web 服务器:

git remote add blue deploy@blue_server_ip:sample_app
git remote add green deploy@green_server_ip:sample_app

我们现在应该能够将我们的应用程序推送到我们的两个服务器。 让我们通过将应用程序的版本 1 推送到两台服务器来测试它。

git push blue main
git push green main

您可能必须在第一次部署时接受每个服务器的密钥指纹。 您应该会看到如下所示的输出:

OutputThe authenticity of host '111.111.111.111 (111.111.111.111)' can't be established.
ECDSA key fingerprint is 30:a1:2c:8b:ec:98:a3:3c:7f:4a:db:46:2b:96:b5:06.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '111.111.111.111' (ECDSA) to the list of known hosts.
Counting objects: 3, done.
Writing objects: 100% (3/3), 246 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Main ref received.  Deploying main branch to blue...
remote: Git hooks deploy complete.
To deploy@111.111.111.111:sample_app
 * [new branch]      main -> main

如您所见,以“remote:”开头的行包含来自我们服务器上的 post-receive 挂钩的 echo 语句。 请记住将您的应用程序推送到您的两台服务器。

我们可以使用 curl 测试我们的应用程序的初始部署是否成功:

curl blue_server_ip
curl green_server_ip

对于这两个调用,响应应该如下:

OutputApp v1

这表明我们的部署脚本工作正常。

设置浮动 IP 地址以路由流量

现在我们已经部署了应用程序的初始版本,我们可以创建一个浮动 IP 地址并将其最初指向我们的“绿色”服务器。

在 DigitalOcean 控制面板中,单击“网络”选项卡,然后单击“浮动 IP”菜单项。 在提供的菜单中,选择您的绿色服务器,然后单击“分配浮动 IP”按钮:

几秒钟后,IP 应该分配给您的绿色服务器:

您现在可以将此 IP 地址用作生产应用程序部署的主要入口点。 如果您想为您的网络应用程序设置一个域名,您可以将该域指向这个浮动 IP 地址。

通过键入以下内容测试您的应用程序是否可以通过浮动 IP 地址访问:

curl floating_IP_addr

您应该看到应用程序的版本 1:

OutputApp v1

绿色服务器当前正在提供此响应。

第 6 步 – 练习蓝绿部署

现在我们的配置已经完成,我们可以在实践中演示蓝绿部署是如何工作的。 目前,我们的浮动 IP 地址指向我们的绿色服务器。 如前所述,浮动 IP 地址代表生产流量,是我们附加应用程序域名的位置。

在您的本地或开发机器上,我们可以对我们的应用程序进行一些更改。 打开索引文件:

cd ~/sample_app
nano index.html

让我们通过增加版本号来对我们的应用程序进行可见的更改:

~/sample_app/index.html

App v2

完成后保存并关闭文件。

将文件添加到 git 暂存区域并通过键入以下内容提交您的更改:

git add .
git commit -m "Application version 2"

接下来,我们可以将我们的新更改推送到我们的非活动环境。 这将使我们有机会在不影响生产服务器的情况下测试我们的部署。

由于我们的浮动 IP 地址当前指向我们的绿色环境,我们将部署到我们的蓝色服务器。 通过在本地开发机器上键入以下内容,将新更改推送到蓝色环境:

git push blue main

如果我们访问我们的 浮动 IP 地址 ,我们应该看到我们的应用程序的版本 1 仍在提供服务:

curl Floating_IP_addr
OutputApp v1

但是,如果我们检查我们的 blue 服务器的常规 IP 地址 ,我们可以测试我们的应用程序的版本 2:

curl blue_server_IP
OutputApp v2

这是我们期望的,也是我们想要的。 我们现在可以通过我们需要的任何内部测试来运行我们的蓝色服务器环境。 一直以来,绿色服务器将继续为我们的生产流量服务。

一旦您测试了应用程序的最新版本并确信它按预期运行,我们就可以将生产流量切换到蓝色服务器。

为此,请访问 DigitalOcean 控制面板。 单击“网络”选项卡,然后选择“浮动 IP”导航项。 在“浮动 IP”列表中,您应该看到您的浮动 IP,它当前指向绿色服务器:

在我们切换之前,在您的一个终端窗口中,启动一个 while 循环,以便我们可以通过浮动 IP 地址发出重复请求。 这让我们可以立即看到我们的生产应用程序版本从 v1 到 v2 的转换:

while true; do curl Floating_ip_addr; sleep 2; done

它应该开始输出网络请求的结果:

OutputApp v1
App v1
App v1
App v1

现在,要进行切换并“发布”您的软件的新版本,请单击浮动 IP 分配右侧的蓝色按钮以重新分配 IP 地址。 选择您的蓝色服务器:

几秒钟后,您的浮动 IP 将重新分配给您的蓝色服务器。 在您的终端窗口中,更改应该很明显:

OutputApp v1
App v1
App v2
App v2

按“Ctrl+C”停止 while 循环。

您的生产流量现在正在路由到您的应用程序的新版本。 您之前的生产服务器(绿色服务器)现在已设置为您的回滚机器和您的下一个暂存区。

如果在将流量转移到应用程序的新版本后发现问题,此发布策略允许您快速轻松地回滚到以前的版本。 为此,只需反转过程并将您的浮动 IP 地址指向绿色服务器。

第 7 步 - 处理数据库更新(可选)

为了专注于部署和发布策略本身,上面概述的场景被缩小了范围。 我们没有涵盖更复杂但更常见的设置,例如涉及数据库的设置。

您可以使用几种不同的策略来处理两个环境之间的持久数据。

可以为每个环境维护一个单独的数据库。 但是,此策略要求您将生产数据库中的数据复制到非活动数据库,并在启动切换时停止事务。 基本上,它需要实时数据库迁移以及每次部署的片刻停机时间。 这很快就会变得非常耗时且容易出错。

更好的选择通常是在绿色和蓝色环境之间共享一个数据库系统。 应用程序代码将可以使用蓝绿发布策略进行切换,而数据库本身将被两种环境使用。

这种方法的主要关注点是如何部署和发布包含非向后兼容数据库迁移的更新。 如果我们将一个新版本部署到 staging 中,以一种不适用于当前生产部署的方式添加或更改数据库,我们将破坏我们的应用程序。

为防止这种情况发生,通常最好将迁移与代码库部署分开部署,并在必要时分阶段进行。 这种修改后的过程有时称为 blue-turquoise-green 部署 。 基本上,它取决于部署可以处理旧版本和新版本数据库的应用程序代码的中间版本。

中间应用程序代码与旧版本几乎完全相同,但有一些额外的逻辑,为迁移发生后将存在的新数据结构做好准备。 通常,这是通过构建迁移来完成的,以便它们创建全新的数据结构而不是修改现有的数据结构。 这样,您可以保留旧的数据结构,比如说一个表,并创建一个包含重大更改的新数据结构。

中间 turquoise 部署部署为迁移过程的第一步。 此部署将首先读取和写入旧表,但它会检查新结构是否存在。 接下来,迁移本身运行,在旧版本旁边创建新版本的数据结构。 turquoise 部署的逻辑应配置为识别新结构已就位,并应开始将更改写入 ' 旧结构和新结构。 它将暂时继续从旧结构中读取。

此时,所有新活动都将记录在两种数据结构中。 您可以用旧结构中的数据回填新结构,并沿途对其进行转换以满足新结构的条件。 完成后,您的所有记录都应存在于这两个位置。 为了继续过渡,下一个应用程序部署可能会继续写入这两个结构,但可能会从新结构中读取。 在确认一切顺利运行后,另一个部署可能会切断旧结构的写入,并且可能会删除旧结构。

这个过程一开始似乎相当复杂,但在实践中通常不会有太多额外的工作。 主要工作包括建立一个临时使用旧结构和新结构的安全网。 这使您有时间在提交迁移之前深入测试迁移,并允许您随时回滚到数据结构的先前工作版本。 有关如何进行数据迁移的示例,请查看 Etsy 的 Mike Brittain 的一些 这些幻灯片

结论

虽然可以采用许多其他策略来将部署与新代码的实际发布分开,但蓝绿部署是一种可以快速实施的机制。 它提供了一个完全反映生产环境的良好暂存环境,同时在发布后如果事情没有按预期进行,则提供立即回滚的机会。

接下来,您可能想学习如何使用现代 Kubernetes 工具 (例如 ArgoCD)执行 蓝绿部署。