3. 冗長構成の Fargate ステージング環境

git の release ブランチへのマージで、ロードバランサを備えた評価用環境が更新される環境を作成します。

3.1. パイプラインの改変

インフラ環境更新に必要な設定値をローカルファイルに追記します。


In [ ]:
cat << EOT >> ~/config/.env 
export STAGING_STACK_NAME="fargate-handson-staging-env"
EOT

Edge 環境のリソース定義に この パッチをあてます。


In [ ]:
patch ~/notebook/infrastructure/sam.yaml < ~/notebook/infrastructure/diff/staging

このテンプレートの適用により、以下のような構成へ更新していきます。

適用前に、新しい sam.yaml の内容が正しいことを検証します。


In [ ]:
aws cloudformation validate-template --template-body file://infrastructure/sam.yaml

成功時応答例)

{
    "Description": "A fargate application with a CI/CD pipeline",
    "Parameters": [
        {
            "ParameterKey": "ProjectID",
            "NoEcho": false
        },
    ..

Lambda 関数を含むテンプレートのため、パッケージングします。


In [ ]:
source ~/config/.env

aws cloudformation package \
    --template-file infrastructure/sam.yaml \
    --output-template-file infrastructure/cfn.yaml \
    --s3-bucket "${S3_BUCKET_NAME}" \
    --s3-prefix "cloudformation"

成功時応答)

Uploading to cloudformation/0e64849e597b443d9a1349275098ab43  766 / 766.0  (100.00%)
Successfully packaged artifacts and wrote output template to file infrastructure/cfn.yaml
..

パッケージングした CloudFormation テンプレートでインフラを更新します。


In [ ]:
aws cloudformation deploy \
    --stack-name "${BASE_STACK_NAME}" \
    --template-file infrastructure/cfn.yaml \
    --parameter-overrides \
        ProjectID="${PROJECT_ID}" \
        S3BucketName="${S3_BUCKET_NAME}" \
        EdgeStackName="${EDGE_STACK_NAME}" \
        StagingStackName="${STAGING_STACK_NAME}" \
        ApprovalEmail="${GIT_EMAIL_ADDRESS}" \
    --capabilities CAPABILITY_IAM

成功時応答)

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - fargate-handson-base

3.2. ステージング環境の Fargate を起動する

こちら のテンプレートを使い、ステージング環境を作成します。
この環境はロードバランサーやオートスケーリングの設定がされ、本番環境を想定した構成になっています。

- CloudWatch Logs ロググループ
- ECS クラスター
- ECS タスク定義
- ECS サービス
- IAM ロール * 2

TODO: 図を用意する

開発環境 Fargate とステージング・本番環境 Fargate の差分を確認してみます。


In [ ]:
diff ~/notebook/application/deploy/cfn-master.yaml \
     ~/notebook/application/deploy/cfn-release.yaml

では実際にステージング環境の Fargate をデプロイしてみます。


In [ ]:
DOCKER_IMAGE=dockercloud/hello-world

aws cloudformation deploy \
    --stack-name "${STAGING_STACK_NAME}" \
    --template-file application/deploy/cfn-release.yaml \
    --parameter-overrides \
        ProjectID="${PROJECT_ID}" \
        DockerImage="${DOCKER_IMAGE}" \
    --capabilities CAPABILITY_IAM

成功時応答)

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - fargate-handson-staging-env

構築が完了したら、ステージング環境の Fargate を確認してみましょう。


In [ ]:
STAGING_ENDPOINT="http://$( aws cloudformation describe-stacks \
    --stack-name "${STAGING_STACK_NAME}" --output text \
    --query 'Stacks[*].Outputs[?OutputKey==`LoadBalancerDNSName`].OutputValue' )"

cat << EOT >> ~/config/.env
export STAGING_ENDPOINT="${STAGING_ENDPOINT}"
EOT
echo "${STAGING_ENDPOINT}"

応答例)

http://farga-LoadB-xxx-xxx.ap-northeast-1.elb.amazonaws.com

ロードバランサー経由で Fargate にアクセスできましたか?
オートスケーリングの設定もされており、これで負荷分散する Fargate の完成です。

3.3. リリースブランチへのマージでステージング環境を更新する

CodeCommit に現在の master ブランチから release ブランチを作り、ステージング環境も更新しましょう。


In [ ]:
cd ~/notebook/application
git log -1

aws codecommit create-branch \
    --repository-name "fargate-handson" \
    --branch-name "release" \
    --commit-id "$( git log -1 --pretty=%H )"

応答例)

commit 2236df05287049512148f204395f0d433c47a382 (HEAD -> master, origin/master)
Author: foo <foo@bar.com>
Date:   Mon Jul 23 13:24:04 2018 +0000

    first commit

CodePipeline のコンソールから、リリース版パイプライン が更新されるのを眺めます。


In [ ]:
codebuild_project_name=$( aws cloudformation describe-stacks \
    --stack-name "${BASE_STACK_NAME}" --output text \
    --query 'Stacks[*].Outputs[?OutputKey==`CodePipelineRelease`].OutputValue' )
codebuild_console="https://ap-northeast-1.console.aws.amazon.com/codepipeline/home"
echo "${codebuild_console}?region=${AWS_DEFAULT_REGION}#/view/${codebuild_project_name}"

更新されたら、Fargate の内容が新しくなっていることを確認しましょう。


