今回のサンプル チュートリアルでは、シンプルな Go アプリを Amazon EC2 Container Service (ECS) にデプロイします。次に、CircleCI を使用して、アプリの後続バージョンを自動的にビルド、テスト、デプロイします。使用するテクノロジーを正しく確実に理解できるように、手順を次のように大きく分けて段階的に行っていきます。

  1. セキュリティ グループを作成する
  2. 1 つのインスタンスで ECS クラスタを作成する
  3. ECS タスク定義を作成する
  4. タスク定義を実行するサービスを作成する
  5. CircleCI クラスタの ECS サービスに関連付ける Amazon Elastic Load Balancing (ELB) およびターゲット グループを作成して構成する
  6. 作成した ELB で DNS 名を使用してアプリケーションにアクセスする (動作することをテストするため)
  7. イメージが更新されたらビルドし Amazon Elastic Container Registry (ECR) にプッシュするように、circleci/aws-ecr@6.2.0 Orb を使用して CircleCI 設定ファイルを構成する
  8. 先ほど作成したクラスタに更新後のイメージをデプロイするように、circleci/aws-ecs@0.0.11 Orb を使用して CircleCI 設定ファイルを構成する

Amazon ECS の利点を十分に把握するには、まず Dockerについて理解する必要があります。このチュートリアルを進めるには、Docker とコンテナの知識が必要となります。また、継続的インテグレーションと継続的デプロイメント (CI/CD) の基本を理解している必要もあります。次のセクションでは、今回使用するテクノロジーと用語の一部を説明します。

使用するテクノロジーと用語の概要

  • ECS の概要: ECS は、Amazon Web Services (AWS)のコンテナ管理用クラウド コンピューティング サービスです。このサービスでは、アプリケーション プログラミング インターフェイス (API) 呼び出しとタスク定義を通じて、サーバーのグループ (クラスタ) で実行されるスケーラブルなアプリケーションをデプロイおよび管理できます。基本的に、ECS はタスク スケジューラです。ECS が作成するタスクは、実行中の Docker コンテナにマップされます。ECS は使用可能なリソースに基づいて、クラスタ内のリソースのどこでタスクを実行するかを決定します。他のコンテナ テクノロジー (LXC、RKT など) も利用できるようになっていますが、Docker が広く採用されていることから、ECS は Docker コンテナでネイティブに動作するように設計されています。

  • タスク定義: タスク定義は、コンテナの実行方法を記述したレシピと言えます。タスク定義には、公開するコンテナのポート、割り当てるメモリと CPU、コンテナを起動する Docker イメージなどの情報を指定します。このサンプルのタスク定義では、コンテナ 1 つ、使用するイメージ、割り当てる CPU とメモリ、および公開するポートを設定します。

  • タスク: ECS におけるタスクは、タスク定義に基づき実行中のコンテナをインスタンス化してまとめたものであり、同一インスタンス上で一緒に実行される 1 〜 N 個のコンテナの論理グループです (N は 1〜10 の間で定義されます)。ニーズに応じて、1 つのタスク定義で複数のタスクを作成できます。

  • サービス: ECS におけるサービスは、いかなるときも複数のタスクが常時実行されていることを保証するためのものです。エラーが発生しタスクのコンテナが終了した場合、またはバックエンドの EC2 インスタンスで障害が発生し置き換えられる場合、ECS サービスにより失敗したタスクが置き換えられます。クラスタを作成するのは、サービスが十分なリソース (使用する CPU、メモリ、ネットワーク ポート) を利用できるようにするためです。実行されている限り、どのインスタンス タスクが実行されるかは重要ではありません。サービス設定ファイルは、タスク定義を参照します。サービスはタスクの作成を担当します。

  • クラスタ:ECS クラスタ は、単一のリージョン内にある (コンテナ) インスタンス (Fargate ではタスク) をまとめたグループですが、複数の可用性ゾーンにまたがる場合もあります。ECS は、これらのインスタンスへのスケーリング リクエストのスケジューリング、管理、および処理のロジックを処理します。また、CPU とメモリのニーズに基づいて各タスクを自動で最適に配置します。クラスタでは多くのサービスを実行できます。プロダクトが複数のアプリケーションで構成される場合、それらのいくつかを 1 つのクラスタに配置できます。これにより、利用可能なリソースを効率的に使用し、セットアップの時間を最小限に抑えることができます。

  • コンテナ インスタンス: これは、Amazon ECS コンテナ エージェントを実行している Amazon Elastic Compute Cloud (EC2) インスタンスのことです。コンテナ インスタンスには IAM ポリシーロール が明確に定義されており、クラスタに登録されています。Amazon ECS でタスクを実行すると、EC2 起動タイプに従い、ユーザーのタスクがアクティブなコンテナ インスタンスに配置されます。

  • CircleCI Orb: Orb は、プロジェクト間で共有できる CircleCI 設定ファイルのパッケージです。Orb を使用すると、ジョブ、コマンド、Executor から成る単一のバンドルを作成して、相互に参照したり、CircleCI ビルド構成にインポートして固有の名前空間で呼び出したりすることができます。

