🎄

Terraform Provider Mackerel を利用したメンテナンス表示自動化例

2022/12/18に公開

本記事は以下の18日目のエントリです。

今年は随所でだいぶ CircleCI に取り組んだ1年でした。
記事にはしてないのですが、コストを意識しつつ、ダイナミックコンフィグテストの並列分割インサイト、といったあたりを工夫する日々だったように思います。
今年前半で一旦プロトタイプ開発がひと区切りとなり引き渡してメンテナンスフェーズとなったプロジェクトがあります。こちらで CircleCI と Terraform Provider Mackerel、その他いろいろを組み込んで、自分史上最高にオペレーション自動化を実現できました。小規模ながら決まった名前でブランチを切り出せば staging へ[3]、それを main にマージすれば production へ、全自動デプロイができた、という感じです。こちらをご紹介します。何かの参考となれば幸いです。

メンテナンス表示の概要

  • デプロイ中は「メンテナンス表示」する
  • 表示するのは簡単なHTML。static な HTML を表示するのを Fargate で実現(S3でよかったのかも)
  • 処理の流れはだいたい次の通り
    • メンテナンス開始
      • メンテナンス表示 Fargate 起動
      • Mackerel の外形監視を mute にする
      • ALB リスナーの先頭(優先順位最高)に「メンテナンス表示する」ルールを terraform で apply
    • メンテナンス解除
      • ALB リスナーの先頭(優先順位最高)から「メンテナンス表示する」ルールを terraform destroy で 除去
      • Mackerel の外形監視の mute を解除
      • メンテナンス表示 Fargate 停止

.circleci/config.yml をご紹介

  • (抜粋です。これだけではちゃんと動かないかも。)
  • _ar は auto release の略です
  • 自作 Orb
    • sogaoh/orb-tfenv
      • tfenv をインストールする Orb。つくった時の記事↓

https://zenn.dev/sogaoh/articles/21-08-27-5f56bcf3a7348a

version,orbs,executers,parameters

(Header部(?))
.circleci/config.yml (1)

version: 2.1

orbs:
  aws-ecr: circleci/aws-ecr@6.15.3
  ecspresso: fujiwara/ecspresso@0.0.15
  tfenv: sogaoh/orb-tfenv@0.0.1
  slack : circleci/slack@3.4.2

executors:
  amzn2:
    docker:
      - image: XXXXXX/image-builder-amzn2:latest
        auth:
          username: $DOCKER_LOGIN
          password: $DOCKER_PWD

parameters:
  branch:
    type: string
    default: development
  suffix:
    type: string
    default: ""
  environment:
    type: string
    default: development
  # Job Trigger: execute by API v2
  run_deploy:
    type: boolean
    default: false

anchors,commands

  • 小分類レベルの使用している自動処理の定義です
    • なるべく細かめにしていて、失敗した時に問題箇所を特定しやすくしています
(サブルーチン群)
.circleci/config.yml (2)

anchors:
  - install_ecspresso: &install_ecspresso
      ecspresso/install:
        version: v1.7.13
  - terraform_version: &terraform_version
      TERRAFORM_VERSION: 1.1.7
  - install_terraform: &install_terraform
      run:
        name: Install terraform & Show Version
        command: |
          tfenv install ${TERRAFORM_VERSION}
          tfenv use ${TERRAFORM_VERSION}
          terraform --version
  - download_maintenance_envrc_ar: &download_maintenance_envrc_ar
      run:
        name: Download .envrc for ecs/maintenance
        command: |
          cd infrastructures/environments/<< parameters.environment >>/ecs/maintenance
          aws s3 cp s3://XXXXXX-envrc-store/environments/<< parameters.environment >>/ecs/.envrc ./.envrc
  - scale1_maintenance_ar: &scale1_maintenance_ar
      run:
        name: Maintenance Service set desiredCount 1
        command: |
          cd infrastructures/environments/<< parameters.environment >>/ecs/maintenance
          direnv allow . && eval "$(direnv export bash)"
          make verify
          make scale1
  - scale0_maintenance_ar: &scale0_maintenance_ar
      run:
        name: Maintenance Service set desiredCount 0
        command: |
          cd infrastructures/environments/<< parameters.environment >>/ecs/maintenance
          direnv allow . && eval "$(direnv export bash)"
          make verify
          make scale0
