継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

CodePipeline と CodeBuild で Git のタグを使ったリリースを行う

タダです.

下記の画像のような CodePipeline と CodeBuild でパイプラインを作っていた時に GitHub で登録した最新のタグでリリースを行いたいといった要望があったので試してみた内容をこの記事にまとめていきます.

f:id:sadayoshi_tada:20210715051202p:plain

前提

冒頭の話について今回の設定と関連する部分を補足します.CodePipelineの設定は当初下記の画像のようなデフォルト設定でGitHub(バージョン2)の接続がされている状況でした.

f:id:sadayoshi_tada:20210715051410p:plain f:id:sadayoshi_tada:20210715051427p:plain

また,CodeBuild の IAM ロールは下記のデフォルト設定が入っている状態だったとします.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:logs:ap-northeast-1:xxxxxxxxxxxx:log-group:/aws/codebuild/xxxx",
                "arn:aws:logs:ap-northeast-1:xxxxxxxxxxxx:log-group:/aws/codebuild/xxxx:*"
            ],
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::codepipeline-ap-northeast-1-*"
            ],
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:GetBucketAcl",
                "s3:GetBucketLocation"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "codebuild:CreateReportGroup",
                "codebuild:CreateReport",
                "codebuild:UpdateReport",
                "codebuild:BatchPutTestCases",
                "codebuild:BatchPutCodeCoverages"
            ],
            "Resource": [
                "arn:aws:codebuild:ap-northeast-1:xxxxxxxxxxxx:report-group/xxxx*"
            ]
        }
    ]
}

CodeBuild のタグリリースに関するコマンド追加

ビルドをする段階で最新のタグをチェックアウトしたいのでコマンドをbuildspec.ymlに追加します.buildフェーズにgit checkout $(git describe --tags --abbrev=0)というコマンドを追加しています.

version: 0.2
env:
  git-credential-helper: yes
~中略~
  build:
    commands:
      - git checkout $(git describe --tags --abbrev=0)
~中略~

タグとしては次のようなものが設定されていた場合,git describe --tags --abbrev=0を実行すると最新のv1.6が選ばれるのでこのコマンドの実行結果を使ってチェックアウトしています.

% git tag
v1.0
v1.1
v1.2
v1.3
v1.4
v1.5
v1.6
% git describe --tags --abbrev=0
v1.6

関連情報

docs.aws.amazon.com

CodePipeline の設定を変更

git describe --tags --abbrev=0を実行するために Git メタデータを取得する必要があり CodePipeline の設定で完全クローンを指定します.

f:id:sadayoshi_tada:20210716094545p:plain

CodePipeline で GitHub ソースアクションの完全なクローンオプションを選択できます。パイプラインビルドアクションで Git メタデータの CodeBuild コマンドを実行するには、このオプションを使用します。

CodeBuild の IAM ロール の設定変更

GitHub(バージョン2)を使っていてかつ CodePipeline の完全クローンオプションを使う時そのままリリースしたいブランチでコードをあげたところ Command did not exit successfully git checkout $(git describe --tags --abbrev=0) exit status 128というエラーがでました.

[Container] 2021/07/02 12:02:44 Command did not exit successfully git checkout $(git describe --tags --abbrev=0) exit status 128
[Container] 2021/07/02 12:02:44 Phase complete: BUILD State: FAILED
[Container] 2021/07/02 12:02:44 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: git checkout $(git describe --tags --abbrev=0). Reason: exit status 128

ドキュメント を確認したところ codestar-connections の権限がなかったので CodeBuild の IAM ロールを変更しています.

CodeBuild サービスのロールは、接続を使用するアクセス許可で更新する必要があるため、最初のパイプライン実行は失敗します。codestar-connections:UseConnectionサービスロールポリシーに対する IAM アクセス許可を追加。

IAM ロール変更後,再度パイプラインを動かしたところ意図したタグでチェックアウトができました.

[Container] 2021/07/14 00:13:19 Running command git checkout $(git describe --tags --abbrev=0)
Previous HEAD position was xxxx Merge pull request #4 from tasogare0919/xxxx
HEAD is now at xxxx v1.6

まとめ

CodePipeline と CodeBuild で最新の Git タグによるリリースを行うために行った設定をまとめていきました.タグリリースを使えるようになると万が一過去のバージョンに戻したい時に戻しやすくなるし開発方針に合えば使うのを検討したいですね.

