Terraform의 provisioner와 Ansible 연동하기
이전 글에서 Terraform의 provisioner를 설명했는데 간단한 프로비저닝은 local-exec나 remote-exec를 사용하면 되지만 보통은 프로비저닝할 때 더 많은 작업을 해야 하므로 이 둘만으로는 충분하지 않고 개인적으로 Terraform에서 전부 다 하는 게 좋다고 보지도 않는다. 프로비저닝을 할 수 있는 도구가 많지만, 최근에 내가 많이 써보려고 하는 Ansible과 연동을 해보고 있다.
일단 EC2 인스턴스를 생성해야 하므로 다음과 같은 간단한 tf 파일을 만든다.
1resource "aws_instance" "demo" {
2 ami = "ami-d39a02b5" # ubuntu 16.04
3 instance_type = "t2.nano"
4 subnet_id = "subnet-xxxxxxxx"
5 security_groups = ["sg-xxxxxxxx"]
6 key_name = "your-key-pair"
7
8 connection {
9 user = "ubuntu"
10 type = "ssh"
11
12 private_key = "${file("~/.ssh/your_private_key.pem")}"
13 timeout = "2m"
14 }
15
16 provisioner "remote-exec" {
17 inline = [
18 "sudo apt-get update",
19 "sudo apt-get install -y python",
20 ]
21 }
22}
이전 글에서 본 내용과 거의 같으므로 자세한 내용은 이전 글을 참고한다. 여기서는 Ubuntu를 사용했는데 접속한 뒤 remote-exec로 Python만 설치했다. Ansible을 사용할 때 타겟 머신에 Python은 설치되어 있어야 한다. 없으면 MODULE FAILURE 오류가 발생한다.
1resource "aws_instance" "demo" {
2 # 생략
3 provisioner "remote-exec" {
4 # 생략
5 }
6
7 provisioner "local-exec" {
8 command = <<EOF
9 echo "[demo]" > inventory
10 echo "${aws_instance.demo.public_ip} ansible_ssh_user=ubuntu ansible_ssh_private_key_file=~/.ssh/your_private_key.pem" >> inventory
11 EOF
12 }
13
14 provisioner "local-exec" {
15 command = <<EOF
16 ANSIBLE_HOST_KEY_CHECKING=False \
17 ansible-playbook -i inventory playbook.yml
18 EOF
19 }
20}
원격 서버 설정은 끝났으므로 이번에는 local-exec를 사용했다. 2번 사용한 것은 그냥 긴 명령어를 보기 좋게 작성하기 위해서다. 로컬에서 Ansible을 실행해서 원격 서버에 접속하는 것이므로 local-exec를 사용한 것이다. 이 말은 로컬 머신에는 Ansible이 설치되어 있어야 한다는 의미이다.
위 명령어를 좀 더 설명하면 Ansible이 서버를 인식할 수 있도록 inventory 파일을 생성한 것이다. 서버를 생성하면서 만들어진 퍼블릭 IP를 이용해서 필요한 파라미터를 지정해서 인벤토리 파일을 만드는데 아래와 같은 형태가 된다. 서버를 만들 때마다 IP가 바뀌므로 자동으로 생성되게 한 것이다.
1[demo]
254.95.180.208 ansible_ssh_user=ubuntu ansible_ssh_private_key_file=~/.ssh/your_private_key.pem
두 번째로 나온 local-exec에서 실제로 ansible-playbook을 실행하는데 ANSIBLE_HOST_KEY_CHECKING=False 환경변수는 SSH로 접속할 때 호스트 서버를 확인하는 프롬프트가 나오지 않도록 지정한 것이다. 이게 있으면 terraform apply 중에 프롬프트를 기다리느라 결국 실패하거나 사용자가 입력을 해야 하게 된다. ansible-playbook -i inventory playbook.yml에서 앞에서 만든 인벤토리 파일을 지정하고 실제 프로비저닝의 내용이 있는 playbook.yml을 사용하게 했다. 이 글에서 playbook.yml의 내용은 중요치 않지만, 예제를 위해 다음과 같이 git과 nginx를 설치하도록 간단히 작성했다.
1---
2- hosts: demo
3
4 tasks:
5 - name: ping
6 ping:
7
8 - name: install packages
9 apt: name={{ item }} update_cache=yes state=latest
10 with_items:
11 - git
12 become: true
13
14 - name: install nginx
15 apt: name={{ item }} update_cache=yes state=latest
16 with_items:
17 - nginx
18 become: true
이 테라폼 파일을 실행하면 아래와 같이 Ansible과 자동으로 연동되어 git과 nginx를 설치하는 것을 볼 수 있다.
1$ terraform apply
2
3aws_instance.demo: Creating...
4 ami: "" => "ami-d39a02b5"
5 associate_public_ip_address: "" => "<computed>"
6 availability_zone: "" => "<computed>"
7 ebs_block_device.#: "" => "<computed>"
8 ephemeral_block_device.#: "" => "<computed>"
9 instance_state: "" => "<computed>"
10 instance_type: "" => "t2.nano"
11 ipv6_address_count: "" => "<computed>"
12 ipv6_addresses.#: "" => "<computed>"
13 key_name: "" => "outsider-aws"
14 network_interface.#: "" => "<computed>"
15 network_interface_id: "" => "<computed>"
16 placement_group: "" => "<computed>"
17 primary_network_interface_id: "" => "<computed>"
18 private_dns: "" => "<computed>"
19 private_ip: "" => "<computed>"
20 public_dns: "" => "<computed>"
21 public_ip: "" => "<computed>"
22 root_block_device.#: "" => "<computed>"
23 security_groups.#: "" => "1"
24 security_groups.541019735: "" => "sg-247b2042"
25 source_dest_check: "" => "true"
26 subnet_id: "" => "subnet-2784b551"
27 tenancy: "" => "<computed>"
28 volume_tags.%: "" => "<computed>"
29 vpc_security_group_ids.#: "" => "<computed>"
30aws_instance.demo: Still creating... (10s elapsed)
31aws_instance.demo: Provisioning with 'remote-exec'...
32aws_instance.demo (remote-exec): Connecting to remote host via SSH...
33aws_instance.demo (remote-exec): Host: 54.95.180.208
34aws_instance.demo (remote-exec): User: ubuntu
35aws_instance.demo (remote-exec): Password: false
36aws_instance.demo (remote-exec): Private key: true
37aws_instance.demo (remote-exec): SSH Agent: true
38aws_instance.demo (remote-exec): Connected!
39aws_instance.demo (remote-exec): Setting up libpython2.7-stdlib:amd64 (2.7.12-1ubuntu0~16.04.3) ...
40aws_instance.demo (remote-exec): Setting up python2.7 (2.7.12-1ubuntu0~16.04.3) ...
41aws_instance.demo (remote-exec): Setting up libpython-stdlib:amd64 (2.7.11-1) ...
42aws_instance.demo (remote-exec): Setting up python (2.7.11-1) ...
43aws_instance.demo: Provisioning with 'local-exec'...
44aws_instance.demo (local-exec): Executing: ["/bin/sh" "-c" " echo \"[demo]\" > inventory\n echo \"54.95.180.208 ansible_ssh_user=ubuntu ansible_ssh_private_key_file=~/.ssh/outsider-aws.pem\" >> inventory\n "]
45aws_instance.demo: Provisioning with 'local-exec'...
46aws_instance.demo (local-exec): Executing: ["/bin/sh" "-c" " ANSIBLE_HOST_KEY_CHECKING=False \\\n ansible-playbook -i inventory playbook.yml\n "]
47aws_instance.demo: Still creating... (40s elapsed)
48
49aws_instance.demo (local-exec): PLAY [demo] ********************************************************************
50
51aws_instance.demo (local-exec): TASK [Gathering Facts] *********************************************************
52aws_instance.demo (local-exec): ok: [54.95.180.208]
53
54aws_instance.demo (local-exec): TASK [ping] ********************************************************************
55aws_instance.demo (local-exec): ok: [54.95.180.208]
56
57aws_instance.demo (local-exec): TASK [install packages] ********************************************************
58aws_instance.demo (local-exec): ok: [54.95.180.208] => (item=['git'])
59
60aws_instance.demo (local-exec): TASK [install nginx] ***********************************************************
61aws_instance.demo (local-exec): changed: [54.95.180.208] => (item=['nginx'])
62
63aws_instance.demo (local-exec): PLAY RECAP *********************************************************************
64aws_instance.demo (local-exec): 54.95.180.208 : ok=4 changed=1 unreachable=0 failed=0
65
66aws_instance.demo: Creation complete after 55s (ID: i-0116967d4db9e1946)
67
68Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
이제 이후부터는 ansible을 어떻게 사용할지만 고민하면 된다.
Comments