自分への備忘録を兼ねて。
はじめに
AWSでEC2+RDS(MySQL)なインフラを構築する際に、可用性を考慮してRDSをMulti-AZ配置にすることはよくあると思います。
この際に注意したいのがAZをまたぐことによるレイテンシです。
Multi-AZ配置にした場合、EC2とRDSのAZが異なる場合にはレイテンシが大きくなるため、なるべくならEC2とRDSのプライマリは同じAZにしたいものです。
しかし、CloudFormationでRDSの環境を構築する際にMulti-AZを有効にしようとすると、AZを指定することができません。
また、RDSインスタンスを複数利用しようとすると、RDSインスタンスのプライマリAZがばらけることもあるため、こちらにあるようにEC2のAZをRDSに合わせるということも難しくなります。
そこで、ある工夫をすることでMulti-AZなRDSのプライマリAZを指定する方法を試してみました。
方法
簡単に言うと、CloudFormationのRDS設定ではMulti-AZをfalseにした上で、EC2のUserData側でRDSのMulti-AZを有効にします。
UserDataの中ではAWS CLIを利用してRDSの設定を変更するため、EC2インスタンスでjqを利用できる必要があります。
(jq以外でも条件指定で要素の抽出ができるようなJSONパーサで代用ができるかも知れません)
RDSの記述
RDSに関する設定では、下記のようにプライマリにしたいAZを指定した上でMulti-AZはfalseに設定します。
"MyDB" : {
"Type" : "AWS::RDS::DBInstance",
"Properties" : {
(中略)
"AvailabilityZone" : "指定したいAZ",
(中略)
"MultiAZ" : "false",
(中略)
}
}
EC2の記述
EC2では、EC2インスタンスを配置するサブネットの設定で、RDSインスタンスに指定したAZに属するサブネットを指定します。
その上で、DependsOnにサブネットとRDSを指定した上で、UserDataの中でRDSのEndpoint.AddressからDBInstanceIdentifierを取得し、RDSのMulti-AZを有効にします。
"EC2Instance" : {
"Type" : "AWS::EC2::Instance",
"DependsOn" : "MyDB",
"Properties" : {
(中略)
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
(中略)
"aws rds modify-db-instance --db-instance-identifier `aws rds describe-db-instances | /path/to/jq '.DBInstances[]' | /path/to/jq -r 'select(.Endpoint[\"Address\"]==\"", { "Fn::GetAtt": [ "MyDB", "Endpoint.Address" ] }, "\") | .DBInstanceIdentifier'` --multi-az --apply-immediately\n"
] ] } }
},
(中略)
}
補足
- DBInstanceIdentifierを明示的に指定している場合はEndpoint.AddressからDBInstanceIdentifierを取得などせず、modify-db-instanceのパラメタに直接指定するだけなのでJSONのパースなども不要です
- CloudFormationのイベントとしては、RDSインスタンス構築後、EC2インスタンスの構築時にRDSに対するmodifyと再起動が発生することとなります
まとめ
CloudFormationのタイプごとの記述だけで全てを管理しなくても良い場合は、こういう合わせ技を用いることでやりたいことを満たせる可能性があります。
AWS CLIでできることを把握しておくと、CloudFormationだけでは難しいことも実現できるかも知れません。