Terraform 整合Ansible 實戰(remote)
前言
Terraform 和 Ansible 是兩個獨立的工具,有各自的用途,但它們可以整合以解決典型用例以及它們相互補充的方式,這使得它們更加受歡迎。
一 工具簡介
1.1 Terraform
它是作為程式碼(IaC)軟體工具的基礎設施,用於構建、更改和版本控制基礎設施。它與500多個供應商合作,這些供應商的資源可用於提供基礎設施。
1.2 Ansible
它是一種配置管理工具,可在已設定的基礎架構上配置和部署應用程式時派上用場。在本文中,我們使用 Ansible 配置了 terraform 部署的基礎設施。
本文將使用Terraform對騰訊雲基礎設施資源進行編排管理,使用Ansible對主機例項進行配置管理。
二 架構及流程
總體方案使用terraform來進行雲上基礎設施編排,使用ansible進行主機或其他設施的配置管理。
編排出一臺獨立的遠端ansible manager 主機作為ansible 配置管理的管理伺服器,需要後續在其上進行ansible 配置、金鑰下發,及生成管控目標伺服器的invertory主機清單,對需要管控的node節點需要在該伺服器進行操作。
三 實戰
3.1 編排Ansible manager節點
利用本地金鑰編排ansible節點
- 安裝ansible
- 將ansible的配置檔案傳輸到ansible manger的特定目錄
```shell locals { cidr_block_vpc_name = "10.0.0.0/16" cidr_block_subnet_name = "10.0.1.0/24" count = 1 username = "root" local_key_file = "id_rsa" local_ansible_config = "ansible.cfg" ansible_dir = "/opt/ansibleworkspace/" key_file = "/Users/xuel/.ssh/id_rsa" }
data "tencentcloud_images" "image" { image_type = ["PUBLIC_IMAGE"] os_name = "centos 7.5" }
data "tencentcloud_instance_types" "instanceType" { cpu_core_count = 1 memory_size = 1 filter { name = "instance-family" values = ["S3"] } }
data "tencentcloud_availability_zones" "zone" {}
resource "tencentcloud_vpc" "vpc" { cidr_block = local.cidr_block_vpc_name name = "xuel_tf_vpc" }
resource "tencentcloud_subnet" "subnet" { availability_zone = data.tencentcloud_availability_zones.zone.zones.0.name cidr_block = local.cidr_block_subnet_name name = "xuel_tf_subnet" vpc_id = tencentcloud_vpc.vpc.id }
resource "tencentcloud_key_pair" "xuel-tf-key" { key_name = "xuel_tf_ansible_manager" public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCcJxzfjqQDikRRShExu22kvNOfMKeWrn++s5nQMTm+9SNsQISEk+P15JhFVachgjumupMcOIrfJQAAQcnzNmxoRCTCJQfmegGpDZVpE1cyHUhGMA5kwu67PmK1Snm8hkg2Xzlduhr1xysL2mRn3+6o5tsFXhGrYOcSSXnf5SpTPgMjqo339ksH0iv8kvu3NaZRueygLYaVEMjixJvsnUisL3uY8LQ+4cm2Zu5mdQamhWhN0kkSdlfbjPgzxexL4AglD9YDy4I9Q80vKzy33Ubwo17a2aNCF3uPpYvCKiV0H9z2XtMxisKDfsQQA01Q1vpccUIK6L48xSbersxxxxxxxxxxxxxxxxxi1SGabjYLsv23ki6EMGjM/AK+fq+vj3pIPUMpscX3xVDGmz/zusq6v1KfOtQw7B/Dg8c2cxKUlEWZqqC3A7rt3JO/RVEbeqSe5mlRm2yngINVemmhkcfZNs= [email protected]" }
resource "tencentcloud_instance" "xuel_tf_ansible" { availability_zone = data.tencentcloud_availability_zones.zone.zones[0].name image_id = data.tencentcloud_images.image.images[0].image_id instance_type = data.tencentcloud_instance_types.instanceType.instance_types[0].instance_type vpc_id = tencentcloud_vpc.vpc.id subnet_id = tencentcloud_subnet.subnet.id // allocate_public_ip = true system_disk_size = 50 system_disk_type = "CLOUD_PREMIUM" key_name = tencentcloud_key_pair.xuel-tf-key.id allocate_public_ip = true internet_max_bandwidth_out = 2
data_disks { data_disk_size = 50 data_disk_type = "CLOUD_PREMIUM" } hostname = format("xuel-tf-ansible-manager-%d", count.index) instance_charge_type = "POSTPAID_BY_HOUR" count = local.count instance_name = format("xuel-tf-server-%d", count.index)
tags = { tagkey = "xuel-ansible-manager" } }
// ansible 伺服器部署ansible resource "null_resource" "shell" { depends_on = [tencentcloud_instance.xuel_tf_ansible] count = local.count
triggers = { instance_ids = element(tencentcloud_instance.xuel_tf_ansible..id, count.index) } provisioner "remote-exec" { connection { host = element(tencentcloud_instance.xuel_tf_ansible..public_ip, count.index) type = "ssh" user = "root" private_key = file("${local.key_file}") }
inline = [
// 安裝nginx
"echo index.html > /xueltf.txt",
"yum -y install nginx",
"echo tf-nginx > /usr/share/nginx/html/index.html",
"systemctl start nginx",
"systemctl status nginx",
// 安裝ansible
"yum install epel-release -y",
"yum install ansible -y",
// 配置ansible目錄
format("[ ! -d %s ] && mkdir -pv %s || echo 'dir create ok'", local.ansible_dir,local.ansible_dir)
]
} }
// 拷貝ansible檔案至ansible 管理節點 resource "null_resource" "file_copy" { depends_on = [null_resource.shell]
connection { host = tencentcloud_instance.xuel_tf_ansible.0.public_ip user = local.username type = "ssh" private_key = file("${local.key_file}") }
// ansible 配置檔案 provisioner "file" { source = local.local_ansible_config destination = format("${local.ansible_dir}/%s", local.local_ansible_config) } }
output "summary" { value = { // image = {for k,v in data.tencentcloud_images.image:k=>v} // instanceType = {for k,v in data.tencentcloud_instance_types // .instanceType:k=>v} // zone = {for k,v in data.tencentcloud_availability_zones.zone: k=>v} instance = { for k, v in tencentcloud_instance.xuel_tf_ansible : k => v }
ip = tencentcloud_instance.xuel_tf_ansible.0.public_ip
} } ```
3.2 編排node節點
- 利用本地金鑰編排多臺node節點
- 從ansible_manager 的 terraform_remote_state 獲取ansible manger的ip地址資訊,用於傳輸node節點的IP.txt 列表檔案至ansible
- 將本地編排node節點的ansible playbook指令碼上次至目標伺服器。
- 將建立node節點的私鑰傳輸到ansible,供後續ansible免密登入node節點執行ansible playbook
```shell locals { cidr_block_vpc_name = "10.0.0.0/16" cidr_block_subnet_name = "10.0.1.0/24" count = 3 local_ip_file = "ip.txt" ansible_dir = "/opt/ansibleworkspace/"
// 本地ansible play_bookfile = "instance.yaml" local_ansible_playbook_dir = "ansible_playbook" key_file = "id_rsa"
}
// 獲取ansible_manager 的公網IP data "terraform_remote_state" "ansible_manager_ip" { backend = "cos" config = { region = "ap-beijing" bucket = "tfproject-1253329830" prefix = "ansible_manager/default" } }
data "tencentcloud_images" "image" { image_type = ["PUBLIC_IMAGE"] os_name = "centos 7.5" }
data "tencentcloud_instance_types" "instanceType" { cpu_core_count = 1 memory_size = 1 filter { name = "instance-family" values = ["S3"] } }
data "tencentcloud_availability_zones" "zone" {}
resource "tencentcloud_vpc" "vpc" { cidr_block = local.cidr_block_vpc_name name = "xuel_tf_vpc" }
resource "tencentcloud_subnet" "subnet" { availability_zone = data.tencentcloud_availability_zones.zone.zones.0.name cidr_block = local.cidr_block_subnet_name name = "xuel_tf_subnet" vpc_id = tencentcloud_vpc.vpc.id }
// 定義公鑰 resource "tencentcloud_key_pair" "xuel-tf-key" { key_name = "xuel_node_key" public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCcJxzfjqQDikRRShExu22kvNOfMKeWrn++s5nQMTm+9SNsQISEk+P15JhFVachgjumupMcOIrfJQAAQcnzNmxoRCTCJQfmegGpDZVpE1cyHUhGMA5kwu67PmK1Snm8hkg2Xzlduhr1xysL2mRn3+6o5tsFXhGrYOcSSXnf5SpTPgMjqo339ksH0iv8kvu3NaZRueygLYaVEMjixJvsnUisL3uY8LQ+4cm2Zu5mdQamhWhN0kkSdlfbjPgzxexL4AglD9YDy4I9Q80vKzy33Ubwo17a2aNCF3uPpYvCKiV0H9z2XtMxisKDfsQQA01Q1vpccUIK6L48xSbers8hV2xxpSEWzEuoZg18eG2ikAencA6mhGjFWcp9A1dllY2rUhcEdrjcjXji1SGabjYLsv23ki6EMGjM/AK+fq+vj3pIPUMpscX3xVDGmz/zusq6v1KfOtQw7B/Dg8c2cxKUlEWZqqC3A7rt3JO/RVEbeqSe5mlRm2yngINVemmhkcfZNs= [email protected]" }
resource "tencentcloud_instance" "xuel_tf_ansible_node" { availability_zone = data.tencentcloud_availability_zones.zone.zones[0].name image_id = data.tencentcloud_images.image.images[0].image_id instance_type = data.tencentcloud_instance_types.instanceType.instance_types[0].instance_type vpc_id = tencentcloud_vpc.vpc.id subnet_id = tencentcloud_subnet.subnet.id // allocate_public_ip = true system_disk_size = 50 system_disk_type = "CLOUD_PREMIUM" key_name = tencentcloud_key_pair.xuel-tf-key.id allocate_public_ip = true internet_max_bandwidth_out = 2
data_disks { data_disk_size = 50 data_disk_type = "CLOUD_PREMIUM" } hostname = format("xuel-tf-server-%d", count.index) instance_charge_type = "POSTPAID_BY_HOUR" count = local.count instance_name = format("xuel-tf-server-%d", count.index)
tags = { name = "xuel_tf_ansibles" } }
// 生成IP地址檔案 resource "local_file" "ip_local_file" { filename = local.local_ip_file content = join("\n", tencentcloud_instance.xuel_tf_ansible_node.*.public_ip) }
// 第一步,將生產的公網IP地址傳送給遠端ansible伺服器 resource "null_resource" "copy_file" { depends_on = [tencentcloud_instance.xuel_tf_ansible_node]
connection { type = "ssh" host = data.terraform_remote_state.ansible_manager_ip.outputs.summary.ip user = var.ansible_user private_key = file("/Users/xuel/.ssh/id_rsa") }
// 生成的目標伺服器的ip地址 provisioner "file" { source = local.local_ip_file destination = format("${local.ansible_dir}/%s", local.local_ip_file) }
// 到目標伺服器執行的ansible playbook provisioner "file" { source = format("%s/%s", local.local_ansible_playbook_dir,local.play_bookfile ) destination = format("${local.ansible_dir}/%s", local.play_bookfile) }
// 到目標伺服器的金鑰 provisioner "file" { source = "/Users/xuel/.ssh/id_rsa" destination = "${local.ansible_dir}/${local.key_file}" }
// 目標伺服器進行授權 provisioner "remote-exec" { inline = [ "chmod 600 ${local.ansible_dir}/${local.key_file}" ] }
}
// 第二步,登入ansible 控制節點執行ansible playbook resource "null_resource" "exec_ansible_playbook" { depends_on = [null_resource.copy_file]
provisioner "remote-exec" {
connection {
type = "ssh"
host = data.terraform_remote_state.ansible_manager_ip.outputs.summary.ip
user = var.ansible_user
private_key = file("/Users/xuel/.ssh/id_rsa")
}
inline = [
"cd ${local.ansible_dir}",
"ansible-playbook ${local.play_bookfile}"
]
} } ```
在對node節點編排出來後,使用以下ansible playbook進行對伺服器執行配置管理,主要內容為安裝http,git並啟動http服務,對資料盤/dev/vdb進行格式化掛在,並在其寫入html檔案
```yaml - name: integration of terraform and ansible hosts: all
tasks: - name: installing httpd package: name: httpd state: present - name: installing php package: name: php state: present - name: starting httpd service service: name: httpd state: started - name: installing git package: name: git state: present - name: formatting storage filesystem: fstype: ext4 dev : /dev/vdb - name: making folder file: path: /var/www/html/web state: directory
- name: mounting storage
mount:
fstype: ext4
src: /dev/vdb
path: /var/www/html
state: mounted
- name: create html file
ansible.builtin.shell:
cmd: echo "xuel tf ansible page" > index.html
chdir: /var/www/html
```
四 測試
本實戰將backend儲存在騰訊雲cos中,workspace使用default
4.1 編排ansible manger節點
進行編排ansible manger節點
檢視儲存在cos中的state檔案
登入編排出來的ansible manger檢視資訊
4.2 編排node節點
再次編排三臺node節點。
檢視儲存在cos中的state狀態檔案
登入任意伺服器進行檢視情況,http已經啟動,並且磁碟已經格式化並掛載。
檢視web頁面
其他
- 在寫入多個ip列表時
content = join("\n", tencentcloud_instance.xuel_tf_ansible_node.*.public_ip)
- ansible 的配置檔案中定義了inventory 和 private_key_file 的路徑
shell
[defaults]
inventory = /opt/ansibleworkspace/ip.txt
host_key_checking= False
private_key_file=/opt/ansibleworkspace/id_rsa
remote_user=root
[privilege_escalation]
become=true
become_method=sudo
become_user=root
become_ask_pass=false
後期可以結合將編排出的主機進行分組,並配合git流程實現基於Gitlab 的DevOPS流程。
參考連結
- https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource
其他
- 我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿。
- 雲原生架構之配置中心-總述
- 雲原生架構之Spring Cloud Kubernetes配置中心方案
- 雲原生架構之SpringCloudKubernetes 服務註冊發現方案(東西流量)
- 雲原生架構之服務發現與註冊-Kubernetes中服務註冊發現
- 雲原生架構之Springboot Gateway K8s 服務註冊發現方案(南北流量)
- 雲原生架構之SpringBoot K8s service服務註冊發現方案(東西流量)
- 雲原生架構之服務發現與註冊-總述
- 雲原生備份恢復工具Velero二開實戰
- Kubernetes安全之KubeEye
- Terraform 整合Ansible 實戰(remote)
- Terraform Gitlab CI簡單整合方案
- 實戰Kubernetes Gitlab CI
- Kubernetes叢集中流量暴露的幾種方案
- Kubectl外掛開發及開源釋出分享
- Client-go原始碼分析之SharedInformer及實戰
- TAPD 整合 GitLab
- Gin框架優雅關機和重啟
- Gin Swagger快速生成API文件
- Golang 視覺化工具之go-callvis
- 跨平臺資料備份工具之restic詳解