2019年5月30日木曜日

minikube で Redis コンテナを立ち上げる方法を考える

概要

シングルノード、Sentinel、Cluster のそれぞれを k8s 上でコンテナとして起動しアクセスする方法を考えます
k8s 環境は minikube を使います 

環境

  • macOS 10.14.5
  • minikube v0.28.2

minikube 起動

  • minikube start

シングルノードで起動

まずは redis 1 台で起動させてみます

Deployment 作成

Pod 直接ではなく Deploment を使います

  • vim redis_deployment.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-master
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: master
    spec:
      containers:
      - name: master
        image: redis
        ports:
        - containerPort: 6379

特にポイントはないです
labels に関しては好きなラベルを設定してください
あとで Service と紐付けるのに使います

  • kubectl apply -f redis_deployment.yml
  • kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
redis-master-86f6bffc7f-xrzht   1/1     Running   0          7m

デプロイして Pod が起動すれば OK です
redis-cli コマンドを使ってコンテナが起動しているか確認しましょう

  • kubectl exec redis-master-86f6bffc7f-xrzht redis-cli info replication
# Replication
role:master
connected_slaves:0
master_replid:695cd5de82eb682d3ebd2f20dc39dc1cb3ee05a6
master_replid2:0000000000000000000000000000000000000000

Service 作成

ホストマシンからアクセスできるように Service を作ります

  • vim redis_service.yml
apiVersion: v1
kind: Service
metadata:
  name: redis-single
spec:
  type: NodePort
  selector:
    role: single
  ports:
  - protocol: TCP
    port: 6379
    targetPort: 6379

Ingress は面倒なので type: NodePort を使います
minikube -> VirtualBox -> コンテナという経路でアクセスすることになります

  • kubectl apply -f redis_service.yml
  • redis-cli -h 192.168.99.100 -p 31267 info replication

で先ほどと同じ結果が得られると思います
ちなみにこれらの IP アドレスとポートは Mac から見える minikube 用の Virtualbox のIP とポートになります

  • minikube ip
  • minikube service redis-single --format "{{.Port}}"

ちなみにこの状態で Deployment の replicas: 1replicas: 3 などにすると Pod が 3 台になります
そして上記の redis-cli でアクセスすると各コンテナに分散してアクセスしているので確認できると思います

Sentinel で起動

次に Sentinel 環境を考えます
戦略として master, slave, sentinel それぞれで Deployment と Serivce を作ってお互いを通信する感じです

master Deployment 作成

まず master 用のコンテナを作成するための Deployment を作成します

  • vim redis_master_deployment.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-master
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: master
    spec:
      containers:
      - name: master
        image: redis
        ports:
        - containerPort: 6379
  • kubectl apply -f redis_master_deployment.yml

これは先程とほぼ変わりません

  • kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
redis-master-86f6bffc7f-kx4sc   1/1     Running   0          3m
  • kubectl exec redis-master-86f6bffc7f-kx4sc redis-cli info replication
role:master
connected_slaves:0

master Service 作成

次に master 用のコンテナに他のコンテナからアクセスできるように Service を定義します

  • vim redis_master_service.yml
apiVersion: v1
kind: Service
metadata:
  name: redis-master
spec:
  type: NodePort
  selector:
    role: master
  ports:
  - protocol: TCP
    port: 6379
    targetPort: 6379
  • kubectl apply -f redis_master_service.yml
  • kubectl get ep redis-master

これで master にアクセスするためのエンドポイントが表示されます

NAME           ENDPOINTS         AGE
redis-master   172.17.0.7:6379   3s

slave Deplyment 作成

次に slave コンテナ用の Deployment を作成します

  • vim redis_slave_deployment.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-slave
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: slave
    spec:
      containers:
      - name: slave
        image: redis
        ports:
        - containerPort: 6379
        command: ["redis-server"]
        args: ["--slaveof", "172.17.0.7", "6379"]

master の Deployment に command を追加しています
--slaveof オプションを使って master のエンドポイントを指定することで slave として redis-server が立ち上がるようにしています

これで起動し redis-cli でレプリケーションの状態を確認してみましょう

  • kubectl apply -f redis_slave_deployment.yml
  • kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
redis-master-86f6bffc7f-kx4sc   1/1     Running   0          6m
redis-slave-5556967686-l7t58    1/1     Running   0          4m
  • kubectl exec redis-slave-5556967686-l7t58 redis-cli info replication
role:slave
master_host:172.17.0.7
master_port:6379

こんな感じで master のエンドポイントを参照し slave になっていれば OK です

slave Service 作成

failover した際に slave にアクセスすることになるので slave も Service を作成しておきます

  • vim redis_slave_service.yml
apiVersion: v1
kind: Service
metadata:
  name: redis-slave
spec:
  type: NodePort
  selector:
    role: slave
  ports:
  - protocol: TCP
    port: 6379
    targetPort: 6379
  • kubectl apply -f redis_slave_service.yml
  • kubectl get ep redis-slave
NAME          ENDPOINTS         AGE
redis-slave   172.17.0.8:6379   17s

sentinel Deployment 作成

作成した master/slave を Sentinel で監視し自動 failover できるようにします
まず Sentinel 用のイメージを作成します

  • mkdir sentinel
  • cd sentinel
  • vim sentinel.conf
port 26379
dir /tmp
sentinel monitor mymaster 172.17.0.7 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 5000

