GitLab で管理している AWS SAM プロジェクトの自動デプロイに AWS CodePipeline を使う

GitLab で自動デプロイを実施するには GitLab CI/CD が使えるのですが、今回は AWS CodePipeline (と CodeCommit、CodeBuild) を使って AWS SAM プロジェクトの自動デプロイを構成してみました。

前提 (なぜ GitLab CI/CD を使わないのか)

GitLab CI/CD の実行環境 (Runner) として、GitLab が提供している Shared runner とユーザーが用意する Specific runner があり、この上で Docker コンテナを動かす形になります。

管理が楽な Shared runner + 公式の (提供元が信頼できる) Docker イメージを使いたいところですが、

  • 作成した AWS SAM プロジェクトには Python 以外の言語 (Go 言語) で実装された Lambda 関数がある

そして、

  • AWS SAM CLI の実行には Python ランタイムが必要
  • Go による Lambda 関数のビルドには Go ランタイムが必要

上記の条件により、Docker コンテナ内に Python と Go 両方のランタイムが必要になります。

そのような (公式提供の) Docker イメージはなさそうだったので、

  • Python か Go の公式イメージを使い、CD 中にもう片方をインストールする
  • 両方のランタイムをインストールした自前の Docker イメージを作成して使う

どちらかになりますが、前者は CD の時間が伸びること、後者は自前ビルドの管理の手間が増えるため、できれば避けたいです。

そこで、GitLab と AWS CodePipeline+CodeCommit+CodeBuild の連携による自動デプロイを試すことにしました。

構成

f:id:ozaki-linkode:20210826092116p:plain

GitLab 上のリポジトリを CodeCommit にミラーリングし、CodeBuild でデプロイを実施します。

GitLab への git push で自動デプロイが走るようにするため、 CodePipeline で CodeCommit と CodeBuild を繋げる必要があります。

AWS SAM プロジェクトを作成

プロジェクトを作成し、リポジトリを構成します。

プロジェクト名は aws-sam-sample としました。

sam init --name aws-sam-sample --runtime go1.x --package-type Zip
cd aws-sam-sample
git init

デプロイ設定を samconfig.toml に記述します。

version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "aws-sam-sample"
s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket"
s3_prefix = "aws-sam-sample"
region = "ap-northeast-1"
confirm_changeset = false
capabilities = "CAPABILITY_IAM"

CodeBuild のビルド仕様ファイル buildspec.yml を作成します。

version: 0.2

phases:
  install:
    runtime-versions:
      golang: latest
  build:
    commands:
      - sam build
  post_build:
    commands:
      - sam deploy --debug

コミットします。

git add .
git commit -m"Initial commit"

CodeCommit のリポジトリを作成

CodeCommit にミラーリング用のリポジトリを作成します。

f:id:ozaki-linkode:20210826104251p:plain

以下のリポジトリ URL (HTTPS) が与えられました。

https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/aws-sam-sample

ミラーリング用の AWS ユーザーを作成

GitLab から CodeCommit へ git push できるようにするためのユーザーを作成します。

以下のようなシンプルな権限で十分なので、専用ユーザーにした方が良いです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "codecommit:GitPull",
                "codecommit:GitPush"
            ],
            "Resource": "arn:aws:codecommit:ap-northeast-1:XXXXXXXXXXXX:aws-sam-sample"
        }
    ]
}

作成後、ユーザーの「認証情報」タブの「AWS CodeCommit の HTTPS Git 認証情報」で認証情報を作成します。

f:id:ozaki-linkode:20210826104830p:plain

生成されたユーザー名 (ServiceUserName) とパスワード (ServicePassword) は、次の GitLab の設定で使用します。

GitLab のリポジトリを作成 & ミラーリングの設定

リポジトリを作成し、Settings→Repository→Mirroring repositories に CodeCommit のリポジトリ情報を入力します。

f:id:ozaki-linkode:20210826105025p:plain

「Git repository URL」は CodeCommit リポジトリに前述の ServiceUserName を付与した URL (以下の形式) を、「パスワード」は ServicePassword を指定します。

https://ServiceUserName@git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/aws-sam-sample

