💡

RustでAWS Lambda上でFeatureFlagを扱う

2025/03/01に公開

目的

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