sentinel monitor で指定する IP は master のエンドポイントを指定します

  • vim Dockerfile
FROM redis

ADD sentinel.conf /etc/redis/sentinel.conf
RUN chown redis:redis /etc/redis/sentinel.conf

CMD ["redis-server", "/etc/redis/sentinel.conf", "--sentinel"]
  • eval $(minikube docker-env)
  • docker build -t sentinel:v1 .

これでイメージが minikube で起動した Virtualbox 上に作成されます
このイメージを使って Sentinel の Deloyment を定義します

  • vim redis_sentinel_deployment.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-sentinel
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: sentinel
    spec:
      containers:
      - name: sentinel
        image: sentinel:v1
        ports:
        - containerPort: 26379

containerPort は sentinel.conf で指定したポートに合わせます
また image も先程作成したイメージに書き換えます
バージョンの指定も忘れずに行いましょう

  • kubectl apply -f redis_sentinel_deployment.yml
  • kubectl get po
NAME                              READY   STATUS    RESTARTS   AGE
redis-master-86f6bffc7f-kx4sc     1/1     Running   0          18m
redis-sentinel-6d775867b9-pqtpj   1/1     Running   0          9s
redis-slave-5556967686-l7t58      1/1     Running   0          16m

これで master, slave, sentinel の必要なコンテナが揃いました

sentinel Service 作成

Sentinel API を叩くので一応 Service 登録しておきます

  • vim redis_sentinel_service.yml
apiVersion: v1
kind: Service
metadata:
  name: redis-sentinel
spec:
  type: NodePort
  selector:
    role: sentinel
  ports:
  - protocol: TCP
    port: 26379
    targetPort: 26379
  • kubectl apply -f redis_sentinel_service.yml

ホストから Sentinel API にアクセスできるか確認します

  • minikube ip
  • minikube service redis-sentinel --format "{{.Port}}"

でホストからアクセスする IP とポートを確認し redis-cli を実行します

  • redis-cli -h $(minikube ip) -p 31293 sentinel get-master-addr-by-name mymaster
1) "172.17.0.7"
2) "6379"

こんな感じでちゃんと master が見えていれば OK です

動作確認

failover させてちゃんと master が切り替わるか確認します
k8s には Pod を停止する方法はないので Deployment を削除します

  • kubectl delete deploy redis-master

このときの Sentinel のログは以下の通りです

  • kubectl logs -f redis-sentinel-6d775867b9-pqtpj
1:X 28 May 2019 07:17:12.368 # +failover-state-reconf-slaves master mymaster 172.17.0.7 6379
1:X 28 May 2019 07:17:12.428 # +failover-end master mymaster 172.17.0.7 6379
1:X 28 May 2019 07:17:12.429 # +switch-master mymaster 172.17.0.7 6379 172.17.0.8 6379
1:X 28 May 2019 07:17:12.431 * +slave slave 172.17.0.7:6379 172.17.0.7 6379 @ mymaster 172.17.0.8 6379
1:X 28 May 2019 07:17:17.449 # +sdown slave 172.17.0.7:6379 172.17.0.7 6379 @ mymaster 172.17.0.8 6379

ちゃんと failover していることが確認できます
slave の Service にアクセスすると master に昇格していることがわかると思います

  • redis-cli -h $(minikube ip) -p 32109 info replication
role:master
connected_slaves:0

slave は 0 台になっています
これは元 master がまだダウンしているためです
再度 master を Deployment してみましょう

  • kubectl apply -f redis_master_deployment.yml

Sentinel のログを見ていると以下のようなログが流れると思います

1:X 28 May 2019 07:24:34.164 # -sdown slave 172.17.0.7:6379 172.17.0.7 6379 @ mymaster 172.17.0.8 6379
1:X 28 May 2019 07:24:44.133 * +convert-to-slave slave 172.17.0.7:6379 172.17.0.7 6379 @ mymaster 172.17.0.8 6379

これで再度 slave の数を確認すると connected_slaves:1 になっていることが確認できると思います
実際に master と slave に対して redis-cliget/set してみると更に挙動を確認できると思います

$ redis-cli -h $(minikube ip) -p 32582 set a a
(error) READONLY You can't write against a read only replica.
$ redis-cli -h $(minikube ip) -p 32109 set a a
OK
$ redis-cli -h $(minikube ip) -p 32582 get a
"a"

Cluster で起動

ちょっと長くなってしまったので別記事で紹介したいと思います
どうやら Redis Cluster の場合はステートフルなので StatefulSets を使ったほうが良いみたいです

最後に

k8s 上に Redis のシングルノード環境と Sentinel 環境を構築してみました
どちらも Deployment と Service を使えば構築できることがわかりました
特に Sentinel の方は Service を使わないと各ロール間で通信できないので必須になると思います

ネットでいろいろ調べると他にも方法がありそうでした
Pod だけでやる方法も頑張ればできるかなと思います (ConfigMap を使った方法もあるかなと思います)
結局は docker 上にコンテナを立てるのが目的でそれを実現するのに k8s のどの機能を使うのかという話になるかなと思います
今回は Deployment と Serivce を使った方法を紹介しましたが、それ以外を使っても実現することはできるということです
この辺りは k8s の経験がものを言うかなと思っています

参考サイト

0 件のコメント:

コメントを投稿