この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
コンバンハ、千葉(幸)です。
ルートテーブル、きちんと管理できていますか?
そこまで量が多くない環境であれば目で確認していくこともできますが、一定量を超えると人力では太刀打ちできなくなります。
ある一定規模の環境で、以下のような確認をしたい機会がありました。
- この NAT Gateway 向けのルートを持つサブネットってどれだ?
- S3 向けの VPC エンドポイント(ゲートウェイ)へのルートってどのルートテーブルに入っている?
- パブリックサブネットだけ一覧化したい!
早々にマネジメントコンソールとのにらめっこを諦めて、AWS CLI を駆使して対応しました。ポイントとなるのはjq
と--filters
オプションです。
それぞれの使い方のメモを残しておきます。
jq を使って必要な情報のみ出力する
jq
を使うと何が嬉しいかを見ていきましょう。
ルートテーブルを一覧出力するコマンド aws ec2 describe-route-tables
を(なにも指定せず)そのまま実行した時の出力イメージは以下の通りです。
{
"RouteTables": [
{
"Associations": [
{
"Main": true,
"RouteTableAssociationId": "rtbassoc-0df3f54e06EXAMPLE",
"RouteTableId": "rtb-09ba434c1bEXAMPLE"
}
],
"PropagatingVgws": [],
"RouteTableId": "rtb-09ba434c1bEXAMPLE",
"Routes": [
{
"DestinationCidrBlock": "10.0.0.0/16",
"GatewayId": "local",
"Origin": "CreateRouteTable",
"State": "active"
},
{
"DestinationCidrBlock": "0.0.0.0/0",
"NatGatewayId": "nat-06c018cbd8EXAMPLE",
"Origin": "CreateRoute",
"State": "blackhole"
}
],
"Tags": [],
"VpcId": "vpc-0065acced4EXAMPLE",
"OwnerId": "111122223333"
},
{
"Associations": [
{
"Main": true,
"RouteTableAssociationId": "rtbassoc-9EXAMPLE",
"RouteTableId": "rtb-a1eec7de"
}
],
"PropagatingVgws": [],
"RouteTableId": "rtb-a1eec7de",
"Routes": [
{
"DestinationCidrBlock": "172.31.0.0/16",
"GatewayId": "local",
"Origin": "CreateRouteTable",
"State": "active"
},
{
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": "igw-fEXAMPLE",
"Origin": "CreateRoute",
"State": "active"
}
],
"Tags": [],
"VpcId": "vpc-3EXAMPLE",
"OwnerId": "111122223333"
},
------以下略------
ハイライト部がひとつのルートテーブルに関する情報の始まりを表します。
ひとつのルートテーブルだけでも情報量が多いですね。これがルートテーブルの数だけ出力されていきます。大抵の場合において必要となる情報はこの一部のみですので、jq
を使用してひとつのルートテーブルあたりの情報を減らします。
jq
のインストール方法や、そもそも何ぞやという部分については本エントリでは割愛します。
特定の値のみ出力する
ルートテーブル ID だけを出力する場合。
$ aws ec2 describe-route-tables\
| jq '.RouteTables[].RouteTableId'
"rtb-xxxb19fd681afbc82"
"rtb-xxxe65ace9b3d862e"
"rtb-xxx4253d4d1a43486"
"rtb-xxxeed7382f10fa48"
"rtb-xxxc22e8a1cf8d06b"
"rtb-xxxc8ccc385f199d6"
"rtb-xxxa20f086b660e42"
"rtb-xxx3b90c62efdef3f"
"rtb-xxxc9a553b256976c"
------以下略------
ルートだけを出力する場合。
$ aws ec2 describe-route-tables\
| jq '.RouteTables[].Routes[]'
{
"DestinationCidrBlock": "192.168.0.0/16",
"GatewayId": "local",
"Origin": "CreateRouteTable",
"State": "active"
}
{
"DestinationCidrBlock": "10.0.0.0/16",
"Origin": "CreateRoute",
"State": "active",
"VpcPeeringConnectionId": "pcx-xxx34603"
}
{
"DestinationCidrBlock": "0.0.0.0/0",
"NatGatewayId": "nat-xxx094c175acfbfbd",
"Origin": "CreateRoute",
"State": "active"
}
------以下略------
ルートの中の宛先 CIDR ブロック(IPv4)のみを出力する場合。(IPv6 CIDR DestinationIpv6CidrBlock
を持つルートがあった場合、該当部はnull
となります。)
$ aws ec2 describe-route-tables\
| jq '.RouteTables[].Routes[].DestinationCidrBlock'
"192.168.0.0/16"
"10.0.0.0/16"
"0.0.0.0/0"
null
"172.31.0.0/16"
------以下略------
条件付きで特定の値のみ出力する
タグのうち、キーが Name であるもののみ Value を出力する場合。
$ aws ec2 describe-route-tables\
| jq '.RouteTables[].Tags[] | select(.Key == "Name").Value'
"DEV-ABC-PUB"
"PRO-MIRROR-PRV"
"STG-EFG-PUB"
"PRO-EFG-PUB"
------以下略------
ソートして出力する
ルートテーブルIDの昇順にソートしてルートテーブルIDを出力する場合。
$ aws ec2 describe-route-tables\
| jq '.RouteTables | sort_by('.RouteTableId')| .[].RouteTableId'
"rtb-0164253d4d1a43xxx"
"rtb-016e33abd3513fxxx"
"rtb-023975477d1adfxxx"
"rtb-02978f64b1ba54xxx"
"rtb-02e107895525a7xxx"
------以下略------
複数の値を出力する
ルートテーブルIDと VPC ID を並列で出力する場合。
$ aws ec2 describe-route-tables\
| jq -c '.RouteTables[] | [.RouteTableId,.VpcId]'
["rtb-xxxb19fd681afbc82","vpc-xxx645cc0765bfcb5"]
["rtb-xxxe65ace9b3d862e","vpc-xxxeae90"]
["rtb-xxx4253d4d1a43486","vpc-xxxd34c5"]
["rtb-xxxeed7382f10fa48","vpc-xxxee4adfe7047309"]
["rtb-xxxc22e8a1cf8d06b","vpc-xxxc4055"]
------以下略------
ルートテーブルとタグを並列で出力する場合。
$ aws ec2 describe-route-tables\
| jq -c '.RouteTables[] | [.RouteTableId,.Tags[]]'
["rtb-xxxb19fd681afbc82",{"Key":"Name","Value":"DEV-ABC-PUB"},{"Key":"Env","Value":"Dev"}]
["rtb-xxxe65ace9b3d862e",{"Key":"Name","Value":"PRO-MIRROR-PRV"},{"Key":"Env","Value":"Pro"}]
["rtb-xxx4253d4d1a43486",{"Key":"Env","Value":"Test"}]
["rtb-xxxeed7382f10fa48",{"Key":"Name","Value":"STG-EFG-PUB"},{"Key":"Env","Value":"Stg"}]
ルートテーブル ID と Name タグを並列で出力する場合。(Nameタグが無い場合はルートテーブルIDのみ。)
$ aws ec2 describe-route-tables\
| jq -c '.RouteTables[] | [.RouteTableId,(.Tags[] | select(.Key == "Name").Value)]'
["rtb-xxxb19fd681afbc82","DEV-ABC-PUB"]
["rtb-xxxe65ace9b3d862e","PRO-MIRROR-PRV"]
["rtb-xxx4253d4d1a43486"]
["rtb-xxxeed7382f10fa48","STG-EFG-PUB"]
------以下略------
いろいろ組み合わせて出力する
Nameタグの値でソートして Name タグとルートテーブル ID を表示する場合。
$ aws ec2 describe-route-tables\
| jq -c '.RouteTables | sort_by(.Tags[] | select(.Key == "Name").Value) | .[] | [(.Tags[] | select(.Key == "Name").Value),.RouteTableId]'
["rtb-xxx3388d"]
["rtb-xxx409ac"]
["rtb-xxx0484c"]
["rtb-xxx88aaa"]
["DEV-ABC-PRV","rtb-xxx3b90c62efdef3f"]
["DEV-ABC-PUB","rtb-xxxc22e8a1cf8d06b"]
["DEV-EFG-PRV","rtb-xxx87907a844afb48"]
------以下略------
Nameタグの値でソートして Name タグとルートテーブルIDと VPC ID と関連づけサブネットを表示する場合。
$ aws ec2 describe-route-tables\
| jq -c '.RouteTables | sort_by(.Tags[] | select(.Key == "Name").Value) | .[] | [(.Tags[] | select(.Key == "Name").Value),.RouteTableId,.VpcId,.Associations[].SubnetId]'
["rtb-xxx3388d","vpc-xxxeae90",null]
["rtb-xxx409ac","vpc-xxxfcde0",null,"subnet-xxx5c1d5","subnet-xxx16567"]
["rtb-xxx0484c","vpc-xxxd34c5",null]
["rtb-xxx88aaa","vpc-xxx456d7",null]
["DEV-ABC-PRV","rtb-xxx3b90c62efdef3f","vpc-xxxc4055","subnet-xxxcfbfccc9552f14"]
["DEV-ABC-PUB","rtb-xxxc22e8a1cf8d06b","vpc-xxxc4055","subnet-xxx4c5f1f492899d8","subnet-xxx8661855ac07146"]
["DEV-EFG-PRV","rtb-xxx87907a844afb48","vpc-xxx7871f72aa43134","subnet-xxxade7d3be17d66a"]
------以下略------
jq の使い方については以下もあわせてご参照ください。
--filters オプションを使って特定のリソースのみ出力対象にする
jq
を使用することで必要な値のみ出力させることができることがわかりました。しかし、あくまで個々のルートテーブルに関する情報を絞っているだけで、母数となるルートテーブルの数は変わっていません。
特定の条件に合致するルートテーブルをフィルタリングし、その上でjq
で特定の値を出力したいです。フィルタリングに使用できるものとして、 AWS CLI の--filters
オプションがあります。
--filters
オプションはすべての AWS CLI コマンドで使用できるわけではなく、describe
系オペレーションの一部で使用可能となっています。実行したいコマンドで対応しているか、都度リファレンスで確認すると良いでしょう。
--filters オプションの書式
以下のような書式で指定することになります。
--filters "Name=xxx,Values=yyy"
Values には複数の値を指定できます。
--filters "Name=xxx,Values=yyy,zzz"
Name と Values の組み合わせを複数指定することもできます。
--filters "Name=xxx,Values=yyy,zzz" "Name=aaa,Values=bbb"
Values を複数指定した場合は OR 条件、--filters
を複数指定した場合には AND 条件となります。
--filters オプションの Name と Value(s)
どういった条件でフィルタリングするかはName
とValues
に指定した値によって決まります。そしてどのような値を指定できるかはコマンドごとに異なります。これも都度リファレンスを参照するとよいでしょう。
今回は aws ec2 describe-route-tables
で指定できるものを取り上げます。
Name | 説明 | Value例 |
---|---|---|
association.route-table-association-id | ルートテーブルのアソシエーション ID | rtbassoc--xxx |
association.route-table-id | アソシエーション内のルートテーブル ID | rtb-xxx |
association.subnet-id | アソシエーション内のサブネット ID | subnet-xxx |
association.main | メインルートテーブルであるか否か | true | false |
owner-id | ルートテーブルの owner のアカウント番号 | 111111111111 |
route-table-id | ルートテーブル ID | rtb-xxx |
route.destination-cidr-block | ルートで宛先として指定された IPv4 CIDR 範囲 | 0.0.0.0/0 |
route.destination-ipv6-cidr-block | ルートで宛先として指定された IPv6 CIDR 範囲 | 2001:db8:1234:1a00::/56 |
route.destination-prefix-list-id | ルートで宛先として指定されたプレフィックスリストID | pl-xxx |
route.egress-only-internet-gateway-id | ルートでターゲットとして指定された EIGW ID | eigw-xxx |
route.gateway-id | ルートでターゲットとして指定された IGW ID | igw-xxx |
route.instance-id | ルートでターゲットとして指定されたインスタンス ID(NATインスタンスなど) | i-xxx |
route.nat-gateway-id | ルートでターゲットとして指定された NAT Gateway ID | nat-xxx |
route.transit-gateway-id | ルートでターゲットとして指定された Transit Gateway ID | tgw-xxx |
route.origin | ルートがどのように作成されたか | ※後述 |
route.state | ルートのステータス | active | blackhole |
route.vpc-peering-connection-id | ルートでターゲットとして指定されたピアリング ID | pcx-xxx |
tag :<key> | タグのキーと値の組み合わせでフィルタリングする場合に使用 | <key>タグの値 |
tag-key | タグのキーだけでフィルタリングする場合に使用 | Name |
vpc-id | ルートテーブルが所属する VPC ID | vpc-xxx |
「※後述」としたroute.origin
の Value は以下のいずれかをとります。
CreateRouteTable
: ルートテーブルの作成時に作成されたルート(local
)CreateRoute
: 手動で作成されたルートEnableVgwRoutePropagation
: ルート伝播により作成されたルート
冒頭の Output 例を鑑みると、概ねすべての情報を使用してフィルタリングが可能であることがわかります。
これを踏まえて幾つかのシナリオでフィルタリングしていきましょう。
特定の NAT Gateway をターゲットに持つルートテーブルをフィルタリング
特定の NAT Gateway でデータ転送料金が大きく掛かっており、そこを経由するインスタンスがどのサブネットに属しているか?というのを確認したいケースがありました。
NAT Gatewayとコストのお話については以下をどうぞ。
NAT Gateway は他の多くの VPC のゲートウェイと異なり、AZ を跨ぐ構成は取れません。マルチ AZ 構成をとる環境では、大抵の場合、それぞれの AZ に NAT Gatway を作成することになります。そして、各 AZ のワークロードが同じであれば、各 NAT Gateway で似たようなデータ転送量をとることになるでしょう。
今回のケースでも、「特定の NAT Gateway」は2つありました。
- nat-xxx
- nat-yyy
上記のいずれかの NAT Gateway をターゲットに持つルートを含むルートテーブルをフィルタリングしたいです。--filters
オプションでは Name にroute.nat-gateway-id
を、Values には nat-xxx
とnat-yyy
を指定することで実現できます。
aws ec2 describe-route-tables \
--filters "Name=route.nat-gateway-id,Values=nat-xxx,nat-yyy" \
| jq '.RouteTables[].RouteTableId'
"rtb-xxxc22e8a1cf8d06b"
"rtb-xxx338e9d9d5280c5"
"rtb-xxxe33abd3513ff20"
------以下略------
jq
は先述の通り様々な出力の仕方ができますので、必要に応じて変更してください。
S3 向けのゲートウェイ VPC エンドポイント宛のルートを持つルートテーブルをフィルタリング
NAT Gateway を経由する通信が意図通りのものであればよいですが、内訳に S3 宛の通信が含まれていると困ります。 S3 向けの VPC エンドポイントを経由した場合は追加の通信料はかからないため、 NAT Gateway を経由している場合には余計なコストを払っているということになるからです。
S3 向けの VPC エンドポイント(ゲートウェイ型)をターゲットに持つルートをフィルタリングしたくなります。
--filters
の Name には VPC エンドポイントを指定できるものが用意されていません。代わりに、route.destination-prefix-list-id
を使用します。
ゲートウェイエンドポイントをターゲットに指定する際、宛先にはプレフィックスリスト ID を指定することになります。東京リージョンの S3 であればpl-61a54008
が該当します。--filters
の Value としてそちらを指定すれば問題なくフィルタリングできます。
プレフィックスリスト ID はマネジメントコンソールで確認することもできますし、以下のコマンドで確認することもできます。
% aws ec2 describe-prefix-lists --region ap-northeast-1
{
"PrefixLists": [
{
"Cidrs": [
"52.94.8.0/24"
],
"PrefixListId": "pl-78a54011",
"PrefixListName": "com.amazonaws.ap-northeast-1.dynamodb"
},
{
"Cidrs": [
"3.5.152.0/21",
"52.219.0.0/20",
"52.219.136.0/22",
"52.219.16.0/22",
"52.219.68.0/22"
],
"PrefixListId": "pl-61a54008",
"PrefixListName": "com.amazonaws.ap-northeast-1.s3"
}
]
}
以下のようなコマンドで S3 VPC エンドポイント向けルートを持つルートテーブルをフィルタリングできます。
$ aws ec2 describe-route-tables\
--filters "Name=route.destination-prefix-list-id,Values=pl-61a54008"
先ほどとの結果とあわせて、特定の NAT Gateway 向けのルートを持つルートテーブルが VPC エンドポイント向けルートも持っているか、を AND 条件でフィルタリングすることもできます。
$ aws ec2 describe-route-tables\
--filters "Name=route.destination-prefix-list-id,Values=pl-61a54008"\
"Name=route.nat-gateway-id,Values=nat-xxx,nat-yyy"
パブリックサブネットをフィルタリング
--filters
オプションではワイルドカードを使用することもできます。
フィルタの値には、ワイルドカードを使用することもできます。アスタリスク (*) は 0 個以上の文字、クエスチョンマーク (?) は 0 文字または 1 文字にマッチングします。
以下のように指定すれば、(特定のリソースに限定せず)いずれかのインターネットゲートウェイをターゲットとするルートを持つルートテーブル、というフィルタリングの仕方ができます。
aws ec2 describe-route-tables \
--filters "Name=route.gateway-id,Values=igw-*"
Values はigw-*
でなく*
で問題ないのでは、と考えていたのですが、*
を指定すると IGW をターゲットに持たないものも含めてすべてが該当してしまい、フィルタリングを行う前と同様の結果となりました。(出力結果の個数をwc -l
で確認)
Values で * を指定した場合
$ aws ec2 describe-route-tables\
--filters "Name=route.gateway-id,Values=*"\
| jq '.RouteTables[].RouteTableId' | wc -l
82
フィルタリングをしない場合
aws ec2 describe-route-tables\
| jq '.RouteTables[].RouteTableId' | wc -l
82
もちろんi*
でもig*
でもいいのですが、何かしら指定する必要があります。
--filters
オプションについては以下もあわせてご参照ください。
終わりに
AWS CLI の--fliters
オプションとjq
を使用してお目当てのルートテーブルを探し当てる、というエントリでした。ルートテーブルが適用されているサブネットを引っ張ってくることもできるのは便利ですね。
jq
でなく--query
オプションを使用しても同じようなことはできるはずなのですが、なんとなくjq
の書き方の方が好みなのでついつい使ってしまいます。
ささっと作業するにはマネジメントコンソールが便利ですが、ある程度の規模になったら AWS CLI でいい感じにこねくり回していきたいですね。
以上、千葉(幸)がお送りしました。