Edited at

Redash を GKE 上で起動しようとしてやったこと

Redash 沢山作らないといけないとなった時に GKE でやろうとしたら何箇所か手こずったのでメモ書き。

(随時追加)


前提


  • 1 から構成用の yaml ファイルを作るのは辛いので KomposeRedash のソースに含まれている docker-compose.production.yml を変換して使う

  • 当然 HTTPS

  • 作り直しても IP 変わらないように static な IP を指定する

  • postgresql のデータは永続化する

$ kompose convert -f docker-compose.production.yml


tl;dr

ざっくりこんな感じの docker-compose.yaml 作って前述の通り何箇所か修正すれば良い。

$ cat docker-compose.yaml

version: '2'
services:
redash:
image: redash/redash:latest
command: server
depends_on:
- postgres
- redis
ports:
- "5000:5000"
environment:
PYTHONUNBUFFERED: 0
REDASH_LOG_LEVEL: "INFO"
REDASH_REDIS_URL: "redis://redis:6379/0"
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
REDASH_COOKIE_SECRET: **************
REDASH_WEB_WORKERS: 4
labels:
kompose.service.type: NodePort
kompose.service.expose: "redash.example.com"
kompose.service.expose.tls-secret: <tls-secret-resource-name>
restart: always
worker:
image: redash/redash:latest
command: scheduler
environment:
PYTHONUNBUFFERED: 0
REDASH_LOG_LEVEL: "INFO"
REDASH_REDIS_URL: "redis://redis:6379/0"
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
QUEUES: "queries,scheduled_queries,celery"
WORKERS_COUNT: 2
restart: always
redis:
image: redis:3.0-alpine
ports:
- "6379:6379"
restart: always
postgres:
image: postgres:9.5.6-alpine
ports:
- "5432:5432"
labels:
kompose.volume.size: 1Gi
environment:
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- "postgres:/var/lib/postgresql/data"
restart: always

$ kompose convert -f docker-compose.yml --stdout\
| yq -y ".items[]"\
| yq -y "select(.kind == \"Ingress\").metadata.annotations[\"kubernetes.io/ingress.allow-http\"] = \"false\""\
| yq -y "select(.kind == \"Ingress\").metadata.annotations[\"kubernetes.io/ingress.global-static-ip-name\"] = \"<static-ip-label>\""\
| yq -y "select(.kind == \"Deployment\" and .metadata.name == \"redash\").spec.template.spec.containers[0].readinessProbe = {httpGet:{path:\"/static/images/favicon-32x32.png\",port:5000}}"\
| tee gke.yaml


1. Kompose して起動すると Deployment の Name がとても衝突しやすい名称

docker-composer.yaml に記載されたサービス名?が Deployment の Name として使用される変換ロジックなのでさもありなん。

$ kompose up -f docker-compose.production.yml

で起動するだけならさらっとできるけどそんな理由で却下。


A. Namespace を指定する

$ # Ex.

$ kompose convert -f docker-compose.production.yml -o <redash-on-gke.yml>
$ kubectl apply -n <namespace> -f <redash-on-gke.yml>

とか


2. Redash サーバーのフロントに立ってる Worker コンテナが起動しない

こんなエラー

$ kubectl logs $(basename $(kubectl get pod -o name -n <namespace> -l 'io.kompose.service==worker'))

Starting scheduler and 2 workers for queues: queries,scheduled_queries,celery...
[2018-08-28 06:14:10,224][PID:1][INFO][root] Generating grammar tables from /usr/lib/python2.7/lib2to3/Grammar.txt
[2018-08-28 06:14:10,270][PID:1][INFO][root] Generating grammar tables from /usr/lib/python2.7/lib2to3/PatternGrammar.txt
Traceback (most recent call last):
File "/usr/local/bin/celery", line 11, in <module>
...
redis.exceptions.ConnectionError: Error -2 connecting to redis:6379. Name or service not known.


A. Service redis を作る

元の docker-compose.yml で redis は ports が指定されていません。

