Kubernetes用プロファイリングツール necoperfの紹介

はじめに

こんにちは、Necoチームの竹村です。

今回のブログ記事では、新しく開発したプロファイリングツールである necoperf について紹介します。necoperf は Kubernetes 上で動作するコンテナに対して、簡単にプロファイリングを行えるようにするツールです。 本記事では necoperf を開発した背景や特徴、使い方について紹介します。

necoperf をなぜ開発したのか

necoperf をなぜ開発したのかを簡単に紹介します。

現在の perf でプロファイリングを取る方法

サイボウズではKubernetesクラスタをマルチテナント運用しており、1つのクラスタに複数のチームがアプリケーションをデプロイしています。 NecoチームではこのKubernetesクラスタを開発・運用しています。
アプリケーション開発者がアプリケーションを運用していると、アプリケーションのパフォーマンスがでないという問題が発生することがあります。 アプリケーションのパフォーマンス解析をするためにLinuxのperfコマンドを利用するのですが、マルチテナント環境ではアプリケーション開発者はサーバーにログインする権限を持たないため、以下のいずれかの方法でperfを実行する必要がありました。

  1. プロファイリング対象のコンテナ内で perf を実行する方法
    • perf のバイナリを計測対象のコンテナ内に配置して、perfを実行します
  2. プロファイリングを実行するためのサイドカーコンテナを用意する方法
    • サイドカーコンテナから計測対象のコンテナに対して perf を実行します
    • この方法では、shareProcessNamespacetrueに設定し、perf を実行するための権限をサイドカーコンテナに付与する必要があります
  3. エフェメラルコンテナからperf を実行する方法
    • この方法では、EphemeralContainers サブリソースを JSON ファイルとして作成し、Kubernetes の API サーバに対して PUT リクエストを送ることで エフェメラルコンテナを追加します。追加したエフェメラルコンテナから計測対象のコンテナに対してperfを実行します
    • kubectl debugコマンド経由ではエフェメラルコンテナを作成する際にvolumeMounts を指定することができないため、EphemeralContainers サブリソースの JSONファイル を作成して API サーバに対してリクエストを送信する必要があります

今までの運用で感じていた問題点

以前のperf を実行する方法には、以下の2つの問題がありました。
1つ目の問題としては、アプリケーション開発者に強い権限を付与する必要がある点です。 Necoチームが運用しているKubernetesクラスタではアプリケーション開発者へ付与される権限は必要最小限にされており、デフォルトの権限ではperfを実行することができません。 アプリケーション開発者がコンテナ内でperfを実行できるようになるためには、Necoチームが追加の権限を付与する必要があります。 Necoチームとしてはアプリケーション開発者へ付与する権限は最小限にしたいと考えており、perfを実行できるようにするためだけにアプリケーション開発者に強い権限を付与したくないという問題がありました。
2つ目の問題としては、perf のバイナリを用意するために非常に手間がかかる点です。 利用する perf のバージョンは ホスト OS の Linux のカーネルのバージョンにできるだけ合わせることが望ましいとされています。そのため、アプリケーション開発者はコンテナイメージを作成する際に、 パッケージ管理ツールを利用してホストOSに近いバージョンのperfを用意する必要がありました。 このパッケージを探す作業は非常に手間がかかり、perfを利用できるようになるまでのハードルを高くしていました。

これらの問題があり、Kubernetesクラスタ上でperfを実行するために非常に苦労していました。

necoperf

今までの運用で感じていた問題点を解決するために開発したのが necoperf です。necoperf は Kubernetes 上で動作するコンテナに対して、Linux の perf コマンドを簡単に使えるようにするツールです。

特徴

necoperf は以下のような特徴を持っています。

  • アプリケーション開発者に perf を実行するために必要な権限を付与する必要がなくなる
  • 1 コマンドでプロファイリングが簡単に行える
  • perf のバイナリの用意をテナントチームが行う必要がなくなる
  • コンテナの単位でプロファイリングを行うことができる

仕組み

