CDKパイプラインを用いたランダラのカナリア展開


このポストでは、我々は我々のラムダのカナリア配備を実行しなければなりません。我々は自動展開のためのCDKパイプラインを使用します。


Canaryは、アプリケーションをユーザーのサブセットに段階的に解放する展開戦略です.これは、ブラスト半径を制限し、失敗した場合に簡単にロールバックするために行われます.
我々が成し遂げたいことは、
  • CDKパイプラインを使用してAPI展開を自動化する
  • CodeDeployの展開グループを使用して、ラムダ別名を使用してカナリー展開を実行します.これは、現在の関数と前の関数バージョンの間の重み付けルーティングを行います.
  • ラムダとロールバックのいずれかのエラーをチェックするアラームを作成します.
  • すべてが動作するかどうかをチェックする単純なロードテストを実行します.
  • ここでは、右に飛び込むか、ウォークスルーと一緒に従う人のためのレポです.

    ラインズ17 / ラムダカナリー展開


    APIゲートウェイとラムダを使ったラムダ


    必要条件

  • CDK前提条件bootstrapping でAWS CLIを設定するdefault プロファイルを仮定した.
  • 我々の倉庫がGithubの上にあるので、我々は倉庫をフェッチするためにCodepipelineのためにアクセストークンを作成する必要があります.これを実行するには、repo and admin:repo_hook オプションをチェック.
  • 次に、このトークンを保存する秘密マネージャの秘密を作成する必要があります.

  • 私たちは前提条件に従っています.APIの作成に移りましょう.

    APIスタック


    このスタックには、APIゲートウェイREST APIが含まれます.また、必要なトラフィックを現在のバージョンから最新の展開バージョンに移行するCodeDeploy配備グループを追加します.
    何らかの理由で展開が誤っている場合、Codedeployは現在のバージョンにロールバックする必要があります.このために、我々は使用されますCloudWatch Alarms これは、ラムダが何らかのエラーを与え、アラームがアラーム状態にあるかどうかをチェックします.
    ラムダから始めましょう
    // lib/api-stack.ts
    
    const aliasName = 'stage'
    
    const handler = new Lambda(this, 'apiHandler')
    const stage = new lambda.Alias(this, 'apiHandlerStage', {
      aliasName,
      version: handler.currentVersion,
    })
    
    ラムダ関数を作成しますapiHandler という名前のエイリアスapiHandlerStage 現在のバージョンを指します.新しいバージョンを展開するとき、Codedeployは現在のバージョンと最新の展開バージョンの両方を指す別名を使用して重み付けルーティングを実行します.
    次に、残りのAPIを作成します.
    // lib/api-stack.ts
    
    const api = new apiGw.LambdaRestApi(this, 'restApi', {
      handler: stage,
      deployOptions: { stageName: 'staging' },
    })
    
    CDKは、きちんとした構造を提供しますLambdaRestApi ラムダプロキシ統合を使用して指定するラムダに到着するリクエストを自動的にルーティングします.ここで指定しましたstage これは実際にエイリアスです.
    重要なステップ、すなわちエラーの場合のロールバックのためのアラームを構成する.
    // lib/api-stack.ts
    
    const failureAlarm = new cw.Alarm(this, 'lambdaFailure', {
      alarmDescription: 'The latest deployment errors > 0',
      metric: new cw.Metric({
        metricName: 'Errors',
        namespace: 'AWS/Lambda',
        statistic: 'sum',
        dimensionsMap: {
          Resource: `${handler.functionName}:${aliasName}`,
          FunctionName: handler.functionName,
        },
        period: cdk.Duration.minutes(1),
      }),
      threshold: 1,
      evaluationPeriods: 1,
    })
    
    これを壊しましょう.まず、このアラームの説明を作成しますlambdaFailure .
    それから、我々は、我々がアラームを反応させたいメートル法を指定します.ここでメトリックという名前のAWSですErrors の下にAWS/Lambda 名前空間.
    我々は、我々が指定するように、エラーの総数を観察したいですsum 統計として.この統計を適用する時間を指定するperiod そして、我々はそれを1分にセットしました.
    我々が指定する必要がある次元はFunctionName すなわち、ラムダ関数名Resource この場合、ラムダエイリアス名となります.エイリアス名は常にfunctionName:aliasName . 我々は、特にこの機能のエラーメトリックを見ています.
    次に、threshold 単純な用語では、アラームがアラーム状態に入る前に、どのように多くのエラーが発生するかを意味します.たとえ我々が1つのエラーに遭遇したとしても、我々はこの場合、アラームを誘発したいです.
    最後にevaluationPeriods これは、統計が閾値と比較される期間の数です.我々が欲しいものが1に1を設定したので、ラムダエラーが1回以上のとき、1分の期間で警報を引き起こすことになっています.
    我々はアラームを作成しました、現在我々の配備グループでこれを使いましょう.
    // lib/api-stack.ts
    
    new cd.LambdaDeploymentGroup(this, 'canaryDeployment', {
      alias: stage,
      deploymentConfig: cd.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES,
      alarms: [failureAlarm],
    })
    
    我々は、ラムダエイリアスと5分で10 %のカナリア展開を指定するCodeDeploy展開グループを作成します.
    それで、最初の5分の間、我々は現在のラムダバージョンの90 %と新しく配備されたラムダバージョンの10 %を提供しています.5分後、全体のトラフィックが新たに展開ラムダバージョンにシフトされ、それが現在のバージョンになります.また、2011年に作成されたアラームを提供しましたalarms . つ以上のアラームを指定することができます.
    最後に、ラムダ関数を見てみましょう.
    // functions/apiHandler.ts
    
    import { ProxyHandler } from 'aws-lambda'
    
    export const handler: ProxyHandler = async (event) => {
      return {
        body: JSON.stringify({
          message: 'API version 1 has been deployed!',
          path: event.path,
        }),
        headers: { 'Content-Type': 'application/json' },
        statusCode: 200,
      }
    }
    
    これはメッセージで200を返す単純なラムダ関数です.それでは、APIを展開するパイプラインのステージを作成しましょう.

    ステージスタック


    パイプラインのアプリケーションステージを定義する必要があります.パイプラインは、dev、stage、および生産のような複数のステージを持つことができます.この場合、ステージングステージを定義します.
    // lib/stages.ts
    
    import * as cdk from '@aws-cdk/core'
    import { ApiStack } from './api-stack'
    
    export class StagingStage extends cdk.Stage {
      constructor(scope: cdk.Construct, id: string, props?: cdk.StageProps) {
        super(scope, id, props)
    
        new ApiStack(this, 'ApiStackStaging')
      }
    }
    
    新しいステージを作りますStagingStage のインスタンスを作成しますApiStack はい.この段階はAPIとラムダ関数をブートストラップし、私たちのパイプラインでこのステージを使用します.

    CDKパイプライン


    レポ、アーティファクト、シンセステップのための値を含むCDKパイプラインを作成することから始めましょう.
    // lib/pipeline-stack.ts
    
    const sourceArtifact = new codepipeline.Artifact()
    const cloudAssemblyArtifact = new codepipeline.Artifact()
    
    const pipeline = new pipelines.CdkPipeline(this, 'deployApi', {
      cloudAssemblyArtifact,
      sourceAction: new codepipelineActions.GitHubSourceAction({
        actionName: 'GH',
        output: sourceArtifact,
        oauthToken: cdk.SecretValue.secretsManager('github-token'),
        owner: 'ryands17',
        repo: 'lambda-canary-deployments',
        branch: 'main',
      }),
      synthAction: pipelines.SimpleSynthAction.standardYarnSynth({
        cloudAssemblyArtifact,
        sourceArtifact,
      }),
    })
    
    これを壊しましょう
  • まず、S 3に格納されるアーティファクトを持っています.
  • 次に、GitHubSourceAction 上記でsourceArtifact , oAuthToken 我々は、前提条件として作成された、repo、所有者、および支店codeipelineからプルされます.
  • 最後に、synthアクションを指定します.ここでCDKは自動的にstandardYarnSynth これは依存関係をインストールし、synth 対応するCloudformationテンプレートを作成するコマンド.NPMを使用している場合は、使用する必要がありますstandardNpmSynth .
  • 上を進みましょうStaging このパイプラインへのステージ
    // lib/pipeline-stack.ts
    
    const stagingStage = new StagingStage(this, 'staging', {
      env: { region: process.env.region || 'us-east-2' },
    })
    
    pipeline.addApplicationStage(stagingStage)
    
    我々のインスタンスを作成しますStagingStage を使用してパイプラインに追加しますaddApplicationStage メソッド.これは、残りのAPIを展開しますApiStack ) それは我々が作成したStagingStage .

    アプリの配備


    私たちはその構文を使っています.では、アプリを配備しましょうyarn cdk deploy .
    注:あなたが私の代わりにあなた自身の倉庫を使用しているならば、あなたは最初にこのコードをあなたのRPOにプッシュして、それから実行する必要がありますyarn cdk deploy さもなければ、それはあなたの倉庫を見つけません.
    展開の後、我々は初めてパイプラインを走らせるのを見ることができます.

    これが完了したら、CloudFormationに頭を置き、スタックの出力セクションからAPIゲートウェイURLを取得します.

    これを開くと、我々はラムダから送信されたメッセージを正常に見ます.

    ラムダにメッセージを変えましょうAPI version 2 . コミットとプッシュを実行すると、CodePipelineはソースを自動的に取得し、パイプラインで継続することがわかります.

    ラムダ関数をチェックすると、エイリアスが現在のバージョンと新しく配備されたものに重み付きルーティングを実行していることがわかります.ブラウザでAPI URLを試してみると、両方のメッセージが表示されます.API version 1 and API version 2 を複数回リフレッシュします.

    ここでバージョン1は現在のバージョンですAPI version 1 ) そして、バージョン2は我々の新しく配備されたバージョンですAPI version 2 ).

    エラーがなかったので、展開グループは5分後に正常にトラフィックをシフトしたことがわかります.
    最後に、私たちのCloudWatchアラームをトリガすることを期待して、関数に明示的なエラーを追加することでエラーをシミュレートします.
    // functions/apiHandler.ts
    import { ProxyHandler } from 'aws-lambda'
    
    export const handler: ProxyHandler = async (event) => {
      if (Math.random() > 0.5) throw Error('an unexpected error occured!')
    
      return {
        body: JSON.stringify({
          message: 'API version 2 has been deployed!',
          path: event.path,
        }),
        headers: { 'Content-Type': 'application/json' },
        statusCode: 200,
      }
    }
    
    このコードを押すと、パイプラインが起動し、現在、ツールを使ってAPIをテストしますartillery .
    artillery quick -c 30 -n 100 -d 10 $API_URL
    

    あなたが見ることができるように、APIからの502の応答の多く.今、アラームをチェックしましょう.

    Voila!アラームは、ラムダの調整のためにトリガされます.codepipelineをチェックすると、展開が失敗したことがわかりますAPI version 2 戻る.再び我々のAPIが動くかどうか見るために、大砲を走らせましょう.


    そして、我々はすべて200を得る!厄介なエラーを修正し、メッセージを更新するコミットしましょうAPI version 3 . これは再びパイプラインとメッセージを実行しますAPI version 3 成功した展開の後に表示されます.

    カナリーでないとき


    私はカナリア展開が推奨されていない点について議論しました、そして、あなたがラムダ許可を更新している時です.
    この場合、我々は許可不一致がある状態を持ちたくないです、そして、これによる誤りは常に警報とロールバックを引き起こします.
    この場合、次のようにして、展開グループでCanaryを一度にすべて置き換えることができます.
    new cd.LambdaDeploymentGroup(this, 'canaryDeployment', {
      alias: stage,
      // deploymentConfig: cd.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES,
    
      deploymentConfig: cd.LambdaDeploymentConfig.ALL_AT_ONCE,
      alarms: [failureAlarm],
    })
    
    設定変更、すなわちIAM許可の変更があるたびに、ALL_AT_ONCE 配備と次の展開のためのカナリアへの切り替え.

    結論


    ここではまだそれをチェックしていない人のためのレポです.

    ラインズ17 / ラムダカナリー展開


    APIゲートウェイとラムダを使ったラムダ


    また、スタックを破壊することを忘れないでくださいyarn cdk destroy また、StagingStack Cloudformationコンソールから余分な料金が発生しません.
    そして、我々はしました!これを読んでのおかげで、私はコメントでこれについてのあなたの考えを聞くのが大好きだ!あなたがこのポストが好きであるならば、それを与えて、共有して、私について来てください.次回まで!