Loading
BLOG 開発者ブログ

2022年3月7日

【AWS】Terraformを触って感じたことを述べる

今回は簡単なwebアプリケーション構成をTerraformで作成した感想を述べていきます。

メリット・デメリット両方ありましたが、クラウドという技術の真価を発揮させるために是非ともTerraformを習得したいなと感じました。

目次

 

はじめに

こんにちは。
クラウドソリューショングループのyoshikawa.kです。

今回のテーマは「インフラのコード化」(いわゆる IaC : Infrastructure as Code)についてです。

自分もAWSを触り始めて1年近く経過したのでそろそろTerraformを触る時期かなと思い、少し前から学習を始めました。

本記事はTerraformに興味はあるが中々手が出ない方向けに、「インフラをコード化するってどういうこと?」という疑問を少しでも解決する記事です。

 

Terraformとは

TerraformはAWSやGCP、Azureなどクラウドサービスのインフラリソースを、コードで作成出来るようにするツールです。

コードでインフラリソースを記述したものをファイル化し、TerraformがインストールされているCUI環境にてterraformコマンドを実行すると、高速でインフラリソースを作成することができます。

と言っても実感が湧かないと思いますので、画像で比較してみましょう。

 

下記の画像は皆さんお馴染みのAWSの管理画面からサブネットを作成する様子です。

AWSの管理画面からサブネットを作成する様子

 

一方で、下記の画像は作成予定のサブネットをコードにしたものです。

サブネットの設定をコードで記述し、CUI環境でterraformコマンドを実行することで、AWSの管理画面と同じように目的のサブネットを作成することができます。

作成予定のサブネットをコードに記述した様子

 

AWSで早速やってみた

今回は以下の構成図で作成してみました。

シンプルなWebアプリケーション構成で、これくらいであれば慣れていると30分もあればAWSの管理画面で作成できます。

 

実際のコード

作成はTerrafromの公式ドキュメントなどを参照しました。

カーソルをコード上にかざすとファイル名が表示され、スクロースするとコードが最後まで確認できます。

#=========================================================
#リージョンの指定
provider "aws" {
  region = "ap-northeast-1"
}
#=========================================================
#VPCの作成
resource "aws_vpc" "dev-vpc-main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
}
#=========================================================
#サブネットの作成
resource "aws_subnet" "dev-subnet-public-1" {
  vpc_id                  = aws_vpc.dev-vpc-main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = true
}

resource "aws_subnet" "dev-subnet-public-2" {
  vpc_id                  = aws_vpc.dev-vpc-main.id
  cidr_block              = "10.0.2.0/24"
  availability_zone       = "ap-northeast-1c"
  map_public_ip_on_launch = true<br>}

resource "aws_subnet" "dev-subnet-private-1" {
  vpc_id            = aws_vpc.dev-vpc-main.id
  cidr_block        = "10.0.11.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_subnet" "dev-subnet-private-2" {
  vpc_id            = aws_vpc.dev-vpc-main.id
  cidr_block        = "10.0.12.0/24"
  availability_zone = "ap-northeast-1c"
}
#=========================================================
#インターネットゲートウェイの作成
resource "aws_internet_gateway" "dev-igw-main" {
  vpc_id = aws_vpc.dev-vpc-main.id
}
#=========================================================
#ルートテーブルの作成
resource "aws_route_table" "dev-rtb-public" {
  vpc_id = aws_vpc.dev-vpc-main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.dev-igw-main.id
  }
}

resource "aws_route_table" "dev-rtb-private" {
  vpc_id = aws_vpc.dev-vpc-main.id
}
#=========================================================
#ルートテーブルをサブネットに設定
resource "aws_route_table_association" "public-1" {
  subnet_id      = aws_subnet.dev-subnet-public-1.id
  route_table_id = aws_route_table.dev-rtb-public.id
}

resource "aws_route_table_association" "public-2" {
  subnet_id      = aws_subnet.dev-subnet-public-2.id
  route_table_id = aws_route_table.dev-rtb-public.id
}

resource "aws_route_table_association" "private-1" {
  subnet_id      = aws_subnet.dev-subnet-private-1.id
  route_table_id = aws_route_table.dev-rtb-private.id
}

