如何在 DigitalOcean 上使用 Packer 和 Terraform 构建 Hashicorp Vault 服务器

作者选择了自由和开放源码基金作为为国家写作计划的一部分接受捐赠。

简介

HashiCorp公司的Vault是一种开源工具,用于在动态云环境中安全存储秘密和敏感数据。它提供强大的数据加密、使用自定义策略的基于身份的访问、秘密租赁和撤销,以及随时记录的详细审计日志。Vault 还提供 HTTP API,是在 Kubernetes 等分散的面向服务部署中存储凭证的理想选择。

同样由 HashiCorp 开发的 PackerTerraform 可共同用于创建和部署 Vault 的映像。在这一工作流程中,开发人员可以使用 Packer 通过一个配置文件为不同平台编写不可变的映像,该文件指定了映像应包含的内容。然后,Terraform 将根据需要部署所创建映像的定制实例。

在本教程中,您将使用 Packer 创建已安装 Vault 的系统的不可变快照,并使用 Terraform 协调其部署。最终,您将拥有一个部署 Vault 的自动化系统,从而可以专注于使用 Vault 本身,而不是底层安装和配置过程。

先决条件

步骤 1 - 创建包装器模板

在此步骤中,您将编写一个称为 template 的 Packer 配置文件,该文件将指示 Packer 如何构建一个预装了 Vault 的映像。您将以JSON格式编写配置,这是一种常用的人类可读配置文件格式。

在本教程中,所有文件都存放在"~/vault-orchestration "目录下。运行以下命令创建该目录:

1mkdir ~/vault-orchestration

导航到它:

1cd ~/vault-orchestration

Packer 和 Terraform 的配置文件将分别存储在不同的子目录中。使用以下命令创建它们:

1mkdir packer terraform

因为您首先要使用 Packer,所以请导航到它的目录:

1cd packer

使用模板变量

将私人数据和应用程序机密存储在单独的变量文件中,是防止它们进入模板的理想方法。在构建映像时,Packer 会将引用的变量替换为它们的值。将机密值硬编码到模板中会带来安全风险,尤其是当模板要与团队成员共享或放到 GitHub 等公共网站上时。

您将把它们保存在 packer 子目录下一个名为 variables.json 的文件中。使用您最喜欢的文本编辑器创建该文件:

1nano variables.json

添加以下几行

1[label ~/vault-orchestration/packer/variables.json]
2{
3    "do_token": "your_do_api_key",
4    "base_system_image": "ubuntu-20-04-x64",
5    "region": "fra1",
6    "size": "s-1vcpu-1gb"
7}

变量文件由一个 JSON 字典组成,其中映射了变量名和变量值。您将在即将创建的模板中使用这些变量。如果你愿意,可以根据 developer docs 编辑基础图像、区域和 Droplet 大小值。

请记住将 your_doo_api_key 替换为您的 API 密钥(作为先决条件的一部分创建),然后保存并关闭文件。

创建构建器和供应器

变量文件准备就绪后,现在要创建 Packer 模板。

您将在名为 template.json 的文件中存储 Vault 的 Packer 模板。使用文本编辑器创建该文件:

1nano template.json

添加以下几行

 1[label ~/vault-orchestration/packer/template.json]
 2{
 3     "builders": [{
 4         "type": "digitalocean",
 5         "api_token": "{{user `do_token`}}",
 6         "image": "{{user `base_system_image`}}",
 7         "region": "{{user `region`}}",
 8         "size": "{{user `size`}}",
 9         "ssh_username": "root"
10     }],
11     "provisioners": [{
12         "type": "shell",
13         "inline": [
14             "sleep 30",
15             "sudo apt-get update",
16             "sudo apt-get install unzip -y",
17             "curl -L https://releases.hashicorp.com/vault/1.8.4/vault_1.8.4_linux_amd64.zip -o vault.zip",
18             "unzip vault.zip",
19             "sudo chown root:root vault",
20             "mv vault /usr/local/bin/",
21             "rm -f vault.zip"
22         ]
23    }]
24}

