hero_picture

Amazon Elastic File System (Amazon EFS) におけるバーストとプロビジョニングの変更を自動化する

2019/08/26

クラウド事業部の原口です。

AWSのEFSが発表されてから「これを待っていた!!」というエンジニアの方も多かったと思いますが、割と落とし穴的な制限が多く、

その中の一つとしてEFSの「バースト」と「プロビジョニング」の2つのスループットモードがあります。

バーストスループットモードとは何か。

誤解を恐れず簡単に説明しますと、スマフォの帯域制限みたいな機能となります。

EFSではバーストクレジットと呼ばれる値があり、このクレジットがある間は非常に高速なスループットでデータのやり取りが可能なのですが

クレジットがなくなると制限がかかりめちゃくちゃ低速なファイルストレージになってしまいます。

これはプロダクション環境ではサーバ障害とも言えるレベルでの低速さになります。

バーストクレジットを回復するには?

基本的にファイルストレージを使っていなければ回復していきます。

そのため、EFSの利用頻度がクレジットの回復力を下回る場合はクレジットは減る事がありません。

また、使っているEFS内のサイズによってもクレジットのそもそもの量が異なります。

大きいファイルが多数あるなど、EFS内のデータ量が多ければクレジットのそのものの絶対量が多く付与されます。

これを理解してない場合に起こる問題

基本的には運用時、EFSはバーストスループットモードとして動作する事がほとんどですが、

よくある事例としては「テスト段階では高速で快適に動いていたのに、本番運用していたら急激に低速になり障害があった」という事例です。

CloudWatchのメトリクスに「BurstCreditBalance」というメトリクスがありますが、こちらがバーストクレジットで、

ゼロになると低速なストレージになってしまいます。

上記のようなグラフの場合は運用で徐々にクレジットが減っていってるのでずっと運用しているといつかは障害となりそうです。

プロビジョニングスループット

さて、バーストクレジットを使い果たして低速となってしまった場合はどうすればいいのでしょうか?

もちろん「大きなファイルを作ってバーストクレジットを増やす」というのも一つの解決策ですが、

一般的にはバーストモードからプロビジョニングスループットというモードへ変更する事で対策できます。

プロビジョニングスループットは帯域保証のような機能で50(MB/秒)といった値を設定する形でスループット値を保証させるモードです。

このモードではクレジットなどに関係なく設定した速度は必ず保証されます。

しかし・・・このプロビジョニングスループットは非常に高額です。

1MB/秒 の保証にあたり月額7.2USDもかかります。

件のように50MB/秒を保証させると月額360USD ≒ 38,000円。

ただでさえEFSは高額なのにこの価格は無視できる額ではありませんよね。

プロビジョニングとバーストクレジットを交互に設定をする事でコスト削減を図る

実はバーストクレジットからプロビジョニングスループットに変更すると

プロビジョニングスループット中はバーストクレジットが回復していきます。

8/23の途中からプロビジョニングスループットとしていますが、3日ほどでクレジット最大値まで回復していますね。

つまり、最もコスト効率がいいのは

クレジットを限界まで使い直前でプロビジョニングスループットに変更 。クレジットが回復したらバーストスループットに変更。

という形となります。

CloudWatch + SNS + lambda を使ったバーストとプロビジョニングの変更を自動化

ここからが本題。

上記の形がコスト効率が最もよいのですがこんなの人の手でぽちぽちやってれないので自動化します。

まずは、バーストとプロビジョニングの変更を行うlambda関数を作成します

バーストモードにするものとプロビジョニングにするものの2つ

EFS-to-bursting

1console.log('Loading function');
2var https = require('https');
3var http = require('http');
4const aws = require('aws-sdk');
5var efs = new aws.EFS();
6exports.handler = (event, context) => {
7(event.Records || []).forEach(function (rec) {
8if (rec.Sns) {
9var message = JSON.parse(rec.Sns.Message);
10var params = {
11FileSystemId: message.AlarmDescription, /* required */
12ThroughputMode: 'bursting'
13};
14efs.updateFileSystem(params, function(err, data) {
15if (err) console.log(err, err.stack); // an error occurred
16else     console.log(data);           // successful response
17});
18// ここでslack等に通知するといいと思う
19}
20});
21};

EFS-to-provisioned-iops

1console.log('Loading function');
2var https = require('https');
3var http = require('http');
4const aws = require('aws-sdk');
5var efs = new aws.EFS();
6exports.handler = (event, context) => {
7(event.Records || []).forEach(function (rec) {
8if (rec.Sns) {
9var message = JSON.parse(rec.Sns.Message);
10var params = {
11FileSystemId: message.AlarmDescription, /* required */
12ProvisionedThroughputInMibps: '10', // プロビジョニング10MB
13ThroughputMode: 'provisioned'
14};
15efs.updateFileSystem(params, function(err, data) {
16if (err) console.log(err, err.stack); // an error occurred
17else     console.log(data);           // successful response
18});
19// ここでslack等に通知するといいと思う
20}
21});
22};

FileSystemId: message.AlarmDescriptionの部分ですが

受け取ったCloudWatchアラームにはどのEFSがアラームを出したか、というものが取得しずらかったので

CloudWatchアラームのDescriptionにEFSのIDを載せる形にしました

SNS(Simple Notification Service)のトピックを作成してlambdaに連携します

SNSは通知先にメールやhttpなどだけでなくlambdaに通知できます。

ですので、バーストモードにするlambdaに連携するトピックと、

プロビジョンドにするlambdaに連携するトピックの2つを作成します。

cloudwatchにてアラームを作成

BurstCreditBalanceを監視し、上記作成のSNSに通知するアラームを作成します。

こちらも、

・バーストクレジットが一定数以上の時 -> プロビジョンモードにするSNSに発火

・バーストクレジットが一定数以下の時 -> バーストモードにするSNSに発火

という形で2つ作成します。

lambdaの所でも書いた通り、DescriptionにはEFSのIDを入力します

バーストとプロビジョニングの変更を自動化できました

このように複数EFSあると便利さがわかりますね!

クレジットが減るとプロビジョニングになり、クレジットが充足するとバーストモードになる形です

注意点

EFSの制限で、スループットモードの変更は1日に1回までしかできません!

そのため、1日でバーストクレジットを使い果たしてしまうような環境ではずっとプロビジョニングモードで動かす必要があります。

本日は以上で!