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

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

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

介绍

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

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

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

先决条件

注意: 本教程专门用 Terraform 1.0.2 测试过。


第 1 步 - 定义液滴

在此步骤中,您将定义 Droplet,稍后您将在其上运行 Ansible 剧本,该剧本将设置 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
  }
}

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

完成后保存并关闭文件。

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

第 2 步 — 编写 Ansible 剧本

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

在编写剧本之前,请确保您的公共和私有 SSH 密钥(对应于您的 DigitalOcean 帐户中的密钥)在您运行 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 = "~> 2.0"
    }
  }
}

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

通过将 become 设置为 yes,您指示 Ansible 以超级用户身份运行命令,并通过为 hosts 指定 all,您允许 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。 您将在下一步中从 Terraform 提供 pub_key 变量的值。

您现在可以通过附加以下任务来订购 Apache 和 mod_rewrite 模块的安装:

~/terraform-ansible/apache-install.yml

    - name: Wait for apt to unlock
      become: yes
      shell:  while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 5; done;
      
    - 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 包管理器的任何包安装完成。 第二个任务将运行 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: Wait for apt to unlock
      become: yes
      shell:  while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 5; done;
      
    - 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

完成后,检查所有 YAML 元素的缩进是否正确并与上面显示的一致。 这就是您需要在 Ansible 端定义的所有内容,因此请保存并关闭 playbook。 现在,您将修改 Droplet 部署代码以在 Droplet 完成配置后执行此 playbook。

第 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 连接到目标服务器。 要运行它,您需要在运行 ansible-playbook 命令的 Droplet 定义中定义一个 local-exec 配置程序。 这会传入用户名(root)、当前 Droplet 的 IP(使用 ${self.ipv4_address} 检索)、SSH 公钥和私钥,并指定要运行的 playbook 文件(apache-install.yml)。 通过将 ANSIBLE_HOST_KEY_CHECKING 环境变量设置为 False,您可以跳过预先检查服务器是否已连接。

如前所述,local-exec 配置器运行时无需等待 Droplet 可用,因此 playbook 的执行可能先于 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"

输出会很长。 您的 Droplets 将进行配置,然后将与每个 Droplet 建立连接。 接下来 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 将为每个 Droplet 运行 local-exec 配置器,该配置器执行 Ansible。 以下输出显示了其中一个液滴的情况:

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 地址的列表:

Outputdroplet_ip_addresses = {
  "web-0" = "..."
  "web-1" = "..."
  "web-2" = "..."
}

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

这意味着 Terraform 配置了您的服务器,并且您的 Ansible playbook 在其上成功执行。

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

ssh -i private_key_location sammy@droplet_ip_address

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

输出将类似于以下内容:

OutputWelcome 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 密钥。

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

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

在这一步中,您已将 Ansible playbook 执行作为 local-exec 配置器添加到您的 Droplet 定义中。 为确保服务器可用于连接,您已包含 remote-exec 配置程序,它可用于安装 python3 必备项,之后 Ansible 将运行。

结论

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

本教程是 如何使用 Terraform 管理基础架构系列的一部分。 该系列涵盖了许多 Terraform 主题,从首次安装 Terraform 到管理复杂的项目。

您还可以在我们的 Ansible 主题页面 上找到其他 Ansible 内容资源。