インフラの奥村です。
とある運用ツールをLambda化した際に、GitLab-CIとTerraformとLambrollを利用してCI/CDの実装を行ったのでその時の知見を共有します。
特に難しい事はしていないですが、Lambda layerを利用したLambdaのGitLab-CIの実装の一例として参考にして頂ければ幸いです。
当記事では、CDについて詳しく記載しております。実際にはCIとしてユニットテストなどを設定するのが良いと思います。
背景
- Lambda関数に更新があった場合、Terraformのリソースと関係なくデプロイしたい。Lambda関数のデプロイにはLambrollを用いる。
- Lambda関数のデプロイのたびにzipモジュールをアップロードするのには時間が掛かるのでレイヤーを利用する。
- レイヤーを利用すれば、Lambda関数が新たに作成されたときに共通のモジュール参照先として利用できる。
使用するもの
- Lambda
- Python 3.7
- Docker Image
- Lambroll
- 0.3.1
- Terraform
- 0.12.28
- Lambroll
- GitLab-CI
- Bashスクリプト
各リソースの更新フロー
基本的に GitLab-Flow でデプロイできるようにしています。
Lambda関数の更新
- ローカルでPythonファイルを更新する
- GitLabへPush
- Gitlab-ciによるunittest(当記事ではスキップ)
- masterブランチへマージ
- masterブランチからproductionブランチへマージリクエスト&マージ
- GitLab-CIより手動実行用のジョブが作成される
- ジョブを手動実行
- Lambrollによるデプロイ
レイヤーの更新(requirements.txtの更新)
- ローカルで
requirements.txt
ファイルを更新する - GitLabへPush
- masterブランチへマージ
- GitLab-CIによるジョブの実行
module_setup_for_terraform.sh
の実行terraform_deploy_user.sh
の実行- terraform planの実行
- masterブランチからproductionブランチへマージリクエスト&マージ
- GitLab-CIにより手動実行用のジョブが作成される
- ジョブを手動実行
module_setup_for_terraform.sh
の実行terraform_deploy_user.sh
の実行- terraform applyの実行
Terraformの更新
- ローカルで
.tf
ファイルを更新する - GitLabへPush
- masterブランチへマージ
- GitLab-CIによるジョブの実行
module_setup_for_terraform.sh
の実行terraform_deploy_user.sh
の実行- terraform planの実行
- masterブランチからproductionブランチへマージリクエスト&マージ
- GitLab-CIにより手動実行用のジョブが作成される
- ジョブを手動実行
module_setup_for_terraform.sh
の実行terraform_deploy_user.sh
の実行- terraform applyの実行
成果物
ディレクトリ構成
├── sample-lambda-function │ ├── __init__.py │ ├── lambda_function.py │ ├── production.json │ ├── README.md │ └── requirements.txt ├── terraform │ └── production │ ├── deploy_user.tfvars │ ├── lambda.tf │ ├── provide.tf │ └── vars.tf └── tool_scripts ├── module_setup_for_terraform.sh ├── set_aws_config.sh └── terraform_deploy_user.sh
Docker Image
TerraformとLambrollのバージョンは私の環境で動作していたバージョンを指定しています。
ビルドしたイメージをGitLab-CIでPullできるレジストリにPushします。(私の部署では GitLab Registryを利用しています。)
FROM alpine AS builder RUN apk update && apk add curl tar && \ curl -sSL https://github.com/fujiwara/lambroll/releases/download/v0.3.1/lambroll_v0.3.1_linux_amd64.tar.gz > lambroll_v0.3.1_linux_amd64.tar.gz && \ tar xf lambroll_v0.3.1_linux_amd64.tar.gz FROM python:3.7 ENV TERRAFORM_VERSION=0.12.28 COPY --from=builder /lambroll_v0.3.1_linux_amd64/lambroll /usr/local/bin/ RUN apt update && apt install zip && \ wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /bin && \ rm -f terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ python -m pip install --upgrade pip && \ python -m pip install awscli boto boto3
.gitlab-ci.yml
Lambda関数が複数作成された場合に別ジョブを簡単に定義できるように、共通化できる部分にはアンカーを用いています。
また、当記事ではCIに関する記述は記載していません。
stages: - pre_deploy - terraform_deploy - lambda_deploy .template_deploy_python_lambda: &template_deploy_python_lambda stage: lambda_deploy image: lambroll-terraform script: - cd $DIRECTORY - lambroll deploy --function="${ENVIRONMENT}.json" deploy_sample_lambda_function_production: <<: *template_deploy_python_lambda variables: DIRECTORY: sample-lambda-function ENVIRONMENT: production only: refs: - production changes: - sample-lambda-function/**/* when: manual .template_terraform_production: &template_terraform_production image: lambroll-terraform before_script: - bash tool_scripts/set_aws_config.sh - bash tool_scripts/module_setup_for_terraform.sh sample-lambda-function production - cd terraform/production - terraform init -backend-config deploy_user.tfvars - cd ../../ terraform_plan_production: stage: pre_deploy <<: *template_terraform_production script: - bash tool_scripts/terraform_deploy_user.sh plan production only: refs: - master changes: - terraform/**/* - sample-lambda-function/requirements.txt terraform_apply_production: stage: terraform_deploy <<: *template_terraform_production script: - bash tool_scripts/terraform_deploy_user.sh apply production only: refs: - production changes: - terraform/**/* - sample-lambda-function/requirements.txt when: manual
Lambroll
Lambda関数に関するリソースは sample-lambda-function
にまとめられております。
Lambda関数本体は今回の記事の対象外なので記載しておりません。
sample-lambda-function
に含める Lambrollの設定ファイルであるJSONファイルは当記事の例では production.json
としております。
{ "FunctionName": "sample-lambda-function", "Handler": "lambda_function.lambda_handler", "MemorySize": 128, "Role": "arn:aws:iam::xxxxxxxxxxxx:role/blog-sample-okumura-role", "Runtime": "python3.7", "Layers": ["arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:layer:sample-lambda-function-layer:1"], "Tags" : { "environment" : "production" } }
Terraform
terraform/production/deploy_user.tfvars
デプロイスクリプトで指定するデプロイユーザーを指定するtfvarsを用意します。
profile = "your_profile" role_arn = ""
terraform/production/lambda.tf
filename
で指定しているzipファイルは、GitLab-CIによってデプロイスクリプトの中で生成されます。
resource "aws_lambda_layer_version" "lambda_layer" { filename = "${var.name}.zip" layer_name = "${var.name}-layer" compatible_runtimes = ["python3.7"] }
terraform/production/vars.tf
name変数をディレクトリ名と一致させる必要があります。
variable profile {} variable role_arn {} variable name { default = "sample-lambda-function" }
デプロイ用Bashスクリプト
tool_scripts/set_aws_config.sh
AWS関連の設定をGitLab-CIのコンテナ内に生成するスクリプトです。
GitLab-CIによって実行されます。
#!/bin/bash mkdir ~/.aws echo "[profile infra_aws_deploy]" >> ~/.aws/config echo "region = ap-northeast-1" >> ~/.aws/config echo "aws_access_key_id = $AWS_ACCESS_KEY_ID" >> ~/.aws/config echo "aws_secret_access_key = $AWS_SECRET_ACCESS_KEY" >> ~/.aws/config
tool_scripts/module_setup_for_terraform.sh
Lambda layerをTerraformでplanもしくはapplyするので、Terraformのコマンドを実行するまえに、Layer用のzipファイルを生成する必要があります。
引数でLambda関数名と、環境を指定します。
当スクリプトは、GitLab-CIのTerraformコマンドの前に実行されます。
#!/bin/bash set -u lambda_dir=$1 environment=$2 python -m pip install -r "${lambda_dir}/requirements.txt" -t ./python zip -r "${lambda_dir}.zip" python mv "${lambda_dir}.zip" terraform/"${environment}" rm -rf ./python
tool_scripts/terraform_deploy_user.sh
Terraformを実行するラッパースクリプトです。
ローカル環境でも実行しやすいようになっています。
#!/bin/bash function plan(){ echo "plan" cd terraform/"$1" terraform plan -var-file deploy_user.tfvars } function apply(){ echo "apply" cd terraform/"$1" terraform apply -var-file deploy_user.tfvars -auto-approve } set -u environment=$2 $1 "${environment}"
まとめ
Lambdaのデプロイで何のソリューションを用いるかは議題に上がる事が多々あると思います。
当記事が方法の悩んでいる人の少しの助けにでもなれば幸いです。
[お知らせ]
アドウェイズエンジニアブログ公式Twitterアカウントの運用を開始しました。
ぜひフォローお願いします! @ADWAYS_ENGINEER