- download_monitor_mute_on_off_tfvars_ar: &download_monitor_mute_on_off_tfvars_ar
      run:
        name: Download terraform.tfvars for monitor mute on/off
        command: |
          cd surroundings/mackerel/external-monitor/<< parameters.environment >>
          aws s3 cp s3://XXXXXX-tf-store/mackerel/external-monitor/terraform.tfvars ./terraform.tfvars
  - change_monitor_mute_on_ar: &change_monitor_mute_on_ar
      run:
        name: Change External Monitor is_mute ON
        command: |
          cd surroundings/mackerel/external-monitor/<< parameters.environment >>
          terraform init
          terraform plan -var 'is_mute=true'
          terraform apply -auto-approve -var 'is_mute=true'
  - change_monitor_mute_off_ar: &change_monitor_mute_off_ar
      run:
        name: Change External Monitor is_mute OFF
        command: |
          cd surroundings/mackerel/external-monitor/<< parameters.environment >>
          terraform init
          terraform plan -var 'is_mute=false'
          terraform apply -auto-approve -var 'is_mute=false'
  - download_maintenance_tfvars_ar: &download_maintenance_tfvars_ar
      run:
        name: Download terraform.tfvars for alb_listener_rule_maintenance
        command: |
          cd infrastructures/environments/<< parameters.environment >>/alb_listener_rule_maintenance
          aws s3 cp s3://XXXXXX-tf-store/<< parameters.environment >>/alb_listener_rule_maintenance/terraform.tfvars ./terraform.tfvars
  - insert_alb_maintenance_rule_ar: &insert_alb_maintenance_rule_ar
      run:
        name: Insert Maintenance ALB Listener Rule
        command: |
          cd infrastructures/environments/<< parameters.environment >>/alb_listener_rule_maintenance
          terraform init
          terraform plan
          terraform apply -auto-approve
  - delete_alb_maintenance_rule_ar: &delete_alb_maintenance_rule_ar
      run:
        name: Delete Maintenance ALB Listener Rule
        command: |
          cd infrastructures/environments/<< parameters.environment >>/alb_listener_rule_maintenance
          terraform init
          terraform plan -destroy
          terraform apply -destroy -auto-approve
  - aws_ecr_login: &aws_ecr_login
      aws-ecr/ecr-login:
        account-url: AWS_ECR_ACCOUNT_URL
        aws-access-key-id: AWS_ACCESS_KEY_ID
        aws-secret-access-key: AWS_SECRET_ACCESS_KEY
        region: AWS_DEFAULT_REGION
  - download_ecs_service_envrc_ar: &download_ecs_service_envrc_ar
      run:
        name: Download .envrc for ecs/sodachi2
        command: |
          cd infrastructures/environments/<< parameters.environment >>/ecs/sodachi2
          aws s3 cp s3://XXXXXX-envrc-store/environments/<< parameters.environment >>/ecs/.envrc ./.envrc
  - ecspresso_deploy_ar: &ecspresso_deploy_ar
      run:
        name: ECS Deploy
        command: |
          cd infrastructures/environments/<< parameters.environment >>/ecs/sodachi2
          direnv allow . && eval "$(direnv export bash)"
          make verify
          make dry-deploy
          make deploy
          make dereg
  - download_one_time_task_envrc_ar: &download_one_time_task_envrc_ar
      run:
        name: Download .envrc for ecs/one-time-task
        command: |
          cd infrastructures/environments/<< parameters.environment >>/ecs/one-time-task
          aws s3 cp s3://XXXXXX-envrc-store/environments/<< parameters.environment >>/ecs/.envrc ./.envrc
  - db_migrate_ar: &db_migrate_ar
      run:
        name: Database Migration
        command: |
          cd infrastructures/environments/<< parameters.environment >>/ecs/one-time-task
          direnv allow . && eval "$(direnv export bash)"
          make verify
          make mg-run
          make dereg

commands:
  start-notify:
    steps:
      - slack/notify:
          title: "${MARK_S}"
          color: '#FFC300'
          message: "\n
          :neutral_face: ${CIRCLE_USERNAME}  :evergreen_tree: ${CIRCLE_BRANCH} \n
          Job: ${CIRCLE_JOB}  \n"
          webhook: "${SLACK_ENDPOINT}"
  end-notify:
    steps:
      - slack/status:
          fail_only: true
          failure_message: "\n
          :neutral_face: ${CIRCLE_USERNAME}  :evergreen_tree: ${CIRCLE_BRANCH} \n
          Workflow: https://circleci.com/workflow-run/${CIRCLE_WORKFLOW_ID} \n
          Job: ${CIRCLE_JOB} \n
          Build URL: ${CIRCLE_BUILD_URL} \n"
          webhook: "${SLACK_ENDPOINT}"
      - slack/notify:
          title: "${MARK_E}"
          color: '#42f486'
          message: "\n
          :neutral_face: ${CIRCLE_USERNAME}  :evergreen_tree: ${CIRCLE_BRANCH} \n
          Workflow: https://circleci.com/workflow-run/${CIRCLE_WORKFLOW_ID} \n
          Job: ${CIRCLE_JOB}  \n
          Build URL: ${CIRCLE_BUILD_URL} \n"
          webhook: "${SLACK_ENDPOINT}"

jobs

  • 中分類レベルの処理の流れ(steps)の定義です
    • 流れを概観できるレベルにしているつもりです
(job:Executerに何を使って、変数の定義が何で(environments,parameters)、何をするか(steps))
.circleci/config.yml (3)

