1日50円で使えるマイクロサービスなアプリを動かすAWS環境を作ってみた

| 9 min read
Author: toshio-ogiwara toshio-ogiwaraの画像

今回は「MicroProfileのサンプルリニューアル – 今度はほんとにMSA」の番外編として記事で紹介したMSAなサンプルアプリを動作させるために構築した1日50円で運用可能なケチケチAWS環境を紹介したいと思います。実業務の本番環境としてはハッキリ言って使い物にならない部分も多いですが、ケチってもこのくらい出来るのネ程度に生暖かく読んでもらえればと嬉しいです。なお、今回はわけあってAWSの個人アカウントを使って環境を構築しています(理由は記事の最後に)。

利用料金は自分でも確認しましょう!

記事は執筆時点(2023/5/28)の情報をもとにしています。クラウドサービスの料金体系は頻繁に改訂されます。また取り上げる情報の正確性は十分確認を行っていますが、筆者の理解が間違っている可能性もあります。お金が絡むことなので料金体系はご自身で確認されることをお勧めいたします。

動作させるMSAなアプリ

#

ケチケチ環境で動かすMSAなアプリは「MicroProfileのサンプルリニューアル – 今度はほんとにMSA」でも紹介した次のアプリになります。

overview

このアプリを動かすために必要な環境は次のとおりです。

要素 必要な環境
SPAクライアント 静的webページのホスティング環境
CLIクライアント (ローカル環境のみの動作のため対象外)
ApiGateway コンテナ実行環境
RentalItemService コンテナ実行環境
ReservationService コンテナ実行環境
UserService コンテナ実行環境

DBはh2のインメモリデータベースを使ったDatabase per Service構成のため、それ用の実行環境は不要です。また、その他サンプルアプリに関する詳細はこちらにまとめてあります。

構築環境のポリシー

#

記事のタイトルにも関係しますが、次のことをポリシーとしてMSAなアプリを動作させる環境を構築しました。

  • データを管理するサービスはプライベートサブネットに配置する
  • その他の可用性やセキュリティなどの要素はコストを最優先(安さが一番!)でよい

データを管理するサービスやDBをパブリックサブネットに曝すのはさすがに抵抗があるのと、パブリックサブネットOnlyにした場合、環境構築の難易度が低く芸がないため、データを管理するサービスはプライベートサブネットに配置することを必須としました。それ以外は自腹の個人アカウントを使うためコスト優先に考えることにしています。

結論、考えたらこうなった

#

結論から先にいうと次に示す構成にすることでMSAなアプリを1日50円で運用できるようになりました。

aws-arch

なお、構成はコスト最優先のためシングルAZ構成で、また詳細は後述しますが料金が高額になるALB(Application Load Balancer)も利用していません。

安く上げるための考慮ポイント

#

と、いきなりAWSの全体構成をだされても、なんでそうしたの?がわからないと思いうため、ここからは安く上げるために、あえて普通じゃない、もしくは工夫したポイントを個別に説明してきます。

前段のサービスだけはFargateを使わずにEC2に

#

自分で何個もEC2インスタンスを管理するのは面倒でイヤなのでコンテナ実行環境はECS Fargateにすることを考えていました。が、、それには1つ悩ましい問題がありました。それはVPCに入ってくるリクエストをどのようにECSタスクにルーティングするかです。

今回の例でいえば、外部からのリクエストは各サービスの前段にいるApiGatewayサービスが受け取る必要がありますが、FargateのECSタスクにElastic IP(固定パブリックIP)を割り当てることはできません。

このため、外部からのリクエストを受け付けるECSタスクの前段にはALB(Application Load Balancer)を置き、ALB経由でECSタスクにリクエストをルーティングさせるのが定石です。また、これにはもう一つ、ACM(AWS Certificate Manager)で発行したSSL/TSL証明書をALBに紐づけて使うことができる利点があります。しかも証明書の発行は無料です。

alb

そんな素敵なALBですが、最大のネックは料金です。ALBは利用時間と利用料に応じて料金[1]が発生します。また、EC2やECSタスクであれば、利用していないときは停止することで料金の発生を抑えることができますが、ALBは削除しない限り料金が発生し続けます。このため、1か月間単に稼働させただけでも$16.2(=0.0225[時間単価]x 30[日]x24[時間])となり130円換算で最低2100円の料金が発生します。さらに、これに使った分だけの料金が加算されるため、個人アカウントでは心配でオチオチ使っていられません。

