【问题标题】:Detect Ansible changes in Terraform and execute them检测 Terraform 中的 Ansible 更改并执行它们
【发布时间】:2019-06-03 20:51:38
【问题描述】:

我想将 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:

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

但是应用更新的正确方法是什么?我尝试使用depends_on 链接到我的vm 资源的null_ressource,但Terraform 也没有检测到Ansible 部分的更改。似乎是来自 Terraform 插件的lack of change detection

在文档中,我只找到了销毁时间供应商。但没有更新。我可以摧毁并重新创造这台机器。这会使事情变慢很多。我喜欢 Ansible 检查什么是存在的方法,只应用不存在的更改,这似乎是一种很好的配置方式。

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

根据我目前的经验(比 Terraform 更 Ansible),我认为没有其他方法可以放弃漂亮的插件并自行执行 Ansible。但这也会放弃良好的集成。所以我需要自己甚至手动生成库存文件(在我看来这错过了自动化方法)。

source_code_hash 可能是一个选项,但不灵活:当有多个播放/角色时,我需要为每个文件手动执行此操作,这很容易出错。

【问题讨论】:

    标签: ansible terraform provisioning


    【解决方案1】:

    使用带有伪触发器的null_ressource

    tedsmitt 的想法使用时间戳作为触发器,这似乎是强制配置器的唯一方法。但是,从 CLI 运行 ansible-playbook plain 会产生手动维护库存的开销。你不能从这里调用python dynamic inventory script,因为terraform apply需要在之前完成

    在我看来,更好的方法是在这里运行ansible provisioner

    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" 
          }
        }
      }
    }
    

    这里只有drawbag: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.
    

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

    使用动态清单手动运行 Ansible

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

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

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

    ansible-playbook -i /etc/ansible/terraform.py ansible-test.yml
    

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

    在我的用例(Provision Rancher testcluster)中,null_ressource 方法似乎更好,因为一切都是使用单个 Terraform 命令构建的。无需额外执行 Ansible。但是根据要求,最好将 Ansible 保留一个单独的步骤,因此我将其发布为替代方案。

    安装插件

    在尝试这个方案的时候,记住需要从here安装对应的Terraform插件:

    version=0.0.4
    wget https://github.com/nbering/terraform-provider-ansible/releases/download/v${version}/terraform-provider-ansible-linux_amd64.zip -O terraform-provisioner-ansible.zip
    unzip terraform-provisioner-ansible.zip
    chmod +x linux_amd64/*
    mv linux_amd64 ~/.terraform.d/plugins
    

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 这个可以和dynamic inventory plugin一起使用吗?我手动尝试了这个插件,在ansible-playbook 执行之前似乎需要terraform apply。因此,我不确定是否可以将对 Python 脚本的调用集成到 Terraform 资源本身中。
      • 我自己没有看过那个插件,但是通过快速阅读您用作库存文件的 terraform.py 脚本,应该仍然可以从local-exec 调用。 local-exec 的行为就像您从普通 cli 运行某些东西一样。绝对值得测试!
      • 不起作用,但我根据您的方法找到了另一种方法。发布了我测试过的两种方法
      【解决方案3】:

      你可以试试这个,它对我有用。

      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 playbook。

      【讨论】:

        猜你喜欢
        • 2021-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-28
        • 1970-01-01
        • 2019-10-19
        • 1970-01-01
        • 2022-01-24
        相关资源
        最近更新 更多