ECS Fargate の AutoScaling 設定に関するコマンドをまとめておく

タダです.

AWS CLI で ECS Fargate の AutoScaling 設定をしているのですが,都度調べていたりしたのでよく使うコマンドをまとめておきます.

AutoScaling ポリシーを定義・更新する

AutoScaling ポリシーを定義・更新する時のコマンドとしてput-scaling-policyを使います.ECS の AutoScaling は CPU,メモリ,ターゲットグループごとのリクエスト数を追跡してスケーリングのトリガーにできます.下記のはターゲットグループごとのリクエスト数をベースにしています.そして,AutoScaling のポリシーを参照するには describe-scaling-policies を使い,誤って作ってしまったポリシーを削除するには delete-scaling-policyを使います.

※定義・更新
aws application-autoscaling put-scaling-policy \
--service-namespace ecs --scalable-dimension ecs:service:DesiredCount \
--resource-id service/クラスター名/サービス名 \
--policy-name ポリシー名 \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration file://config.json

※参照
aws application-autoscaling describe-scaling-policies --service-namespace ecs

※削除
aws application-autoscaling delete-scaling-policy --policy-name ポリシー名 --scalable-dimension ecs:service:DesiredCount --resource-id service/クラスター名/サービス名 --service-namespace ecs

なお,put-scaling-policyで指定しているconfig.jsonは次のような定義をいれます.

{
     "TargetValue": 1000.0,
     "PredefinedMetricSpecification": {
         "PredefinedMetricType": "ALBRequestCountPerTarget",
         "ResourceLabel": "app/ELB 名/xxx/targetgroup/ターゲットグループ名/xxx"
     },
     "ScaleOutCooldown": 60,
    "ScaleInCooldown": 60
}

関連情報

awscli.amazonaws.com

awscli.amazonaws.com

awscli.amazonaws.com

スケジューリングで AutoScaling のスケールアウト・スケールインを設定する

AutoScaling をスケジュール設定を入れたい場合があります.特定のイベントやメディア露出が分かっているような時に予めコンテナを増やしたいと言った要求向けの作業です.スケールアウトさせたい時間とスケールインさせたい時間でput-scheduled-actionを使ってMinCapacityの値を変更するスケジュールを登録しておく設定になっています.なお,cronで指定する時間は UTC 時間です.スケーリングの設定の参照はdescribe-scheduled-actionsを使い,削除にはdelete-scheduled-actionを使います.

※スケールアウト
aws application-autoscaling put-scheduled-action \
--service-namespace ecs \
--resource-id service/クラスター名/サービス名 \
--scheduled-action-name スケジューリングの名前 \
--schedule "cron(00 10 ? * * *)" \
--scalable-dimension ecs:service:DesiredCount \
--scalable-target-action MinCapacity=4,MaxCapacity=10

※スケールイン
aws application-autoscaling put-scheduled-action \
--service-namespace ecs \
--resource-id service/クラスター名/サービス名 \
--scheduled-action-name スケジューリングの名前 \
--schedule "cron(00 13 ? * * *)" \
--scalable-dimension ecs:service:DesiredCount \
--scalable-target-action MinCapacity=2,MaxCapacity=10

※スケジュールアクションの参照
aws application-autoscaling describe-scheduled-actions --service-namespace ecs

※削除
aws application-autoscaling delete-scheduled-action \
    --service-namespace ecs \
    --scalable-dimension ecs:service:DesiredCount \
    --resource-id service/クラスター名/サービス名 \
    --scheduled-action-name スケジューリングの名前

関連情報

awscli.amazonaws.com

awscli.amazonaws.com

awscli.amazonaws.com

まとめ

小ネタですが,ECS Fargate の AutoScaling 設定に関するコマンドについて都度調べていたのをまとめておきたくて記事にしました.

ECS Fatgate の FastAPI 関連プロセスを Mackerel で監視する

タダです.

前回の記事で Mackerel のカスタムプラグインを使った監視をやってみたんですが,EC2 の Uvicorn プロセス数の取得とプロセスの監視をしてみました.今回は前回のカスタムプラグインを使って ECS Fargate 上のプロセス監視をいれていきます.

sadayoshi-tada.hatenablog.com

監視のための準備

1, アプリケーション のコンテナ側での設定

