GitHub+CodeBuildでLambdaコードとレイヤーを管理する

今回は AWS Lambda 一式の管理を、GitHub と AWS CodeBuild を用いて実装する内容で、コード込みでダラダラと説明する回です。

ぶっちゃけ、この程度の内容はいまや AI を利用すればザックリ全体を構成したり、細かいパーツを組み上げるだけになるので、記事としての価値はだいぶ下がってきたように思いますが、まぁ趣味みたいなものなので気にせず参りましょう。



目的と要点

今回の内容は、目的も実装方法も誰かに必ずしもちょうどフィットするわけではなく、ところどころで様々な選択と手法があると思いますので、1つの事例として捉えつつ各々の環境用に考察と実装をしていただければ、という感じになっています。

まず目的としては、
  • Lambda を Python で動かし、バージョン更新を容易にする
  • Lambda コードは GitHub プライベート・リポジトリで管理する
  • Lambda は ARM64 で動かす

これに対して要点としては、
  • GitHub Actions で pipパッケージやライブラリのファイルを自動的にレイヤー保存する
  • GitHubプライベートリポジトリは Actions を ARM64 で動かせないので Self-Hosted Runnner として CodeBuild を使う
  • OSや各バージョンを統一する

Lambda を ARM64 で動かす方が安価な利点があるとはいえ、そもそもそこまで額面的には高くならない箇所ではあるので、X86 で十分だとか、パブリック・リポジトリで ARM64 を使える、というのであれば CodeBuild 不要で GitHub Hosted Runner で動かすことになります。

こういう細かい選択は好みやセンスによるところですが、私としては最初に構築してしまえば今後ずっと利点があるならそうすることが多いってのと、Self-Hosted Runner という武器をきっちり仕上げておいた方が別のシステムでも役立つだろう、という観点から選択しています。


アーキテクチャ説明

絵を書くのが面倒なので、箇条書きで失礼しやす。

1) GitHub リポジトリの main にマージされる
2) GitHub Actions の処理が CodeBuild で稼働する
3) CodeBuild からメインコードとレイヤーの zip を S3 にアップロード
  ※レイヤー用の処理は GitHub の環境変数で ON/OFF 切り替え可能
4) Terraform で Lambda を更新して完了

これ自体は特に難しくもないのですが、自動化するにあたって選択肢がいくつかあるので、条件の整理や動作確認を丁寧にやっていきましょう、という感じで以下に続きます。


環境条件の整理

本記事の目的の1つである Lambda Python のバージョン更新について、現在は最新が python3.13 で、3.9 が 2025/12/15 に EoL となっています。だいたい3年間隔での更新を必要とすると考えておけばよいでしょう。

そして OS は同ページより、3.12 以降は Amazon Linux 2023 となっているのがわかります。

次に ARM64 について、GitHub Hosted Runner はプライベート・リポジトリにおいて、Linux ARM64 に非対応であることが書かれています。基本的にインストールの成果物(今回は Lambdaレイヤー)は、CPUアーキテクチャが異なると動かないと考えておいたほうがよく、この時点で Self-Hosted Runner に選択肢が絞られます。

Self-Hosted の選択肢はいくつかありますが、ここではベターな CodeBuild とします。コンピューティングのモードには EC2 と Lambda がありますが、インストール処理を含むため /tmp にしか書き込み権限がない Lambda で無理やりなんとかしようとする理由は少ないため、EC2 を選択します。

Amazon Linux 2023 の標準パッケージにおいて、現時点では python3.12 が最新となるのでこれを Lambda でも使用することに決定します。別に普通にソースからインストールすれば python3.13 も利用可能ですが、自分の昔からのポリシーとして『可能な限り既存パッケージでやりくりする』というものがあるためです。

フォーラムなどでは 2023 に python3.13 パッケージが用意されていないことも話されていますが、私の要件的には EoL が主であって機能面に細かい要求はないので、多少 EoL まで短くとも更新を容易にできる仕組みにしておけば、たいした問題ではありません。


ローカルで動作確認

