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を使用した場合のバックアップ方法についても次回以降で解説をいたいと思います。