Amazon Web Services ブログ

AWS Fargate を使って AWS CodeCommit リポジトリのリージョン間レプリケーションを行う

(この記事は、Replicate AWS CodeCommit Repositories between Regions using AWS Fargate を翻訳したものです。)

このブログでは、サーバーレスアーキテクチャを使用して、ある AWS リージョンから別の AWS リージョンへの AWS CodeCommit リポジトリの継続的なレプリケーションを設定する手順について説明します。CodeCommit は、ソースコードからバイナリまであらゆるものを保存する、フルマネージドでスケーラブルなソース管理サービスです。既存の Git ツールとシームレスに連携し、独自のソース管理システムを運用する必要がなくなります。ある AWS リージョンから別の AWS リージョンへの AWS CodeCommit リポジトリのレプリケーションを行うことで、世界各地の開発者がpullを行う際のレイテンシを低くすることができます。これと同じアプローチを使用して、他のサービス (GitHub や BitBucket など) で現在ホストされているリポジトリを AWS CodeCommit に自動的にバックアップすることもできます。

このソリューションは、継続的なレプリケーションを行うために AWS LambdaAWS Fargate を使用します。このアプローチの利点は次のとおりです。

  • レプリケーションは、リポジトリへのコミットなどのイベントをトリガーに実行されるよう、簡単に設定できます。
  • サーバーレスアーキテクチャを使用することで、サーバーをプロビジョニング、維持、管理する必要がなくなります。
  • このソリューションを独自の DevOps パイプラインに組み込むことができます。詳細については、「AWS CodePipeline のパイプラインで AWS Lambda 関数を呼び出す」を参照してください。

注: AWS Fargate にはストレージに 最大200 GB の制限があります。また、アジアパシフィック (東京) や アジアパシフィック (大阪) を含む数多くのリージョンで利用できますが、ご利用いただけないリージョンもあります。Amazon EC2 インスタンスを使用してスケジュールに従ってリポジトリをレプリケートする同様のソリューションが以前のブログで公開されており、リポジトリがストレージや利用可能なリージョンなどの条件を満たしていない場合に使用できます。
(ブログ訳注:英語版 Blog 執筆時から状況が変わっているため、2021/06 時点の最新状況に合わせて内容を修正しています)

Fargate を使用したレプリケーション

このブログに従って手順を進めることで、次のようなアーキテクチャを構築できます。

AWS CodeCommit リポジトリに変更を加えると、Lambda 関数がトリガーされます。Lambda 関数は、Git コマンドラインツールを使用してリポジトリをレプリケートする Fargate タスクを呼び出します。

ユーザーが東京(ap-northeast-1)リージョンのリポジトリ(レプリケート元)から大阪(ap-northeast-3)リージョンのリポジトリ(レプリケート先)にレプリケートしたいとします。そのための手順を説明します。
(ブログ訳注:英語版 Blog ではバージニア北部リージョンからオレゴンリージョンにレプリケートする内容となっていますが、日本語訳の際に東京リージョンから大阪リージョンへのレプリケートする内容に修正しています。それに合わせて、この後のコマンド例、ソースコード例も修正しています。)

前提条件

レプリケート元とレプリケート先の両方のリポジトリ、IAM CreateRole、AttachRolePolicy、および Amazon ECR 権限に対するアクセス許可を持つ Amazon EC2 の AWS サービスロールを作成します。私が使用した EC2 のサービスロールにアタッチしたポリシーは次のとおりです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "codecommit:*",
                "ecr:*",
                "iam:CreateRole",
                "iam:AttachRolePolicy"
            ],
            "Resource": "*"
        }
    ]
}
  • このソリューションを構築するには、Docker 環境が必要です。EC2 インスタンスを起動して Docker をインストールできます。または、Docker と Git がプリインストールされた AWS Cloud9 を使用できます。私は EC2 インスタンスを使用し、そこにDocker をインストールしました。EC2 インスタンスを作成するときは、前のステップで作成した IAM ロールを使用します。以下の手順では、この環境を「Docker 環境」と呼びます。
  • Docker 環境に AWS CLI をインストールする必要があります。AWS CLI のインストールについては、このページを参照してください。
  • Docker 環境に Git コマンドラインを含め、Git をインストールする必要があります。

ステップ 1: Docker イメージを作成する

