GKEでRe;dashを作成する
はじめに
GKEからGoogle Cloud SQLに接続するにはいくつかのパターンがあります。
接続方法 | 備考 |
---|---|
IPアドレス + SSL証明書 | 証明書を用意する必要がある |
IPアドレスのみ | セキュリティ観点から選択肢としてなし |
Cloud SQL Proxy | JavaとGoのライブラリが存在する。 公式ドキュメントにはPythonの例があるが cloud_sql_proxy デーモンを使用する方法のみ記載されているので cloud_sql_proxy を使用しなければならい。 ※第1世代では使用できない。 |
Cloud SQL Proxy Docker Image | サイドカー or service-Replicaset(or DaemonSet)として起動させることが可能。 ※第1世代では使用できない。 |
今回、Re;dashをGKEで起動させるのでセキュリティと実装のスマートさからCloud SQL Proxy Docker Imageを選択します。 また、Worker数(コンテナ数)とするため、サイドカーとしてではなくService-ReplicaSetで起動します。
また、Re;dash管理で使用するRedis及びPostgreSQLはGKEでPersistent Volume + StatefulSetを使用します。 理由としてはGKE関係者とお話させて頂いた際に、社内ではGKEにDB(RDBMS)を構築することがよくあるそうです。
Docker Containerの作成
今回作成するコンテナは以下の通りです。
- Nginx
- Redash Server
- Redash worker
- Postgresql
- Redis
1. Nginx Docker File
Dockerfile-nginx
Nginxの設定でログの出力先にそれぞれ /dev/stdout
, /dev/stderr
を指定するのも良いと思います。
FROM nginx:1.15.5-alpine-perl COPY nginx.conf /etc/nginx/nginx.conf RUN ln -sf /dev/stdout /var/log/nginx/access.log && \ ln -sf /dev/stderr /var/log/nginx/error.log
nginx.conf
httpsを有効にした場合にhealthcheckがリダイレクトしないよう設定を
それ用に設定を行います。
また、redashへのproxyはK8sのServiceを使用して通信を行うのでSevice Nameを redash
とし、
Serviceに対してproxyを行う様に設定します。
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; upstream redash { server redash-server:5000; } server { listen 80 default; gzip on; gzip_types *; gzip_proxied any; location = /healthcheck { return 200; } set $redirect 0; if ($http_x_forwarded_proto = http) { set $recirect 1; } if ($http_x_health_check = on) { set $recirect 0; } if ($recirect = 1) { return 301 https://$host$request_uri; } 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; } } }
2. Redash Server/Worker
dockerhubのredashイメージを使用します。
3. Postgresql
dockerhubのpostgresqlイメージを使用します。
4. Redis
dockerhubのredisイメージを使用します。
Name Spaceの作成
kubectl create namespace redash
k8sクラスタは作成済みの想定していますが、terraformを使用する場合 以下の様にtfファイルを作成すれば用意できます。
ingress用のstatic ipを取得も合わせて行っています。
### GKE resource "google_container_cluster" "gke" { name = "${var.cluster_name}" zone = "${var.zone}" network = "${var.network}" initial_node_count = "${var.initial_node_count}" description = "${var.description}" min_master_version = "${var.min_master_version}" node_version = "${var.node_version}" node_config { machine_type = "${var.machine_type}" disk_size_gb = "${var.disk_size}" oauth_scopes = [ "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/trace.append", "https://www.googleapis.com/auth/monitoring", "https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/servicecontrol", "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-platform.read-only", "https://www.googleapis.com/auth/service.management", "https://www.googleapis.com/auth/service.management.readonly", ] } } resource "google_compute_global_address" "redash-ingress-ip" { name = "redash-ingress" }
Deploymentの作成
1. nginx deployment
apiVersion: apps/v1 kind: Deployment metadata: name: redash-nginx namespace: redash labels: app: redash-nginx spec: replicas: 1 selector: matchLabels: app: redash-nginx template: metadata: labels: app: redash-nginx spec: containers: - name: redash-nginx image: gcr.io/${$PROJECT_ID}/redash-nginx:${REVISION_ID} resources: limits: memory: "256Mi" requests: memory: "256Mi" ports: - containerPort: 80 name: redash-nginx readinessProbe: httpGet: path: /healthcheck port: 80
2. redash server deployment
apiVersion: apps/v1 kind: Deployment metadata: name: redash-server namespace: redash labels: app: redash-server spec: replicas: 1 selector: matchLabels: app: redash-server template: metadata: labels: app: redash-server spec: containers: - name: redash-server image: redash/redash:5.0.2.b5486 resources: limits: memory: "512Mi" requests: memory: "512Mi" command: - /bin/sh - -c - /app/bin/docker-entrypoint create_db; /app/bin/docker-entrypoint server env: - name: POSTGRES_USER valueFrom: secretKeyRef: name: redash-postgresql key: user - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: redash-postgresql key: password - name: PYTHONUNBUFFERED value: "0" - name: REDASH_LOG_LEVEL value: "INFO" - name: REDASH_WEB_WORKERS value: "4" - name: REDASH_REDIS_URL value: "redis://redash-redis:6379/0" - name: REDASH_DATABASE_URL value: "postgresql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@redash-postgresql/redash-user" ports: - containerPort: 5000 name: redash-server
パスワードについてはk8sのsecretに設定しておきます。
kubectl create secret generic redash-postgresql --from-literal=user=redash-user --from-literal=password=<password>
3. redash worker deployment
apiVersion: apps/v1 kind: Deployment metadata: name: redash-worker namespace: redash labels: app: redash-worker spec: replicas: 1 selector: matchLabels: app: redash-worker template: metadata: labels: app: redash-worker spec: containers: - name: redash-worher image: redash/redash:5.0.2.b5486 resources: limits: memory: "256Mi" requests: memory: "256Mi" env: - name: POSTGRES_USER valueFrom: secretKeyRef: name: redash-postgresql key: user - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: redash-postgresql key: password - name: PYTHONUNBUFFERED value: "0" - name: REDASH_LOG_LEVEL value: "INFO" - name: REDASH_REDIS_URL value: "redis://redash-redis:6379/0" - name: REDASH_DATABASE_URL value: "postgresql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@redash-postgresql/redash-user" - name: WORKERS_COUNT value: "2" - name: QUEUES value: "queries,scheduled_queries,celery" args: ["scheduler"]
4. redis deployment
apiVersion: apps/v1 kind: StatefulSet metadata: name: redash-redis namespace: redash labels: app: redash-redis spec: replicas: 1 selector: matchLabels: app: redash-redis serviceName: redash-redis template: metadata: labels: app: redash-redis spec: containers: - name: redash-redis image: redis:5.0.0-alpine resources: limits: memory: "256Mi" requests: memory: "256Mi" ports: - name: redash-redis containerPort: 6379 volumeMounts: - name: redash-redis-persistent-storage mountPath: /data volumeClaimTemplates: - metadata: name: redash-redis-persistent-storage spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 5Gi
redisについてはPersistent Volumeを用いてコンテナが停止してもデータを永続して サービスを提供し続けられる状態にしておきます。 これによりRedisの不具合等があった場合においてもデータロストをあまり気にせずに バージョンアップ等が可能になります。
4. postgresql deployment
apiVersion: apps/v1 kind: StatefulSet metadata: name: redash-postgresql namespace: redash labels: app: redash spec: replicas: 1 selector: matchLabels: app: redash-postgresql serviceName: redash-postgresql template: metadata: labels: app: redash-postgresql spec: containers: - name: redash-postgres image: postgres:11.0-alpine resources: limits: memory: "512Mi" requests: memory: "512Mi" env: - name: POSTGRES_USER valueFrom: secretKeyRef: name: redash-postgresql key: user - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: redash-postgresql key: password ports: - containerPort: 5432 name: postgres volumeMounts: - name: redash-postgresql-persistent-storage mountPath: /var/lib/postgresql/db-data volumeClaimTemplates: - metadata: name: redash-postgresql-persistent-storage spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 20Gi
Redis同様Postgresqlのストレージ領域をPersistent Volumeに保存しておきます。 バックアップについては現時点で機能提供が見つけられない状態でしたが バックアップ用のjobコンテナを作成し、定期実行しておけば然程問題ではないと言えます。
※少しめんどくさいですが・・・
Deploymentのまとめ
manifestファイルが増えるとSpinnakerを使用する事を検討した方がいい!
しかし、小さく始めるのであればSpinnakerは起動するだけでもリソースをある程度大きく消費するので
既に使用しているCI/CDツールとSkaffoldを使用する事をお勧めしまします。
メモリやストレージ容量の設定はミニマムに開始する為の設定を行ったので必要であればチューニングを行って下さい。
Serviceの作成
Ingress -> Nginx
と接続させるため、 Nginxはクラスタ外からアクセスできる
NodePortを使用し、 クラスタ内からアクセスできるNginx -> Redash
, Redash -> Redis
,
Redash -> Postgresql
はClusterIPを使用します。
具体的な書き方としては今回以下の様な設定をしました。
1. Nginx service
apiVersion: v1 kind: Service metadata: name: redash-nginx namespace: redash labels: app: redash-nginx spec: type: NodePort ports: - port: 80 targetPort: 80 protocol: TCP selector: app: redash-nginx
2. redash server service
apiVersion: v1 kind: Service metadata: name: redash-server namespace: redash labels: app: redash-server spec: type: ClusterIP ports: - port: 5000 targetPort: 5000 protocol: TCP selector: app: redash-server
3. redis service
apiVersion: v1 kind: Service metadata: labels: app: reddash-redis name: redash-redis spec: type: ClusterIP ports: - port: 6379 targetPort: 6379 protocol: TCP name: redash-redis selector: app: redash-redis
4. postgresql service
apiVersion: v1 kind: Service metadata: labels: app: redash-postgresql name: redash-postgresql spec: type: ClusterIP ports: - port: 5432 targetPort: 5432 protocol: TCP name: redash-postgresql selector: app: redash-postgresql
Ingressの作成
今回、HTTPSでのアクセスを想定しているのでTLSの設定も実施しています。 証明書については提供事業者によって手順は異なるとは思いますが 作成済みと仮定し、GKEへの登録は公式ドキュメントの手順通りに進めれば 問題ないと思います。
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.allow-http: "false" kubernetes.io/ingress.global-static-ip-name: redash-ingress generation: 1 name: redash namespace: redash spec: backend: serviceName: redash-nginx servicePort: 80 tls: - secretName: hogehoge-com
まとめ
Redashは基本的に非機能要件であるのでService提供用のGKE Clusterに作成するのはどうなのかと思いますが 非機能要件をまとめたGKE Clusterを作成するパターンもありだなと感じました。
一方で管理するGKE Clusterを増やすのも面倒なので 1つのGKE Clusterに専用nodeを作成し、node affinity 機能を使用した パターンも実際に使用してみたので次回はそのデザインパターンについての記事を書いてみようと思います。
また、K8sでPersistent Volumeを使用した場合のバックアップ方法についても次回以降で解説をいたいと思います。