Bitbucket PipelinesとAWS CodePipelineを連携する!


背景

とある案件でCI/CD パイプラインをAWS CodePipelineで実装することになったのだが、以下の諸々の制約を解消する連携方法を模索することになった。

  • ソース管理にはBitbucketを使用する
  • 作成するパイプラインでは複数のAWSサービス(CodeDeployやSSM Automation、CloudFormation)と連携が必要
  • 一つのBitbucketリポジトリから起動したいパイプラインの種類が複数ある

関連する各種サービスの仕様は以下の通り。

  • CodePiplineのソースとしてはBitbucketはサポートされていない
  • CodePipelineのソースとしてS3はサポートされている
  • CodeBuildはBitbucketと連携可能

ということで、当初はこんな感じの構成を考えていた。

ところが、後になって以下の制約が判明!

  • セキュリティ上Bitbucketは特定のIPアドレスの範囲からしかアクセスできない

AWS CodeBuildとしては以下の仕様があるのでIPアドレスの範囲に追加してもらうことを交渉してみる。

  • CodeBuildはVPC内で実行することも可能

しかし、お客様がCodeBuildからもBitbucketにアクセスできるようにすることに乗り気でなかったのでBitbucketのパイプライン機能に白羽の矢を立てることになる。
いろいろ調べたところBitbucket Pipelinesの機能で色々と出来ることが判明し、一気に問題は解決の方向へ!

これらを踏まえ以下の流れでBitbucketをソースとしたCodePiplineの起動が出来るようになった。

実装の手順

ここでは、実際にBitbucket PipelinesとCodePiplineを構成し連携するための手順を記載する。

CodePiplineの構成

連携用S3バケットの作成

  • S3のコンソールからバケットを作成するをクリック

  • 任意の名前を入力して作成をクリック

  • 作成したバケットを開く

  • プロパティを選択

  • バージョニングを選択

  • バージョニングを有効化を選択して保存

  • 有効化されたことを確認

※CodePipeのソースステージで指定するバケットはバージョニングを有効化している必要がある

CodePipelineの作成

  • CodePiplineのコンソールからパイプラインを作成する

  • 任意のパイプライン名を入力し、サービスロールで新しいサービスロールを選択して次に

  • ソースステージのプロバイダーにAWS S3を選択し、バケットに事前に作成したバケット名を、オブジェクトキーにsource.zipを入力し次に

  • ビルドステージのプロバイダーでAWS CodeBuildを選択し、プロジェクトを作成する

  • 別のウィンドウが開くので、以下の項目を入力してCodePipelineに進む
    ※デフォルトからの変更点のみ

項目名
プロジェクト名 任意の名前
オペレーティングシステム Amazon Linux 2
ランタイム Standard
イメージ aws/codebuild/amazonlinux2-x86_64-standard:3.0
ビルド仕様 ビルドコマンドの挿入

※ビルドコマンドはエディターを開くことで直接編集可能
内容は以下を設定

version: 0.2
phases:
  install:
    runtime-versions:
      python: 3.7
    commands:
      - ls -R

中身はPython 3.7のランタイムをインストールしたコンテナ上でls コマンドを実行するのみ

  • 元の画面のビルドプロジェクト名に入力した値が反映されたことを確認の上次に

  • デプロイステージは今回はスキップするので導入段階をスキップ

  • 表示されるダイアログでもスキップをクリック

  • CodePipeline全体の構成を確認しパイプラインを作成する

  • AWS CodePipelineは作成時に必ず起動するが、この段階ではS3にsource.zipが存在していないため必ずエラーになる

