🦀

Redashをv10.1にバージョンアップしました

2023/12/19に公開

こんにちは、こんばんは、おはようございます。LAPRASでエンジニアのような何かをしているdenzow です。この記事はLAPRAS Advent Calendar 2023 17日目の記事です。
またLAPRAS Advent Calendar 2021 12日目の記事でもあります。

まとめると

LAPRAS社では長らくRedashを利用しており、クエリも先程7000を超えた程度に活用されています。導入当初はv3をEC2で動かしていたRedashが、気がつけばv8 + ECSの構成に変わり、そのままEKSに引っ越しをしていましたが、この度漸くv10.1の最新(Release 2021/11/24)に移行しました。色々踏んだので、そこを書いておきますので誰かの検索に引っかかればと思います。

バージョンアップのモチベ

運用上、実はv8でもそれほど困ってるところはなかったので中々重い腰があがりませんでしたが、アドカレネタになるかなということで勢い良くv10.1にしました。

LAPRASのRedashの構成

LAPRASではプライベートリポジトリに登録したRedashのDocker Imageを元にEKS上にPodをデプロイしています。メタデータはPostgreSQLのRDSを使用しています。直接パブリックのredashのイメージを使っていないのは以下の点からです。

  • docker hubの制限や障害の影響に引きずられたくない
  • 一部独自の拡張を利用したいため、イメージ作成時点でコードにパッチしたい
  • gevent等を追加したい(もしかしてもう不要かも…?)

バージョンアップ手順

まずは、v10.1用のイメージを作りますが、これはプライベートリポジトリのDockerファイルを更新するだけなので割愛します。

v10.1化は本家を参考に進めます。

Upgrading

簡素なものですが、v10にする際にservicesschedulerコンテナから不要になったQUEUESWORKERS_COUNT を削除し、 workerQUEUES に"periodic emails default" を指定するようにあります。また、Redashのバージョンアップ時はメタデータのDBのスキーマを更新するためにpython ./manage.py db upgrade を実行する必要があります。

メタデータDBのバックアップ

とても大事。DBのマイグレーションが走るので戻すの面倒なので。

manage.py db upgrade の実行

こちらはEKSなのでk8sのjobで実行しました。kube-job というショットでJOBを実行するOSSを利用しました。LAPRASではkube-jobは各所で使われています。job.yamlを作成し引数として渡して実行しています。

job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: redash-adhoc-migrate-job
  namespace: XXXX
  labels:
    app: redash-adhoc-migrate-job
spec:
  :
    spec:
      containers:
        - name: redash
          image: XXXX
          imagePullPolicy: Always
          args: ["python ./manage.py db upgrade"]
          envFrom:
		  :
          resources:
            :
      restartPolicy: Never
  backoffLimit: 0

こちらは問題なく実行されました。

deploymentの変更

基本的にはimageをv10.1のものに入れ替えて、ドキュメント通りにWORKERS_COUNTQUEUES を変更しました。

       containers:
         - name: redash
-          image: v8なimage
+          image: v10なimage
           args: ["/usr/local/bin/gunicorn", "-b", "0.0.0.0:XXXX", "--name", "redash", "-w", "4", "-k", "gevent", "-t", "180", "redash.wsgi:app"]
           ports:
@@ -122,7 +122,7 @@ spec:
                       - "node"
       containers:
         - name: redash
-          image: v8なimage
+          image: v10なimage
           args: ["worker"]
           envFrom:
             - secretRef:
@@ -137,7 +137,7 @@ spec:
             - name: WORKERS_COUNT
               value: "2"
             - name: QUEUES
-              value: "queries,scheduled_queries,celery,schemas"
+              value: "queries,periodic emails default"
@@ -195,7 +195,7 @@ spec:
                       - "node"
       containers:
         - name: redash
-          image: v8なimage
+          image: v10なimage
           args: ["scheduler"]

-            - name: WORKERS_COUNT
-              value: "1"
-            - name: QUEUES
-              value: "celery"
             - name: REDASH_THROTTLE_LOGIN_PATTERN
               value: "50/second"

正常にアクセスできるようになりました。

起動後の問題

勝ったな、と思ったのですがRefresh Schemaが終わらず、スキーマ一覧が一生取得されません。 当たり前といえばそうなのですがキューに積まれたスキーマのリフレッシュを行うWokerがいないからです。ドキュメント通りなのにこれは、、とは思いますがきっと意図を読み違えているのでしょう。QUEUESschemas を追加することで解消します。

また、スケジュールされたクエリも実行されていないことに気が付きました。こちらも同様にscheduled_queries を見ているWorkerがいないためなのでQUEUES に追加して解消しました。(なんかschedulerコンテナ何してんだろって気持ちになるけどいいのかな)

             - name: QUEUES
-              value: "queries,scheduled_queries,celery,schemas"
+              value: "scheduled_queries,schemas,queries,periodic emails default"

いくつかのバグ