シンプルな Go アプリケーション

今回のプロジェクトのディレクトリ構造は次のとおりです。

.
├── .circleci
│   └── config.yml
├── Dockerfile
├── README.md
├── ecs-service.json
├── main.go
└── task-definition.json

1 directory, 6 files

完全なアプリケーションは、こちらのリポジトリ にあります。チュートリアルを自分の手で進めたい方は、お使いの端末で次のコマンドを実行し、任意のクローンを作成してください。

$ git clone https://github.com/daumie/circleci-ecs.git

Dockerfile のみが必要な場合は、こちらを参照してください。この Go アプリケーションの main.go ファイルは こちらにあります。

では、これらをまとめて実行していきましょう。最初に、AWS アカウントを作成してアクティブにします。次に、ローカル マシンに AWS CLI をインストールして設定します。AWS CLI は、AWS とのやり取りに使用します。

Virtual Private Cloud (VPC) は、AWS アカウントの作成時に自動で作成されるデフォルトのものを使用します。これが利用できない場合は、次のコマンドを実行してデフォルトの VPC を作成します。

$ aws ec2 create-default-vpc

次のコマンドを実行して、利用できる VPC があることを確認します。

$ aws ec2 describe-vpcs

デフォルトの VPC があることを確認したら、後で使用するセキュリティ グループを作成しましょう。

$ aws ec2 create-security-group --group-name circleci-demo-sg --description "Circle CI Demo Security Group"

次に、ECS クラスタ および関連する EC2 インスタンスを作成します。circleci-demo-clusterクラスタを呼び出します。先ほど作成した circleci-demo-sg セキュリティ グループをアタッチする必要があります。

  • クラスタ名: circleci-demo-cluster
  • EC2 インスタンス タイプ: t2.medium
  • ネットワーキング: すべてのサブネットでデフォルト VPC を使用
  • セキュリティ グループ: (circleci-demo-sg) この ID を使用
  • コンテナ インスタンス IAM ロール: ecsInstanceRole

クラスタコンフィグ

数分間待ってから、コンテナ インスタンスが circleci-demo-clusterに正常に登録されたことを確認します。登録されているかどうかは、Clusters/my-clusterの下の[ECS インスタンス]タブをクリックすると確認できます。 デモクラスタ CircleCI

アプリケーション イメージを作成し AWS ECR にプッシュする

Docker イメージをローカルで作成し、ECR にプッシュします。

$ docker build -t circleci-ecs:v1 .

Step 1/14 : FROM golang:latest as builder
 ---> be63d15101cb
...

ECR の手順に従ってイメージ リポジトリを作成し、circleci-demoという名前を付けます。

リポジトリの作成例

AWS アカウントには一意の ID が設定されています。次のコマンドの 634223907656 をその ID に変更してください。実際のリポジトリ名を入力して、イメージにタグを付けます。

$ docker tag circleci-ecs:v1 634223907656.dkr.ecr.eu-west-2.amazonaws.com/circleci-demo:latest

認証情報ヘルパーを使用して、Docker CLI の AWS ECR リポジトリを認証 できます。次のコマンドを使用して認証します (必要に応じてリージョンを変更します)。

$ aws ecr get-login --no-include-email --region eu-west-2 | bash

次に、イメージを ECR リポジトリにプッシュします。

$ docker push 634223907656.dkr.ecr.eu-west-2.amazonaws.com/circleci-demo:latest

これで、ECR リポジトリにイメージを配置できました。今度は、Go アプリケーションを起動するブループリントになるタスク定義が必要です。本プロジェクトのルートにある task-definition.json ファイルに、次のコード行が記述されています。

{
    "family": "circleci-demo-service",
    "containerDefinitions": [
        {
            "name": "circleci-demo-service",
            "image": "634223907656.dkr.ecr.eu-west-2.amazonaws.com/circleci-demo:latest",
            "cpu": 128,
            "memoryReservation": 128,
            "portMappings": [
                {
                    "containerPort": 8080,
                    "protocol": "tcp"
                }
            ],
            "command": [
                "./main"
            ],
            "essential": true
        }
    ]
}

