如何使用 Ansible 和 Terraform 进行配置管理

作为Write for DOnations计划的一部分,作者选择了免费和开源基金来接受捐赠

介绍

Ansible是一种执行playbooks的配置管理工具,playbooks是在指定目标服务器上用 YAML 编写的可定制操作列表。它可以执行所有引导操作,例如安装和更新软件、创建和删除用户以及配置系统服务。因此,它适用于启动您使用 Terraform 部署的服务器,这些服务器默认创建为空白。

Ansible 和 Terraform 不是竞争解决方案,因为它们解决了基础设施和软件部署的不同阶段。Terraform 允许您定义和创建系统的基础设施,包括您的应用程序将在其上运行的硬件。相反,Ansible 通过在提供的服务器实例上执行其剧本来配置和部署软件。在 Terraform 创建后直接提供的资源上运行 Ansible 可以让您更快地使资源可用于您的用例。它还可以更轻松地进行维护和故障排除,因为所有部署的服务器都将应用相同的操作。

在本教程中,您将使用 Terraform 部署 Droplet,然后在它们创建后立即使用 Ansible 引导 Droplet。当资源部署时,您将直接从 Terraform 调用 Ansible。您还将避免在配置中使用 Terraform配置器引入竞争条件,这将确保 Droplet 部署在进一步设置开始之前完全完成。remote-execlocal-exec

先决条件

注意:本教程已专门使用 Terraform 进行了测试0.13

第 1 步 – 定义液滴

在此步骤中,您将定义稍后将在其上运行 Ansible playbook 的 Droplet,这将设置 Apache Web 服务器。

假设您位于terraform-ansible作为先决条件的一部分创建目录中,您将定义一个 Droplet 资源,通过指定创建它的三个副本count,并输出它们的 IP 地址。您将定义存储在名为droplets.tf. 通过运行创建并打开它进行编辑:

  • nano droplets.tf

添加以下几行:

~/terraform-ansible/droplets.tf
resource "digitalocean_droplet" "web" {
  count  = 3
  image  = "ubuntu-18-04-x64"
  name   = "web-${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"

  ssh_keys = [
      data.digitalocean_ssh_key.terraform.id
  ]
}

output "droplet_ip_addresses" {
  value = {
    for droplet in digitalocean_droplet.web:
    droplet.name => droplet.ipv4_address
  }
}

在这里,您定义了一个运行 Ubuntu 18.04 的 Droplet 资源,在该区域的 CPU 内核上具有 1GB RAM fra1Terraform 将从您的帐户中提取您在先决条件中定义的 SSH 密钥,并将其添加到已配置的 Droplet,并将指定的唯一 ID列表元素传入ssh_keys. 由于count设置参数,Terraform 将部署 Droplet 3 次它后面的输出块将显示三个 Droplet 的 IP 地址。循环遍历 Droplet 列表,对于每个实例,将其名称与其 IP 地址配对,并将其附加到结果映射中。

完成后保存并关闭文件。

您现在已经定义了 Terraform 将部署的 Droplet。在下一步中,您将编写一个 Ansible 剧本,该剧本将在三个已部署的 Droplet 中的每一个上执行,并将部署 Apache Web 服务器。您稍后将返回到 Terraform 代码并添加与 Ansible 的集成。

第 2 步 — 编写 Ansible Playbook

现在,您将创建一个 Ansible 剧本来执行初始服务器设置任务,例如创建新用户和升级已安装的包。您将通过编写任务来指导 Ansible 做什么,任务是在目标主机上执行的操作单元。任务可以使用内置函数,或指定要运行的自定义命令。除了初始设置的任务外,您还将安装 Apache Web 服务器并启用其mod_rewrite模块。

在编写剧本之前,请确保与您的 DigitalOcean 帐户中的密钥相对应的公钥和私钥 SSH 密钥在运行 Terraform 和 Ansible 的机器上可用且可访问。在 Linux 上存储它们的典型位置是~/.ssh(尽管您可以将它们存储在其他地方)。

