🦧

GKE上に構築したArgo WorkflowsをDatadogで監視する

2024/08/20に公開

概要

Argo Workflowsで作成したワークフローをDatadogで監視してみました。
Argo ServerをGKE上に構築し、Datadog Agent経由でArgo WorkflowsのメトリクスをDatadogに送ります。
Datadogでは使える機能に制限はあるもののFreeプランが用意されているので、今回はそれを使用します。

GitHub: https://github.com/hosimesi/code-for-techblogs/tree/main/argo_workflows_datadog_monitoring

今回作成したものは以下のように見ることができます。

検証環境

開発時は以下の検証環境を用意しました。

チップ: Apple M3 Max
メモリ: 64 GB

デプロイするインフラは以下の通りです。

1.29.6-gke.1326000

Argo WorkflowsとDatadog

Argo Workflowsとは

Kubernetes上で並列ジョブを管理するためのオープンソースのコンテナワークフローエンジンです。Argo WorkflowsはKubernetesのCRDで定義され、ステップごとに柔軟なインフラリソースの利用などが利用になります。
Workflow ControllerとArgo Serverが主要なコンポーネントになっています。
https://argoproj.github.io/workflows/
https://github.com/argoproj/argo-workflows

主なカスタムリソースには、Workflow、WorkflowTemplate、CronWorkflowなどがあります。
Workflowはその名の通り、Kubernetes Workflowと同様ワークフローの実行を行います。WorkflowTemplateはWorkflowを再利用できる形にパッケージ化したものであり、複数のWorkflowTemplateを組み合わせて使用できたりします。CronWorkflowはスケジュール実行をするWorkflowになります。
プロダクトで利用するときはWorkflowTemplateかCronWorkflowを使うが多いと思います。

Datadogとは

Datadogとはモダンなモニタリングとセキュリティを提供するSaas型のモニタリングツールです。
単純な監視だけでなく、トレーシングやプロファイラも提供しています。
詳しくは以下を参考にしてください
https://docs.datadoghq.com/ja/getting_started/

Argo Workflowsの監視方法

一般的にプロダクト利用でミニマムな監視方法としてはExitHandlerを使う方法があると思います。
ExitHandlerは成功または失敗に関係なく、ワークフローの最後に常に実行されるテンプレートです。
ExitHandlerの使用例としては以下のようなものがあります。

  • ワークフロー実行後のリソースの削除
  • Slackへのワークフローの成否の通知
  • retry制御や他ワークフローとの連携

具体的には以下のように使用できます。以下では成否をメール送信しています。抜粋

exit-handler-workflow.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: exit-handlers-
spec:
  entrypoint: intentional-fail
  onExit: exit-handler                  # invoke exit-handler template at end of the workflow
  templates:
  # primary workflow template
  - name: intentional-fail
    container:
      image: alpine:latest
      command: [sh, -c]
      args: ["echo intentional failure; exit 1"]

  # Exit handler templates
  # After the completion of the entrypoint template, the status of the
  # workflow is made available in the global variable {{workflow.status}}.
  # {{workflow.status}} will be one of: Succeeded, Failed, Error
  - name: exit-handler
    steps:
    - - name: notify
        template: send-email
      - name: celebrate
        template: celebrate
        when: "{{workflow.status}} == Succeeded"
      - name: cry
        template: cry
        when: "{{workflow.status}} != Succeeded"
  - name: send-email
    container:
      image: alpine:latest
      command: [sh, -c]
      args: ["echo send e-mail: {{workflow.name}} {{workflow.status}} {{workflow.duration}}"]
  - name: celebrate
    container:
      image: alpine:latest
      command: [sh, -c]
      args: ["echo hooray!"]
  - name: cry
    container:
      image: alpine:latest
      command: [sh, -c]
      args: ["echo boohoo!"]

ただ取れるメトリクスには限りがあります。もちろんアプリケーション内で出したログはある程度は残すことができますが、変数として定義されているものは以下のみです。
https://argo-workflows.readthedocs.io/en/latest/variables/
また、ExitHandlerにはいくつか問題もあります。Argo Workflowsでは以下のようにTimeoutを設定することができます。

