🐟

[ハンズオン形式]AWSネットワークで通信できない場合(初級編ルーティング①)

2023/03/04に公開

1. はじめに

1.1 本記事の目的

本記事の目的はAWSネットワークにおける、主にルーティングの設定に起因して通信できない場合のトラブルシューティング方法を理解し、AWS環境のトラブルシューティング能力を向上させることです。
実際に手を動かしながらトラブルシューティングすることで知識が定着しやすいと思いますので、本記事中には環境構築できるようにTerraformコードを記載してあります。

また、以下のような順を追って説明します。

  1. Terraformによって問題が埋め込まれた環境を作成
  2. 問題点特定 & 解決方法
  3. 問題点を解消したTerraformによって意図した環境が作られていることを確認

"問題点特定 & 解決方法"を見る前に自力でトラブルシューティングしたい方は以下をゴールとして挑戦してみてください。

Terraformでapplyした際に出力されるEC2インスタンスにaws ssm start-sessionコマンドを利用して接続し、EC2インスタンスから1.1.1.1に対してpingコマンドで疎通確認ができること。

1.2 前提条件

本記事を読むにあたり、以下に対するある程度の理解がある前提としています。

1.3 注意事項

  • 記載のTerraformコードを実行する際には適切なIAM権限を付与してください
  • 記載のTerraformコードを実行したことによって生じた一切の障害について当方はその責任を負いません
  • 作成される環境は$0.1未満/日程度の費用かと思いますが、不要になった際にはterraform destroyコマンドですぐに削除することをおすすめします

2. 今回フォーカスするシナリオ

本記事は [ハンズオン形式]AWSネットワークで通信できない場合(初級編ルーティング①) と題している通り、主にルーティングに関するトラブルシューティングにフォーカスします。
具体的には、以下のシナリオに沿って進めていきます。

  • シナリオ: EC2インスタンスからインターネットに接続できない

3. トラブルシューティングの前に

3.1 トラブルシューティングに必要な情報の収集

今回のようなトラブルシューティングをする前には、問題を正確に特定するために必要な情報を収集することが重要です。
具体的には、環境構成およびネットワークに関する情報を収集しましょう。

  • VPCのCIDRブロック
  • サブネットのCIDRブロック
  • 紐付けされているルートテーブル

また、今回の「インターネットに接続できない」という場合は以下の情報も追加であると良いでしょう。

  • インターネットゲートウェイがアタッチされているかどうか
  • ルートテーブルが正しく構成されているかどうか
    • 意図したルーティングが記載されているか?
    • 意図する宛先のステータスが"blackhole"になっていないか?

トラブルシューティングの実践

4.1 シナリオ: EC2インスタンスからインターネットに接続できない

4.1.1 インターネット接続不可状態のTerraform実装例

以下は、EC2インスタンスからインターネットに接続できない状態のTerraform実装例です。
この実装には後述する通り、いくつかの問題点があります。
Terraformコード中には最終的な動作確認のためにAWS Systems Manager Session ManagerでEC2インスタンスに接続できるよう、EC2インスタンスへIAMロールを付与しています。
※コード中では便宜上public subnetと表記しています。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.1.0"
    }
  }
}

locals {
  name_tag = "scenario-1"
}

provider "aws" {
  region = "ap-northeast-1"
  default_tags {
    tags = {
      scenario_name = local.name_tag
    }
  }
}

# Create VPC
resource "aws_vpc" "scenario_1_vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  instance_tenancy     = "default"

  tags = {
    Name = "${local.name_tag}-vpc"
  }
}

# Create Subnet
resource "aws_subnet" "scenario_1_public_subnet" {
  vpc_id     = aws_vpc.scenario_1_vpc.id
  cidr_block = "10.0.1.0/24"
  tags = {
    Name = "${local.name_tag}-public-subnet"
  }
}

# Get latest Amazon Linux 2 AMI ID from SSM parameter store
data "aws_ssm_parameter" "amzn2_ami" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2"
}

# Create IAM for SSM session manager
data "aws_iam_policy_document" "assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "ssm_sm_role" {
  name               = "ssm-session-manager-role"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

