terraformでクラウド構築の省力化
経緯
株式会社システムアイでインフラエンジニアをしている川合です。
少し前から、AWSやAzureなどでゲートウェイからインスタンスなどを決まった構成で組めるようにしたい。とか、インスタンスの構築方法自体を変更管理したい、クラウド構築を共有したい、というケースが生じたので、terraformを使ってIaC化しようと思って軽く触ってみました。
今回使用したコードはこちらにアップしています。
https://github.com/kwryoh/sample_terraform
terraform とは
terraform は、Ansible のように AWS などのクラウド環境をコードで表すことができるツールの1つです。
基本として、terraform では EC2のインスタンスやVPCの設定を、"resource {}"という単位で定義していきます。例えば、EC2インスタンスを構築する場合、次のようなコードとなります。
provider "aws" {
profile = "default"
region = "ap-northeast-1"
}
resource "aws_instance" "app_server" {
ami = "ami-830c94e3"
instance_type = "t3.micro"
tags = {
Name = "TFSampleInstance"
}
}
"ami"、"ap-northeast-1"や"t3.micro"など見たことある名称がありますね。
こんな形でわりと直感的に、クラウド環境構築を定義でき、コードであるため、Gitで管理かつ共有も可能になります。
terraform からインスタンスの作成をするには次のコマンドを実行します。
# リソースの作成
$ terraform apply
# リソースの削除
$ terraform destroy
terraform で AWS の EC2 をたてるまで
ここでは下図のような単純な EC2 インスタンスを作ることを目的とします。
環境準備
まず terraform をインストールします。Mac の場合は brew を使うと容易にインストールできます。また、Windows の場合は scoop や chocolatey を使うと同じようにインストールできます。
インストール方法の詳細についてはこちらをご参照ください。
https://learn.hashicorp.com/tutorials/terraform/install-cli
# for Mac
$ brew install terraform
# for Windows
> scoop install terraform
# or
> choco install terraform
正常にインストールできたか次のコマンドで確認します。
$ terraform -version
次に AWC CLI をインストールします。これも brew でインストールします。(Windows は scoop または chocolatey)
他 OS の場合はこちらを見てください。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html
# for Mac
$ brew install awscli
# for Windows
> scoop install aws
# or
> choco install awscli
次のコマンドでインストールができたか確認します。
aws --version
作業ディレクトリを作成し、移動します。
$ install -m 0755 terraform-aws-sample
$ cd terraform-aws-sample
terraform の準備
terraform で AWS にインスタンスを構築するに当たり、AWSへのアクセスが必要なため、アクセスキーとシークレットキーを環境変数に設定します。
# for Mac
# AWSのアクセスキー
$ export AWS_ACCESS_KEY_ID="<YOUR_AWS_ACCESS_KEY_ID>"
# AWSのシークレットキー
$ export AWS_SECRET_ACCESS_KEY="<YOUR_AWS_SECRET_ACCESS_KEY>"
# リージョン名
$ export AWS_DEFAULT_REGION="<YOUR_AWS_DEFAULT_REGION>"
# for Windows
# AWSのアクセスキー
> set AWS_ACCESS_KEY_ID="<YOUR_AWS_ACCESS_KEY_ID>"
# AWSのシークレットキー
> set AWS_SECRET_ACCESS_KEY="<YOUR_AWS_SECRET_ACCESS_KEY>"
# リージョン名
> set AWS_DEFAULT_REGION="<YOUR_AWS_DEFAULT_REGION>"
ではファイルを用意しましょう。
ファイル名を "main.tf" として、次の内容を記載します。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
required_version = ">= 0.14.9"
}
provider "aws" {
region = "ap-northeast-1"
}
一度ファイルを保存して閉じます。
次にterraformの環境準備をします(providerに沿ったプラグインのダウンロード)。
$ terraform init
そして、正しく記載できているか確認するため、次のコマンドを実行します。”Success! The configuration is valid.”と表示されればOKです。
$ terraform validate
Success! The configuration is valid.
EC2インスタンスの定義
再度、main.tfを開きます。
terraformではresourceでEC2などを定義するため、provider の下辺りに resourceを記述していきます。
provider "aws" {
region = "ap-northeast-1"
}
# ↓を追記
resource "aws_instance" "demo" {
ami = "ami-02c3627b04781eada" # AmazonLinux2のAMI ID
instance_type = "t3.micro"
tags = {
Name = "tf-demo"
}
}
resourceの構文は、
resource "リソースタイプ" "任意の定義名" {
リソース内の設定値
}
の形式で記述します。
今回はEC2インスタンスの定義のため、リソースタイプは"aws_instance"となります。
resource内の内容はリソースタイプによって異なります。EC2インスタンスの場合は、"ami"と"instance_type"が最低あれば構築できます。
ami に指定する値はAMIカタログなどで表示されているAMI IDです。
instance_type はそのままですね。今回は"t2.micro"にしています。
最後に、tagsという部分について触れておきますと、これはインスタンスのタグを指定おり、上記のように"Name"タグを指定することで、(コンソールなどで表示される)インスタンス名を設定しています。
terraform を使ってリソースの生成・削除
以下のコマンドでどのような構成でリソースが作成されるか確認しましょう。
$ terraform plan
実行計画(どういう構成で環境構築を行うかの構成内容)を確認できます。
計画内容に問題なければ次のコマンドで環境構築を行います。
$ terraform apply
上記コマンド実行後次の確認が出るので"yes"と入力し、処理をすすめるとインスタンスが生成されます。
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
EC2の画面側で確認すると作成したインスタンスが確認できるかと思います。
確認できたら忘れず削除しておきましょう。
$ terraform destroy
ネットワークの作成
前述でEC2インスタンスを作成したので、そのインスタンスが外部接続可能なようにネットワークの準備をします。
VPC
VPCを構成するため、以下のコードをさきほどの main.tf に追記してください。
resource "aws_vpc" "demo_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "tf-demo"
}
}
参照: Terraform Registry - aws_vpc
$ terraform plan
で確認し、
$ terraform apply
でVPCを適用させます。
AWSマネジメントコンソールからVPCが作成されているか確認しておきましょう。
サブネット
次に、サブネットを構成します。以下のコードをさきほどの main.tf に追記してください。
resource "aws_subnet" "demo_subnet" {
vpc_id = aws_vpc.demo_vpc.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
tags = {
Name = "tf-demo"
}
}
参照: Terraform Registry - aws_subnet
$ terraform plan
で適用内容を確認し、
$ terraform apply
でサブネットを適用させます。
AWSマネジメントコンソールで作成されているか確認します。
Internet Gateway
外部アクセスのためInternet Gatewayを作ります。以下のコードを main.tf に追記してください。
resource "aws_internet_gateway" "demo_igw" {
vpc_id = aws_vpc.demo_vpc.id
tags = {
Name = "tf-demo"
}
}
参照: Terraform Registry - aws_internet_gateway
$ terraform plan
で適用内容を確認し、
$ terraform apply
で適用させます。AWSマネジメントコンソールで作成されているか確認します。
Route Table、Route Table Association
Internet Gatewayを通じてアクセスができるよう、ルーティング設定をします。
以下のコードを main.tf に追記します。
resource "aws_route_table" "demo_rt_tbl" {
vpc_id = aws_vpc.demo_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.demo_igw.id
}
tags = {
Name = "tf-demo"
}
}
参照: Terraform Registry - aws_route_table
また、ルーティング設定とサブネットの紐付け設定も追記します。
resource "aws_route_table_association" "demo_rt_assoc" {
subnet_id = aws_subnet.demo_subnet.id
route_table_id = aws_route_table.demo_rt_tbl.id
tags = {
Name = "tf-demo"
}
}
参照: Terraform Registry - aws_route_table_association
$ terraform plan
で適用内容を確認し、
$ terraform apply
で適用させます。
Security Group
最後にファイアウォール設定をします。
今回はSSH接続とWeb接続を許可します。
まず、大枠のセキュリティグループを作成します。
resource "aws_security_group" "demo_sg" {
vpc_id = aws_vpc.demo_vpc.id
name = "tf-demo"
description = "Terraform Demo"
tags = {
Name = "tf-demo"
}
}
参照: Terraform Registry - aws_security_group
次に、ここのルールを追記していきます。
# Outbound 設定
resource "aws_security_group_rule" "demo_egress_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.demo_sg.id
}
# SSH接続
resource "aws_security_group_rule" "demo_allow_ssh" {
description = "Allow SSH"
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.demo_sg.id
}
# HTTP接続
resource "aws_security_group_rule" "demo_allow_http" {
description = "Allow HTTP"
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.demo_sg.id
}
一通り記述したら、
$ terraform plan
で適用内容を確認し、
$ terraform apply
で適用させます。
これで一通りの準備が整いました。(ElasticIPを使いたい場合はElasticIPのリソースも作成します)
EC2の外部公開
EC2を作成したVPC、セキュリティグループに所属させることで外部からアクセスできるようにしていきます。
EC2をサブネットに所属させる
初めに記載したEC2リソースの記述を下記のように編集します。
resource "aws_instance" "demo" {
ami = "ami-02c3627b04781eada" # AmazonLinux2のAMI ID
instance_type = "t3.micro"
# ↓追記
subnet_id = aws_subnet.demo_subnet.id
vpc_security_group_ids = [aws_security_group.demo_sg.id]
# ↑ここまで
tags = {
Name = "tf-demo"
}
}
いつもどおり
$ terraform plan
で適用内容を確認し、
$ terraform apply
で適用させます。
ログイン用SSH鍵を設定
最後にSSH接続ができるようにインスタンスに設置する鍵を指定します。
なお、SSH鍵は先にAWSマネジメントコンソールで作成しておきます(ここの自動化する方法がありますが、今回は手動で作成することにします)。
先程のEC2インスタンスのリソースに設定を追加します。
resource "aws_instance" "demo" {
ami = "ami-02c3627b04781eada" # AmazonLinux2のAMI ID
instance_type = "t3.micro"
subnet_id = aws_subnet.demo_subnet.id
vpc_security_group_ids = [aws_security_group.demo_sg.id]
key_name = "登録済みSSH鍵名"
tags = {
Name = "tf-demo"
}
}
記述したら
$ terraform plan
で適用内容を確認し、
$ terraform apply
で適用します。
次のコマンドで外部公開のDNS名を取得し、接続できるか試してみましょう。
$ terraform show | grep -e "public_dns"
# => ec2-18-181-217-113.ap-northeast-1.compute.amazonaws.com など接続DNS名を取得
$ ssh ec2-XXX-XXX-XXX-XXX.ap-northeast-1.compute.amazonaws.com
一通り操作を終えたら破棄しておきましょう。
$ terraform destroy
最後に
今回細かくVPCからEC2のリソースを定義しました。
AWSの各リソースを自分で定義しないといけないためクラウド環境の構成をよく知っていないと使いこなすのは難しいです。
ただ、構成をコードの形で定義しておくと、デプロイでミスを減らせれるし、人にもお任せできるのでよいツールだなと思いました。
まだまだ説明できていない機能があり、もっと複雑な構成(インスタンスを2つ、3つ作るなど)ができるので、クラウドを使う場合はなるべくterraformで定義しようと思います。
※ちなみにterraformからはEC2インスタンスの停止はできません。
最後に、最終的なコードを載せておきます(各リソースで同じタグを設定するので、"provider"の中でdefault_tagsとして指定しています。また、ami_idはlocalsで変数定義しています)。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
required_version = ">= 0.14.9"
}
# Provider
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs
provider "aws" {
region = "ap-northeast-1"
# 全てのリソースに同じタグを設定する
default_tags {
tags = {
Name = "tf-demo"
}
}
}
# Local Variables
# https://www.terraform.io/language/values/locals
locals {
ami_id = "ami-02c3627b04781eada" # AmazonLinux2のAMI
}
# Instance
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/instance
resource "aws_instance" "demo" {
ami = local.ami_id
instance_type = "t3.micro"
subnet_id = aws_subnet.demo_subnet.id
vpc_security_group_ids = [aws_security_group.demo_sg.id]
key_name = "登録済みSSH鍵名"
}
# VPC
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc
resource "aws_vpc" "demo_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
}
# Subnet
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet
resource "aws_subnet" "demo_subnet" {
vpc_id = aws_vpc.demo_vpc.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
}
# Internet Gateway
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway
resource "aws_internet_gateway" "demo_igw" {
vpc_id = aws_vpc.demo_vpc.id
}
# Route Table
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table
resource "aws_route_table" "demo_rt_tbl" {
vpc_id = aws_vpc.demo_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.demo_igw.id
}
}
# Route Table Associate
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association
resource "aws_route_table_association" "demo_rt_assoc" {
subnet_id = aws_subnet.demo_subnet.id
route_table_id = aws_route_table.demo_rt_tbl.id
}
# Security Group
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
resource "aws_security_group" "demo_sg" {
vpc_id = aws_vpc.demo_vpc.id
description = "Terraform Demo"
}
# Security Group Rule
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
resource "aws_security_group_rule" "demo_egress_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.demo_sg.id
}
resource "aws_security_group_rule" "demo_allow_ssh" {
description = "Allow SSH"
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.demo_sg.id
}
resource "aws_security_group_rule" "demo_allow_http" {
description = "Allow HTTP"
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.demo_sg.id
}
__________________________________
【ご案内】
ITシステム開発やITインフラ運用の効率化、高速化、品質向上、その他、情シス部門の働き方改革など、IT自動化導入がもたらすメリットは様々ございます。
IT業務の自動化にご興味・ご関心ございましたら、まずは一度、IT自動化の専門家リアルグローブ・オートメーティッド(RGA)にご相談ください!
お問合せは以下の窓口までお願いいたします。
【お問い合わせ窓口】
代表窓口:info@rg-automated.jp
URL: https://rg-automated.jp