AWS EFS利用時のウェブアプリケーションデプロイに関する考察

EC2 インスタンスを複数起動しロードバランシングによる負荷分散を行う際にデータの共有方法としてEFSを利用することは、データの同期に関する管理を必要としない点で非常に優れた方法だと思う。現に、単一のEC2 インスタンスへのデプロイですべてのインスタンスで同一のデータが参照できる点で非常に重宝している。

この方法を選択した場合にEFSへのアクセスが遅くウェブアプリケーションのデプロイに思いのほか時間がかかることが、別の問題を生じる場合がある。また、デプロイ以外にも、ウェブアプリケーションによって動的にファイルを生成する場合にも同様のアクセス速度の問題が生じている。


課題

デプロイ時のデータ転送に関して現状NFS(EFS)を利用しているが、NFSへの書き込み速度の関連でデプロイにかかる時間が長い。アプリケーションのレスポンスもおそらくこれによって遅延していることが考えられる。また、EFSの想定される利用方法の範囲内であるので杞憂かもしれないが、今後アクセスが増加した場合にWebサーバのオートスケーリングによるNFSへの負荷も高まることが想定される。

この点、データを複数インスタンスに分散して維持する管理では、インスタンス間でのデータ同期が必要となる。


NFSの負荷分散を解決 - NFSのオートスケーリング

EFSからEC2へのリプレイスを行い、オートスケーリングを実装する。EFSとEC2によるNFS構築の同期に関するドキュメントは発見できなかったが、大差ないか悪くなると想定される。


NFSを利用せず、サーバ内のデータを同期 - EC2 インスタンス間でデータを同期

直接デプロイするサーバから他のサーバにデータを同期する。データ自体はサーバのローカルに存在するのでアプリケーションのレスポンスは改善する。また、この場合に課題として持ち上がるデータの同期に関しては、以降の対策を考える。


データの同期

直接デプロイする想定のサーバ以外には数台サーバがあり、一部のサーバはオートスケーリングの機能を有している。プライベートなIPアドレスはオートスケーリングの前後で変動するため、デプロイが必要なインスタンスに対して固定的に同期を行うことはできず、リアルタイムでデプロイのターゲットを取得した上でデータの同期を行う必要がある。


対策 1 - Rambdaを利用して同期する

候補として利用できそうなAWSのサービスは「Rambda」。S3にデプロイを行い、データの更新をフックしてデプロイスクリプトを実行する。全インスタンスで同一のインスタンスを参照してデプロイを行う。


課題


  • Rambdaには300秒の実行時間制限があり、場合によってはタイムアウトしてしまう可能性がある

  • Rambdaには「aws cli」が無いため、構築が面倒


対策2 - 直接デプロイするサーバからデータを取得する

特定のサーバに直接デプロイ後、何らかのイベントフックで同期を行う。


直接デプロイするサーバのデータを更新した際に、そのサーバがデータの同期を行う

デプロイに関してはデータの同期がなされるため、問題がない。incronを利用して同期スクリプトが実行できる。

一方、一部サーバのインスタンスにはオートスケーリングの機能があるため、オートスケーリングの際にもデータは同期されていなければならない。


課題


  • デプロイ時に、最新の状態のWEBサーバのAMIを作成する必要がある

  • 起動テンプレートのAMIを変更する必要がある


  • 一方で、オートスケーリングによるインスタンス起動時にオートスケーリングしたサーバがデータを同期する方法も考えられる - 不採用



同期


各インスタンスのPrivateIPを取得

ターゲットとなるインスタンス数を取得。

aws ec2 describe-instances --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=[TAG NAME]" | jq '.Reservations | length'

タグ名で絞り込む例。

aws ec2 describe-instances --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=[TAG NAME]" | jq '.Reservations[].Instances[].PrivateIpAddress'


各インスタンスにデータを転送

lsync + rsyncd


AMI作成

※ AMI作成時にインスタンスは再起動されるので注意が必要

create-image

aws ec2 create-image --instance-id [BASE INSTANCE ID] --name "My server" --description "An AMI for my server"

{

"ImageId": "[AMI ID]"
}


起動テンプレートの更新

※ オートスケリンググループの起動テンプレートのバージョンは、「$Latest」にするほうが安全

create-launch-template-version

aws ec2 create-launch-template-version --launch-template-id [LT ID] --version-description Version --source-version [LatestVersionNumber] --launch-template-data '{"ImageId":"[AMI ID]"}'

{

"LaunchTemplateVersion": {
"VersionDescription": "Version",
"LaunchTemplateId": [LT ID],
"LaunchTemplateName": "WebServers",
"VersionNumber": [NewVersionNumber],
"CreatedBy": "[ARN]",
"LaunchTemplateData": {
"ImageId": "[AMI ID]",
"InstanceType": "[TYPE]",
"NetworkInterfaces": [
{
"Ipv6Addresses": [
{
"Ipv6Address": ":::::"
}
],
"DeviceIndex": 0,
"SubnetId": "[SN ID]",
"AssociatePublicIpAddress": true
}
]
},
"DefaultVersion": false,
"CreateTime": "2017-12-01T13:35:46.000Z"
}


起動テンプレートのデフォルトバージョンを変更

aws ec2 modify-launch-template --launch-template-id "[LT ID]" --default-version "[NewVersionNumber]" --region ap-southeast-2