複数の Lambda 関数で構成される環境を、基本的な構成は Terraform で管理しつつ、Lambda 関数のコードは make で aws cli を呼び出してデプロイするメモ。
Lambda 関数のコードも含めてすべてを Terraform で管理することもできると思いますが・・・それだとコードを修正したときのデプロイに無駄に時間が掛かりすぎて辛いので却下。
Terraform での Lambda 関数の定義
Lambda 関数の作成は Terraform でやりますが、コードの更新は aws cli を使うので、Terraform でコードの内容の変化は無視しておく必要があります。
locals { # ダミーのファイル(空で良い) dummy_file = { filename = "dummy.zip" source_code_hash = filebase64sha256("dummy.zip") } } resource "aws_lambda_function" "aaa" { function_name = "aaa" role = aws_iam_role.lambda.arn handler = "index.aaa" runtime = "nodejs12.x" filename = local.dummy_file.filename source_code_hash = local.dummy_file.source_code_hash # filename と source_code_hash を無視する lifecycle { ignore_changes = [ filename, source_code_hash, ] } }
Makefile
make で Lambda 関数のコードのアップロードや、ログの表示ができます。
# Lambda 関数にアップロードするアーカイブを作成 make build # すべての Lambda 関数にアーカイブをアップロード make deploy # 特定の Lambda 関数にアーカイブをアップロード make deploy/aaa # すべての Lambda 関数のログを aws logs tail --follow で表示 # 複数のロググループを並列に tail --follow するために -j で並列処理させる make logs -j # 特定の Lambda 関数のログを aws logs tail --follow で表示 make logs/aaa
make logs/aaa
などで指定している aaa
は make のターゲットで指定しやすくするためのエイリアスのようなもので envs/dev.mk
などの環境ごとのファイルで次のように定義しています。IAM クレデンシャルのプロファイルを切り替えるために aws コマンドもここで指定しています。
# aws コマンド(対象となる環境によってプロファイルを切り替える) AWS := aws --profile oreore # make のターゲットで使用する名前のリスト TARGETS := aaa bbb ccc # ↑のターゲット名と実際の Lambda 関数の名前のマッピング LAMBDA_aaa := test-dev-lambda-aaa LAMBDA_bbb := test-dev-lambda-bbb LAMBDA_ccc := test-dev-lambda-ccc
Makefile は次のような内容です。
# 対象となる環境ごとの定義ファイルをインクルード include envs/$(APP_ENV).mk # ソースファイル # 直下の *.js と src ディレクトリ内の *.js # node_modules は含めません SRC_FILES := $(wildcard *.js) $(shell find src -name '*.js') # ビルド用ディレクトリ # アップロード用のアーカイブやデプロイ結果が保存されます BUILD_DIR := .build BUILD_ENV_DIR := $(BUILD_DIR)/$(APP_ENV) .PHONY: all all: deploy .PHONY: build build: $(BUILD_DIR)/package.zip # lambda にアップロードするアーカイブを作成します # node_modules だけ含むアーカイブをコピーしてソースファイルを追加します # 変数 $@ や $< で書いたら見辛い気がしたのであえて使ってません $(BUILD_DIR)/package.zip: $(BUILD_DIR)/node_modules.zip $(SRC_FILES) mkdir -p $(BUILD_DIR) cp $(BUILD_DIR)/node_modules.zip $(BUILD_DIR)/package~.zip zip -r $(BUILD_DIR)/package~.zip $(SRC_FILES) mv $(BUILD_DIR)/package~.zip $(BUILD_DIR)/package.zip # node_modules だけ含むアーカイブを作成します # package.json や package-lock.json が更新されたときだけリビルドします # devDependencies は含めたくないので npm ci --prod してアーカイブ化した後に npm i で戻します $(BUILD_DIR)/node_modules.zip: package.json package-lock.json mkdir -p $(BUILD_DIR) npm ci --prod rm -f $(BUILD_DIR)/node_modules.zip zip -r $(BUILD_DIR)/node_modules.zip node_modules npm i # すべての Lambda 関数をデプロイ、次のように展開されます # deploy: deploy/aaa deploy/bbb deploy/ccc .PHONY: deploy deploy: $(TARGETS:%=deploy/%) # 特定の Lambda 関数をデプロイ、次のように展開されます # deploy/aaa: .build/dev/aaa/deploy.json # deploy/bbb: .build/dev/bbb/deploy.json # deploy/ccc: .build/dev/ccc/deploy.json # ただのワイルドカードパターンだといろいろ不都合があったので、 # 静的パターンルールで記述します .PHONY: $(TARGETS:%=deploy/%) $(TARGETS:%=deploy/%): deploy/%: $(BUILD_ENV_DIR)/%/deploy.json # .build/dev/aaa/deploy.json のようなターゲット名から、 # ワイルドカード部分の aaa を取り出して TARGET 変数に入れます $(BUILD_ENV_DIR)/%/deploy.json: TARGET = $* # LAMBDA_aaa などの変数から Lambda 関数名を取得して LAMBDA 変数に入れます $(BUILD_ENV_DIR)/%/deploy.json: LAMBDA = $(LAMBDA_$(TARGET)) # アップロードを実行して成功したら .build/dev/aaa/deploy.json のようなファイルを作成します $(BUILD_ENV_DIR)/%/deploy.json: $(BUILD_DIR)/package.zip mkdir -p $(@D) $(AWS) lambda update-function-code --function-name $(LAMBDA) --zip-file fileb://$< > $@~ mv $@~ $@ # すべての Lambda 関数のログを tail -f します # make -j で並列実行させないと一つのログしか tail -f できません .PHONY: logs logs: $(TARGETS:%=logs/%) .PHONY: $(TARGETS:%=logs/%) # logs/aaa のような名前から TARGET に aaa を入れます logs/%: TARGET = $* # LAMBDA_hoge などの変数から Lambda 関数名を取得して LAMBDA 変数に入れます logs/%: LAMBDA = $(LAMBDA_$(TARGET)) # 特定の Lambda 関数のログを tail -f します(Ctrl+C するまで続行) # ただのワイルドカードパターンだと .PHONY にできないので、 # 静的パターンルールで記述します $(TARGETS:%=logs/%): logs/%: deploy/% $(AWS) logs tail "/aws/lambda/$(LAMBDA)" --follow --format=short --since 1s # tmux でペイン分割して複数のログを tail -f します # Lambda 関数が多くなると見辛いので基本的には↑を使ってます # 動的にコマンドを生成するために Makefile の foreach 関数使ってます .PHONY: tmux-logs tmux-logs: deploy tmux new : \; $(foreach t,$(TARGETS), split make logs/$(t) \; ) set sync \; select-layout main-v .PHONY: clean clean: rm -fr $(BUILD_DIR)
補足とか
Serverless Framework
複数の Lambda 関数のデプロイを管理するなら apex が便利かと思ってたんですけど こういう状況になっているので もう使わないほうが良さそうです。
代替としては Serverless Framework とか? ただこれは CloudFormation を使うようです。今回は自社管理外の AWS 環境にちょこっと Lambda 関数をデプロイするだけのものなのであまり大げさなことはやりたくありませんでした。
Lambda Layer
デプロイの都度 node_modules がまるごとアップロードされているのと、すべての Lambda 関数に同じ node_modules が含まれた状態になっていて無駄なので、node_modules は Lambda layer にして共有するのが良いかも。
さいごに
Makefile 毎回書き方ググってる気がするので未来の自分用にメモ。