こんにちは。AS部DS1課の兼安です。
先週末、夏の思い出に山陰の皆生温泉のマリンアスレチックに行きました。45 分の予約制で、着実に楽しく遊べる良いアトラクションでした。・・・嘘です楽しい通り越してやられました。ペース配分を間違えて、30 分ぐらいでバテました。残り 15 分が長かったです。マリンアスレチックはいろいろなところでやっているので、興味持たれた方はペース配分を考慮して行かれるとよいと思います。
- イントロダクション
- CodeBuild と Alembic によるマイグレーションの流れ
- CodePipeline と CodeBuild の設定
- 検証ソースの構成
- buildspec.yml の設定
- DB マイグレーションを使う理由
- 最後に
イントロダクション
本記事では、GitHub リポジトリへのプッシュをトリガーに、CodeBuild で Alembic を用いた DB マイグレーションを行います。これ自体は CodeCommit や、GitHubActions を組み合わせても実現可能と思いますが、今回は GitHub + CodePipeline + CodeBuild で構成しています。
本記事のソースはこちらの記事をベースにしています。
なお、Python のバージョンは、3.11 に変えています。
blog.serverworks.co.jp
本記事の検証ソース一式はこちらのリポジトリにあります。 github.com
CodeBuild と Alembic によるマイグレーションの流れ
今回は CodeBuild までで、CodeDeploy の方は使用していません。
CodeBuild は RDS に対して操作を行うので、VPC の中に入れています。
CodePipeline と CodeBuild の設定
CodePipelineの全体像はこちら。
ソースの設定はこちらです。プロバイダーに GitHub を選択しています。GitHub は、今はGitHub (バージョン 2)
の方を使うのが推奨のようですが、こちらについては別の機会で触れようと思います。
CodeBuild は、VPC の設定をしています。VPC の設定は、CodeBuild の設定画面の「追加の設定」から行います。RDS 側のセキュリティグループは、CodeBuild のセキュリティグループから通信可能にするのを忘れないようにしてください。
なお、今回の構成では、VPC に NAT ゲートウェイと Elastic IP を用意しています。これは、VPC の中に置いた CodeBuild が S3 に置いたファイルを取得するのに、固定 IP が必要だったからです。ここは、NAT ゲートウェイではなく、エンドポイントでも良いと思うので、ご使用の状況に合わせてください。
CodeBuild に与えているロールのポリシーは以下の通りです。DB 接続情報のやり取りにパラメータストアを用いているので(後述)、そのポリシーを付与しています。
{ "Version": "2012-10-17", "Statement": [ { "Action": ["s3:*", "kms:Decrypt", "kms:DescribeKey", "ssm:GetParameter"], "Resource": "*", "Effect": "Allow" } ] }
その他の設定はデフォルトです。CodeBuildのパラメータそのものは、こちらの記事で詳しく触れています。 blog.serverworks.co.jp
検証ソースの構成
本ソースでは、モデルファイルを app フォルダの下に置いています。
python-alembic/ ├── alembic/ │ ├── env.py │ └── (Alembic に関連する他のファイルやディレクトリ) ├── alembic.ini ├── buildspec.yml ├── poetry.lock ├── pyproject.toml ├── app/ │ ├── __init__.py │ ├── models/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── address.py │ │ └── user.py └── __pycache__
pyproject.toml の packages 設定もそれに合わせています。
[tool.poetry] name = "python-alembic" version = "0.1.0" description = "" authors = ["Satoshi Kaneaysu <satoshi.kaneaysu@serverworks.co.jp>"] readme = "README.md" packages = [ {include = "app/**/*"}, # app フォルダの下にあるファイルを認識させます ]
alembic.ini と alembic フォルダ
alembic.ini は、Alembic でマイグレーションを実行する際の設定ファイルです。そして、alembic フォルダには Alemic の動作を決める env.py などがあります。
どちらも、alembic init
コマンドで自動性生成されるファイルです。なお、alembic フォルダはこのフォルダ名でないとダメではありません。ここのフォルダ名はalembic init
で初期設定する時に指定可能です。
DB接続情報の指定
alembic.ini には、DB 接続情報があります。ここを書き換えることで Alembic は DB 操作をできるようになります。
sqlalchemy.url = driver://user:pass@localhost/dbname
ですが、ここに DB 接続情報をベタで書きすることはセキュリティ上よろしくありません。一方、alembic フォルダの env.py は、alembic.ini の sqlalchemy.url
パラメータを書き換えることができます。これを利用して、env.py の先頭に以下のように書きます。
import boto3 import json # パラメータストアから値を取得 ssm = boto3.client('ssm') response = ssm.get_parameter(Name='MyAppDBConnection', WithDecryption=True) db_connection_dict = json.loads(response['Parameter']['Value']) print(db_connection_dict) db_host = db_connection_dict['db_host'] db_port = db_connection_dict['db_port'] db_user = db_connection_dict['db_username'] db_pass = db_connection_dict['db_password'] db_schema = db_connection_dict['db_schema'] db_connection_string = f"postgresql://{db_user}:{db_pass}@{db_host}:{db_port}/{db_schema}" # `alembic.ini`の`sqlalchemy.url`をパラメータストアから取得した値に置き換える config = context.config config.set_main_option('sqlalchemy.url', db_connection_string)
パラメータストアにMyAppDBConnection
という名前で DB 接続情報を格納しておき、env.py でそれを取得する方式です。
モデルの基底クラスの指定
Alembic は、env.py の指定で Base クラスを親に持つクラスをモデルと認識しています。
target_metadata = Base.metadata # モデルの基底クラスのメタデータを指定します
従って、env.py の Base クラスのインポートを書き換えておきます。
from app.models.base import Base
session.py について
本ソースのベースにした記事ではsession.py
がありましたが、session.py
は DBからモデルを逆算するsqlacodegen の方で使うファイルなので今回は省略しています。
buildspec.yml の設定
buildspec.yml は、CodeBuild で実行するコマンドを定義するファイルです。 CodeBuild はこのファイルに基づいて、ビルドを実行するので、ここにマイグレーションの下準備と実行を記述します。
version: 0.2 phases: install: runtime-versions: python: 3.11 commands: - pip install poetry pre_build: commands: - poetry install build: commands: - poetry run alembic upgrade head
この buildspec.yml は以下のことを行なっています。
- poetry をインストール
- poetry でライブラリをインストール
- alembic でマイグレーションを実行
alembic upgrade head
コマンド は、alembic.ini で設定したデータベースに対して、app フォルダの下にあるモデルファイルを元にマイグレーションを実行します。
alembic upgrade head
コマンドの実行は、poetry run
で行います。
これは、poetry shell
が対話形式のコマンドなので、CodeBuild では使えないからです。
マイグレーションの対象となるモデルファイルはここにあるファイルです。address.pyとuser.pyにあるクラスは、base.pyのBaseクラスを継承しているので、先述のenv.pyの指定により、モデルと認識されDBマイグレーションが走ります。
│ ├── __init__.py │ ├── models/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── address.py # このファイルを元にマイグレーションを実行 │ │ └── user.py # このファイルを元にマイグレーションを実行
buildspec.yml のもっと詳しい使い方はこちらのページをご覧ください。 docs.aws.amazon.com
CodePipeline で CodeBuild を実行し、CodeBuild で Alembic を実行して DB マイグレーションを行う説明は以上です。ここまでの設定をした後、GitHub にソースをプッシュすればモデルファイルに従い、DB マイグレーションが実行されます。
DBにテーブルがない状態で、検証ソースをプッシュすると、2つのテーブルと1つの管理テーブルができるのを確認できます。
alembic_rds=> \dt List of relations Schema | Name | Type | Owner --------+-----------------+-------+--------- public | address | table | alembic public | alembic_version | table | alembic public | user | table | alembic
DB マイグレーションを使う理由
今回 CodeBuild で DB マイグレーションを実行する例を書きましたが、そもそも DB マイグレーションを使う理由はなのでしょうか?
仮にテーブル定義を SQL を直接実行して変更するとしても、本番リリースの前にリハーサルで手順を確立しておけば、そうそう問題は起きることはないと言えます。
ですが・・・、個人的に本番リリースの時は大なり小なり焦っていてリハーサルの時よりパフォーマンスが落ちていることが多いです。そういう時に、一部だけでも手順が自動化されていると、気持ちのバッファのような効果を感じます。効果が地味だとは思いますが、小さな自動化の積み重ねていけば、デプロイは一大イベントではなくなり、リードタイムの短縮に繋がっていくのではと思います。
最後に
サーバワークスでは、お客様のシステム内製を支援する活動を行なっています。
兼安 聡(執筆記事の一覧)
アプリケーションサービス部 DS3課所属
2024 Japan AWS Top Engineers (Database)
2024 Japan AWS All Certifications Engineers
認定スクラムマスター
広島在住です。今日も明日も修行中です。