在模板中,您定义了 buildersprovisioners 的数组。构建器告诉 Packer 如何构建系统映像(根据其类型)以及将其存储在何处,而供应器则包含 Packer 在将系统映像转化为不可变映像之前应在系统上执行的操作集,例如安装或配置软件。如果没有配置器,最终得到的将是一个未经修改的基本系统镜像。构建器和配置器都会暴露参数,以便进一步定制工作流程。

您首先要定义一个类型为 "digitalocean "的单一构建器,这意味着当接到构建镜像的命令时,Packer 将使用所提供的参数,使用所提供的 API 密钥,以指定的基本系统镜像,在指定的区域内,创建一个定义大小的临时 Droplet。获取变量的格式是 {{用户 'variable_name'}},其中突出显示的部分是其名称。

临时Droplet配置完成后,配置器将使用指定用户名通过SSH连接到该Droplet,并依次执行所有已定义的配置器,然后从Droplet创建DigitalOcean快照并将其删除。

供应器属于 shell 类型,将在目标机上执行给定的命令。命令可以以字符串数组的形式 "内联 "指定,也可以在单独的脚本文件中定义(如果插入模板会因文件大小而不方便)。模板中的命令将等待系统启动30秒,然后下载并解压Vault 1.8.4。请查看Vault 官方下载页面,并将命令中的链接替换为适用于 Linux 的更新版本(如果有的话)。

完成后,保存并关闭文件。

要验证模板的有效性,请运行以下命令:

1packer validate -var-file=variables.json template.json

Packer 通过 -var-file 参数接受变量文件的路径。

您将看到以下输出:

1[secondary_label Output]
2The configuration is valid.

如果出现错误,Packer 会准确指出错误发生的位置,以便您纠正。

您现在有了一个可运行的模板,它能生成安装了 Vault 的映像,并在单独文件中定义了您的 API 密钥和其他参数。现在您可以调用 Packer 并构建快照了。

第 2 步 - 建立快照

在这一步中,你将使用 Packer build 命令从模板中创建一个 DigitalOcean 快照。

要创建快照,请运行以下命令:

1packer build -var-file=variables.json template.json

该命令需要一些时间才能完成。你会看到很多类似下面的输出:

 1[secondary_label Output]
 2digitalocean: output will be in this color.
 3
 4==> digitalocean: Creating temporary RSA SSH key for instance...
 5==> digitalocean: Importing SSH public key...
 6==> digitalocean: Creating droplet...
 7==> digitalocean: Waiting for droplet to become active...
 8==> digitalocean: Using SSH communicator to connect: ...
 9==> digitalocean: Waiting for SSH to become available...
10==> digitalocean: Connected to SSH!
11==> digitalocean: Provisioning with shell script: /tmp/packer-shell464972932
12    digitalocean: Hit:1 http://mirrors.digitalocean.com/ubuntu focal InRelease
13 ...
14==> digitalocean:   % Total    % Received % Xferd Average Speed Time Time Time Current
15==> digitalocean:                                  Dload Upload Total Spent Left Speed
16==> digitalocean: 100 63.5M 100 63.5M 0 0 110M 0 --:--:-- --:--:-- --:--:--  110M
17    digitalocean: Archive:  vault.zip
18    digitalocean:   inflating: vault
19==> digitalocean: Gracefully shutting down droplet...
20==> digitalocean: Creating snapshot: packer-1635876039
21==> digitalocean: Waiting for snapshot to complete...
22==> digitalocean: Destroying droplet...
23==> digitalocean: Deleting temporary ssh key...
24Build 'digitalocean' finished after 5 minutes 6 seconds.
25
26==> Wait completed after 5 minutes 6 seconds
27
28==> Builds finished. The artifacts of successful builds are:
29--> digitalocean: A snapshot was created: 'packer-1635876039' (ID: 94912983) in regions 'fra1'

