检测Terraform中的Ansible更改并执行它们

时间:2019-06-03 20:51:38

标签: ansible terraform provisioning

我想将Ansible与Terraform结合使用,以便Terraform创建机器,而Ansible将对其进行配置。使用terraform-provisioner-ansible可以将它们无缝地组合在一起。但是我看到缺少变更检测功能,而Ansible独立运行时不会发生这种情况。

TL; DR :如何将在Ansible中所做的更改应用于Terraform Ansible插件?还是至少在每次更新时都执行ansible插件,以便Ansible可以自己处理?

用例示例

考虑这本安装了某些软件包的剧本

- name: Ansible install package test
  hosts: all
  tasks: 
  - name: Install cli tools
    become: yes
    apt:
      name: "{{ tools }}"
      update_cache: yes
    vars:
      tools:
        - nnn
        - htop

使用插件集成到Terraform中

resource "libvirt_domain" "ubuntu18" {
  # ...
  connection {
    type = "ssh"
    host = "192.168.2.2"
    user = "ubuntu"
    private_key = "${file("~/.ssh/id_rsa")}"
  }
  provisioner "ansible" {
    plays {
      enabled = true
      become_method = "sudo"

      playbook = {
        file_path = "ansible-test.yml" 
      }
    }
  }
}

将在第一次运行时正常。但是后来我发现缺少一些包裹

- name: Ansible install package test
  hosts: all
  tasks: 
  - name: Install cli tools
    become: yes
    apt:
      name: "{{ tools }}"
      update_cache: yes
    vars:
      tools:
        - nnn
        - htop
        - vim # This is a new package

运行terraform plan时,我会得到No changes. Infrastructure is up-to-date.我的新软件包vim将永远不会安装!因此Ansible无法运行,因为如果Ansible运行,它将安装新软件包。

问题似乎是the provisioner itself

  

创建时供应商仅在创建期间运行,而不在更新或任何其他生命周期中运行。它们是执行系统引导的一种手段。

但是应用更新的正确方法是什么?我尝试了null_ressourcedepends_on链接到我的vm ressource,但是Terraform也没有检测到Ansible部分的更改。似乎是Terraform插件中的lack of change detection

在文档中,我仅发现销毁时间供应者。但是没有更新。我可以销毁并重新创建机器。这会使事情变慢很多。我喜欢Ansible的方法,即检查已安装的内容并仅应用尚不存在的更改,这似乎是一种很好的配置方式。

不可能用Terraform做类似的事情吗?

根据我目前的经验(比Terraform更为Ansible),我没有其他方法可以丢弃漂亮的插件并自己执行Ansible。但这也会降低很好的集成度。因此,我需要自己甚至手动生成库存文件(在我看来,这缺少自动化方法)。

source_code_hash可能是一个选择,但不灵活:当有多个播放/角色时,我需要手动处理每个文件,这样容易出错。

3 个答案:

答案 0 :(得分:0)

正如您在问题中提到的,插件中没有更改检测。您可以在null_resource上实现触发器,以使其在每次应用时运行。

resource "null_resource" "ansible-provisioner" {
  triggers {
      build_number = "${timestamp()}"
  }
  provisioner "local-exec" {
    command = "ansible-playbook ansible-test.yml"
  }
}

答案 1 :(得分:0)

使用带有伪触发器的print(multipolygon[:1]) MULTIPOLYGON (((0 0, 1 1, 1 0, 0 0)))

tedsmitt中的想法使用时间戳作为触发器,这似乎是强制供应者的唯一方法。无论如何从CLI单纯运行null_ressource都会产生手工维护清单的开销。您无法从此处致电python dynamic inventory script,因为ansible-playbook需要在

之前完成

我认为,一种更好的方法是在此处运行ansible provisioner

terraform apply

这里唯一的提包是:Terraform每次都会识别伪更改

resource "null_resource" "ansible-provisioner" {
  triggers {
      build_number = "${timestamp()}"
  }
  depends_on = ["libvirt_domain.ubuntu18"]

  connection {
    type = "ssh"
    host = "192.168.2.2"
    user = "ubuntu"
    private_key = "${file("~/.ssh/id_rsa")}"
  }
  provisioner "ansible" {
    plays {
      enabled = true
      become_method = "sudo"

      playbook = {
        file_path = "ansible-test.yml" 
      }
    }
  }
}

根据其他解决方法,这似乎是对我最好的折衷方案。

通过动态广告资源手动运行Ansible

我发现的另一种方法是dynamic inventory plugin,可以找到详细的描述in this blog entry。它集成到Terraform中,让您将资源指定为清单宿主,例如:

Terraform will perform the following actions:

-/+ null_resource.ansible-provisioner (new resource required)
      id:                    "3365240528326363062" => <computed> (forces new resource)
      triggers.%:            "1" => "1"
      triggers.build_number: "2019-06-04T09:32:27Z" => "2019-06-04T09:34:17Z" (forces new resource)


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

Python脚本使用此信息来生成动态清单,可以这样使用:

resource "ansible_host" "k8s" {
  inventory_hostname = "192.168.2.2"
  groups             = ["test"]
  vars = {
    ansible_user = "ubuntu"
    ansible_ssh_private_key_file = "~/.ssh/id_rsa"
  }
}

一个很大的好处是:它使您的配置保持干燥。 Terraform具有领先的配置文件,无需维护单独的Ansible文件。还有可变用法的能力(例如,不应该像我的示例中那样硬编码库存主机名用于生产用途)。

在我的用例(Provision Rancher测试集群)中,ansible-playbook -i /etc/ansible/terraform.py ansible-test.yml 方法似乎更好,因为一切都是使用单个Terraform命令构建的。无需另外执行Ansible。但是根据要求,最好将Ansible分开进行,因此我将其发布为替代方法。

安装插件

尝试此解决方案时,请记住您需要从here安装相应的Terraform插件:

null_ressource

还要注意,automated provisioner from the solution above必须先删除,因为它具有相同的名称(可能会发生冲突)。

答案 2 :(得分:0)

您可以尝试一下,它对我有用。

resource "null_resource" "ansible-swarm-setup" {
  local_file.ansible_inventory ]
  #nhu
  triggers= {
      instance_ids = join(",",openstack_compute_instance_v2.swarm-cluster-hosts[*].id)
  }
  connection {
    type = "ssh"
    user = var.ansible_user
    timeout = "3m"
    private_key = var.private_ssh_key
    host = local.cluster_ips[0]
  }
}

当它检测到实例索引/ id的变化时,它将触发ansible剧本。