背景
サービスを提供している場合、マスタデータの登録ってついてまわりますよね
この作業ってトイルに分類されるものだと思っています
自動化できる部分は自動化したいものです
幸いなことに、AWS環境には、CodePipelineやLambdaと自動化への環境は整えられています
細かい作り込みは必要となりましたが、自動化そのものは実現できましたので、ご参考になりましたら
実装関係
使ったサービスやコマンド
- CodePipeline
- CodeCommit
- CodeBuild
- git
- SSM(ParameterStore)
- S3
- Lambda
CodeCommit
ポイントは1つだけです
出力アーティファクト形式に「完全クローン」を選択してください
CodeBuild
細かい技術の組み合わせとなっています
ポイントとそれに関する説明をしていきます
登録用データファイルの作成
git archive
コマンドでgitで管理しているファイルをzip形式で出力できます
git diff
コマンドで特定のCommit間のファイルを特定できます
この2つのコマンドで、前回CommitとHEADの間のファイルをzipファイルに出力できます
git archive [ブランチ] `git diff --name-only ${PRE_COMMIT} HEAD --diff-filter=AM` -o ${FILENAME} --worktree-attributes
PRE_COMMIT
はSSMのパラメータストアに保持するようにしました
下のコマンドで取得しています
PRE_COMMIT=$(aws ssm get-parameter --name [パラメータストア名] | jq ".Parameter.Value" | sed -e "s/\"//g")
apiの戻り値のJSONからjqでパラメータストアの値を取得し、sedコマンドでダブルコーテーションを除去しています
--worktree-attributes
は、.gitattributesに記載したgit archiveに含めたくないファイルを反映してくれるオプションです
buildspec.yml export-ignore
script/ export-ignore
後は、ファイルをS3に上げて
aws s3 cp ${FILENAME} s3://${S3_BUCKET}/${S3_KEY}
最新のcommit番号をパラメータストアに更新し直して、Codeシリーズのタスクは完了です
なお、--overwrite
オプションがないとエラーになります
- NOW_COMMIT=$(git rev-parse HEAD)
- aws ssm put-parameter --name [パラメータストア名] --value ${NOW_COMMIT} --overwrite
対象のファイルがなかったら
commitするのはデータファイルばかりではないので、登録対象のファイルがない場合もあります
マスタデータの登録を行うLambdaでの対応も出来ますが、無駄にS3にファイルを上げたりLambdaを実行したりするのは嫌だったので、
CodeBuildのbuildspec.ymlにまつわるexitいろいろ を参考に下のshellファイルで処理を止めるようにしました
Dai@FastLabel さん、ありがとうございます
#!/bin/sh
CNT=$(git diff --name-only ${PRE_COMMIT} HEAD --diff-filter=AM -- data | wc -l)
echo $CNT
if [ $CNT -eq 0 ]; then
exit 1
fi
対象となるマスターファイルはdataディレクトリに置く運用になっています
commitされたファイルをdataディレクトリのものに絞って、その件数が0かどうかで判定するようにしました
Lambdaの起動
後は、S3のPUTイベントで、Lambdaを起動して、LambdaでDynamoDBに登録すれば完了です
Lambdaの処理内容は、Qiitaを読むレベルの方には蛇足になりますので、2点だけ述べておきます
エイリアスを付与したLambdaを実行
開発、ステージング、本番と環境を別に設けているケースがほとんどだと思います
環境ごとに同じLambdaを用意するのは無駄なコストになりますので、devやstg、prdといったエイリアスをつけて
環境を識別するようにしました
Stage = context.invoked_function_arn.split(":")[-1]
unzip先はディレクトリ
S3にアップロードしたファイルはzipファイルなので当然解凍しますが、解凍先のパラメータをファイルと思い込んでいまして、無駄に時間を費やしました
下のpythonコードの最後の行のunzip_dirのところですね
同じ轍を踏みませんように
with zipfile.ZipFile(zip_file, "r") as z:
for f_name in z.namelist():
if f_name.endswith(".csv") or f_name.endswith(".json"):
z.extract(f_name, unzip_dir)
終わりに
まだ本格的な運用が始まっていないこと、私がshellをあまり書いたことがないことから、不具合やもっと効率の良い書き方もあると思います
ですが、現時点ではやりたかったことを実現できましたので、Qiitaでシェアすることにしました
皆様のトイル撲滅の一助になりましたら