Docker イメージを作成するには、まず Dockerfile が必要です。Dockerfile は、皆さんのDocker イメージが使うベースイメージと、そのイメージにインストールしたり実行したりするコマンドなどが記述されたテキストファイルです。Dockerfile の詳細については、Dockerfile Reference にアクセスしてください。

1. Docker 環境でディレクトリを選択し、そのディレクトリで次の手順を実行します。私は /home/ec2-user ディレクトリを使用して、次の手順を実行しました。

2. Docker 環境で AWS CodeCommit リポジトリをクローンします。Docker 環境のターミナルを開き、次のコマンドを実行してソース AWS CodeCommit リポジトリをクローンします(コマンドは /home/ec2-user ディレクトリから実行しました)。

$ git config --global credential.helper '!aws codecommit credential-helper $@' 
$ git config --global credential.UseHttpPath true 
$ git clone --mirror https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/Source LocalRepository
$ cd LocalRepository
$ git remote set-url --push origin https://git-codecommit.ap-northeast-3.amazonaws.com/v1/repos/Destination

注:赤でマークされた URL をレプリケート元およびレプリケート先リポジトリの URL に変更します。

3. Dockerfile という名前 (大文字と小文字が区別されることに注意) のファイルを次の内容で作成します (ここでは /home/ec2-user ディレクトリに作成しました)。

# Pull the Amazon Linux latest base image
FROM amazonlinux:latest

#Install aws-cli and git command line tools
RUN yum -y install unzip aws-cli
RUN yum -y install git

WORKDIR /home/ec2-user
RUN mkdir LocalRepository

WORKDIR /home/ec2-user/LocalRepository

#Copy Cloned CodeCommit repository to Docker container
COPY ./LocalRepository /home/ec2-user/LocalRepository
#Copy shell script that does the replication

COPY ./repl_repository.bash /home/ec2-user/LocalRepository

RUN chmod ugo+rwx /home/ec2-user/LocalRepository/repl_repository.bash
WORKDIR /home/ec2-user/LocalRepository

#Call this script when Docker starts the container 
ENTRYPOINT ["/home/ec2-user/LocalRepository/repl_repository.bash"]

4. 次のシェルスクリプトを、repl_repository.bash というファイル名で作成し、Docker 環境の DockerFile と同じディレクトリに配置します (ここでは /home/ec2-user ディレクトリに作成しました)。

#!/bin/bash

git config --global credential.helper '!aws codecommit credential-helper $@'
git config --global credential.UseHttpPath true

git fetch -p origin
git push --mirror

5. 上記の手順を完了すると、ディレクトリ構造は次のようになります。

-rw-r--r--   1      0    Apr  3 13:21 Dockerfile
drwxr-xr-x   2     68    Apr  3 13:21 LocalRepository
-rw-r--r--   1      0    Apr  3 13:21 repl_repository.bash

6. LocalRepository ディレクトリから repl_repository.bash スクリプトを実行して、レプリケーションが機能しているかどうかを確認します。LocalRepository ディレクトリに移動し、次のコマンドを実行します。. ../repl_repository.bash

コマンドが成功すると、最終行に「Everything up-to-date」という結果が表示されます。

$ . ../repl_repository.bash
Everything up-to-date

ステップ 2: Docker イメージをビルドする

1. 前の手順で Docker 環境で DockerFile を作成したディレクトリから次のコマンドを実行して、Docker イメージを作成します (ここでは /home/ec2-user ディレクトリから実行しました)。

$ docker build -t ccrepl .

実行した際に出力された内容:ステップ 1 から 3 の一部として、Dockerfile からさまざまなパッケージをインストールし、環境変数を設定します。Dockerfile のステップ 4 から 11 では、次のような内容が出力されます。

