🐓

なんとなくわかるインフラ構築 〜Terraform編

2023/01/14に公開

はじめに

Terraformの名前だけ知ってる人向けに、なんとなく分かるレベルでTerraformを説明していく。

今回書いたコードはこちら。
AWS IAMの認証情報の設定をして、READMEに記載されたコマンドを実行すれば本記事に記載された環境を構築することができる。
https://github.com/kkznch/2023-01-06_sample-terraform

Terraformとは

HashiCorp社が提供する、インフラをコードで管理するためのツール。インフラをコードで管理することにより、効率よく且つ安全にインフラの管理をすることを目的としている。TerraformはAPIを介することで、AWS、Azure、GCPなどのクラウドプラットフォーム、GitHubやDockerなどのサービスの管理を行うことができる。

例えばAWSでインフラを構築する際、Terraformを使わない場合はAWSマネジメントコンソールにログインし、各サービスの画面まで移動して一つ一つのリソースを作成する必要がある。インフラを構築したあと、修正したい場合や作り直したい場合など、同様の手順で画面を移動してぽちぽちと操作を行う必要があるため、非常に手間がかかる上に、変更内容を記録しておかないと後々面倒になる。

https://zenn.dev/kekezun/articles/c21ae2b9ce5a23

Terraformを使ってAWSでインフラ構築する場合、設計した内容をコードに落とし込み、Terraformコマンドを時効するだけでAWS上にインフラが構築される。インフラを修正したい場合もコードを修正し、Terraformコマンドを実行するだけで差分がAWSインフラに反映される。
Terraformコマンドを実行して環境を全て消しさることもできるので、納得できる環境ができるまで作って壊してを繰り返すことができるため非常に便利。Terraformコマンドを実行してからAWSインフラに反映されるまで多少時間はかかるが、Dockerイメージを作るときと同じ感覚で進めることができるので良き。

Terraformでインフラをコード化する

なんとなくわかるインフラ構築 〜AWS編 で使用した下記の図の構成について、Terraformを用いてコード化していく。

プロバイダを指定する

まず初めに、Terraformでインフラを構築するプロバイダを設定する。
今回はインフラの構築先がAWSのため、AWSをプロバイダとして指定する。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.49.0"
    }
  }
}

この段階ではプロバイダ指定しているだけなので、実行しても何も作成はされない。

VPCとインターネットゲートウェイの作成

ネットワークの大枠となるVPCと、その入り口となるインターネットゲートウェイを作成する。

VPCの作成には aws_vpc リソースを用い、CIDR( cidr_block プロパティ)にIPアドレスを指定している。

インターネットゲートウェイの作成には aws_internet_gateway リソースを用いる。このとき aws_internet_gateway リソースブロック内の vpc_id プロパティにはVPC( aws_vpc.this )のIDを指定している。これにより、VPC( aws_vpc.this )と紐づいたインターネットゲートウェイを作成することができる。

resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id
}

上述した resource "aws_vpc" "this"this はTerraform上で作成したVPCリソースの管理をするための名前であり、実際にAWS上で作成されたリソースに使われる名前ではない。
余談だが、作成するリソースが一つしかない場合などは便宜上 this と名前をつける方がいいとTerraform公式ドキュメントに記載されている( Terraform - Resource and data source arguments )ので、本記事でも this としている。

なお aws_vpc リソースに限った話ではないが、リソースブロック内のプロパティは必須のものと任意のものがあり、必須のものが入力されていない場合はTerraform実行時にエラーとなり、任意のものが入力されていない場合はそのプロパティのデフォルト値が使用される。

サブネットとルーティングの作成

VPCとインターネットゲートウェイを作成したことでネットワークの大枠ができたので、次は外部からのアクセスを許可するためのサブネットとルーティングを作成する。

サブネットの作成には aws_subnet リソースを用い、 vpc_id プロパティには先ほど作成したVPC( aws_vpc.this )のID、サブネットを作成するアベイラビリティゾーン( availability_zone プロパティ)には任意のアベイラビリティゾーン、サブネットのCIDR( cidr_block プロパティ)にはVPC( aws_vpc.this )で指定したCIDRの範囲内のCIDRを指定している。

サブネットに対してルーティングの設定を反映するためには、複数のリソースを用いる必要がある。
まずはルートテーブルの作成をするために aws_route_table リソースを用い、 vpc_id プロパティにVPC( aws_vpc.this )のIDを指定する。
次にルートテーブルにルートの定義をするために aws_route リソースを用い、VPC内部からの通信全て( 0.0.0.0/0 )をインターネットゲートウェイ( aws_internet_gateway.this )経由で行わせるよう指定する。
最後に、サブネット( aws_subnet.this )がサブネット内部からの通信をインターネットゲートウェイ( aws_internet_gateway.this )経由で行うために aws_route_table_association リソースを用い、サブネット( aws_subnet.this )とルートテーブル( aws_route_table.this )を関連づけている。

resource "aws_subnet" "this" {
  vpc_id = aws_vpc.this.id

  availability_zone = "ap-northeast-1a"
  cidr_block        = "10.0.128.0/24"
}

resource "aws_route_table" "this" {
  vpc_id = aws_vpc.this.id
}

resource "aws_route" "this" {
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.this.id
  route_table_id         = aws_route_table.this.id
}

resource "aws_route_table_association" "this" {
  route_table_id = aws_route_table.this.id
  subnet_id      = aws_subnet.this.id
}

EC2の作成

最後にEC2を作成し、外部インターネットからEC2の80番ポートにアクセス可能な状態にする。
このとき、EC2を作成するだけだと外部インターネットからアクセスができる状態にはならないので、先にセキュリティグループから作成し、通信の流れを制御する。

外部インターネットからEC2への80番ポートへのアクセスを可能にするために aws_security_group リソースを用い、 ingress ブロックには外部インターネットからEC2への通信を許可するための条件を指定し、 egress ブロックにはEC2からアクセス元への返信を許可するための条件を指定する。ここでは外部インターネットからEC2への80番ポートへの通信を許可しており、EC2からアクセス元へレスポンスを返す際には全ての通信を許可するように指定している。

次にEC2を作成するために aws_instance リソースを用いる。 ami プロパティにはEC2で使用するAMI、 instance_type プロパティには起動するEC2のインスタンスタイプ、 subnet_id プロパティにはサブネット( aws_subnet.this )のID、 private_ip プロパティにはサブネットに割り当てたCIDRの範囲のIPアドレス、 security_groups プロパティには作成したセキュリティグループ( aws_security_group.this )のIDをそれぞれ指定する。

# EC2の設定
resource "aws_security_group" "this" {
  vpc_id = aws_vpc.this.id

  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" "this" {
  ami           = "ami-0bba69335379e17f8"
  instance_type = "t2.micro"

  subnet_id                   = aws_subnet.this.id
  private_ip                  = "10.0.128.100"
  associate_public_ip_address = true
  security_groups             = [aws_security_group.this.id]
}

ここまでコードを書いた後、Terraformコマンドを実行することで上図の構成でインフラが構築される。
なお、EC2に80番ポートでアクセス可能な状態にはしているが、EC2上でWebサーバが起動していなければアクセスできないので、EC2にログインするなりTerraform実行時にshellを操作してWebサーバを起動するなりする必要がある。今回はそこまではやらない。

おわりに

Terraformを用いてAWS上にインフラ構築する方法について説明した。
小規模であれば本記事のようにコードを一つのファイルにまとめるだけで十分だが、中規模・大規模になった場合にはモジュール化してテンプレートとして扱う方が良い。モジュール化の方法についてはまた次回以降に書いていく。

参考

Discussion