【AWSチュートリアル】ECS用にLaunch Templateを作成する!

LaunchTemplate作成 AWS
スポンサーリンク

ECS(EC2)では、コンテナの基盤にEC2インスタンス(以降、コンテナインスタンスと呼びます)を使用します。

今回は、そのEC2インスタンスの元になるLaunch Templateを作成していきます。

Build software better, together
GitHub is where people build software. More than 100 million people use GitHub to discover, fork, and contribute to over...
スポンサーリンク

前提

1.前回までの記事を読んでいること!

今回の目標

以下のリソースを作成していきます。

  • Launch Template
  • IAMロール
  • セキュリティグループ

VPCのIDをoutputに追加する。

今回、Launch Template以外にセキュリティグループなども作成します。その際、VPCのIDが必要になってきます。以前の記事でネットワーク系を作成しましたが、今回はそちらを参照することになります。ただ、別Stateで管理されているため、現状のままだと参照することができません。そのため、まずはネットワーク系のTerraformコードでoutputする必要があります。

modules部分とstaging部分で2回outputする必要があるので注意。

terraform/network/modules/output.tf

output "vpc_id" {
  value = aws_vpc.study_infra.id
}

terraform/network/staging/output.tf

output "vpc_id" {
  value = module.network.vpc_id
}

以上です。

main.tfを作成する。

以前作成したネットワーク系の main.tf とほぼ同じです。

ポイントは terraform_remote_state です。この機能を利用することで、別のStateを参照することができます。今回は、networkのStateを参照して、VPCのIDを参照します。

terraform_remote_state で必要な設定を変数にしています。

variable "network_remote_state_config" {
  type = map(string)
  default = {
    bucket   = "terraform-state-develop-0001"
    key      = "study-infra-tutorial/staging.tfstate"
    region   = "ap-northeast-1"
    role_arn = "arn:aws:iam::XXXXXXXXXXX:role/study-infra-terraform-role"
  }
}

main.tf を作成します。

terraform/system/staging/main.tf

terraform {
  backend "s3" {
  }
}

provider "aws" {
  region = "ap-northeast-1"
  assume_role {
    role_arn = var.iam_role_for_terraform
  }
  default_tags {
    tags = var.common_tags
  }
}

data "terraform_remote_state" "network" {
  backend = "s3"
  config  = var.network_remote_state_config
}

# 変数をいくつか設定しています。
module "web" {
  source        = "../modules/web"
  common_tags   = var.common_tags
  vpc_id        = data.terraform_remote_state.network.outputs.vpc_id
  ami_id        = var.ami_id
  instance_type = var.instance_type
  key_name      = var.key_name
  cluster_name  = var.cluster_name
}

コンテナインスタンスにアタッチするインスタンスプロファイルを作成する。

インスタンスプロファイルとは?

EC2インスタンスにアタッチするIAMロール」と覚えてもらえたらOKです。EC2インスタンスにインスタンスプロファイル(≒IAMロール)をアタッチして、EC2インスタンス自身に権限を付与させることができます。

インスタンスプロファイルを作成する。

まずは、IAMロールに付与するIAMポリシーを2つ作成します。

1つ目は、信頼ポリシー。今から作成するIAMロールをEC2が引き受ける(AssumeRoleと言います)ことを許可します。

terraform/system/modules/web/ec2_iam.tf

