2020年9月7日月曜日

ECS(laravel)のCI/CDとログ収集(laravel.log)を試す


以下のこと試してみました。
  • CloudFromation, rainで検証環境を作る。
  • ローカル環境(docker)で、ngin,php-fpm(laravel)を動かす。
  • AWS環境のECSで、ngin,php-fpm(laravel)を動かす。
  • AWS環境のECSは、Bitbucket,CodePipelineでCI/CDする。
  • laravel.log をCloudWatchLogsで見る。


検証環境の構成は下図のとおり。


作業環境(ローカル環境)


構築作業は、WSL2(ubuntu20.04)で実施します。
なお、AWSのリージョンは us-east-1を使用します。
  • Windows10
  • WSL2(ubuntu20.04)
  • docker 19.03.12
  • make 4.2.1
  • aws-cli 2.1.31
  • rain 1.1.2

rain


CloudFormationのCLI実行ツールです。rainについては下記URL参照
今回は、Makefileにrainコマンドを記載して使用します。

aws-cli


rain, aws-cli実行時はプロファイルを指定します。プロファイルの設定は下記のとおり。
  • blue21 という名称でプロファイルを作成
  • リージョンは us-east-1 を使用し, Output形式は json
  • 管理者権限のアクセスキーを使用

ソース一式のdownload


環境構築に必要なCloudFormationテンプレートなどは、Bitbucketに登録しています。
Bitbucketの下記URLからダウンロードできます。

ローカルのDocker環境構築


CloudFormationを実行し、VPCとECRを構築します。
ローカル環境で必要なのはECRだけですが、VPCもついでに構築します。
$ cd <InstallDir>/aws/cfn
$ make -f Makefile.rain cfnstep1 RAINOPT="-y"


作成したスタックを確認します。

$ make -f Makefile.rain stackls
aws cloudformation describe-stacks \
--profile blue21 \
--query 'Stacks[?contains(StackName,`Blue21`)].{StackName:StackName,StackStatus:StackStatus,Desc:Description}' \
--output table
-----------------------------------------------------
|                  DescribeStacks                   |
+--------------+----------------+-------------------+
|     Desc     |   StackName    |    StackStatus    |
+--------------+----------------+-------------------+
|  ECR         |  Blue21EcrPoC  |  CREATE_COMPLETE  |
|  VPC & subnet|  Blue21VpcPoC  |  CREATE_COMPLETE  |
+--------------+----------------+-------------------+


Dockerイメージをビルドし、ローカル環境用としてECRへPushします。

$ cd <InstallDir>/docker
$ make build
$ make push_local


ECRにPushしたイメージは下記のとおり。
nginx, php-fpm の2つです。imageTagに"local"を設定しています。

$ aws ecr list-images --repository-name blue21/nginx --profile blue21
{
    "imageIds": [
        {
            "imageDigest": "sha256:c52d95ceaee7842700ea4449128f90afd7bb440372ac6a569ecf7160ee5523db",
            "imageTag": "local"
        }
    ]
}
$ aws ecr list-images --repository-name blue21/php-fpm --profile blue21
{
    "imageIds": [
        {
            "imageDigest": "sha256:48d720bcb6bf55036ef10a00139231e510104951c3e30db376cc54764dee6da4",
            "imageTag": "local"
        }
    ]
}


docker環境を起動します。

イメージは、ECRからPullします。

$ cd <InstallDir>/docker
$ make up


ローカル環境のデモサイトにアクセスして動作確認します。

$ curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/1.19.0
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/7.4.13
Cache-Control: no-cache, private
Date: Sat, 06 Mar 2021 12:35:08 GMT
Set-Cookie: XSRF-TOKEN=eyJpdiI6Ikd2eUdKcUMwbTZsTC93bDZ1cDBNd3c9PSIsInZhbHVlIjoidHIwNEE3b3lkZWVZRXZLaFdkbWUySWRLYSt2MkNBVlF3L1o4VDV5dDluZXFpNm9IM1BNNHdKUlM1bFhBeE5CY3cxdXdjVEV6ci9zQmE5by8zOC9DQzZNRTZYaXpMdElFMFRsZG9tUDFxTzN0SzRRYjhnVmxOMUdVTzZvL0ozQi8iLCJtYWMiOiI3ZjRiZWQxNDBmZDFkZTE5ZjAyMGY1MjI5ZmQwNWU2ZjBlMGVjM2MzNzU4MGU3NmY0ZDFmYTVhM2U2MjRiM2I5In0%3D; expires=Sat, 06-Mar-2021 14:35:08 GMT; Max-Age=7200; path=/; samesite=lax
Set-Cookie: laravel_session=eyJpdiI6ImJLaFQzblhpcllSWTRQUERFZDZQUXc9PSIsInZhbHVlIjoicHlkR2FEMjIxSTJqajdwVmZxZkFMMURTanllV0lXQmFSVlR6emkzK0Exak1BVTIvbnh0MkVTbDJiM25wT1FTMzQ4MmI0V0RQRlp1dVZmZEdYeUxkUXZSZ2RFMmpMQ0ZnL3BGeTlMYmYxblJ2d3NSQ2RVTnBTSGowcDZQQjFvZkwiLCJtYWMiOiJkODk1YWVjYWQ0OTc2MmYxODkxOWQ1MTA2NDA0NjM3YzUxMmE3MzZiYzdiN2M0ZDc0OTA1ZjkyYzMzYWZjOWFiIn0%3D; expires=Sat, 06-Mar-2021 14:35:08 GMT; Max-Age=7200; path=/; httponly; samesite=lax

下記URLにアクセスすると、laravel.log へデバッグ用のログを出力します。
$ curl http://localhost/log_test
log test


laravel.log の内容を確認します。

$ tail -1 app/storage/logs/laravel.log
[2021-03-06 12:37:09] local.INFO: ログ出力テスト


Docker環境を停止します。

$ cd <InstallDir>/docker
$ make down


DockerHubのトークン作成


CI/CDでは、DockerHubからnginx, php-fpmのイメージをPullします。
DockerHubからPullできる回数には制限がありますが、匿名ユーザは、「6時間あたり100回」のようです。
今回、CodeBuildを使用していますが、匿名ユーザだと回数制限でPullがエラーになることがありました。
(詳しくは、下記URL参照)
なので、DockerHubの無料ユーザでを使うことにしました。
無料ユーザは「6時間あたり200回」のようです。

CodeBuildでは、DockerHubの無料アカウントでトークン作成して使用します。
「AccountSettings」からトークンを作成できます。



DockerHubのユーザIDとトークンは、AWSコンソールを使用してSSMパラメータストアに登録します。



CodestarConnectionの作成


今回、CodePipelineでBitbucketからソースを取得するのにCodestarConnectionを使用します。
AWSコンソールでCodestarConnection を作成します。
ARNを、後続のステップで使用します。



ECS及びCI/CD環境構築


ECSで使用するDockerイメージをECRにPushします。
$ cd <InstallDir>/docker
$ make push_base

ECRにPushしたイメージは下記のとおり。
imageTagに"poc"を設定しています。

$ aws ecr list-images --repository-name blue21/nginx --profile blue21
{
    "imageIds": [
        {
            "imageDigest": "sha256:c52d95ceaee7842700ea4449128f90afd7bb440372ac6a569ecf7160ee5523db",
            "imageTag": "local"
        },
        {
            "imageDigest": "sha256:c52d95ceaee7842700ea4449128f90afd7bb440372ac6a569ecf7160ee5523db",
            "imageTag": "poc"
        }
    ]
}
$ aws ecr list-images --repository-name blue21/php-fpm --profile blue21
{
    "imageIds": [
        {
            "imageDigest": "sha256:48d720bcb6bf55036ef10a00139231e510104951c3e30db376cc54764dee6da4",
            "imageTag": "poc"
        },
        {
            "imageDigest": "sha256:48d720bcb6bf55036ef10a00139231e510104951c3e30db376cc54764dee6da4",
            "imageTag": "local"
        }
    ]
}

SSMパラメータストアにパラメータをPutします。
$ cd <InstallDir>/aws/ssm
$ make ssmput_all

登録したパラメータは以下のとおり。
$ make ssmlist
aws ssm describe-parameters \
--profile blue21 \
--query Parameters[].Name --filters "Key=Name,Values=/blue21/"
[
    "/blue21/app/poc/.env",
    "/blue21/cwa/poc/config",
    "/blue21/nginx/poc/.htpasswd",
    "/blue21/nginx/poc/Dockerfile",
    "/blue21/nginx/poc/default.conf",
    "/blue21/php-fpm/poc/Dockerfile",
    "/blue21/php-fpm/poc/php.ini",
    "/blue21/php-fpm/poc/www.conf"
]