: image には、先ほど ECR にプッシュしたものを指定してください。

コマンド ライン インターフェイスで次のコマンドを実行して、タスク定義を登録します。

$ aws ecs register-task-definition --cli-input-json file://task-definition.json

ECS コンソールで、以下のようにタスク定義が正常に登録されたことを確認します。

タスクの設定

後で ECS サービスに関連付ける ELB とターゲット グループを作成する

複数のコンテナ間でリクエストを負荷分散し、テスト用に Go アプリケーションをインターネットに公開することが最終目的であるため、次は ELB を作成します。ELB の作成は AWS コンソールで行います。EC2 コンソール で、[ロードバランシング] 、[ロードバランサー] 、[ロードバランサーの作成]の順にクリックして、[Application Load Balancer]を選択します。

ロード バランサーの構成

  • ロード バランサーに circleci-demo-elb という名前を付けて、[インターネット向け] を選択します。
  • [リスナー] で、HTTP プロトコルとポート 80 が設定されたデフォルトのリスナーを使用します。
  • [アベイラビリティーゾーン] で、クラスタの作成時に使用した VPC を選択し、必要なサブネットを選択します。

セキュリティ設定の構成

  • SSL は使用しないため、警告をスキップします。

セキュリティ グループの構成

  • 新しいセキュリティ グループを circleci-demo-elb-sg という名前で作成し、ポート 80 とソース 0.0.0.0/0 を開いて、ELB に外部から自由にポート 80 で アクセスできるようにします。

ルーティングの構成

  • ポート 80 を使用する新しいターゲット グループ circleci-demo-target-group を作成します。

ターゲットの登録

  • ECS インスタンスを選択して、既存のターゲットを登録します。 ターゲットの登録イラスト

レビュー

  • ロード バランサーの詳細を確認します。 ロードバランサーの画像

circleci-demo-elb-sg セキュリティ グループでは、circleci-demo-elb ロード バランサーのポート 80 が外部向けに開かれています。そのため、ECS インスタンスに関連付けられた circleci-demo-sg セキュリティ グループで、ロード バランサーからのトラフィックを許可する必要があります。すべての ELB トラフィックがコンテナ インスタンスに到達できるようにするため、次のコマンドを実行します。

$ aws ec2 authorize-security-group-ingress --group-name circleci-demo-sg --protocol tcp --port 1-65535 --source-group circleci-demo-elb-sg

EC2 コンソールで、次のようにセキュリティ グループにこれらのルールが追加されたことを確認します。

inbound rules

outbound rules

これらのセキュリティ グループ ルールの内容は次のとおりです。

  • ELB のポート 80 のみを外部に公開する。
  • ELB から circleci-demo-target-group グループのコンテナ インスタンスに送信されるトラフィックをすべて許可する。

サービスを作成する

次のステップは、circleci-demo-service タスク定義 (task-definition.json ファイルで定義したもの) を実行するサービスを作成することです。本プロジェクトのルートにある ecs-service.json ファイルに、次のコード行が記述されています。

{
    "cluster": "circleci-demo-cluster",
    "serviceName": "circleci-demo-service",
    "taskDefinition": "circleci-demo-service",
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:eu-west-2:634223907656:targetgroup/circleci-demo-target-group/a5a0f047c845fcbb",
            "containerName": "circleci-demo-service",
            "containerPort": 8080
        }
    ],
    "desiredCount": 1,
    "role": "ecsServiceRole"
}

EC2 コンソールで [ロードバランシング]、[ターゲットグループ]、 circleci-demo-target-group の順にクリックして、circleci-demo-elb ロード バランサーの作成時に作成した targetGroupArn を見つけます。 その値をコピーして、ecs-service.json ファイルの targetGroupArnの値と置き換えます。

次に、circleci-demo-service ECS サービスを作成します。

$ aws ecs create-service --cli-input-json file://ecs-service.json

ECS コンソールで [クラスター] 、[circleci-demo-cluster] 、[circleci-demo-service] の順にクリックし、[タスク] タブに移動します。以下のようにコンテナが実行されていることを確認します。

コンテナ

すべてが問題なく機能しているかテストする

curl を使用して、ELB で公開されている DNS エンドポイントを確認します。

$ curl circleci-demo-elb-129747675.eu-west-2.elb.amazonaws.com; echo

Hello World!

DNSエンドポイント

次のように、ブラウザーから確認することもできます。

ブラウザー確認イラスト