Step 4/11 : WORKDIR /home/ec2-user
---> 37a5113bd2ce
Removing intermediate container ed89954b9a4f
Step 5/11 : RUN mkdir LocalRepository
---> Running in b9e4fab2b264
---> 816b553261c9
Removing intermediate container b9e4fab2b264
Step 6/11 : WORKDIR /home/ec2-user
---> 60ad564a70be
Removing intermediate container 0897cfb7dd5d
Step 7/11 : COPY ./LocalRepository/ /home/ec2-user/LocalRepository/
---> 03420e4e7d0a
Step 8/11 : COPY ./repl_repository.bash /home/ec2-user/LocalRepository
---> 7eea3f045f38
Step 9/11 : RUN chmod ugo+rwx /home/ec2-user/LocalRepository/repl_repository.bash
---> Running in 7ef77a0ad886
---> 2ff00242c190
Removing intermediate container 7ef77a0ad886
Step 10/11 : WORKDIR /home/ec2-user/LocalRepository
---> 41f2aa473cf8
Removing intermediate container 9f233487943b
Step 11/11 : ENTRYPOINT /home/ec2-user/LocalRepository/repl_repository.bash
---> Running in 0aaff1cc2e29
---> 2066cf6a9c7d
Removing intermediate container 0aaff1cc2e29
Successfully built 2066cf6a9c7d
Successfully tagged ccrepl:latest

2. 次のコマンドを実行して、イメージが正常に作成されたことを確認します。成功すると最後に「Everything up-to-date」と表示されます。

[ec2-user@ip-172-1-1-210 LocalRepository]$ docker run ccrepl
Everything up-to-date

ステップ 3: Docker イメージを Amazon Elastic Container Registry (ECR) にプッシュする

Docker 環境で次の手順を実行します。

1. AWS CLI configure コマンドを実行し、レプリケート元リポジトリのリージョンをデフォルトリージョンをとして設定します (私は ap-northeast-1 を使用しました)。

$ aws configure set default.region <Source Repository Region>

2. このコマンドを使用してステップ2 で作成した ccrepl イメージを登録するための Amazon ECR リポジトリを作成します (出力された repositoryUri をメモしておいてください)。

$ aws ecr create-repository --repository-name ccrepl

出力結果:

3. 次のコマンドを使用して、前のステップの repositoryUri 値で ccrepl イメージにタグを付けます。

$ docker tag ccrepl:latest <your account id>.dkr.ecr.ap-northeast-1.amazonaws.com/ccrepl

4. 次のコマンドを使用して、Amazon ECR リポジトリにログインを行います。

$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <your account id>.dkr.ecr.ap-northeast-1.amazonaws.com

5. 次のコマンドを使用して、ステップ 2 の repositoryUri を使用して Docker イメージを Amazon ECR にプッシュします。

$ docker push <your account id>.dkr.ecr.ap-northeast-1.amazonaws.com/ccrepl

ステップ 4: Fargate タスク定義とクラスターを作成する

1. Docker 環境で次の内容の trustpolicyforecs.json というテキストファイルを作成します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
            "Service": "ecs-tasks.amazonaws.com"
        },
            "Action": "sts:AssumeRole"
        }
    ]
}

2. Docker 環境で次のコマンドを使用して、AccessRoleForCCfromFG というロールを作成します。

$ aws iam create-role --role-name AccessRoleForCCfromFG --assume-role-policy-document file://trustpolicyforecs.json

3. Docker 環境で次のコマンドを使用して、上記のロールに CodeCommit へのフルアクセス権限を割り当てます。

$ aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AWSCodeCommitFullAccess --role-name AccessRoleForCCfromFG

4. Amazon ECS コンソールで、[リポジトリ] を選択し、前のステップで作成した ccrepl リポジトリを選択します。リポジトリ URI をコピーします。
5. Amazon ECS コンソールで、[タスク定義] を選択し、[新しいタスク定義の作成] をクリックします。
6. 起動タイプの互換性の選択で、 [FARGATE] を選択し、[次のステップ] をクリックします。
7. タスク定義の作成画面で、次の操作を行います。

  • [タスク定義名] に ccrepl と入力
  • [タスクロール] で、AccessRoleForCCfromFG を選択
  • [タスク メモリ] で 2GB を選択
  • [タスク CPU] で、1 vCPU を選択
  • 同じ画面の [コンテナの定義] の下にある [コンテナの追加] をクリックします。[コンテナの追加] 画面で、次の操作を行います。
    • [コンテナ名] を ccreplcont として入力
    • [イメージ] に手順 4 でコピーした URL を入力
    • [メモリ制限] に 128 と入力し、[追加] をクリック

注:「ecsTaskExecutionRole」 が既に存在する場合は、[タスク実行ロール] で ecsTaskExecutionRole を選択します。そうでない場合は、新しいロールの作成を選択すると、「ecsTaskExecutionRole」が作成されます。

