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

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。

介绍

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

同样由 HashiCorp 开发的 PackerTerraform 可以一起使用来创建和部署 Vault 的图像。 在此工作流程中,开发人员可以使用 Packer 从单个配置文件中为不同平台编写不可变图像,该文件指定图像应包含的内容。 然后,Terraform 将根据需要部署尽可能多的已创建图像的自定义实例。

在本教程中,您将使用 Packer 创建安装了 Vault 的系统的不可变快照,并使用 Terraform 编排其部署。 最后,您将拥有一个用于在适当位置部署 Vault 的自动化系统,让您可以专注于使用 Vault 本身,而不是底层安装和配置过程。

先决条件

  • Packer 安装在本地计算机上。 有关说明,请访问官方文档
  • Terraform 安装在本地计算机上。 要实现这一点,请遵循 如何使用 Terraform 与 DigitalOcean 教程的 Step 1
  • 对您的 DigitalOcean 帐户具有读写权限的个人访问令牌(API 密钥)。 要了解如何创建一个,请访问文档中的 如何创建个人访问令牌
  • 您将用于对已部署的 Vault Droplet 进行身份验证的 SSH 密钥,可在您的本地计算机上使用并添加到您的 DigitalOcean 帐户中。 您还需要它的指纹,您可以在添加后从您帐户的 Security 页面复制它。 有关详细说明,请参阅 DigitalOcean 文档如何设置 SSH 密钥 教程。

第 1 步 — 创建打包程序模板

在这一步中,您将编写一个名为 模板 的 Packer 配置文件,它将指导 Packer 如何构建包含预安装 Vault 的映像。 您将以 JSON 格式编写配置,这是一种常用的人类可读的配置文件格式。

出于本教程的目的,您将在 ~/vault-orchestration 下存储所有文件。 通过运行以下命令创建目录:

mkdir ~/vault-orchestration

导航到它:

cd ~/vault-orchestration

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

mkdir packer terraform

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

cd packer

使用模板变量

将私有数据和应用程序机密存储在单独的变量文件中是将它们排除在模板之外的理想方法。 在构建映像时,Packer 将用它们的值替换引用的变量。 将秘密值硬编码到您的模板中存在安全风险,尤其是在要与团队成员共享或发布在 GitHub 等公共网站上时。

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

nano variables.json

添加以下行:

~/vault-orchestration/packer/variables.json

{
    "do_token": "your_do_api_key",
    "base_system_image": "ubuntu-20-04-x64",
    "region": "fra1",
    "size": "s-1vcpu-1gb"
}

变量文件包含一个 JSON 字典,它将变量名称映射到它们的值。 您将在即将创建的模板中使用这些变量。 如果您愿意,可以根据 开发者文档 编辑基本图像、区域和液滴大小值。

请记住将 your_do_api_key 替换为您在先决条件中创建的 API 密钥,然后保存并关闭文件。

创建构建器和供应商

准备好变量文件后,您现在将创建 Packer 模板本身。

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

nano template.json

添加以下行:

~/vault-orchestration/packer/template.json

{
     "builders": [{
         "type": "digitalocean",
         "api_token": "{{user `do_token`}}",
         "image": "{{user `base_system_image`}}",
         "region": "{{user `region`}}",
         "size": "{{user `size`}}",
         "ssh_username": "root"
     }],
     "provisioners": [{
         "type": "shell",
         "inline": [
             "sleep 30",
             "sudo apt-get update",
             "sudo apt-get install unzip -y",
             "curl -L https://releases.hashicorp.com/vault/1.8.4/vault_1.8.4_linux_amd64.zip -o vault.zip",
             "unzip vault.zip",
             "sudo chown root:root vault",
             "mv vault /usr/local/bin/",
             "rm -f vault.zip"
         ]
    }]
}

在模板中,您定义了 buildersprovisioners 的数组。 构建器告诉 Packer 如何构建系统映像(根据它们的类型)以及存储它的位置,而配置器包含 Packer 在将系统转换为不可变映像之前应在系统上执行的操作集,例如安装或配置软件。 如果没有任何配置程序,您最终会得到一个未受影响的基本系统映像。 构建器和供应器都公开参数以进一步定制工作流程。