FastAPI を動かすコンテナ側で Mackerel の監視設定とカスタムプラグインスクリプトを追加していきます.ディレクトリとして mackerelを追加してそこに設定ファイルとスクリプトを入れていきます.

.
├── app ・・・アプリケーション のコードが格納されているディレクトリ 
├── mackerel ・・・Mackerel の設定及びカスタムプラグインのスクリプトが格納されているディレクトリ 
├── ecs_setting・・・タスク定義が格納されているディレクトリ 

まず設定ファイルとしてmackerel.yamlを作ります.この中でカスタムプラグインのどのスクリプト がメトリックの取得をし,プロセス監視をするのかを設定していきます.

apikey: "YOUR_MACKEREL_APIKEY"
plugin:
  metrics:
    uvicorn:
      command: sh /xxx/mackerel/uvicorn-ps-count.sh ・・・メトリック監視
  checks:
    check_uvicorn:
      command: sh /xxx/mackerel/uvicorn-ps-check.sh・・・プロセス監視
ignoreContainer: '\Amackerel-container-agent\z'

mackerel.io

次にスクリプトの配置ですが,次のスクリプト を配置し,実行権限を与えています.

uvicorn-ps-count.sh

#!/bin/sh
metric_name='uvicorn.process.counts'
metric=`ps aux |grep uvicorn | grep -v grep | wc -l`
date=`date +%s`

echo "${metric_name}\t${metric}\t${date}"

uvicorn-ps-check.sh

#!/bin/sh
count=`ps aux |grep uvicorn | grep -v grep | wc -l`
counts=`printf "%d\n" $count`
if [ $counts -ge (正常なプロセスの数) ]; then
  exit 0
else
  exit 2
fi

最後にアプリケーションコンテナの Dockerfile とタスク定義に追加の設定を行います.Dockerfile では Mackerel のサイドカーコンテナから設定ファイルやスクリプトにアクセスできるようにVOLUMEの設定をし,タスク定義には設定ファイルのパスとマウント設定を記載していきます.

Dockerfile の該当箇所抜粋

WORKDIR /xxx/
COPY . .
VOLUME /xxx/mackerel

タスク定義の該当箇所抜粋

    {
      "name": "mackerel-container-agent",
      "volumesFrom": [
        {
          "sourceContainer": "アプリケーションコンテナ名"
        }
      ],
      "environment": [
        {
          "value": "fargate",
          "name": "MACKEREL_CONTAINER_PLATFORM"
        },
        {
          "value": "/xxx/mackerel/mackerel.yaml",
          "name": "MACKEREL_AGENT_CONFIG"
        }
      ],
    }

関連記事

sadayoshi-tada.hatenablog.com

2, mackerel-container-agent のカスタム設定

今回で,mackerel-container-agentのコンテナから監視のシェルを叩いてもps: not foundが表示されていたため,ps コマンドをいれていきます.

2021/07/05 11:28:03 INFO <main> starting mackerel-container-agent (version:0.5.0, revision:e1a219c)
2021/07/05 11:28:03 INFO <agent> wait for the platform status to be running
2021/07/05 11:28:07 INFO <agent> start the agent: host id = xxxx, host name = xxxx
2021/07/05 11:28:07 INFO <metric> plugin fastapi (sh /xxx/mackerel/uvicorn-ps-count.sh): "/xxx/mackerel/uvicorn-ps-count.sh: 1: /xxx/mackerel/uvicorn-ps-count.sh: ps: not found\n"

mackerel-container-agent では公開されているコンテナイメージを使ったカスタマイズが可能です.mackerel-container-agentDebian ベースのため ps コマンド実行に必要な処理を Dockerfile に記述し,ECR に格納していきます.

hub.docker.com

Dockerfile は次の定義をして ECR に格納しました.

FROM mackerel/mackerel-container-agent:plugins
RUN apt update && apt install -y procps

コンテナ起動後の動作確認

コンテナを起動して意図したプロセス数の収集とプロセス監視ができているかを確認します.両方の設定が画面から確認できたのとプロセス監視はアラートが飛ぶことも確認できました.

プロセス数収集

f:id:sadayoshi_tada:20210707133143p:plain

プロセス監視

f:id:sadayoshi_tada:20210707134136p:plain f:id:sadayoshi_tada:20210707133405p:plain

まとめ

ECS Fargate でmackerel-container-agentにないプロセス監視をやってみました.今回の設定を確認していくにあたって Mackerel サポートの方に助けていただきました.ありがとうございました!同じように ECS Fargate 環境でのカスタムプラグインによる監視をされたい方の参考になれば嬉しいです!