注意:在 Linux 上,您需要确保私钥文件具有适当的权限。您可以通过运行来设置它们:

  • chmod 600 your_private_key_location

您已经为私钥定义了一个变量,因此您只需为公钥位置添加一个变量。

provider.tf通过运行打开以进行编辑:

  • nano provider.tf

添加以下行:

~/terraform-ansible/provider.tf
terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
      version = "1.22.2"
    }
  }
}

variable "do_token" {}
variable "pvt_key" {}
variable "pub_key" {}

provider "digitalocean" {
  token = var.do_token
}

data "digitalocean_ssh_key" "terraform" {
  name = "terraform"
}

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

随着pub_key变量现在定义,你会开始写剧本Ansible。您将把它存储在一个名为apache-install.yml. 创建并打开它进行编辑:

  • nano apache-install.yml

您将逐步构建剧本。首先,您需要定义 playbook 将在哪些主机上运行、其名称以及任务是否应以root身份运行添加以下几行:

~/terraform-ansible/apache-install.yml
- become: yes
  hosts: all
  name: apache-install

通过设置becomeyes,您指示 Ansible 以超级用户身份运行命令,并通过指定allhosts,您允许 Ansible 在任何给定服务器上运行任务——即使是通过命令行传入的任务,就像 Terraform 一样。

您将添加的第一个任务将创建一个新的非 root 用户。将以下任务定义附加到您的剧本中:

~/terraform-ansible/apache-install.yml
. . .
  tasks:
    - name: Add the user 'sammy' and add it to 'sudo'
      user:
        name: sammy
        group: sudo

首先定义任务列表,然后向其中添加任务。它将创建一个名为sammy的用户,sudo通过将他们添加到适当的组来授予他们超级用户访问权限

下一个任务会将您的公共 SSH 密钥添加到用户,以便您稍后可以连接到它:

~/terraform-ansible/apache-install.yml
. . .
    - name: Add SSH key to 'sammy'
      authorized_key:
        user: sammy
        state: present
        key: "{{ lookup('file', pub_key) }}"

此任务将确保从本地文件中查找的公共 SSH 密钥present在目标上。您将pub_key在下一步中来自 Terraform变量提供值

设置用户任务后,接下来是使用以下命令更新 Droplet 上的软件apt

~/terraform-ansible/apache-install.yml
. . .
    - name: Update all packages
      apt:
        upgrade: dist
        update_cache: yes
        cache_valid_time: 3600

到目前为止,目标 Droplet 具有可用软件包的最新版本和可用的非 root 用户。您现在可以mod_rewrite通过附加以下任务来订购 Apache 和模块的安装

~/terraform-ansible/apache-install.yml
. . .
    - name: Install apache2
      apt: name=apache2 update_cache=yes state=latest

    - name: Enable mod_rewrite
      apache2_module: name=rewrite state=present
      notify:
        - Restart apache2

  handlers:
    - name: Restart apache2
      service: name=apache2 state=restarted

第一个任务将运行apt包管理器来安装 Apache。第二个将确保mod_rewrite模块是present. 启用后,您需要确保重新启动 Apache,您无法从任务本身配置它。为了解决这个问题,你调用一个处理程序来发出重新启动。

此时,您的剧本将如下所示:

~/terraform-ansible/apache-install.yml
- become: yes
  hosts: all
  name: apache-install
  tasks:
    - name: Add the user 'sammy' and add it to 'sudo'
      user:
        name: sammy
        group: sudo
    - name: Add SSH key to 'sammy'
      authorized_key:
        user: sammy
        state: present
        key: "{{ lookup('file', pub_key) }}"
    - name: Update all packages
      apt:
        upgrade: dist
        update_cache: yes
        cache_valid_time: 3600
    - name: Install apache2
      apt: name=apache2 update_cache=yes state=latest
    - name: Enable mod_rewrite
      apache2_module: name=rewrite state=present
      notify:
        - Restart apache2

  handlers:
    - name: Restart apache2
      service: name=apache2 state=restarted