您首先定义一个类型为 digitalocean 的构建器,这意味着当命令构建图像时,Packer 将使用提供的参数使用提供的 API 密钥创建一个定义大小的临时 Droplet,并指定基本系统映像和指定区域。 获取变量的格式是 模板:User 'variable name',其中突出显示的部分是它的名称。

配置临时 Droplet 时,配置程序将使用指定用户名的 SSH 连接到它,并在从 Droplet 创建 DigitalOcean 快照并将其删除之前依次执行所有定义的配置程序。

Provisioner 的类型为 shell,它将在目标上执行给定的命令。 命令可以指定为 inline,作为字符串数组,或者如果将它们插入到模板中由于大小而变得笨拙,则可以在单独的脚本文件中定义。 模板中的命令将等待系统启动 30 秒,然后下载并解压缩 Vault 1.8.4。 检查 官方 Vault 下载页面 并将命令中的链接替换为适用于 Linux 的较新版本(如果可用)。

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

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

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

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

您将看到以下输出:

OutputThe configuration is valid.

如果您收到错误,Packer 将准确指定错误发生的位置,以便您进行更正。

您现在有了一个工作模板,该模板可以生成安装了 Vault 的图像,您的 API 密钥和其他参数定义在单独的文件中。 您现在已准备好调用 Packer 并构建快照。

第 2 步 — 构建快照

在此步骤中,您将使用 Packer build 命令从模板构建 DigitalOcean 快照。

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

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

此命令需要一些时间才能完成。 您会看到很多输出,类似于以下内容:

Outputdigitalocean: output will be in this color.

==> digitalocean: Creating temporary RSA SSH key for instance...
==> digitalocean: Importing SSH public key...
==> digitalocean: Creating droplet...
==> digitalocean: Waiting for droplet to become active...
==> digitalocean: Using SSH communicator to connect: ...
==> digitalocean: Waiting for SSH to become available...
==> digitalocean: Connected to SSH!
==> digitalocean: Provisioning with shell script: /tmp/packer-shell464972932
    digitalocean: Hit:1 http://mirrors.digitalocean.com/ubuntu focal InRelease
 ...
==> digitalocean:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
==> digitalocean:                                  Dload  Upload   Total   Spent    Left  Speed
==> digitalocean: 100 63.5M  100 63.5M    0     0   110M      0 --:--:-- --:--:-- --:--:--  110M
    digitalocean: Archive:  vault.zip
    digitalocean:   inflating: vault
==> digitalocean: Gracefully shutting down droplet...
==> digitalocean: Creating snapshot: packer-1635876039
==> digitalocean: Waiting for snapshot to complete...
==> digitalocean: Destroying droplet...
==> digitalocean: Deleting temporary ssh key...
Build 'digitalocean' finished after 5 minutes 6 seconds.

==> Wait completed after 5 minutes 6 seconds

==> Builds finished. The artifacts of successful builds are:
--> 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 子目录:

cd ~/vault-orchestration/terraform

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

nano do-provider.tf

添加以下行:

~/vault-orchestration/terraform/do-provider.tf

terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
  }
}

variable "do_token" {
}

variable "ssh_fingerprint" {
}

variable "instance_count" {
  default = "1"
}

variable "do_snapshot_id" {
}

variable "do_name" {
  default = "vault"
}

variable "do_region" {
}

variable "do_size" {
}

provider "digitalocean" {
  token = var.do_token
}

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

保存并关闭文件。

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

nano definitions.tfvars

添加以下行:

~/vault-orchestration/terraform/definitions.tfvars

do_token         = "your_do_api_key"
ssh_fingerprint  = "your_ssh_key_fingerprint"
do_snapshot_id   = your_do_snapshot_id
do_name          = "vault"
do_region        = "fra1"
do_size          = "s-1vcpu-1gb"
instance_count   = 1

