Kubernetes 上に Redis Sentinel を構築する
GCP環境でRedisを使う
GCP には Cloud Memorystore という Redis 互換のマネージドサービスがあるのですが、対応している Redis のバージョンが古い(4系)ので自前で GKE 上に Redis を立てることにしました。どうせ立てるなら master/slave 構成のほうが良いだろうということで Redis Sentinel にしました。
環境
- GKE 1.13.6
- Redis 5
Redis Sentinelって何?
Redis Sentinel は master/slave 構成の Redis の監視を行ってくれるサーバソフトウェアです。自動フェイルオーバー機能を持っています。
Redis Sentinelの構成
構成は以下のようになります。
上の図では省略しましたが Sentinel は Sentinel 同士で繋がっていてすべての Redis を監視しています。
Redisの設定
Redis の Config ファイルとコンテナ起動時に必要なスクリプトを作成します。
RedisのConfigファイルを作成する
Redis の master/slave 用の Config ファイルと Sentinel 用の Config ファイルを作成します。 はじめにマスター用の Config ファイル master.conf を作成します。
bind 0.0.0.0 port 6379 dir /redis-data
次にスレーブ用の Config ファイル slave.conf を作成します。
bind 0.0.0.0 port 6379 dir /redis-data slaveof redis-0.redis 6379
最後に Sentinel 用の Config ファイル sentinel.conf を作成します。
bind 0.0.0.0 port 26379 sentinel monitor master redis-0.redis 6379 2 sentinel parallel-syncs master 1 sentinel down-after-milliseconds master 10000 sentinel failover-timeout master 20000
redis-0.redis をマスターに設定し sentinel でマスターを監視するように config に指定しています。 sentinel.confの設定内容については以下の記事が参考になります。
コンテナ起動時に実行するスクリプトを作成します
Redis コンテナ起動時に実行するスクリプト init.sh を作成します。どのコンテナをマスターにするか決定するためのものです。
#!/bin/bash if [[ ${HOSTNAME} == 'redis-0' ]]; then redis-server /redis-config/master.conf else redis-server /redis-config/slave.conf fi
Sentinel 用のスクリプト sentinel.sh を作成します。マスターが立ち上がるのを待ってからredis-sentinel
コマンドを実行します。
#!/bin/bash while ! ping -c 1 redis-0.redis; do echo 'Waiting for server' sleep 1 done redis-sentinel /redis-config/sentinel.conf
Redis 関連のファイルは以上で作成完了です。ファイル構成は以下のようになります。
|-- master.conf
|-- slave.conf
|-- sentinel.conf
|-- init.sh
`-- sentinel.sh
Redisの設定をConfigMapにまとめる
RedisのConfigファイルとコンテナ起動時に使うスクリプトをまとめて redis-config という名前の ConfigMap を作成します。
$ kubectl create configmap \ > --from-file=slave.conf=./slave.conf \ > --from-file=master.conf=./master.conf \ > --from-file=sentinel.conf=./sentinel.conf \ > --from-file=init.sh=./init.sh \ > --from-file=sentinel.sh=./sentinel.sh \ > redis-config
ワークロードの作成
次に Redis コンテナを定義する redis.yaml を作成します。デプロイメント(Deployment)ではなくステーフルセットを使うとポッドに ID が採番されポッド名-インデックス
の形式でポッドにアクセスできます。replicas に3を指定しているので redis-0, redis-1, redis-2 という名前のポッド(Pod)が生成されます。Redis Sentinel 構成にする場合は Redis のコンテナが最低でも3つ以上必要なのとPodのIPアドレスをPod名から取得したかったのでこのような構成にしています。
apiVersion: apps/v1 kind: StatefulSet metadata: name: redis spec: selector: matchLabels: app: redis replicas: 3 serviceName: redis template: metadata: labels: app: redis spec: initContainers: - image: busybox:latest name: redis-init command: [sh, -c, cp -L /tmp/* /config] volumeMounts: - mountPath: /tmp name: tmp - mountPath: /config name: config containers: - command: [sh, -c, source /config/init.sh] image: redis:5-alpine name: redis ports: - containerPort: 6379 name: redis volumeMounts: - mountPath: /config name: config - mountPath: /redis-data name: data - command: [sh, -c, source /config/sentinel.sh] image: redis:5-alpine name: sentinel ports: - containerPort: 26379 name: sentinel volumeMounts: - mountPath: /config name: config volumes: - configMap: name: redis-config name: tmp - emptyDir: name: config - emptyDir: name: data
サービスの作成
Redis にアクセスするためのサービスを作成します。clusterIP に None を設定することで Pod の IPアドレスを直接取得することができます。
apiVersion: v1 kind: Service metadata: name: redis spec: ports: - port: 6379 name: port-redis - port: 26379 name: port-sentinel clusterIP: None selector: app: redis
IP アドレスを設定しないサービスのことを Headless サービスと呼びます。今回のように Headless Service + StatefulSet の組み合わせで使うことが多いようです。Headless サービスの説明は以下を参照してください。
k8s 1.13以降はConfigMapが読み取り専用になったので注意が必要です
Redis Sentinel は Config ファイルにアクセスして内容を更新します。ConfigMap のままでは正常に動作しません。なのでボリュームに ConfigMap を読み込んでから書き込み可能な emptyDir なボリュームにファイルをコピーする必要があります。
# ファイルコピー部分の抜粋 initContainers: - image: busybox:latest name: redis-init command: [sh, -c, cp -L /tmp/* /config] volumeMounts: - mountPath: /tmp #読み取り専用 name: tmp - mountPath: /config #書き込み可能 name: config
ConfigMap はボリューム作成時にシンボリックリンクとして作成されリンク先が読み取り専用に設定されます。なのでdefaultMode
で書き込み権限を付与しても書き込みできるようにはなりません。
Redisのデプロイ
サービスをデプロイします。
$ kubectl apply -f service.yaml
ステートフルセットをデプロイします。
$ kubectl apply -f redis.yaml
確認
redis-2 にアクセスして IP アドレスが返ってくるか確認します。
$ kubectl exec redis-2 -c redis \ -- redis-cli -p 26379 sentinel get-master-addr-by-name master 10.0.9.6 6379
参考書籍
こちらの「入門 Kubernetes」の14章を参考にしました。
参考
ConfigMap の読み取り専用を回避する方法
sentinel.conf の設定