東京ガス内製開発チーム Tech Blog

東京ガス内製開発チームのTech Blogです!

k6 Operator を利用して Kubernetes 上に導入した k6 と Datadog を連携してみる

みなさんこんにちは!内製開発チームの杉山です!

だんだん寒さが本格的になってきましたね。寒いといえば(?)、私とSREチームの迫田は、11月にソルトレイクシティで行われる KubeCon + CloudNativeCon NA 2024 に参加する予定です!初めての KubeCon, とても楽しみです…!11月のソルトレイクシティはとっても寒そうですね。現地に行かれる方は、ぜひ仲良くしていただけたら嬉しいです🙇‍♂️

さて、今回は負荷試験の環境を Amazon EKS 上に構築して Datadog と連携したので、そのことについて書いてみます! xk6 拡張機能が無いと連携出来ないということで、そのあたりも振り返ってみます。

負荷試験実施にあたって

負荷試験を実施する際には、以下のようなことを考えるかと思います。

  • シナリオを記述する言語
  • 実施する環境
  • 実施結果の確認方法
  • and more...

結論として、当チームでは k6 を採用したのですが、上記を鑑みても k6 は諸々揃っているなと考えました。

  • シナリオは馴染みのある Javascript で記述可能(慣れてる!
  • 実施環境は Kubernetes 上に Operator を利用して構築可能、分散実行もできる(便利!
  • 実施結果は Datadog にメトリクスを送ることでダッシュボードで閲覧可能(見やすい!
  • Operator は Grafana Labs が提供(何気に嬉しいポイント

今回はせっかくの Kubernetes 環境があるので Operator を利用したのですが、Datadog の導入も Operator を利用しましたし、どんどん Operator 頼みになっているなぁと感じている今日この頃です。

k6 Operator とは

k6 を提供する Grafana Labs が公式で提供している Kubernetes Operator です。これを利用することで、Kubernetes クラスター内で分散テストが実行できます。

grafana.com

当チームでは Argo CD + Helm を利用してデプロイしていますが、サクッと導入できて本当に便利ですね。以下のようなマニフェストを作成してプラットフォーム用のノードに Pod がスケジューリングされるようにしています。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  finalizers:
    - resources-finalizer.argocd.argoproj.io
  name: k6-operator
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://grafana.github.io/helm-charts
    chart: k6-operator
    targetRevision: 3.8.0
    helm:
      releaseName: k6-operator
      valuesObject:
        tolerations:
          - key: "dedicated"
            operator: "Equal"
            value: "platform"
            effect: "NoSchedule"
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
                - matchExpressions:
                    - key: service-name
                      operator: In
                      values:
                        - platform
          podAntiAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                      - key: app.kubernetes.io/name
                        operator: In
                        values:
                          - k6-operator
                  topologyKey: topology.kubernetes.io/zone
  destination:
    namespace: k6-operator-system
    server: https://kubernetes.default.svc

あとはカスタムリソースである TestRun で実行環境を定義し、テストシナリオを Javascript で記述するだけ!うーん、便利です。

Datadog との連携

Datadog との連携もサクサク…と思っていましたが、ちょっと工夫が必要でした。主に以下の流れで Datadog 連携を実現しています。

  1. Datadog Agent にて dogstatsd を有効化する。
  2. xk6 拡張機能を利用する Dockerfile を作成してコンテナリポジトリ(当チームでは Amazon ECR) に PUSHする。
  3. TestRun カスタムリソースに 1. で作成したイメージを利用するように記述する。
  4. TestRun カスタムリソースで argumentsenv を追加する。
  5. TestRun を実行し、Datadog にメトリクスが送信されていることを確認!

Datadog Agent についての設定は以下に記載があります。

docs.datadoghq.com

DogStatsD は、Datadog Agent に付属するメトリクス集計サービスです。これがないと始まらないので、Datadog Agent のマニフェストにさくっと追加します。(ここでは Operator 利用を前提としています)

features:
    dogstatsd:
        hostPortConfig:
            enabled: true

また、k6 実行後に、Datadog Agent のホストIP (k6 自身がデプロイされているノードのIP) を与える必要がありますが、これは後ほど…

xk6 extensions の利用

ここから本格的に k6 との連携を行っていきます。まずは k6 の公式ページを見ると、冒頭に目立つ WARNING の文字が。(2024年10月8日現在)

grafana.com

Warning

The built-in StatsD output has been deprecated on k6 v0.47.0 and scheduled to be removed in v0.55.0. You can continue to use this feature by using the xk6-output-statsd extension, or using the OpenTelemetry output depending on your use case.

For more information on the reason behind this change, you can follow this issue in the k6 repository.

どうやら xk6-output-statsd extension というものが必要なようです。ここで、そもそも xk6 extension とは?となった私。以下のページを見ることに。

grafana.com

Explore k6 extensions という意味だったのですね(知らなかった…!)。この拡張機能は、k6 の developer, OSS developer community によって開発されているようです。自分が必要なものも開発できるようで…我々は恩恵に預かってばかりで頭が上がりません。

で、Datadog へ連携する場合は、先ほどの xk6-output-statsd extension が必要ということですね。これは xk6 コマンドでビルドし、バイナリとして利用することが可能です。ビルド方法については以下の StatsD ページに記載があります。

grafana.com

# sample
# Install xk6
go install go.k6.io/xk6/cmd/xk6@latest

# Build the k6 binary
xk6 build --with github.com/LeonAdato/xk6-output-statsd

サンプルではコマンドが提供されていますが、ここは Kubernetes 上で実行するために、イメージを用意したいところ。ですので、以下のように golang ベースのイメージでビルド後、grafana k6 イメージを上書きする形で Dockerfile を用意しました。(もっと良い方法があるかもしれませんが…

# Build
FROM golang:1.23.2-bookworm as builder

RUN go install go.k6.io/xk6/cmd/xk6@latest
RUN xk6 build \
    --with github.com/LeonAdato/xk6-output-statsd@latest \
    --with github.com/avitalique/xk6-file@latest \
    --output /k6

# Override
FROM grafana/k6:latest
COPY --from=builder /k6 /usr/bin/k6

そして、このイメージを TestRun カスタムリソースで呼び出すようにするわけですが、まだ argumentsenv の記述が足りません。そもそも、それぞれ何を記述すれば良いのでしょうか。

改めて k6 と Datadog の Integration 方法を紹介するページに記載されている Run the k6 test を見てみます。

$ K6_STATSD_ENABLE_TAGS=true k6 run --out output-statsd script.js

環境変数K6_STATSD_ENABLE_TAGS を定義し、 arguments として --out output-statsd を指定していますね。なので、これを TestRun カスタムリソースのマニフェストに記載します。

level=error msg="Couldn't flush a batch" error="write udp 127.0.0.1:39641->127.0.0.1:8125: write: connection refused"

むむ・・・自分自身の 8125 ポートにアクセスしようとしていますね・・・改めて以下を確認すると、 K6_STATSD_ADDR の初期値が localhost:8125 になっているのが原因のようです。

StatsD | Grafana k6 documentation

Address of the statsd service, currently only UDP is supported. The default value is localhost:8125.

ここは Datadog Agent にしなくてはならないので、これも TestRun マニフェストに記載する必要がありそうです。

そんなわけで、前述したホストIPも追加したマニフェストは以下のようになりました。

apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
  name: load-test-sample
  namespace: load-test-sample
spec:
  parallelism: 2
  # 公式ドキュメントに記載されていた`--out output-statsd`を arguments で指定.
  # arguments の type は string なので注意.
  arguments: --out output-statsd
  runner:
    # xk6 をビルドして作成した Docker イメージを指定.
    image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/k6-extented:20240101
    env:
      # ここから環境変数を定義する.
      # K6_STATSD_ADDR では Datadog Agent サービスを指定.
      - name: K6_STATSD_ADDR
        value: "datadog-agent.datadog.svc.cluster.local:8125"
      # 公式ドキュメントに記載されたものを指定.
      - name: K6_STATSD_ENABLE_TAGS
        value: "true"
      # Datadog Agent のホストIPを指定.
      - name: DD_AGENT_HOST
        valueFrom:
          fieldRef:
            fieldPath: status.hostIP
  script:
    configMap:
      name: load-test-sample-scenario-001
      file: test01.js

これまで見てきたものを全部記載してみました!ちなみに runner.image など、どうやって指定するかについては、以下に全量があります。

github.com

驚きの6,000行弱…これを実装された方に敬意しかありません。

ここまでの結果・・・

さて、ここまでやってみたところ、無事に Datadog に連携がされていました!

k6 ダッシュボード

メトリクスもバッチリ

あとはダッシュボードを加工したり、色々遊び甲斐(!?)がありますね!

まとめ

今回は Kubernetes 上に構築した k6 から、実行結果を Datadog に連携するまでを書いてみました!この環境を使うことで、今後の負荷試験が捗ると良いなあ、と思っています。ただ、パイプラインの整備が出来ていなかったり、まだまだ改善の余地があるので、これからも継続的に良いものにしていきたいです💪

余談・・・

以下の k6 Operator バージョンを導入しようとしたところ、Argo CD + Helm の構成でエラーが発生してしまいました。

k6-operator version or image
0.0.17

Helm chart version (if applicable)
3.9.0

調べたところ、このバージョンから values.schema.json を導入したようで、当チームのマニフェストでは affinitytoleration にてエラーが発生している模様。

じゃあバージョンを戻すか…という気持ちにもなりましたが、こういうときに issue を上げずにどうするのかと。いつまでも甘えてばかりじゃ駄目だと考えて、Issue を起票してみました。

github.com

たったこれだけで貢献したとは言えませんが、これからも Kubernetes とエコシステムと共に歩むにあたって、積極的に出来ることからやっていきたいな、と思っています!

それでは、最後までお読みいただき、ありがとうございました!


当チームは積極的な採用を行っています!もしこうした環境やチームに魅力を感じる方がいらっしゃいましたら、ぜひお気軽にお話をしましょう!

アプリケーションエンジニアはこちらから! www.wantedly.com

モバイルアプリエンジニアはこちらから! www.wantedly.com

SRE はこちらから! www.wantedly.com