💡
RustでAWS Lambda上でFeatureFlagを扱う
目的
FeatureFlagを使ってデプロイせずにプログラムを動きを制御したいです。
AWS Lambdaで起動しているRustのコードで利用してみます。
FeatureFlagを作るのは以下の記事で説明しています。
AWS AppConfigのFeatureFlagをCDKでデプロイする
AWS Lambdaにデプロイする方法は以下の記事で説明しています。
AWS LambdaにRustのコードをデプロイする
AWS Lambda Layer
Using AWS AppConfig Agent with AWS Lambda
にあるようにAppConfigの情報を取り出してくれるLambda Layerがあるのでこれを利用します。
CDK
LayerはARM64のものを選択してください。
lib/cdk-lambda-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as path from 'path';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
export class CdkLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props)
// Lambda
const func = new lambda.Function(this, "LambdaFunction", {
architecture: lambda.Architecture.ARM_64,
functionName: 'feature-flag-api',
handler: "bootstrap",
logRetention: logs.RetentionDays.ONE_DAY,
runtime: lambda.Runtime.PROVIDED_AL2023,
code: lambda.Code.fromAsset(path.join(__dirname, '../../build/api')),
timeout: cdk.Duration.seconds(600),
memorySize: 128,
layers: [
lambda.LayerVersion.fromLayerVersionArn(
this, 'Layer',
'arn:aws:lambda:ap-northeast-1:980059726660:layer:AWS-AppConfig-Extension-Arm64:79')
],
environment: {
REALM_CODE: 'staging',
AWS_APPCONFIG_EXTENSION_LOG_LEVEL: 'debug',
},
});
func.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE
});
// Role
const role = new iam.Role(this, 'Role', {
roleName: 'AppConfigRole',
assumedBy: func.grantPrincipal,
inlinePolicies: {
UserTablePut: new iam.PolicyDocument({
statements: [new iam.PolicyStatement({
actions: ['appconfig:StartConfigurationSession', 'appconfig:GetLatestConfiguration'],
effect: iam.Effect.ALLOW,
resources: [`arn:aws:appconfig:${this.region}:${this.account}:application/*`]
})]
})
}
});
func.addEnvironment('AWS_APPCONFIG_EXTENSION_ROLE_ARN', role.roleArn);
}
}
Rust
main.rs
use axum::{
http::StatusCode, response::IntoResponse, routing::get, Json, Router
};
const APP_NAME: &str = "feature-flag-sample";
const ENV: &str = "staging";
const PROFILE: &str = "default";
#[tokio::main]
async fn main() {
// ルーターの作成
let app = Router::new()
.route("/", get(feature_flags));
// サーバーの起動
let app = lambda_http::tower::ServiceBuilder::new()
.layer(axum_aws_lambda::LambdaLayer::default())
.service(app);
lambda_http::run(app).await.unwrap();
}
// ハンドラー関数
async fn feature_flags() -> impl IntoResponse {
let client = reqwest::Client::new();
let res = client.get(&format!("http://localhost:2772/applications/{}/environments/{}/configurations/{}", APP_NAME, ENV, PROFILE)).send().await.unwrap();
let json = res.json::<serde_json::Value>().await.unwrap();
(StatusCode::OK, Json(json))
}
まとめ
ローカルでAPIを叩くとFeatureFlagが返ってくるのは面白い仕組みですね。実際にFeatureFlagを変えてみると、速攻で変更するデプロイ戦略なのですぐにかわります(ローカルのキャッシュはありますが)。
Discussion