このため、前段に配置するApiGatewayサービスだけはFargateにすることは諦め、Elastic IP[2]で固定IPを割り当てたEC2上にDockerをインストールし、そのDocker上でApiGatewayサービスを稼働させることにしました。この場合、ACM によるSSL/TSL証明書の発行は行えないため、無料で使えるLet's Encryptで証明書を発行するようにしています。(CloudFrontのエンドポイントの証明書はACMで発行しています)

ec2

NATインスタンス経由でコンテナイメージをpullする

#

外部からVPC内へのルーティングはEC2にすることで一段落しましたが、次に問題となるのがVPCから外部への通信です。

冒頭で触れたように、コストを最優先にするといってもデータを管理するサービスだけはプライベートサブネットに配置することにしました。このため、プライベートサブネット内でコンテナサービスを起動させるのですが、これにはコンテナイメージを取得するためにコンテナレジストリへのアクセスが必要となります。

が、しかし、コンテナレジストリの候補となり得るECR、GitHub PackagesともVPC内にはありません。このため、プライベートサブネットに配置されたコンテナサービスからVPCの外部にあるコンテナレジストリになんらかの方法でアクセス可能にする必要があります。これに対して一般的に用いられる手段としては2つあります。

1つが次のVPCエンドポイント(PrivateLink)を使ってECRにアクセス可能にする方式。

vpcendpoint

もう1つがNATゲートウェイを使ってインターネット経由でECRもしくはGitHub Packagesにアクセス可能にする次の方式です。

natgateway

VPCエンドポイントはエンドポイントごとに料金が発生するデメリットがありますが、インターネットを経由することなくプライベート接続でセキュアにターゲットのエンドポイントにアクセスすることができるメリットがあります。

これに対してNATゲートウェイはNATゲートウェイを経由させればVPC外のどこへでもアクセスすることが可能になりますが、インターネットを経由するというデメリットがあります。

今回はセキュリティの考慮よりもコストを優先に考えてよいので、安ければVPCエンドポイントとNATゲートウェイのどちらを使ってもよかったのですが、どちらとも個人で使うには高すぎました・・

VPCエンドポイント、NATゲートウェイともに料金体系はALBと同様に利用時間と利用量に応じた従量制料金で課金を止める方法も削除しかありません。よって、1か月間ぞれぞれを使った場合の時間課金は次のようになります。

  • VPCエンドポイントは$7.2(=0.01[時間単価]x 30[日]x24[時間])[3]
  • NATゲートウェイは$32.4(=0.045[時間単価]x 30[日]x24[時間])[4]

料金的にNATゲートウェイはなにもしなくても月に4000円程度(130円換算)掛かるので選択肢になり得ません。VPCエンドポイントは時間課金に加えてエンドポイントを流れたデータ量に応じた料金が発生するため、コンテナイメージをダウンロードするECRの接続に使うには少し勇気が要ります。

そこで、他に良い手はないかと考え思いついたのが古き良き手法のNATインスタンスです。以前はNATインスタンス用のEC2イメージがAWSから提供されていましたが今は提供されていません。このため、NATインスタンスといってもその名のサービスがあるわけでなく単にEC2インスタンスに自分でiptablesをインストールし、次のようにパブリックサブネットとプライベートサブネット間のルーティング設定をしたEC2インスタンスになります。

natgateway

今回はApiGatewayサービスはパブリックサブネットのEC2で稼働させることにしたため、そのEC2にiptablesをインストールして設定するだけで済むため、実質追加コストゼロで対応できました🙌

スポットインスタンスの利用

#

これが料金を抑えるのに一番効果を発揮している手段です。

EC2やECSタスクは通常割り当てられたリソースを自分たちが解放するまで占有して使うことができますが、スポットインスタンスやFargateスポット(以降、両者をスポットインスタンスと呼ぶ)はAWSの空きリソースを使って稼働させるものになります。

スポットインスタンスは通常料金の最大90%OFF(大体70%程度)で利用することができますが、空きリソースを使っているため、AWSに空きリソースがない場合や他に優先すべきものがある場合、AWSの都合である時インスタンスが停止されるデメリットがあります。逆にそれ以外はまったく通常のインスタンスと同じです。

