継続は力なり

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

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

SendGrid のバウンスの発生を Golang で取得する

タダです.

前回 SendGrid API を叩いてバウンスが発生したメールと原因を NodeJS で取得したのを記事に書きました.今回は Golang で同様のことをやってみたのでその模様を備忘録としてまとめます.

sadayoshi-tada.hatenablog.com

バウンスが発生したの取得のコード

バウンスを発生の取得するコードは次のような物を作りました.クエリパラメータでバウンスが発生期間の開始と終了を使えるので,start_time が10分前の UNIXTIME で end_time がコード実行時間の UNIXTIME を取得して渡しています.SendGrid の API を叩くライブラリとして sendgrid-go があるのでこれを使って取得します.

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
}

SendGrid の API を叩いた時にレスポンスが下記のように返ってくるので bounceDetail という構造体でレスポンスを定義して呼び出し元に返すことをしています.あとは呼び出し元で必要な情報を取得して処理する事が可能です.

[
        {
          "created": 1443651125,
          "email": "testemail1@test.com",
          "reason": "550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at  https://support.google.com/mail/answer/6596 o186si2389584ioe.63 - gsmtp ",
          "status": "5.1.1"
        },
        {
          "created": 1433800303,
          "email": "testemail2@testing.com",
          "reason": "550 5.1.1 <testemail2@testing.com>: Recipient address rejected: User unknown in virtual alias table ",
          "status": "5.1.1"
        }
]

まとめ

SendGrid の APIGolang で利用したのがはじめてだったのでその模様をまとめました.Golang を使うこと自体転職してからになるため,表現や書きっぷりに慣れていきたいし,学びをこのブログに投稿していければと思います.

SendGrid のバウンスの発生を取得する

タダです.

SendGrid を利用していると遭遇するのはバウンスメールだと思います.そこで,バウンスが発生しているメールアドレスがあったら検知する仕組みを検証したのでこの記事にまとめます.

バウンスメール発生検知の仕組み構成

今回の仕組みでは,Event Webhookを使ってもいいかと思ったんですが,即時性はそこまで必要ではなかったので EventBridge -> Lambda -> SendGrid API -> Slack 通知 を実行するようにしました. 検証では Lambda -> SendGrid API 部分を確認したかったため本記事もこの部分に焦点を当てます.

バウンスメールの取得

バウンスメールの取得は Bounce API で行うことができます.バウンスメールが存在すると,レスポンスが次のように返ってきます.引数で start_timeend_time があるので EventBridge で指定した時間と前回実行の間で発生したバウンスメールを取得しようと考えました.

サンプルイベント

[
        {
          "created": 1443651125,
          "email": "testemail1@test.com",
          "reason": "550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at  https://support.google.com/mail/answer/6596 o186si2389584ioe.63 - gsmtp ",
          "status": "5.1.1"
        },
        {
          "created": 1433800303,
          "email": "testemail2@testing.com",
          "reason": "550 5.1.1 <testemail2@testing.com>: Recipient address rejected: User unknown in virtual alias table ",
          "status": "5.1.1"
        }
]

バウンスメールの取得のコード

SendGrid API ドキュメントを見ると多くの言語がサポートされていましたが, NodeJS で検証してみました.

const client = require('@sendgrid/client');
client.setApiKey("YOUR_API_KEY");
let date = new Date();
const startTime = Math.floor( date.getTime());
const endTime = Math.floor( date.getTime() - 300000 )
const req = {
    method: "POST",
    url: "/v3/suppression/bounces",
    start_time: startTime,
    end_time: endTime
}
let bounceDetailText = ""

try {
    const res = client.request(req)
    if (res.length === 0) {
        console.log('No Bounce mail.')
    } else {
        res.map( bounce,i => {
            bounceDetailText = "```\n対象email: " + bounce[i].email + "\nバウンスの原因: " + bounce[i].reason +"\n```"
        })
    }
    console.log(bounceDetailText)
    console.log('Check Bounce mail sucessfully')
} catch (error) {
    console.error('Failed to chek Bounce mail.')
}

バウンスメールが無ければ,No Bounce mail. と表示され,バウンスメールが有った場合,Slack に投稿するようメッセージ形式を整形している感じです.

まとめ

SendGrid のバウンスメールを取得するコードを検証したので,そのコードをまとめました.次は Slack への投稿まで運用に載せてみて記事にしたいと思います.