😊

VPC peeringとEC2使ってping飛ばしてみた

2023/06/22に公開

はじめに

VPC peeringを作成して、それぞれのVPC内にあるEC2でpingが通るか試してみました。

ついでに、今流行りのEC2 Instance Connect(EIC)を使ってプライベートサブネットのEC2に接続してみました。
そしていくつか詰まった点も解説していきます。

概要

今回はterraformを使って構築を行なっています。
terraformはあまり触ったことがないので、参考程度に見て下さい。

今回のファイル構成はこんな感じです。シンプルですね。

.
├── main.tf
├── provider.tf
└── variables.tf

一応、今回の構成はgithubにコードをあげています。
https://github.com/Sou-y-g/CT-Subject/tree/main/week2/ct2-2

最終的にはこんな感じの構成になります。

VPCとサブネットの作成

ではまずVPCとプライベートサブネットを作成していきます。

ファイル構成としてはリソースと変数を分けていますが、わかりやすい?ように同時に記載します。

変数 => リソース作成
の流れで書いていきます。

#AZの変数
variable "availability_zone1a" {
  description = "The availability zones1a to use"
  type        = string
  default     ="ap-northeast-1a"
}

#VPC1の変数
variable "vpc1_cidr" {
  description = "The CIDR block for the VPC"
  type        = string
  default     = "10.0.0.0/16"
}

#subnet1の変数
variable "private1_subnet_cidr" {
  description = "The CIDR block for the private1 subnet"
  type        = string
  default     = "10.0.1.0/24"
}

#VPC1作成
resource "aws_vpc" "vpc1" {
  cidr_block = var.vpc1_cidr
}

#VPC1のsubnet
resource "aws_subnet" "private1" {
  vpc_id = aws_vpc.vpc1.id
  cidr_block = var.private1_subnet_cidr
  availability_zone = var.availability_zone1a
  map_public_ip_on_launch = false
}

#VPC2の変数
variable "vpc2_cidr" {
  description = "The CIDR block for the VPC"
  type        = string
  default     = "172.18.0.0/16"
}

#subnet2の変数
variable "private2_subnet_cidr" {
  description = "The CIDR block for the private2 subnet"
  type        = string
  default     = "172.18.1.0/24"
}

#VPC2作成
resource "aws_vpc" "vpc2" {
  cidr_block = var.vpc2_cidr
}

#VPC2のサブネット
resource "aws_subnet" "private2" {
  vpc_id = aws_vpc.vpc2.id
  cidr_block = var.private2_subnet_cidr
  availability_zone = var.availability_zone1a
  map_public_ip_on_launch = false
}

実際は全てにtagを入れていますが、長くなるのでカットしています。
これでVPCとサブネットができました。

特に困ったことはありませんでした。

デフォルトでパブリックIPはfalseになっていますが、
プライベートサブネットと分かりやすいように明示的にコードを追加しています。

map_public_ip_on_launch = false

VPC peeringの作成

では、次にこのVPCをpeering接続します。

#VPC peering作成
resource "aws_vpc_peering_connection" "peering" {
  vpc_id = aws_vpc.vpc1.id
  peer_vpc_id = aws_vpc.vpc2.id
  auto_accept = true
}

設定項目は少ないですね。
接続元のVPCと接続先のVPCを設定するだけです。

auto_accept = true

これは接続先のVPCでの承認を自動的に行う設定です。
承認というのは、本当に接続しても大丈夫ですか?に対する承認です。

セキュリティ要件が厳しく手動承認が必要な場合はfalseですね。

ルートテーブルの作成

次はルートテーブルを作成していきます。

ルートテーブルは
テーブルを作成、ルートを作成、テーブルにルートを入れる。
という流れです。

#subnet1の変数
variable "subnet1_route_table" {
  description = "The CIDR Block for private subnet route table"
  type = string
  default = "172.18.0.0/16"
}

#subnet1のルートテーブル
resource "aws_route_table" "subnet1" {
  vpc_id = aws_vpc.vpc1.id
}

#ルート
resource "aws_route" "subnet1" {
  destination_cidr_block = var.subnet1_route_table
  route_table_id = aws_route_table.subnet1.id
  vpc_peering_connection_id = aws_vpc_peering_connection.peering.id
}

#ルートの割り当て
resource "aws_route_table_association" "subnet1" {
  subnet_id = aws_subnet.private1.id
  route_table_id = aws_route_table.subnet1.id
}

