CDKでEC2を作りました。
作った後にしばらくして、EC2の設定を変えたいことがありCDKを変更しました。
その際には、設定変更したところだけcdk diff
で差分が出ていました。
その際は、リプレースの表示はありませんでした。
しかし、cdk deploy
してみるとEC2がリプレースされていました。
こちらの事象の原因と対策についてです。
結論から書きます。
- 原因: CFnでEC2インスタンスのイメージIDの指定方法がSSMパラメータストアになっていた。パラメータストアの中身の変更は「cdk diff」で検知できなかった。
- 対策: AMI IDを直接指定する、
cachedInContext
を使う
事象: CDKで作ったEC2インスタンスが「cdk diff」でリプレース無かったのに置き換えれれた
サンプルコードで事象を説明します。
以下のようなコードでEC2インスタンスを作成しました。
new ec2.Instance(this, "Instance", {
vpc,
machineImage: ec2.MachineImage.latestAmazonLinux({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
}),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
})
何日か経って、セキュリティグループを追加しました。
new ec2.Instance(this, "Instance", {
vpc,
machineImage: ec2.MachineImage.latestAmazonLinux({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
}),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
})
// 追加
server.connections.allowFromAnyIpv4(ec2.Port.tcp(80))
cdk diff
を確認します。
% npx cdk diff (git)-[master]
Stack CdkEc2ReplaceStack
Security Group Changes┌───┬───────────────────────────────────────────┬─────┬──────────┬─────────────────┐│ │ Group │ Dir │ Protocol │ Peer │├───┼───────────────────────────────────────────┼─────┼──────────┼─────────────────┤
│ + │ ${Instance/InstanceSecurityGroup.GroupId} │ In │ TCP 80 │ Everyone (IPv4) │
└───┴───────────────────────────────────────────┴─────┴──────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[~] AWS::EC2::SecurityGroup Instance/InstanceSecurityGroup InstanceInstanceSecurityGroupF0E2D5BE
└─ [+] SecurityGroupIngress
└─ [{"CidrIp":"0.0.0.0/0","Description":"from 0.0.0.0/0:80","FromPort":80,"IpProtocol":"tcp","ToPort":80}]
上記の結果からは、EC2インスタンスのリプレースは発生しなさそうです。
しかし、cdk deploy
してみるとEC2インスタンスが置き換えられてしまいました。(以前作成したEC2インスタンスが削除され、新しいEC2インスタンスが作成された)
原因: CFnでEC2インスタンスのイメージIDの指定方法がSSMパラメータストアになっていた
イメージIDが変わってしまって、リプレースされた様子でした。
(CFnのドキュメントにあるようにイメージIDの更新にはリプレースが必要です)
CloudFormation UserGuide AWS::EC2::Instance
上記のコードで生成されたCFnを見てみます。イメージIDの部分はSSM パラメータストアを指している様子です。
"ImageId": {
"Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter"
},
SSMパラメータストアの部分を見てみると、AWSが提供しているパラメータストアを参照している様子です。
"SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value",
"Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
},
パラメータストアの値を見てみると、イメージIDが保存されていることや最終更新日時がわかります。
$ aws ssm get-parameters --name /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
{
"Parameters": [
{
"Name": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2",
"Type": "String",
"Value": "ami-0923d9a4d39b22a91",
"Version": 56,
"LastModifiedDate": "2022-01-11T06:19:32.876000+09:00",
"ARN": "arn:aws:ssm:ap-northeast-1::parameter/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2",
"DataType": "text"
}
],
"InvalidParameters": []
}
SSMパラメータストアの中身の値は、実行しているCDKでは管理していません。
そのため、cdk diff
で差異を検出できませんでした。
対策: AMI IDの指定方法を変える
AWS管理のSSMパラメータストアから毎回取得しないようにする(キャッシュ)、またはイメージIDにSSMパラメータストアを使わないようにする必要があります。
CDKでEC2にオプションcachedInContext
を使う
まずは、cdk.context.jsonに保存してその値を使う方法です。
interface AmazonLinuxImageProps · AWS CDK
machineImage: ec2.MachineImage.latestAmazonLinux({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
cachedInContext: true // 追加
}),
cachedInContext
を追加することで、以下のようにイメージIDがキャッシュされます。
"ssm:account=XXXXXXXXXX:parameterName=/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2:region=ap-northeast-1": "ami-0923d9a4d39b22a91"
生成されるCFnでは、イメージIDが直接指定されます。
"ImageId": "ami-0923d9a4d39b22a91",
lookupなどイメージIDが固定される方法でmachineImageを指定する
イメージの指定方法は色々あると思いますが、例えば以下のように、lookup
などでイメージ名を指定するとイメージIDが固定化されます。
machineImage:ec2.MachineImage.lookup({
name: "amzn2-ami-hvm-2.0.20211223.0-x86_64-gp2",
owners: ['amazon'],
}),
まとめ
EC2が「cdk diff」無しで置き換わる事象でした。
ゴールデンAMI作ってそれを指定することが多いと思うので、あまりec2.MachineImage.latestAmazonLinux
などを使ってイメージを指定することは少ないかもしれません。
AWS管理のSSMパラメータストアからイメージIDを取るようなケースがあったら、cachedInContext
などを使って回避するのが良いと思います。