这就是您需要在 Ansible 端定义的全部内容,因此请保存并关闭剧本。您现在将修改 Droplet 部署代码以在 Droplet 完成配置后执行此剧本。

第 3 步 — 在部署的 Droplet 上运行 Ansible

现在您已经定义了 Ansible 将在目标服务器上执行的操作,您将修改 Terraform 配置以在创建 Droplet 时运行它。

Terraform 提供了两个执行命令的配置器:local-execremote-exec,分别在本地或远程(在目标上)运行命令。remote-exec需要连接数据,例如类型和访问密钥,而local-exec在 Terraform 正在执行的机器上执行所有操作,因此不需要连接信息。请务必注意,local-exec在您为其定义的资源完成配置后立即运行;因此,它不会等待资源实际启动。它在云平台确认其存在于系统中后运行。

您现在将向 Droplet 添加配置程序定义以在部署后运行 Ansible。打开droplets.tf编辑:

  • nano droplets.tf

添加突出显示的行:

~/terraform-ansible/droplets.tf
resource "digitalocean_droplet" "web" {
  count  = 3
  image  = "ubuntu-18-04-x64"
  name   = "web-${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"

  ssh_keys = [
      data.digitalocean_ssh_key.terraform.id
  ]

  provisioner "remote-exec" {
    inline = ["sudo apt update", "sudo apt install python3 -y", "echo Done!"]

    connection {
      host        = self.ipv4_address
      type        = "ssh"
      user        = "root"
      private_key = file(var.pvt_key)
    }
  }

  provisioner "local-exec" {
    command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u root -i '${self.ipv4_address},' --private-key ${var.pvt_key} -e 'pub_key=${var.pub_key}' apache-install.yml"
  }
}

output "droplet_ip_addresses" {
  value = {
    for droplet in digitalocean_droplet.web:
    droplet.name => droplet.ipv4_address
  }
}

与 Terraform 一样,Ansible 在本地运行并通过 SSH 连接到目标服务器。要运行它,您需要local-exec在运行ansible-playbook命令的 Droplet 定义中定义一个供应商这会传入用户名 ( root )、当前 Droplet 的 IP(使用 检索${self.ipv4_address})、SSH 公钥和私钥,并指定要运行的剧本文件 ( apache-install.yml)。通过将ANSIBLE_HOST_KEY_CHECKING环境变量设置False,您可以跳过检查服务器是否已预先连接。

如前所述,local-exec供应器在不等待 Droplet 可用情况下运行,因此剧本的执行可能先于 Droplet 的实际可用性。为了解决这个问题,您定义了remote-exec供应商以包含要在目标服务器上执行的命令。对于remote-exec执行目标服务器必须是可用的。由于remote-execlocal-exec调用 Ansible 时服务器将完全初始化之前运行python3预装在 Ubuntu 18.04 上,因此您可以根据需要注释或删除该命令。

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

然后,通过运行以下命令部署 Droplets。请记住分别用您的私钥和公钥的位置替换private_key_locationpublic_key_location

  • terraform apply -var "do_token=${DO_PAT}" -var "pvt_key=private_key_location" -var "pub_key=public_key_location"

输出会很长。您的 Droplet 将进行配置,然后与每个 Droplets 建立连接。接下来,remote-exec供应商将执行并安装python3

Output
... digitalocean_droplet.web[1] (remote-exec): Connecting to remote host via SSH... digitalocean_droplet.web[1] (remote-exec): Host: ... digitalocean_droplet.web[1] (remote-exec): User: root digitalocean_droplet.web[1] (remote-exec): Password: false digitalocean_droplet.web[1] (remote-exec): Private key: true digitalocean_droplet.web[1] (remote-exec): Certificate: false digitalocean_droplet.web[1] (remote-exec): SSH Agent: false digitalocean_droplet.web[1] (remote-exec): Checking Host Key: false digitalocean_droplet.web[1] (remote-exec): Connected! ...

之后,Terraform 将为local-exec每个 Droplet运行配置器,然后执行 Ansible。以下输出显示了其中一个 Droplet 的情况:

Output
... digitalocean_droplet.web[2] (local-exec): Executing: ["/bin/sh" "-c" "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u root -i 'ip_address,' --private-key private_key_location -e 'pub_key=public_key_location' apache-install.yml"] digitalocean_droplet.web[2] (local-exec): PLAY [apache-install] ********************************************************** digitalocean_droplet.web[2] (local-exec): TASK [Gathering Facts] ********************************************************* digitalocean_droplet.web[2] (local-exec): ok: [ip_address] digitalocean_droplet.web[2] (local-exec): TASK [Add the user 'sammy' and add it to 'sudo'] ******************************* digitalocean_droplet.web[2] (local-exec): changed: [ip_address] digitalocean_droplet.web[2] (local-exec): TASK [Add SSH key to 'sammy''] ******************************* digitalocean_droplet.web[2] (local-exec): changed: [ip_address] digitalocean_droplet.web[2] (local-exec): TASK [Update all packages] ***************************************************** digitalocean_droplet.web[2] (local-exec): changed: [ip_address] digitalocean_droplet.web[2] (local-exec): TASK [Install apache2] ********************************************************* digitalocean_droplet.web[2] (local-exec): changed: [ip_address] digitalocean_droplet.web[2] (local-exec): TASK [Enable mod_rewrite] ****************************************************** digitalocean_droplet.web[2] (local-exec): changed: [ip_address] digitalocean_droplet.web[2] (local-exec): RUNNING HANDLER [Restart apache2] ********************************************** digitalocean_droplet.web[2] (local-exec): changed: [ip_address] digitalocean_droplet.web[2] (local-exec): PLAY RECAP ********************************************************************* digitalocean_droplet.web[2] (local-exec): [ip_address] : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ...

在输出结束时,您将收到三个 Droplet 及其 IP 地址的列表:

Output
droplet_ip_addresses = { "web-0" = "..." "web-1" = "..." "web-2" = "..." }

您现在可以导航到浏览器中的 IP 地址之一。您将到达默认的 Apache 欢迎页面,这表示 Web 服务器安装成功。

阿帕奇欢迎页面

这意味着 Terraform 已成功配置您的服务器和在其上执行的 Ansible playbook。

要检查 SSH 密钥是否已正确添加到已配置的 Droplet 上的sammy,请使用以下命令连接到其中之一:

  • ssh -i private_key_location sammy@droplet_ip_address

请记住输入已配置的 Droplet 之一的私钥位置和 IP 地址,您可以在 Terraform 输出中找到这些信息。

输出将类似于以下内容:

Output
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-121-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of ... System load: 0.0 Processes: 88 Usage of /: 6.4% of 24.06GB Users logged in: 0 Memory usage: 20% IP address for eth0: ip_address Swap usage: 0% IP address for eth1: ip_address 0 packages can be updated. 0 updates are security updates. New release '20.04.1 LTS' available. Run 'do-release-upgrade' to upgrade to it. *** System restart required *** Last login: ... ...

您已成功连接到目标并获得了sammy用户的shell 访问权限,这确认为该用户正确配置了 SSH 密钥。

您可以通过运行以下命令来销毁已部署的 Droplet,并yes在出现提示时输入:

  • terraform destroy -var "do_token=${DO_PAT}" -var "pvt_key=private_key_location" -var "pub_key=public_key_location"

在此步骤中,您已将 Ansible playbook 执行添加为local-execDroplet 定义的供应商。为了确保服务器可用于连接,您已经包含了remote-exec供应商,它可以用于安装python3先决条件,之后 Ansible 将运行。

结论

Terraform 和 Ansible 共同构成了一个灵活的工作流程,用于使用所需的软件和硬件配置来启动服务器。作为 Terraform 部署过程的一部分直接运行 Ansible 可以让您更快地启动服务器并引导您的开发工作和应用程序的依赖项。

有关使用 Terraform 的更多信息,请查看我们的如何使用 Terraform 系列管理基础设施您还可以在我们的Ansible 主题页面上找到更多 Ansible 内容资源

觉得文章有用?

点个广告表达一下你的爱意吧 !😁