継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

Amazon OpenSearch Service のデータノードを Gravition にスペックアップする

タダです.

AWS OpenSearch Service のデータノードで Graviton のタイプに変更しようと思い,気付きもあったため検証した内容をまとめます.

Graviton インスタンスタイプへの変更

経緯としてはデータノードが t3 系だったのを直近のワークロードを見た結果, Graviton のインスタンスタイプにしたいと思い,検証してみました.ノードの変更からデータノードを今回は c6g 系を選択しました.これで予定していた作業は終わりだったのですが,マスターノードも t3 系を使っていたため引きづられて Graviton 系への変更を行う必要がありました.これは自分が勉強不足だったので,検証の中で気付いたことでした.

まとめ

AWS OpenSearch Service のデータノードのインスタンスタイプ変更を検証した際に気付きを備忘録としてまとめました.

AWS WAF で特定条件でブロックされたリクエストの発生でアラートを飛ばす

タダです.

AWS WAF のログを Datadog に送って運用している中で,特定条件でブロックされたリクエストがあったらアラートを飛ばしたいとなり,Terraform でコード化したのでこの記事でまとめます.

特定条件について

今回の条件として,WAF は特定の IP アドレスのみアクセス許可している状態です.仮にクライアント側の IP アドレスが変わったりしたりしたら検知する必要があり,WAF のルールで指定している IP アドレスがブロックされた時を検知するようなモニターを作ります.

Datadog のモニター設定

上記の条件で設定を定義したのが下記の Terraform のコードです.@webaclIdAWS WAF の ARN を指定し,@network.client.ipvar.source_ipsAWS WAF で許可している IP アドレス群を指定しています.これらがブロックされた場合にアラートを発報するようにしています.

variable "source_ips" {
  type = list(string)
  default = ["111.111.111.111", "222.222.222.222", "333.333.333.333"]
}

resource "datadog_monitor" "hoge" {
  name  = "hoge WAF Blocked"
  type  = "log alert"
  query = "logs(\"@webaclId:\\\"${data.aws_wafv2_web_acl.hoge.arn}\\\" @network.client.ip:${join("OR", var.source_ips)} @system.action:BLOCK\").index(\"*\").rollup(\"count\").last(\"${var.interval}m\") > 0"
  monitor_thresholds {
    critical = 0
  }
}

まとめ

AWS WAF のログの中から特定 IP アドレスがブロックされたときにアラートを設定する機会があったのでまとめました.

Datadog の APM をローカルから利用する

タダです.

Datadog の APM を試したいと思いやったことをこの記事に備忘録としてまとめます.

ローカルで APM を試す

ローカルから APM を試すことでトレーシングできるかを確認したいと思い,docker のイメージ( gcr.io/datadoghq/agent )で提供されているエージェントを使います.以下のコマンドで起動でき,ローカルのアプリケーションを動かしてトレースできるかを確認する準備ができました.

docker run -d --cgroupns host \
              -v /var/run/docker.sock:/var/run/docker.sock:ro \
              -v /proc/:/host/proc/:ro \
              -v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro \
              -p 8126:8126/tcp \
              -e DD_API_KEY=<DATADOG_API_KEY> \
              -e DD_APM_ENABLED=true \
              -e DD_SITE=datadoghq.com \
              gcr.io/datadoghq/agent:latest

docs.datadoghq.com

トレースのためのコードを埋める

トレースのためにアプリケーションのコードに専用のコードを埋め込んでいきます.Datadog のドキュメントではサポートされている言語ごとに情報がありますが,Go のサンプルコードでローカルでの確認向けコードでいじったことを以下に書きます.WithAgentAddr でコンテナのアドレスとポートを指定し,WithServiceNameでサービス名を指定し,WithDebugMode を有効化しているのはログ上にトレース結果を流してくれるため動作確認目的で有効化しています.

package main