In [ ]:
echo "${STAGING_ENDPOINT}"

3.4. プルリクエストで環境を更新する

master を更新し、release に対しプルリクを作成し、マージされることでも同様の環境更新がされることを確認します。

3.4.1. アプリケーション定義の書き換え、master へ push

  • Dockerfile を書き換え、ip-api.com へのリバースプロキシを Fargate で動かします
  • Basic 認証がかかるよう、CodeBuild 経由で cfn.yaml に渡す環境変数をセットします

ここまでを git commit -> push します。


In [ ]:
cd ~/notebook/application

cat << EOF > Dockerfile
FROM pottava/proxy

ENV APP_PORT=80 \\
    PROXY_PATTERNS="*=http://ip-api.com/json" \\
    HEALTHCHECK_PATH=/health \\
    ACCESS_LOG=true
EOF

cat << EOT > deploy/edge/env.sh
export BASIC_AUTH="BasicAuthUsername":"edge","BasicAuthPassword":"fargate"
EOT
chmod +x deploy/edge/env.sh
cat << EOT > deploy/staging/env.sh
export BASIC_AUTH="BasicAuthUsername":"staging","BasicAuthPassword":"fargate"
EOT
chmod +x deploy/staging/env.sh

git add .
git commit -m "reverse-proxy to ip-api.com"
git push

応答例)

[master 628191e] reverse-proxy to ip-api.com
 2 files changed, 2 insertions(+), 2 deletions(-)
Counting objects: 7, done.
..

開発(Edge)環境のパイプラインが動き、環境が更新されます。
内容が正しそうか確認しましょう。


In [ ]:
codebuild_project_name=$( aws cloudformation describe-stacks \
    --stack-name "${BASE_STACK_NAME}" --output text \
    --query 'Stacks[*].Outputs[?OutputKey==`CodePipeline`].OutputValue' )
codebuild_console="https://ap-northeast-1.console.aws.amazon.com/codepipeline/home"
echo "${codebuild_console}?region=${AWS_DEFAULT_REGION}#/view/${codebuild_project_name}"

In [ ]:
cluster_name=$( aws cloudformation describe-stacks \
    --stack-name "${EDGE_STACK_NAME}" --output text \
    --query 'Stacks[*].Outputs[?OutputKey==`Cluster`].OutputValue' )
task_id=$( aws ecs list-tasks --cluster "${cluster_name}" \
    --family "${EDGE_STACK_NAME}" | jq -r '.taskArns[0]' )
eni_id=$( aws ecs describe-tasks --cluster "${cluster_name}" --task "${task_id}" \
    | jq '.tasks[0].attachments[0].details[]' \
    | jq -r 'select( .name | contains("networkInterfaceId")).value' )
edge_endpoint="http://$( aws ec2 describe-network-interfaces \
    --network-interface-ids ${eni_id} \
    | jq -r '.NetworkInterfaces[].Association.PublicIp' )"

echo "${edge_endpoint}/json"

Basic 認証は上記変更の通り、ユーザー名は edge、パスワードは fargate です。

ブラウザでアクセスすると、以下のような JSON か返ってくるかと思います。
応答例)

{
  as: "AS16509 Amazon.com, Inc.",
  countryCode: "JP",
  city: "Tokyo",
  country: "Japan",
  isp: "Amazon.com",
  query: "13.115.178.180",
  ..
}

Edge 環境で動作確認が取れたら、release ブランチに対してプルリクエストを作成します。


In [ ]:
repo="fargate-handson"
aws codecommit create-pull-request \
    --title "My first Pull Request" \
    --description "Please review this immediately!" \
    --targets "repositoryName=${repo},sourceReference=master,destinationReference=release"

応答例)

{
    "pullRequest": {
        "pullRequestId": "1",
        "title": "My first Pull Request",
        "description": "Please review this immediately!",
        "pullRequestTargets": [
            {
                "repositoryName": "fargate-handson",
                "sourceReference": "refs/heads/master",
                "destinationReference": "refs/heads/release",
    ..
}

残念ながら、孤独に一人で開発中なので、自分でレビューをしてマージしましょう・・
https://ap-northeast-1.console.aws.amazon.com/codecommit/home?region=ap-northeast-1#/repository/fargate-handson/pull-requests

release ブランチにマージされると、ステージング環境へのデプロイが開始されます。


In [ ]:
codebuild_project_name=$( aws cloudformation describe-stacks \
    --stack-name "${BASE_STACK_NAME}" --output text \
    --query 'Stacks[*].Outputs[?OutputKey==`CodePipelineRelease`].OutputValue' )
codebuild_console="https://ap-northeast-1.console.aws.amazon.com/codepipeline/home"
echo "${codebuild_console}?region=${AWS_DEFAULT_REGION}#/view/${codebuild_project_name}"

ステージング環境でも内容が変更されたことを確認してみましょう。


In [ ]:
echo "${STAGING_ENDPOINT}/json"

Basic 認証のユーザー名は staging、パスワードは fargate です。

開発環境同様、以下のような JSON か返ってくれば OK です!
応答例)

{
  as: "AS16509 Amazon.com, Inc.",
  countryCode: "JP",
  city: "Tokyo",
  country: "Japan",
  isp: "Amazon.com",
  query: "13.115.178.180",
  ..
}