NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

インフラ編Part3 ~AWSで構築するCI/CD・監視・アラート通知~

こちらの記事の続きとなっております。

tech.nri-net.com

本シリーズのトップページはこちら!!

tech.nri-net.com

CI/CD(継続的インテグレーション・デリバリー)

CI/CDとは、アプリケーションやソフトウェアの変更をテストし、本番環境に適用するまでのプロセスを自動化する手法のことです。「自動化」といわれている通り、CI/CDはリリースの加速や生産性の向上等、多くのメリットをもたらすため、システム開発において重要な役割を果たしています。
本システムにおいても、機能追加や変更をしていきたいと考えているので、AWS CodePipelineを用いて簡単なCI/CDプロセスを構築していきます。

今回は、以下のような流れのパイプラインを構築していきます。

1. GitHubのryotaリポジトリ・mainブランチへのプッシュをトリガーとしてパイプライン起動

2. AWS CodeBuildでビルド作業を実施

3. ECRへのpushおよびECSへの展開

パイプラインの作成

では、パイプラインを構築していきましょう。
パイプラインはソースステージ、ビルドステージ、デプロイステージの3つで構成されます。それぞれのステージで以下の内容を設定していきます。

  • ソースステージ : GitHubとPipelineの接続
  • ビルドステージ : AWS CodeBuildでビルドプロジェクト作成
  • デプロイステージ : プッシュ、デプロイ先指定

ソースステージ

ソースステージでは、パイプラインをトリガーするリモートリポジトリの設定を実施します。

ソースプロバイダ(GitHubやBitBucket等のリポジトリサービス)、リポジトリ名、ブランチを設定していきます。

ビルドステージ

次に、AWS CodeBuildでビルドステージの設定を実施していきます。ビルドやテストの設定はAWS CodeBuildのビルドプロジェクト内で記述します。本章ではビルドプロジェクト設定項目のうち、Buildspecサービスロールについて解説していきます。

Buildspec

Buildspecは簡単に言うと、ビルド計画を設定する項目です。ビルド計画はbuildspec.ymlと呼ばれるYAML形式のファイルで記述していきます。

buildspec.ymlは基本的に次のような構成となっています。

  • ビルド前の処理 : (環境変数呼び出し、ECRへのログインなど)
  • ビルド(コンテナイメージの構築、コンテナイメージへのタグ付与)
  • ビルド後に行う作業(イメージのプッシュやタスク定義ファイルの作成など)
# buildspec.yml
version: 0.2

# ビルドの前準備
env:
  parameter-store:
    REPOSITORY_URI: REPOSITORY_URI
    ACCOUNT_ID: ACCOUNT_ID

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR... # ECRへのログイン
      - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com 
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) #イメージに付与するタグを作成
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Building the Docker image... #コンテナイメージの構築
      - docker build -t $REPOSITORY_URI:latest . 
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG # タグ付与
  post_build:
    commands:
      - echo Pushing the Docker images...  # ECRへのプッシュ
      - docker push $REPOSITORY_URI:latest 
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image definitions file... 
      - printf '[{"name":"soccer-info","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
    files: imagedefinitions.json

ビルド前はenv, pre_build、ビルド時はbuild、ビルド後はpost_buildといったように、buildspec.ymlでは各フェーズごとに実行するコマンドを記述していきます。

サービスロール

CodeBuildがビルドやテストを実行するためのロールで、ビルドプロジェクト構築時にデフォルトで作成されます。

このロールにはS3バケットやCloudWatchへのアクセス許可ポリシーがデフォルトで付与されています。
しかし、今回のビルドでECRへのプッシュ、Parameter Storeへのアクセスが生じるため、それぞれのサービスへアクセスを許可するポリシーをアタッチする必要があります。
ECRへのアクセス許可ポリシーは以下の通りです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ecr:CompleteLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:InitiateLayerUpload",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage"
            ],
            "Resource": "arn:aws:ecr:*:アカウントID:repository/*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ecr:GetAuthorizationToken",
            "Resource": "*"
        }
    ]
}

デプロイステージ

Build成果物のデプロイ先を指定します。今回はデプロイプロバイダーとしてAmazon ECSを選択し、デプロイ先のクラスター、サービスを指定します。

監視・アラート通知

完璧に設計できた!と思ってもシステムは障害や想定外の挙動をすることが普通です。障害が発生するのは仕方のないことですが、重要なのはそれをリアルタイムで検知し、迅速に対応することです。
AWSには監視やアラート機能を備えた多くのサービスがあるので、それらを活用して高可用性のシステムを構築していきたいと考えています!