docker で起動するならそれでも問題ないですが、 GKE で起動するにはクラスター内の別 Pod と通信するための仕様に沿う必要があります。

クラスター内の別 Pod と通信するには Service を用意してあげる必要があります。

また Redis でエラーになってますが、どうせ PostgreSQL も同じことになるので一緒に対応します。

@@ -36,5 +36,9 @@ services:

redis:
image: redis:3.0-alpine
+ ports:
+ - "6379:6379"
restart: always
postgres:
image: postgres:9.5.6-alpine
+ ports:
+ - "5432:5432"


3. Redash サーバーのフロントに立ってる Nginx コンテナも起動しない

こんなエラー

$ kubectl logs $(basename $(kubectl get pod -o name -n <namespace> -l 'io.kompose.service==nginx'))

2018/08/28 06:12:26 [emerg] 1#1: host not found in upstream "redash:5000" in /etc/nginx/conf.d/default.conf:2
nginx: [emerg] host not found in upstream "redash:5000" in /etc/nginx/conf.d/default.conf:2


A-1. serverredash に変更する

GKE では metadata.name がクラスタ内で使用できるホスト名となる。

kompose を介す場合は docker-compose.yml の services に記載したサービス名が該当する。

@@ -7,5 +7,5 @@

workers.
version: '2'
services:
- server:
+ redash:
image: redash/redash:latest


A-2. nginx を使用しない

後述するがそもそも HTTPS 対応において nginx は不要となる。

そのため docker-compose.yml からこの段階で nginx を削除してしまう。

そうすればエラーも何もなくなる。

@@ -33,10 +37,1 @@ services:

restart: always
- nginx:
- image: redash/nginx:latest
- ports:
- - "80:80"
- depends_on:
- - server
- links:
- - server:redash
- restart: always


A-3. 両方やる

どっちにしろ server という名称は用途が分からなくなるので redash に変えてしまうのはあり、という話。


4. Redash サーバーのフロントに立ってる Nginx コンテナだと HTTPS に (多分) できない

HTTP で良ければ nginx の Service のタイプを LoadBalancer にすれば可能。

kompose 経由で指定する場合は labels に kompose.service.type: LoadBalancer を追加する

@@ -53,4 +53,6 @@ services:

- server
links:
- server:redash
+ labels:
+ kompose.service.type: LoadBalancer
restart: always

ただ HTTPS にする方法がなさそう。


A. Ingress を利用する

Ingress という機能がある。

kompose 経由で証明書を使うように指定すると自動的に Ingress を使うための設定も吐き出させれる。

nginx は前述の通り消してしまい、 redash の labels に以下を追加すると SSL 証明書まで設定された HTTPS 通信が実現できる。

Ingress はデフォルトで HTTP ポートも開けるのでこの段階では redash には HTTP/HTTPS の両方でアクセスできる。(HTTP を塞ぐ方法は後述)

また Ingress は実際には GCP Load Balancing backend service

@@ -21,4 +21,7 @@ services:

REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
REDASH_COOKIE_SECRET: veryverysecret
REDASH_WEB_WORKERS: 4
+ labels:
+ kompose.service.expose: "redash.example.com"
+ kompose.service.expose.tls-secret: <tls-secret-resource-name>
restart: always



  • kompose.service.expose: ... は SNI のホスト名として使用されるが不要なら "true" にしておく


  • kompose.service.expose.tls-secret: <tls-secret-resource-name> で証明書を指定する


証明書を <tls-secret-resource-name> を GKE に登録するには

こればかりは秘密鍵を含むので kompose を利用することも出来ない。

$ kubectl create secret tls <tls-secret-resource-name> -n <namespace> --key=privkey.pem --cert=fullchain.pem


5. Ingress で HTTP を塞ぐ

Kompose は対応していないので生成した <redash-on-gke.yml> を書き換える。

具体的には Ingress の metadata.annotations"kubernetes.io/ingress.allow-http": "false" を追加する。


6. Ingress から Service に届かない

待てども繋がるようにならない。

これはいくつか理由がある。

ここが一番手間だった。


A-1. Service を NodePort にする