timeout-workflow.yaml
# https://argo-workflows.readthedocs.io/en/latest/walk-through/timeouts/
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: timeouts-
spec:
  activeDeadlineSeconds: 10 # terminate workflow after 10 seconds
  entrypoint: sleep
  templates:
  - name: sleep
    container:
      image: alpine:latest
      command: [sh, -c]
      args: ["echo sleeping for 1m; sleep 60; echo done"]

Timeoutを超えた場合、ExitHandlerがうまく実行されず即時終了してしまいます。もしExitHandlerでメトリクスを送ってる場合、この時の結果は送られてきません。
上記のものに加えてもう少し詳細のメトリクス(実行中のワークフロー数、サーバーのメトリクス)を取りたいとなった時には/metricsを使用する必要があります。
今回はこれらのメトリクスを取るため、Workflow Controllerから出るメトリクスと、ワークフロー本体から出すカスタムメトリクスをDatadogで監視したいと思います。

実装

Datadogのアカウントの作成

ここからアカウントを作成します。
表示に従って進め、以下の画面になればOKです。

GKEの作成

まずはArgo WorkflowsをデプロイするためのGKEを構築していきます。Terraformのドキュメントを参考に簡単なクラスターとノードを作成していきます。

main.tf
resource "google_service_account" "argo_monitoring" {
  account_id   = "argo-monitoring"
  display_name = "argo-monitoring"
}

resource "google_container_cluster" "argo_monitroing" {
  project                  = var.project
  name                     = "argo-monitoring-cluster"
  location                 = "asia-northeast1-a"
  remove_default_node_pool = true
  initial_node_count       = 1
}


resource "google_container_node_pool" "primary_preemptible_nodes" {
  name       = "argo-monitoring-node-pool"
  location   = "asia-northeast1-a"
  cluster    = google_container_cluster.argo_monitroing.name
  node_count = 1

  node_config {
    preemptible  = true
    machine_type = "e2-medium"

    service_account = google_service_account.argo_monitoring.email
    oauth_scopes = [
      "https://www.googleapis.com/auth/cloud-platform"
    ]
  }
}

リソースを作成します。

$ cd path/to/infra
$ terraform init
$ terraform plan
$ terraform apply

コンソールからClusterが作成されていることを確認します。

そして、ConnectからこのClusterに繋げれるように設定します。

Argo Workflowsの構築

次に監視対象のArgo WorkflowsをClusterにインストールしていきます。helmを使用してインストールしていきます。

$ brew install helm
$ helm repo add argo https://argoproj.github.io/argo-helm
$ helm install --create-namespace --namespace argo argo-workflows argo/argo-workflows

実行後argoというnamespaceが作成され、そこにArgo Workflowsに関連するリソースが作成されていることを確認します。

$ kubectl get namespaces

$ kubectl get pods -n argo

Datadog Agentのインストール

Datadog Agentをdatadog-operatorでインストールしていきます。
基本的にDatadogを設定後のガイドに沿っていきます。

$ helm repo add datadog https://helm.datadoghq.com
$ helm repo update
$ kubectl create secret generic datadog-secret --from-literal api-key=<your-api-key>

今回は直接Secret登録していますが、本番運用時等はSecret Managerなどを使いましょう。
その後datadog-agent.yamlを作成し、デプロイしていきます。

datadog-agent.yaml
apiVersion: datadoghq.com/v2alpha1
kind: DatadogAgent
metadata:
  name: datadog
spec:
  global:
    clusterName: 	argo-monitoring-cluster
    site: ap1.datadoghq.com
    credentials:
      apiSecret:
        secretName: datadog-secret
        keyName: api-key
$ kubectl apply -f datadog-agent.yaml

Datadog Agentが動いていることを確認できたらOKです。

Argo Workflowsのメトリクスの取得

Argo WorkflowsのWorkflow Controllerはデフォルトで9090番ポートでPrometheus形式のメトリクスを吐き出します。吐き出されるメトリクスの一覧はこちらに載っています。
現在動いているArgo WorkflowsのWorkflow Controllerがメトリクスを吐いているか確認します。
まずはローカルにポートフォワードします。