8. タスク定義画面の [作成] ボタンをクリックして、タスクを作成します。タスク、実行ロール、および AWS CloudWatch ロググループが正常に作成されます。
9. Amazon ECS コンソールで、[クラスター] をクリックしてクラスターを作成します。クラスターテンプレートは「ネットワーキングのみ」を選択し、[次のステップ] をクリックします。
10. クラスター名に ccreplcluster を入力し、[作成] をクリックします。

ステップ 5: Lambda 関数を作成する

このセクションでは、Lambda から Amazon Elastic Container Service (ECS) 実行タスク API を使用して Fargate タスクを実行します。
(ブログ訳注:英語版 Blog 執筆時点から画面構成が変わっているため、以下の手順は原文から一部修正しています。)

1. IAM コンソールで、ECS タスクの実行に必要な AWS CodeCommit、Amazon ECS へのアクセス許可を持つ ECSLambdaRole という新しいロールを作成します。ポリシーステートメントは次のようになります ( <your account id> を置き換えてください)。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
            "codecommit:*"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ecs:RunTask"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::<your account id>:role/ecsTaskExecutionRole",
                "arn:aws:iam::<your account id>:role/AccessRoleForCCfromFG"
            ]
        }
    ]
}

2. AWS マネジメントコンソールで、VPC サービスを選択し、左側のナビゲーション画面で [サブネット] をクリックします。Fargate タスクを実行するサブネット ID をメモしておきます。

3. Lambda コンソールで、[関数の作成] をクリックします。
4. 関数の作成画面で次の操作を行います。

  • [関数名] に FargateTaskExecutionFunc を入力
  • [ランタイム] で Node.js を選択
  • [アクセス権限] で「既存のロール」を選び、ECSLambdaRole を設定

(ブログ訳注:翻訳にあたり、2021/06時点で最新の Node.js 14 にて動作検証を行いました。)
5. FargateTaskExecutionFunc の画面で、コードソースにて index.js を選択し、コードを以下の内容に書き換えて、[Deploy] をクリックします。

注:サブネットの値 (赤色でマークしている箇所) を、このセクションのステップ 2 で Fargate タスクを実行するサブネットとしてメモしたサブネット ID に置き換えます。

var AWS = require('aws-sdk');
var ecs = new AWS.ECS();
exports.handler = (event, context, callback) => {
    var params = {
        cluster: "ccreplcluster",
        launchType: 'FARGATE',
        taskDefinition: "ccrepl:1",
        count: 1,
        platformVersion: 'LATEST',
        networkConfiguration: {
            awsvpcConfiguration: {
                subnets: [ /* required */
                    '<subnet ID1>',
                    '<subnet ID2>'
                ],
                assignPublicIp: 'ENABLED',
            }
        }
    };
    ecs.runTask(params, function(err, data) {
        if (err) console.log(err, err.stack); // an error occurred
        else console.log(data); // successful response
    });
};

ステップ 6: CodeCommit トリガーに Lambda を割り当てる

1. ステップ5 に引き続き、FargateTaskExecutionFunc の画面で、関数の概要の [トリガーの追加] で、[CodeCommit] を選択します。

2. [トリガーの構成] 画面で、次の操作を行います。

  • [リポジトリ名] でレプリケート元リポジトリを選択
  • [トリガー名] で LambdaTrigger を入力
  • [イベント]は「すべてのリポジトリイベント」のままにする
  • [ブランチ名]は「すべてのブランチ」を選択
  • [追加] ボタンをクリック

ステップ 7: 検証

アプリケーションをテストするには、コミットを行い、AWS CodeCommit のレプリケート元リポジトリに変更をプッシュします。これにより、Lambda 関数が自動的にトリガーされ、レプリケート先リポジトリに変更がレプリケートされます。これを確認するには、Lambda と ECS の CloudWatch Logs をチェックするか、単にレプリケート先リポジトリに移動して変更が表示されることを確認します。

まとめ

おめでとうございます!AWS Lambda と AWS Fargate を使用して、AWS CodeCommit リポジトリのレプリケーションを設定することができました。この手法は、デプロイパイプラインで使用できます。AWS CodeCommit のトリガー設定を行うことで、AWS CodeCommit でサポートされているイベントをトリガーに Lambda 関数を呼び出すこともできます。

CodeCommit の詳細については、AWS CodeCommit のドキュメントを参照してください。

翻訳はパートナーソリューションアーキテクトの田中 創一郎 が担当しました。原文はこちらです。