4
5

More than 3 years have passed since last update.

MetabaseをKubernetes上で冗長化してみる

Posted at

概要

MetabaseのコンテナをKubernetesを使って複数台構成にして冗長化できたので、その方法を共有する。
冒頭で言ってしまうと身も蓋もないのだが、同じようなことを下記サイトにあるHelmを使ってもできそうなので、Helmに詳しい方はこちらを参考にしたほうが簡単にできるかも。
https://github.com/helm/charts/tree/master/stable/metabase

Metabaseコンテナの構造

今回、以下のMetabaseコンテナを利用する。
https://www.metabase.com/docs/latest/operations-guide/running-metabase-on-docker.html

Metabaseの中身をざっくりと見たのが下図。コンテナの中にアプリケーション本体と、設定情報(ユーザアカウントやコレクション等)を格納したアプリケーションDBが存在する。アプリケーションDBはJavaプラットフォーム上で動くH2データベース上で起動している。

このままの状態でMetabaseコンテナを複数立ち上げてしまうと、各コンテナの中に別個にアプリケーションDBが出来てしまうので、アプリの設定が適切に反映されなくなってしまう。そこでちょっと工夫が必要。

手順の概要

上記の問題を解決するために、まずはコンテナ内に入っているアプリケーションDBをコンテナ外に予め作成したPostgreSQLのDBに変換する。以下がそのイメージ。
Metabaseのコンテナ起動時にオプションを使うとPostgreSQL形式に変換してくれるので、今回はそれを利用してKubernetesのJobを作成する。

その後、サービス用のMetabaseコンテナを立ち上げる。デフォルトだとH2データベースのアプリDBが作られてしまうので、環境変数でポスグレのアプリDB情報を指定する必要あり。下図が冗長化構成にしたイメージ。

それでは具体的な方法を記載する。

データベースサーバーの用意

アプリケーションデータを格納するためのDBサーバ(今回はPostgreSQL)を用意する。

  • DBサーバーは各環境に合わせて用意してもらえればいいが、本例ではKubernetesで作成(永続化はしていないので注意)
  • コンテナイメージは下記Docker Hubのpostgresを利用

マニフェストは以下

postgres.yml
# PostgreSQLのServiceリソースに関する定義
# このリソースがあることで、同じKubernetesクラスター内の他のPodから
#「metadata.name」で指定したホスト名(=postgres)でアクセス可能となる
apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  type: ClusterIP
  selector:
    app: postgres
  ports:
  - protocol: TCP
    port: 5432
    targetPort: 5432
---
# パラメータの定義。後でMetabaseのマニフェストを書く際にも利用する。
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-vars
data:
  POSTGRES_DB: postgres
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: pgpasswd
---
# postgresサーバーのデプロイ方法に関する定義
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: postgres
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      # コンテナ本体の定義
      containers:
      - image: postgres
        name: postgres
        env:
        envFrom:
        - configMapRef:
            name: postgres-vars
  • リソース作成
$ kubectl apply -f postgres.yml
  • 各リソースが出来ていることを確認
$ kubectl get pod -l app=postgres
NAME                       READY   STATUS    RESTARTS   AGE
postgres-ff5bfdccf-2wgvl   1/1     Running   0          14m
$ kubectl get deployments -l app=postgres
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
postgres   1/1     1            1           14m
$ kubectl get service
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    76d
postgres     ClusterIP   10.107.54.211   <none>        5432/TCP   16m

H2データベースをPostgreSQLに変換

手順の概要で述べたとおり、Metabaseの設定情報(ユーザアカウントやコレクション等)はデフォルトではH2データベースに格納されていため、先ほど作成したPostgreSQLのDBに移行する。
以下サイトにMetabaseのDockerコンテナを使ってH2データベースをPostgreSQLに変換する方法が記載されており、これを参考に「H2データベース→PostgreSQL変換」のJobを実行するマニフェストを書く。

load-from-h2.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: metabase-load-vars
# Metabaseの設定ファイルをPostgreSQLに変換するためのパラメータを定義
data:
  MB_DB_FILE: /metabase.db
  MB_DB_TYPE: postgres
  MB_DB_PORT: "5432"
  MB_DB_HOST: postgres
---
apiVersion: batch/v1
kind: Job
metadata:
  name: load-from-h2