$ kubectl port-forward <workflow-controller-pod-name> 9090:9090 -n argo

以下のコマンドでメトリクスが取れるか確認します。

$ curl -s http://localhost:9090/metrics

次に、Workflow ControllerからDatadogエージェントがメトリクスを取得できるようにannotationしていきます。
新たにDeploymentを作成し、applyします。ここで、%%host%%の部分で自動でhostを探してくれるようになります。今回は簡単のためにmetricsをargo_workflowsのprefixがついた全てのリソースを取得していますが、課金が多く発生する可能性があるので必要なものだけに絞ると良いと思います。

workflow-controller-values.yaml
apiVersion:  apps/v1
kind:  Deployment
metadata:
  name:  argo-workflows-workflow-controller
  namespace:  argo
spec:
  replicas:  1
  selector:
    matchLabels:
      app.kubernetes.io/name:  argo-workflows-workflow-controller
      app.kubernetes.io/instance:  argo-workflows
  template:
    metadata:
      annotations:
        ad.datadoghq.com/controller.checks:  |
          {
              "openmetrics": {
              "init_config": {},
              "instances": [
                  {
                  "openmetrics_endpoint": "http://%%host%%:9090/metrics",
                  "namespace": "argo-workflows",
                  "metrics": ["argo_workflows_*"],
                  }
              ]
              }
          }
    spec:
      serviceAccountName:  argo-workflows-workflow-controller
      containers:
        -  name:  controller
``
```sh
$ kubectl apply -f workflow-controller-values.yaml

すると、Datadogのmetrics一覧に以下のようにargo_workflowsのメトリクスが取れるようになります。

Workflowのメトリクスの取得

先ほどまではWorkflow Controllerのメトリクスを取得していました。ただ、実務ではワークフローのステップ単位の実行時間やワークフロー自体の成功・失敗などもう少し細かくメトリクスが取りたくなります。
それらのカスタムメトリクスを取る方法もArgo Workflowsには用意されています。
WorkflowやWorkflowTemplate内でmetricsプロパティを設定してあげると、これまでと同様に9090番ポートから設定したメトリクスが取得できます。
今回は簡単なWorkflowを用いてメトリクスを送ってみます。
簡単な標準出力をするだけのワークフローにmetricsプロパティを追加します。

workflow.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: steps-
  namespace: argo
spec:
  entrypoint: hello

  templates:
    - name: hello
      steps:
        - - name: hello
            template: print-message
            arguments:
              parameters: [{ name: message, value: "hello1" }]

    - name: print-message
      inputs:
        parameters:
          - name: message
      container:
        image: busybox
        command: [echo]
        args: ["{{inputs.parameters.message}}"]

      metrics:
        prometheus:
          - name: workflow_duration
            help: duration of current workflow
            labels:
              - key: template_name
                value: sample-workflow
            gauge:
              value: "{{workflow.duration}}"
          - name: workflow_result
            help: result of workflow executed lastly
            labels:
              - key: template_name
                value: summary-workflow
              - key: status
                value: "{{workflow.status}}"
            gauge:
              value: "1"

また、このままだと権限が足りずワークフローが実行できないため、roleを作成します。

role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: argo
  name: pod-patch-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-patch-role-binding
  namespace: argo
subjects:
- kind: ServiceAccount
  name: default
  namespace: argo
roleRef:
  kind: Role
  name: pod-patch-role
  apiGroup: rbac.authorization.k8s.io
$ kubectl -f role.yaml
$ argo submit -n argo workflow.yaml --watch

正常にワークフローが実行され、Datadogで以下のメトリクスが取れていると成功しています。

  • argo_workflows.argo_workflows_workflow_duration
  • argo_workflows.argo_workflows_workflow_result

これらを組み合わせると以下のようなダッシュボートが作成できます。

終わりに

Datadogエージェントを使ってGKEで動作するArgo Workflowsを監視する方法を紹介しました。ManagedのPrometheusなどを使用する方法もありますが、Datadogもかなり簡単に使えるので一行の余地があるかと思います。

参考

Discussion