さて、作業順的にいったん本題から少々ズレますが、動かしたいコードの動作確認を手元で行っていきます。

どんなシステムでもそうですが、システムの挙動や確立までを確認するのは簡易的な場所で行うべきで、クラウド上など更新に手間と時間のかかる場所を主軸にしたり、毎回全体を通すような確認をするのはナンセンスです。動作内容を確定・確信すべきパーツを判断し、そこは手早く仕上げ、それ以外は実際の環境上で解決したり調整することで、全体の時間を短縮しつつ品質精度も向上できるからです。

Python と pip

まずは手元で Docker を起動します。


作業に必要なコマンドを適当に入れつつ、Python3.12 と pip で必要なパッケージを入れたのちに、pip で必要パッケージを作業場に入れます。


今回は最新の boto3 と、Aurora や Valkey への接続用に mysqlclient , redis を入れました。mysqlclient のインストールにはライブラリファイルが必要なので、先に mariadb-connector-c-devel を入れています。

MySQL

インストール物は Python バージョンや CPUアーキテクチャが関与する場合があり、mysqlclient がその1つです。出来上がるファイル名にそれらの情報が含まれているのがわかります。
  • python/MySQLdb/_mysql.cpython-312-x86_64-linux-gnu.so
  • 環境が合わないところで import MySQLdb すると ModuleNotFoundError: No module named ‘MySQLdb’

また、ライブラリファイルはこの辺に配置されており、これは pip インストール時だけでなく使用時も必要なので、Lambdaレイヤーに含めて保存することになります。


これがないと、以下のようなエラーになります。


Lambda 上でこのファイルを保持しつつ動作することを確認するために、ライブラリファイルの実体・シンボリックリンクをそのままコピーしつつ、元となる mariadb-connector-c-devel を削除して接続しようとするところまでを確認します。


ここまででも十分ですが、本当に接続できるか確認する場合は、MySQLサーバーをおざなりに入れつつクエリを流してみます。


redis

redis はシンプルなので、こちらもサクッとサーバーを入れて動作確認のコードを実行してみるだけです。まずはサーバーを入れて、


Python を実行してみます。


たいていの pip パッケージは、入れておけばそうコードに影響することなく動くでしょ、って感じにはなりますが、重要であったり繊細なモノは確認をしたり、pip インストール時にバージョンを指定して固定せず常に最新になる場合は動作確認した方が良い場合もあると思います。

そういう時に、サッと手元で確認する手段と、確認すべき範囲の判断ができると、より想定外の挙動に悩まされて引っかかるようなことがなくなるので、オススメしたい進行方法の1つです。


Terraform で GitHub 接続と CodeBuild を作成

ここからは実際のリソース作成で、AWS から GitHub への接続と、Self-Hosted Runner として処理を担当する CodeBuild もろもろを作成します。

CodeConnections

Codeシリーズの管理画面にて、サイドバーの “設定” → “接続” によって、GitHub との接続を作成します。


これだけだと、まだ接続は利用できないので、対象となるリポジトリの管理者である GitHub ユーザーでログインした状態にし、AWS管理画面で接続を承認する作業を行います。ステータスが「利用可能」になれば準備 OK です。

GitHub の方では、Settings → GitHub Apps に「AWS Connector for GitHub」があるので、そこで許可状況について確認することができます。

IAM Role

CodeBuild に割り当てる Role を作成します。CodeConnections は GetConnection, GetConnectionToken あたりを必要としますが、ここでは * にしておきます。あとは S3 に zip を保存する権限と、Logs でもあれば十分です。


CodeBuild

GitHub Actions を実行するコンピューティングとなる CodeBuild 一式を作成します。

GitHub の workflow ファイルの処理がこちらで実行されることになりますが、workflow の設定で buildspec-override:true にすることで、buildspec.yml を処理内容とすることもできるので、なんか良い感じの事情があったら切り替えてもよいでしょう。

あと当然の違いとして、GitHub Hosted Runner から AWS リソースを操作するには、今なら OIDC(OpenID Connect)を使って認証するのが基本ですが、CodeBuild は AWS リソースなので操作権限は IAM Role に委ねられます。