Mackerel のカスタムプラグインを自作して FastAPI 関連プロセスを監視してみる

タダです.

Mackerel のプラグインには様々なものが既に用意されていますが,そのプラグインにないものがあれば作ってみようということで,FastAPI を動かすために必要な Uvicorn のプロセスを監視するプラグインを作ってみたのでこの記事でその模様をまとめます.

プラグインの種類

プラグインには2つの種類があり,メトリックプラグインとチェックプラグインがあります.前者はサーバーリソースの値をメトリック値を取得,送信するためのもので CPU やメモリなどが標準のサーバーのメトリックとしてありますが,標準のメトリック以外のものはカスタムメトリックとしてグラフに描画されます.後者はホスト上の特定プロセスの死活状況をチェックしたりするときに使い,メトリックプラグインのようにグラフの描画はないです.

メトリックプラグインとチェックプラグインを作る

それではメトリックプラグインとチェックプラグインを作っていきます.

メトリックプラグインを作る

Uvicorn のプロセスは uvicorn main:app --reload のように実行しても1プロセスしかあがらないようで,ドキュメント にも本番環境では gunicorn と一緒に使うことが推奨されているため下記のようなコマンドを実行したとします.

gunicorn example:app -w [ワーカー数] -k uvicorn.workers.UvicornWorker

www.uvicorn.org

メトリックプラグインのコードとして次のものを用意しました.メトリックプラグインのインターフェース仕様で {metric name}\t{metric value}\t{epoch seconds}というフォーマットで標準出力されることが求められているためスクリプトの最終行で echo -e "${metric_name}\t${metric}\t${date}" を行っている以外は大したことしてないかと思います.スクリプトの実行権限を chmod +x uvicorn-metrics.sh を与えることも忘れずにやりましょう.

uvicorn-metrics.s

#!/bin/sh

metric_name="uvicorn.test_metric.number"
metric=`ps aux |grep uvicorn | grep -v grep | wc -l`
date=`date +%s`

echo -e "${metric_name}\t${metric}\t${date}"

そして,/etc/mackerel-agent/mackerel-agent.conf にも下記の定義を追加して sudo systemctl restart mackerel-agent で再起動します.

[plugin.metrics.uvicorn]
command = "/xxx/xxx/uvicorn-metrics.sh"

数分待つと,メトリックのデータが送られてきてるのを確認できました.Uvicorn の8つのプロセスが記録されていることが確認できました.

f:id:sadayoshi_tada:20210630073845p:plain

チェックプラグインを作る

次にチェックプラグインをつくっていきますが,メトリックプラグイン同様にスクリプトとして次のようなものを用意しました.正常なプロセスの数はメトリックプラグインの記録された値として8を入れてその時は正常ですが,それ以外の値になったら Critical になるように設定しています(チェックプラグインの仕様はドキュメント に記載があります).スクリプトの実行権限を chmod +xuvicorn-ps-check.shで与えます.

uvicorn-ps-check.sh

#!/bin/sh

count=`ps aux |grep uvicorn | grep -v grep | wc -l`
if [ $count -eq 8(正常なプロセスの数) ]; then
  exit 0
else
  exit 2
fi

そして,/etc/mackerel-agent/mackerel-agent.conf にも下記の定義を追加して sudo systemctl restart mackerel-agent で再起動します.

[plugin.checks.uvicorn]
command = "/xxx/xxx/uvicorn-ps-check.sh"

数分待つと,Monitors のセクションに uvicorn が監視されていることを確認できました.

f:id:sadayoshi_tada:20210630074619p:plain

試しにプロセスを落としてアラートが出るかを確認してみます.

$ gunicorn main:app -w 8 -k uvicorn.workers.UvicornWorker
~中略~
[2021-06-29 22:20:44 +0000] [22251] [INFO] Waiting for application startup.
[2021-06-29 22:20:44 +0000] [22251] [INFO] Application startup complete.
^C[2021-06-29 22:47:40 +0000] [22246] [INFO] Handling signal: int
~中略~
[2021-06-29 22:47:40 +0000] [22246] [INFO] Shutting down: Master

Critical 扱いでアラートが出ることが確認できました.プロセスが落ちてもその状況をモニタリングできるようになりました.

