LoginSignup
5
0

More than 3 years have passed since last update.

Datadogのアラート設定をterraform化して複数オーガニゼーションで使い回す

Last updated at Posted at 2019-12-22

はじめに

本記事はDatadog Advent Calendar 2019の12/23(月)分の記事です。
別アドベントカレンダーで、Datadogを全社的に導入した時にやったことという記事も書いたので、見ていただけると幸いです。

導入背景

VISITSでは、GCPを使ったプロダクトが増えており、基本的になアーキテクチャは同じ場合が多いです。
各プロダクトは、DatadogのManaging Multiple-Organization機能を使用して、
オーガニゼーションを分けています。

マルチオーガニゼーションの例)
スクリーンショット 2019-10-24 14.38.23.png

この場合、各オーガニゼーションで設定するMonitors(以下、アラート)は、基本的に同じである場合が多く、
であればコード化して汎用性のあるものにしようと考え、terraform化しました。
Monitoring as Codeという言うらしいです。

結果

まず最初にterraform化して感じたメリデメをご紹介。

メリット

  • 使い回しができる。オーガニゼーションが増えても簡単にアラート設定が可能。(もちろん作りによります)
  • アラート再現性がある。(仮に私が退職しても誰でもアラート設定できる)
  • アラートの文言を統一できる。(仮に1人がアラート設定してても文言はバラバラになることが多い)
  • 各プロダクトで得られた知見を取り込むことで、横展開が可能となる。(このアラート設定は他のオーガニゼーションでも使えるね!等)
  • コード管理しているという満足感。(自己満)

デメリット

  • 1つのアラートを修正したら全オーガニゼーションに適用し直す必要がある。(差分が出てしまうので)
  • terraformを使える人があまりいないので、terraformのアップロードが属人化してしまう。(適用は簡単だが、更新はできない等)

設計思想

  • tfファイルは共通化して variables で各オーガニゼーションごとのパラメーターを調整する。
  • チームで運用することを想定して、tfstateファイルはGCSで管理。(事前にGCSバケットを作る必要があります)
  • 一旦 terraform planterraform applyのみにしぼる(Importとかできない)

ファイル構成


├── README.md
├── <プロダクト名>
│   ├── datadog.tfvars # プロダクトの毎の変数(パラメーター)を設定
│   ├── monitor_service_list.txt #アラート設定したいテンプレートを指定
│   ├── secrets.tfvars #Datadogの機密情報(API key等)を設定。gitignoreでGit管理対象外とする。
│   └── tmp #作業ディレクトリ。ここにtfファイルをコピーして適用
├── templates #各アラートのテンプレート
│   ├── apm.tf
│   ├── cloudfunction.tf
│   ├── cloudsql.tf
│   ├── gae.tf
│   ├── gae_log.tf
│   ├── gce.tf
│   ├── gce_docker_containers.tf
│   ├── gce_proces_nginx.tf
│   ├── lb_log.tf
│   ├── provider.tf
│   ├── synthetics.tf
│   ├── synthetics_vi.tf
│   ├── tfstate.tf
│   └── variables.tf
├── terraform.sh #terraformのラッパースクリプト

以下に各ファイル、ディレクトリの補足説明をします。

datadog.tfvars

プロダクトの毎のパラメーターを設定しています。
例えば、Slackのチャンネル名、GCPプロジェクトID、アラートの閾値、外形監視するURL等。

例)

## Slack Channel Name
slacke_channel = "<your slack channel>"

## GCP Project ID
gcp_project_id = "<your gcp project id>"

## Production ENV Name
env_name = "<your env>"

monitor_service_list.txt

templatesの中からアラート設定を適用するファイル名(.tfより前)を指定します。

gce
gce_proces_nginx
gae
gae_log
cloudsql
lb_log
synthetics
apm_ruby

secrets.tfvars

DatadogのAPI KeyやAPP Keyといった秘匿性のある情報を管理します。
このファイルは、.gitignoreでignoreしましょう。

datadog_api_key = "*************"
datadog_app_key = "*************"

tmp(ディレクトリ)

後述のterraformラッパースクリプトがこのディレクトリにtfファイルやvariablesをコピーしてapplyします。

各アラートのテンプレート

