はじめに
本記事はAWSのEC2とRDSをTerraformをで構築する方法について記載しています。
terraform apply
で実行にかかる時間は、約3分程度です。カップラーメンの待ち時間で構築できます。
デプロイするのは以下の環境(※)になります。また、あわせてEC2は最低限のOSセットアップも行います。
Terraformの基本から入りたい方は、以前書いたOracle Cloudで始めるTerraform 自動化の真骨頂を参照ください。
(※)本記事で記載しているtfファイルはGitHubで公開しています。
Terraform構築
AWS環境におけるTerraformによる構築を行うためには、IAMでユーザ作成を行い、必要なクレデンシャル情報を用意します。あとはTerraformをインストールし、Terraform実行に必要なtfファイルを用意します。
AWS環境の前提条件を以下に記載します。
- IAMでユーザの作成
- 必要な権限を付与していること
- SSHで使用するキーペアを作成していること
tfファイルの作成
tfファイルについて解説します。
はじめに任意の作業ディレクトリに移動します。
本記事では以下のディレクトリ構成になり、カレントディレクトリはcommonディレクトリとします。なお、sshのディレクトリの場所については他のディレクトリでも問題ありません。
- ディレクトリ構成
.
|-- common
| |-- userdata
| |-- cloud-init.tpl
| |-- ec2.tf
| |-- env-vars
| |-- network.tf
| |-- provider.tf
| |-- rds.tf
`-- ssh
|-- id_rsa
|-- id_rsa.pub
- 各種ファイル説明
ファイル名 | 役割 |
---|---|
cloud-init.tpl | EC2用の初期構築スクリプト |
ec2.tf | EC2のtfファイル |
env-vars | プロパイダーで使用する変数のtfファイル |
network.tf | ネットワークのtfファイル |
provider.tf | プロパイダーのtfファイル |
rds.tf | RDSのtfファイル |
id_rsa | SSH秘密鍵 |
id_rsa.pub | SSH公開鍵 |
- env-vars
### Authentication
export TF_VAR_aws_access_key="<access_keyの中身をペースト>"
export TF_VAR_aws_secret_key="<secret_keyの中身をペースト>"
(※)引用符の中にはそれぞれaccess_keyとsecret_keyの中身をペーストします。
- provider.tf
# Variable
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {
default = "ap-northeast-1"
}
# Provider
provider "aws" {
access_key = var.aws_access_key
secret_key = var.aws_secret_key
region = "ap-northeast-1"
}
- network.tf
# vpc
resource "aws_vpc" "dev-env" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_hostnames = "false"
tags = {
Name = "dev-env"
}
}
# subnet
## public
resource "aws_subnet" "public-web" {
vpc_id = "${aws_vpc.dev-env.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "public-web"
}
}
## praivate
resource "aws_subnet" "private-db1" {
vpc_id = "${aws_vpc.dev-env.id}"
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "private-db1"
}
}
resource "aws_subnet" "private-db2" {
vpc_id = "${aws_vpc.dev-env.id}"
cidr_block = "10.0.3.0/24"
availability_zone = "ap-northeast-1c"
tags = {
Name = "private-db2"
}
}
# route table
resource "aws_route_table" "public-route" {
vpc_id = "${aws_vpc.dev-env.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.dev-env-gw.id}"
}
tags = {
Name = "public-route"
}
}
resource "aws_route_table_association" "public-a" {
subnet_id = "${aws_subnet.public-web.id}"
route_table_id = "${aws_route_table.public-route.id}"
}
# internet gateway
resource "aws_internet_gateway" "dev-env-gw" {
vpc_id = "${aws_vpc.dev-env.id}"
depends_on = [aws_vpc.dev-env]
tags = {
Name = "dev-env-gw"
}
}
- ec2.tf
# Security Group
resource "aws_security_group" "public-web-sg" {
name = "public-web-sg"
vpc_id = "${aws_vpc.dev-env.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"]
}
tags = {
Name = "public-web-sg"
}
}
resource "aws_security_group" "praivate-db-sg" {
name = "praivate-db-sg"
vpc_id = "${aws_vpc.dev-env.id}"
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["10.0.1.0/24"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "public-db-sg"
}
}
# EC2 Key Pairs
resource "aws_key_pair" "common-ssh" {
key_name = "common-ssh"
public_key = "<公開鍵の中身をペースト>"
}
# EC2
resource "aws_instance" "webserver" {
ami = "ami-011facbea5ec0363b"
instance_type = "t2.micro"
key_name = "common-ssh"
vpc_security_group_ids = [
"${aws_security_group.public-web-sg.id}"
]
subnet_id = "${aws_subnet.public-web.id}"
associate_public_ip_address = "true"
ebs_block_device {
device_name = "/dev/xvda"
volume_type = "gp2"
volume_size = 30
}
user_data = "${file("./userdata/cloud-init.tpl")}"
tags = {
Name = "webserver"
}
}
# Output
output "public_ip_of_webserver" {
value = "${aws_instance.webserver.public_ip}"
}
(※)Security Groupで記載しているcidr_blocksは例になります。実際に使用する場合はセキュリティを充分に配慮し、特にSSHは送信元を制限しましょう。
- rds.tf
# RDS
resource "aws_db_subnet_group" "praivate-db" {
name = "praivate-db"
subnet_ids = ["${aws_subnet.private-db1.id}", "${aws_subnet.private-db2.id}"]
tags = {
Name = "praivate-db"
}
}
resource "aws_db_instance" "test-db" {
identifier = "test-db"
allocated_storage = 20
storage_type = "gp2"
engine = "postgres"
engine_version = "11.5"
instance_class = "db.t3.micro"
name = "testdb"
username = "test"
password = "test"
vpc_security_group_ids = ["${aws_security_group.praivate-db-sg.id}"]
db_subnet_group_name = "${aws_db_subnet_group.praivate-db.name}"
skip_final_snapshot = true
}
(※)passwordの値は例として記載。使用不可な文字列もあります。
- cloud-init.tpl
#cloud-config
runcmd:
# ホスト名の変更
- hostnamectl set-hostname webserver
# パッケージのインストール
## セキュリティ関連の更新のみがインストール
- yum update --security -y
## PostgreSQL client programs
- yum install -y postgresql.x86_64
# タイムゾーン変更
## 設定ファイルのバックアップ
- cp -p /etc/localtime /etc/localtime.org
## シンボリックリンク作成
- ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
Terraform構築
はじめに、以下の準備作業を行います。
- 環境変数の有効化
$ source env-vars
- 環境変数の確認
$ env
準備作業完了後、いよいよTerraform構築です。
Terraform構築作業は次の3Stepです!
-
terraform init
で初期化 -
terraform plan
で確認 -
terraform apply
で適用
terraform
コマンドの説明については割愛します。
terraform apply
実行後、**Apply complete!**のメッセージが出力されると各リソースが作成されます。
RDSへはEC2インスタンスからpsqlを実行して接続するか、SSH経由でDBeaverなどのSQLクライアントから接続することができます。
terraform apply
plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_db_instance.test-db will be created
+ resource "aws_db_instance" "test-db" {
+ address = (known after apply)
+ allocated_storage = 20
+ apply_immediately = (known after apply)
+ arn = (known after apply)
+ auto_minor_version_upgrade = true
+ availability_zone = (known after apply)
+ backup_retention_period = (known after apply)
+ backup_window = (known after apply)
+ ca_cert_identifier = (known after apply)
+ character_set_name = (known after apply)
+ copy_tags_to_snapshot = false
+ db_subnet_group_name = "praivate-db"
+ endpoint = (known after apply)
+ engine = "postgres"
+ engine_version = "11.5"
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ identifier = "test-db"
+ identifier_prefix = (known after apply)
+ instance_class = "db.t3.micro"
+ kms_key_id = (known after apply)
+ license_model = (known after apply)
+ maintenance_window = (known after apply)
+ monitoring_interval = 0
+ monitoring_role_arn = (known after apply)
+ multi_az = (known after apply)
+ name = "testdb"
+ option_group_name = (known after apply)
+ parameter_group_name = (known after apply)
+ password = (sensitive value)
+ performance_insights_enabled = false
+ performance_insights_kms_key_id = (known after apply)
+ performance_insights_retention_period = (known after apply)
+ port = (known after apply)
+ publicly_accessible = false
+ replicas = (known after apply)
+ resource_id = (known after apply)
+ skip_final_snapshot = true
+ status = (known after apply)
+ storage_type = "gp2"
+ timezone = (known after apply)
+ username = "test"
+ vpc_security_group_ids = (known after apply)
}
/*中略*/
aws_db_instance.test-db: Still creating... [3m0s elapsed]
aws_db_instance.test-db: Creation complete after 3m5s [id=test-db]
Apply complete! Resources: 13 added, 0 changed, 0 destroyed.
Outputs:
public_ip_of_webserver = <IPアドレス(※)>
(※)EC2のパブリックIPが出力されます。
ナレッジ
AWS環境におけるリソース作成時の留意事項について以下に記載します。
- アベイラビリティゾーン
RDSの作成は複数のアベイラビリティゾーンの指定が必要です。単一では作成できません。本記事執筆時点で日本リュージョンの場合、以下から2つのアベイラビリティゾーンを指定する必要があります。
zones: ap-northeast-1c, ap-northeast-1a, ap-northeast-1d.
-
terraform destroy
実行時にRDSを削除したい場合
RDSはリソース削除時にデフォルトでスナップショットの作成が求められるため、terraform destroy
を行うためにはtfファイルにskip_final_snapshot
のオプションをtrueに指定する必要があります。デフォルトはfalse。本番環境で行う場合は注意しましょう。 -
RDSのPostgreSQL仕様
RDSにおけるPostgreSQLの照合順序及びCtypeのデフォルトは、en_US.UTF-8になっています。性能を考慮する場合はpsqlで接続してdropして再作成した方が良さそうです。 -
OpensshでSSHキーペアを作成した場合
OpensshでSSHキーペアを作成し、DBeaverなどのSQLクライアントを使用してSSH経由で接続する場合は、鍵の形式を変更する必要があります。
おわりに
以上、Terraform3分クッキングでした。