LoginSignup
19
4

More than 1 year has passed since last update.

AWS Lambdaで使いたい環境変数をAWS SAM CLIでどうするか

Last updated at Posted at 2021-09-30

はじめに

環境変数の扱いについて、戸惑いがありましたのでまとめます。
用途や仕様の背景が理解し切れて一ませんが、少なくとも現状はベストエフォートとは思えず、改善が進むと嬉しいです。

やっていたことの概要

  • 言語:Rust(記事としては、Rustに依存する内容ではないです)
  • 作っていたもの:AWS Lambda関数
  • 動作確認の方法:後述(※)
  • デプロイの方法:AWS SAM CLI(sam deploy
  • その他:AWSの設定はCloudFormation(template.yaml)に集約
※動作確認の方法

AWS SAMを使うとローカルでLambda関数の動作確認が可能ですが、私の環境ではビルドが遅くデプロイ直前までは、cargo runで動作確認を行っていました。

動作確認タイミング 動作確認の例 動作確認環境
開発時 cargo run, cargo test MacOS
デプロイ前検証 sam local invoke カスタムランタイムのエミュレーションコンテナ
デプロイ後検証 AWSコンソールからLambdaトリガー実行 Lambda上のカスタムランタイムのコンテナ(Zipでのデプロイのため)

ここで問題なのは、動作確認のタイミングで全ての環境が異なる点です。
Rustに限らず環境変数は.envファイルを作成し何らかの手段でプログラム利用するのが多いかと思います。
ただデプロイ前検証、デプロイ後検証では、macに置いた.envファイルは何もしなければ持ち込まれません。

なお、環境設定ファイルなので環境ごとに設定するものを記載すべきかもしれませんが、GitHubに上げたくない隠匿したい情報を記載しています。環境ごとの違いはなく同じファイルを読んで欲しいのに読んでくれない・・・という問題に直面しました。

解決策

いくつか試しました。

sam local invokeで環境設定ファイルが読み込めない

まずは通常のRust開発を行なった状態から。
Rustにおける環境設定ファイルの利用は、dotenvonce_cellを使いました。

/work/project/.env
TEST=HelloEnv
AWS_S3_BUCKET=my-work-project-bucket
/work/project/src/config/config.rs
use serde::Deserialize;
use config::ConfigError;
use dotenv::dotenv;
use once_cell::sync::Lazy;

#[derive(Deserialize, Debug)]
pub struct Config {
    pub test: String,
    pub aws_s3_bucket: String,
}
// 初期化(定数へセット)
pub static CONFIG: Lazy<Config> = Lazy::new(|| 
    {
        dotenv().ok();
        Config::from_env().unwrap()
    }
);

// 環境変数を読み込む
impl Config {
    pub fn from_env() -> Result<Self, ConfigError> {
        let mut cfg = config::Config::new();
        cfg.merge(config::Environment::new())?;
        cfg.try_into()
    }
}

mod tests {
    use super::*;
    #[test]
    fn get_env() {
       assert_eq!(CONFIG.test, "HelloEnv");
   }
}

cargoでは、問題なく動作しています。

$ cd /work/project
$ cargo test
test config::config::tests::get_env ... ok

しかし、sam local invokeでは環境変数がなく構造体に展開できないエラーが出ます。

$ cd /work
$ sam build
$ sam local invoke {関数名}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: missing field `test`', src/config/config.rs:15:28

このコマンドを実行しているのは、MacOS上ですが、sam local invokeではコンテナが起動してその中で関数が実行されるため、.envが参照できていません。

解決策①ビルド時に.envをコピー対象にする

ビルドに利用しているMakefile内に1行追加。

/work/project/Maikefile(抜粋)
build-StockRustFunction:
	cargo build --bin stock --release --target x86_64-unknown-linux-musl
	cp ./target/x86_64-unknown-linux-musl/release/stock $(ARTIFACTS_DIR)/bootstrap
	cp ./.env $(ARTIFACTS_DIR)/.env # ★★今回追加★★

デプロイ前動作確認にて問題ないことを確認

再ビルドが必要
$ cd /work
$ sam build
$ sam local invoke {関数名} // 成功

デプロイ後動作確認にて、環境変数という形で参照できないものの関数実行は成功することを確認。
スクリーンショット 2021-09-29 14.18.09.png
image.png

これでいいんだ感。
いろいろ試しましたが、開発環境とデプロイデプロイ環境で同一の設定情報を使う限りにおいては、編集ファイルが少なく一番手っ取り早い手段でした。商用利用するサービスでは確実に開発と商用環境は異なると思いますのであくまで個人開発の範疇でしたら利用してもいいかもという感覚です。
Lambdaの「環境変数」で見えないため、デプロイ後時間が空いてしまうと設定していない情報がなぜか使えているという挙動が分かりづらい点も気になります。

解決策②template.yamlに環境変数をベタ書きする

template.yamlには、環境変数を定義する箇所があります。
これは、ビルド情報に含まれるためデプロイ前(sam local invoke)環境、デプロイ後(sam deploy)環境どちらでも有効に働きます。

/work/template.yaml(抜粋)
Resources:
  StockRustFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment: 
        Variables:
          TEST: HelloEnv
          AWS_S3_BUCKET: my-work-project-bucket

デプロイ前動作確認にて問題ないことを確認

再ビルドが必要
$ cd /work
$ sam build
$ sam local invoke {関数名} // 成功

デプロイ後動作確認にて、環境変数が設定されていることを確認。
なお、この画面上の「編集」ボタンにて追加削除も可能なので、template.yamlを使わずともデプロイ後の関数に追加することも可能ですが、毎回手での追加作業が発生します。
スクリーンショット 2021-09-29 13.51.20.png

開発環境は、.env、デプロイ環境はtemplate.yamlというわけで環境ごとにファイルが分けられているのも自然ですし、特に問題ない手段かと思いますが、template.yamlにベタ書きなので設定する環境変数をまとめて管理したい場合や秘匿情報は別管理したい場合に不向きです。

##番外編--env-varsの利用
公式ドキュメントより、sam local invokeの環境変数の持ち込みに使えそうなオプションは、--env-vars--parameter-overridesがあります。

まずは、リンク先に従い、--env-varsから試してみます。
スクリーンショット 2021-09-29 13.12.12.png
なお、後述しますが、こちらは、sam buildsam deployのオプションとしては存在していないため、最終的には利用をしていません。
--env-vars PATHのPATH先のファイルは、jsonのみ指定可能で、なんと.envは利用できないため、新たに作成します。

/work/project/env.json
{
    "StockRustFunction": {
        "TEST": "HelloEnv!!",
        "AWS_S3_BUCKET": "my-work-project-bucket"
    }
}

Environment部分を追加していますが、env.jsonの定義に差し替えられる形になります。

/work/template.yaml(抜粋)
Resources:
  StockRustFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment: 
        Variables:
            TEST: TEST
            AWS_S3_BUCKET: AWS_S3_BUCKET

デプロイ前動作確認にて問題ないことを確認

再ビルドが必要
$ cd /work
$ sam build
$ sam local invoke --env-vars env.json {関数名} // 成功

次にデプロイ後の確認ですが、前述の通りsam buildsam deploy--env-varsは存在しませんので、デプロイ自体できませんでした。

$ sam deploy --env-vars env.json
Usage: sam deploy [OPTIONS]
Try 'sam deploy -h' for help.

Error: no such option: --env-vars

デプロイできる他の方法を模索します。
私の手順では、template.yamlenvironmentに任意の文字列を定義しましたが、本来はここにデプロイ後に使う値が設定されており、デプロイ前確認時(sam local invoke)に別環境でテストする場合などに、上書きするような使い方が想定されているように思います。
ですので、用途が若干違うというか、今後sam buildsam deployにも当該オプションがつくかというと難しいでしょう。

解決策③--parameter-overridesの利用

--parameter-overridesは、sam local invokesam buildsam deploy全てに存在するオプションですのでこちらを利用します。

スクリーンショット 2021-09-29 13.14.58.png

/work/template.yaml(抜粋)
Parameters:
   Test:
     Type: String
   AwsS3Bucket:
     Type: String

Resources:
  StockRustFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment: 
        Variables:
          TEST: !Ref Test
          AWS_S3_BUCKET: !Ref AwsS3Bucket

デプロイ前動作確認にて問題ないことを確認

再ビルドが必要
$ cd /work
$ sam build
$ sam local invoke {関数名} --parameter-overrides Test="HelloEnv" AwsS3Bucket="my-work-project-bucket" // 成功

デプロイ後動作確認にて問題ないことを確認

deploy時もオプションの指定が必要
$ sam deploy --parameter-overrides Test="HelloEnv" AwsS3Bucket="my-work-project-bucket"

実行時のパラメータ指定が多いとだいぶ厳しいですね。
④でこのパラメータをなくしていきます。

解決策④samconfig.tomlにパラメータを定義する

/work/template.yaml
//③と同じものを使うため、省略

こちらの公式の記載ルールに従い、実行時のパラメータを設定ファイルに記載していきます。

/work/samconfig.toml(抜粋)
version = 0.1
[default]
[default.global.parameters]
parameter_overrides = "Test=HelloEnv AwsS3Bucket=my-work-project-bucket"

[default.deploy]
[default.deploy.parameters]
#parameter_overrides = "Test=HelloEnv AwsS3Bucket=my-work-project-bucket"

[default.local_invoke]
[default.local_invoke.parameters]
#parameter_overrides = "Test=HelloEnv AwsS3Bucket=my-work-project-bucket"

[default.deploy.parameters][default.local_invoke.parameters]にそれぞれ別の定義をすることもできます。
今回は、環境ごとの違いがないので、[default.global.parameters]で共通設定しました。

デプロイ前動作確認にて問題ないことを確認。オプション指定も不要です。

再ビルドが必要
$ cd /work
$ sam build
$ sam local invoke {関数名} // 成功

デプロイ後動作確認にて問題ないことを確認。オプション指定も不要です。

deploy時もオプションの指定が不要となりました
$ sam deploy

スクリーンショット 2021-09-29 13.51.20.png

実行時のパラメータは略すことできましたが、samconfig.tomlに情報が混在してしまいました。
また、現在は2つの変数だけですが数が増えたらみにくくなります。

おわりに

現状は、④を使っていますが、一般的なのは③でしょうか。
結局同じ情報が.envsamconfig.tomlで二つあるのですが。。

参考

試せませんでしたが、S3にenv.jsonをS3にアップしてその場所を参照するようにする方法も見かけました。

template.yaml抜粋
      Environment:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: s3://*******/environment.json
19
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
4