data "aws_iam_policy" "ssm" {
  arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_role_policy_attachment" "default" {
  role       = aws_iam_role.ssm_sm_role.name
  policy_arn = data.aws_iam_policy.ssm.arn
}

resource "aws_iam_instance_profile" "ssm" {
  name = "InstanceProfile"
  role = aws_iam_role.ssm_sm_role.name
}

# Create EC2 instance
resource "aws_instance" "scenario_1_public_instance" {
  ami                  = data.aws_ssm_parameter.amzn2_ami.value
  instance_type        = "t3.nano"
  iam_instance_profile = aws_iam_instance_profile.ssm.name
  subnet_id            = aws_subnet.scenario_1_public_subnet.id

  tags = {
    Name = "${local.name_tag}-public-instance"
  }
}

output "ec2_instance_id" {
  description = "EC2 Instance ID"
  value       = aws_instance.scenario_1_public_instance.id
}

再掲にはなりますが、"問題点特定 & 解決方法"を見る前に自力でトラブルシューティングしたい方は以下をゴールとして挑戦してみてください。

Terraformでapplyした際に出力されるEC2インスタンスにaws ssm start-sessionコマンドを利用して接続し、EC2インスタンスから1.1.1.1に対してpingコマンドで疎通確認ができること。

4.1.2 問題点特定 & 解決方法

問題点特定をするために、まずは4.1.1の環境構成図を見てみましょう。

図にしてみると問題点を特定しやすいと思うのですが、以下の問題があります。

  1. Internet Gatewayがない
    VPC内からインターネットへ接続する際にはInternet Gatewayが必要になります。

  2. EC2が配置されているSubnetからインターネットへのルートがない
    インターネットへの通信をするためには「NAT GWを設置してルートを追加する」 or 「Internet Gatewayへのルートを追加する(Public Subnetとする)」などの対処法がありますが、今回は後者の方法でインターネットへの通信を実現させます。

  3. EC2にPublic IPアドレスが付与されていない
    こちらは少しわかり辛いかもしれませんが、2.で記載した対応をするのであればEC2に対してPuclic IPを付与する必要があります。
    なお、自力で挑戦した方でNAT GWを利用する選択肢をした方はそれでも問題ないです(実務では要件と照らし合わせて設定してください)

以下に1. ~ 3.の問題を解決するために追加が必要なTerraformコード例を記述します。

Internet Gatewayを追加

# Create Internet Gateway
resource "aws_internet_gateway" "scenario_1_igw" {
  vpc_id = aws_vpc.scenario_1_vpc.id
  tags = {
    Name = "${local.name_tag}-igw"
  }
}

ルートテーブル作成 & アソシエート

# Create Route
resource "aws_route_table" "scenario_1_public_routetable" {
  vpc_id = aws_vpc.scenario_1_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.scenario_1_igw.id
  }

  tags = {
    Name = "${local.name_tag}-public-routetable"
  }
}

# Assosiate Route table
resource "aws_route_table_association" "scenario_1_public" {
  route_table_id = aws_route_table.scenario_1_public_routetable.id
  subnet_id      = aws_subnet.scenario_1_public_subnet.id
}

EC2にPublic IPアドレスを付与

resource "aws_instance" "scenario_1_public_instance" {}内に以下を追加

  associate_public_ip_address = true

4.1.3 インターネット接続可能状態のTerraform実装例

以下は、パブリックサブネットに配置してあるEC2インスタンスからインターネットに接続できる状態のTerraform実装例です。
4.1.1のTerraformコードから追加した行頭には+が付与されています。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.1.0"
    }
  }
}

locals {
  name_tag = "scenario-1"
}

provider "aws" {
  region = "ap-northeast-1"
  default_tags {
    tags = {
      scenario_name = local.name_tag
    }
  }
}

# Create VPC
resource "aws_vpc" "scenario_1_vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  instance_tenancy     = "default"

  tags = {
    Name = "${local.name_tag}-vpc"
  }
}