このため、プロダクション環境での利用は躊躇われますが、今回のようなデモ環境や開発環境用途であれば困ることはありません。

ということで、今回コンテナを実行させているEC2の 1インスタンスとFargateのECSの3タスクともすべてスポットインスタンスを使って稼働させています。

ちなみにですが、2か月くらいスポットインスタンスを使っていますが、アマゾンさんから停止のお手紙をもらったことはまだありません。

コンテナレジストリにGitHub Packagesを使う

#

ECRは無料枠もありますが一定量を超えた場合、管理するコンテナイメージのデータ量に応じて料金が課金[5]されます。これに対しGitHub Packages(正確にはGitHub Packages Container registry)はパブリックイメージであればどれだけ使っても無料[6]です。

GitHub Packagesで管理する対象がjarやnpmモジュールの場合、パッケージの公開範囲がパブリックの場合でもそのダウンロードにはGitHubの認証が必要となり、使い勝手が悪いため無料でも利用を敬遠しがちでした。そんなGitHub Packagesですが、実はコンテナイメージに限ってはパブリックイメージであればGitHubの認証なくイメージをダウンロードすることができ、使い勝手にも全く問題がありません。

したがって、コストだけを考えてパブリックなコンテナイメージを管理するのであれば、どれだけ使っても無料なGitHub Packagesに軍配があがります。

githubpackages

ただし、今回はコスト優先のため無視していますが、AWS上で使うことを前提にした場合、セキュリティ面では次に挙げるとおりECRに圧倒的に軍配があります。

  • 他のAWSサービスと同様IAMでコンテナイメージの取得等を制限できる
  • ECRはコンテナイメージに対する脆弱性スキャンを無料でかつ簡単に行うことができるが、GitHub Packagesを使う場合、GitHub Actionsで他のサービスを組み込んで利用する必要がある
  • コンテナレジストリのエンドポイントはGitHub PackagesとECRのどちらともVPC外になるが、ECRはVPCエンドポイント使うことでインターネット網を経由せずコンテナイメージを取得をすることができるが、GitHub Packagesはインターネット網経由となる[7]

今回は用途がデモサイトのためセキュリティに関する考慮はある程度無視できるため、コスト優先のGitHub Packagesを採用しましたが、業務で利用する場合など一定のセキュリティが求められる環境ではECRを使うのが一般的です。

CI/CDはAWSのCodeシリーズではなくGitHub Actionsを使う

#

コンテナレジストリにGitHub Packagesを使うことにしたので、CI/CDにはGitHub Actionsを悩まず使うことにしました[8]

CodeDeployはそもそもAWS内へのデプロイであればいくら使っても無料[9]で、CodeCommitも今回の個人用途であれば無料枠[10]に収まる範囲だと思います。しかし、CodeBuildとCodePipelineは少し微妙です。

CodeBuildは一定のビルド時間までは無料ですが、それ以上は料金がかかります[11]。実際問題金額は大した額ではないですが、ビルドする度に今月はまだそれほど使ってないよな・・などと思うのは精神衛生上よろしくありません。また、CodePipelineも1本は無料ですが、それ以降は料金が発生します[12]

これに対して、GitHubはGitもActionsもどれだけ使ってもパブリックリポジトリであれば無料です。最初にGitHub Packagesを使うからCI/CDは無条件にGitHub Actionsでといいましたが、この前提がなくフラットに考えても今回の用途ではGitHub Actionsを選択したと思います。

githubpackages

じゃ、AWSのCodeシリーズに良いところはないのか?というとそんなことはなく、GitHub Packagesの選定と同じように、AWSのCodeシリーズはそれぞれのサービスに対して権限管理をIAMで一元的に管理できることと、AWSの閉域網内でタスクが完結するといったセキュリティ面での強みがあります。

使わない時間帯はGitHub Actionsを使ってサービスを止める

#

自分で使うにしても、他人に使ってもらうにしても深夜、早朝にサンプルアプリを使うことはまずありません。このため、深夜11時から翌9時まではGitHub Actionsのスケジュール起動で起動/停止するようにしています。AWSのサービスをスケジュール停止する方法としてはAWS Lambda + CloudWatch Eventsをよく見かけますが、今回はGitHub ActionsのワークフローからAWS CLIを使って起動と停止を行っています。その理由はもちろん気兼ねなく無料で使えるからです。