上記で作成したCodestarConnectionのARNを指定して、ECS環境とCICD環境を構築します。
$ cd <InstallDir>/aws/cfn
$ make -f Makefile.rain cfnstep2 RAINOPT="-y" CODESTAR="CodestarConnectionのArn"


作成したスタックは下記のとおり。

$ make -f Makefile.rain stackls
aws cloudformation describe-stacks \
--profile blue21 \
--query 'Stacks[?contains(StackName,`Blue21`)].{StackName:StackName,StackStatus:StackStatus,Desc:Description}' \
--output table
-------------------------------------------------------------------------------------
|                                  DescribeStacks                                   |
+----------------------------------------------+----------------+-------------------+
|                     Desc                     |   StackName    |    StackStatus    |
+----------------------------------------------+----------------+-------------------+
|  CodePipeline For ECS Fargate with Bitbucket |  Blue21CicdPoC |  CREATE_COMPLETE  |
|  Ecs service                                 |  Blue21SvcPoC  |  CREATE_COMPLETE  |
|  Ecs cluster                                 |  Blue21EcsPoC  |  CREATE_COMPLETE  |
|  ALB                                         |  Blue21AlbPoC  |  CREATE_COMPLETE  |
|  ECR                                         |  Blue21EcrPoC  |  CREATE_COMPLETE  |
|  VPC & subnet                                |  Blue21VpcPoC  |  CREATE_COMPLETE  |
+----------------------------------------------+----------------+-------------------+

CodePipeline構築が成功すると、CodePipelineが自動的に実行されます。



ECSタスク起動


初期状態は、ECSサービスの「必要数」が0です。

「必要数」を1に変更して、タスクを起動します。

まず、ECRに登録したイメージのタグを確認します。

$ aws ecr list-images --repository-name blue21/php-fpm --profile blue21
{
    "imageIds": [
        {
            "imageDigest": "sha256:73bf5ee98aebb6526c7c25f4a51080ba2a5c33e3761d5e37067c7ca186d9eb0a",
            "imageTag": "poc-1-7759e75"
        },
        {
            "imageDigest": "sha256:48d720bcb6bf55036ef10a00139231e510104951c3e30db376cc54764dee6da4",
            "imageTag": "poc"
        },
        {
            "imageDigest": "sha256:48d720bcb6bf55036ef10a00139231e510104951c3e30db376cc54764dee6da4",
            "imageTag": "local"
        }
    ]
}

poc, local はアプリケーションを含んでいないので、ECSでは使用できません。

上記例だと、"poc-1-7759e75"がECS用の最新です。

CodeBuildで作成したイメージのタグの命名規則は、「POC-<ビルド番号>-<コミットID>」となります。


下記のように「必要数」と「イメージタグ」を指定してCloudFormationを実行します。

$ make -f Makefile.rain cfnsvc00 ECS_COUNT=1 ECS_TAG=poc-1-7759e75


タスク起動に成功すると、AWSコンソールでは下図のように表示されます。


タスク起動に失敗すると、タスクの再起動を永遠に繰り返します。AWSコンソールで「必要数」を0に変更すれば、再起動を停止できます。


ECSのデモサイト確認


ECSで起動中のデモサイトにアクセスします。

まず、ALBのDNS名を確認します。


ブラウザで下記URLのデモサイトにアクセスします。
  • http://<ALBのDNS名>
BASIC認証でアクセス制限しているので下記IDとパスワードでログインします。
BASIC認証のIDとパスワードは、SSMパラメータストアで管理しています。
  • ユーザ:blue21
  • パスワード:test

ログイン後、下図のように表示されたら成功です。



ログ確認


ブラウザで下記URLのデモサイトにアクセスします。
  • http://<ALBのDNS名>/log_test


CloudWatchLogsの/blu21/app でログ出力を確認します。

下図のように表示されたら成功です。



CI/CD確認


mastarブランチで空コミットしてPushします。

$ cd <InstallDir>
$ git commit --allow-empty -m "cicd test"
$ git push


自動的にCodePipelineが実行されます。

CodePipelineの画面には、下図のように、上記のコミットIDが表示されます。


CodePipelineが正常終了後、ECSタスクを確認して、下図のようにCodepipelineと同じコミットIDが表示されたら成功です。