作成すると、GitHub から AWS に対して Webhook が実行されて、処理内容や結果のやり取りが行われることになります。

GitHub 管理画面の Settings → Webhooks にそれが表示され、その中の “Recent Deliveries” にはリクエストやレスポンスの履歴がキッチリ残っているので、もし上手く動作しない場合はログを頼りに調整していきます。


GitHub に Lambdaコードと Actions を作成

Lambda コード用のリポジトリはもう存在するとして、適当な内容のプログラムファイルと、
Actions 用の .github/workflows/build_lambda.yml (ファイル名は任意)を作成します。

Lambda コード

これは人それぞれの部分なので省略します。動作確認用としては test.py でも作って、レイヤーの import や必要なら Aurora への接続などを書けばいいと思います。

Actions ファイル

分割すると見づらいかもなので、まとめて記載しつつコメントを中に書いていきます。



環境変数

先に gh コマンドをインストールしておきます。

管理画面でも登録できますが、コマンドでちゃちゃっとやっちゃいましょう。



動作確認

これで必要なものは揃いましたが、実際にはチマチマ確認しながら進めたほうがわかりやすいし確実です。

CodeBuild の起動

AWS 側で CodeConnections + CodeBuild 一式 があって、GitHub Actions での runs-on の指定が正しければ、webhook が叩かれて CodeBuild が起動するはずです。

最初は Actions の step は簡素なものにして、push や merge などの条件を満たした時に webhook が叩かれるログと、AWS 側でも CodeBuild が起動していることを確認するのがよいです。これらの処理のログは GitHub の webhook にちゃんと残り、CodeBuild 側にはあまりヒントは残らないと思われます。

また、ついでに step の仮処理において、printenv などを出力して確認しておくと、完了通知メッセージあたりで使えそうな CodeBuild としての環境変数が見つかるかもなので、見ておくとよいです。

zip 保存

Lambda のメインコードの zip 化は、処理としてはミスりようがないので、実行後は保存された S3 を確認します。レイヤーの zip は環境変数を on にしていれば作成され、S3 に保存されています。

今回の場合の保存先は
  • s3://${{ env.UPLOAD_BUCKET }}${{ env.UPLOAD_PREFIX }}/python3.12/main.zip
  • s3://${{ env.UPLOAD_BUCKET }}${{ env.UPLOAD_PREFIX }}/python3.12/arm64/layer.zip
となります。

メインコードは CPU アーキテクチャに関係なく、レイヤーは関係あるのでこうしました。もしメインコードも内容の更新ごとに分けたかったりしたら、パスのルールを変えればいいだけです。

Lambda 更新

あとは、Terraform なりで管理されているだろう Lambda を更新して実際に動かしてみて完了です。

aws_lambda_layer_version と aws_lambda_function の、s3_bucket, s3_key, runtime あたりを更新することで、作成済みの任意の zip に切り替えます。

切り替えると言っても、通常は CPU アーキテクチャはそう変えないので、例えば Amazon Linux 2023 に Python3.13 パッケージができたら、環境変数 PYTHON_VERSION を更新して Actions を実行し、Lambda の zip 指定も変更する。くらいの運用を想定しています。


おわりに

今回の仕組みは、全体の自動化と、環境変数による条件の変更を主な目的としました。

ほぼ必要ないとはいえ、CPUアーキテクチャの指定も気軽にできるようにしたかったのですが、CodeBuild 実行時の上書き設定には CPU アーキテクチャは含まれないので、変更したい場合は Terraform をちょっと書き換えて CodeBuild を作り直す必要があります。


CodeCommit が使えた頃は、外からミラーリングして CodePipeline を発動して── のようなこともしていましたが、亡くなってしまったので GitHub と直にこういうことをデキるようにしておいた方が便利ですよねってのと、

環境条件を丁寧に整理すること、動作確認を丁寧に手早くすること、あたりをキッチリできるようにしていきたいですねって感じの回でした:-)