我想将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_ressource
和depends_on
链接到我的vm ressource,但是Terraform也没有检测到Ansible部分的更改。似乎是Terraform插件中的lack of change detection。
在文档中,我仅发现销毁时间供应者。但是没有更新。我可以销毁并重新创建机器。这会使事情变慢很多。我喜欢Ansible的方法,即检查已安装的内容并仅应用尚不存在的更改,这似乎是一种很好的配置方式。
根据我目前的经验(比Terraform更为Ansible),我没有其他方法可以丢弃漂亮的插件并自己执行Ansible。但这也会降低很好的集成度。因此,我需要自己甚至手动生成库存文件(在我看来,这缺少自动化方法)。
source_code_hash可能是一个选择,但不灵活:当有多个播放/角色时,我需要手动处理每个文件,这样容易出错。
答案 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"
}
}
}
}
根据其他解决方法,这似乎是对我最好的折衷方案。
我发现的另一种方法是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剧本。