各サービスのごとのアラート設定ファイルをおきます。
以下、Datadog APM監視のSampleです。

# apm.tf

##########################################################
# 説明
# APMを監視するtfファイルです。
#
# 動作要件
#  以下のvariableを各プロダクトの `datadog.tfvars` に設定してください。
##########################################################

##############################
## variable
##############################
variable "apm_service_web" {}
variable "apm_service_db" {}
variable "apm_service_web_error_rate_critical" {}
variable "apm_service_db_error_rate_critical" {}
variable "apm_service_web_latency_critical" {}
variable "apm_service_db_latency_critical" {}
variable "apm_exclude_condition" {}
variable "env_name" {}



##############################
## APM web Error Rate 監視
##############################
resource "datadog_monitor" "apm_error_rate_web" {
  name               = "[APM] Service ${var.apm_service_web} has a high error rate"
  type               = "query alert"
  message            = <<EOF
 Service ${var.apm_service_web}のError RateがSLOを超過しています。
詳細を[Datadog Logs](https://app.datadoghq.com/logs)から確認してください。
@${var.slack_channel}

  EOF
  query              = "avg(last_10m):( avg:trace.rack.request.errors{service:${var.apm_service_web},env:${var.env_name}} / avg:trace.rack.request.hits{service:${var.apm_service_web},env:${var.env_name}} ) > ${var.apm_service_web_error_rate_critical}"
  escalation_message = ""
  thresholds = {
    critical = "${var.apm_service_web_error_rate_critical}"
  }
  notify_no_data      = false
  new_host_delay      = 300
  renotify_interval   = 0
  timeout_h           = 0
  include_tags        = true
  require_full_window = true
  notify_audit        = false
  tags                = []
}

##############################
## APM db Error Rate 監視
##############################
resource "datadog_monitor" "apm_error_rate_db" {
  name               = "[APM] Service ${var.apm_service_db} has a high error rate"
  type               = "query alert"
  message            = <<EOF
 Service ${var.apm_service_db}のError RateがSLOを超過しています。
詳細を[Datadog Logs](https://app.datadoghq.com/logs)から確認してください。
@${var.slack_channel}

  EOF
  query              = "avg(last_10m):( avg:trace.mysql2.query.errors{service:${var.apm_service_db},env:${var.env_name}} / avg:trace.mysql2.query.hits{service:${var.apm_service_db},env:${var.env_name}} ) > ${var.apm_service_db_error_rate_critical}"
  escalation_message = ""
  thresholds = {
    critical = "${var.apm_service_db_error_rate_critical}"
  }
  notify_no_data      = false
  new_host_delay      = 300
  renotify_interval   = 0
  timeout_h           = 0
  include_tags        = true
  require_full_window = false
  notify_audit        = false
  tags                = []
}

##############################
## APM web latency 監視
##############################
resource "datadog_monitor" "apm_request_latency_web" {
  name               = "[APM] Service ${var.apm_service_web} has a high p90 latency"
  type               = "metric alert"
  message            = <<EOF
 Service ${var.apm_service_web}のRequest LatencyがSLOを超過しています。
 詳細を[Datadog APM](https://app.datadoghq.com/apm/search)から確認してください。
@${var.slack_channel}

  EOF
  query              = "avg(last_10m):avg:trace.rack.request.duration.by.service.90p{service:${var.apm_service_web},env:${var.env_name}${var.apm_exclude_condition}} > ${var.apm_service_web_latency_critical}"
  escalation_message = ""
  thresholds = {
    critical = "${var.apm_service_web_latency_critical}"
  }
  notify_no_data      = false
  new_host_delay      = 300
  renotify_interval   = 0
  timeout_h           = 0
  include_tags        = true
  require_full_window = true
  notify_audit        = false
  tags                = []
}

##############################
## APM db latency 監視
##############################
resource "datadog_monitor" "apm_request_latency_db" {
  name               = "[APM] Service ${var.apm_service_db} has a high p90 latency"
  type               = "metric alert"
  message            = <<EOF
 Service ${var.apm_service_db}のRequest LatencyがSLOを超過しています。
 詳細を[Datadog APM](https://app.datadoghq.com/apm/search)から確認してください。
@${var.slack_channel}

  EOF
  query              = "avg(last_10m):avg:trace.mysql2.query.duration.by.service.90p{service:${var.apm_service_db},env:${var.env_name}} > ${var.apm_service_db_latency_critical}"
  escalation_message = ""
  thresholds = {
    critical = "${var.apm_service_db_latency_critical}"
  }
  notify_no_data      = false
  new_host_delay      = 300
  renotify_interval   = 0
  timeout_h           = 0
  include_tags        = true
  require_full_window = false
  notify_audit        = false
  tags                = []
}

terraform.sh

terraformのラッパースクリプトのサンプルです。
bash terraform <プロダクト名>といった感じで実行し、terraformをapplyします。(雑ですいません)

#!/bin/bash

set -u

# Check argument
if [ $# -ne 1 ]; then
  echo "実行するには1つの引数が必要です。" 1>&2
  echo "引数には各プロダクト名を指定してください。" 1>&2
  echo "例) bash terraform.sh <プロダクト名>" 1>&2
  exit 1
fi

PRODUCT_NAME=$1

ls -d ${PRODUCT_NAME} 1>/dev/null 2>/dev/null 
if [ $? -ne 0 ]; then
    echo "${PRODUCT_NAME}のディレクトリが存在しません。terraformを適用するプロダクト毎にディレクトリを作成してください。"
    exit 1
fi

# prepare
gsutil 1>/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
  echo "gsutilがインストールされていません。"
  exit 1
fi

gcloud version 1>/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
  echo "gcloudがインストールされていません。"
  exit 1
fi

ls -d ${PRODUCT_NAME}/tmp 1>/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
  mkdir ${PRODUCT_NAME}/tmp
else
  rm -rf ${PRODUCT_NAME}/tmp/*.tf
  rm -rf ${PRODUCT_NAME}/tmp/*.tfvars
fi

# setup gcp project
gcloud config set project visits-technologies-share
if [ $? -ne 0 ]; then
  echo "GCP <Your GCP Project Name> プロジェクトが設定出来ませんした。GCPで権限を確認してください。"
  exit 1
fi

# copy tf files
if [ -e ${PRODUCT_NAME}/monitor_service_list.txt ]; then
  while read line
  do
    echo "cp -pr templates/${line}.tf ${PRODUCT_NAME}/tmp/"
    cp -pr templates/${line}.tf ${PRODUCT_NAME}/tmp/
  done < ${PRODUCT_NAME}/monitor_service_list.txt
else
  echo "${PRODUCT_NAME}/monitor_service_list.txtが見つかりません。"
fi

## copy PRODUCT tfvars
cp -p ${PRODUCT_NAME}/*.tfvars ${PRODUCT_NAME}/tmp/
## copy common tf files
cp -p templates/provider.tf ${PRODUCT_NAME}/tmp/
cp -p templates/tfstate.tf ${PRODUCT_NAME}/tmp/
cp -p templates/variables.tf ${PRODUCT_NAME}/tmp/

# setup tfstate
gsutil ls gs://visits-${PRODUCT_NAME}-terraform-state
if [ $? -ne 0 ]; then
  echo "tfstate用のGCSバケットが作成されていません。tfstate.tfファイルを確認してください。"
  exit 1
fi

if [ -e ${PRODUCT_NAME}/tmp/tfstate.tf ]; then
  sed -i '' "s/__PRODUCT_NAME__/${PRODUCT_NAME}/g" ${PRODUCT_NAME}/tmp/tfstate.tf
fi

# init
cd ${PRODUCT_NAME}/tmp
terraform init

# plan
terraform plan -var-file=datadog.tfvars -var-file=secrets.tfvars
if [ $? -ne 0 ]; then
  echo "terraform planでエラーが発生しました。詳細を確認してください。"
  exit 1
fi

# apply
terraform apply -var-file=datadog.tfvars -var-file=secrets.tfvars

まとめ

まだまだ改善の余地しかないですが、一応最低限は運用できています。
再現性のあるものができたとは思いつつ、監視を変更するたびにyamlを書き換える必要があるので、なかなか運用が面倒だなーというのが正直な感想です。
このあたりは、もう少しPDCA回して最適化していきたいです。

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0