この記事はニフティグループ Advent Calendar 2019の13日目の記事です。
昨日は@hmmrjnさんの「WebページもOSのDark Modeに対応できる」でした。
個人的にDark Modeは大好きなので、対応がしやすくなるのはとても嬉しいですね。
はじめに
近年、TerraformのようなオーケストレーションツールやChef・Ansibleといった構成管理ツールの利用により、リソースの構築・管理がより容易になってきています。
私も今更ながらAnsibleを扱い始めました。そのなかで、クラウドリソースはIPアドレスが決まっていないことが多いため、インベントリの扱いが非常に面倒なことがありました。
この記事では、Terraformで立てたec2インスタンスに対して、Ansibleで用途ごとにグループ分けして構成管理することを目標としています。
これを達成する手段の一つとして、AnsibleのInventory Pluginを利用してみました。
環境
- OS: macOS Catalina 10.15.1
- Ansible: 2.9.2
- Terraform: v0.12.18
ディレクトリ構成
今回は簡単化のため、以下に示す最小限のディレクトリ構成で進めていきます。
実際に利用する際は各ツールのベストプラクティスに従ってください。
.
├── ansible
│ ├── group_vars
│ │ └── aws_ec2.yml
│ ├── inventory
│ │ └── aws_ec2.yml
│ └── site.yml
└── terraform
├── ec2.tf
├── main.tf
└── vpc.tf
前準備
Ansibleで管理する対象サーバーをTerraformで作成します。
tfファイル作成
今回はタグのみが異なる3つのec2インスタンスを作成するように記述します。
各ec2インスタンスに対するタグの付け方は以下のようになっています。
- タグなし
- Nameタグ: "web-ec2"、Webserverタグ: "httpd"
- Nameタグ: "web-ec2"、Webserverタグ: "nginx"
このタグの付け方がAnsibleのインベントリを作成する上で重要になってきます。
terraform/main.tfの内容
provider "aws" {
version = "~> 2.41"
region = "ap-northeast-1"
}
terraform/ec2.tfの内容
resource "aws_security_group" "web_sg" {
vpc_id = aws_vpc.example.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "example" {
ami = "ami-068a6cefc24c301d2"
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
key_name = "my-aws-key"
vpc_security_group_ids = [aws_security_group.web_sg.id]
}
resource "aws_instance" "httpd" {
ami = "ami-068a6cefc24c301d2"
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
key_name = "my-aws-key"
vpc_security_group_ids = [aws_security_group.web_sg.id]
tags = {
Name = "web-ec2"
Webserver = "httpd"
}
}
resource "aws_instance" "nginx" {
ami = "ami-068a6cefc24c301d2"
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
key_name = "my-aws-key"
vpc_security_group_ids = [aws_security_group.web_sg.id]
tags = {
Name = "web-ec2"
Webserver = "nginx"
}
}
terraform/vpc.tfの内容
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.example.id
cidr_block = "10.0.0.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-northeast-1a"
}
resource "aws_internet_gateway" "example" {
vpc_id = aws_vpc.example.id
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.example.id
}
resource "aws_route" "public" {
route_table_id = aws_route_table.public.id
gateway_id = aws_internet_gateway.example.id
destination_cidr_block = "0.0.0.0/0"
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
リソース作成
terraform plan
で確認後、間違えがなければterraform apply
を実行します。
$ cd terraform
$ terraform init
$ terraform plan
$ terraform apply
インベントリ作成
Ansibleでサーバを管理するためには、各サーバのIPアドレスを知らなければいけません。しかし、ec2インスタンスのようなクラウドリソースは作成するたびに、IPアドレスが変化します。また、WEBサーバやDBサーバといった用途ごとに使い分ける場合、グループ分けをする必要があります。これらを手動でインベントリに記述していくことは大変だと思います。
そこで、Dynamic InventoryやInventory Pluginを利用することで、取得した情報から動的にインベントリを作成することができます。今回はInventory Pluginのうち、AWS EC2用プラグインを利用します。
Inventory Plugins — Ansible Documentation
aws_ec2 – EC2 inventory source
aws_ec2 プラグイン
aws_ec2プラグインにより、AWS上にあるec2インスタンスに対するインベントリをコンパイルできます。
以下のようにファイルを記述することで、各ec2インスタンスをタグやインスタンスタイプ等でグループ分けできます。
ここで注意が必要なのは、ファイル名のサフィックスがaws_ec2.(yml|yaml)
である必要があります。
プラグインを利用したファイルは通常のインベントリファイルと同様にansible-playbook
などで扱うことができます。
plugin: aws_ec2
regions:
- ap-northeast-1
strict: no
keyed_groups:
# Nameタグの値により、グループを分類。グループ名のプレフィックスに"tag_Name_"を付与
- key: tags.Name
prefix: tag_Name_
separator: ""
# インスタンスタイプ(t2.microなど)により、グループを分類
- key: instance_type
prefix: instance_type
# Webserverタグの値により、グループを分類
- key: tags.Webserver
separator: ""
groups:
# Nameタグのプレフィックスが"web-"のとき、webグループに分類
web: "tags.Name is match('^web-')"
# ホストごとの変数の設定。
# ここではグローバル経由でアクセスするため、`ansible_host`変数にグローバルIPを格納する。
compose:
ansible_host: global_ip_address
インベントリの確認
ansible-inventory
コマンドを利用することでプラグインから作成されたグループ構成を確認することができます。
$ ansible-inventory -i inventory/aws_ec2.yml --graph
@all:
|--@aws_ec2:
| |--ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com
| |--ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com
| |--ec2-52-68-129-109.ap-northeast-1.compute.amazonaws.com
|--@httpd:
| |--ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com
|--@instance_type_t2_micro:
| |--ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com
| |--ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com
| |--ec2-52-68-129-109.ap-northeast-1.compute.amazonaws.com
|--@nginx:
| |--ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com
|--@tag_Name_web_ec2:
| |--ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com
| |--ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com
|--@ungrouped:
|--@web:
| |--ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com
| |--ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com
設定したタグやインスタンスタイプごとに、グループ分けができていることがわかります。
Playbookの実行
最後に、以下のPlaybookを実行して、グループごとに構成管理できるかを確認します。
httpdグループにapache、nginxグループにnginxをインストールして起動します。(このようなグループ分けはあまりなさそうですが、例のために行います)
---
- hosts: httpd
gather_facts: no
become: yes
tasks:
- yum:
name: httpd
state: latest
- systemd:
name: httpd
state: started
- hosts: nginx
gather_facts: no
become: yes
tasks:
- command: "amazon-linux-extras enable nginx1.12"
changed_when: false
- yum:
name: nginx
enablerepo: amzn2extra-nginx1.12
state: present
- systemd:
name: nginx
state: started
$ cd ansible
$ ansible-playbook -i inventory/aws_ec2.yml site.yml
PLAY [httpd] *********************************************************************************************************************************************
TASK [Install httpd] *************************************************************************************************************************************
changed: [ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com]
TASK [Start httpd] ***************************************************************************************************************************************
changed: [ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com]
PLAY [nginx] *********************************************************************************************************************************************
TASK [command] *******************************************************************************************************************************************
ok: [ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com]
TASK [yum] ***********************************************************************************************************************************************
changed: [ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com]
TASK [systemd] *******************************************************************************************************************************************
changed: [ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com]
PLAY RECAP ***********************************************************************************************************************************************
ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
curl
コマンドでサーバにアクセスしてみます。
それぞれapacheとnginxがインストールできていることがわかります。
$ curl -I ec2-13-113-228-112.ap-northeast-1.compute.amazonaws.com
HTTP/1.1 403 Forbidden
Date: Fri, 13 Dec 2019 14:30:29 GMT
Server: Apache/2.4.41 ()
Upgrade: h2,h2c
Connection: Upgrade
Last-Modified: Tue, 22 Oct 2019 22:56:48 GMT
ETag: "e2e-59587b710ac00"
Accept-Ranges: bytes
Content-Length: 3630
Content-Type: text/html; charset=UTF-8
$ curl -I ec2-52-199-119-151.ap-northeast-1.compute.amazonaws.com
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Fri, 13 Dec 2019 14:31:13 GMT
Content-Type: text/html
Content-Length: 3520
Last-Modified: Wed, 28 Aug 2019 19:52:13 GMT
Connection: keep-alive
ETag: "5d66db6d-dc0"
Accept-Ranges: bytes
おわりに
Inventory Pluginにより、Terraformで立てたec2サーバをAnsibleで管理することができました。予めリソースに対するタグの付け方をルール化することで、より良い構成管理ができると思います。
検証機や勉強会用サーバといった同一のサーバを立てたり、クラウド間の移行など、構成管理をしておくことは非常に重要だと思います。そのため、さらにベターなリソース管理を目指して頑張っていきたいです。