1923

都内と業界の隅っこで生活しているエンジニアのノート

Azure Container Apps と Dapr と Go のサンプル

Azure Container Apps で動かす Goアプリから Dapr 経由でHTTPとかSMTPのサービスを使ってみます。合わせて、よく使いそうなSendGridでメール送信、Cronで定期実行も試したので記載。

まずは単純にnet/httpパッケージで、サンプルコードを実行するHTTPサーバーを実装。

type service struct {
    client dapr.Client
}
func main() {
    // Dapr
    cli, err := dapr.NewClient()
    if err != nil {
        panic(err)
    }
    defer cli.Close()

    svc := &service{
        client: cli,
    }

    http.HandleFunc("/cron", cron)
    http.HandleFunc("/smtp", svc.Smtp)
    http.HandleFunc("/sendgrid", svc.SendGrid)
    http.HandleFunc("/http", svc.Http)
    http.ListenAndServe(":8080", nil)
}

仕様を参考にContainer AppsでDaprを呼ぶコードを実装。コンポーネントの設定からContainer Appsで不要な schema を削除していきます。

Example

HTTP, SMTP, SendGridのOutput Bindingは、リクエスト内容は異なりますが、dapr.NewClient() で作ったclientでリクエストを送るだけですので基本的には同じです。

HTTP

HTTP binding spec

- name: http
  type: bindings.http
  version: v1
  metadata:
    - name: url
      value: http://example.com
func (svc *service) Http(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    in := &dapr.InvokeBindingRequest{
        Name:      "http",
        Operation: "get",
        Data:      []byte("\"Hello, HTTP.\""),
        Metadata:  map[string]string{"path": "/"},
    }

    out, err := svc.client.InvokeBinding(ctx, in)
    if err != nil {
        fmt.Fprint(w, err.Error())
        return
    }
    data := string(out.Data)
    log.Print("Hello, HTTP.")
    fmt.Fprint(w, data)
}
SMTP

SMTP binding spec

- name: smtp
  type: bindings.smtp
  version: v1
  metadata:
    - name: host
      value: "example.com"
    - name: port
      value: "587"
    - name: user
      value: "USER"
    - name: password
      value: "PASSWORD"
    - name: skipTLSVerify
      value: true
    - name: emailFrom
      value: "test@example.com"
    - name: emailTo
      value: "test@example.com"
    - name: subject
      value: "Example Smtp"
func (svc *service) Smtp(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    in := &dapr.InvokeBindingRequest{
        Name:      "smtp",
        Operation: "create",
        Data:      []byte("\"Hello, SMTP.\""),
        Metadata:  map[string]string{"subject": "SMTP"},
    }

    _, err := svc.client.InvokeBinding(ctx, in)
    if err != nil {
        fmt.Fprint(w, err.Error())
        return
    }
    log.Print("Hello, SMTP.")
    fmt.Fprint(w, "OK. SMTP.")
}
Twilio SendGrid

Twilio SendGrid binding spec

- name: sendgrid
  type: bindings.twilio.sendgrid
  version: v1
  metadata:
    - name: emailFrom
      value: "test@example.com"
    - name: emailTo
      value: "test@example.com"
    - name: subject
      value: "Example SendGrid"
    - name: apiKey
      value: "APIKEY"
func (svc *service) SendGrid(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    in := &dapr.InvokeBindingRequest{
        Name:      "sendgrid",
        Operation: "create",
        Data:      []byte("\"Hello, SendGrid.\""),
        Metadata:  map[string]string{"subject": "SendGrid"},
    }

    _, err := svc.client.InvokeBinding(ctx, in)
    if err != nil {
        fmt.Fprint(w, err.Error())
        return
    }
    log.Print("Hello, SendGrid.")
    fmt.Fprint(w, "OK. SendGrid.")
}
Cron

Cron binding spec

cronはコンポーネントの名前に設定したエンドポイントに、metadataに設定されたタイミングで HTTP POST リクエストが来ます。サンプルはログに「Hello, Cron.」と出力していますので、Log Analyticsの設定をしてログで確認。

- name: cron
  type: bindings.cron
  version: v1
  metadata:
    - name: schedule
      value: "@every 10m"
func cron(w http.ResponseWriter, _ *http.Request) {
    log.Print("Hello, Cron.")
    fmt.Fprint(w, "OK. Cron.")
}

f:id:taka1923:20211218095043p:plain

デプロイ

ACRなどを利用してAZコマンドで試すときは、 components.yaml などに上記のコンポーネント設定をまとめて1ファイルで記載し、az containerapp create の dapr-components で、そのファイルを指定。

コマンド例

az containerapp create `
  --name $NAME `
  --resource-group $RESOURCE_GROUP `
  --environment $CONTAINERAPPS_ENVIRONMENT `
  --image $IMAGE `
  --target-port 8080 `
  --ingress 'external' `
  --min-replicas 1 `
  --max-replicas 1 `
  --enable-dapr `
  --dapr-app-port 8080 `
  --dapr-app-id $ID `
  --dapr-components "./components.yaml" `
  --registry-login-server $LOG_SERVER `
  --registry-username $USERNAME `
  --registry-password $PASSWORD 

無事デプロイ完了。ポータルでも確認できました。

f:id:taka1923:20211218095116p:plain