🔖

Cloud Run + Metabaseで起動しないトラブル

2022/12/08に公開

3行まとめ

原因はきちんと解き明かせていないが以下が起きた。

  1. 何らかの理由でとあるMetabaseコンテナがロックを取得したが、正しくロックを破棄せず終了
  2. 1.の要因として、最小コンテナ1、最大コンテナ2で稼働させていたからが考えられる
  3. 新しくコンテナが起動しようとしてもロックが残っているので起動できず、

調査

状況

MetabaseやCloud Runの説明は本家を参照していただくとして、あるタイミングからCloud Run + Metabaseで稼働させていた環境にアクセスができなくなった。

コンテナのCPU使用率が異常に高く、何らかのクエリが暴走しているのかと思ったため再デプロイを行うものの事象は収束しなかった。

Metabaseの確認

Metabaseへブラウザ経由でアクセスすると、ログイン画面にすら遷移できず「Service Unavailable」の文字が表示される。

ログを確認すると、Metabaseの起動プロセスは何段階かあるうちの特定段階で落ちていた。

  1. JVMの起動
  2. Metabase用DBへの接続
  3. DBのマイグレーション
  4. Metabaseアプリケーション本体の起動

この時、3.のDBマイグレーションを行おうとして、lockがかかっているためにJVMインスタンスが終了 -> 再起動を繰り返しその際の起動処理でCPUを使っている様子だった。

Cloud Loggingで確認すると、以下のようなログを確認できた。

ERROR metabase.core :: Metabase Initialization FAILED
liquibase.exception.LockException: Database has migration lock; cannot run migrations. You can force-release these locks by running `java -jar metabase.jar migrate release-locks`. at metabase.db.liquibase$wait_for_migration_lock_to_be_cleared$fn__30994.invoke(liquibase.clj:125) at

復旧に向けてもう少し深堀り

java -jar metabase.jar migrate release-locks を実行すれば良いことは分かった。

具体的に 3.DBのマイグレーションでなぜコケるのか確認すると、 databasechangeloglock に値が入っていることが原因だった。
当時のログを消失したため、どういった内容だったかは覚えていないが、lockgranted / lockedbyカラムに値が入った状態のレコードが挿入されていた。

### 以下は、ロックされていない状態
metabasedb> select * from databasechangeloglock;

metabasedb=> select * from databasechangeloglock;
 id | locked | lockgranted | lockedby 
----+--------+-------------+----------
  1 |    f   |             | 
(1 row)

同様に困っている人が書いたであろう内容に出会ったが、該当のテーブルをtruncateすることでも良いと記載はある。
https://discourse.metabase.com/t/docker-container-fails-with-migration-lock/19093
一方で、別の記事では「マイグレーションがどこまで行われたかわからなくなるのでtruncateはするな」という記載も見つける。

ということで初志貫徹、ログで示されている通りコマンドを実行する。

コマンド実行

しかしながら、Cloud Runで実行している場合コンテナの中にはログイン(e.g. docker exec)できない。
そのため、Metabaseコンテナの実行時の引数に渡そうと考えたが、(勘違い / 焦りにより)うまく実行できなかった。

そのため、cloud_sql_proxy + ローカルでのmetabase起動を行うことで対応する方向にシフトした。

事前準備

  1. ローカルPCへgcloudのインストールおよびログイン
  2. ローカルPCへcloud_sql_proxyのインストール
  3. metabaseバイナリのダウンロード
  4. JDKのインストール

ローカルから実行する

  1. gcloud authコマンドを実行してログインする
    $ gcloud auth

  2. cloud_sql_proxyを実行し、起動できないMetabaseが利用しているCloud SQLインスタンスへ接続する
    $ /path/to/bin/loud_sql_proxy -instances=YOUR_CLOUD_SQL_INSTANCE=tcp:10000

  3. 以下を実行する(表記しやすいようにワンライナーで記載しているが、環境変数として定義しても良い)

$ MB_JETTY_PORT=13000 \
  MB_DB_TYPE=postgres \
  MB_DB_USER=USER_NAME \
  MB_DB_PORT=10000 \
  MB_DB_DBNAME=DB_NAME \
  MB_DB_PASS=PASSWORD \
  MB_DB_HOST=127.0.0.1 \
  java -jar metabase.jar migrate release-locks
  1. 実行完了後、再度metabaseにアクセスし問題ないことを確認する

反省

以下を確認する限り、デフォルトでは以下のシェルがコンテナ起動時に実行される
https://github.com/metabase/metabase/blob/master/bin/docker/run_metabase.sh

また、実行時に渡したオプションも実行するようなシェルスクリプトとなっているため
Cloud Run実行時のコマンドおよび引数に以下を引き渡せば良さそうだった。

コンテナコマンド: "/app/run_metabase.sh"
コンテナの引数: "java -jar metabase.jar migrate release-locks"

Discussion