f:id:ozaki-linkode:20210826104916p:plain

ソースコードを GitLab にプッシュ

プロジェクトのソースコードを一旦 GitLab にプッシュし、CodeCommit にミラーリングされることを確認します。

CodeBuild のビルドプロジェクトを作成

CodeBuild のビルドプロジェクトを作成します。

f:id:ozaki-linkode:20210826105423p:plain

「ソースプロバイダ」は AWS CodeCommit、「リポジトリ」は CodeCommit のリポジトリを指定します。

f:id:ozaki-linkode:20210826105440p:plain

「環境」については、デフォルトの値を選んでいきます。「サービスロール」は「新しいサービスロール」を選び、後でロールに AWS SAM のデプロイに必要なポリシーをアタッチすることにします。

f:id:ozaki-linkode:20210826084021p:plain

CodeBuild のサービスロールにデプロイ権限を付与

今回はサンプルなので、デプロイに必要なポリシーをロールに直接アタッチしました。

  • IAMFullAccess
  • AmazonS3FullAccess
  • AmazonAPIGatewayAdministrator
  • AWSCloudFormationFullAccess
  • AWSLambda_FullAccess

実運用する際には、CloudFormation が AssumeRole するためのデプロイ用ロールを別途用意し、それに PassRole するポリシーを作成してアタッチする等で権限を制限するのが安全かと思います。

デプロイの動作確認

CodeBuild で「ビルドを開始」を選択し、デプロイが完了するか確認します。

f:id:ozaki-linkode:20210826110118p:plain

CodePipeline のパイプラインを作成

自動デプロイを構成するため、CodePipeline のパイプラインを作成します。

f:id:ozaki-linkode:20210826110354p:plain

「サービスロール」は「新しいサービスロール」で良いです。

ソースステージに CodeCommit のリポジトリを、ビルドステージに CodeBuild のプロジェクトを追加します。

f:id:ozaki-linkode:20210826110410p:plain

f:id:ozaki-linkode:20210826110423p:plain

自動デプロイの動作確認

手元のリポジトリに適当にコミットを追加し、GitLab にプッシュします。

自動でデプロイが走り、完了することを確認します。

f:id:ozaki-linkode:20210826111842p:plain

自動デプロイでは「送信者」が codepipeline になっています。

メリット/デメリット

以上で一通りの流れを確認しました。

GitLab CI/CD と比較した場合のメリットは、

  • ビルド/デプロイ環境を管理する必要がない
  • ビルド/デプロイ処理の自由度が高い
  • GitLab 側に与える AWS へのアクセス権限を最小化できる (CodeCommit の GitPush/Pull のみ)
    • GitLab CI/CD でデプロイする場合はデプロイ用ロールのクレデンシャルを与える必要がある
  • 構成管理が AWS 側に集約できる
    • デプロイログ→CloudWatch

デメリットとしては、

  • CodePipeline+CodeCommit+CodeBuild 分のコストがかかる

余談

sam build には --use-container オプションがあり、コンテナ上でビルドを実行することができます (sam build を実行する環境にビルド用ランタイムが必要ない) 。

そのため、当初は GitLab CI/CD 上で Docker in Docker による方法を検討しましたが、Specific runner が必要であり、結局外部リソースを持つことになります。

(そして --use-container は Go に対応していないため無意味でした)

CodeBuild のビルド/デプロイ環境 (Docker イメージ) を GitLab CI/CD で使えないかとも考えましたが、AWS 外には提供されてないようで、また使えたとしても内容的にかなりサイズが大きいと思われるため、無キャッシュ時のデプロイに時間がかかることが懸念されました。

まとめ

GitLab で管理しているプロジェクトを AWS CodePipeline で自動デプロイする構成を試しました。

金銭的コストは増えますが、最終的なデプロイ先が AWS であるならば、構成の柔軟性、管理の一元化、セキュリティの面から見て価値があると思います。

参考 URL

GitLabとAWS CodePipelineを使ってAWS ECS/FargateのCI/CD | KeisukeYamashita

GitLabリポジトリをCodeCommitリポジトリにミラーリングする | DevelopersIO