Udemyの講座楽しい
terraformで構築してみた。
個人用メモ。
死ぬほど参考にさせていただいたサイト
まずは公式のチュートリアルで概要を掴む
Introduction to Infrastructure as Code with Terraform | Terraform - HashiCorp Learn
どんなresourceを使うのかなど流れを把握
VPC - Terraformで構築するAWS
【Terraform 再入門】EC2 + RDS によるミニマム構成な AWS 環境をコマンドライン一発で構築してみよう – PSYENCE:MEDIA
公式ドキュメントでプロパティ等を確認しながら書いていく
Docs overview | hashicorp/aws | Terraform Registry
やらなかったこと
- countやmoduleによる共通化
- Wordpressのインストール
countは下記の通りやればいけそう。ただのリファクタになりそうだったのでモチベ上がらず。
Automate Terraform | Terraform - HashiCorp Learn
Wordpressのくだりは本来docker-compose.ymlやプロビジョンツールで行うことなので、terraformでやることではないと判断したため。
便利なshell関数を作った
terr
と実行するとterraformコマンドをfzfで絞り込んで実行できる。
alias terr="_terraform_execute" _terraform_execute() { local cmd=$(terraform -help | grep '^ \S' | sed 's/ //' | fzf --with-nth=1 --preview='echo {2..}' --preview-window=up:1 | awk '{print $1}') [ -z "$cmd" ] && return print -s "terraform $cmd $1" terraform $cmd $1 }
詰まった箇所
resourceの書き方。resource "aws_vpc" "main"
のmainって何につかうの?
tfファイル内でのみ使用。aws上では使用されない。aws上で使用されるのはtags.Nameの部分。
tfファイル内ではsubnet作成などでvpc_id = aws_vpc.main.id
のように使用される。
vimでterraform書く
Terraform職人入門: 日々の運用で学んだ知見を淡々とまとめる - Qiita
ここ参考に。hashivim/vim-terraform
をインストールする。
CocのLSPを使う方法は、見当違いなlinterエラーが出るようになってしまったのでアンインストールした。
ec2でsshするためのキーペア
あとからキーペアをアタッチできない。
terraformでキーペアを作成できない。
Macで予めキーを作成しておき、それをterraformで指定する形。
$ ssh-keygen -t rsa
destroyを個別に指定したい(Ctrl-Cで終了しちゃったときとか)
-target
をつければOK
terraform destroyで特定のものだけ消したい場合 – ADACHIN SERVER LABO
$ terraform destroy -target=aws_instance.main
複数のingressを設定したい
aws_security_group_rule
で設定可能
ec2のuser_dataでhttpdをインストールできない
yumがうまく実行できていないっぽい。
[ec2-user@ip-10-0-10-53 ~]$ yum list 読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd Could not retrieve mirrorlist http://amazonlinux.ap-northeast-1.amazonaws.com/2/core/latest/x86_64/mirror.list error was 12: Timeout on http://amazonlinux.ap-northeast-1.amazonaws.com/2/core/latest/x86_64/mirror.list: (28, 'Connection timed out after 5001 milliseconds') One of the configured repositories failed (不明),
egressを設定したらいけた。security_group作成時、terraformだとアウトバウンドがデフォルトで全拒否になってしまうため。GUIで設定すると全許可がデフォルトで設定される。
DBのセキュリティグループでソースってどうやって指定するの?
source_security_group_id
で指定する
- 【Terraform 再入門】EC2 + RDS によるミニマム構成な AWS 環境をコマンドライン一発で構築してみよう – PSYENCE:MEDIA
既存のAWS環境をterraformに落とし込むには?
terraform import
を使って現在の設定を表示し、terraform plan
で変更がないことを確認しながらtfファイルを作っていく。
- 既存のAWS環境を後からTerraformでコード化する | DevelopersIO
例:既存のroute53をterraformでimportする
main.tfに定義を先に書いておく必要がある。
resource "aws_route53_zone" "main" { name = 'yourdomain.com' }
定義したら下記を実行
terraform import aws_route53_zone.main Z076386518IPI8D2J7O7O
レコードのimport方法
terraform import aws_route53_record.www ZONEID_RECORDNAME_TYPE_SET-IDENTIFIER
これもすでにmain.tfに定義を先に書き込んでおく必要がある。
例:
terraform import aws_route53_record.www Z076386518IPI8D2J7O7O_rasukarusan.tk_A
amazon web services - Terraform + Route53 - manage existing record - Stack Overflow
RDSの作成が長い
8分ぐらいかかる。気長に待つ。
Error: DB Instance FinalSnapshotIdentifier is required when a final snapshot is requiredが出てRDSをdestroyできない
final_snapshot_identifier
を設定していないと削除できなくなる。一旦手動でインスタンスを削除し、applyし直す。
が、これをするとオプショングループが削除できなくなるのでskip_final_snapshot = true
のみ設定する。
オプショングループの削除ができない
... aws_db_option_group.terraform-mysql80: Still destroying... [id=terraform-mysql80, 19m50s elapsed] Error: Error Deleting DB Option Group: InvalidOptionGroupStateFault: The option group 'terraform-mysql80' cannot be deleted because it is in use. status code: 400, request id: 297384d1-d362-4b33-9ee9-36d1dc0b80bf
スナップショットが作成されており、それに使用されてしまっているため。スナップショットを手動で削除し、destroyを実行し直すと削除できる。
final_snapshot_identifier
をコメントアウトしてskip_final_snapshot = true
にすること。
terraform showは絞り込みができない?
destroyの-target=のようにしたができなかった。 jqで対応した。
terraform show -json | jq '.values.root_module.resources[] | select(.address=="aws_route53_zone.main")'
全体のソース
main.tf
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 2.70" } } } provider "aws" { profile = var.profile region = var.region } ######################################## # ネットワーク # - VPC # - サブネット # - インターネットゲートウェイ # - ルーティング ######################################## resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" tags = { Name = "udemy-terraform" } } resource "aws_subnet" "public_1a" { vpc_id = aws_vpc.main.id availability_zone = "ap-northeast-1a" cidr_block = "10.0.10.0/24" map_public_ip_on_launch = true tags = { Name = "terraform-public-1a" } } resource "aws_subnet" "public_1c" { vpc_id = aws_vpc.main.id availability_zone = "ap-northeast-1c" cidr_block = "10.0.11.0/24" map_public_ip_on_launch = true tags = { Name = "terraform-public-1c" } } resource "aws_subnet" "private-1a" { vpc_id = aws_vpc.main.id availability_zone = "ap-northeast-1a" cidr_block = "10.0.20.0/24" tags = { Name = "terraform-private-1a" } } resource "aws_subnet" "private-1c" { vpc_id = aws_vpc.main.id availability_zone = "ap-northeast-1c" cidr_block = "10.0.21.0/24" tags = { Name = "terraform-private-1c" } } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = { Name = "terraform-igw" } } # ルートテーブル作成 resource "aws_route_table" "public" { vpc_id = aws_vpc.main.id tags = { Name = "terrform-public-route" } } # ルート設定 resource "aws_route" "public" { destination_cidr_block = "0.0.0.0/0" route_table_id = aws_route_table.public.id gateway_id = aws_internet_gateway.main.id } # ルートテーブルとサブネットの紐付け resource "aws_route_table_association" "public_1a" { subnet_id = aws_subnet.public_1a.id route_table_id = aws_route_table.public.id } resource "aws_route_table_association" "public_1c" { subnet_id = aws_subnet.public_1c.id route_table_id = aws_route_table.public.id } ######################################## # Webサーバー # - キーペア # - セキュリティグループ # - Elastic IP # - EC2 ######################################## resource "aws_key_pair" "main" { key_name = "terraform-web-key" public_key = file(var.public_key_path) } resource "aws_security_group" "terraform-web-security" { vpc_id = aws_vpc.main.id name = "terraform-web-security" } resource "aws_security_group_rule" "inbound_ssh" { type = "ingress" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.terraform-web-security.id } resource "aws_security_group_rule" "inbound_http" { type = "ingress" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.terraform-web-security.id } resource "aws_security_group_rule" "outbound_allow_all" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.terraform-web-security.id } resource "aws_instance" "ec2" { ami = "ami-0992fc94ca0f1415a" instance_type = "t2.micro" subnet_id = aws_subnet.public_1a.id key_name = aws_key_pair.main.id vpc_security_group_ids = [aws_security_group.terraform-web-security.id] private_ip = "10.0.10.10" user_data = <<EOS #!/bin/bash yum update -y yum install -y httpd yum install -y mysql systemctl start httpd.service EOS tags = { Name = "terraform-ec2-web" } } resource "aws_eip" "terraform_web" { instance = aws_instance.ec2.id vpc = true } ######################################## # DBサーバー # - セキュリティグループ # - DBサブネットグループ # - パラメータグループ # - オプショングループ # - RDS ######################################## resource "aws_security_group" "terraform-db-security" { vpc_id = aws_vpc.main.id name = "terraform-db-security" } resource "aws_security_group_rule" "inbound_mysql" { type = "ingress" from_port = 3306 to_port = 3306 protocol = "tcp" source_security_group_id = aws_security_group.terraform-web-security.id security_group_id = aws_security_group.terraform-db-security.id } resource "aws_db_subnet_group" "main" { name = "terraform-db-subnet-group" subnet_ids = [aws_subnet.private-1a.id, aws_subnet.private-1c.id] tags = { Name = "terraform-db-subnet-group" } } resource "aws_db_parameter_group" "terraform-mysql80" { name = "terraform-mysql80" family = "mysql8.0" } resource "aws_db_option_group" "terraform-mysql80" { name = "terraform-mysql80" engine_name = "mysql" major_engine_version = "8.0" } resource "aws_db_instance" "main" { allocated_storage = 20 storage_type = "gp2" engine = "mysql" engine_version = "8.0.15" instance_class = "db.t2.micro" name = "aws_terraform_db" identifier = "terraform-web" # snapshotを削除時に生成すると、destroyによるオプショングループの削除が不可能になる # final_snapshot_identifier = "final-snapshot-terraform-web" skip_final_snapshot = true username = var.db_username password = var.db_password parameter_group_name = aws_db_parameter_group.terraform-mysql80.name option_group_name = aws_db_option_group.terraform-mysql80.name max_allocated_storage = 0 vpc_security_group_ids = [aws_security_group.terraform-db-security.id] db_subnet_group_name = aws_db_subnet_group.main.name availability_zone = "ap-northeast-1a" port = 3306 backup_retention_period = 30 backup_window = "19:00-19:30" copy_tags_to_snapshot = true maintenance_window = "Sun:20:00-Sun:20:30" } ######################################## # ドメイン # - Route53 # - Aレコード登録 ######################################## resource "aws_route53_zone" "main" { name = var.domain } resource "aws_route53_record" "A-record" { zone_id = aws_route53_zone.main.zone_id name = aws_route53_zone.main.name type = "A" alias { name = aws_lb.main.dns_name zone_id = aws_lb.main.zone_id evaluate_target_health = true } } ######################################## # ALB # - EC2冗長化 # - セキュリティグループ # - ターゲットグループ # - ALB ######################################## resource "aws_instance" "ec2-2" { ami = "ami-0992fc94ca0f1415a" instance_type = "t2.micro" subnet_id = aws_subnet.public_1c.id key_name = aws_key_pair.main.id vpc_security_group_ids = [aws_security_group.terraform-web-security.id] private_ip = "10.0.11.10" user_data = <<EOS #!/bin/bash yum update -y yum install -y httpd yum install -y mysql systemctl start httpd.service EOS tags = { Name = "terraform-ec2-web" } } resource "aws_security_group" "terraform-alb-security" { vpc_id = aws_vpc.main.id name = "terraform-alb-security" } resource "aws_security_group_rule" "alb-inbound-http" { type = "ingress" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.terraform-alb-security.id } resource "aws_security_group_rule" "alb-allow-all" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.terraform-alb-security.id } resource "aws_lb_target_group" "main" { name = "terraform-alb-tg" port = 80 protocol = "HTTP" target_type = "instance" vpc_id = aws_vpc.main.id health_check { path = "/index.html" healthy_threshold = 2 interval = 10 } } resource "aws_lb_target_group_attachment" "main" { target_group_arn = aws_lb_target_group.main.arn target_id = aws_instance.ec2.id port = 80 } resource "aws_lb_target_group_attachment" "main2" { target_group_arn = aws_lb_target_group.main.arn target_id = aws_instance.ec2-2.id port = 80 } resource "aws_lb_listener" "main" { load_balancer_arn = aws_lb.main.arn port = "80" protocol = "HTTP" default_action { type = "forward" target_group_arn = aws_lb_target_group.main.arn } } resource "aws_lb" "main" { name = "terraform-alb" internal = false load_balancer_type = "application" security_groups = [aws_security_group.terraform-alb-security.id] subnets = [aws_subnet.public_1a.id, aws_subnet.public_1c.id] tags = { Name = "terraform-alb" } }
variables.tf
variable "profile" {} variable "db_username" {} variable "db_password" {} variable "domain" {} variable "region" { default = "ap-northeast-1" } variable "public_key_path" { default = "~/.ssh/terraform.pub" description = <<EOS Command: ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/Users/you/.ssh/id_rsa): /Users/you/.ssh/terraform Enter passphrase (empty for no passphrase): `empty` Enter same passphrase again: `empty` EOS }
outputs.tf
output "elastic_ip" { value = aws_eip.terraform_web.public_ip } output "db_endpoint" { value = aws_db_instance.main.endpoint } output "name_servers" { value = aws_route53_zone.main.name_servers } output "public_ip" { value = aws_instance.ec2-2.public_ip }
terraform.tfvars
profile = "YOUR_AWS_PROFILE" db_username = "YOUR_DB_USERNAMEJ" db_password = "YOUR_DB_PASSWORD" domain = "YOUR_DOMAIN"