本文背后的想法是探索在不停机的情况下将Jenkins发行版部署到Docker Swarm的方法 。 我们将使用蓝绿色程序。 有关该过程和一种可能的实现的更多信息,请参见蓝绿色部署,自动化和自我修复过程文章。 我们在该文章中使用的过程的缺点之一是Ansible本身。 虽然它可能是配置和编排的最佳工具,但当我们尝试将其用作部署容器的工具时,它也有一些缺点。 当过程复杂时,这一点尤其明显。 Ansible缺乏大多数编程语言中常见的一些构造。 这次,我们将尝试实现相同的过程,但使用的是由CloudBees开发并为Jenkins开源项目贡献力量的Jenkins工作流插件 。
简而言之,我们将执行以下操作。
- 调配Swarm集群
- 设置代理服务
- 拉最新版本
- 与当前版本并行部署最新版本
- 运行集成前测试,以确认一切似乎正常运行
- 更新代理服务
- 运行集成后测试,以确认代理服务似乎一切正常
- 停止上一个版本
在前五个步骤中,用户将使用当前版本,而无需考虑我们部署的新版本的存在。 由于我们将同时运行两个版本,因此在部署过程中不会停机。
在这些步骤之上,如果出现问题,该过程应该能够回滚。 我们将引入的其他功能是请求扩展服务并在后续部署中维护扩展的实例数。
首先,我们将看到作业的实际效果,然后讨论其完成方式。
设置Docker Swarm集群和Jenkins
我们将从创建运行Docker Swarm所需的集群开始。 我们将使用VirtualBox和Vagrant创建VM,然后为其提供Ansible 。 我不会详细介绍我们将如何运行Ansible剧本,因为可以在此博客的其他帖子中找到该信息。 该设置将类似于最近发布的《 使用Docker Swarm和Docker Networking部署容器》中描述的设置。 主要区别是将添加Jenkins和工作流插件。
我假设您已经安装了VirtualBox , Vagrant和Git 。 如果您是Windows用户,请先按照在Windows上运行Linux VM中介绍的说明进行操作,然后再介绍以下内容。
我们走吧! 我们可以通过运行以下命令来创建服务器。
git clone https://github.com/vfarcic/blue-green-docker-jenkins.git
cd blue-green-docker-jenkins
vagrant plugin install vagrant-cachier
vagrant up swarm-master swarm-node-1 swarm-node-2
接下来,我们应该为服务器配备Docker Swarm,Docker Compose,Consul,Consul模板和Registrator。 如果您不熟悉 Docker Swarm,则可以阅读``比较的Docker集群工具:Kubernetes与Docker Swarm''一文中的内容。 关于Consul以及我们将用于服务发现的其他工具的信息可以在Service Discovery:Zookeeper vs etcd vs Consul文章中找到。
创建这三台服务器可能会花费一些时间,尤其是这是您第一次将Vagrant与Ubuntu结合使用时。 一旦它们启动并运行,我们就可以使用Ansible来配置集群。 由于创建VM包括在swarm-master节点中安装Ansible,因此我们可以使用它来运行swarm.yml剧本。
vagrant ssh swarm-master
ansible-playbook /vagrant/ansible/swarm.yml \
-i /vagrant/ansible/hosts/prod
最后,剩下的唯一事情就是建立詹金斯。 我们将继续使用Ansible进行此任务。 它将创建几个目录,运行Jenkins容器,摆弄一些配置,安装我们需要的插件,最后创建将进行实际部署的工作。 关键是要尽可能地自动化,Jenkins的安装也不例外。 由于本文的重点是在蓝绿色部署到Swarm集群的环境中尝试Jenkins Workflow,因此,我将跳过jenkins.yml剧本如何工作的说明。 该设置类似于我在Jenkins,Docker和Ansible的“ 持续集成,交付或部署”文章中使用的设置,但几乎没有改进。 随意看一下代码。
ansible-playbook /vagrant/ansible/jenkins.yml \
-i /vagrant/ansible/hosts/prod
探索Jenkins工作流程
让我们开始构建。 由于第一次运行的时间比连续运行的时间长,因此我们将利用这段时间来讨论解决方案。 请打开http://10.100.192.200:8080/job/books-ms/build?delay=0sec ,然后单击“ 生成”按钮。 您可以通过打开http://10.100.192.200:8080/job/books-ms/lastBuild/console页面来监视进度。
在作业运行期间,让我们快速看一下我们将用于运行测试和部署服务的docker-compose.yml 。
app:
image: vfarcic/books-ms
ports:
– 8080
environment:
– SERVICE_NAME=books-ms
– DB_HOST=books-ms-db
app-blue:
environment:
– SERVICE_NAME=books-ms-blue
extends:
service: app
app-green:
environment:
– SERVICE_NAME=books-ms-green
extends:
service: app
db:
container_name: books-ms-db
image: mongo
environment:
– SERVICE_NAME=books-ms-db
integ:
image: vfarcic/books-ms-tests
volumes:
– ./src:/source/src
environment:
– TEST_TYPE=integ
– DOMAIN=$DOMAIN
应用目标代表了我们的主要服务。 但是,由于我们需要将蓝色部署与绿色部署区分开来,因此还有两个额外的目标,分别是app-blue和app-green 。 真正重要的部分是目标名称。 它们都扩展了应用程序目标,以避免重复。 我们正在传递环境变量SERVICE_NAME ,该变量将帮助Registrator在将信息发送给Consul之前更好地定义服务。 除了应用程序*目标,我们有MongoDB的确定为DB和INTEG目标,我们将使用运行测试。
借助Docker Compose,我们可以继续进行探索并进行实际部署的Groovy脚本service-flow.groovy 。 请注意,为简单起见,我跳过了构建容器,运行单元和功能测试以及将容器推送到注册表的步骤。 建议您将它们放入我们将要探索的工作流程中。 在这种情况下,Groovy脚本假定这些步骤已在单独的过程中执行,并且已验证了我们将要部署的容器。
首先,我们应该声明几个变量。
def swarmMaster = "10.100.192.200"
def proxy = "10.100.192.200"
def currentColor = getCurrentColor(swarmMaster, service)
def nextColor = getNextColor(currentColor)
def instances = getInstances(swarmMaster, service)
swarmMaster和代理代表我们稍后将使用的IP。 currentColor和nextColor变量值是通过咨询Consul的函数设置的,并检索当前部署的颜色以及将要部署的颜色的值。 最后,我们发现应该部署多少个实例。 getInstances函数具有两个条件。 如果Jenkins构建屏幕上的instances参数设置为0 (默认值),则该函数将向Consul请求此信息,该信息将返回我们当前正在运行的实例数,如果这是第一次部署,则返回1。 另一方面,如果Jenkins job build参数不为0 ,它将用作实例变量。 换句话说,我们可以通过在Jenkins构建屏幕中设置一些值来决定要部署多少个实例,或者让脚本部署与当前版本相同数量的容器。 如果您对这些功能的工作方式感兴趣(请在脚本的底部),请查阅service-flow.groovy脚本。
我们应该从配置群集开始。 即使我们已经设置了Swarm和服务发现工具,确保所有内容仍按预期运行始终是一个好主意。 除了集群,我们还需要负载均衡器(在本例中为nginx )。 如果一切设置正确,则配置仅需几秒钟。 另一方面,如果某个进程停止运行,或者像nginx一样,它甚至从未运行过,那么我们的供应阶段将对此进行纠正。
脚步:
- 设置Docker Swarm集群
- 预配负载均衡器(nginx)
node("cd") {
env.PYTHONUNBUFFERED = 1
stage "> Provisioning"
if (provision.toBoolean()) {
sh "ansible-playbook /vagrant/ansible/swarm.yml \
-i /vagrant/ansible/hosts/prod"
sh "ansible-playbook /vagrant/ansible/nginx.yml \
-i /vagrant/ansible/hosts/prod --extra-vars \
\"proxy_host=swarm-master\""
}
我们首先声明这些步骤将在名为或标记为cd (连续部署的缩写)的Jenkins节点内运行。 为简单起见,在这种情况下, cd是swarm-master节点的标签之一。 在生产中,应尽可能在专用于连续交付而不是在生产中的服务器中运行。 接下来是具有多个目的的阶段声明。 它标志着一组步骤,允许我们限制并发性,并且,如果您选择使用CloudBees Jenkins Enterprise Edition ,则可以提供可视化效果,从选定阶段重新启动的能力以及其他一些功能。 在该阶段的下面,您会注意到命令位于if
语句内。 Provisioning变量的值来自您在Jenkins 构建屏幕中看到的复选框。 此代码段的“内容”是两个sh
语句。 “ sh”是工作流插件提供的步骤类型之一,您可能已经猜到了,它运行我们指定的任何shell命令。 在这种情况下,我们将使用它来运行Ansible剧本,以应对配置问题。 请注意,设置服务器时未安装nginx。 nginx.yml剧本将为我们做到这一点。
进行自动调配后,我们应该开始部署。 由于我们正在部署已经构建并推送到Docker Hub的Docker容器,因此我们需要的是一个docker-compose.yml文件,该文件最好与服务代码保存在同一存储库中。 这样,维护代码的团队可以完全控制定义容器的部署和测试方式,而无需了解Jenkins。 我们可以通过简单的git步骤克隆存储库来获取文件。
脚步:
- 从存储库克隆代码
stage "> Deployment"
git url: "https://github.com/${repo}.git"
请注意${repo}
的用法。 这是利用可以在Jenkins构建屏幕中指定的参数的另一种情况。
现在,我们准备部署新版本。
脚步:
- 拉新版本
- 运行新版本
- 存储我们在Consul中部署的实例数
env.DOCKER_HOST = "tcp://${swarmMaster}:2375"
sh "docker-compose -f docker-compose-jenkins.yml \
pull app-${nextColor}"
sh "docker-compose -f docker-compose-jenkins.yml \
--x-networking up -d db"
sh "docker-compose -f docker-compose-jenkins.yml \
rm -f app-${nextColor}"
sh "docker-compose -f docker-compose-jenkins.yml \
--x-networking scale app-${nextColor}=$instances"
sh "curl -X PUT -d $instances \
http://${swarmMaster}:8500/v1/kv/${service}/instances"
首先,我们将DOCKER_HOST变量设置为指向Swarm Master。 从现在开始,所有Docker命令将被发送到该IP /端口。 接下来,我们要拉应用程序。 接下来是数据库的部署和要部署的目标的删除。 比我们正在运行服务本身。 请注意,我们正在使用实例变量来扩展服务。 最后,我们将实例数量发送给Consul,以便下次运行部署时使用相同的数量(除非我们在Jenkins构建屏幕中明确更改了实例)。
现在该服务已部署在Swarm集群内部的某个位置,我们可以进入部署后任务。 请注意,如果这不是第一次部署,则我们的代理服务将指向旧版本,而我们的用户将忽略我们刚刚部署的新版本的存在。 在重新配置代理之前,我们应该测试新版本是否正常运行。
脚步:
- 找到新部署的服务
- 运行集成前测试
- 如果测试失败,请停止新版本
stage "> Post-Deployment"
def address = getAddress(swarmMaster, service, nextColor)
try {
env.DOCKER_HOST = ""
sh "docker-compose -f docker-compose-jenkins.yml \
run --rm -e DOMAIN=http://$address integ"
} catch (e) {
env.DOCKER_HOST = "tcp://${swarmMaster}:2375"
sh "docker-compose -f docker-compose-jenkins.yml \
stop app-${nextColor}"
error("Pre-integration tests failed")
}
关于Swarm的事情是,我们不再完全受到控制。 我们不决定将容器部署在何处。 Docker Swarm为我们完成了这项工作,我们必须找出它在哪里部署了我们的服务。 由于Registrator将与新部署的发行版相关的信息存储到Consul,我们要做的就是查找IP,而端口就是查询它。 这就是getAddress函数的作用。 如果您有兴趣查看功能代码,请查阅service-flow.groovy源代码。
一旦获得我们的服务位置,就可以运行集成测试。 由于我们不想浪费生产服务器中的资源,因此我们必须删除DOCKER_HOST变量的值,以便在本地运行测试。 您会注意到,测试的执行在try块内。 如果失败,脚本将跳入catch语句。 如果发生这种情况,我们将停止刚刚部署的容器,并退出并显示错误脚本。
现在我们知道我们部署的发行版可以正常工作,我们可以重新配置代理服务,以便我们的用户可以开始使用它。
脚步:
- 生成nginx配置
- 将配置复制到代理服务器
- 重新加载nginx
updateProxy(swarmMaster, service, nextColor);
使用Consul Template可以轻松更改nginx配置。 在这种情况下,我们需要一个模板,该模板存储在服务存储库中( nginx-upstreams-blue.ctmpl和nginx-upstreams-green.ctmpl )并运行命令。 在这种情况下,结果是从模板生成的nginx-upstreams.conf文件和存储在Consul中的数据。
请注意,我们现在所做的一切都在cd节点中执行,因此对生产服务器的影响最小。 现在该切换到代理服务器了( lb )。 有关实现的详细信息,请查看service-flow.groovy源代码中的updateProxy函数。
通过第一轮测试(预集成),我们验证了除了代理本身以外的所有其他东西都已正确部署和配置。 目标是在不影响我们用户的情况下进行尽可能多的测试(代理直到最近都指向旧版本)。 尽管这不是本文的一部分,但在构建容器并将其推送到中心之前,我会运行各种单元,功能以及无需部署服务即可执行的任何其他类型的测试。
现在是进行最后一轮测试(集成后)的时候了。 我们应该验证代理配置中的更改是否正确完成,并且确实可以通过代理访问该服务。
脚步:
- 运行集成后测试
- 如果测试失败,请还原代理配置
- 如果测试失败,请停止新版本
try {
env.DOCKER_HOST = ""
sh "docker-compose -f docker-compose-jenkins.yml \
run --rm -e DOMAIN=http://${proxy} integ"
} catch (e) {
if (currentColor != "") {
updateProxy(swarmMaster, service, currentColor)
}
env.DOCKER_HOST = "tcp://${swarmMaster}:2375"
sh "docker-compose -f docker-compose-jenkins.yml \
stop app-${nextColor}"
error("Post-integration tests failed")
}
sh "curl -X PUT -d ${nextColor} http://${swarmMaster}:8500/v1/kv/${service}/color"
该模块几乎与我们用于集成前测试的模块相同。 主要区别在于,这一次我们不是直接通过服务的IP和端口来测试服务,而是通过代理来使用服务使用者访问服务的方式。 如果测试失败,则不仅需要停止新版本,而且还必须将更改还原到nginx配置。 最后,如果一切顺利完成,我们将使用刚刚部署的颜色信息更新Consul。
我们快完成了。 该服务已部署,测试(多次),并且代理已相应更改。 剩下的唯一事情就是清理工作。
脚步:
- 停止旧版本(除非这是第一个版本的部署)
if (currentColor != "") {
env.DOCKER_HOST = "tcp://${swarmMaster}:2375"
sh "docker-compose -f docker-compose-jenkins.yml \
stop app-${currentColor}"
}
我们已经完成了对用于对Docker Swarm进行蓝绿色部署的Jenkins工作流脚本的探索。 希望到这个时候,Jenkins构建的第一次运行完成。 导致速度缓慢的主要原因是测试容器,其虚拟大小超过2GB。 虽然在这种情况下,我们仅使用它来运行后端集成测试,但在其他少数情况下,我却使用它来运行在FireFox和Chrome上运行的前端测试以及完整的后端测试套件。 它包含JDK,Scala,NodeJS,很多Bower和Scala库,等等。 这是一个巨大的容器,其中包含我测试该服务所需的一切。 在将容器推送到Docker Hub之前,使用了相同的容器执行单元和功能测试。 尽管可以使用这么大的尺寸进行测试,但请注意生产容器应尽可能小。 例如, books-ms容器的虚拟大小超过300MB。
请返回到Jenkins控制台屏幕,并确认构建完成。 最后一个条目应为Finished:SUCCESS 。 如果它仍在运行,请接受我的歉意,请耐心等待。 我没有时间只为本文创建一个较小的示例。
如果查看此巨大的日志要求太高,请尝试“ 工作流程步骤”屏幕。 该链接位于左侧菜单中,它显示了脚本中每个步骤的单独日志。
您还可以查看已部署的容器。
export DOCKER_HOST=tcp://10.100.192.200:2375
docker ps --filter name=books --format "table {{.Names}}"
就我而言, docker ps
命令的输出如下。
NAMES
swarm-node-1/booksms_app-blue_1
swarm-node-2/books-ms-db
Swarm将应用程序容器部署到节点1,将db容器部署到节点2。请注意,不能保证您的输出是相同的。 Swarm可能已决定将容器部署到不同的节点。
继续并重复构建多次。 使用instances参数的不同值进行实验。 例如,查看如果选择部署下一个发行版的五个实例会发生什么。 完成这样的部署后,您可以重复docker ps
命令并查看Swarm在哪里部署了容器。 每次运行构建时,颜色都会改变,并且在此过程中不会停机。 此外,该发行版将在生产中进行测试,而不会影响用户。 如果您喜欢冒险,请尝试停止Nginx容器或swarm节点之一。 由于供应是部署的一部分,因此一旦完成新的构建,一切将恢复正常。 第一次工作要花很长时间,不要气disc。 现在已配置了所有服务器,并且所有容器都已被拉出。 连续运行将更快。
告别思想
希望本文概述了成功实施到Docker Swarm的蓝绿色部署可能需要执行的可能步骤。 我们所做的是将群集的配置和配置与蓝绿色的部署过程结合在一起。 更重要的是,我希望您看到了Jenkins Workflow插件带来的好处,它比组织Jenkins职位的更多常用方法更具优势。 同一过程可能需要多个工作,而这些工作将很难维护。 而且,与尝试使用标准的Jenkins Jobs XML语法相比,读写Groovy脚本要容易得多,而且速度更快。 该脚本可以并且应该在源代码存储库中。 可以通过与通常更改代码相同的方式来完成此作业的任何更改。 我们需要做的就是重新运行Ansible剧本,然后更新工作。
请注意,即使此脚本在开始时看起来令人生畏,但对您自己的组织进行了一些改动,也可以轻松地重用它来部署许多不同的服务。 只要使用某些命名约定(主要是使用我们命名docker-compose.yml目标的方式),您就应该能够在大多数(如果不是全部)部署中重用它。 您要做的就是将更多条目添加到defaults / main.yml文件中的jobs变量中,然后再次运行该剧本。
我们做事方式的主要缺点是通过Ansible创建了詹金斯的工作。 尽管它满足了我们的需求,但它感觉更像是一种解决方法,而不是长期的解决方案。 我认为问题在于当前的詹金斯OSS解决方案来管理工作效率更低。 CloudBees Jenkins Platform Enterprise Edition通过模板化插件解决了此问题,并为Jenkins Workflow提供了更多功能。 如果您有兴趣尝试一下,CloudBees支持人员将很乐意提供指导。
翻译自: https://www.javacodegeeks.com/2015/12/blue-green-deployment-docker-swarm-jenkins-workflow-plugin.html