Packer 会记录构建模板时的所有步骤。最后一行包含快照的名称(如 packer-1635876039)和括号中红色标记的 ID。请记下快照的 ID,因为下一步会用到它。

如果构建过程因 API 错误而失败,请等待几分钟,然后重试。

您已根据模板构建了一个 DigitalOcean 快照。快照中预装了 Vault,现在可以将其作为系统镜像部署 Droplets。下一步,你将编写用于自动进行此类部署的 Terraform 配置。

第 3 步 - 编写 Terraform 配置

在此步骤中,您将编写 Terraform 配置,以便使用 Packer 自动部署包含刚刚构建的 Vault 的快照的 Droplet。

在编写用于从先前构建的快照部署Vault的Terraform实际配置之前,首先需要为其配置DigitalOcean提供商。运行以下命令,导航到 "terraform "子目录:

1cd ~/vault-orchestration/terraform

然后,创建一个名为 do-provider.tf 的文件,在其中存储提供程序:

1nano do-provider.tf

添加以下几行

 1[label ~/vault-orchestration/terraform/do-provider.tf]
 2terraform {
 3  required_providers {
 4    digitalocean = {
 5      source = "digitalocean/digitalocean"
 6      version = "~> 2.0"
 7    }
 8  }
 9}
10
11variable "do_token" {
12}
13
14variable "ssh_fingerprint" {
15}
16
17variable "instance_count" {
18  default = "1"
19}
20
21variable "do_snapshot_id" {
22}
23
24variable "do_name" {
25  default = "vault"
26}
27
28variable "do_region" {
29}
30
31variable "do_size" {
32}
33
34provider "digitalocean" {
35  token = var.do_token
36}

该文件声明了参数变量,并为 "digitalocean "提供程序提供了一个 API 密钥。稍后你将在 Terraform 模板中使用这些变量,但首先需要指定它们的值。为此,Terraform 支持在_变量定义文件_中指定变量值,与 Packer 类似。文件名必须以 .tfvars.tfvars.json 结尾。稍后您将使用 -var-file 参数将该文件传递给 Terraform。

保存并关闭文件。

使用文本编辑器创建名为 definitions.tfvars 的变量定义文件:

1nano definitions.tfvars

添加以下几行

1[label ~/vault-orchestration/terraform/definitions.tfvars]
2do_token         = "your_do_api_key"
3ssh_fingerprint  = "your_ssh_key_fingerprint"
4do_snapshot_id   = your_do_snapshot_id
5do_name          = "vault"
6do_region        = "fra1"
7do_size          = "s-1vcpu-1gb"
8instance_count   = 1

请记住,将 your_doo_api_key, your_ssh_key_fingerprintyour_doo_snapshot_id 分别替换为您的账户 API 密钥、SSH 密钥指纹和上一步中记录的快照 ID。do_region "和 "do_size "参数的值必须与 Packer 变量文件中的值相同。如果想一次部署多个实例,可将 instance_count 调整为所需值。

完成后,保存并关闭文件。

有关 DigitalOcean Terraform 提供商的更多信息,请访问 官方文档

您将在 terraform 目录下名为 deployment.tf 的文件中存储 Vault 快照部署配置。使用文本编辑器创建该文件:

1nano deployment.tf

添加以下几行

 1[label ~/vault-orchestration/terraform/deployment.tf]
 2resource "digitalocean_droplet" "vault" {
 3  count              = var.instance_count
 4  image              = var.do_snapshot_id
 5  name               = var.do_name
 6  region             = var.do_region
 7  size               = var.do_size
 8  ssh_keys = [
 9    var.ssh_fingerprint
10  ]
11}
12
13output "instance_ip_addr" {
14  value = {
15    for instance in digitalocean_droplet.vault:
16    instance.id => instance.ipv4_address
17  }
18  description = "The IP addresses of the deployed instances, paired with their IDs."
19}