ダッシュボードのソートでInternal Server Error

ここをクリックするとInternal Server Errorになります。

サーバ側ではこんなエラーが記録されています。

     e, statement, parameters, cursor, context
   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1473, in _handle_dbapi_exception
     util.raise_from_cause(sqlalchemy_exception, exc_info)
   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 398, in raise_from_cause
     reraise(type(exception), exception, tb=exc_tb, cause=cause)
   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 152, in reraise
     raise value.with_traceback(tb)
   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1249, in _execute_context
     cursor, statement, parameters, context
   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 580, in do_execute
     cursor.execute(statement, parameters)
 sqlalchemy.exc.ProgrammingError: (psycopg2.errors.InvalidColumnReference) SELECT DISTINCT ON expressions must match initial ORDER BY expressions
 LINE 2: FROM (SELECT DISTINCT ON (dashboards.created_at, dashboards....

PostgreSQLが受け取れない構文のSQLが発行されているためで、Redashのバグです。すでにBUGFIX: Fixing dashboard order by name #5645 で修正されています。v10.1の次が長らく出てないですが、最近動きがありそうなのですし影響も軽微なバグなのでリリースを待ちます。

内部APIの戻り型の変更

redasql というredashに対してクエリを実行するCLIツールを作って仕事でも使ってるのですが、PostgreSQLデータソースに接続した際にクラッシュするようになりました。確認したところ、/api/data_sources/ID/schema というスキーマ一覧を戻すAPIの戻り値が、PostgreSQLの場合以下のように変更されていました。

v8
    {
        'schema': [
            {
                'name': 'city',
                'columns': ['ID', 'Name', 'CountryCode', 'District', 'Population']
            },
        ]
    }
v10.1(多分v10も)
    {
        'schema': [
            {
                'name': 'city',
                'columns': [
                    {'name': 'id', 'type': 'integer'},
                    {'name': 'created_at', 'type': 'timestamp without time zone'},
                ]
            },
        ]
    }

他にも形式変更があったデータソースはあるかもしれませんが、少なくともMySQLではそうなっておらず一部だけ変更されたようです。

PostgreSQLデータソースの該当箇所はこちら

カラム名に加えてデータ型も取得するように変更されていることがわります。

データ型がAPIから取れる分には嬉しいのですが一部データソースだけなので、一旦redasqlはデータ型を無視する方向で修正しています。

アラートのカスタムテンプレートの変数名変更

リリース後、Slackに通知されるアラートの内容がおかしくなっているという報告で気が付きました。
v8時点ではテスト的に導入されていたアラートのカスタムテンプレート機能ですが、v10(v9時点で)から変数名が変更されていたためでした。

こちらの記事 を参考にさせていただきました。

なぜか、現在のredashでv8.0.0とタグが打たれているコードではrowsではなくQUERY_RESULT_VALUEで実装されているのでソースを見てるときには気がつけなかったので助かりました…

現在のv8.0.0のアラートのコード

原因がわかったので直していくかとアラート一覧を開くと71件もあった上に、画面を開いても設定済みのテンプレートが表示されず、心が折れたのでメタデータDBの中身を直接更新します。

v8(いつのかわからない)の時点ではカスタムテンプレートはalertテーブルのoptionsカラムにJSONとしてtemplate属性に保存されています。しかし以降はcustom_subjectcustom_bodyという属性に保存されるように変更されていました。まずはここを変更しないと、RedashのUI上からテンプレートを更新することができません。

templateとsubjectをcustom_subject/custom_bodyに割り当てるSQL
update
alerts
set
options = (options::jsonb||jsonb_build_object(
    'custom_subject', to_jsonb(options::jsonb->'subject'),
    'custom_body', to_jsonb(options::jsonb->'template')
))::text
where
    options::jsonb ? 'template';

そもそも弊社はSlack連携しか使っていないのでsubject属性が参照されていないのですが、一応移植しました。また、optionsカラムはJSONが入っているもののDBの型としてはText型なのでjsonにキャストしながら組み換えをしています。

この時点でUI上からは設定済テンプレートが更新可能になっています。さらに変数名の変更を行います。幸い弊社ではrowsしか使われていなかったため、そのままQUERY_RESULT_ROWS に置き換えを行います。

rowsをQUERY_RESULT_ROWSに書き換えるSQL
update
alerts
set options = replace(options, 'rows', 'QUERY_RESULT_ROWS')
where options like '%row%

仮にrowという文字列が入っていても置き換えられてしまうので、実際はもう少し丁寧にやったほうがいいと思いますが、問題になりそうな箇所もなかったためこちらで済ませました。今の所、アラートの内容に関して問題も見つかっていないので正しく置き換えができているようです。

まとめ

今更ながら無事にv10.1にアップグレードできて良かったです。v11なのかRedash 2024なのかわかりませんが、ぼちぼち次のリリースもでるような気がしていますので来年も新しいバージョンのRedashでお会いできれば幸いです。

Discussion