Bitbucket Pipelinesで使用するIAMユーザーの作成

  • IAMのコンソールからユーザーメニューを選択しユーザーを追加

  • 任意のユーザー名を入力し、プログラムによるアクセスを選択し、次のステップ

  • このまま次のステップ

  • このまま次のステップ

  • このユーザーにはアクセス権限がありませんと表示されるが、そのままユーザーの作成

    • ユーザー作成中にはインラインポリシーの作成が出来ないため一度作成を完了している
  • 自動的にアクセスキーIDとシークレットアクセスキーが発行されるのでコピーするかcsvをダウンロードしておく

  • 作成されたユーザーを選択

  • インラインポリシーの追加を選択

  • JSONタブを選択

  • 以下のJSONのResource内のS3のARNのバケット名を先ほど作成したものに変更してからコピペしたらポリシーの確認をクリック
    ※以下の太字部分を変更
    arn:aws:s3:::bitbucket-sourcecodeとarn:aws:s3:::bitbucket-sourcecode/*

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::bitbucket-sourcecode",
                "arn:aws:s3:::bitbucket-sourcecode/*"
            ]
        }
    ]
}

  • 任意のポリシー名を入力しポリシーの作成

Bitbucket Pipelinesの構成

Bitbucket リポジトリの作成

  • Bitbucketにアクセスしリポジトリメニューからリポジトリを作成

  • 任意のプロジェクト名とリポジトリ名を入力しリポジトリの作成

Pipelinesの有効化

  • Pipelinesメニューを選択

  • 下部に今回のPipelineで使用する言語テンプレートの選択箇所があるので今回はPythonを選択

  • 言語を選択した時点で自動的にエディターが開き、Pipelineの構成ファイルの初期テンプレートを設定してくれる

  • 初期の状態だと起動トリガー(ブランチやタグ)を指定していない状態になっている

image: python:3.7.3

pipelines:
  default:
    - step:
        caches:
          - pip
        script: # Modify the commands below to build your repository.
          - pip install -r requirements.txt
  • 起動トリガーをmasterブランチに変更があった場合のみにするため以下のように編集する
image: python:3.7.3

pipelines:
  branches:
    master:
      - step:
          caches:
            - pip
          script: # Modify the commands below to build your repository.
            - pip install -r requirements.txt
  • さらにエディターの右ペインに使用できるPipeが表示されるので、検索用のテキストエリアにs3を入力し、AWS S3 DeployPipeを表示する
  • Pipe名の横のコピーアイコンをクリックするとSnippetがコピーできる

  • コピーされるSnippetは以下の通り

- pipe: atlassian/aws-s3-deploy:0.4.4
  variables:
    AWS_ACCESS_KEY_ID: '<string>' # Optional if already defined in the context.
    AWS_SECRET_ACCESS_KEY: '<string>' # Optional if already defined in the context.
    AWS_DEFAULT_REGION: '<string>' # Optional if already defined in the context.
    S3_BUCKET: '<string>'
    LOCAL_PATH: '<string>'
    # CONTENT_ENCODING: '<string>' # Optional.
    # ACL: '<string>' # Optional.
    # STORAGE_CLASS: '<string>' # Optional.
    # CACHE_CONTROL: '<string>' # Optional.
    # EXPIRES: '<timestamp>' # Optional.
    # DELETE_FLAG: '<boolean>' # Optional.
    # EXTRA_ARGS: '<string>' # Optional.
    # DEBUG: '<boolean>' # Optional.

  • このSnippetも参考にしつつ設定した最終的な実装は以下の通り
  • この内容でCommit fileをクリックする
# This is a sample build configuration for Python.
# Check our guides at https://confluence.atlassian.com/x/x4UWN for more examples.
# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: python:3.7.3

pipelines:
  branches:
    master:
      - step:
          caches:
            - pip
          script: # Modify the commands below to build your repository.
            - apt update && apt install -y zip
            - zip -r source.zip * -x "./.git/*"
            - mkdir to_s3
            - cp source.zip ./to_s3
            - pipe: atlassian/aws-s3-deploy:0.4.4
              variables:
                AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID # Optional if already defined in the context.
                AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY # Optional if already defined in the context.
                AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION # Optional if already defined in the context.
                S3_BUCKET: 'bitbucket-sourcecode'
                LOCAL_PATH: './to_s3'
39c4-4b67ef4143a0.png)
  • 処理は以下の通り

    • zipコマンドのインストール
    • .gitディレクトリ以外の内容をsource.zipに圧縮
    • to_s3ディレクトリにsource.zipをコピー
    • aws-s3-deployのパイプを以下の変数で実行
      • AWS_ACCESS_KEY_ID:環境変数AWS_ACCESS_KEY_IDから取得
      • AWS_SECRET_ACCESS_KEY:環境変数AWS_SECRET_ACCESS_KEYから取得
      • AWS_DEFAULT_REGION:環境変数AWS_DEFAULT_REGIONから取得
      • S3_BUCKET:前の手順で作成したS3バケット名
      • LOCAL_PATH: source.zipをコピーしたto_s3ディレクトリを指定
  • 設定した内容がbitbucket-pipelines.ymlとしてmasterブランチに登録される

  • masterブランチへの変更があったとみなされすぐに初回のPipelineが起動するが環境変数の設定をしていないため初回は以下のように失敗する

環境変数へAWS認証情報を設定

  • Repository settingsメニューを選択し、PIPELINESセクションのRepository variablesを選択

  • 以下の環境変数を作成する

    • securedをチェックすると値をmaskして保管できるのでシークレットアクセスキーはmaskすると良い
変数名
AWS_ACCESS_KEY_ID IAMユーザーの作成時にコピーした値
AWS_SECRET_ACCESS_KEY IAMユーザーの作成時にコピーした値
AWS_DEFAULT_REGION ap-northeast-1

実行

masterブランチへの変更

  • Bitbucketのリポジトリのソースメニューを選択し、右上のリポジトリのドロップダウンメニューからファイルを追加を選択

  • リポジトリ直下に任意のファイルを作成しコミットする

  • コミットのコメントの内容を確認しコミットする

  • masterへの変更によりPipelineが起動しているはずなのでPipelinesメニューを選択

  • #2として今コミットした内容でパイプラインが起動しているのでStatusのIn Progressを選択し、Pipelineを開く

  • S3へのアップロードが完了するとPipelineのステータスがSuccessになるのでそれまで待機

  • Bitbucket Pipelineが完了するとAWS CodePipelineが起動しているはずなのでCodePipelineのコンソールを開くと初回は失敗していたSourceステージをクリアして先に進んだことが分かる

  • Buildステージの詳細をクリックするとCodeBuildのログが参照できる

  • buildspecで仕込んだlsコマンドでbitbucketのファイルが連携できていることが確認できた

所感

  • Good
    • 環境に依存する設定ファイルの配布などをBitbucket側で吸収しておくことで、AWS CodePipelineは環境に依存する処理が不要になった
    • パイプラインのトリガーをbitbucket-pipelines.yamlのみで管理できるため見通しが良い
    • CodeBuildでBitbucketのファイルを取得する構成にする場合はパイプラインの数分CodeBuildのプロジェクトが必要なので、作成するリソースを少なくすることが出来た
  • Bad
    • 時々BitbucketのIncidentによりWebhookが停止してしまい、Bitbucket Pipelineが起動しないことあるため運が悪いとデプロイが出来ない時間が発生する
    • 契約しているプランごとに使用できる時間に以下の制約がある
      • Premiumでも制約あり