首页 理论教育 使用Terraform在AWS中创建Swarm集群

使用Terraform在AWS中创建Swarm集群

时间:2023-11-06 理论教育 版权反馈
【摘要】:Terraform并不强制使用任何特定的文件结构。所有变量都在terraform/aws/variables.tf文件中。文件terraform/aws/common.tf包含的元素的定义可能在其他场合被重用。最后,文件terraform/aws/swarm.tf含有Swarm特定的资源。文件terraform/aws/variables.tf的内容如下:在集群中添加节点会用到swarm_manager_token和swarm_worker_token。文件terraform/aws/common.tf的内容如下:每个资源都是用类型和名称来定义的。其余端口用于Swarm内部通信。更多信息请参考AWS_SECURITY_GROUP。在我们的例子中,仅使用定义在terraform/aws/common.tf中的docker组。swarm init命令就是其中之一。

使用Terraform在AWS中创建Swarm集群

如果你阅读本节时打开了一个新的终端会话,那么会先重新定义与Packer一起使用的环境变量

请将[…]替换为实际的值。

Terraform并不强制使用任何特定的文件结构。我们可以在文件中定义所有的东西。但是,这并不意味着应该如此。Terraform的配置会变大,将逻辑相关部分分离并放入单独的文件中通常是一个好主意。在我们的例子中,将有三个tf文件。所有变量都在terraform/aws/variables.tf文件中(https://github.com/vfarcic/ cloud-provisioning/blob/master/terraform/aws/variables.tf)。

如果要改变参数,则要知道在哪里可以找到它。文件terraform/aws/common.tf(https://github.com/vfarcic/cloud-provisioning/blob/master/terraform/aws/common.tf)包含的元素的定义可能在其他场合被重用。最后,文件terraform/aws/swarm.tf(https://github.com/vfarcic/cloud-provisioning/blob/master/terraform/aws/swarm.tf)含有Swarm特定的资源。

现在将浏览Terraform的每一个配置文件。

文件terraform/aws/variables.tf(https://github.com/vfarcic/cloud-provisioning/ blob/master/terraform/aws/variables.tf)的内容如下:

在集群中添加节点会用到swarm_manager_token和swarm_worker_token。swarm_ami_id将保存我们使用Packer创建的镜像的ID。swarm_manager_ip变量是我们需要为节点加入集群提供的manager的IP。swarm_managers和swarm_workers定义了每种类型有多少个节点。swarm_instance_type是我们要创建的实例的类型。默认值是最小的和最便宜的(通常是免费的)实例。如果你开始使用这个Terraform配置来创建一个“真正的”集群,那么稍后请将它改为更有效的类型。最后,swarm_ init变量允许我们指定这是否是第一次运行,并使用节点来初始化集群。很快就会看到它的使用情况。

文件terraform/aws/common.tf(https://github.com/vfarcic/cloud-provisioning/ blob/master/terraform/aws/common.tf)的内容如下:

每个资源都是用类型(例如aws_security_group)和名称(示例docker)来定义的。类型决定了应创建哪些资源,并且必须是当前支持的资源。

第一个资源aws_security_group包含应该打开的所有入口端口。SSH需要端口22。端口80和端口443用于HTTP和HTTPS对代理的访问。其余端口用于Swarm内部通信。TCP端口2377用于集群管理通信,TCP和UDP端口7946用于节点之间的通信,TCP和UDP端口4789用于覆盖网络(overlay network)流量。使用Docker Machine建立集群时,可以打开相同的端口。请注意,除了端口22、80和443之外,所有端口都是内部使用的。这意味着它们只对属于同组内的其他服务器可用。任何外部访问都将被阻止。

aws_security_group中的最后一个条目是egress,允许集群与外部世界进行不受任何限制的通信。

更多信息请参考AWS_SECURITY_GROUP(https://www.terraform.io/docs/providers/ aws/d/security_group.html)。

现在实打实的部分来了。文件terraform/aws/swarm.tf(https://github.com/ vfarcic/cloud-provisioning/blob/master/terraform/aws/swarm.tf)包含我们创建的所有实例的定义。因为这个文件的内容比其他内容多,所以将分别查看每个资源。

第一个资源类型是aws_instance,名字是swarm-manager。它的目的是创建一个Swarm manager节点:

资源包含引用了我们使用Packer创建的镜像的ami。实际值是在运行时定义的变量。instance_type指定了我们想要创建的实例的类型。默认值是从变量swarm_ instance_type中得到的。默认情况下,它被设置为t2.micro。就像任何其他变量一样,它可以在运行时被覆盖。

count字段定义了我们想要创建多少个manager。当第一次运行terraform时,值应该是1,因为我们希望从一个manager开始来初始化集群。之后,值应该是变量中定义的值。很快就会看到这两者结合的用例。

这些tag仅用于信息目的。

vpc_security_group_ids字段包含我们希望与服务器绑定的所有组的列表。在我们的例子中,仅使用定义在terraform/aws/common.tf中的docker组。

key_name是保存在AWS中的密钥的名字。我们在本章开始的时候生成了devops21密钥。请再确认一遍,密钥没有被删除。没有密钥,就无法登录到机器中。

connection字段定义了SSH连接的细节。用户是ubuntu。我们将使用devops21.pem密钥来代替密码。

最后定义了provisioner。我们的想法是在创建镜像的过程中实现尽可能多的配置。这样,实例的创建速度会快很多,因为唯一的操作是用镜像创建虚拟机。但是,总有一部分配置在创建镜像时无法完成。swarm init命令就是其中之一。在得到服务器的IP之前,我们无法初始化第一个Swarm节点。换句话说,服务器需要在swarm init命令执行之前运行起来(因此得有一个IP)。

由于第一个节点必须初始化集群,而后任何其他节点都应该加入集群,所以我们使用if语句来区分这两种情况。如果变量swarm_init为true,则执行docker swarm init命令。如果变量swarm_init为false,则执行docker swarm join命令。这种情况下,我们使用另一个变量swarm_manager_ip来告知节点要使用哪个manager加入集群。

请注意,IP是使用特殊的语法self.private_ip获得的。可以引用自己来得到private_ip,还可以从资源中得到很多其他属性。

更多信息请参考AWS_INSTANCE(https://www.terraform.io/docs/providers/aws/ r/instance.html)。

swarm-worker资源几乎与swarm-manager资源一样。唯一的区别是count字段使用了swarm_workers变量以及provisioner的不同。由于worker无法初始化集群,不需要if语句,所以要执行的唯一命令是docker swarm join。

Terraform使用的命名规则允许我们通过添加TF_VAR前缀将值指定为环境变量。例如,可以通过设置环境变量TF_VAR_swarm_ami_id来指定变量swarm_ami_id的值。另一种方法是使用-var参数。我更喜欢使用环境变量,因为只需要指定一次,而不用向每个命令添加-var。

ter raform/aws/swarm.tf规范(https://github.com/vfarcic/cloud-provisioning/ blob/master/terraform/aws/swarm.tf)的最后一部分是输出。

在构建可能会比较复杂的基础设施时,Terraform存储了所有资源的成百上千个属性值。但是,作为用户,我们可能只对一些重要的值感兴趣,例如manager的IP。输出是告诉Terraform相关数据的一种方法。当调用apply时输出这些数据,并且可以使用terraform output命令查询这些数据。

定义的输出如下:

它们是manager公开的和私有的IP地址。由于需要知道worker的IP的原因很少(如果有),所以没有将它们定义为输出。更多信息请参考Output Configuration(https://www.terraform.io/docs/configuration/outputs.html)。因为我们将使用由Packer创建的AMI,所以需要从packer-ubuntu-docker.log中得到ID。以下命令解析输出并得到了ID:

在创建集群及周边的基础设施之前,应当让Terraform展示执行计划。

执行计划是资源及其属性的扩展列表。因为输出太多而无法打印,所以此处仅输出资源的类型和名字:

由于这是第一次执行,因此,如果要执行Terraform apply,则将创建所有的资源。我们将得到五个EC2实例、三个manager和两个worker。随之而来的还有一个安全组。

如果看到完整的输出,就会注意到有些属性的值被设置为<computed>。这意味着Terraform在创建资源之前无法知道实际的值是什么。一个很好的例子是IP地址。它们在创建EC2实例之前是不存在的。

还可以使用graph命令输出计划:

输出如下:

这本身并不是十分有用。

graph命令用于生成配置或执行计划的可视化表示。输出为DOT格式,Graphviz可以用它来生成图形。

请打开Graphviz下载页面(http://www.graphviz.org/Download..php),下载与你的操作系统兼容的发行版。

现在可以将graph命令和dot结合起来了:

输出将与图12-11一致。

图12-11 根据terraform graph命令的输出来由Graphviz生成图像

计划的可视化允许我们查看不同资源之间的依赖关系。在本例中,所有资源都将使用aws provider。这两种实例类型都依赖于安全组docker。

在定义依赖关系时,不需要显式指定所需的所有资源。

例如,当将Terraform限定为一个Swarm manager节点时,让我们看一看Terraform制订的计划,这样就可以初始化集群了:

将使用运行时变量swarm_init和swarm_managers告知Terraform,我们想使用一个manager来初始化集群。plan命令考虑了这些变量并输出执行计划。

仅限于资源类型和名称的输出如下:

尽管指定了只需要swarm-manager资源的计划,但Terraform注意到它依赖于安全组docker,并将其包含在执行计划中。

在开始创建AWS资源之前,唯一缺少的是将SSH密钥devops21.pem复制到当前的目录中。配置期望如此:

在复制之前,请使用正确的路径修改KEY_PATH的值。

现在将从小规模开始,只创建一个用于初始化集群的manager实例。正如从计划中看到的,它依赖于安全组,所以Terraform也会创建它。

输出很多而无法在书中呈现。如果你从终端查看它,就会注意到首先创建了安全组,因为swarm-manager依赖于它。请注意,我们没有明确指定依赖关系。但是,由于资源在vpc_security_group_id字段中指定了依赖项,所以Terraform理解它是依赖项。

一旦创建了swarm-manager实例,Terraform就一直等待直到SSH访问可用。在成功连接到新实例之后,它执行provisioning命令初始化集群。

输出的最后部分如下:

输出定义在文件terraform/aws/swarm.tf(https://github.com/vfarcic/cloud-provisioning/blob/master/terraform/aws/swarm.tf)的最后。请注意不是所有的输出都列出来了,只有那些被创建的资源才列出来了。

可以使用新创建的EC2实例公开的IP地址和SSH登录它。

你可能倾向于复制IP。但实际没有必要这样做。Terraform有一个命令,可以用来检索我们定义为输出的任何信息。

查询第一个(当前只有一个manager)公开的IP的命令如下所示:

输出如下:

可以利用输出命令来构造SSH命令。例如,下面的命令将登录机器并查询Swarm节点的列表:

输出如下(为简洁起见,删除了ID):

从现在开始,我们将不再局限于使用单个manager节点来初始化集群。可以创建所有其余的节点。然而,在这样做之前,我们需要发现manager和worker的令牌。出于安全考虑,最好不要将它们存储在任何地方,所以我们将设置环境变量:

还需要设置环境变量swarm_manager_ip:

尽管可以在文件terraform/aws/swarm.tf(https://github.com/vfarcic/cloud-provisioning/blob/master/terraform/aws/swarm.tf)中使用aws_instance.swarm-manager.0.private_ip,那么把它定义为环境变量也是一个不错的主意。这么做,如果第一个manager发生故障,无需修改tf文件也可以使用swarm_manager_2_private_ip。

现在看看生成所有缺少的资源的计划:

不需要指定目标,因为这一次,我们希望创建所有缺少的资源。

输出的最后一行如下:

可以看到该计划将生成四个新的资源。因为已经有一个manager正在运行,并且指定期望有三个manager、两个额外的manager以及两个worker将被创建。

让我们执行该计划:

输出的最后一行如下:

所有四个资源都被创建,并得到了manager的公开和私有IP。

下面登录其中一个manager并确认集群确实在工作:

node ls命令的输出如下(为简洁起见,移除了ID):

所有的节点都在,集群看起来工作正常。

要完全相信一切都如预期般工作,那么要部署几个服务。这些是我们创建的贯穿全书的一些服务,所以可以节省一些时间,要部署的是vfarcic/docker-flow-proxy/docker-composestack.yml栈(https://github.com/vfarcic/docker-flow-proxy/blob/ master/docker-compose-stack.yml)和vfarcic/go-demo/docker-compose-stack.yml栈(https://github.com/vfarcic/go-demo/blob/master/docker-compose-stack.yml)。

从代码库下载脚本,给予可执行权限并执行脚本。最后,我们列出了所有的服务。(www.xing528.com)

稍等片刻,service ls命令的输出应该如下(为简洁起见,移除了ID):

最后,通过proxy给go-demo服务发送了一个请求。如果它返回了正确的响应,那么可以确定一切工作正常:

输出如下:

工作了!

完成了吗?很可能是。作为最后一次检查,让我们验证一下proxy是否可以在安全组以外访问。可以通过退出服务器并从笔记本电脑发送请求来确认:

输出如下:

让我们模拟一个有故障的实例看看会发生什么。

可以使用AWS CLI删除一个实例,也可以使用Terraform删除一个实例。但是,使用AWS CLI删除一个实例更接近于模拟节点的意外故障。要删除一个实例,需要找到它的ID。因此,可以用terraform show命令。假设我们想要删除第二个worker,那么查找其所有信息的命令如下:

输出如下:

在其他数据中,我们获得了ID。在我的例子中,它是i-6a3a1964。执行下面的命令之前,请将ID更改为从terraform state show命令获得的:

输出如下:

AWS把实例的状态从running改为shutting-down。

让我们再次运行terraform plan命令:

输出的最后一行代码如下:

Terraform推断需要添加一个资源swarm-worker.1以协调它在本地存储的状态与集群实际状态之间的差异。

要将集群恢复到期望的状态,必须运行terraform apply:

输出的最后一行代码如下:

可以看到已经添加了一个资源。已经重新创建了终止的worker,并且该集群继续以全部容量在运行。

集群的状态保存在terraform.tfstate文件中。如果你不是总在相同的计算机上使用它,那么可以把该文件与其他配置文件一起保存在代码库中。另外的方法是使用Remote State(https://www.terraform.io/docs/state/remote/index.html),例如保存在Consul中。

改变集群期望的状态也很简单。我们要做的就是添加一个资源并重新运行terraform apply。

前面已经简要介绍了AWS上的Terraform。

执行过程的流程如图12-12所示。

图12-12 Terraform过程的流程

比较在AWS中创建和管理Swarm群集的不同方法之前,让我们先销毁已有的东西:

集群不再存在以节省不必要的花费。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