在这里,你需要定义一个名为 "vault "的 "digitalocean_droplet "类型的单一资源。然后,根据变量值设置其参数,并在 Droplet 资源中添加来自 DigitalOcean 账户的 SSH 密钥(使用其指纹)。最后,将所有新部署实例的 IP 地址 "输出 "到控制台。

保存并关闭文件。

在对部署配置进行其他操作之前,需要将该目录初始化为 Terraform 项目:

1terraform init

您将看到以下输出:

 1[secondary_label Output]
 2Initializing the backend...
 3
 4Initializing provider plugins...
 5- Finding digitalocean/digitalocean versions matching "~> 2.0"...
 6- Installing digitalocean/digitalocean v2.15.0...
 7- Installed digitalocean/digitalocean v2.15.0 (signed by a HashiCorp partner, key ID F82037E524B9C0E8)
 8
 9Partner and community providers are signed by their developers.
10If you'd like to know more about provider signing, you can read about it here:
11https://www.terraform.io/docs/cli/plugins/signing.html
12
13Terraform has created a lock file .terraform.lock.hcl to record the provider
14selections it made above. Include this file in your version control repository
15so that Terraform can guarantee to make the same selections by default when
16you run "terraform init" in the future.
17
18Terraform has been successfully initialized!
19
20You may now begin working with Terraform. Try running "terraform plan" to see
21any changes that are required for your infrastructure. All Terraform commands
22should now work.
23
24If you ever set or change modules or backend configuration for Terraform,
25rerun this command to reinitialize your working directory. If you forget, other
26commands will detect it and remind you to do so if necessary.

将目录初始化为项目时,Terraform 会读取可用的配置文件,并在必要时下载插件,如输出中记录的那样。

现在,您已准备好部署 Vault 快照的 Terraform 配置。现在您可以继续验证快照并将其部署到 Droplet 上。

第 4 步 - 使用 Terraform 部署 Vault

在本节中,您将使用 validate 命令验证 Terraform 配置。验证成功后,您将 "应用 "该配置并部署一个 Droplet。

运行以下命令测试配置的有效性:

1terraform validate

您将看到以下输出:

1[secondary_label Output]
2Success! The configuration is valid.

接下来,运行 plan 命令,查看 Terraform 将如何根据你的配置配置基础设施:

1terraform plan -var-file="definitions.tfvars"

Terraform 通过 -var-file 参数接受变量定义文件。

输出结果类似于

 1[secondary_label Output]
 2Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
 3symbols:
 4  + create
 5
 6Terraform will perform the following actions:
 7
 8  # digitalocean_droplet.vault[0] will be created
 9  + resource "digitalocean_droplet" "vault" {
10      + backups              = false
11      + created_at           = (known after apply)
12      + disk                 = (known after apply)
13      + graceful_shutdown    = false
14      + id                   = (known after apply)
15      + image                = "94912983"
16      + ipv4_address         = (known after apply)
17      + ipv4_address_private = (known after apply)
18      + ipv6                 = false
19      + ipv6_address         = (known after apply)
20      + locked               = (known after apply)
21      + memory               = (known after apply)
22      + monitoring           = false
23      + name                 = "vault"
24      + price_hourly         = (known after apply)
25      + price_monthly        = (known after apply)
26      + private_networking   = (known after apply)
27      + region               = "fra1"
28      + resize_disk          = true
29      + size                 = "s-1vcpu-1gb"
30      + ssh_keys             = [
31          + "...",
32        ]
33      + status               = (known after apply)
34      + urn                  = (known after apply)
35      + vcpus                = (known after apply)
36      + volume_ids           = (known after apply)
37      + vpc_uuid             = (known after apply)
38    }
39
40Plan: 1 to add, 0 to change, 0 to destroy.
41
42Changes to Outputs:
43  + instance_ip_addr = (known after apply)
44  ...