请记住将 your_do_api_key, your_ssh_key_fingerprintyour_do_snapshot_id 分别替换为您的帐户 API 密钥、SSH 密钥的指纹和您在上一步中记下的快照 ID。 do_regiondo_size 参数的值必须与 Packer 变量文件中的值相同。 如果您想一次部署多个实例,请将 instance_count 调整为您想要的值。

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

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

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

nano deployment.tf

添加以下行:

~/vault-orchestration/terraform/deployment.tf

resource "digitalocean_droplet" "vault" {
  count              = var.instance_count
  image              = var.do_snapshot_id
  name               = var.do_name
  region             = var.do_region
  size               = var.do_size
  ssh_keys = [
    var.ssh_fingerprint
  ]
}

output "instance_ip_addr" {
  value = {
    for instance in digitalocean_droplet.vault:
    instance.id => instance.ipv4_address
  }
  description = "The IP addresses of the deployed instances, paired with their IDs."
}

在这里,您定义了一个名为 vaultdigitalocean_droplet 类型的单个 resource。 然后,您根据变量值设置其参数,并将您的 DigitalOcean 帐户中的 SSH 密钥(使用其指纹)添加到 Droplet 资源。 最后,将所有新部署实例的 IP 地址 output 发送到控制台。

保存并关闭文件。

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

terraform init

您将看到以下输出:

OutputInitializing the backend...

Initializing provider plugins...
- Finding digitalocean/digitalocean versions matching "~> 2.0"...
- Installing digitalocean/digitalocean v2.15.0...
- Installed digitalocean/digitalocean v2.15.0 (signed by a HashiCorp partner, key ID F82037E524B9C0E8)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

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

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

第 4 步 — 使用 Terraform 部署 Vault

在本节中,您将使用 validate 命令验证 Terraform 配置。 一旦验证成功,您将 apply 它并部署一个 Droplet。

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

terraform validate

您将看到以下输出:

OutputSuccess! The configuration is valid.

接下来,运行 plan 命令以查看 Terraform 在根据您的配置配置基础设施时会尝试什么:

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

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

输出将类似于:

OutputTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.vault[0] will be created
  + resource "digitalocean_droplet" "vault" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + graceful_shutdown    = false
      + id                   = (known after apply)
      + image                = "94912983"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "vault"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "fra1"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + ssh_keys             = [
          + "...",
        ]
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + instance_ip_addr = (known after apply)
  ...

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

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

出现提示时输入 yes。 几分钟后,Droplet 将完成配置,您将看到类似于以下内容的输出:

Output
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.vault[0] will be created
  + resource "digitalocean_droplet" "vault" {
    ...
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + instance_ip_addr = (known after apply)
...

digitalocean_droplet.vault[0]: Creating...
digitalocean_droplet.vault[0]: Still creating... [10s elapsed]
...
digitalocean_droplet.vault[0]: Creation complete after 44s [id=271950984]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

instance_ip_addr = {
  "271950984" = "your_new_server_ip"
}

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

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

第 5 步 - 验证您部署的 Droplet

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

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

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

ssh root@your_server_ip

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

vault

您将看到它的“帮助”输出,如下所示:

OutputUsage: vault <command> [args]

Common commands:
    read        Read data and retrieves secrets
    write       Write data, configuration, and secrets
    delete      Delete secrets and configuration
    list        List data or secrets
    login       Authenticate locally
    agent       Start a Vault agent
    server      Start a Vault server
    status      Print seal and HA status
    unwrap      Unwrap a wrapped secret

Other commands:
    audit          Interact with audit devices
    auth           Interact with auth methods
    debug          Runs the debug command
    kv             Interact with Vault's Key-Value storage
    lease          Interact with leases
    namespace      Interact with namespaces
    operator       Perform operator-specific tasks
    path-help      Retrieve API help for paths
    plugin         Interact with Vault plugins and catalog
    policy         Interact with policies
    print          Prints runtime configurations
    secrets        Interact with secrets engines
    ssh            Initiate an SSH session
    token          Interact with tokens

您可以通过键入 exit 来退出连接。

您现在已经验证了您新部署的 Droplet 是从您创建的快照创建的,并且 Vault 已正确安装。

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

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

结论

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

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