#subnet2の変数
variable "subnet2_route_table" {
  description = "The CIDR Block for private subnet route table"
  type = string
  default = "10.0.0.0/16"
}

#subnet2のルートテーブル
resource "aws_route_table" "subnet2" {
  vpc_id = aws_vpc.vpc2.id
}

#ルート
resource "aws_route" "subnet2" {
  destination_cidr_block = var.subnet2_route_table
  route_table_id = aws_route_table.subnet2.id
  vpc_peering_connection_id = aws_vpc_peering_connection.peering.id
}

#ルートの割り当て
resource "aws_route_table_association" "subnet2" {
  subnet_id = aws_subnet.private2.id
  route_table_id = aws_route_table.subnet2.id
}

出来上がったルートテーブルはこのようになります。

今回はシンプルにVPC同士を繋げるだけなので
余計なもの(InternetGatewayやNatGateway)は作成していません。

ここまでで、ネットワーク部分は作成が完了しました。
次からEC2関連のリソースを作成していきます。

セキュリティグループの作成

#EC1のセキュリティグループ
resource "aws_security_group" "sg1" {
  name = "allow ICMP"
  vpc_id = aws_vpc.vpc1.id
}

resource "aws_security_group_rule" "sg1_in" {
    type        = "ingress"
    from_port   = -1
    to_port     = -1
    protocol    = "ICMP"
    cidr_blocks = ["172.18.0.0/16"]

    security_group_id = aws_security_group.sg1.id
}

resource "aws_security_group_rule" "sg1_out" {
    type        = "egress"
    from_port   = -1
    to_port     = -1
    protocol    = "ICMP"
    cidr_blocks = ["172.18.0.0/16"]

    security_group_id = aws_security_group.sg1.id
}

#EC2のセキュリティグループ
resource "aws_security_group" "sg2" {
  name = "allow ICMP"
  vpc_id = aws_vpc.vpc2.id
}

resource "aws_security_group_rule" "sg2_in" {
    type        = "ingress"
    from_port   = -1
    to_port     = -1
    protocol    = "ICMP"
    cidr_blocks = ["10.0.0.0/16"]

    security_group_id = aws_security_group.sg2.id
}

resource "aws_security_group_rule" "sg2_out" {
    type        = "egress"
    from_port   = -1
    to_port     = -1
    protocol    = "ICMP"
    cidr_blocks = ["10.0.0.0/16"]

    security_group_id = aws_security_group.sg2.id
}

どちらのセキュリティグループもping疎通のためにICMPを許可しています。
ここで少し詰まりました。

詰まったところ

今回はpingの疎通を確認するために、ICMPの通信を許可しています。
しかし、VPC1のEC2からVPC2のEC2に対してpingコマンドを打っても反応がありませんでした。

原因は、アウトバウンドの通信を設定していなかったから、でした。
(上記のコードは修正した後のコードです。)

セキュリティグループをマネジメントコンソールから作成すると
デフォルトでアウトバウンドでは全ての通信を通す設定になっています。

terraformなどのIaCツールでは明示的に設定しなければならないことを忘れていました。

もう一点、つまづきポイントとしては、ICMPではなくTCPで設定してしまうパターンでしょうか。
pingを確認するのであればICMPが必要です。

EC2の作成

ではEC2を作成します。

#keyの変数
variable "key_name" {
  description = "EC2 key"
  default = "ct_key"
}

# 最新のAMIを取得
data "aws_ssm_parameter" "amazonlinux_2023" {
  name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64" # x86_64
}

# サブネット1のEC2
resource "aws_instance" "ec2-1"{
  ami                         = data.aws_ssm_parameter.amazonlinux_2023.value
  instance_type               = "t2.micro"
  availability_zone           = var.availability_zone1a
  vpc_security_group_ids      = [aws_security_group.sg1.id, aws_security_group.eic_to_ec2.id]
  subnet_id                   = aws_subnet.private1.id
  associate_public_ip_address = false
  key_name                    = var.key_name
}

# サブネット2のEC2
resource "aws_instance" "ec2-2"{
  ami                         = data.aws_ssm_parameter.amazonlinux_2023.value
  instance_type               = "t2.micro"
  availability_zone           = var.availability_zone1a
  vpc_security_group_ids      = [aws_security_group.sg2.id]
  subnet_id                   = aws_subnet.private2.id
  associate_public_ip_address = false
  key_name                    = var.key_name
}