ECS起動/停止バッチ処理監視

ECS起動・停止Lambda関数の実行状況をモニタリングし、Slackへ通知します。 今回はCloudWatch Logsでログに特定の文字列があった時、アラームを発報し、SNSを経由してSlackに通知していきます。

Amazon CloudWatch Logs

まず、CloudWatch Logs内のロググループでメトリクスフィルターを作成していきます。メトリクスフィルターとは、ログデータから特定の文字列をフィルタリングし、その結果をメトリクスとして数値化・可視化する機能です。

文章だけで説明しても分かりにくいと思うので、実際にメトリクスフィルターを作っていきましょう。今回は、「END」という文字列をフィルタリングするメトリクスフィルターを発行していきます。
まず、 ECS起動/停止バッチ処理監視のロググループを選択し、「メトリクスフィルターを作成」を押します。

続けて、フィルターパターンに検知文字列(今回は、End)を入力したのち、メトリクス名前空間 メトリクス名、メトリクス値(今回は1とします。あとで使うので覚えておきましょう)を設定します。

メトリクスフィルターの作成

Amazon CloudWatch Alarm

メトリクスフィルターの設定でフィルタリングの設定は完了しました。次はCloudWatch Alarmで「End」がログ中に現れた時、アラームを発報するように設定していきます。先ほど作成したメトリクスを選択し、「アラームを作成」を押します。

すると、アラームを発報する条件画面へと遷移します。先ほどのメトリクスフィルターの設定では、「End」という文字列が現れた際にメトリクス値が1になるように設定しました。そのため、アラームの閾値を1以下に設定するようにしましょう。この設定により、「ログに特定の文字列が現れた時にアラームが発報する」という要件を満たすことができます。

次に、アラームの通知先を指定していくのですが、こちらをAmazon SNSで作成していきます。

Amazon SNS

Amazon SNS (Simple Notification Service) とは、AWSが提供するフルマネージドのメッセージングサービスです。これにより、配信者から受信者へのメッセージ配信が簡単に行えます。

SNSはAmazon CloudWatch Alarmと統合されており、例えば、特定のメトリクスが閾値を超えた場合に、SNSトピックに通知を送信し、メールやSMS等でアラートを受け取ることができます。

snsトピック
トピックはFIFOスタンダードの二種類から選択できます。今回はメッセージ順序を考慮しないので、スタンダードを選択しました。

AWS ChatBot

SNSはメールやSMSに通知を送信することができますが、SlackやTeamsのチャットツールにアラートを通知したい場合、 AWS ChatBotを経由する必要があります。
先ほど作成したSNS トピックSlackの任意のチャンネルを設定することでLambda関数の実行ログをSlackで閲覧することができます。

ECSタスク状態変更イベントの通知

起動時間外の予期せぬタスク起動、及び起動・停止の正常な実施を検知するため、Eventbridgeルールを作成し、タスク状態変化イベント内容をSlackに通知していきます。

まず、イベントのターゲットに先ほど作成したSNSトピックを選択します。
次にイベントパターンの設定です。以下のように記述します。

{
  "source": ["aws.ecs"],
  "detail-type": ["ECS Task State Change"],
  "detail": {
    "lastStatus": ["RUNNING", "STOPPED"]
  }
}

このように設定することで、ECSタスクのlastStatusRUNNING, STOPPEDになった時、イベントがトリガーされます。そしてターゲット先であるSNSに通知が送信され、AWS ChatBotに紐づけられた Slackチャンネルへ転送することができます。

タスク異常終了イベントの検知

タスクの異常終了(exitcodeが0以外)場合、Slackに通知します。

イベントパターンは以下の通りです。

{
  "source": ["aws.ecs"],
  "detail-type": ["ECS Task State Change"],
  "detail": {
    "lastStatus": ["STOPPED"],
    "containers": {
      "exitCode": [
        {
          "anything-but": 0
        }
      ]
    }
  }
}

Slackへの通知画面は以下の通りです。

Lambda実行状況(左)とECS状態変化(右)

以上でインフラ編は終了です!!
明日からはPythonやDockerを使って本格的にアプリケーション開発に取り組んでいきます!

tech.nri-net.com

執筆者:井手 亮太
職種:インフラエンジニア
推しのサッカー選手:ケビン・デブライネ
執筆記事一覧:https://tech.nri-net.com/archive/author/r-ide-ryota