2019年6月1日土曜日

Redis Cluster を k8s 上に構築する

概要

前回 k8s 上に Redis Sentinel 環境を構築するところまでやってみました
今回は Redis Cluster に挑戦したいと思います
StatefulSet と ConfigMap を使って構築しています

環境

  • macOS 10.14.5
  • minikube v0.28.2
  • Redis 5.0.5

ConfigMap

  • vim redis_cluster_configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-cluster
data:
  redis.conf: |+
    port 6379
    cluster-enabled yes
    cluster-config-file /data/nodes.conf
    cluster-node-timeout 5000
    appendonly yes
  • kubectl apply -f redis_cluster_configmap.yml

StatefulSet

  • vim redis_cluster_sts.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
spec:
  selector:
    matchLabels:
      app: redis
  serviceName: "redis"
  replicas: 6
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis
        command: ["redis-server", "/conf/redis.conf"]
        ports:
        - containerPort: 6379
          name: client
        - containerPort: 16379
          name: gossip
        volumeMounts:
        - name: data
          mountPath: /data
        - name: conf
          mountPath: /conf
      volumes:
      - name: conf
        configMap:
          name: redis-cluster
          defaultMode: 0755
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
  • kubectl apply -f redis_cluster_sts.yml
  • kubectl exec redis-cluster-0 redis-cli info cluster
# Cluster
cluster_enabled:1

Headless Service

あとからわかったのですが redis-cli --cluster create はホスト名では使えないようです
なので Headless Service のデプロイは不要です

  • vim redis_cluster_service.yml
apiVersion: v1
kind: Service
metadata:
  name: redis
  labels:
    app: redis
spec:
  ports:
  - port: 6379
    name: client
  - port: 16379
    name: gossip
  clusterIP: None
  selector:
    app: redis
  • kubectl apply -f redis_cluster_service.yml
  • kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
/ # nslookup redis-cluster-0.redis
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      redis-cluster-0.redis
Address 1: 172.17.0.7 redis-cluster-0.redis.default.svc.cluster.local

クラスタ作成

kubectl exec -it redis-cluster-0 -- \
redis-cli --cluster create \
172.17.0.7:6379 \
172.17.0.8:6379 \
172.17.0.9:6379 \
172.17.0.10:6379 \
172.17.0.11:6379 \
172.17.0.12:6379 \
--cluster-replicas 1
  • kubectl exec redis-cluster-0 -- redis-cli --cluster info 172.17.0.7:6379
