🎯

Lambdaのビルドとデプロイをコマンド一発で!?AWSのIaCツール「Rain」のイケてる新機能

2024/12/01に公開

この記事は、GENDA Advent Calendar 2024 1日目の記事です。
https://qiita.com/advent-calendar/2024/genda

GENDAのテック組織の多彩なメンバーがお送りする25日間、どんな記事が登場するのか私自身も楽しみにしております!

はじめに

みなさん、AWSのIaCツールは何を使われていますか?
私はRainを愛用しています。
CloudFormationのyamlテンプレートをそのまま扱えるためCloudFormationに慣れていれば馴染みやすく、さらに独自の記法でテンプレートを拡張してより便利に使うことができるのが魅力です。

Rainの使い方については本記事では詳しく説明しません。
使い方を知りたい方は、クラスメソッドさんの記事に要点がまとまっているのでぜひご覧ください。
https://dev.classmethod.jp/articles/aws-cloudformation-rain/

Rainの独自記法一覧(v1.19.0現在)

Rainでは、yamlテンプレートの中で !Rain:: で始まる記法を利用できます。
これらは rain pkg または rain deploy 実行時に、CloudFormationが解釈できる形式に展開されます。

公式README に各機能の分かりやすい例が載っているため、気になる機能がありましたらあわせてご覧ください。

文字列やテンプレートを再利用できる機能

  • !Rain::Embed
    • ローカルファイルから文字列を読み取れる
  • !Rain::Include
    • ローカルファイルからyamlを読み取れる
  • !Rain::Env
    • 環境変数から文字列を読み取れる
  • !Rain::Constant
    • パラメータや他のConstantを参照して定数を定義し、テンプレート内で利用できる
  • !Rain::Module (実験的)
    • リソースの Type として他のテンプレートを呼び出せる
    • オブジェクト保存用のS3バケットとログ出力用のS3バケット、といった定番の組み合わせを1つのTypeとして扱える

ローカルファイルをS3にアップロードする機能

  • !Rain::S3Http
    • ローカルファイルをS3にアップロードし https:// で始まるS3 URLに変換する
  • !Rain::S3
    • ローカルファイルをS3にアップロードし s3:// で始まるS3 URIに変換する
    • この記事の主役

その他

  • Metadata commands (実験的)
    • S3バケットのデプロイ前後にコマンドを実行したり、作成したバケットにファイルを置いたりできる

本記事では、新機能が追加された !Rain::S3 に焦点を当てて紹介していきます。

!Rain::S3 の基本

本題に入る前に、 !Rain::S3 の基本的な挙動を紹介しておきます。

リソースをS3にアップロードし、それをテンプレートに反映したうえで変更セットを作成します。CloudFormationでいう aws cloudformation package と同様の機能です。

以下の例は公式READMEからの引用です。
テンプレートに !Rain::S3 {filename} と記載して……

Resources:
  Test:
    Type: A::B::C
    Properties:
      TheS3URI: !Rain::S3 s3.txt

rain pkg または rain deploy を実行します。
すると、ローカルファイルをS3にアップロードした上で、以下のようにS3のパスに変換してくれます。
rain pkg ならアップロードと変換のみ、 rain deploy ならこのまま変更セットの作成まで進んでくれます。

Resources:
  Test:
    Type: A::B::C
    Properties:
      TheS3URI: s3://rain-artifacts-755952356119-us-east-1/a84b588aa54068ed4b027b6e06e5e0bb283f83cf0d5a6720002d36af2225dfc3 

rain-artifacts-{accountId}-{region} はrainコマンドの初回実行時に作られるバケットです。 !Rain::S3Http または !Rain::S3 で指定したファイルはこのバケットにアップロードされます。

!Rain::S3 に追加された Run オプション

さて、ようやく本題です。
今回ご紹介したいのは、v1.16.0で新しく追加された Run オプションです。

Run でシェルスクリプトの場所を指定すると、S3にアップロードする前にそのシェルスクリプトを実行してくれます。
つまり、ビルド用のシェルスクリプトを用意しておけば rain deploy だけでLambda関数のビルドとデプロイが一気にできるということです。
デプロイ前にビルドコマンドを個別に実行する必要はありません!

まずは、変換前のテンプレートを作成します。
Path にはLambda関数のルートとなるディレクトリを、 Run にはシェルスクリプトのファイル名を、テンプレートから見た相対パスで書きます。

変換前
Resources:
  LambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code: !Rain::S3
        Path: './lambda/func1/package'
        Zip: true
        BucketProperty: 'S3Bucket'
        KeyProperty: 'S3Key'
        Run: './lambda/func1/build.sh'
      Handler: 'function.lambda_handler'
# 以下略

次に、ビルドスクリプトを作成します。
コマンドの実行場所はテンプレートのあるディレクトリです。必要に応じて cd しましょう。
この例では、必要なファイルを lambda/func1/package に集約しています。

./lambda/func1/build.sh
#!/bin/bash
set -e

cd `dirname $0` # このshファイルがあるディレクトリ(=lambda/func1/)に移動
mkdir -p package
pip install --target=package example1 example2
cp function.py package/

この状態で rain pkg または rain deploy を実行すると、 build.sh が実行されて lambda/func1/package の内容は以下のようになり……

lambda/func1/package
├── __pycache__
├── bin
├── example1
├── example2
└── function.py

lambda/func1/package 以下がzipで固められてS3にアップロードされ、テンプレートがCloudFormationで解釈できる形式に変換されます。

Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: rain-artifacts-{accountId}-{region}
        S3Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

前述の通り、 rain deploy を実行すると変更セットの作成まで進んでくれるので、あとはプレビューを確認して y を押せばLambda関数が作成できます!

今後の展望

今回紹介した実装には少し残課題があります。
このビルドスクリプトでは、ソースに変更がなくても lambda/func1/package 以下のファイルのタイムスタンプが変わってしまうため、 rain deploy を実行するたび毎回Lambda関数が更新されてしまいます。
そのため、同じテンプレート内の他のリソースを変更しただけでもLambda関数が書き換わってしまうので、内容に更新がないと分かっていても少しハラハラします。

回避策として、テンプレートをLambda関数と周辺リソースに特化し、なるべく他のリソースを同居させないようにしています。
しかし、この記事を書くにあたって検証したところ、ビルドスクリプト実行後の Path ディレクトリ以下に変更がなければ更新されないことが分かりました。ビルドスクリプト冒頭に「ソースファイルに変更がなければ何もせず終了する」ような判定を追加するとさらに良さそうです。

おわりに

これまで、Lambda関数のIaCとしては AWS SAM を使うか、パッケージインストールが不要なちょっとした処理を ZipFile でyamlの中に書くか、しか経験がありませんでした。
パッケージインストールが必要なLambda関数をRainでどうやって管理しよう、と悩んでいたのですが、これまでと同じデプロイ手順で実行できる方法があってとても助かりました。チームで開発していると、「テンプレートによってデプロイ手順に例外があります」というのは周知しにくいですからね……

この新機能を見つけたときは、ちょうど欲しい機能が実装されたばかりでラッキー!と舞い上がりました。Rainのコントリビューターのみなさん、いつもありがとうございます。

GENDA

Discussion