【実例】これで解決!AWS CDKのバージョン違いで起こったデプロイ障害とその対策
【実例】これで解決!AWS CDKのバージョン違いで起こったデプロイ障害とその対策
Contents
概要
AWS CDKを使ったデプロイでEC2インスタンスに接続できなくなったり、プライベートサブネットからインターネットに出られなくなった経験はありませんか?今回はAWS CDKのバージョンが原因で発生した障害の事例を紹介し、その解決方法を解説します。
結論と教訓
単純に AWS CDK のバージョンが低い環境(v2.130.0)で cdk deploy
を実行したことによる先祖返り障害です。CDKのバージョンアップを行ってから再デプロイすることで解決できました。
今回のことで得られた教訓は次のとおりです。
-
どんな環境でも、差分の確認は必ず行うべき
-
いつもと違う作業環境でデプロイを実施するときは特に、CDKのバージョン確認は怠らないことが重要
トラブルの概要
- S3バケットを追加する必要があったのでバケット作成のコードを追加
- プライベートな検証環境で
cdk deploy
コマンドを使用して追加リソースをデプロイ - EC2インスタンスにセッションマネージャーで接続不可
- トラブル調査
- VPC Reachability Analyzerで接続状態を確認
- CloudFormationの変更セットで差分チェック
変更内容がS3バケットの追加のみだったため、詳細な差分を確認せずにデプロイを実行してしまいました。
※個人のプライベートAWSアカウントだからといって、差分チェックを怠ったことが適切ではありませんでした。
デプロイログには、NATインスタンスの入れ替えがありました。前回デプロイが4月なのでNATインスタンスに使用している Amazon Linux 2023 の新しいAMIがリリースされたためだろうと考えました。
しばらくして、追加したS3バケットの作成も終わり、デプロイが完了しました。
その後、EC2にセッションマネージャーでWindows Serverにリモートデスクトップ接続を行いましたが、繋がりません。
EC2コンソールから確認しても、セッションマネージャーと接続できなくなっていました。再起動しても接続できませんでした。
トラブル調査
VPC Reachability Analyzerで接続状態を確認
NATインスタンスが入れ替わったことが原因なのは予想できたので、VPC Reachability Analyzerを使って接続状態を確認してみました。このツールは非常に便利です。
プライベートサブネットにあるEC2からインターネットに出られるかを確認しましたが、結果は 到達不能
でした。
正しく接続できる場合は、EC2インスタンス→NATインスタンス→インターネットゲートウェイとなります。
CloudFormationの変更セットで差分チェック
次に、デプロイしたCloudFormationの変更セットを確認しました。
NATインスタンスのAMIのほかに、UserDataの文字列に差分がありました。CloudFormationテンプレート上ではBase64でエンコードされているのでデコードしました。
< 変更前 >
1 #!/bin/bash
2 yum install iptables-services -y
3 systemctl enable iptables
4 systemctl start iptables
5 echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf
6 sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
★7 sudo /sbin/iptables -t nat -A POSTROUTING -o $(route | awk '/^default/{print $NF}') -j MASQUERADE
8 sudo /sbin/iptables -F FORWARD
9 sudo service iptables save
< 変更後 >
1 #!/bin/bash
2 yum install iptables-services -y
3 systemctl enable iptables
4 systemctl start iptables
5 echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf
6 sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
★7 sudo /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
8 sudo /sbin/iptables -F FORWARD
9 sudo service iptables save
7行目(★)に注目してください。コマンドが違います。
この違いは、aws-ec2: NatProvider.instanceV2 primary network interface #29720の issue によるものです。
このUserDataのコマンドは、AWSドキュメントのNATインスタンスの部分に記載があります。そこには下記注意があります。
プライマリネットワークインターフェイスが eth0 でない場合は、eth0 を、
前のステップでメモしたプライマリネットワークインターフェイスに置き換えます。
NatProvider.instanceV2として、AL2023に対応されたときのコードは下記です。
const userData = UserData.forLinux();
userData.addCommands(
'yum install iptables-services -y',
'systemctl enable iptables',
'systemctl start iptables',
'echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf',
'sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf',
'sudo /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE',
'sudo /sbin/iptables -F FORWARD',
'sudo service iptables save',
);
これが、#29720
の後には次のように変更されていました。「プライマリネットワークインターフェイスが eth0 でない場合」が考慮されています。
public static readonly DEFAULT_USER_DATA_COMMANDS = [
'yum install iptables-services -y',
'systemctl enable iptables',
'systemctl start iptables',
'echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf',
'sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf',
"sudo /sbin/iptables -t nat -A POSTROUTING -o $(route | awk '/^default/{print $NF}') -j MASQUERADE",
'sudo /sbin/iptables -F FORWARD',
'sudo service iptables save',
];
:
:
let userData = this.props.userData;
if (!userData) {
userData = UserData.forLinux();
userData.addCommands(...NatInstanceProviderV2.DEFAULT_USER_DATA_COMMANDS);
}
つまり、NATインスタンスを作成したときの環境では#29720
が対応済みバージョンのCDKでしたが、今回使用した環境では#29720
が対応されていないバージョンのCDKだったということで先祖返りしてしまいました。
まとめ
NatProvider.instanceは廃止されていますが、開発環境や個人環境などでコスト削減目的でNATインスタンスを利用したい場合は、NatProvider.instanceV2を利用することで簡単に作成できます。
ただし、CDKのバージョンに注意しないと、思わぬところでハマってしまいます。
// NATインスタンスを使うか、NATインスタンスを使わない(=NATゲートウェイを使うか)をパラメータで制御できます。
// NATインスタンスに使用するインスタンスタイプがパラメータで指定できます。
const natInstance = ec2.NatProvider.instanceV2({
instanceType: props.natInstanceType
? new ec2.InstanceType(props.natInstanceType)
: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.NANO),
machineImage: ec2.MachineImage.latestAmazonLinux2023({
edition: ec2.AmazonLinuxEdition.STANDARD,
cpuType: ec2.AmazonLinuxCpuType.ARM_64, //X86_64
}),
defaultAllowedTraffic: ec2.NatTrafficDirection.OUTBOUND_ONLY,
});
// VPC
this.vpc = new ec2.Vpc(this, 'MyVpc', {
vpcName: [id, 'VPC', accountId].join('/') ,
ipAddresses: ec2.IpAddresses.cidr(props.vpcCIDR),
maxAzs: props.maxAzs ?? 2, // 2 Availability Zones
subnetConfiguration: [
{
cidrMask: 24,
name: 'PublicSubnet',
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: 'PrivateSubnet',
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
},
],
natGateways: props.natgateways ?? 0;,
natGatewaySubnets: {subnetType: ec2.SubnetType.PUBLIC},
natGatewayProvider: props.isNatInstance ? natInstance : ec2.NatProvider.gateway(),
// Security Hub EC2.2
// https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/ec2-controls.html#ec2-2
restrictDefaultSecurityGroup: true,
});
補足・参考文献
aws-ec2: NatProvider.instanceV2 primary network interface #29720
上記が反映されたバージョンはこちら→v2.137.0
AWSドキュメント: DEFAULT_USER_DATA_COMMANDS
GitHub: aws-cdk/packages/aws-cdk-lib/aws-ec2/lib/nat.ts > DEFAULT_USER_DATA_COMMANDS
Discussion