resource "aws_route_table_association" "private-2" {
  subnet_id      = aws_subnet.dev-subnet-private-2.id
  route_table_id = aws_route_table.dev-rtb-private.id
}
#=========================================================
#SGの作成
resource "aws_security_group" "dev-sg-elb" {
  name   = "dev-sg-elb"
  vpc_id = aws_vpc.dev-vpc-main.id
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["xxx.xxx.xxx.xxx/32"] 
  }
  egress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.dev-sg-ec2.id]
  }
}

resource "aws_security_group" "dev-sg-ec2" {
  name   = "dev-sg-ec2"
  vpc_id = aws_vpc.dev-vpc-main.id
}
resource "aws_security_group_rule" "dev-sg-ec2-rule-1" {
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.dev-sg-elb.id
  security_group_id        = aws_security_group.dev-sg-ec2.id
}
resource "aws_security_group_rule" "dev-sg-ec2-rule-2" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.dev-sg-ec2.id
}

resource "aws_security_group" "dev-sg-rds" {
  name   = "dev-sg-rds"
  vpc_id = aws_vpc.dev-vpc-main.id
  ingress {
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.dev-sg-ec2.id]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
#=========================================================
#ALBの作成
resource "aws_lb" "dev-elb-alb" {
  name               = "dev-elb-alb"
  internal           = false
  load_balancer_type = "application"

  security_groups = [
    aws_security_group.dev-sg-elb.id
  ]

  subnets = [
    aws_subnet.dev-subnet-public-1.id,
    aws_subnet.dev-subnet-public-2.id,
  ]
}
#=========================================================
#ALBリスナーの作成
resource "aws_lb_listener" "dev-elb-alb" {
  load_balancer_arn = aws_lb.dev-elb-alb.arn
  port              = "80"
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.dev-tg-elb.arn
  }
}
#=========================================================
#リスナールールの作成
resource "aws_lb_listener_rule" "forward" {
  listener_arn = aws_lb_listener.dev-elb-alb.arn
  priority     = 99
  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.dev-tg-elb.arn
  }
  condition {
    path_pattern {
      values = ["/*"]
    }
  }
}
#=========================================================
#TGの作成
resource "aws_lb_target_group" "dev-tg-elb" {
  name     = "dev-tg-elb"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.dev-vpc-main.id
  health_check {
    path = "/"
  }
}
#=========================================================
#インスタンスにTGを設定
resource "aws_lb_target_group_attachment" "dev-ec2-ap-1" {
  target_group_arn = aws_lb_target_group.dev-tg-elb.arn
  target_id        = aws_instance.dev-ec2-ap-1.id
  port             = 80
}
resource "aws_lb_target_group_attachment" "dev-ec2-ap-2" {
  target_group_arn = aws_lb_target_group.dev-tg-elb.arn
  target_id        = aws_instance.dev-ec2-ap-2.id
  port             = 80
}
#=========================================================
#ec2の作成
resource "aws_instance" "dev-ec2-ap-1" {
  ami                    = "ami-09d28faae2e9e7138"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.dev-subnet-public-1.id
  vpc_security_group_ids = [aws_security_group.dev-sg-ec2.id]
  user_data              = <<EOF
#! /bin/bash
sudo yum install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd
EOF
}

resource "aws_instance" "dev-ec2-ap-2" {
  ami                    = "ami-09d28faae2e9e7138"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.dev-subnet-public-2.id
  vpc_security_group_ids = [aws_security_group.dev-sg-ec2.id]
  user_data              = <<EOF
#! /bin/bash
sudo yum install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd
EOF
}
#=========================================================
#RDSの作成
resource "aws_db_instance" "dev-rds-db" {
  identifier             = "dev-rds-db"
  db_name                = "test"
  allocated_storage      = 20
  storage_type           = "gp2"
  engine                 = "mysql"
  engine_version         = "5.7"
  instance_class         = "db.t3.medium"
  username               = "hogehoge"
  password               = "examplepw"
  db_subnet_group_name   = aws_db_subnet_group.dev-subnet-group.name
  skip_final_snapshot    = true
  vpc_security_group_ids = [aws_security_group.dev-sg-rds.id]
}
#=========================================================
#DBサブネットグループの作成
resource "aws_db_subnet_group" "dev-subnet-group" {
  name       = "dev-subnet-group"
  subnet_ids = [aws_subnet.dev-subnet-private-1.id, aws_subnet.dev-subnet-private-2.id]
}

 