spec:
  completions: 1
  template:
    spec:
      restartPolicy: Never
      containers:
      - image: metabase/metabase # ★自作でイメージを作った場合はここを変更
        name: load-from-h2
        args: ["load-from-h2"]
        envFrom:
        - configMapRef:
            name: metabase-load-vars
        # DB名、DBユーザー名、パスワードはpostgresの
        # コンテナと同じパラメータを利用
        env:
        - name: MB_DB_DBNAME
          valueFrom:
            configMapKeyRef:
              name: postgres-vars
              key: POSTGRES_DB
        - name: MB_DB_USER
          valueFrom:
            configMapKeyRef:
              name: postgres-vars
              key: POSTGRES_USER
        - name: MB_DB_PASS
          valueFrom:
            configMapKeyRef:
              name: postgres-vars
              key: POSTGRES_PASSWORD
        # 以下はMetabaseのH2データベース形式の設定ファイル
        # (metabase.db.mv.db,metabase.db.trace.db)を
        # PersistentVolumeを経由してマウントする例
        # Metabaseを一から新しく作る場合や、コンテナ内にカスタマイズ済みの
        # 設定ファイルが既に格納されている場合は、以後の記載は不要
        volumeMounts:
        - name: settings-vol
          mountPath: /metabase.db
      volumes:
      # ここには記載しないが、persistentVolumeと
      # persistentVolumeClaimを別途作る必要あり
      - name: settings-vol
        persistentVolumeClaim:
          claimName: metabase-pvc

このマニフェストでは、Metabaseの設定ファイルをPersistentVolumeを使って反映している。この手順が面倒な場合は、予め設定ファイルをコンテナ内にコピーしたイメージをDockerfile等で作っておき、自作イメージをコンテナ化するマニフェストを書いても差し支えない。(上記マニフェストの# ★自作でイメージをとコメントした部分のコンテナ名を書き換える)

  • 以下コマンドで、変換Jobを実行
$ kubectl apply -f load-from-h2.yml
  • Jobが完了していることの確認(筆者の環境では1分ほどかかった)
$ kubectl get jobs
NAME           COMPLETIONS   DURATION   AGE
load-from-h2   1/1           75s        76s

Metabaseのマニフェストを書いてデプロイ

以下がMetabaseのサービス化用マニフェスト

deploy.yml
apiVersion: v1
kind: Service
metadata:
  name: metabase
spec:
  type: ClusterIP
  selector:
    app: metabase
  ports:
  - protocol: TCP
    port: 3000 # 公開するポート
    targetPort: 3000
  # 環境に応じて適宜設定。
  # サービス公開するポート番号が任意でよければ、
  # NodePortを使ってexternalIPsは書かなくても問題ない
  externalIPs:
    - <ホストOSのIP>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: metabase
  name: metabase
spec:
  replicas: 2 # デプロイするPodの数を記載
  selector:
    matchLabels:
      app: metabase
  template:
    metadata:
      labels:
        app: metabase
    spec:
      containers:
      - image: metabase/metabase
        name: metabase
        ports:
        - containerPort: 3000
          protocol: TCP
        # ポスグレに格納したアプリデータにアクセスするための設定
        # load-from-h2のJobで設定した環境変数と全く同じ
        envFrom:
        - configMapRef:
            name: metabase-load-vars
        env:
        - name: MB_DB_DBNAME
          valueFrom:
            configMapKeyRef:
              name: postgres-vars
              key: POSTGRES_DB
        - name: MB_DB_USER
          valueFrom:
            configMapKeyRef:
              name: postgres-vars
              key: POSTGRES_USER
        - name: MB_DB_PASS
          valueFrom:
            configMapKeyRef:
              name: postgres-vars
              key: POSTGRES_PASSWORD
  • Metabaseのリソース作成と稼働状況の確認
$ kubectl apply -f deploy.yml 
service/metabase created
deployment.apps/metabase created
$ kubectl get all -l app=metabase
NAME                            READY   STATUS    RESTARTS   AGE
pod/metabase-6c8b6d84bb-gb47j   1/1     Running   0          106s
pod/metabase-6c8b6d84bb-8m8rf   1/1     Running   0          106s


NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/metabase   2/2     2            2           106s

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/metabase-6c8b6d84bb   2         2         2       106s

おまけ

最初はアプリDBをポスグレに移行せずに、下図のようにH2データベースが格納されたボリュームを別途作って、各コンテナのH2データベースがここを共通にアクセスすることを試みていたのだが、上手く行かなかった。

具体的には、一方のコンテナから以下のようなエラーが出力。どうやらデータには一つのH2データベースからしかアクセスできない模様。

2020-11-07 07:45:18,797 ERROR driver.util :: Database connection error
org.h2.jdbc.JdbcSQLException: Database may be already in use: null. Possible solutions: close all other connection(s); use the server mode [90020-197]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:357) ~[metabase.jar:?]

複数のH2データベースからアクセス可能する方法を調べたら、下記サイトで「H2データベースのパス指定でAUTO_SERVER=trueを追加すればOK」という情報を見つける。だがMetabaseのコンテナ起動時のオプションではそのような指定は不可能。(自分で新たにDockerfileから作ればいけるかもしれないが…)

そもそも一つのデータを複数のDBサーバから共有して使おうとすること自体間違っていたのかも。

4
5
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
4
5