はじめに
こんにちは、SREエンジニアの@hayaosatoです。
日々の業務では、インフラができるSREの一環として、Infrastracture As Codeを推進しています。
TerraformとAnsible
今回はインフラの定義からサーバ設定までをシュッと実行してみようと思います。
具体的には、AWSでサーバを作成する際にTerraformでインフラの定義を行なって、その後のサーバのミドルウェアのインストールなどをAnsibleで行います。
また、Terraformでサーバ作成した直後にそのままプロビジョニングとしてAnsibleを実行します。
コードはこちら。
Terraform
まずは、AWSでEC2インスタンスを作成します。
今回作成するインスタンスはUbuntu18.04の最新AMIから作成しようと思います。
Terraformおよびプロバイダのバージョンは以下の通りです。
Terraform v0.12.16
+ provider.aws v2.38.0
+ provider.local v1.4.0
AMI
最新のAMIのidを取得するためには以下のように取得します。
data "aws_ami" "latest-ubuntu" {
most_recent = true
owners = ["679593333241"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
取得したいAMIはAWS CLIのec2 describe-images
コマンドで確認することができるので、
他のOSで同様に最新イメージを取得したい場合は確認していただければと思います。
一応コードのec2_images.tf
にCentos7とAmazon Linux2の最新イメージの取得方法も記載してあります。
EC2
続いて、EC2インスタンスの作成ですが、以下のようになりました。
resource "aws_instance" "default" {
ami = data.aws_ami.latest-ubuntu.id
key_name = var.key_name
private_ip = var.private_ip
count = 1
security_groups = var.security_groups
subnet_id = var.subnet_id
instance_type = "t3.medium"
tags = {
Name = "terraform-ansible"
}
provisioner "remote-exec" {
connection {
type = "ssh"
user = "ubuntu"
host = var.private_ip
private_key = file("~/.ssh/${var.key_name}.pem")
}
inline = [
"sudo apt install -y python"
]
}
provisioner "local-exec" {
command = "ansible-playbook -i hosts ${var.service_name}.yml"
}
}
provisioner
より上部の各パラメータは公式ドキュメントを参照いただけると幸いです。
今回のAnsible実行はprovisioner
の箇所で実現されています。
terraformにはProvisioningの機能が様々あり、
その中でremote-exec
とlocal-exec
を利用しています。
remote-exec
ec2リソースのプロビジョニングであるremote-exec
はサーバが作成され次第SSHなどで接続してコマンドを実行したりファイルを配置したりすることができます。
今回はAnsibleの実行のためにPythonをインストールしています。
local-exec
local-exec
は文字通りterraformコマンドを実行する、ローカル環境で実行してくれるコマンドを指定することができます。
今回はそのままAnsibleコマンドを実行するように指定します。
また、ansibleコマンドの指定でハマった点としてコマンド指定を
command = "ansible-playbook -i hosts ${var.service_name}.yml --private-key=~/.ssh/${var.key_name}.pem"
のようにしていたのですが、たまにterraform fmt
のフォーマッタで
command = "ansible-playbook -i hosts ${var.service_name}.yml --private-key = ~/.ssh/${var.key_name}.pem"
のように、--private-key
指定のイコールの前後にスペースを入れてくれてしまうので、
できれば~/.ansible.cfg
のprivate_key_file
の設定で対応したほうがいいです。
[defaults]
host_key_checking = False
private_key_file = /path/to/private_key_file
Ansbile
さて、ここまででTerraformでのEC2インスタンス作成の定義までできたと思います。
実際流すAnsibleの設定を作成していきます。
Ansibleのバージョンは以下の通りです。
ansible 2.7.15
今回のtaskはとてもシンプルにvimをaptでインストールするだけにします。
- name: install vim
become: yes
apt:
name: vim
また、playbookは以下のようにします。
- hosts: service
remote_user: ubuntu
roles:
- service
今回、ansibleのplaybookについての説明は省略します。
インベントリファイル
ここで、インベントリファイルについてですが、
hostsの設定はprivate_ipを記載するだけですが、terraform側で管理していればそれを流用するだけですので、
インベントリファイルもterraformで作ってしまおうと思います。
内容はとてもシンプルで以下の通りです。
ローカルでのファイルの作成はlocal
プロバイダのlocal_file
resource "local_file" "hosts_file" {
content = <<EOF
[${var.service_name}]
${var.private_ip}
EOF
filename = "hosts"
file_permission = "0644"
}
このようにterraformで管理することによって、プライベートIPを変える場合に連動してくれるので良いなと思いました。
実行
最後に実行する際にterraformのvarを設定して
terraform apply -var-file sample.tfvars
のように実行するとec2インスタンスを作成してそのサーバに向けてansibleを実行してくれる構成ができました。
最後に
今回の構成はTerraformでインスタンスを作成してそのままAnsibleを実行したい人向けに作ってみました。
そもそもAnsibleでは冪等性が担保されているように構成すると思うので、そこまで必要性は感じませんが、
そもそものAnsibleの設定を作成する時に少しでも使えるといいなぁと思ってやってみました。
また、上記の構成で「こうしたほうがいいだろ!」等のご指摘あれば是非ともよろしくお願いいたします。