资源 "digitalocean_droplet""vault "行开头的绿色 "+"表示 Terraform 将使用后面的参数创建一个名为 "vault "的新 Droplet。这是正确的,因此现在可以运行 terraform apply 来执行计划:

1terraform apply -var-file="definitions.tfvars"

根据提示输入 "是"。几分钟后,Droplet 将完成配置,你会看到类似下面的输出:

 1[secondary_label Output]
 2
 3Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
 4symbols:
 5  + create
 6
 7Terraform will perform the following actions:
 8
 9  # digitalocean_droplet.vault[0] will be created
10  + resource "digitalocean_droplet" "vault" {
11    ...
12    }
13
14Plan: 1 to add, 0 to change, 0 to destroy.
15
16Changes to Outputs:
17  + instance_ip_addr = (known after apply)
18...
19
20digitalocean_droplet.vault[0]: Creating...
21digitalocean_droplet.vault[0]: Still creating... [10s elapsed]
22...
23digitalocean_droplet.vault[0]: Creation complete after 44s [id=271950984]
24
25Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
26
27Outputs:
28
29instance_ip_addr = {
30  "271950984" = "your_new_server_ip"
31}

在输出中,Terraform 会记录其执行的操作(在本例中,创建 Droplet),并在最后显示其公共 IP 地址。下一步将使用它连接到新的 Droplet。

您已从包含 Vault 的快照中创建了一个新的 Droplet,现在准备对其进行验证。

第 5 步 - 验证已部署的 Droplet

在此步骤中,您将使用 SSH 访问新的 Droplet,并验证 Vault 安装是否正确。

如果您使用的是 Windows 系统,可以使用 KittyPutty 等软件使用 SSH 密钥连接到 Droplet。

在 Linux 和 macOS 机器上,可以使用已有的 ssh 命令进行连接:

1ssh root@your_server_ip

出现提示时回答 "是"。登录后,执行以下命令运行 Vault:

1vault

你会看到它的 "帮助 "输出,如下所示:

 1[secondary_label Output]
 2Usage: vault <command> [args]
 3
 4Common commands:
 5    read Read data and retrieves secrets
 6    write Write data, configuration, and secrets
 7    delete Delete secrets and configuration
 8    list List data or secrets
 9    login Authenticate locally
10    agent Start a Vault agent
11    server Start a Vault server
12    status Print seal and HA status
13    unwrap Unwrap a wrapped secret
14
15Other commands:
16    audit Interact with audit devices
17    auth Interact with auth methods
18    debug Runs the debug command
19    kv Interact with Vault's Key-Value storage
20    lease Interact with leases
21    namespace Interact with namespaces
22    operator Perform operator-specific tasks
23    path-help Retrieve API help for paths
24    plugin Interact with Vault plugins and catalog
25    policy Interact with policies
26    print Prints runtime configurations
27    secrets Interact with secrets engines
28    ssh Initiate an SSH session
29    token Interact with tokens

输入 exit 可退出连接。

现在,您已验证新部署的 Droplet 是根据您制作的快照创建的,而且 Vault 已正确安装。

要销毁已配置的资源,请运行以下命令,并在出现提示时输入 "是":

1terraform destroy -var-file="definitions.tfvars"

结论

现在,您有了一个使用 Terraform 和 Packer 在 DigitalOcean Droplets 上部署 Hashicorp Vault 的自动化系统。您现在可以根据需要部署任意数量的 Vault 服务器。要开始使用 Vault,您需要初始化它并进一步配置它。有关操作说明,请访问 官方文档

有关使用 Terraform 的更多教程,请查看我们的 Terraform 内容页面如何使用 Terraform 管理基础架构 系列,其中涵盖从首次安装 Terraform 到管理复杂项目等多个 Terraform 主题。

Published At
Categories with 技术
comments powered by Disqus