# Create Subnet
resource "aws_subnet" "scenario_1_public_subnet" {
  vpc_id     = aws_vpc.scenario_1_vpc.id
  cidr_block = "10.0.1.0/24"
  tags = {
    Name = "${local.name_tag}-public-subnet"
  }
}

+ # Create Internet Gateway
+ resource "aws_internet_gateway" "scenario_1_igw" {
+   vpc_id = aws_vpc.scenario_1_vpc.id
+   tags = {
+     Name = "${local.name_tag}-igw"
+   }
+ }
+
+ # Create Route
+ resource "aws_route_table" "scenario_1_public_routetable" {
+   vpc_id = aws_vpc.scenario_1_vpc.id
+
+   route {
+     cidr_block = "0.0.0.0/0"
+     gateway_id = aws_internet_gateway.scenario_1_igw.id
+   }
+
+   tags = {
+     Name = "${local.name_tag}-public-routetable"
+   }
+ }
+
+ # Assosiate Route table
+ resource "aws_route_table_association" "scenario_1_public" {
+   route_table_id = aws_route_table.scenario_1_public_routetable.id
+   subnet_id      = aws_subnet.scenario_1_public_subnet.id
+ }
+
# Get latest Amazon Linux 2 AMI ID from SSM parameter store
data "aws_ssm_parameter" "amzn2_ami" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2"
}

# Create IAM for SSM session manager
data "aws_iam_policy_document" "assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "ssm_sm_role" {
  name               = "ssm-session-manager-role"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

data "aws_iam_policy" "ssm" {
  arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_role_policy_attachment" "default" {
  role       = aws_iam_role.ssm_sm_role.name
  policy_arn = data.aws_iam_policy.ssm.arn
}

resource "aws_iam_instance_profile" "ssm" {
  name = "InstanceProfile"
  role = aws_iam_role.ssm_sm_role.name
}

# Create EC2 instance
resource "aws_instance" "scenario_1_public_instance" {
  ami                  = data.aws_ssm_parameter.amzn2_ami.value
  instance_type        = "t3.nano"
  iam_instance_profile = aws_iam_instance_profile.ssm.name
  subnet_id            = aws_subnet.scenario_1_public_subnet.id

+   associate_public_ip_address = true
+
  tags = {
    Name = "${local.name_tag}-public-instance"
  }
}

output "ec2_instance_id" {
  description = "EC2 Instance ID"
  value       = aws_instance.scenario_1_public_instance.id
}

問題点を解消した環境構成図です。問題点となっていた以下3点が解消できていることがわかるかと思います。

  • ①Internet Gateway作成
  • ②Internet Gatewayへのルート追加
  • ③EC2へPublic IPの付与

作成したEC2インスタンスへ接続 && インターネットへの接続確認

端末にてAWS CLIのセットアップAWS CLI用のSystems Manager Session Managerプラグイン導入をすることで、AWS CLI経由でEC2インスタンスに接続することが可能です。

以下にEC2インスタンスへ接続するためのaws ssm start-sessionコマンドとインターネットへの接続確認を行うためのpingコマンドを記載します。

$ aws ssm start-session --target "i-0ea2a26412976ca8e"

Starting session with SessionId: Administrator-0b92b039b2d04421c
sh-4.2$ 
sh-4.2$ ping 1.1.1.1 -c 4
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=45 time=1.99 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=45 time=2.02 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=45 time=2.01 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=45 time=2.04 ms

--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 1.998/2.019/2.047/0.048 ms

まとめ

本記事では、[ハンズオン形式]AWSネットワークで通信できない場合(初級編ルーティング①) として、AWSネットワークにおける主にルーティングに関するトラブルシューティングの方法についてハンズオン形式で紹介しました。
そのまま使えるTerraformコード例を記載してあるため、実際に手を動かしてみていただければと思います。
また、この記事を読んでくださった方がAWSネットワークに対しての理解を深めていただけると幸いです。

反響が大きければまた別のハンズオンも作成しようと思いますので、イイねやコメントいただけると非常に嬉しいです。

ここまで読んでくださってありがとうございました。

Discussion