こんにちは。最近、Terraform Associateが無事に取れたので、より普段書いているコードへの理解が深まってさらに楽しくなってきたところです。今回は、TerraformとAnsibleでGCEを作ったり、中身を設定する時の話です。
なにをしたいか
前々からTerraformとAnsibleでリソースの管理を行なっていますが、
- Terraformコマンドを実行してリソースの作成(ここではGCE)
- Ansibleでさらに色々流しこむ
と2段構えでコマンド実行するのがなかなか面倒なんですよね(ものぐさと言うんですが)。なので、Terraformのプロビジョナーとしてlocal-exec
があるので、これを使って、Terraformの実行のみでAnsibleまで完結させることを目標にやっていきます。
local-exec
について
ここで、簡単に今回使うプロビジョナーであるlocal-exec
について簡単に説明します。
resource "google_compute_instance" "instance" {
name = "sample-instance"
machine_type = "n1-standard-1"
zone = "asia-northeast1-a"
tags = []
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
}
}
network_interface {
network = "default"
}
service_account {
scopes = ["cloud-platform"]
}
provisioner "local-exec" {
command = "echo "Hello, World. >> hello.txt"
}
}
上のようにGCEを作ることを仮定します。一番下に
provisioner "local-exec" {
command = "echo "Hello, World. >> hello.txt"
}
を配置していますが、Terraformを実行しているマシンで実行するコマンドを該当のリソースの作成途中に実行することができます。なので、GCEが出来上がった頃にはプロビジョナーで実行されたコマンドも終わっているので、作業しているところにhello.txt
ができ上がっていることが確認できます。なので、今回はlocal-exec
プロビジョナーを使ってAnsibleを実行します。
実際に作る
今回は作ったインスタンスにnginxを仕込む簡単なAnsibleを考えます。
compute_instance.tf
ansible
ansible.cfg
inventory
nginx.yaml
sshkey
roles
nginx
tasks
main.yaml
のディレクトリ構成で考えます。
実行する
resource "google_compute_instance" "instance" {
name = "sample-instance"
machine_type = "n1-standard-1"
zone = "asia-northeast1-a"
tags = []
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
}
}
network_interface {
network = "default"
}
metadata = {
ssh-keys = "ansible:${file("./ansible/sshkey/id_rsa.pub")}"
}
service_account {
scopes = ["cloud-platform"]
}
provisioner "local-exec" {
working_dir = "./ansible/"
command = "chmod 400 sshkey/id_rsa && ansible-playbook -i inventory nginx.yaml"
}
}
Ansibleユーザーがコマンドを実行できるように事前にmetadata
で鍵を埋め込んでおきます。これでterraform plan
を実行すればnginxの入ったインスタンスが完成します!
落ちる
ところが、インスタンスを作成していると、sshはできているようだが、ansibleを実行できずに終わってしまいます。この時、Terraformとしては、リソースは完成しているが、全部設定完了できなかったことになるので、taint
がマークされます。なので、次回実行時は再作成になります。
改善する
結論、Terraformで作成完了したGCEをAnsibleで即時認識しようとしていたため起こった自称だと分かりました。改善するためには、sleep
コマンドを実行を挟むことで改善できました。
resource "google_compute_instance" "instance" {
name = "sample-instance"
machine_type = "n1-standard-1"
zone = "asia-northeast1-a"
tags = []
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
}
}
network_interface {
network = "default"
}
metadata = {
ssh-keys = "ansible:${file("./ansible/sshkey/id_rsa.pub")}"
}
service_account {
scopes = ["cloud-platform"]
}
provisioner "local-exec" {
working_dir = "./ansible/"
command = "chmod 400 sshkey/id_rsa && sleep 15 && ansible-playbook -i inventory nginx.yaml"
}
}
これで、Terraform内でAnsibleを実行できます。(sleepコマンドの期間は任意です)
何がおいしいのか
TerraformをAnsibleをコマンド分けて実行しているとき、Terraformはリソースの作成がゴールになります。しかし、local-exec
を使うことでミドルウェアがインストール完了するところまでがTerraformのゴールになります。そのため、よりインフラの状態を厳密に定義することができるのではないかと私は思います。
ただ、一方でTerraformの分界点、Ansibleの分界点それぞれあると思いますが、それを分けるためにlocal-exec
をAnsibleに使わないという選択肢もあると思います。