Amazon Web Services ブログ

Health Aware CI/CD パイプラインの構築

この記事は Islam Ghanim によって寄稿された Build Health Aware CI/CD Pipelines (記事公開日: 2022 年 6 月 19 日) を翻訳したものです。

Everything fails all the time — Werner Vogels, AWS CTO

障害が起こっているまさにその時に、間違ってデプロイしてしまうことを回避したいと思いませんか?ここでは、本投稿の趣旨を示す短いお話しから始めます。

DevOps チームは 30 分の停止を計画して、データベースのアップグレードを開始しました。チームはアップグレードのフロー全体を自動化し、手作業を必要としない CI/CD パイプラインを起動させ、アップグレードは順調に進んでいました。しかし、開始から 20 分後にパイプラインが止まってしまい、アップグレードが進まなくなってしまいました。メンテナンス期間は終了しましたが、お客様は取引を開始することができません。あなたはサポートケースを作成しました。AWS のエンジニアは us-west-2 リージョンで発生中の AWS Health のインシデントが原因でアップグレードが失敗していることを確認しました。AWS のエンジニアは DevOps チームに、status.aws.amazon.com のページを継続して監視し、インシデント解決に関する最新情報を得るように指示しました。インシデントは 3 時間続き、その間、お客さまは取引することができませんでした。インシデントが解決されると、DevOps チームは失敗したパイプラインを再試行し、正常に完了させることができました。

インシデントの後、DevOps チームは、今後、同じようなインシデントを回避するための可能性を検討しました。チームは、AWS Health へのアクセスを可能にする AWS Health API の存在を知りました。本投稿では、DevOps チームが AWS Health API を最大限に活用して、意図しない障害を未然に防ぐ方法を支援します。

AWS ではビジネスとエンタープライズサポートに加入しているお客様に、AWS Health API へのアクセスを提供しています。お客様は、サービスの利用に影響を与える可能性のある AWS インフラストラクチャの発生中のイベントにアクセスすることができます。インシデントは、リージョン別、AZ別、あるいはアカウント毎に別々に発生する可能性があります。このようなインシデントの間、イベントの影響を受けるサービスをデプロイしたり変更したりすることは推奨されません。

本投稿は、AWS Health API によるチェックを CI/CD パイプラインに組み込み、運用しているリージョンで AWS Health イベントが報告されたときに自動的にデプロイを停止する方法について説明します。さらに、検出と修復を自動化する方法についても紹介します。

デモ

このデモでは、AWS CodePipeline を使って、アイデアを実証していきます。ビルド、テスト、デプロイの詳細には触れずに、コンセプトを実証する簡単なパイプラインを構築します。

CodePipeline フロー

CodePipeline のフローは、次の 3 つのステップによって構成されます。

  1. ソースステージで、 AWS CodeCommit からCloudFormationのテンプレートをダウンロードします。テンプレートは最後のステージでデプロイされます。
  2. カスタムステージで、AWS Lambda 関数を呼び出して AWS Health を評価します。Lambda 関数は AWS Health API を呼び出してデプロイへの影響の有無を評価し、評価結果とともに CodePipeline をコールバックします。
  3. デプロイステージで、最初のステージで CodeCommit からダウンロードした CloudFormation テンプレートをデプロイします。

これがフロー図です。3つの要素が含まれています。AWS Code Pipeline、AWS Lambda、AWS Health API の 3 つです。まず、AWS Code Pipeline は Lambda 関数を非同期で呼び出します。2 つ目は、Lambda 関数が AWS Health API であるDescribeEvents を呼び出す。3 つ目は、DescribeEvents API がヘルスイベントのリストをレスポンスとして返します。最後に、Lambda 関数は PutJobSuccessResult と PutJobFailureResults を連続して呼び出すことで、成功レスポンスと失敗レスポンスのいずれかを返します。

図 1. CodePipeline ワークフロー

Lambda 関数による評価ロジック

Lambda 関数では、発生中の AWS Health イベントによってデプロイが影響を受ける可能性があるかどうかを評価します。安全にデプロイできると判断するためには、次の基準を満たす必要があります。

  • デプロイはバージニア北リージョンで行われるため、Lambda 関数は us-east-1 リージョンでフィルタリングする必要があります。
  • クローズされたイベントは評価する必要がありません。Lambda 関数は、open ステータスのイベントのみをフィルタリングします。
  • AWS Health API は、定期メンテナンス、アカウントと請求の通知など、関連性のないさまざまなイベントタイプを返すことができます。Lambda 関数では “Issue” タイプのイベントのみをフィルタリングします。

