Help us understand the problem. What is going on with this article?

CircleCI で GitHubのブランチ更新をフックしてAWS ECSへデプロイする

More than 1 year has passed since last update.

この文書について

対象読者: サーバサイドエンジニア

AWS の ECSへのデプロイ を GitHub と CircleCI を使って実施する方法を紹介します

具体的には

  • インフラ構成 (GitHub,CircleCI,AWS にそれぞれどんなデータがあるのか)
  • デプロイはどのような流れで行われるのか
  • デプロイフローの構築
    • AWS側で準備すること
    • ビルド用にソースコードリポジトリに準備しておくこと
    • CircleCI側で準備すること
    • CircleCIのYAMLファイルに書くシェルスクリプト

を説明します。

利用するツール

  • GitHub
    • いわずと知れたGitリポジトリのホスティング
    • 外部サービス連携がやりやすい
  • CircleCI 2.0
    • GitHub上のリポジトリのイベントと連携してタスクが実行できます
      • PullRequestに連動してテストコードを実行
      • ブランチのPushに連動してデプロイ (今回はコレ)
  • AWS ECS
    • Dockerイメージを管理・コンテナを起動などができるサービス

前提

  • アプリケーションは The Twelve-Factor App に準拠
    • 詳細は割愛しますが、重要なのは以下の2つ
      • I. Codebase : One codebase tracked in revision control, many deploys
      • III. Config : Store config in the environment
  • デプロイするためのブランチ名は環境別に用意する
    • deploy/development => 開発環境
    • deploy/production => 本番環境

インフラ構成とデプロイフロー

deploy_ecs_with_circleci.png

  1. エンジニアが GitHubに deployブランチをプッシュする
  2. CircleCIがブランチのPushをトリガーに起動
  3. CircleCIがGitHubの該当コミットをチェックアウト
  4. CircleCIがソースコードからDockerImageをビルドし、タグを発行
  5. CircleCIがDockerImageを AWS ECRにプッシュする
  6. CircleCIがソースコードから環境変数を抽出する
  7. CircleCIが環境変数とタグを元にECSのタスク定義を追加
  8. CircleCIがECSのサービスのタスク定義更新し、新しく作ったタスク定義を参照させる
  9. ECSが新しいコンテナを起動する

デプロイフローの構築

AWS

  • ECRのRegistry(イメージ置き場)を作成
    • 名前はソースコードリポジトリと同様にしておく
  • ECSのクラスタを作成
    • クラスタ名は {環境名}-{ソースコードリポジトリ名}-cluster
  • ECSのサービスを作成
    • サービス名は {環境名}-{ソースコードリポジトリ名}-service
  • ECSのタスク作成
    • タスク名は {環境名}-{ソースコードリポジトリ名}-task
  • ECSのタスクを変更可能なIAMユーザを作成
    • ユーザ名は {環境名}-{ソースコードリポジトリ名}-iam-role-for-ecs-task

ソースコード

  • 環境依存の変数を暗号化してコミット
    • AWSのManagementConsoleで タスク定義 を表示し、 JSONを表示
    • 表示したJSONをコピーしてgit管理しているディレクトリに保存
    • 保存したJSONの environment プロパティを編集
      • DBのエンドポイント情報など
    • 編集したJSONを openssl enc で暗号化する
      • ファイル名は {環境名}.json.crypted

CircleCI

  • GitHubのソースコードリポジトリとCircleCIの連携をする
  • CircleCIの管理画面で、対象リポジトリのビルド設定をする
    • AWSのAccessKeyとSecretAccessKeyを登録
      • ECSのタスクを変更可能なIAMユーザのもの
  • CircileCIの環境変数に暗号化したファイルの復号化パスワードを登録しておく
    • 変数名は ENCRYPTION_PASSWORD

CircleCIのYAMLファイルに書くシェルスクリプト

.circleci/config.yml の run で実行するシェルスクリプトのうち、
ECS関連部分を抜粋すると以下のとおりです。

# デプロイブランチ名から環境名を抽出
TARGET_ENV=`echo ${CIRCLE_BRANCH} | sed -e 's#deploy/##g'`
DOCKER_IMAGE_NAME={ソースコードリポジトリ名}
COMMIT_ID=$(git rev-parse HEAD)

# ECRへログイン
ECR_LOGIN_COMMAND=$(aws ecr get-login --no-include-email --region ap-northeast-1)
eval $ECR_LOGIN_COMMAND
# タグ付け
DOCKER_REGISTORY={AWSのID}.dkr.ecr.ap-northeast-1.amazonaws.com
docker tag \
  $DOCKER_IMAGE_NAME:$COMMIT_ID \
  $DOCKER_REGISTORY/$DOCKER_IMAGE_NAME:$COMMIT_ID
# ECRへプッシュ
docker push "$DOCKER_REGISTORY/$DOCKER_IMAGE_NAME:$COMMIT_ID"

# 暗号化した環境変数の復号化
openssl enc -d -aes-256-cbc -in ${TARGET_ENV}.json.crypted -out ${TARGET_ENV}.json -k "${ENCRYPTION_PASSWORD}"
# タスク定義のjson作成 (復号化したjsonにECRに登録したイメージ:タグを注入)
FAMILY="${TARGET_ENV}-{ソースコードリポジトリ名}-task"
TEMPLATE=`cat ${TARGET_ENV}.json`
CONTAINER_DEFINITIONS=$(printf "${TEMPLATE}" ${DOCKER_IMAGE_NAME_AND_TAG})
TASK_ROLE_ARN="arn:aws:iam::{AWSのID}:role/${TARGET_ENV}-{ソースコードリポジトリ名}-iam-role-for-ecs-task"
AWS_REGION={お使いのAWSのリージョン e.g. ap-northeast-1}

# ECSのタスク定義を作成
TASK_REVISION=$(aws ecs register-task-definition \
--region "$AWS_REGION" \
--task-role-arn "$TASK_ROLE_ARN" \
--container-definitions "${CONTAINER_DEFINITIONS}" \
--family ${FAMILY} \
| jq --raw-output --exit-status '.taskDefinition.taskDefinitionArn')

SERVICE="${APP_ENV}-{ソースコードリポジトリ名}-service"
CLUSTER="${APP_ENV}-{ソースコードリポジトリ名}-cluster"

# ECSのサービスを更新
UPDATED_REVISION=$(aws ecs update-service \
--region ap-northeast-1 \
--cluster ${CLUSTER} \
--service ${SERVICE} \
--task-definition ${TASK_REVISION} \
| jq --raw-output --exit-status '.service.taskDefinition')

課題と改善のアイデア

この方法ではタスク定義にDB接続情報などが埋め込まれます。

このため、AWSのManagementConsoleのECSの権限さえもっていれば、
こうした情報を運用担当者が確認できてしまいます。

改善案としては、タスク定義に埋め込むのは暗号化した値とし、
アプリケーション内で復号化をしたほうがより安全になります。

候補 : AWS KMS

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away