Ingress から Service に通信するには NodePort にする必要がある。

@@ -24,2 +24,3 @@ services:

labels:
+ kompose.service.type: NodePort
kompose.service.expose: "redash.example.com"


A-2. Ingress のヘルスチェックが通るようにする必要がある。

Ingress は勝手にヘルスチェックしてステータスコード 200 を要求する。

ヘルスチェックするデフォルトのパスは / だが、 redash の / にリクエストすると /login へのリダイレクトになって 302 となるのでヘルスチェックが成功しない。

Kompose は対応していないので生成した <redash-on-gke.yml> を書き換える。

具体的には Deployment redashspec.template.spec.containers[*]readinessProbe を追加する。

@@ -131,6 +131,10 @@ items:

ports:
- containerPort: 5000
resources: {}
+ readinessProbe:
+ httpGet:
+ path: /static/images/favicon-32x32.png
+ port: 5000
restartPolicy: Always

/login にするとログイン画面のアクセスのリミットに引っかかるので画像ファイルへのアクセスにすると良いと思う。


A-3. GCE Load Balancing Backend Services のリソース数上限を上げる

GKE ダッシュボード上でサービスを見る と以下のような警告が Ingress に対して出ている。


googleapi: Error 403: Quota 'BACKEND_SERVICES' exceeded. Limit: 3.0 globally., quotaExceeded


で実際調べて見ると上限に達している。

$ gcloud compute project-info describe --project <project-name> | grep BACKEND_SERVICES -B1 -A1

- limit: 3.0
metric: BACKEND_SERVICES
usage: 3.0

これはもうリソース数上限を上げるか他の Ingress と共用するかしかない。


A-4. PostgreSQL に redash 用のデータを構築する

Ingress から Service に届くようになったはずだけどまだ画面が表示されないはず。

以下のようなエラーとなると思う。

$ kubectl logs $(basename $(kubectl get pod -o name -n <namespace> -l 'io.kompose.service==postgres'))

...
ERROR: relation "queries" does not exist at character 979

これは DB の構築がまだであるためなので redash サーバー経由で postgres のデータベースを構築する。

$ kubectl exec -it --namespace=<namespace> $(basename $(kubectl get pod -o name -n <namespace> -l 'io.kompose.service==redash')) -- /app/bin/docker-entrypoint create_db


7. static な IP を指定したい


A. GCP で払い出しておいた IP を指定することができる。

$ gcloud compute addresses create <static-ip-label> --global

で IP を払い出しておく。

これも docker-compose.yml の段階でどうにかすることは現状できないようなので、 <redash-on-gke.yml> を書き換える。

具体的には Ingress の metadata.annotations["kubernetes.io/ingress.global-static-ip-name"] = "<static-ip-label>" を追加する。


8. データの永続化をしたい


A. volumes を使えば勝手に PersistentVolumeClaim が生成される

@@ -46,6 +46,10 @@ services:

image: postgres:9.5.6-alpine
ports:
- "5432:5432"
- # volumes:
- # - /opt/postgres-data:/var/lib/postgresql/data
+ labels:
+ kompose.volume.size: 1Gi
+ environment:
+ PGDATA: "/var/lib/postgresql/data/db-files/"
+ volumes:
+ - "postgres:/var/lib/postgresql/data"
restart: always


  • volumes がコメントアウトされていてローカルディスクを使用するようになっているので、 docker-compose.yml でコメントアウトを外す

  • 指定した「:」 の左側が PersistentVolumeClaim の名前となるので絶対パスから修正する

  • GKE でマウントした直下にはシステム上のファイルが生成されてしまい空のディレクトリにならないため postgres の初期化ができない


    • そのため環境変数 PGDATA で postgres のデータフォルダの階層を変更する

    • デフォルトの PGDATA=/var/lib/postgresql/data のままで、マウント先を /var/lib/postgresql としても仕様なのか pod 再構築時に必ず初期化してしまって永続化できない




  • kompose.volume.size を使用するとディスクサイズが指定可能


心の声

全部指定したいな...

ソース読んで見ますかね。