172.17.0.7:6379 (922c7825...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.9:6379 (189ed07f...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.8:6379 (a7d9f418...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
  • kubectl exec redis-cluster-0 -- redis-cli cluster nodes
189ed07f5f7d736d623e5da3b384193ac620f777 172.17.0.9:6379@16379 master - 0 1559107652051 3 connected 10923-16383
a7d9f418035595e68231b2baf079c65064a43a96 172.17.0.8:6379@16379 master - 0 1559107651000 2 connected 5461-10922
922c7825f518a6af2d29ce934c09f43d72f97951 172.17.0.7:6379@16379 myself,master - 0 1559107650000 1 connected 0-5460
8c6c41a4179261abad6f521ce0e21a6c05aed317 172.17.0.10:6379@16379 slave 189ed07f5f7d736d623e5da3b384193ac620f777 0 1559107651347 4 connected
7534abbae8a6ffe4183eb45b294507e19fed5516 172.17.0.11:6379@16379 slave 922c7825f518a6af2d29ce934c09f43d72f97951 0 1559107651548 5 connected
a000022e75dbbeaadecd48b21c2a9322bad73c28 172.17.0.12:6379@16379 slave a7d9f418035595e68231b2baf079c65064a43a96 0 1559107651000 6 connected

failover 動作確認

  • kubectl delete po redis-cluster-0
  • kubectl exec redis-cluster-1 -- redis-cli --cluster info 172.17.0.8:6379
172.17.0.9:6379 (189ed07f...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.11:6379 (7534abba...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.8:6379 (a7d9f418...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
  • kubectl exec redis-cluster-1 -- redis-cli cluster nodes
a000022e75dbbeaadecd48b21c2a9322bad73c28 172.17.0.12:6379@16379 slave a7d9f418035595e68231b2baf079c65064a43a96 0 1559107872000 6 connected
8c6c41a4179261abad6f521ce0e21a6c05aed317 172.17.0.10:6379@16379 slave 189ed07f5f7d736d623e5da3b384193ac620f777 0 1559107873547 4 connected
189ed07f5f7d736d623e5da3b384193ac620f777 172.17.0.9:6379@16379 master - 0 1559107872541 3 connected 10923-16383
922c7825f518a6af2d29ce934c09f43d72f97951 172.17.0.7:6379@16379 slave 7534abbae8a6ffe4183eb45b294507e19fed5516 0 1559107873000 7 connected
a7d9f418035595e68231b2baf079c65064a43a96 172.17.0.8:6379@16379 myself,master - 0 1559107871000 2 connected 5461-10922
7534abbae8a6ffe4183eb45b294507e19fed5516 172.17.0.11:6379@16379 master - 0 1559107872843 7 connected 0-5460

ノード追加

  • vim redis_cluster_sts.yml

replicas: 6 -> replicas: 8

  • kubectl apply -f redis_cluster_sts.yml
  • kubectl get po -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP            NODE
redis-cluster-0   1/1     Running   0          36m   172.17.0.7    minikube
redis-cluster-1   1/1     Running   0          1h    172.17.0.8    minikube
redis-cluster-2   1/1     Running   0          1h    172.17.0.9    minikube
redis-cluster-3   1/1     Running   0          1h    172.17.0.10   minikube
redis-cluster-4   1/1     Running   0          1h    172.17.0.11   minikube
redis-cluster-5   1/1     Running   0          1h    172.17.0.12   minikube
redis-cluster-6   1/1     Running   0          31m   172.17.0.13   minikube
redis-cluster-7   1/1     Running   0          1m    172.17.0.14   minikube
  • kubectl exec -it redis-cluster-1 -- redis-cli --cluster add-node 172.17.0.13:6379 172.17.0.8:6379

=> cdea61f96f575df26eb4828dfa5ab937a3f9f27f

  • kubectl exec -it redis-cluster-1 -- redis-cli --cluster add-node 172.17.0.14:6379 172.17.0.8:6379 --cluster-slave --cluster-master-id cdea61f96f575df26eb4828dfa5ab937a3f9f27f

=> 040da0752197c7bf366f398df1653927f4ff1bef

  • kubectl exec redis-cluster-1 -- redis-cli --cluster info 172.17.0.8:6379
172.17.0.9:6379 (189ed07f...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.11:6379 (7534abba...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.8:6379 (a7d9f418...) -> 0 keys | 5462 slots | 1 slaves.
172.17.0.13:6379 (cdea61f9...) -> 0 keys | 0 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.

reshared

  • kubectl exec -it redis-cluster-1 -- redis-cli --cluster reshard 172.17.0.8:6379

インタラクティブモードになるので以下の順番で入力します

  • 4096
  • cdea61f96f575df26eb4828dfa5ab937a3f9f27f
  • all
  • yes

スロットが正常に reshard されているか確認します

  • kubectl exec redis-cluster-1 -- redis-cli --cluster info 172.17.0.8:6379
172.17.0.8:6379 (a7d9f418...) -> 0 keys | 4096 slots | 1 slaves.
172.17.0.13:6379 (cdea61f9...) -> 0 keys | 4096 slots | 1 slaves.
172.17.0.9:6379 (189ed07f...) -> 0 keys | 4096 slots | 1 slaves.
172.17.0.11:6379 (7534abba...) -> 0 keys | 4096 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.

ノード削除

  • kubectl exec -it redis-cluster-1 -- redis-cli --cluster reshard 172.17.0.8:6379

インタラクティブモードになるので以下の順番で入力します

  • 4096
  • a7d9f418035595e68231b2baf079c65064a43a96
  • cdea61f96f575df26eb4828dfa5ab937a3f9f27f
  • done
  • yes

reshard 後削除します

  • kubectl exec -it redis-cluster-1 -- redis-cli --cluster del-node 172.17.0.13:6379 cdea61f96f575df26eb4828dfa5ab937a3f9f27f
  • kubectl exec redis-cluster-1 -- redis-cli --cluster info 172.17.0.8:6379
172.17.0.8:6379 (a7d9f418...) -> 0 keys | 8192 slots | 2 slaves.
172.17.0.9:6379 (189ed07f...) -> 0 keys | 4096 slots | 1 slaves.
172.17.0.11:6379 (7534abba...) -> 0 keys | 4096 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
  • kubectl exec -it redis-cluster-1 -- redis-cli --cluster del-node 172.17.0.14:6379 040da0752197c7bf366f398df1653927f4ff1bef

rebalance

  • kubectl exec -it redis-cluster-1 -- redis-cli --cluster rebalance 172.17.0.8:6379
  • kubectl exec redis-cluster-1 -- redis-cli --cluster info 172.17.0.8:6379
172.17.0.8:6379 (a7d9f418...) -> 0 keys | 5462 slots | 1 slaves.
172.17.0.9:6379 (189ed07f...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.11:6379 (7534abba...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.

Tips

一度削除したノードがある場合は Pods の他に PersistentVolume も削除しないと前の nodes.conf が残っているため再度 add-node できません

[ERR] Node 172.17.0.13:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

一度 StatefulSet でスケールダウンさせ PersistentVolume を削除するか Pod からアンマウントして PersistentVolume を削除してからスケールアウトして新規で Pod を作成する必要がある
もしくは強制的に pvc -> pv -> pod を削除します

  • kubectl delete pvc data-redis-cluster-6
  • kubectl delete pv pvc-2b491981-81d3-11e9-b396-08002719d78a
  • kubectl delete po redis-cluster-6

で再度 add-node できます

あとしまつ

  • kubectl delete sts redis-cluster
  • for i in {0..7}; do kubectl delete pvc data-redis-cluster-$i; done
  • kubectl delete svc redis

最後に

k8s 上で StatefulSet を使って RedisCluster を構築してみました
Headless Service が使えないので、正直 StatefulSet である必要はないかもしれませんが公式の使用パターンでも推奨しているので特に理由がなければ StatefulSet でいいかなと思います

それよりも Redis Cluster でノードを追加するときにホスト名ベースで追加できるように対応してほしいなと感じました
そうすれば Headless Service が使えるのでもっとキレイにできるようになると思います

参考サイト

0 件のコメント:

コメントを投稿