ECSデプロイ CodePipelineとGitHubを使った手動リリース実行


概要

ECSで以下のようなざっくり要件でデプロイ準備してみた記録です。

  • ECRにデプロイしたいタグイメージは事前に出来上がっている
  • リリース担当者が任意のタイミングで手動デプロイ開始できる
  • GitHubの特定ファイル更新でwebhookからデプロイ開始する形にもできる

出来上がりイメージ

ざっくりTerraform記載例

以下の環境で検証。
terraform v0.12.29
aws provider v2.69.0
github provider v2.9.2

ざっくり値はsampleになっていますが、イメージ的にはこんな感じ。


resource "aws_codepipeline" "sample" {
  name     = "sample"
  role_arn = "arn:aws:iam::XXXXXXXXXXXX:role/SampleRole"

  artifact_store {
    type     = "S3"
    location = "sample-bucket"
  }

  stage {
    name = "Source"

    action {
      name             = "Source"
      category         = "Source"
      owner            = "ThirdParty"
      provider         = "GitHub"
      version          = "1"
      output_artifacts = ["SourceArtifact"]
      namespace        = "SourceVariables"

      configuration = {
        Owner                = "sample"
        Repo                 = "sample-repo"
        Branch               = "master"
        OAuthToken           = aws_ssm_parameter.github_personal_access_token.value
        PollForSourceChanges = "false"
      }
    }
  }

  stage {
    name = "Deploy"

    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "CodeDeployToECS"
      version         = "1"
      input_artifacts = ["SourceArtifact"]
      namespace       = "DeployVariables"

      configuration = {
        AppSpecTemplateArtifact        = "SourceArtifact"
        AppSpecTemplatePath            = "appspec.yml"
        ApplicationName                = "AppECS-cluster-sample-service"
        DeploymentGroupName            = "DgpECS-cluster-sample-service"
        TaskDefinitionTemplateArtifact = "SourceArtifact"
        TaskDefinitionTemplatePath     = "taskdef.json"
      }
    }
  }

  lifecycle {
    ignore_changes = [
      stage[0].action[0].configuration
    ]
  }
}

resource "random_id" "sample" {
  keepers = {
    codepipeline_name = aws_codepipeline.sample.name
  }

  byte_length = 32
}

resource "aws_codepipeline_webhook" "sample" {
  name            = "sample-webhook"
  authentication  = "GITHUB_HMAC"
  target_action   = "Source"
  target_pipeline = aws_codepipeline.sample.name

  authentication_configuration {
    secret_token = random_id.sample.hex
  }

  filter {
    json_path    = "$.ref"
    match_equals = "refs/heads/{Branch}"
  }

  filter {
    json_path    = "$.head_commit.modified.*"
    match_equals = "placeholder-file"
  }
}

resource "github_repository_webhook" "sample" {
  repository = "sample-repo"

  configuration {
    url          = aws_codepipeline_webhook.sample.url
    content_type = "json"
    insecure_ssl = false
    secret       = random_id.sample.hex
  }

  events = ["push"]
}

webhook部分の制御

CodePipelineがGitHubからのwebhook情報を受け取って判定している箇所は以下部分。
AWSのドキュメントだと、Branch部分が指定されているfilter例しか載っていないけれど、GitHubからはwebhook時に他の情報もやってくるので、それを使って判定条件にする。


resource "aws_codepipeline_webhook" "sample" {

  filter {
    json_path    = "$.ref"
    match_equals = "refs/heads/{Branch}"
  }

  filter {
    json_path    = "$.head_commit.modified.*"
    match_equals = "placeholder-file"
  }
}

上記だと、masterブランチに変更があって、placeholder-fileというファイルに変更があった場合、CodePipeline側のデプロイが開始するような記述。

placeholder-fileが存在していなければ自動でデプロイは開始されないので手動開始が実現できる。
placeholder-filetaskdef.jsonに変えておけば、イメージを更新したタスク定義でデプロイが自動開始される。

match_equalsの部分、正規表現使えると嬉しいケースがありそうだけど現状使えない模様。
https://stackoverflow.com/questions/56767494/aws-codepipeline-webhook-filter
https://docs.aws.amazon.com/codepipeline/latest/APIReference/API_WebhookFilterRule.html

webhookのシークレット情報

GitHubとCodePipelineのwebhook連携で使われているシークレット情報、どういう文字列でいいのかな?と思ってGitHubのドキュメントを見てもあまり具体的に載っていなかったので、サンプルを踏襲してhex文字列を使ってみています。
https://docs.github.com/en/developers/webhooks-and-events/securing-your-webhooks


resource "random_id" "sample" {
  keepers = {
    codepipeline_name = aws_codepipeline.sample.name
  }

  byte_length = 32
}

CodePipelineに設定するGitHub Personal Access Tokenの制御

検証したバージョンのaws provider(v2.69.0)だと、OAuthToken部分の更新が毎回走ってしまうので、無理矢理感あるけど以下のように制御入れてある感じ。


  lifecycle {
    ignore_changes = [
      stage[0].action[0].configuration
    ]
  }

このissueで同じ課題持っているなーと思っていたら、aws provider v3.0.0で修正が入って、この記述を入れなくてもOAuthTokenの制御がうまくいくようになった。
https://github.com/terraform-providers/terraform-provider-aws/issues/2854

参考にさせて貰った情報

ECSデプロイについて全般については以下が参考になりました。
https://dev.classmethod.jp/articles/ecs-deploy-all/

GitHubとCodePipeleのwebhook挙動については以下が参考になりました。
https://qiita.com/techneconn/items/cff3e76301006f42c78f