はじめに
当ブログはQiita Advent Calendar2023に投降した下記ブログの転載+加筆になります。 初アドカレついでにQiitaを試してたのですが、運営している本ブログにも載せます。
Infrastructure from Code(IfC)とAmptについて少し学習したので、整理して書きたいと思います。
きっかけ
ServerlessDays Tokyo2023のAWS Eric JohnsonのセッションでIfCという単語を初めて聞いたのですが、中身について触れなかったので何なのか分からなかったのとIaCと何が違うのか気になったためです。
https://tokyo.serverlessdays.io/
Infrastructure from Codeとは何か
アプリケーションコードから必要なクラウドリソースを推測して、自動でアプリケーションの基盤となるインフラストラクチャをクラウドにプロビジョニングするアプリケーション構築のプロセス。
例えば、アプリケーションコード内にAPIのGETやPOSTが書かれていれば、CDNにAPIサーバが必要だと推測して、AWSならCloudFrontにAPI Gateway、Lambdaのリソースを自動でプロビジョニングしてくれるようです。
背景
サーバーレスアーキテクチャの開発ではクラウドリソースのプロビジョニングにはInfrastructure as Codeのプロセスで行っていましたが、アプリケーションの開発者からすると下記の課題があったようです。 インフラストラクチャコードもコードではあるものの、アプリ開発者にとってはインフラにあたるので極力インフラストラクチャコードすらも書かないようにしたいという思いがあるようにも見えます。
①多量のインフラストラクチャコードを書く必要がある
②アプリケーションコード側にサービス制御のコードが書けない。
③アプリケーションコードとインフラストラクチャコードが密結合になり変更が容易でない。
①はインフラストラクチャコードは何十、何百行と書く必要があり、さらにサーバーレスだと複数のサービスを組み合わせてアプリを開発するので、サービスの数だけインフラストラクチャコードが増えてしまい作業時間が増えて、アプリケーションコードを書くのに割ける時間が減ってしまう。
②はタイムアウトや処理中断時の制御はアプリケーション側でも検討するが、制御するコードはインフラストラクチャコードに書かないといけない。例えばLambdaのタイムアウト時間。
③どのアプリケーションコードがどのインフラストラクチャ上で動くのか宣言しておく必要があり、どちらかに変更があると、もう片方への影響調査や改修が発生してしまう。
IaCの例
課題を具体的にコードで例えてみます。 趣味で開発していたBedrockへリクエスト送受信をするだけのLambda関数をSAMテンプレートコード化してみました。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: An AWS Serverless Specification template describing your function. Resources: bedrocktest: Type: AWS::Serverless::Function Properties: CodeUri: . Description: '' MemorySize: 128 Timeout: 70-----② Handler: lambda_function.lambda_handler-----③ Runtime: python3.11 Architectures: - x86_64 EventInvokeConfig: MaximumEventAgeInSeconds: 21600 MaximumRetryAttempts: 2 EphemeralStorage: Size: 512 Layers: - arn:aws:lambda:us-east-1:XXXXXXXXXX:layer:boto3-1_28_73:2 RuntimeManagementConfig: UpdateRuntimeOn: Auto SnapStart: ApplyOn: None PackageType: Zip Policies:----以下② - Statement: - Sid: VisualEditor0 Effect: Allow Action: - bedrock:* Resource: '*' - Effect: Allow Action: - logs:CreateLogGroup Resource: arn:aws:logs:us-east-1:XXXXXXXXXX:* - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: - >- arn:aws:logs:us-east-1:XXXXXXXXXX:log-group:/aws/lambda/bedrock_sample:*
たった1つの関数ですがSAMテンプレートコードの場合はインフラストラクチャコードが45行になりました。実際のアプリだともっとたくさんの関数があるでしょうし、他のサービスも使うとその数だけコード量が増えます。また、関数間やサービス間の通信に認証認可のためのコードも書く必要が出てくると思われます。 これが課題①にあたるところになります。
課題②はTimeoutやPoliciesがそれらにあたると思われます。処理のタイムアウト時間や他サービスとの通信制御をするためのコードはアプリ側ではなく、インフラストラクチャコードに書かざるえません。
課題③はHandlerと②がそれらにあたると思われます。Lambdaのお約束として、Handlerに書かれたLambda関数ハンドラーをアプリケーションコード側にも書く必要があります。そのため、Lambdaから他サービスに移植したいとなったときはアプリケーションコードを改修が必須となります。また、他AWSサービスと連携させたいときには、アプリケーションコードだけでなく、インフラストラクチャコードに連携するAWSサービスとの認証認可のコードを書く必要が出てきており、アプリケーションコードとインフラストラクチャコードが密結合になっていると言えるのではないでしょうか。
*課題②③はコードスニペット内の該当コード右側に「②」「③」を付記しています。
Self Provisioning Rumtime
課題に関して、解釈的アプローチで考えていった結果、アプリケーションコードとインフラを紐づける部分を自動的に推測してプロビジョニングさせることを思いつき「Self Provisioning Rumtime」と名付けたようです。
①アプリケーションロジックから必要なリソースとアクセス権限を推測させる
②イベントハンドラーでサービス制御を処理
③サービスとの通信を構造から自動的にマッピングさせる
この3点について、クラウドアーキテクチャを設計したことがある方は頭の中の思考や設計書で似たようなことしていた経験があるかと思います。 機能要件で求められているある機能のアーキテクチャを設計するときに、数分で処理が終わりそうな機能要件に対してはLambdaを選ぼう。もし、数分で終わらないときを考慮してタイムアウト時間を設けるようにしよう。処理結果をもしS3に保管する必要があるなら、LambdaにS3への書き込み許可を付与するロールを作ろうと言った経験です。 Self Provisioning Rumtimeはまさしくこの思考を自動でやろうというアイデアと言えそうです。
サービス
簡単にですが、IfCのサービスを紹介します。何をベースにしているかで4つに分類できるらしく、既存のアプリケーションコードに簡単に導入できそうなサービスもあれば、一から開発が必要なサービスもあり、使ってみたいときは導入するアプリの状況によって検討することになるかなと思います。
プログラミング言語ベース:Wing、DarkLang クラウドリソース定義のための新しいプログラミング言語を利用する。
SDKベース:Ampt、Nitric アプリケーションコードに独自の SDK を導入して利用する。
Annotations + Frameworks:Encore、Shuttle 既存のプログラミング言語にフレームワークにライブラリの追加やアノテーションを付けて対応している。
Pure Annotations:Klotho 既存のアプリケーションコードのインラインコメントでアノテーションすることで対応する。
ぱっと見ると、プログラミング言語ベースは最もと導入難易度が高く、記載順に導入難易度が下がっていく感じがします。
実際にどうプロビジョニングされるのか
冒頭の技術カンファレンスで紹介されていたという理由でAmptの場合、Lambdaはどのようなコードからプロビジョニングされるのか見てみます。
- Ampt構成
コードの前に少しだけAmptの構成を説明します。 こちらはAmptのサイトにある構成図と実際に操作した経験からイメージした簡単な構成図とアプリ開発の流れになります。図の右側は学習内容と動きから想定されるイメージのため誤っている可能性がある点ご承知おきください。 先述した通り、AmptはSDKベースのサービスです。開発するにはSDKが必要です。ソースコードの管理はGitHubになります。 また、クラウドプラットフォームがありRuntimeやアプリ管理のコンソール画面などの機能を備えています。
ユーザーはAmptのSDKをインストールした開発端末上でアプリ開発を行い、デプロイを行います(図の①)。するとAmptがRumtime機能によりソースコードの解析を行い(図の②)、クラウド上に必要なリソースをプロビジョニングしてアプリが稼働するクラウドインフラを構築します(図の③)。構築したアプリの状況はAmptのコンソール画面にアクセスして管理を行います(図の④)。
- コード例
では、どうLambdaだと推測してプロビジョニングされるのかスケジュールタスクのコードを例に説明します。
//1時間毎にログを出力する。 schedule.every("1 hour", () => { console.log("I run every hour!"); });
「.every」はスケジューリングを定義するためのメソッドで、時間指定やcron形式を用いて定期実行を可能にします。このコードでは1時間ごとにログ出力を行います。AmptのRumtimeはこのコードから何のサービスをプロビジョニングすべきか推測を行います。例では、スケジューリングを行えるサービスとログ出力を行うコンピューティングサービスが必要だと推測して、AWSにあるそれらに該当するサービスであるEventBridgeとLambdaをプロビジョニングする。という流れになるようです。
もう1つ例をあげてみます。
//タイムアウトを20分に設定したコード。 schedule.every("12 hours", { timeout:1200000 } () => { console.log("I run every 12 hours!"); });
前と同じスケジューリングのコードですが、「timeout」のハンドラーが存在しています。タスクのタイムアウトをミリ秒で指定でき、例では1200000ミリ秒=20分を指定しています。このスケジュールタスクをプロビジョニングするとき、timeoutの指定通りに最低20分は稼働するコンピューティングサービスを選択する必要があるため、タイムアウトが最大15分であるLambdaではなく、Fargateをプロビジョニングする判断をするようです。
所感
- サーバーレスにはインフラストラクチャを意識させないという要素がありますが、背景から推察するとIaCではまだどのインフラにアプリを乗せるかといったインフラストラクチャを意識させる部分があり、真のサーバーレスに近づけるためにも新しいプロビジョニング方法が求められていたのかなと思いました。
Fighting off faux-serverless bandits with the true definition of serverless — Momento
- Self Provisionig Rumtimeで自動化する3点は自分も開発で苦労していた部分で、自動化できないのかと考えたこともありましたが、本当に自動化するサービスが実現していたことに驚きました。
- 触ったサービスの特性やまだ歴史が浅いのもありますが、本格的に導入するのはまだ厳しそうに感じました。でも、クラウドインフラストラクチャを一切意識せずにアプリ開発ができるので、小規模な個人開発や新規サービスのモックアプリ開発で導入してみるのはありかもしれないと思います。
- 今回書けなかったですが、学習にあたってAmptのアカウントを作成し、サンプルアプリをプロビジョニングするところまで触ってみました。CLIやブラウザベースのコンソール画面から操作できるのですが、UIとしてもクラウドインフラストラクチャを意識させるようなところはなく、最初の印象としてはAmptはIfCの考えを徹底しているサービスだなと感じています。触ってみたところは、別途ブログにしたいと思います。
- これが本格的に普及したらクラウドエンジニアの仕事が減るかもしれない…
参考
学習のために見たサイトと動画です。
Infrastructure from Code (IfC)
AWS re:Invent 2022 - Unleash developer productivity with infrastructure from code (COM301) - YouTube
State of Infrastructure-from-Code 2023 - Klotho