jobs:
  auto-begin-maintenance-prod:
    executor:
      name: amzn2
    environment:
      <<: *terraform_version
      MARK_S: ":earth_asia:  ENV : << parameters.environment >>"
      MARK_E: ":hammer_and_wrench:  ENV : << parameters.environment >>"
    parameters:
      environment:
        type: string
    steps:
      - start-notify
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - <<: *install_ecspresso
      - <<: *download_maintenance_envrc_ar
      - <<: *scale1_maintenance_ar
      - tfenv/install
      - <<: *install_terraform
      - <<: *download_monitor_mute_on_off_tfvars_ar
      - <<: *change_monitor_mute_on_ar
      - <<: *download_maintenance_tfvars_ar
      - <<: *insert_alb_maintenance_rule_ar
      - end-notify
  auto-finish-maintenance-prod:
    executor:
      name: amzn2
    environment:
      <<: *terraform_version
      MARK_S: ":hammer_and_wrench:  ENV : << parameters.environment >>"
      MARK_E: ":earth_asia:  ENV : << parameters.environment >>"
    parameters:
      environment:
        type: string
    steps:
      - start-notify
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - tfenv/install
      - <<: *install_terraform
      - <<: *download_maintenance_tfvars_ar
      - <<: *delete_alb_maintenance_rule_ar
      - <<: *download_monitor_mute_on_off_tfvars_ar
      - <<: *change_monitor_mute_off_ar
      - <<: *install_ecspresso
      - <<: *download_maintenance_envrc_ar
      - <<: *scale0_maintenance_ar
      - end-notify
  auto-release-prod:
    executor:
      name: amzn2
    environment:
      MARK_S: ":truck:  ENV : << parameters.environment >>"
      MARK_E: ":checkered_flag:  ENV : << parameters.environment >>"
      <<: *node_version
    parameters:
      branch:
        type: string
      suffix:
        type: string
      environment:
        type: string
    steps:
      - start-notify
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - <<: *download_environment_envrc_ar
      - <<: *vue_js_build
      - <<: *docker_image_build_ar
      - <<: *aws_ecr_login
      - <<: *aws_ecr_push_ar
      - <<: *install_ecspresso
      - <<: *download_ecs_service_envrc_ar
      - <<: *ecspresso_deploy_ar
      - end-notify
  auto-migrate-prod:
    executor:
      name: amzn2
    environment:
      MARK_S: ":seedling:  ENV : << parameters.environment >>"
      MARK_E: ":checkered_flag:  ENV : << parameters.environment >>"
    parameters:
      branch:
        type: string
      environment:
        type: string
    steps:
      - start-notify
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - <<: *install_ecspresso
      - <<: *download_one_time_task_envrc_ar
      - <<: *db_migrate_ar
      - end-notify

workflows

  • 大分類レベルの処理の流れ(jobs)の定義です
    • 起動条件の指定はここに集約するように自分はしています
(workflows:どのjobをどういう条件のときに(when,filters,requires,...)どういう流れで実行するか)
.circleci/config.yml (4)

workflows:
  version: 2
  auto-release-production:
    when:
      and:
        - equal: [false, << pipeline.parameters.run_deploy >>]
    jobs:
      - auto-begin-maintenance-prod:
          environment: production
          filters:
            branches:
              only:
                - main
      - auto-release-prod:
          branch: main
          suffix: ""
          environment: production
          requires:
            - auto-begin-maintenance-prod
      - auto-migrate-prod:
          branch: main
          environment: production
          requires:
            - auto-release-prod
      - auto-finish-maintenance-prod:
          environment: production
          requires:
            - auto-migrate-prod

Terraform Provider Mackerel の利用

https://registry.terraform.io/providers/mackerelio-labs/mackerel/latest/docs

https://github.com/mackerelio-labs/terraform-provider-mackerel

当初利用し始めた頃は 0.0.6 くらいだった Terraform Provider Mackerel、現在は 0.2.0 となっており、AWSインテグレーションも IaC で設定できるように進化してます。
まだ自身はこのあたり利用を深められていないのですが、上手に使ってマイクロホストメトリック数を 30 以内に収めることもちゃんと定義できそうで、機会が巡ってきたらしっかりと定義していこうと思っています。

https://mackerel.io/ja/docs/entry/integrations/aws

https://ja.mackerel.io/pricing#plan-limits

https://registry.terraform.io/providers/mackerelio-labs/mackerel/latest/docs/data-sources/aws_integration#excluded_metrics

おまけで、昨年の自身のアドベントカレンダー記事を置いておきます。
最近は一般的になりつつあると思っていますが、IaC で監視設定を定義する際の参考にしていただければ幸いです。

https://zenn.dev/sogaoh/articles/21-12-19-fb58235a2fbb6c

明日19日目の予定は以下になっています。お楽しみに。

脚注
  1. 17日目は Kidapan さんの Mackerelの新機能を振り返ってみた~2022年版~ でした。 ↩︎

  2. 17日目は ・・・12/17 20:00 時点で未定です。 ↩︎

  3. staging へのデプロイに関してはこの記事で紹介してません。production のと同じ内容です。 ↩︎

Discussion