necoperf は、各ノード上で gRPC サーバとして動作する necoperf-daemon と、necoperf-daemon に対して gRPC でリクエストを送信する necoperf-client から構成されています。
necoperf-client は necoperf-daemon に対してプロファイリングを行うためのリクエストを送信します。
necoperf-daemon はリクエストに応じて、CRI(Container Runtime Interface)を利用してコンテナ名から PID を取得します。そして、対象のPIDに対して perf を実行してプロファイリングを行います。プロファイリングの結果は necoperf-daemon から necoperf-client に返され、necoperf-client はプロファイリングの結果をファイルとして保存します。

使い方の紹介

necoperf の使い方として、mocoを使って構築したMySQLのPod に対してプロファイリングを行う方法を紹介します。 mocoに関しては以下の記事をご覧ください。

blog.cybozu.io

以下のツールが入ったUbuntu 22.04 環境を用意してください。

  • Go
  • Docker
  • kubectl
  • Kustomize
  • yq
  • FlameGraphのスクリプト
    • stackcollapse-perf.pl
    • flamegraph.pl

まず、mocoのセットアップを行ないます。

$ git clone https://github.com/cybozu-go/moco.git
$ cd moco/e2e
$ make start
$ export KUBECONFIG=$(pwd)/.kubeconfig
$ cat > mycluster.yaml <<'EOF'
apiVersion: moco.cybozu.com/v1beta2
kind: MySQLCluster
metadata:
  namespace: default
  name: test
spec:
  replicas: 3
  podTemplate:
    spec:
      containers:
      - name: mysqld
        image: ghcr.io/cybozu-go/moco/mysql:8.0.35
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
EOF
$ kubectl apply -f mycluster.yaml

次に、necoperfのセットアップを行います。

$ git clone https://github.com/cybozu-go/necoperf
$ cd necoperf/
$ kustomize build config/namespace/ | kubectl apply -f -
$ kustomize build config/rbac/ | kubectl apply -f -
$ cd e2e
$ yq -i e '.spec.template.spec.containers[0].image = "ghcr.io/cybozu-go/necoperf-daemon:0.1.0"'  manifests/necoperf-daemonset.yaml
$ yq -i e '.spec.containers[0].image = "ghcr.io/cybozu-go/necoperf-cli:0.1.0"'  manifests/necoperf-client.yaml
$ kubectl apply -f manifests

necoperf は necoperf-cli という CLI ツールを提供しています。necoperf-cli を使うことで、簡単にプロファイリングを行えます。 mocoのテストを開始してから、以下のコマンドを実行して、necoperf-cli でMySQL Podのプロファイリングを行ってみてください。

# moco/e2eのディレクトリに移動する
$ make test
# 別ターミナルを開き、プロファイリングを行う
$ kubectl exec -it necoperf-client -- bash
$ necoperf-cli profile moco-test-0

プロファイリングが正常にできていたら、necoperf-clientのPod内に/tmp/moco-test-0.scriptというファイルが作成されているはずです。このファイルはFlameGraphのスクリプトを使うことで、以下のようなフレームグラフを作成することができます。

$ kubectl cp necoperf-client:/tmp/moco-test-0.script /tmp/moco-test-0.script
$ cat /tmp/moco-test-0.script | ./stackcollapse-perf.pl | ./flamegraph.pl > moco-test-0.svg

flamegraph

まとめと今後の予定

Kubernetes 環境でプロファイリングを簡単に行えるようにするために、necoperf を開発しました。今後は、necoperf をユーザに利用してもらい、改善していきたいと考えています。また、necoperf を運用していく中で、以下のような課題が見つかっています。

  • necoperf が利用する perf のバージョンとホストOSのカーネルのバージョンが一致するようにアップデートを行なっていくのが難しい
  • プロファイリング対象のコンテナが子プロセスを起動している場合に、プロファイリングがうまく行えない
  • pidを取得するためにCRI(Container Runtime Interface) APIを利用しており、コンテナランタイムのsocketをマウントする必要がある

今後もnecoperfを積極的に開発し、これらの課題を解決していきたいと考えています。 ぜひ使ってみてください!

github.com