ansible - 检测 Terraform 中的 Ansible 更改并执行它们
问题描述
我想将 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 运行,它会安装新包。
问题似乎是供应商本身:
创建时供应商仅在创建期间运行,而不是在更新或任何其他生命周期期间运行。它们旨在作为执行系统引导的一种手段。
但是应用更新的正确方法是什么?我尝试了指向我的 vm 资源的null_ressource
withdepends_on
链接,但 Terraform 也没有检测到 Ansible 部分的更改。似乎缺少Terraform 插件的更改检测。
在文档中,我只发现破坏时间供应商。但没有更新。我可以摧毁并重新创造这台机器。这会使事情变慢很多。我喜欢 Ansible 检查什么是存在的方法,并且只应用尚未存在的更改,这似乎是一种很好的配置方式。
不能用 Terraform 做类似的事情吗?
以我目前的经验(比 Terraform 更 Ansible),我认为没有其他方法可以放弃漂亮的插件并自己执行 Ansible。但这也会放弃良好的集成。所以我需要自己甚至手动生成库存文件(在我看来这错过了自动化方法)。
source_code_hash可能是一个选项,但不灵活:当有多个播放/角色时,我需要为每个容易出错的单个文件手动执行此操作。
解决方案
使用null_ressource
带有伪触发器的
tedsmitt的想法使用时间戳作为触发器,这似乎是强制提供者的唯一方法。ansible-playbook
但是,从 CLI直接运行会产生手动维护库存的开销。您不能从这里调用python 动态库存脚本,因为 terraform apply
需要先完成
在我看来,更好的方法是在这里运行ansible 配置器:
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"
}
}
}
}
这里唯一的亮点是: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
我发现的另一种方法是动态库存插件,详细说明可以在此博客条目中找到。它集成到 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 保留一个单独的步骤,因此我将其发布为替代方案。
安装插件
尝试此解决方案时,请记住您需要从此处安装相应的 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
还要注意,需要首先删除上述解决方案中的自动配置器,因为它具有相同的名称(可能冲突)。
推荐阅读
- django - 在 TravisCI 中运行 dockerized django 应用程序时,coveralls 无法识别令牌,但只能来自拉取请求
- android - Android Studio中自定义视图中的文本?
- opengl - 这是什么类型的时间纹理过滤,我该如何禁用它?
- google-sheets - 排序范围忽略谷歌表格上空白的“”风格
- database - SQLite 函数插入日期 YYYY-MM-DD 而不是 YYYYMMDD
- php - 如果在函数之前已经定义了变量,我该如何修复 php 未定义的变量?
- java - Сannot 从 NamedParameterJdbcTemplate 获取 Float 数据类型
- c - 如何为您的程序设置内存上限
- javascript - javascript 检查字符串是否以某些字符开头并删除这些字符
- java - 我的应用程序在构建项目 Android Studio 期间崩溃且没有错误