ビルド、テスト、およびデプロイを実行するように CircleCI 設定ファイルを構成する

Go アプリケーションを ECS に正常にデプロイできたので、次は、アプリを更新する度にデプロイし直されるようにします。CircleCI orbを使用することで、CircleCI 設定ファイルに構成済みのコマンド、ジョブ、および Executor をインポートでき、多くの時間を節約できます。これにより、AWS のデプロイで必須となる bash スクリプトを作る手間を大幅に省くとともに、設定ファイル内のコード行も大幅に削減できます。本プロジェクトでは、orbs キーを使用して次の Orb を呼び出します。

  • circleci/aws-ecr@6.2.0: Amazon ECR と連携して、更新されたイメージをビルドおよびプッシュする Orb
  • circleci/aws-ecs@0.0.11: Amazon ECS と連携して、更新されたイメージを以前に作成したクラスタにデプロイする Orb

Orb は次の要素から構成されます。

  • コマンド
  • ジョブ: 実行可能なコマンドやステップをまとめたセット
  • Executor: ジョブのステップの実行環境 (Docker、マシン、macOS など) とその環境の他のパラメーターを定義したもの

CircleCI を使用するには、CircleCI でビルド、テスト、デプロイの各操作を命令するための設定ファイルが必要です。このプロジェクトの config.yml ファイルには、次のコード行が記載されています。

version: 2.1

orbs:
  aws-ecr: circleci/aws-ecr@6.2.0
  aws-ecs: circleci/aws-ecs@0.0.11

workflows:
# AWS にログインしイメージをビルドして Amazon ECR にプッシュする
  build_and_push_image:
    jobs:
      - aws-ecr/build-and-push-image:
          account-url: AWS_ECR_ACCOUNT_URL
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          create-repo: true
          # 使用する Dockerfile の名前。デフォルト値は Dockerfile
          dockerfile: Dockerfile
          # AWS_REGION_ENV_VAR_NAME
          region: AWS_DEFAULT_REGION
          # myECRRepository
          repo: '${MY_APP_PREFIX}'
          # myECRRepoTag
          tag: "$CIRCLE_SHA1"
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          aws-region: AWS_DEFAULT_REGION
          family: '${MY_APP_PREFIX}-service'
          cluster-name: '${MY_APP_PREFIX}-cluster'
          container-image-name-updates: 'container=${MY_APP_PREFIX}-service,tag=${CIRCLE_SHA1}'

GitHub と CircleCI を使用するので、CircleCI アカウントがない場合は作成してください。 GitHub に登録した後、CircleCI ダッシュボードで [Add Project(プロジェクトの追加)] をクリックし、表示されたリストからプロジェクトを追加します。

以下の環境変数を追加します。

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_DEFAULT_REGION
  • AWS_ECR_ACCOUNT_URL (この例では “634223907656.dkr.ecr.eu-west-2.amazonaws.com”)
  • MY_APP_PREFIX (この例では “circleci-demo”)
    ブラウザ

main.go ファイルで

html := "Hello World!" 

html := "Hello World! Now updated with CircleCI"

に変更し、コミットして GitHub にプッシュします。

ターミナルで次のコマンドを実行することで、変更が適用されたことを確認できます。

$ curl circleci-demo-elb-129747675.eu-west-2.elb.amazonaws.com ; echo

Hello World! Now updated with CircleCI

次のように、ブラウザーからも確認できます。

確認画面

まとめ

今回は、シンプルな GO アプリケーションをビルドし、ECR にデプロイしました。この後、テストをアプリケーションに追加すれば、テストに合格したときだけ ECS インスタンスが更新されるように設定できます。このチュートリアルでは、基本的なアプリケーションを使用しましたが、これは多くの実際の状況で機能する成熟したデプロイ パイプラインです。

さらに、CircleCI Orb を使用すると、CircleCI 設定ファイルの記述を簡素化して生産性を高められます。Orb は共有できるため、構築済みのコマンド、ジョブ、および Executor を設定ファイルで繰り返し使用でき、時間を節約できます。Orb の使用は、CircleCI と ECS のデプロイに限定されません。利用可能な Orb の全一覧は Orb レジストリ にまとめられており、使用するクラウド プラットフォーム、プログラミング言語などに適合する Orb を見つけることができます。


Dominic Motuka 氏は Andela 社の DevOps エンジニアであり、コンフィグ管理、CI/CD、DevOps プロセスを活用した、AWS および GCP の本番環境へのデプロイのサポート、自動化、最適化について 4 年以上の実務経験があります。

さんの他の投稿を読む Dominic Motuka