import (
    "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

func main() {
    tracer.Start(
        tracer.WithAgentAddr("localhost:8126"),
        tracer.WithServiceName("test-go-app"),
        tracer.WithDebugMode(true),
    )
    defer tracer.Stop()

    // アプリケーションのロジックのコード等
}

docs.datadoghq.com pkg.go.dev

これらの設定をしつつローカルで動作してみたらトレースができました.

まとめ

簡単ですが,Datadog APM をローカルで試したときに備忘録を書きました.ローカルアプリケーションから Datadog の docker コンテナに疎通が到達せずかなりの時間を溶かしてしまったのですが,いい経験となりました.

Terraform で Aurora の拡張モニタリングを特定環境で設定する

タダです.

Aurora の拡張モニタリングを有効化した時に Terraform を使って行ったのですが,特定の環境にのみ適用させる設定しました.その設定を試した結果をこの記事にまとめます.

事前準備

今回は dev 環境で拡張モニタリングを有効化することにします.まず,拡張モニタリングを有効化するためには IAM ロールを別途作る必要があります.作成のためのコードは以下のような感じになります.

data "aws_iam_policy_document" "assume_role" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["monitoring.rds.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "enhanced_monitoring" {
  count              = var.env == "dev" ? 1 : 0
  name               = "rds-enhanced-monitoring"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

resource "aws_iam_role_policy_attachment" "enhanced_monitoring" {
  count      = var.env == "dev" ? 1 : 0
  role       = aws_iam_role.enhanced_monitoring[0].name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}

拡張モニタリングの有効化

拡張モニタリングの有効化には monitoring_role_arnmonitoring_interval の値が0以上にすればOKです.monitoring_role_arn には事前準備で作成する IAM ロール ARN を指定します.monitoring_interval には 1, 5, 10, 15, 30, 60が使える値なので適宜指定したい値を設定します.

monitoring_role_arn = var.env == "dev" ? aws_iam_role.enhanced_monitoring[0].arn : ""
monitoring_interval = var.env == "dev" ? 60 : 0

関連情報

registry.terraform.io

まとめ

Aurora の拡張モニタリングを有効化したときの備忘録をまとめした.

SendGrid のバウンスの発生を Golang で取得して Slack に通知する

タダです.

前回記事の続きになります.SendGrid のバウンス発生を Golang で取得するまでやったので,その結果を Slack に通知してみました.この記事でその模様をまとめます.

sadayoshi-tada.hatenablog.com

前回記事の振り返り

前回記事では SendGrid API を叩いて bounceDetail の構造体に指定したレスポンスデータを返すところまで書きました.そのため,今回はレスポンスデータを返して以降の Slack 投稿までのコードをまとめます.

前回記事のコード

package main

import (
     "encoding/json"
    "fmt"
    "strconv"
    "time"

    sendgrid "github.com/sendgrid/sendgrid-go"
)
type bounceDetail struct {
    Created int    `json:"created"`
    Email   string `json:"email"`
    Reason  string `json:"reason"`
    Status  string `json:"status"`
}

func getBounceMail(apiKey string) ([]bounceDetail, error) {
    var bounceDetail []bounceDetail
    startTime := time.Now().Add(-10 * time.Minute).Unix()
    endTime := time.Now().Unix()
    req := sendgrid.GetRequest(apiKey, "/v3/suppression/bounces", "https://api.sendgrid.com")
    req.Method = "GET"
    queryParams := make(map[string]string)
    queryParams["start_time"] = strconv.FormatInt(startTime, 10)
    queryParams["end_time"] = strconv.FormatInt(endTime, 10)
    req.QueryParams = queryParams
    res, err := sendgrid.API(req)
    if err != nil {
        fmt.Println(err.Error())
        return nil, err
    }
    if err := json.Unmarshal([]byte(res.Body), &bounceDetail); err != nil {
        fmt.Println(err)
        return nil, err
    }
    return bounceDetail, nil
}

バウンスの発生を受け取ってから Slack 投稿までのコード

バウンスの発生を受け取りを getBounceMail()で行い,そのレスポンスを response 変数に入れています.その後,for 文を回して BouncePostData 構造体に定義したフィールドにマッピングして配列化し,sendSlack を使って Slack に投稿する形です.10分の間隔でその時間帯内におけるバウンスの発生を確認しますが,1件のバウンスメールに付き1個の通知が出て,バウンスの数分その投稿が増える形になるため,1つのメッセージにすべてのバウンスメールと原因をまとめたい場合は,工夫が必要になりますmm

コード抜粋

type SendGridBounceResult struct {
    Result string `json:"result"`
}

type SlackPayload struct {
    UserName string `json:"username"`
    Text     string `json:"text"`
}

type BouncePostData struct {
    Email  string `json:"email"`
    Reason string `json:"reason"`
}

func sendSlack(webhookUrl string, bounceDetails []BouncePostData) error {
    for _, message := range bounceDetails {
        payload, err := json.Marshal(
            SlackPayload{
                UserName: "バウンスチェック",
                Text: fmt.Sprintf("```\nバウンスの発生したメールアドレス:%s\n原因:%s\n```",
                    message.Email, message.Reason,
                ),
            },
        )
        if err != nil {
            fmt.Println(err.Error())
            return err
        }
        res, err := http.PostForm(
            webhookUrl,
            url.Values{"payload": {
                string(payload),
            },
            })
        if err != nil {
            return err
        }
        defer res.Body.Close()
    }
    return nil
}

func main() (SendGridBounceResult, error) {
    var bounceDetails []BouncePostData
    webhookUrl := "Slack Incoming Webhook URL"
    // 前回記事のバウンスメールの取得関数呼び出し
    response, err := getBounceMail(apiKey)
    if err != nil {
        fmt.Println(err.Error())
        return SendGridBounceResult{Result: "Error"}, err
    }
    if len(response) == 0 {
        fmt.Println("No Bounce mail.")
        return SendGridBounceResult{Result: "Success"}, nil
    }
    for _, value := range response {
        bounceDetails = append(bounceDetails, BouncePostData{
            Email:  value.Email,
            Reason: value.Reason,
        })
    }
    err = sendSlack(webhookUrl, bounceDetails)
    if err != nil {
        fmt.Println(err.Error())
        return SendGridBounceResult{Result: "Error"}, err
    }
    return SendGridBounceResult{
        Result: "Success",
    }, nil
}

まとめ

SendGrid のバウンスが発生しているメールは通常都度コンソールをみることになりますが,バウンスが発生してたら Slack に投稿する仕組みを作ったのでその模様をまとめました.実運用に載せましたが,今の所問題は起こっていなく,今まで気づけなかったバウンスに気づきやすく問題に当たれるようになった体感があります.今後も普段のオペレーションから仕組み化すべきなことをコードで解決図っていきたいと思います.