はじめに
サーバーレス環境のAWS Lambdaをローカルで開発できるよう、AWS SAMを使っています。
https://aws.amazon.com/jp/serverless/sam/
ソースコードもgit管理できるため便利ですし、ローカルで開発している間は課金される恐怖からも逃れることができます。
しかし、ローカルとそれ以外の環境は全く同じわけではありません。特に環境変数回りでハマることがあったので、記録しておきます。
やりたいこと
- デプロイする環境に応じて環境変数を分けたい
- セキュアな情報はSecrets Managerで管理したい
結論
- 環境変数はtemplate.yamlに書く
- 環境変数の指定はsamconfig.tomlと実行時コマンドで行う
- Secrets Managerで保管した値を環境変数に指定し、template.yamlで読み込むことができる
前提
Windows 11 + WSL2でPython3環境のスクリプトを開発します。
WSLにはdocker
, sam
, python3
を入れてあります。
また、AWS SAM自体の説明は省略します。
AWS SAMでLambdaを開発する
ローカル環境はDockerで開発する
ローカル環境は sam local invoke
コマンドで実行します。
$ sudo sam build --use-container
$ sudo sam local invoke
各環境はデプロイする
検証環境・本番環境にデプロイする際には、 sam deploy
コマンドを使います。
$ sudo sam build --use-container
$ sam deploy --config-env staging
環境変数管理
前述のように、SAMを使うとローカルで開発できる代わりに、ローカル環境はDocker、それ以外の環境ではAWS環境になります。
ローカル環境は直書きでも問題ないですが、本番環境の環境変数をソースコード内に残したくないので、AWS Secrets Managerに記載します。
環境に応じて設定を分ける
環境変数定義
SAMが使用するtemplate.yaml
に、環境変数一式を定義します。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Environment:
Variables:
DatabaseHost: !FindInMap [ Variables, !Ref Env, DatabaseHost ]
DatabasePort: !FindInMap [ Variables, !Ref Env, DatabasePort ]
DatabaseUser: !FindInMap [ Variables, !Ref Env, DatabaseUser ]
DatabasePassword: !FindInMap [ Variables, !Ref Env, DatabasePassword ]
DatabaseName: !FindInMap [ Variables, !Ref Env, DatabaseName ]
...
Mappings:
Variables:
local:
DatabaseHost: 'db'
DatabasePort: '3306'
DatabaseUser: 'hogehoge'
DatabasePassword: 'example'
DatabaseName: 'test'
staging:
DatabaseHost: '{{resolve:secretsmanager:staging-secret-id:SecretString:DatabaseHost}}'
DatabasePort: '{{resolve:secretsmanager:staging-secret-id:SecretString:DatabasePort}}'
DatabaseUser: '{{resolve:secretsmanager:staging-secret-id:SecretString:DatabaseUser}}'
DatabasePassword: '{{resolve:secretsmanager:staging-secret-id:SecretString:DatabasePassword}}'
DatabaseName: '{{resolve:secretsmanager:staging-secret-id:SecretString:DatabaseName}}'
...
Parameters:
Env:
Type: String
Default: staging
AllowedValues: [local, staging, production]
環境変数をSecrets Manager内に書く
コマンドと環境変数をマッピングする
定義した環境変数を呼ぶには、コマンド内でEnv
指定を渡してあげる必要があります。
samconfig.toml
各環境のparameter_overrides
で、template.yaml
の環境を指定します。
[default]
や[staging]
は、コマンドの --config-env
属性に対応します。
# samconfig.tomlの[staging]呼び出し
$ sam deploy --config-env staging
version = 0.1
[default]
[default.global.parameters]
[default.build.parameters]
cached = true
parallel = true
[default.validate.parameters]
lint = true
[default.deploy.parameters]
capabilities = "CAPABILITY_IAM"
confirm_changeset = true
[default.package.parameters]
[default.sync.parameters]
watch = true
[default.local_start_api.parameters]
warm_containers = "EAGER"
[default.local_start_lambda.parameters]
warm_containers = "EAGER"
[default.local_invoke.parameters]
stack_name = "gate"
parameter_overrides = [
"Env=local"
]
[staging]
[staging.deploy]
[staging.deploy.parameters]
stack_name = "gate-staging"
s3_bucket = "gate-staging-source"
s3_prefix = "staging"
region = "ap-northeast-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
image_repositories = []
parameter_overrides = [
"Env=staging",
]
ソースコード内で参照する
Pythonだとこんな感じになります。
import os
import mysql.connector
def lambda_handler(event, context):
conn = mysql.connector.connect(
host = os.environ['DatabaseHost'],
port = os.environ['DatabasePort'],
user = os.environ['DatabaseUser'],
password = os.environ['DatabasePassword'],
database = os.environ['DatabaseName'],
connection_timeout = 3
)
# 以下、実装
ハマったこと
複数環境にデプロイできない!
こんなコマンドを打っていたためでした。
$ sam deploy production
マニュアルを読むと、引数には必ずオプションが付くことがわかります。
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html
production
とだけ書いても、コマンドは認識してくれず、defaultの staging
をデプロイしようとします。
環境を指定するには、先述のsamconfig.tomlを記載した上で、
$ sam deploy --config-env production
と指定しなければいけません。