インフラコストの最適化
Adventarを支える技術 Advent Calendar 2019 の18日目です。
昨日の記事で実際にかかっているコストを紹介しましたが、今日はコストを最適化するためにやったことを書きます。
Lambda を活用とスペックの調整
昨日の記事を見ても分かる通り、ECS などと比べると Lambda のコストは爆安です。今回、Nuxt.js のサーバーサイドレンダリングを Lambda でやっていて、キャッシュはしていないので、カレンダーページへのリクエストごとに Lambda が実行されます。実行回数はこの function が支配的ですが、その他にも画像サーバーやバッチジョブなどにも Lambda を利用しています。
現時点で実行回数は15万回ぐらいで、おそらく30万回ぐらいで着地すると思います。無料利用枠が10万回分、その後は10万回ごとに $0.2 なので、$0.4 ぐらいで収まる計算です爆安ですね。
実行回数だけでなく、実行時間でも課金されますが、こちらは現在無料枠の半分以下なので無料枠で収まりそうです。なお、実行時間の課金は利用しているメモリの容量が関係します。
https://aws.amazon.com/lambda/pricing/
Serverless Framework ではデフォルトのメモリが 1024 MB になるので、大量に呼ばれる関数には適切に設定してあげないと、思いがけずコストがかかってしまうことがありそうです。今回は大量に Nuxt.js の SSR は 512 MB に設定しました。
これがデフォルトの 1024 MB だと実行時間の計算は倍になり、無料枠の倍ぐらいの実行時間になりそうなので、400,000 * 0.0000166667
で $6.6 ぐらいになりそうです。大した額ではないですが。
メモリの使用量は、Lambda のログに出力されます。
REPORT RequestId: ee702bc9-c647-4d45-95c9-db8a2e6ba8bc Duration: 55.51 ms Billed Duration: 100 ms Memory Size: 512 MB Max Memory Used: 224 MB
現状 200 MB 〜250MB くらいなのでもう少し下げてもメモリは足りそうですが、メモリ使用量に比例して CPU の性能も決まるので、あまり下げすぎると性能が劣化します。実際、以下のように影響が出ています。
カレンダーページのパフォーマンスに直結するので $6 ぐらいであれば 1024 MB に上げてもよさそうだなとこれを書きながら思いました。下げたときはどのぐらい呼び出しがあるか読めなかったのでビビってたのです。
Fargate か EC2 か
これはコストを削減したというよりは、コスト削減と天秤にかけてメンテナンスコストが安いほうを選んだという話です。
ECS はタスクを実行するのに EC2 インスタンスか Fargate を選べます。Fargate だと EC2 のインスタンスを管理しなくてよくなるし、スケールなども楽にできますが、EC2 より少し高くなります。また、EC2 の場合はデプロイ時に複数タスクを配置する必要があるので、余分にリソースを空けておきたいところです。
Fargate の一番小さいサイズでもさばけそうだったので、Fargate は 0.25 CPU, 0.5 GB Memory、EC2 の場合は t3.micro(2vCPU, 1GB Memory)で比較しました。
- Fargate 約 $11/month
- EC2(t3.micro) 約 $10/month
これだとほぼ変わらないので、管理の手間などを考えて Fargate を選ぶことにしました。EC2 はもう一つ下の t3.nano でも足りるかも知れませんが、リソースが足りなくなってスケールアップ/アウトが必要になったときの手間を考えると選びづらい選択でした。
また、スポットインスタンスなどを使えばもっと安く済みますが、さらに管理コストが増えるので採用は見送りました。ちなみにこのときはまだ Fargate Spot が発表されてなかったので試していませんが、時間があったら試してみたいと思っています。
ちなみに以下は一番小さいサイズで余裕で捌けている図です。
RDS のストレージサイズを小さくする
これはコスト最適化というよりは単純にミスったのですが、ストレージサイズを無駄に100GB確保しているのにリリースしたあとに気づいてしまって、途中で最小の20GBに落としました。
https://github.com/adventar/adventar/commit/47d336c02a1619591ac33062e34022ab1a6ec356
ストレージサイズを小さくするのは動かしながらはできないので、2台立ててアクセスが少ない時間帯に手動でデータをマイグレーションして切り替えました 😅
以下が切り替えたときのコストの変動です。
これで $11/month ぐらい節約?できました。
private subnet をおかない
普通、VPC を設計する際、public subnet と private subnet を作り、インターネットから見える必要があるのもは public へ、そうでないものは private に置くことでセキュリティを強固にします。
しかし、private subnet に置かれたサーバーからインターネットにアクセスしようとすると、Nat Gateway が必要になります。
https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html
この Nat Gateway が地味に高くて、起動しっぱなしだと、$40/month ぐらいかかります。必要なときだけ起動するみたいな方法もあるかもしれませんが、手間を考えて public subnet だけの運用にして Security Group でがんばる方針にしました。
基本的な方針として、インターネットからのアクセスは ALB だけに許可して、その他のリソースはすべて VPC 内部からしかアクセスできないようにます。
しかし、DB はデータや設定を見たりする用途で、手元からつなぎたいケースがけっこうあります。ただ、DB はインターネットからのアクセスを許可したくないリソース一番手でもあります。
つなげるときだけ必要なものを詰め込んだ Fargate のタスクを起動して、一時的な踏み台として使う、という手も考えましたが、そこまでしなくてもいいかと思い止まり、手元の IP を許可した Security Group を作って、必要なときだけ RDS に刺すようにしました。
この Security Group は terraform で管理せずに手積みで管理しています。