AWS Health API は、マルチリージョンのアプリケーションアーキテクチャで構成され、アクティブ/パッシブ構成で 2 つのリージョンのエンドポイントを持ちます。アクティブ/パッシブの DNS フェイルオーバーをサポートするために、AWS Health はグローバルエンドポイントを提供します。サンプルの Python コードは GitHub で公開されており、Lambda コードパッケージのビルド方法については README に詳細が記載されています。

Lambda 関数は、AWS Health API、CodePipelineへのアクセス、CloudWatchへのログ出力のために、以下のAWS Identity and Access Management (IAM) 権限を必要とします。

{
  "Version": "2012-10-17", 
  "Statement": [
    {
      "Action": [ 
        "logs:CreateLogStream",
        "logs:CreateLogGroup",
        "logs:PutLogEvents"
      ],
      "Effect": "Allow", 
      "Resource": "arn:aws:logs:us-east-1:replaceWithAccountNumber:*"
    },
    {
      "Action": [
        "codepipeline:PutJobSuccessResult",
        "codepipeline:PutJobFailureResult"
        ],
        "Effect": "Allow",
        "Resource": "*"
     },
     {
        "Effect": "Allow",
        "Action": "health:DescribeEvents",
        "Resource": "*"
    }
  ]
}

処理フロー

図 2. 処理フロー

CodePipeline で、Lambda 関数を非同期で呼び出すためのアクションを持つ新しいステージを作成します。この関数は、AWS Health DescribeEvents API を呼び出して、アクティブなヘルスインシデントのリストを取得します。次に、関数はイベント分析を完了し、実行中のデプロイに影響を与える可能性があるかどうかを判断します。最後に、この関数は PutJobSuccessResult またはPutJobFailureResult のいずれかの API によって評価結果と共に CodePipeline をコールバックします。

Lambda 関数の評価が成功した場合、PutJobSuccessResult API でパイプラインをコールバックします。その結果、パイプラインはそのステップを成功としてマークし、実行を完了します。

AWS Code Pipeline のワークフロー実行スナップショット。最初の Source ステップは、AWS CodeCommit からのソースコードのダウンロードが完了したことを意味します。2 つ目のステップは、AWSサービスのヘルスチェックに成功したことを意味します。

図 3. AWS Code Pipeline ワークフローが正常に実行されたケース

Lambda 関数の評価に失敗した場合、失敗メッセージを指定した PutJobFailureResult API でパイプラインを CodePipeline をコールバックします。イベントが解決されたことを DevOps チームが認識したら、Retry ボタンを押して、ヘルスステータスを再確認します。

AWS CodePipeline の実行スナップショット。最初の Source ステップは、AWS CodeCommit からのソースコードのダウンロードが完了したことを意味します。2 つ目の aws-health-check ステップでは、稼働中の AWS リージョンでヘルスイベント/インシデントが検出され、失敗していることがわかります。

図 4. AWS Code Pipeline ワークフローが失敗したケース

DevOps チームは、デプロイが失敗したことを認識する必要があります。ステージの実行に失敗したことを、関係者に通知するアラートを設定することをお勧めします。ステージの実行が失敗した場合、Slack にメッセージを投稿する通知ルールを作成します。詳しい手順は、通知ルールの作成 – AWS CodePipeline を参照してください。障害発生時に、AWS Chatbot によってSlackへの通知が行われます。

デプロイに失敗した場合に送信された通知を表示している Slack UIスナップショット。通知には AWS CodePipeline Notification というタイトルが表示されています。この通知は、aws-health-check ステージで 1 つのアクションが失敗したことを示しています。この通知は、失敗の理由が Incident In Progress であることも示しています。この通知には、失敗したステージ名だけでなく、Pipeline の名前も記載されています。

図 5. デプロイ失敗時の Slack 画面のスナップショット

さらに洗練された解決策は、通知を SNS トピックにプッシュし、そのトピックが Lambda 関数を呼び出して失敗したステージを再試行することです。Lambda 関数はパイプラインの失敗したステージを抽出し、CodePipelineのAPI である RetryStageExecution を呼び出します。

結論

発生中の AWS Health イベントと連動して、デプロイを進める際に関連するリスクを自動的に評価する方法を学習しました。次に、自動化により、デプロイを続行するか、意図しないダウンタイムを回避するためにデプロイを中断するかを決定します。これによって、アプリケーションの可用性が向上します。

この解決策は CodePipeline に限ったことではありません。このパターンは、DevOps チームが使用する他の CI/CD ツールにも適用することができます。

翻訳はプロフェッショナルサービスの水流が担当しました。原文はこちらです。