作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
作为一名全栈软件工程师,Leah身兼数职. 除了软件,她还擅长安全、可扩展的AWS部署.
除非你一直住在集装箱里,否则你可能听说过集装箱. 该行业正在从持久的基础设施转向短暂的基础设施, 集装箱在移动的中间是正方形的. 原因很简单:虽然容器确实可以帮助开发团队快速启动和运行, 它们甚至更有可能彻底改变运营的面貌.
但这到底是什么样子呢? 当您准备跳过本地运行容器时,会发生什么呢, 或者手动在几个服务器上? 在理想情况下,您只希望将应用程序扔到服务器集群上,然后说“运行它”!”
Well thankfully, that’s where we are today.
In this article, 我们将探索Docker Swarm是什么, 它还提供了一些很棒的功能. 然后我们将看看实际使用蜂群模式和部署到蜂群是什么样子的, 我们将用一些例子来结束我们的演讲,这些例子说明了部署集群的日常操作是怎样的. 一定要具备Docker和容器的基本知识, 但是你可以看看这个很棒的 blog post 首先,如果您是容器的新手.
在我们开始创建和部署我们的第一个蜂群之前, 了解Docker Swarm是什么是很有帮助的. Docker本身已经存在很多年了,今天大多数人都认为它是一个容器运行时. 但实际上,Docker是由许多不同的部分组成的,它们都在一起工作. 例如,容器运行时部分由两个较小的组件处理,称为 runC and containerd. 随着Docker的发展和对社区的回馈, 他们发现,创建这些较小的组件是增长和快速添加功能的最佳方式. 因此,我们现在有了直接内置到Docker中的Swarm kit和Swarm模式.
Docker Swarm是一个容器编排引擎. At a high level, 它需要在不同的主机上运行多个Docker引擎,并允许您一起使用它们. 用法很简单:将应用程序声明为服务堆栈,然后让Docker处理其余部分. 服务可以是任何东西,从应用程序实例到数据库, 或者像Redis或RabbitMQ这样的工具. 如果您曾经在开发中使用过docker-compose,那么这应该听起来很熟悉, 因为这是完全相同的概念. 实际上,堆栈声明实际上只是一个 docker-compose.yml
file with the version 3.1 syntax. 这意味着您可以为开发和集群部署使用类似的(在许多情况下是相同的)组合配置, 但我有点操之过急了. 当你在集群模式下有Docker实例时会发生什么?
在Swarm世界中我们有两种类型的节点(服务器):管理者和工作者. 重要的是要记住,经理也是工人, 他们只是有额外的责任让事情正常运转. 每个集群开始时都有一个被指定为领导者的管理节点. 从那里,只需运行一个命令就可以安全地将节点添加到集群中.
Swarm是高度可用的,这要归功于它对Raft算法的实现. 我不会讲太多关于Raft的细节因为已经有了 great tutorial on how it works, 但总体思路是这样的:领导节点不断地与其他管理节点签入,并同步它们的状态. 为了使状态更改被“接受”,经理节点达成共识, 当大多数节点承认状态变化时会发生什么.
这样做的美妙之处在于,管理节点可以偶尔退出,而不会影响集群的共识. 如果状态变更达成共识, 我们知道它保证存在于大多数管理节点上,并且即使当前的leader发生故障也会持续存在.
假设我们有三个名为A、B和C的管理节点. 当然,A是我们无畏的领导者. 有一天,一个短暂的网络错误使a脱机,只剩下B和C. 很长时间没有A的消息(几百毫秒), B和C等待一段随机生成的时间,然后将自己提交选举并通知对方. 当然,在这种情况下,第一个参加选举的人就会当选. 在这个例子中,B成为新的领导者,并且恢复了法定人数. 但接下来,剧情发生了转折:当A重新上线时会发生什么? 它会认为自己仍然是领导者,对吧? 每次选举都有一个与之相关的任期,所以a实际上是在任期1中当选的. 只要A重新上线,开始对B和C发号施令, 他们会好心地告诉它B是第二学期的leader, and A will step down.
当然,同样的过程适用于更大的范围. 可以有三个以上的管理节点. 不过我还要补充一点. 每个Swarm只能承受一定数量的管理员损失. 一群n个管理节点可能会丢失 (n-1)/2
不失去法定人数的管理人员. 这意味着对于一个3个经理群,你可能会失去一个,对于5个经理群,你可能会失去两个,等等. 这背后的原因又回到了多数人的共识, 当你进入制作阶段时,一定要记住这一点.
到目前为止,我们已经确定,我们的经理非常善于保持同步. Great! 但是他们到底在做什么呢? 还记得我说过如何将服务堆栈部署到Swarm吗? 当你宣布你的服务, 您向Swarm提供了有关您实际希望如何运行服务的重要信息. 这包括诸如每个服务需要多少个副本之类的参数, 副本应该如何分发, 它们是否应该只在某些节点上运行, and more.
Once a service is deployed, 管理人员的工作是确保您设置的任何部署需求继续得到满足. 假设你部署了一个Nginx服务,并指定应该有三个副本. 管理器将看到没有容器正在运行,并将三个容器均匀地分布在可用的节点上.
What’s even cooler, though, 如果容器失败(或者整个节点脱机), Swarm将自动在剩余节点上创建容器以弥补差异. 如果你说你想要运行三个容器, 您将运行三个容器, 而Swarm处理所有的细节. 另外——这是一个很大的好处——扩大或缩小规模就像给Swarm一个新的复制设置一样简单.
我想指出最后一个例子的一个重要但微妙的细节:如果Swarm是在它选择的节点上智能地启动容器, 我们不一定知道这些容器将在哪里运行. 乍听起来可能很吓人,但这实际上是Swarm最强大的功能之一.
继续Nginx的例子, 假设我们告诉Docker这些容器应该公开端口80. 如果您将浏览器指向在端口80上运行该容器的节点, 您将看到容器的内容. There’s no surprise there. 但令人惊讶的是什么呢, 是将请求发送到没有运行该容器的节点吗, 您仍然会看到相同的内容! What’s happening here?
Swarm实际上使用一个入口网络将您的请求发送到运行该容器的可用节点, 同时它也在进行负载平衡. 所以如果你向同一个节点发出三个请求, 你可能会碰到三个不同的容器. 只要你知道集群中单个节点的IP,你就可以访问其中运行的任何东西. Conversely, 这使您可以将负载平衡器(例如ELB)指向集群中的所有节点,而无需担心哪些节点在哪里运行.
它并不局限于外部连接. 运行在同一堆栈上的服务有一个覆盖网络,使它们能够相互通信. 而不是在代码中硬编码IP地址, 您可以简单地使用服务的名称作为要连接的主机名. For example, 如果你的应用程序需要与一个名为“Redis”的Redis服务进行通信,它可以简单地使用“redis”作为主机名,Swarm会将它的请求路由到适当的容器. 因为这在开发中使用Docker -compose和在生产中使用Docker Swarm都可以无缝地工作, 在部署应用程序时少了一件需要担心的事情.
If you’re in ops, 当生产更新出现严重错误时,您可能经历过恐慌发作. 它可能是一个错误的代码更新,或者甚至只是一个配置错误,但是突然之间生产就停止了! 很可能老板不会在意这两种情况. 他们只会知道这是你的错. 别担心,蜂群也会帮你的.
When updating a service, 您可以定义一次应该更新多少个容器,以及如果新容器开始失败应该发生什么. 在某个阈值之后,Swarm可以停止更新或者(从Docker 17开始).04)将容器回滚到之前的映像和设置. 不要担心明天早上要给你的老板送咖啡.
最后,但并非最不重要的是,Docker Swarm自带了出色的开箱即用的安全特性. 当一个节点加入集群时, 它使用一个令牌,不仅可以验证自身,还可以验证它是否加入了您认为它是的群体. 从那一刻起,节点之间的所有通信都使用相互TLS加密进行. 这些加密都是由Swarm自动提供和管理的, 所以你永远不需要担心更新证书, 以及其他典型的安全问题. 当然,如果你想强制按键旋转,这里有一个命令.
最新版本的Docker Swarm还带有内置的秘密管理功能. 这使您可以安全地将密钥和密码等秘密部署到需要它们的服务, 而且只针对需要它们的服务. 当您提供带有秘密的服务时, 该服务的容器将在其文件系统中挂载一个特殊文件,其中包含secret的值. It goes without saying, 但这比使用环境变量要安全得多, 哪些是传统的方法.
如果你像我一样,你会迫不及待地想要加入进来,体验一下所有这些功能! 闲话少说,让我们开始吧!
我创建了一个非常初级的Flask应用程序来演示Docker Swarm的强大功能和易用性. web应用程序只是显示一个页面,告诉你是哪个容器服务了你的请求, 总共服务了多少个请求, 以及“秘密”数据库密码是什么.
它分为三个服务:实际的Flask应用, an Nginx reverse proxy, and a Redis keystore. 对于每个请求,应用程序增加 num_requests
key in Redis, 不管你要访问哪个Flask实例, 您将看到反映的请求的正确数量.
所有的源代码都可以在 GitHub 如果你想“检查”正在发生什么.
在学习本教程的过程中,您可以随意使用自己的服务器,但我强烈建议您使用 play-with-docker.com if you want to just jump in. 这是一个由几个Docker开发人员运行的网站,可以让你启动几个预先安装了Docker的网络节点. 它们将在四小时后关闭,但这对于这个例子来说已经足够了!
Alright, here we go! 继续在PWD (play-with-docker)中创建三个实例,或者在您最喜欢的VPS(虚拟专用服务器)服务中启动三个服务器,并在所有服务器上安装Docker引擎. 请记住,您总是可以创建一个映像,并在将来添加节点时重用它. 在软件方面,管理节点和工作节点之间没有区别, 因此,您不需要维护两个不同的图像.
Still spinning up? Don’t worry, I’ll wait. 好了,现在我们要创建第一个经理和领导节点. 在你的第一个实例中,初始化一个swarm:
docker swarm init --advertise-addr
Replace
使用节点的IP地址. On PWD, 界面顶部显示IP地址, 如果你用的是自己的VPS, 只要可以从网络中的其他节点访问,就可以随意使用服务器的私有IP地址.
You now have a swarm! 这是一个非常无聊的群体,因为它只有一个节点. 让我们继续添加其他节点. 你跑步的时候会注意到的 init
,它会显示一条长消息,解释如何使用连接令牌. 我们不打算使用这个节点,因为它会使其他节点成为工作节点, 我们希望他们成为管理者. 让我们通过在第一个节点上运行以下命令来获取管理器的连接令牌:
Docker群连接令牌管理器
复制结果命令,并在第二个和第三个节点上运行它. Behold, a three node swarm! 让我们验证一下所有的节点是否真的存在. The docker node ls
命令将列出集群中的所有节点. 你应该看到这样的内容:
$ docker node ls
Id hostname status可用性管理器状态
su1bgh1uxqsikf1129tjhg5r8 * node1 Ready Active Leader
t1tgnq38wb0cehsry2pdku10h node3 Ready Active Reachable
wxie5wf65akdug7sfr9uulleui node2 Ready Active Reachable
注意我们的第一个节点在ID旁边有一个星号. 这只是告诉我们,这是我们当前连接的节点. 我们还可以看到该节点当前是Leader, 如果有什么事情发生,其他节点是可以到达的.
花点时间欣赏一下这是多么容易,让我们部署第一个应用程序!
This just in, 业务开发团队向客户承诺,他们的新应用程序将在一小时内部署就绪! Typical, I know. 但不用担心,我们不需要那么多时间,因为它是用Docker构建的! 开发商很好心地把他们的房子借给了我们 docker-compose
file:
version: '3.1'
services:
web:
图片:lsapan / docker-swarm-demo-web
command: gunicorn --bind 0.0.0.0:5000 wsgi:app
deploy:
replicas: 2
secrets:
- db_password
nginx:
图片:lsapan / docker-swarm-demo-nginx
ports:
- 8000:80
deploy:
mode: global
redis:
image: redis
deploy:
replicas: 1
secrets:
db_password:
external: true
我们稍后会详细介绍它,但现在还没有时间. Let’s get it deployed! 继续,在第一个节点上创建一个文件 docker-compose.yml
并用上面的配置填充它.You can do that easily with echo "
.
通常,我们可以部署它,但是我们的配置提到我们使用一个名为 db_password
,所以让我们快速创建这个秘密:
Echo "supersecretpassword" | docker secret create db_password -
Great! 现在我们需要做的就是告诉Docker使用我们的配置:
Docker栈部署-c Docker -compose.yml demo
当你运行这个命令时,你会看到Docker创建了我们定义的三个服务: web
, nginx
, and redis
. 但是,因为我们命名了堆栈演示,所以我们的服务实际上也被命名了 demo_web
, demo_nginx
, and demo_redis
. 我们可以通过运行 docker service ls
命令,它应该显示如下:
$ docker service ls
Id name mode replicas image ports
Cih6u1t88vx7 demo_web replicated 2/2 lsapan/docker-swarm-demo-web:latest
u0p1gd6tykvu demo_nginx global 3/3 lsapan/docker-swarm-demo-nginx:latest *:8000->80/
tcp
Wa1gz80ker2g demo_redis复制1/1 redis:最新
Voila! Docker已经将我们的镜像下载到适当的节点上,并为我们的服务创建了容器. 如果您的副本尚未达到满容量,请稍候并再次检查. Docker可能仍在下载镜像.
但是不要相信我的话(或者Docker的话). 让我们尝试连接到我们的应用. 我们的服务配置告诉Docker在端口8000上公开NGINX. 如果你是残疾人,在页面顶部应该有一个蓝色的链接,上面写着“8000”. PWD实际上自动检测到我们在该端口上运行了一个服务! 点击它,它会将您路由到端口8000上的选定节点. 如果您滚动了自己的服务器,只需导航到端口8000上的服务器的ip之一.
你会看到一个漂亮的屏幕,上面有一些基本信息:
记下是哪个容器提供了您的请求,然后刷新页面. Odds are it changed. But why? Well, 我们告诉Docker为我们的Flask应用创建两个副本, 它将请求分发给这两个实例. 你只是第二次碰巧撞到了另一个集装箱. 你还会注意到请求的数量增加了,因为两个Flask容器都在与我们指定的单个Redis实例通信.
请随意尝试从任何节点访问端口8000. 你仍然会被正确地路由到应用程序.
在这一点上,一切都工作了,希望您发现这个过程是无痛的! 让我们仔细看看 docker-compose.yml
文件,看看我们到底告诉了Docker什么. 在高层次上,我们已经定义了三个服务: web
, nginx
, and redis
. 就像普通的撰写文件一样, 我们为Docker提供了每个服务使用的映像, 以及要运行的命令. In the case of nginx
,我们还指定主机上的端口8000应该映射到容器中的端口80. 到目前为止,所有这些都是标准的撰写语法.
这里的新内容是部署和秘密密钥吗. These keys are ignored by docker-compose
,因此它们不会影响您的开发环境,而是由 docker stack
. 让我们看一下web服务. 很简单,我们告诉Docker我们想要运行两个Flask应用的副本. 我们还让Docker知道web服务需要 db_password
secret. 这确保了容器将有一个文件名 /run/secrets/db_password
包含secret的值.
移动到Nginx,我们可以看到部署模式被设置为 global
. 默认值(在web中隐式使用)是 replicated
,这意味着我们会指定需要多少个副本. When we specify global
,它告诉Docker集群中的每个节点应该只运行一个服务实例. Run docker service ls
again, you’ll notice that nginx
有三个副本,每个节点一个.
最后,我们已经指示Docker在集群的某个地方运行一个Redis实例. It doesn’t matter where, 因为我们的web容器在请求Redis主机时会自动路由到它.
祝贺你将你的第一个应用部署到Docker Swarm上! 让我们花点时间回顾一下您将使用的几个常用命令.
需要检查一下你的服务? Try docker service ls
and docker service ps
. 前者向您展示每个服务的高级概述, 后者为您提供有关为指定服务运行的每个容器的信息. 当您想要查看哪些节点正在运行服务时,该选项特别有用.
当你准备更新应用程序时怎么办? Well, the cool thing about docker stack deploy
它是否也会对现有堆栈应用更新. 假设您已经将一个新的Docker映像推送到存储库. 实际上,您只需运行与第一次使用的相同的deploy命令,您的swarm将下载并部署新映像.
当然,您可能并不总是希望更新堆栈中的每个服务. 我们也可以在服务级别执行更新. 假设我最近为我的web服务更新了图像. 我可以发出这个命令来更新我所有的web容器:
docker service update \
——image lsapan/docker-swarm-demo-web:latest \
demo_web
该命令的另一个好处是,如果您在原始配置中指定了滚动更新,它将应用滚动更新. And even if you didn’t, 你可以向update传递标志,指示它进行滚动更新,如下所示:
docker service update \
——image lsapan/docker-swarm-demo-web:latest \
——update-parallelism 1——update-delay 30s
demo_web
这将一次更新一个容器,更新之间等待30秒.
拥有两个web容器很好,但你知道哪个更好? Having ten! 在集群中向上和向下扩展服务非常简单,如下所示:
Docker服务规模demo_web=10
执行该命令,查看输出结果 docker service ps demo_web
. 您将看到我们现在有10个容器,其中8个刚刚启动. If you’re interested, 您还可以回到web应用程序并刷新页面几次,以查看您现在获得的容器id多于原来的两个.
你的堆栈和服务被部署和扩展,太棒了! 但现在你想让他们下线. 这可以用各自的 rm
command. 要删除我们的demo堆栈,运行命令:
docker stack rm demo
或者,如果您只想删除单个服务,只需使用:
docker service rm demo_web
Remember when we ran docker node ls
来检查集群中的节点? 它提供了关于每个节点的一系列信息,包括它的 Availability. By default, nodes are Active这意味着它们是运行容器的合适对象. 但是,有时可能需要使节点暂时脱机以执行维护. Sure, 你只要把它关掉蜂群就会恢复, 但给莫比(码头鲸)一点提示还是不错的.
这就是引流节点的作用. When you mark a node as Drain, Docker Swarm会将运行在其上的任何容器委托给其他节点, 并且它不会启动节点上的任何容器,直到您将其可用性更改回 Active.
Let’s say we want to drain node1
. We can run:
Docker节点更新——可用性耗尽node1
Easy! 当你准备好让它重新工作时:
Docker节点更新——可用性active node1
As we’ve seen, Docker与Swarm模式相结合,使我们能够比以往更高效、更可靠地部署应用程序. 值得一提的是,Docker Swarm绝不是唯一的容器编排引擎. 事实上,它是最年轻的一个. Kubernetes 已经存在了很长时间,并且肯定在更多的生产应用程序中使用. That said, Swarm是Docker正式开发的一个, 他们每天都在努力增加更多的功能. 无论您选择使用哪种,都要保持容器!
Docker Swarm是一个容器编排引擎. As such, 它以高可用性的方式处理集群中跨节点的容器调度和平衡.
而不是手动管理您的基础结构, Docker Swarm允许你声明式地指定你的应用应该如何部署和扩展. 一旦部署,它将确保不断满足您的规范.
Not much! 只需在您的主机上安装Docker,并让其中一台主机初始化群集. 一旦完成,其他主机可以迅速加入.
由于Docker堆栈使用与Docker -compose相同的语法, 您的开发人员已经完成了编写堆栈配置的大部分工作! 这意味着上线时意外会更少,这总是一件好事.
Located in Scarborough, United States
Member since August 31, 2015
世界级的文章,每周发一次.
世界级的文章,每周发一次.
Join the Toptal® community.