EC2はt2.microを使用しています。
AMIは最新のLinux2013を取得しています。

これである程度の準備は終わりました。
ここからEICエンドポイントを使ってEC2にログインしていきます。

EICの作成

今までパブリックサブネットのEC2にアクセスするには
パブリックサブネットの踏み台EC2やSSM session managerを使って接続する必要がありました。

それが、このEC2 Instance Connectを使うことで接続が簡単にできるようになりました。
今回はそんな便利なサービスを使います。

ただし、まだ発表されたばかりなのでterraformで作成することができませんでした。
(できるのかな?terraformの公式ドキュメントには載っていなかった)

そのため、セキュリティグループはterraform
エンドポイントはマネジメントコンソール
で作成しました。

#自分のIPアドレスを取得
data "http" "ifconfig" {
  url = "http://ipv4.icanhazip.com/"
}

variable "allowed-myip" {
  default = null
}

locals {
  current-ip   = chomp(data.http.ifconfig.body)
  allowed-myip = (var.allowed-myip == null) ? "${local.current-ip}/32" : var.allowed-myip
}

# EICエンドポイントのセキュリティグループ
resource "aws_security_group" "sg_eic" {
  name = "for EIC"
  vpc_id = aws_vpc.vpc1.id
}

#アウトバウンド SSHのみ
resource "aws_security_group_rule" "sg_eic" {
    type        = "egress"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16", local.allowed-myip]

    security_group_id = aws_security_group.sg_eic.id
}

#EICエンドポイント→EC2の通信を許可するためのEC2のセキュリティグループ
resource "aws_security_group" "eic_to_ec2" {
  name = "EIC to EC2"
  vpc_id = aws_vpc.vpc1.id
}

resource "aws_security_group_rule" "eic_to_ec2_in" {
    type        = "ingress"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    source_security_group_id = aws_security_group.sg_eic.id
    security_group_id = aws_security_group.eic_to_ec2.id
}

resource "aws_security_group_rule" "eic_to_ec2_out" {
    type        = "egress"
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    source_security_group_id = aws_security_group.sg_eic.id
    security_group_id = aws_security_group.eic_to_ec2.id
}

ではここからEICエンドポイントを作成します。

マネジメントコンソールで作成したEC2を選択します。

次に、「EC2 Instance Connect エンドポイントを使用して接続する」を選択。
「エンドポイントを選択する」のプルダウンからエンドポイントを作成する。

画面が切り替わるので、EC2 Instance Connect Endpointを選択。
エンドポイントを接続するEC2のVPC、サブネット、セキュリティグループを選択する。

この時選ぶセキュリティグループは上のコードの
EICエンドポイント用のセキュリティグループです。

あとは作成ボタンを押して数分待ちます。
...

それでは、作成できたようなので接続してみます。

お、接続できました!
では、pingを飛ばしてみましょう!

ちゃんと飛びましたね。
これでVPC peeringを通ってVPC間で接続できることが確認できました。

詰まったところ

ここで、EICの詰まったところを書いておきます。

EICエンドポイントを作成した後、EC2に接続使用とするとエラーが出ました。
なんでかな?と思い、いろいろ調べた結果EICエンドポイントにセキュリティグループを割り当てるのを忘れていました。

こちらの記事を見ながら一つずつセキュリティグループを作成して
割り当てることで繋がるようになりました。
https://dev.classmethod.jp/articles/ec2-instance-connect-endpoint-private-access/
ただエンドポイントを作れば繋がる!という訳ではないので注意です。

ちなみに、これはセキュリティーグループがENIに割り当てられているからでしょう。
EC2も細かくいうと、EC2自身にセキュリティグループが割り当てられてるのではなく、
EC2にアタッチされたENIにセキュリティグループが割り当てられているんですね。

まとめ

VPC peeringをterraformで作成してみましたが、公式ドキュメントを睨めっこしながらなんとか作成できました。
IaCを使うと細かい設定まできちんと目を通さないといけないので大変勉強になりました。

あと、流行りの?EICを使ってみましたが、便利すぎてびっくりしました。
今後も使う機会が多いと思うのでセキュリティグループを付け忘れないように注意します。

GitHubで編集を提案

Discussion