ケチケチ作戦を実施した結果

#

ここまで書いた爪に火を点すようなケチケチ作戦を実施した結果、AWSを

  • EC2(t2インスタンス) x 1(スポットインスタンス) x 24時間/日稼働[13]
  • ECSタスク(0.25vCPU/0.5GB) x 3(Fargateスポット) x 14時間(9時~23時)/日稼働

の条件で使った場合の日々のコストを次のようにすることができています。

cost

グラフは執筆時の直近1週間のコストのグラフです。結果は見てのとおり、ネットワーク系の料金が発生しない構成にし、EC2とECSタスクにスポットインスタンスを利用することで毎日$0.42(約50円)に抑えることができています。(5/22の料金が少し多めなのはデバッグで深夜まで時間延長して使ったため)

なお、実業務ではここに表れていないCloudWatchのコストが高くなりがちですが、今回はサンプルということもあり無料枠に収まっています。

まとめ(そもそもなぜケチろうと思ったのか)

#

実践ではあまり訳に立ちそうにないケチケチ作戦の紹介は以上ですが、最後にケチった環境を作ろうと思った動機に少し触れて終わりにしたいと思います。

筆者が所属する豆蔵には技術調査や自己啓発用途で自由に使えるAWS環境が用意されています。このため、会社のAWS環境でアプリを動かす環境を作る選択肢もありましたが、今回はそれはせず筆者個人のアカウントでAWS環境を作ってみました。

これは、自腹でドキドキすることでAWSのコストも身にしてみて意識できるようになろう!という理由が大きいです。

会社で自由に使えるAWS環境は、豆蔵がエンジニアに優しい会社であることを反映してか、月額かなりな予算が割り当てられています。これはこれで何かを調査、検証する際にコストを気にしたり、都度予算を調整したりする必要がない恵まれた環境なのですが、その反面、コストに対する意識が低くなります。

しかし、実際のプロジェクトではコストに対する制約は必ず存在し、かつコストの範囲内でベストな選択することやコストを抑えた工夫が必ず求められます。

このため、今回はコストも気にして環境を作ってみよう!と思ったのですが、上述のとおり会社の環境では意識が甘くなるため、だったら、自腹で!と考え、個人アカウントで作ってみました。

で、実際やってみた結果ですが、間違って実は高額なサービスを使ってしまった場合や低額なサービスでも下手な使い方をした場合、万単位でお金が飛んでいく可能性があるため、AWSの料金説明はコレでもか!くらいに調べるようになりました。

また、やりたいことがあってもそれを実現する代表的なサービスの料金が高い場合、自腹のため、他で代替できないか?などを自然に考えるようになるため、選択する方式の選択肢も広がりました。

CostExploreを見るのがドキドキなこともあったりはしますが、結果としては、ケチケチ運用でお金以上に得られるものはあったかなと思います。


  1. 料金 - Elastic Load Balancing | AWS ↩︎

  2. Elastic IPの料金体系は若干複雑ですが今回のケースは利用するElastic IPは1つで、かつ常時稼働しているインスタンスに割り当てるため料金は発生しません。 ↩︎

  3. 料金 - AWS PrivateLink | AWS ↩︎

  4. 料金 - Amazon VPC | AWS ↩︎

  5. 料金 - AWS ECR | AWS ↩︎

  6. GitHubパッケージの支払いについて - GitHub Docs ↩︎

  7. GitHubをSaaSではなくGitHub Enterpriseを自分でインストールして運用する場合はこの限りではない(が、ECRを利用する場合と比べ大がかりとなる) ↩︎

  8. デプロイごとに指定したコミットハッシュタグのコンテナイメージを起動するシェルスクリプトの配置とコンテナの起動・停止を行うため、EC2のこの部分に対しては限定的にAWS CodeDeployを使っています。 ↩︎

  9. 料金 - AWS CodeDeploy | AWS ↩︎

  10. 料金 - AWS CodeCommit | AWS ↩︎

  11. 料金 - AWS CodeBuild | AWS ↩︎

  12. 料金 - AWS CodePipeline | AWS ↩︎

  13. 前段のApiGatewayサービスだけは時間外エラーをハンドリングしてユーザに通知する必要があるため24時稼働させています。 ↩︎

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。