ec2で稼働中のredash環境をkubernetesに移そう

# この記事について

https://redash.io/help/open-source/setup
にて公開されているamiを利用してec2で作成したredashの環境をkubernetesに引っ越します。

それの作業メモです。
あまり細かいことは気にせず現在の環境と同じように動いて見えるところを目指します。
細かい部分はおいおいやりたい。

# モチベーション

redashがよくハングして意外と運用が面倒なので監視体制の整っているk8s環境下に置きたかった。
（よくハングするのは使い方が悪いせいだとは思うけど）

ので、既にk8s環境を持ってる人向けですね、これのためにk8s立てるのはさすがにちょっとアレ。

# 流れ

ec2の中で動いてるdocker環境をk8sに移すだけ

# 作業

## 現行redash環境の把握

amiで立てたらパッと使えるので便利すぎて内部は全然把握してませんでした。
で、中身を見るとなんとdockerが動いてました。
コンテナが7つも動いていて少しびっくり。
こいつらをk8sで動かせばいいんすね、しかし既にdockerになってるとはラッキー。

## ポイントになりそうな部分

とりあえずの問題はステータスを持つ部分をどうするか。
稼働中のコンテナを見ると `redis` と `postgres` のコンテナがあり、これらがそれに該当しそうです。
inspectしてみたところ、postgresはhostのvolumeをbindしていますが、redisはしておらず、データボリュームを面倒見てやる必要があるのはpostgresだけの様子。

## 作戦

- とりあえずk8s内に現行docker環境と同じようなものを作る
- postgresqlのデータを移す

これだけで無事動いたらいいなあ

## 作成

https://github.com/getredash/redash/blob/master/setup/docker-compose.yml

これを参考にしてkubernetesのmanifestを作成します。
環境変数などは深く考えずにそのまま使います。
imageのバージョンはとりあえず現環境を引き継ぎました。
configMapは現環境の`/opt/redash/env`をコピーしたものです。
明らかにsecretな項目もありますがここは例なので、まあ。

数少ないポイントとしてはdocker-compose.yamlの`command`は`args`にすること。
（`entrypoint`が`commands`に相当するらしいです）
あとはdockerでhost名でやりとりしてた部分を直すこと。
nginxイメージの中にも一箇所あるので書き直したconfigMapをマウントします。
（今回は全部のコンテナを強引に一つのpodに突っ込んだのでlocalhostですがpodを独立させる場合はよしなにやる感じで）

で、このような感じになりました。

```yaml:redash.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: redash
data:
  PYTHONUNBUFFERED: "0"
  REDASH_LOG_LEVEL: INFO
  REDASH_REDIS_URL: redis://localhost:6379/0 #localhostの部分が元はredisだった
  POSTGRES_PASSWORD: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
  REDASH_COOKIE_SECRET: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
  REDASH_DATABASE_URL: postgresql://postgres:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@localhost/postgres #localhostの部分が以下略
  REDASH_DATE_FORMAT: YYYY/MM/DD
  REDASH_HOST: http://redashhost.dummy
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx
data:
  default.conf: |
    upstream redash {
      server localhost:5000; #localhostの部分が云々
    }

    server {
      listen   80 default;

      gzip on;
      gzip_types *;
      gzip_proxied any;

      location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;

        proxy_pass       http://redash;
      }
    }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redash
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redash
  template:
    metadata:
      labels:
        app: redash
    spec:
      containers:
        - name: server
          image: redash/redash:5.0.2.b5486
          envFrom:
            - configMapRef:
                name: redash
          args:
            - server
          env:
            - name: REDASH_WEB_WORKERS
              value: "4"
        - name: scheduler
          args:
            - scheduler
          image: redash/redash:5.0.2.b5486
          envFrom:
            - configMapRef:
                name: redash
          env:
            - name: QUEUES
              value: celery
            - name: WORKERS_COUNT
              value: "1"
        - name: scheduled-worker
          image: redash/redash:5.0.2.b5486
          envFrom:
            - configMapRef:
                name: redash
          args:
            - worker
          env:
            - name: QUEUES
              value: "scheduled_queries,schemas"
            - name: WORKERS_COUNT
              value: "1"
        - name: adhoc-worker
          image: redash/redash:5.0.2.b5486
          envFrom:
            - configMapRef:
                name: redash
          args:
            - worker
          env:
            - name: WORKERS_COUNT
              value: "2"
            - name: QUEUES
              value: queries
        - name: nginx
          image: redash/nginx:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d
        - name: redis
          image: redis:3.0-alpine
        - name: postgres
          image: postgres:9.5.6-alpine
          envFrom:
            - configMapRef:
                name: redash
      volumes:
        - name: nginx-config
          configMap:
            name: nginx
```

`service`や`ingress`は各々の環境に都合よくやってください。

これで試しにアクセスすると500になると思います。
本来走るはずのデータベースの初期化スクリプトがパスワードとかを先に渡した都合で走らなかったとかですかね。
気にせずデータを移します。

redashで使っているデータベース名は`postgres`みたいです、意外と適当ですね。
これをdumpします。
現環境のhostで、

```
# docker exec [postgresのコンテナ名] pg_dump postgres -U postgres > ./datafile
```

とすればdumpファイルの`./datafile`が出来上がるはずです。
これをscpかなんかでローカルにコピーしてから、

```
$ kubectl cp ./datafile -c postgres [redashのpod名]:/datafile
```

としてpostgresコンテナにコピーします。
コピーしたらpostgresコンテナにログインして、

```
# psql postgres -U postgres < /datafile
```

とすればデータの移行は完了。

なんとあっさりアクセスできるようになりました。
適当にごそごそ動かしてみたけど問題なさそう。
さすがはdockerですね、信者になってしまうわ。

# TODO

## データの永続化
このままだとpod消したらせっかく移行したデータが消えます。
hostのボリュームとか外部ボリュームを使ってなんとかしないとダメです。
私はawsを使っているのでEFSにしました。
マウントするpathは `/var/lib/postgresql/data` です

## 監視
k8s環境に置いたことによって各コンテナのcpu使用率など基本的なメトリクスは簡単に見られるようになりました。
あとはredashのステータスの監視なのですが `/status.json` にステータスを見られるエンドポイントが用意されています。
これをよしなに監視すればよいと思います。
（prometheusのexporter作った https://github.com/buildsville/redash-exporter ）

## containerの独立
とりあえずひとつのpodに全コンテナ詰め込みましたがこれはちょっとあんまりなのでバラバラにしたいです。
schedulerとかworkerがどういう関係なのかよくわからない…
これといって特に何もしてなさそうなnginxコンテナはおそらく不要になりそう。

## liveness/readiness
とりあえずserverの`/login`にhttpGetしてみたんですけどリミッターがかかってるらしく429を返すようになります。
`REDASH_THROTTLE_LOGIN_PATTERN`で設定できるらしいですが別のエンドポイントにするとかtcpSocketにするとかはまあお好みで。

## requests/limits

現状確認してる範囲だと、schedulerがよく暴走してcpuを食いつぶすらしいのでlimitを設定しておかないと危険です。
（schedulerの暴走はちゃんと設定してないからとか？まあそのへんはおいおい）

# 蛇足

もうすぐhelmのチャートがリリースされるようですよ…
https://github.com/helm/charts/pull/5071

おわり