0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

kubernetes 上で Grafana + InnoDB Cluster を構成する

Last updated at Posted at 2025-12-08

この記事は Grafana Advent Calendar 2025 の 8 日目になります。

私は GMO GPUクラウドのサービス・インフラストラクチャの開発に従事しており、最近の仕事としては Grafana ダッシュボードを利用できる機能を開発、kubernetes 上にホストして統合しました。

概要

kubernetes 上で Grafana のバックエンドデータベースを InnoDB Cluster に指定してテストした時の備忘録です。

プロダクション想定である以上、各コンポーネントの冗長化は必須であり、Prometheus や TSDB の冗長化はさることながら、Grafana 自身の管理情報を保持するデータベースについても冗長化を検討することになります。

デフォルトでは組み込みの SQLite を使用することになりますが、MySQL Operator を用いた InnoDB Cluster バックエンドに使用できないかな?と思ったものの、意外と情報が無かったので情報を残します。

※なお、サポートされているデータベースでは MySQL 5.7+ は含まれているものの InnoDB Cluster の扱いは不明瞭です。この点はご了承ください。

MySQL Operator とは

MySQL 公式が提供している高可用性ソリューションである InnoDB Cluster を kubernetes 上にデプロイするための Operator です。

もし kubernetes 上に HA 化を実装するとなると Primary/Replica や LB の設計などが大変ですが、InnoDB Cluster という HA ソリューションを簡単にデプロイできるのが魅力です。

また、Operator をプロダクションで利用する場合は品質が気になるところですが、MySQL Operator は Oracle 社が開発を支援しており、定期的なメンテナンス・リリースもあることから、安心して選定しやすいと言えます。

構築手順

基本的には公式の Installation に沿って行きます。

前提

  • kubernetes クラスタ
  • StorageClass を用意する(今回は Ceph による SC を作成)

1. MySQL Operator のデプロイ

CRD/Operator の yaml を入手し、apply します。

$ wget https://raw.githubusercontent.com/mysql/mysql-operator/refs/heads/trunk/deploy/deploy-crds.yaml
$ wget https://raw.githubusercontent.com/mysql/mysql-operator/refs/heads/trunk/deploy/deploy-operator.yaml
$ kubectl apply -f path/to/deploy-crds.yaml
$ kubectl apply -f path/to/deploy-operator.yaml
$ kubectl -n mysql-operator get po
NAME                              READY   STATUS    RESTARTS   AGE
mysql-operator-59b79c9fb4-9qxrk   1/1     Running   0          2m14s

注意点

お使いのクラスタ CNI が Cillium の場合、MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN 変数にクラスタドメインを設定してください。

$ vim deploy-operator.yaml
...
           env:
             - name: MYSQLSH_USER_CONFIG_HOME
               value: /mysqlsh
             - name: MYSQLSH_CREDENTIAL_STORE_SAVE_PASSWORDS
               value: never
             - name: MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN <<<
               value: cluster.local                    <<<
...

2. InnoDB Cluster のデプロイ

デプロイ用の Namespace を作成し、Secret Deployment を設定していきます。

$ kubectl create ns monitoring
$ vim secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: mycluster-sec
  namespace: monitoring    # NameSpace 名指定
stringData:
  rootUser: root
  rootHost: '%'
  rootPassword: 'p@ssword!'
$ vim deploy.yaml
---
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mysql              # 名称変更
  namespace: monitoring        # NameSpace 名指定
spec:
  secretName: mycluster-sec    # Secret 名指定
  baseServerId: 100
  tlsUseSelfSigned: true
  instances: 2
  router:
    instances: 1
  datadirVolumeClaimTemplate:
    resources:
      requests:
        storage: 2Gi
    storageClassName: my-sc    # StorageClass 指定

デプロイし、すべての Pod が動作することを確認します。

$ kubectl apply -f secret.yaml
$ kubectl apply -f deploy.yaml
$ kubectl -n monitoring get po
NAME                                READY   STATUS    RESTARTS   AGE
mysql-0                         2/2     Running   0          2m10s
mysql-1                         2/2     Running   0          2m9s
mysql-router-8674dcd4f5-lcn8c   1/1     Running   0          14s

念のためテスト用のコンテナからアクセスを確認しておきます。

$ kubectl run mysql-client --rm -it --image=mysql:8 --restart=Never -- bash
...
bash-5.1# mysql -h mysql.monitoring.svc.cluster.local -u root -p
Enter password:  <<< 'p@ssword!'

mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 9.4.0     |
+-----------+
1 row in set (0.00 sec)

3. Grafana のデプロイ

InnoDB Cluster に Grafana 用のデータベースを作成しておきます。

$ mysql -h "${MYSQL_HOST}" -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "
              CREATE DATABASE IF NOT EXISTS grafana CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
              CREATE USER IF NOT EXISTS 'grafana'@'%' IDENTIFIED BY 'p@ssw0rd!';
              GRANT ALL PRIVILEGES ON grafana.* TO 'grafana'@'%';
              FLUSH PRIVILEGES;
           "

続けて Grafana の Deploy を行いますが、ここでは grafana:latest を pull して StatefulSet を実装します。ポイントとして、Grafana の DB 参照先を作成した InnoDB Cluster の grafana データベースに向けます。

...
      containers:
      - name: grafana
        image: grafana/grafana:latest
        imagePullPolicy: Always
        env:
        - name: GF_DATABASE_TYPE
          value: "mysql"
        - name: GF_DATABASE_HOST
          value: "mysql:3306"        # 作成した InnoDB Cluster の SVC
        - name: GF_DATABASE_NAME
          value: "grafana"
        - name: GF_DATABASE_USER     # grafana データベースユーザ
          valueFrom:
            secretKeyRef:
              name: grafana-secret
              key: db-user
        - name: GF_DATABASE_PASSWORD # grafana データベースのパスワードの Secret
          valueFrom:
            secretKeyRef:
              name: grafana-secret
              key: db-password
...

正常に Pod が起動すれば OK です。

$ kubectl -n monitoring get po
NAME                            READY   STATUS    RESTARTS      AGE
grafana-0                       1/1     Running   0             1d
grafana-1                       1/1     Running   0             1d
mysql-0                         2/2     Running   0             1d
mysql-1                         2/2     Running   0             1d
mysql-router-8674dcd4f5-lcn8c   1/1     Running   0             1d

Known Issue

InnoDB Cluster をバックエンドに指定すると、なぜか Grafana Pod が正常に起動してくれません。
Error: ✗ migration failed (id = copy alert_rule_tag v1 to v2): Error 3098 (HY000): The table does not comply with the requirements by an external plugin. とのことで、どうやら Grafana -> InnoDB Cluster の接続がうまくいっていないようです。

logger=migrator t=****-**-**T**:**:**.*********Z level=error msg="Executing migration failed" id="copy alert_rule_tag v1 to v2" error="Error 3098 (HY000): The table does not comply with the requirements by an external plugin." duration=1.468343ms
logger=migrator t=****-**-**T**:**:**.*********Z level=error msg="Exec failed" error="Error 3098 (HY000): The table does not comply with the requirements by an external plugin." sql="INSERT INTO `alert_rule_tag` (`tag_id`\n, `alert_id`) SELECT `tag_id`\n, `alert_id` FROM `alert_rule_tag_v1`"
logger=migrator t=****-**-**T**:**:**.********* level=info msg="Unlocking database"
Error: ✗ migration failed (id = copy alert_rule_tag v1 to v2): Error 3098 (HY000): The table does not comply with the requirements by an external plugin.

色々調べると同様の問題があるようで、Grafana が使用する alert_rule_tag_v1 テーブルに PRIMARY_KEY が無いので、InnoDB Cluster 側に怒られる(?)ようですね。。。

mysql> SHOW INDEX FROM alert_rule_tag_v1 WHERE Key_name = 'PRIMARY';
Empty set (0.01 sec)

仕方がないので DB に入ってプライマリキーを設定します。

mysql> use grafana;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> alter table alert_rule_tag_v1
    -> add constraint alert_tag_v1_pk
    -> primary key (tag_id, alert_id);
Query OK, 0 rows affected (0.28 sec)

しばらくするとマイグレートが進むようになりますが、別のテーブル(annotation_tag_v2)で同様のエラーになるので再度プライマリキーを手動付与します。

mysql> use grafana;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

mysql> alter table annotation_tag_v2
    -> add constraint annotation_tag_v2_pk
    -> primary key (tag_id, annotation_id);
Query OK, 0 rows affected (0.26 sec)

まとめ

一部すんなりいかない部分もあり難航しましたが、Grafana と InnoDB Cluster を組み合わせて動作させることに成功しました。
kubernetes 上で使用する場合は作りやすく、冗長化も Operator に任せられることから運用性も高いと思いますので、是非参考にしていただければ幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?