AWSでの利用手順

ポイントとなる部分のみを記載します。

  1. 上記のコードを「*.tf」ファイルとして各種作成する
  2. AWSの管理画面からCloud9を立ち上げ、「~/environment」に上記ファイルを全てアップロード・配置する
  3. コマンドを「terraform init」「terraform validate」「terraform apply」の順に実行し、「terraform apply」の途中で「yes」を入力する。しばらく待つとリソースの作成が完了する
  4. リソースを削除するときは「terraform destroy」を実行し、「yes」を入力し削除する

以上です。今回はデフォルトでterraformがインストールされているCloud9を利用しましたが、自分で環境を用意できるならローカルでもEC2インスタンスでも大丈夫です。

 

Terraformに触って感じたメリット

■同じ構成の環境をいくつも短時間で複製できる

インフラをコード化する一番のメリットはこれかなと思います。

実際の利用時には開発環境や本番環境など目的によってリソースに付与する名称等を変更することが多いです。
しかし大部分の設定は使いまわすことが可能で、労力自体は大幅に削減出来ます。

また手順書等を用意する必要がなく、設定も全てコードで管理されているので属人性を排除出来ます。

 

■AWSやGCP、Azureをより深く理解できる

管理画面ではデフォルトで設定されていたものを、コードで記述する際はわざわざ明示的に設定しなければならないことも多いです。

その際に意識していなかった設定を改めて調べ直す機会にもなり、各種サービスのより深い理解に繋げられます。

 

■スキルの客観的証明

AWSやGCP、Azureの管理画面では中々クラウド技術の証明は難しかったですが、コードで記述することで誰でも簡単にクラウドスキルを証明することが可能になりました。

また一度書いたコードでもアップデートして、随時自分のスキルセットを追加で証明することができます。

 

Terraformに触って感じたデメリット

■慣れるまで難しい

AWSの管理画面だと、今回作成した構成図も30分程で作成できます。

ですが今回はTerraformに慣れていないこともあり、上記のコードを記述し、エラー無くAWS上で起動するまでに4~5時間程かかりました。

コードをどうやって記述すればいいのか調べるのはもちろん、実際にコマンドで実行すると何度もエラーを吐かれたので修正するのに結構苦労しました。

業務で活用するレベルに持っていくには力を入れて学習する必要があります。

 

■いきなりTerraformから学習するのは現実的でない

いきなりAWSやGCP、Azureの学習を飛ばして、Terraformから学習を始めるのはかなり厳しいです。

最初はGUIでAWSやGCP、Azureのことを理解し、それらの知識をベースに徐々にインフラをコード化するのが王道です。

Terraformが流行っているからと、いきなり手を出すのは挫折するきっかけになるかもしれません。

 

■小規模で構築する環境の数が少ないなら管理画面から作成する方が早い(かも)

これはコードの書き方を調べたり、エラー対処の労力がそれなりに掛かるからです。

大規模で同じ環境を複数作成するのであればコード化のメリットは大きくなりますが、そうでなければ普通に管理画面から作成した方が早いと思われます。

 

■解説記事が少ない(日本語の記事がほとんどない)

AWSのVPCやEC2などよく使われるサービスならそこそこ解説記事がありますが、少しマイナーなサービスになるとTerraformの解説記事はほとんどありません。

現状では公式ドキュメントや海外のサイトを上手く使う必要があるでしょう。

 

まとめ

まだまだ自分もTerraform初心者ですが、簡単な構成をコード化し実行するだけでも色々な発見がありました。

デメリットもいくつか書きましたが、要は「最初の学習が難しい」ということであり、参入障壁が高くまだまだ使えるエンジニアが少ないです。

だからこそ使いこなせるようになれば、インフラをコード化するメリットと相まって非常に需要が高いスキルだと思います。

Terraformの本も数は少ないですが、何冊か評判のいい良書があるみたいですので、体系的に知識を獲得すると効率よく勉強できるのではないでしょうか。

 

のブログ