f:id:sadayoshi_tada:20210630074922p:plain

まとめ

今回の開発で a-know (id:a-know)さんの記事を参考に Uvicorn のプロセス監視のプラグインを作っていきました.a-know さんありがとうございます! Go でも作っていきたいけどまずはさくっとシェルスクリプトプラグインを作れる体験を持てたのはとても大きかったです.こっからプラグイン開発に入り込んで行けたらなと思います.

blog.a-know.me

AWS Chatbot から Lambda の発火させて手動作業を自動化する

タダです.

業務で不定期ではあるけれど定型作業の依頼が非エンジニアから飛んでくる時があり,最初はすぐに済ませるため手作業で行っていたタスクを仕組み化しようと思い,AWS Chatbot を入れているので Chatbot と Lambda を使って作業を省力化したのでこの記事にまとめます.

作業の省力化のモチベーション

この取り組みのモチベーションとしては大きく2点あります.1つ目は定型作業が仕組み化できてないことです.もう1つは非エンジニアから依頼があったときにエンジニアに依頼するより非エンジニア側の作業で完結できるようにした方が効率的と考えたことです.

仕組みの検討

仕組みをどのようにしようかと考えたときにSlack でコミュニケーションをとっているため Slack で完結するようなものにしたいと思って Chatbot を使っていこうと考えました.Chatbot から Lambda を呼び出すことができるのでこの仕組みをつかっていきます.

docs.aws.amazon.com

Chatbot から Lambda を呼び出すための設定

Chatbot から Lambda を呼び出すための設定として①Chatbot の IAM ロール設定②Lambda のコードを準備します.

①Chatbot の IAM ロール設定

Chatbot の IAM ロールで Lambda を呼び出すための権限を足します.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:invokeAsync",
                "lambda:invokeFunction"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

docs.aws.amazon.com

②Lambda のコード

Lambda は CloudFront のキャッシュを無効化する処理をさせるための関数です.Slack から CloudFront の DistributionId が渡ってくるので event のパラメーターとしてとってます.

import json
import boto3
import logging
import time

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def lambda_handler(event, context):
    cf = boto3.client('cloudfront')
    distribution_id = event['distribution_id']
    logger.info('Creating invalidation')
    res = cf.create_invalidation(
        DistributionId=distribution_id,
        InvalidationBatch={
            'Paths': {
                'Quantity': 1,
                'Items': ['/*'],
            },
            'CallerReference': str(time.time())
            
        }
    )

    invalidation_id = res['Invalidation']['Id']
    logger.info('InvalidationId is %s', invalidation_id)

    logger.info('Execution complete')
    return "Complete."

以上で準備が完了です.

Slack から Lambda を発火させる

aws のボットを対象のチャンネルに招待して@aws invoke Lambda名 --payload {“distribution_id”: “DistributionId”}を実行します.すると,Would you like me to do so?とでるのでYesを押せば Lambda が発火します.StatusCodePayloadの部分も処理が完了していることを確認できました.CloudFront の画面でもログに出ている InvalidationId でキャッシュ無効化処理が完了していることも確認できたので完了です.

Slack の実行画面

f:id:sadayoshi_tada:20210626173703p:plain

Lambda の実行ログ

START RequestId: aef1bac9-a44b-4a8c-b07c-14554732243a Version: $LATEST
[INFO]  2021-06-14T03:23:52.584Z    aef1bac9-a44b-4a8c-b07c-14554732243a    Found credentials in environment variables.
[INFO]  2021-06-14T03:23:53.323Z    aef1bac9-a44b-4a8c-b07c-14554732243a    Creating invalidation
[INFO]  2021-06-14T03:23:54.304Z    aef1bac9-a44b-4a8c-b07c-14554732243a    InvalidationId is I1CA2NN7EAW1TR(CloudFront の InvalidationId)
[INFO]  2021-06-14T03:23:54.322Z    aef1bac9-a44b-4a8c-b07c-14554732243a    Function complete
END RequestId: aef1bac9-a44b-4a8c-b07c-14554732243a

CloudFront の Invalidations 画面

f:id:sadayoshi_tada:20210627105250p:plain

まとめ

Chatbot から Lambda を呼び出す処理を実装するためにやったことを整理しました.Slack でコミュニケーションをしている場合にオペレーション省力化の1つとしてこう言った手法が取れることをしれてよかったです.同じような課題を持っている方の参考になれば嬉しいです!