# ECSコンテナインスタンスの信頼ポリシー。
data "aws_iam_policy_document" "ecs_container_instance_web_sts" {
  statement {
    actions = [
      "sts:AssumeRole"
    ]
    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

2つ目は、EC2インスタンスがECSを使う上で必要なポリシーを作成します。(ただ、今回は、AWSで既に用意されているポリシー(AWS管理ポリシー)を使用するため、厳密には作成していません。そのため、Terraformも data を使っています。)

terraform/system/modules/web/ec2_iam.tf

# ECSコンテナインスタンス用に用意されているAWS管理ポリシー。
data "aws_iam_policy" "ecs" {
  name = "AmazonEC2ContainerServiceforEC2Role"
}

「AmazonEC2ContainerServiceforEC2Role」はECSで必要な権限が設定されているIAMポリシーです。

次は、IAMロールを作成します。また、↑で作成したIAMポリシーも忘れずにアタッチします。

使用するリソースは、 aws_iam_roleaws_iam_role_policy_attachment です。

terraform/system/modules/web/ec2_iam.tf

resource "aws_iam_role" "ecs_container_instance_web" {
  name               = "ecs-container-instance-web"

  # 信頼ポリシーは assume_role_policy としてアタッチします。
  assume_role_policy = data.aws_iam_policy_document.ecs_container_instance_web_sts.json
}

resource "aws_iam_role_policy_attachment" "ecs_web" {
  role       = aws_iam_role.ecs_container_instance_web.name
  policy_arn = data.aws_iam_policy.ecs.arn
}

そして、最後にIAMロールからインスタンスプロファイルを作成します。

使用するリソースは、 aws_iam_instance_profile です。

terraform/system/modules/web/ec2_iam.tf

resource "aws_iam_instance_profile" "ecs_web" {
  name = "ecs-web"
  role = aws_iam_role.ecs_container_instance_web.name
}

以上でインスタンスプロファイルの作成は完了です。

コンテナインスタンスにアタッチするセキュリティグループを作成する。

コンテナインスタンスにアタッチするセキュリティグループを作成します。セキュリティグループのルールは、後ほど追加していくため、今回は「全ての送信を許可」するルールのみ作成します。

まずは、セキュリティグループを作成します。

使用するリソースは、 aws_security_group です。

terraform/system/modules/web/ec2_sg.tf

resource "aws_security_group" "ecs_container_instance_web" {
  name   = "ecs-container-instance-web"
  vpc_id = var.vpc_id
  tags = {
    Name = "ecs-container-instance-web"
  }
}

セキュリティグループのルールは、aws_security_groupegressingress を利用するか、 aws_security_group_rule を利用するかの2パターンの指定方法があり、どちらか一方しか使用できないので注意! aws_security_groupの方はルールを1組ずつしか設定することができないため、aws_security_group_rule こちらを使用します。

作成するルールは「全ての送信を許可」です。

terraform/system/modules/web/ec2_sg.tf

resource "aws_security_group_rule" "ecs_container_instance_to_outbound" {
  type              = "egress"
  security_group_id = aws_security_group.ecs_container_instance_web.id
  from_port         = 0  # 「0」だと指定なしとなります。
  to_port           = 0  # 「0」だと指定なしとなります。
  protocol          = -1 # 「-1」だと指定なしとなります。
  cidr_blocks       = ["0.0.0.0/0"]
}

以上でセキュリティグループの作成が完了です。

Launch Templateを作成する。

Launch Templateは、EC2インスタンスを作成するためのテンプレートです。ここに諸々設定しておくことで、インスタンス作成を簡略化することができます。

コンテナインスタンスの場合、後で作成するAutoScalingGroupからLaunch Templateを使ってインスタンス作成することになります。

変数を作成する。

Launch Templateを作成するにあたり、いくつか変数にして使い回せるようにしようと思います。

terraform/system/staging/variables.tf

# コンテナインスタンスの元になるAMI。
variable "ami_id" {
  type        = string
  default     = "ami-0a428a8bcfce0f804"
  description = "パラメータストアから取得した2021/11/25時点のAMI。https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-optimized_AMI.html"
}

# コンテナインスタンスのインスタンスタイプ。
variable "instance_type" {
  type    = string
  default = "t3.nano"
}

# コンテナインスタンスに使用するSSHキー。
variable "key_name" {
  type    = string
  default = "ecs-container-stg"
}

# コンテナインスタンスが所属するECSクラスター名。
variable "cluster_name" {
  type    = string
  default = "study-infra-web"
}

こっちに追加することも忘れずに。

terraform/system/modules/web/variables.tf

variable "ami_id" {}
variable "instance_type" {}
variable "key_name" {}
variable "cluster_name" {}

user dataで使用するシェルスクリプトを作成する。

EC2インスタンス起動時に、ECSクラスターに参加するようにuser dataに処理を書きます。

terraform/system/modules/web/template/userdata.sh

#!/bin/bash
echo ECS_CLUSTER=${CLUSTER_NAME} >> /etc/ecs/ecs.config

template_file を使って、↑で作成したシェルスクリプトをTerraformで読み込みます。また、 template_file の中でさらに templatefile([読み込みたいファイルのパス], {[読み込んだファイルの中で使っている変数=設定したい値]}) を使っているので注意。

terraform/system/modules/web/ec2_launch_template.tf

data "template_file" "userdata" {
  template = templatefile("${path.module}/template/userdata.sh", {
    CLUSTER_NAME = var.cluster_name
  })
}

Launch Templateを作成する。

使用するリソースは、 aws_launch_template です。

terraform/system/modules/web/ec2_launch_template.tf

resource "aws_launch_template" "web" {
  name = "ecs-container-instance-web"

  # LauchTemplateの更新時にデフォルトのバージョンをアップするかどうかの設定。default_versionと競合するため、どちらか設定する。
  # LaunchTemplateを更新したら、その更新したバージョンをデフォルトにしたいため、有効にする。
  update_default_version = true

  # EC2インスタンスに付与するEBSボリュームの設定。
  block_device_mappings {
    ebs {
      delete_on_termination = true
      encrypted             = false
      volume_size           = 20
      volume_type           = "gp3"
    }
  }

  # インスタンスタイプがT系の場合、CPUクレジットを無制限にするか標準にするかの設定。
  # 無制限だと課金の可能性があるため、標準を選択。
  credit_specification {
    cpu_credits = "standard"
  }

  # trueにすると終了保護が有効化される。今回は無効。
  disable_api_termination = false

  # EBS最適化は使用しないため無効。
  ebs_optimized = false

  # 前半で作成したインスタンスプロファイルを設定。
  iam_instance_profile {
    arn = aws_iam_instance_profile.ecs_web.arn
  }

  # AMI。
  image_id = var.ami_id

  # インスタンスをシャットダウンした時のデフォルトの動作。
  # 「終了」ではなく「停止」を選択。
  instance_initiated_shutdown_behavior = "stop"

  # インスタンスタイプ。
  instance_type = var.instance_type

  # SSHキー。
  key_name      = var.key_name

  # インスタンスメタデータの利用に関する設定。
  # https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
  metadata_options {
    http_endpoint = "enabled"
    # セキュアなIMDSv2しか利用できないようにする。
    http_tokens = "required"
    # コンテナ環境の場合、デフォルトの1だとコンテナ→インスタンスとなり応答が返ってこない。そのため2を設定する。
    # https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/WindowsGuide/instancedata-data-retrieval.html
    http_put_response_hop_limit = 2
    http_protocol_ipv6          = "disabled"
  }

  # 拡張モニタリングの設定。課金対象のため今回は無効。
  monitoring {
    enabled = false
  }

  network_interfaces {
    # パブリックIPは付与する必要がないため無効。
    associate_public_ip_address = false
    # EC2インスタンス削除時にネットワークインターフェースも削除する。
    delete_on_termination       = true
  }

  # セキュリティグループ。
  vpc_security_group_ids = [
    aws_security_group.ecs_container_instance_web.id
  ]

  # user data。Base64でエンコードする必要があるため、base64encode()を使用。
  user_data = base64encode(data.template_file.userdata.rendered)

  # OS停止時にメモリの値をディスクに書き出し、OS起動時にディスクから読み出しメモリにキャッシュする機能。
  hibernation_options {
    configured = false
  }
}

Terraformの実行

1. terraform plan を実行する。

$ cd /tf/system/staging
$ terraform plan

...

Plan: 6 to add, 0 to change, 0 to destroy.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply"
now.

2. terraform apply を実行する。

$ terraform apply

...

Plan: 6 to add, 0 to change, 0 to destroy.

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**

module.web.aws_security_group.ecs_container_instance_web: Creating...
module.web.aws_iam_role.ecs_container_instance_web: Creating...
module.web.aws_security_group.ecs_container_instance_web: Creation complete after 2s [id=sg-0189f3fe2fa5ecef9]
module.web.aws_security_group_rule.ecs_container_instance_to_outbound: Creating...
module.web.aws_security_group_rule.ecs_container_instance_to_outbound: Creation complete after 0s [id=sgrule-3187669363]
module.web.aws_iam_role.ecs_container_instance_web: Creation complete after 3s [id=ecs-container-instance-web]
module.web.aws_iam_role_policy_attachment.ecs_web: Creating...
module.web.aws_iam_instance_profile.ecs_web: Creating...
module.web.aws_iam_role_policy_attachment.ecs_web: Creation complete after 2s [id=ecs-container-instance-web-20211213003411293000000001]
module.web.aws_iam_instance_profile.ecs_web: Creation complete after 4s [id=ecs-web]
module.web.aws_launch_template.web: Creating...
module.web.aws_launch_template.web: Creation complete after 0s [id=lt-0e1a580ca5f11b7da]

Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
/tf/system/staging #

以上で、Launch Templateの作成が完了です。

プロフィール
この記事を書いた人
katsuya

SESからキャリアをスタートし、現在はフリーランスとして活動しています。フリーランスになってから6年で年収1,000万円を達成しました。「Study Infra」では、今までの経験やITインフラに関する情報を発信中です。

katsuyaをフォローする
AWSAWSチュートリアルEC2
スポンサーリンク
シェアする
katsuyaをフォローする

コメント

タイトルとURLをコピーしました