首页 理论教育 使用Terraform在DigitalOcean中创建

使用Terraform在DigitalOcean中创建

时间:2023-11-06 理论教育 版权反馈
【摘要】:Terraform并不强制我们使用任何特定的文件结构。如果一开始使用这个Terraform配置来创建一个真正的集群,那么可以将swarm_instance_size设置为更大的值。第一种资源digitalocean_ssh_key允许我们管理droplet的SSH密钥。我们使用的第二种资源是digitalocean_floating_ip。这表示执行Terraform apply时将显示的值,并且可以使用output命令轻松查询。输出是告诉Terraform哪些数据是相关的一种方法。里面的第一个是名为swarm_manager的digitalocean_droplet类型的资源。当第一次运行Terraform时,值应该是1,因为我们希望从一个manager开始来初始化集群。name、region和private_networking应该是自我解释的。

使用Terraform在DigitalOcean中创建

Terraform是everyone-uses-a-different-environment-variable-for-the-token俱乐部的第三个成员。它希望将令牌存储在环境变量DIGITALOCEAN_TOKEN中:

请使用实际的令牌替换[...]。

Terraform并不强制我们使用任何特定的文件结构。我们可以在一个文件中定义所有的东西。但是,这并不意味着应该如此。Terraform的配置会变大,将其在逻辑上分段并置于单独的文件中通常是一个好主意。在我们的例子中,将有三个tf文件。terraform/do/variables.tf(https://github.com/vfarcic/cloud-provisioning/ blob/master/terraform/do/variables.tf)包含了所有变量。如果需要修改任何参数,那么应该知道在哪里可以找到它。terraform/do/common.tf(https://github.com/ vfarcic/cloud-provisioning/blob/master/terraform/do/common.tf)文件包含了在其他情况下可能重用的元素的定义。最后,terraform/do/swarm.tf(https://github.com/ vfarcic/cloud-provisioning/blob/master/terraform/do/swarm.tf)文件具有Swarm特有的资源。下面将分别研究每一个Terraform配置文件。

将节点加入集群中需要swarm_manager_token和swarm_worker_token。swarm_ snapshot_id将保存我们使用Packer创建的快照ID。swarm_manager_ip变量是我们要为节点加入集群提供的一个manager的IP。swarm_managers和swarm_ workers定义了我们想要的各自的节点数量。swarm_region定义了当swarm_instance_size设置为1 GB时集群所运行的区域。如果一开始使用这个Terraform配置来创建一个真正的集群,那么可以将swarm_instance_size设置为更大的值。最后,swarm_init变量允许我们指定这是否是第一次运行并使用节点来初始化集群。很快就会看到它的使用情况。

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

每种资源都定义为类型(例如,digitalocean_ssh_key)和名称(例如,docker)。该类型决定应该创建哪种资源,并且必须是当前支持的资源之一。

第一种资源digitalocean_ssh_key允许我们管理droplet的SSH密钥。在droplet的配置中,可以通过其ID或指纹来引用该资源创建的密钥。我们将其设置为即将创建的devops21-do.pub文件的值。

我们使用的第二种资源是digitalocean_floating_ip。它表示一个可被公开访问的静态IP地址,可以映射到我们的一个droplet上。我们定义了其中的三个,可用于DNS配置。这样,当向你的域发出请求时,DNS会将它重定向到一个浮动IP地址上。如果其中一个droplet停机,那么DNS应该使用其他的droplet。这样,你就有时间将浮动IP地址从失效的droplet更改为新的droplet。

更多信息请参考DIGITALOCEAN_SSH_KEY页面(https://www.terraform.io/docs/ providers/do/r/ssh_key.html)和DIGITALOCEAN_FLOATING_IP页面(https://www.terraform.io/docs/providers/do/r/floating_ip.html)。

除资源外,还定义了一些输出。这表示执行Terraform apply时将显示的值,并且可以使用output命令轻松查询。

当构建可能比较复杂的基础设施时,Terraform可为所有资源存储成百上千个属性值。但是,作为用户,可能只对一些重要的值感兴趣,例如manager的IP地址。输出是告诉Terraform哪些数据是相关的一种方法。

在我们的例子中,输出是浮动IP地址。

更多信息请参考输出配置页面(https://www.terraform.io/docs/configuration/ outputs.html)。

现在来看一个真实的例子。terraform/do/swarm.tf文件(https://github.com/ vfarcic/cloud-provisioning/blob/master/terraform/do/swarm.tf)包含我们将创建的所有实例的定义。

因为这个文件的内容比其他文件的要多一些,所以将分别检查每个资源。

里面的第一个是名为swarm_manager的digitalocean_droplet类型的资源。它的目的是创建Swarm manager节点:

资源包含引用我们使用Packer创建的快照的镜像。实际值是我们在运行时定义的变量。size可以指定我们想要创建的实例的大小。默认值取自变量swarm_ instance_size。默认情况下,它设置为1 GB。就像任何其他变量一样,它可以在运行时被覆盖。

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

name、region和private_networking应该是自我解释的。sshkeys类型是一个数组,此时它只包含一个元素,就是在common.tf文件中定义的digitalocean_ssh_key资源的ID。

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

最后定义了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.ipv4_address_private来获得的。我们引用自己并获得ipv4_address_private。还可以从资源中获得许多其他属性。更多信息请参考DIGITALOCEAN_DROPLET页面(https://www.terraform.io/docs/providers/do/r/droplet.html)。

让我们看一下名为swarm-worker的digitalocean_droplet资源:

swarm-worker资源几乎与swarm-manager相同。唯一的区别是count字段使用了swarm_workers变量和provisioner。由于worker无法初始化集群,因此不需要if语句,所以我们要执行的唯一命令是docker swarm join。Terraform使用一个命名约定,它允许我们通过添加TF_VAR前缀将值指定为环境变量。例如,可以通过设置环境变量TF_VAR_swarm_snapshot_id来指定变量swarm_snapshot_id的值。另一种方法是使用-var参数。我更喜欢环境变量,因为允许我指定它们一次,而不用将-var添加到每个命令中。规范terraform/do/swarm.tf(https://github.com/vfarcic/cloud-provisioning/blob/master/terraform/do/swarm.tf)的最后一部分是输出。

定义的输出如下:

它们是manager公有的和私有的IP地址。由于知道worker的IP地址的理由很少(如果有的话),所以没有将它们定义为输出。

由于我们将使用Packer创建快照,所以需要从packer-ubuntu-docker.log中获取ID。让我们再看一下该文件:

重要的输出行如下:

下面的命令解析输出并提取ID:

让我们再次检查命令是否工作正常:

输出如下:

我们得到了快照的ID。在开始创建资源之前,需要创建SSH密钥devops21-do,并在Terraform配置中引用它。

现在将使用ssh-keygen创建SSH密钥:

当被要求输入保存密钥的文件时,请使用devops21-do回答。其余的问题可以使用你认为合适的任何方式来回答。我不会为这些问题指定答案。

输出应该与以下的代码类似:

现在devops21-do密钥已经创建好了,可以开始使用Terraform了。在创建集群及其周围的基础设施之前,应该要求Terraform向我们展示执行计划。

给Terraform v0.8+用户的说明

通常,我们不需要指定targets来查看整个执行计划。然而,Terraform v0.8引入了一个缺陷,如果一个资源引用了另一个尚未创建的资源,那么它有时会阻止我们的输出计划。这种情况下,digitalocean_floating_ip.docker_2和digitalocean_floating_ip.docker_3就是这样的资源。以下命令中的targets是临时解决方案,直到问题解决为止:

结果包含大量的资源及其属性。由于输出太大,无法打印,所以限定只输出资源类型和名字:

因为这是第一次执行,因此,如果执行terraform apply,则所有的资源都将被创建。我们会得到五个droplet、三个manager和两个worker。伴随着三个浮动IP地址和一个SSH密钥。

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

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

输出如下:

这些内容本身并不是十分有用。

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

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

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

输出应与图13-3所示相同。

图13-3 Graphviz由terraform graph命令的输出生成的图像

计划的可视化可以让我们看到不同资源之间的依赖关系。在本例中,所有资源都将使用digitalocean provider。这两种实例类型都将依赖SSH key docker,浮动IP地址将与作为manager的droplet绑定。

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

作为一个例子,当将它限制在一个Swarm manager节点时,让我们看看Terraform将生成的计划,这样就可以初始化集群了:

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

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

尽管只指定了swarm-manager资源的计划,但Terraform注意到它依赖于SSH key docker,所以将其包含在执行计划中。

现在将从简单的开始,只创建一个manager实例来初始化集群。正如我们从计划中看到的,它依赖于SSH密钥,所以Terraform也会创建它:

输出代码太长,不能在书中全部展示出来。如果从终端查看它,你会注意到SSH密钥是首先创建的,因为swarm-manager依赖它。请注意,我们没有明确指定依赖关系。但是,由于资源在ssh_key字段指定了它,所以Terraform知道它是依赖项。

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

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

输出定义在terraform/do/swarm.tf文件的最后(https://github.com/vfarcic/ cloud-provisioning/blob/master/terraform/do/swarm.tf)。请注意,并没有列出所有的输出,而是只列出了所创建的资源的输出。

可以使用新创建的droplet的公开IP地址并登录它。

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

获取第一个,也是当前唯一的manager公开的IP地址的命令如下所示:

输出如下:

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

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

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

还需要设置环境变量swarm_manager_ip:

可以使用terraform/do/swarm.tf文件中的digitalocean_droplet.swarm-manager.0.private_ip(https://github.com/vfarcic/cloud-provisioning/blob/ master/terraform/ do/swarm.tf)。将其定义为环境变量是一个好主意。这样,如果第一个manager发生了故障,那么可以很容易将其更改为swarm_manager_2_private_ip,而无需修改.tf文件。

现在,让我们看看创建其余Swarm节点的计划:

相关的输出如下:

由以上代码可以看到,该计划将创建四个新的资源。因为已经有一个manager在运行,并指定了所需的数量是三,所以将创建两个额外的manager和两个worker。

让我们应用执行计划:

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

所有四个资源都已经创建,我们得到了manager公开的和私有的IP地址的输出。

让我们登录其中一个manager并确认集群确实已经工作:

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

所有节点都在了,集群似乎正在工作。

为了确信一切都能正常工作,下面将部署一些服务。这些服务将与我们在本书中创建的服务相同,因此可节省一些时间并部署vfarcic/docker-flow-proxy/ docker-compose-stack.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)栈:

从代码库中下载栈,并执行stack deploy命令。

现在要做的就是等待片刻,执行service ls命令,并确认所有副本都在运行:

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

最后,让我们通过代理向go-demo服务发送一个请求。如果它返回正确的响应,就表明一切都工作正常:

输出如下:

它工作了!

我们完成了吗?差不多是的。作为最后的检查,让我们验证一下代理是否可以从服务器外部访问。可以通过退出服务器并从我们的笔记本电脑发送请求来确认这一点:

输出如下:

还缺少浮动的IP地址。虽然对于这个演示不是必需的,但如果这是一个生产集群,我们就得创建它们,并使用它们来配置DNS。

这一次,可以创建计划而不用指定targets:

相关的输出如下:

如你所见,Terraform检测到除了浮动的IP地址以外,所有的资源都已经创建,因此它生成的计划只执行三个资源的创建。

让我们应用该计划:

输出如下:

创建浮动的IP地址后,可以看到其IP的输出命令。

剩下的就是确认浮动IP地址确实是正确地创建和配置了。可以向其中之一发送请求来确认这一点:

正如预期的那样,输出为状态200 OK:

让我们看看,如果模拟实例发生故障,会发生什么。

可以使用DigitalOcean API删除一个实例,也可以使用Terraform来删除一个实例。但是,使用API删除节点可以更好地模拟节点的意外故障。

要删除一个实例,需要找到它的ID。可以使用terraform show命令来完成这个任务。(www.xing528.com)

假设要删除第二个worker,查找其所有信息的命令,如下:

输出如下:

在其他数据中,我们得到了ID。在我的例子中,它是33909722。

运行下面的命令之前,请将ID更改为从terraform state show命令得到的ID:

相关的输出如下:

对于DELETE请求,DigitalOcean不提供任何响应内容,因此状态204表示操作成功。

这将需要几分钟,直到droplet完全删除。

让我们再次运行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。

现在完成了对DigitalOcean中的Terraform的简要介绍。

执行的流程可以通过图13-4来描述。

图13-4 Terraform过程的流程图

在DigitalOcean中创建和管理Swarm群集有不同的方法,在比较它们之前,先销毁我们所做的事情:

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

集群已经消失,就像它从来不存在一样,这样可使我们免于不必要的开销。下面看看如何删除快照。

在删除创建的快照之前,需要找到它的ID。

将返回所有快照列表的请求如下:

响应的输出如下:

将使用jq得到快照的ID:

可以发送一个HTTP GET请求来获取所有的快照,并使用jq来得到ID。结果存储在环境变量SNAPSHOT_ID中。

现在,可以发送一个DELETE请求来删除快照:

相关的输出响应如下:

快照已经被删除。在DigitalOcean账户上没有资源在运行,除了运行本章中的练习的花费以外,不会收取你的任何费用。

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

我要反馈