Redis+Sentinelを構成する理由
Redisキャッシュサーバーのアクセスの集中を軽減して、スケールするために、読書き可能なRedisマスターと読取り専用のRedisスレーブを組み合わせた構成を取ることがあります。これを Kubernetes上で実現する方法について、確認したのでメモとして残します。今更、HelmやOperatorで構築さればいいじゃない? と言われるかもしれませんが、Redisで高可用性を組む時のRedisの内容を理解しておきたいと思います。
IKS(IBM Cloud Kubernetes Service) https://cloud.ibm.com/ のK8sクラスタ上で動かしたのですが、使用しているマニフェストは、クラウド中立なマニフェストなので、どのクラウドでも動作すると思います。
Redisの監視とマスター選出の機能をになったSentinelが、Redisのプロジェクトから提供されています。Sentinelも可用性とマスター選出投票のために、複数プロセスが機能するようになっています。
Sentinelは、全てのRedisを監視しておき、マスターの停止に対して、次のマスターを選出します。そして、Sentinelに問い合わせることで、その時点でのマスターを教えてくれます。もちろん、Slaveも知ることができます。
Redisのクライアントであるアプリは、Sentinelに問い合わせて、RedisマスターのIPアドレスを求め、Redisのマスターへ書き込みます。また、読取りでは、スレーブをアクセスして、マスターが過負荷になることを回避します。Sentinelに問い合わせるための、プログラム言語のクライアントライブラリは、参考資料 にリンクを挙げておきます。
Redis + Sentinel の高可用性構成を起動する。
それでは、上図の構成をKubernetes上で作っていきます。
Kubernetesのクラスタが作られ、kubectlコマンドで操作できる状態を想定して、書いていきます。
それから、この構成は、参考資料 1 を、そのまま利用しています。 ここではフォークしたリポジトリをクローンしています。
git clone https://github.com/takara9/examples
cd examples/staging/storage/redis
# 起動用 Redisマスター + Sentinel のポッドをスタート
kubectl create -f redis-master.yaml
# Sentinelのサービスを起動 Sentinelにリクエストする時は、これにアクセスする
kubectl create -f redis-sentinel-service.yaml
# Redisサーバーを、レプリケーションコントローラー下で起動する
kubectl create -f redis-controller.yaml
# Sentinelサーバーも、レプリケーションコントローラー下で起動する
kubectl create -f redis-sentinel-controller.yaml
# レプリカ数を増やして、冗長化と性能アップを実施
kubectl scale rc redis --replicas=3
kubectl scale rc redis-sentinel --replicas=3
起動後に、Sentinelのサービス名とIPアドレスを確認しておきます。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.21.0.1 <none> 443/TCP 4d8h
redis-sentinel ClusterIP 172.21.195.107 <none> 26379/TCP 92s
さらに、RedisサーバーとSentinelのポッドのIPアドレス付きリストを確認する。
i$ kubectl get po -o wide
NAME READY STATUS AGE IP NODE
redis-d62nc 1/1 Running 5m28s 172.30.94.152 10.193.10.14
redis-master 2/2 Running 6m2s 172.30.222.152 10.193.10.58
redis-sentinel-f49jh 1/1 Running 5m37s 172.30.94.150 10.193.10.14
redis-sentinel-m6dq8 1/1 Running 5m20s 172.30.222.153 10.193.10.58
redis-sentinel-p4jtb 1/1 Running 5m20s 172.30.94.153 10.193.10.14
redis-vnmsq 1/1 Running 5m28s 172.30.94.151 10.193.10.14
Sentinelに繋いでRedisマスターを求めて、Redisマスターに、テスト用データを書き込む
アプリケーションのクライアント相当としてredis-cliで、Redis+Sentinelの構成へ繋ぎます。
$ kubectl run -it redis-cli --rm --image redis --restart=Never -- bash
If you don't see a command prompt, try pressing enter.
root@redis-cli:/data#
Sentinelへ繋いで、RedisマスターのIPアドレスを求めます。Sentinelのコマンドは参考資料 2 にあります。
root@redis-cli:/data# redis-cli -h redis-sentinel -p 26379
redis-sentinel:26379> SENTINEL get-master-addr-by-name mymaster
1) "172.30.222.152"
2) "6379"
Redisマスターからスレーブへのデータ同期を確認するために、RedisマスターにKeyとValueをセットします。
redis-sentinel:26379> connect 172.30.222.152 6379
172.30.222.152:6379> set abc 1000
OK
172.30.222.152:6379> get abc
"1000"
172.30.222.152:6379> incr abc
(integer) 1001
172.30.222.152:6379> incr abc
(integer) 1002
Redisスレーブをリストして、IPアドレスとポート番号を確認します。
redis-sentinel:26379> SENTINEL slaves mymaster
1) 1) "name"
2) "172.30.94.151:6379"
<中略>
2) 1) "name"
2) "172.30.94.152:6379"
スレーブに繋いで、データが同期されているか確認します。目論見通りにデータが取り出せます。
172.30.222.152:6379> connect 172.30.94.151 6379
172.30.94.151:6379> get abc
"1002"
172.30.94.151:6379> connect 172.30.94.152 6379
172.30.94.152:6379> get abc
"1002"
マスター停止のテスト
RedisマスターのIPアドレスを調べて、kubectlコマンドで、マスターとなっているポッドを削除して、
フェイルオーバーを確認します。
redis-sentinel:26379> SENTINEL get-master-addr-by-name mymaster
1) "172.30.222.152"
2) "6379"
別のターミナルから、マスターのポッドを削除します。
起動時に、ポッドだけで立ち上げたものですが、後から追加したレプリケーションコントローラーのセレクターのラベルと同じにしているため、レプリケーションコントローラーは、このポッドも管理下として見なし、削除されると、新たなポッドを起動して、レプリカ数を維持するように振舞います。
$ kubectl delete po redis-master
pod "redis-master" deleted
レプリケーションコントローラーから起動されたポッドは、Redisスレーブとして参加します。そして、Sentinelの働きで、マスターに昇格するために投票が行われ、新たなRedisマスターが決まります。
redis-sentinel:26379> SENTINEL get-master-addr-by-name mymaster
1) "172.30.94.156"
2) "6379"
まとめ
既にビルドしたコンテナを使っているので、本番利用を考えるには、コンテナのビルドから考えないといけません。これに対して、参考資料1のGitHubに含まれています。
マスタースレーブ方式のスケール可能なキャッシュサーバを Kubernetes上で利用できることが、確認できました。
参考資料
- k8s redis例題, https://github.com/kubernetes/examples/tree/master/staging/storage/redis
- センチネルクライアント, https://redis.io/topics/sentinel-clients
- PHP Redisクライアントライブラリ、https://github.com/jamescauwelier/PSRedis
- Java Redisクライアントライブラリ、https://github.com/redisson/redisson