Redis: Master-Slave репликация и запуск в Kubernetes

Автор: | 09/23/2020
 

Задача — запустить Redis в Kubernetes.

Используем Master-Slave репликацию и Sentinel для мониторинга и failover.

См. Redis: репликация, часть 2 — Master-Slave репликация, и Redis Sentinel.

Redis cluster vs Redis replication

См. Redis: репликация, часть 1 — обзор. Replication vs Sharding. Sentinel vs Cluster. Топология Redis и Choose between Redis Helm Chart and Redis Cluster Helm Chart.

Вкратце:

  • Replica — включает в себя Redis Master, который выполняет чтение-запись, и копирует данные на Redis Slaves, которые обслуживает запросы на чтение. При этом слейвы могут заменить мастера, если он упадёт
  • Cluster — имеет смысл, если у вас данных больше, чем памяти на отдельном сервере. Кластер умеет Sharding, и клиент, запрашивающий определённый ключ, будет направлен на ту ноду, которая этот ключ хранит.

Варианты запуска Redis в Kubernetes

Что надо — это Redis с репликацией.

Как можно реализовать:

Что нам упрощает жизнь, так это то, что нам не нужна персистентность данных — наш Redis будет использоваться толкьо для хранения кеша, который при рестарте можно терять, а значит — не будет лишней головной боли с Kubernetes PersistentVolume.

Helm chart deploy

Сначала запустим все сервисы, посмотрим на них, потом перейдём к доступным опциям.

Добавляем репоизторий Bitnami:

helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories

Деплоим Redis:

helm install backend-redis bitnami/redis
NAME: backend-redis
LAST DEPLOYED: Tue Sep 22 14:48:02 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
** Please be patient while the chart is being deployed **
Redis can be accessed via port 6379 on the following DNS names from within your cluster:
backend-redis-master.default.svc.cluster.local for read/write operations
backend-redis-slave.default.svc.cluster.local for read-only operations
To get your password run:
export REDIS_PASSWORD=$(kubectl get secret --namespace default backend-redis -o jsonpath="{.data.redis-password}" | base64 --decode)
To connect to your Redis server:
1. Run a Redis pod that you can use as a client:
kubectl run --namespace default backend-redis-client --rm --tty -i --restart='Never' \
--env REDIS_PASSWORD=$REDIS_PASSWORD \
--image docker.io/bitnami/redis:6.0.8-debian-10-r0 -- bash
2. Connect using the Redis CLI:
redis-cli -h backend-redis-master -a $REDIS_PASSWORD
redis-cli -h backend-redis-slave -a $REDIS_PASSWORD
To connect to your database from outside the cluster execute the following commands:
kubectl port-forward --namespace default svc/backend-redis-master 6379:6379 &
redis-cli -h 127.0.0.1 -p 6379 -a $REDIS_PASSWORD

Получаем пароль, который был сгенерирован во время установки чарта:

kubectl get secret --namespace default backend-redis -o jsonpath="{.data.redis-password}" | base64 --decode
TySS43UhAW

Запускаем port-forward, что бы подключиться к поду с Redis master:

kubectl port-forward --namespace default svc/backend-redis-master 6379:6379
Forwarding from [::1]:6379 -> 6379
Forwarding from 127.0.0.1:6379 -> 6379

Подключаемся:

redis-cli -h 127.0.0.1 -p 6379 -a TySS43UhAW
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379>

«It works!» (с)

Проверяем сеть:

kk get svc redis-service
NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
redis-service   NodePort   172.20.119.242   <none>        6379:32445/TCP   117d

NodePort, а нам требуется LoadBalancer.

Кроме того — нам требуется Redis Sentinel, а по дефолту он:

sentinel.enabled Enable sentinel containers false

Окей, переходим к опциям — включим Sentinel и LoadBalancer.

Redis Options

Что нам потребуется из опций:

  • global.redis.password
  • metrics:
    • metrics.enabled
    • metrics.serviceMonitor.enabled
    • metrics.serviceMonitor.namespace
  • Persiste:
    • master.persistence.enabled
    • slave.persistence.enabled
  • Service
    • master.service.type
    • master.service.annotations
    • slave.service.type
    • slave.service.annotations
  • Sentinel
    • sentinel.enabled
    • sentinel.service.type — need for LBso clients can ask for Master/Slaves
    • sentinel.service.annotations

Создаём файл с параметрами ~/Temp/redis-opts.yaml:

global:
  redis:
    password: "blablacar"

metrics:
  enabled: true
  serviceMonitor:
    enabled: true
    namespace: "monitoring"

master:
  persistence:
    enabled: false
  service:
    type: LoadBalancer
    annotations: 
      kubernetes.io/ingress.class: alb
      alb.ingress.kubernetes.io/scheme: internal 

slave:
  persistence:
    enabled: false
  service:
    type: LoadBalancer
    annotations:
      kubernetes.io/ingress.class: alb
      alb.ingress.kubernetes.io/scheme: internal

sentinel:
  enabled: true
  service:
    type: LoadBalancer
    annotations: 
      kubernetes.io/ingress.class: alb
      alb.ingress.kubernetes.io/scheme: internal

Обновляем деплоймет, через -f указываем файл с параметрами:

helm upgrade --install backend-redis bitnami/redis -f ~/Temp/redis-opts.yaml

Проверим пароль:

kubectl get secret --namespace default backend-redis -o jsonpath="{.data.redis-password}" | base64 --decode
blablacar

Лоад-балансеры:

kk get svc -l app=redis
NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP                                                           PORT(S)                          AGE
backend-redis            LoadBalancer   172.20.204.235   af5e1294a4a73426692c7e25f7bb947d-915967.us-east-2.elb.amazonaws.com   6379:30647/TCP,26379:32523/TCP   80s
backend-redis-headless   ClusterIP      None             <none>                                                                6379/TCP,26379/TCP               80s
backend-redis-metrics    ClusterIP      172.20.66.2      <none>                                                                9121/TCP                         80s

Коннект:

redis-cli -h af5e1294a4a73426692c7e25f7bb947d-915967.us-east-2.elb.amazonaws.com -a blablacar
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
af5e1294a4a73426692c7e25f7bb947d-915967.us-east-2.elb.amazonaws.com:6379>

Работает.

Но почему LoadBalancer публичный, если указывали тип internal?

А потому что аннотация alb.ingress.kubernetes.io/scheme: internal — для ALB Ingress Controller, а мы создаём простой Kubernetes Service с типом LoadBalancer, который создаёт AWS Classic Load Balancer.

Читаем доку https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer, обновим аннотации: вместо «alb.ingress.kubernetes.io/scheme: internal» — указываем «service.beta.kubernetes.io/aws-load-balancer-internal: "true"«:

...
master:
  service:
    type: LoadBalancer
    annotations: 
      service.beta.kubernetes.io/aws-load-balancer-internal: "true"

slave:
  service:
    type: LoadBalancer
    annotations:
      service.beta.kubernetes.io/aws-load-balancer-internal: "true"

sentinel:
  enabled: true
  service:
    type: LoadBalancer
    annotations: 
      service.beta.kubernetes.io/aws-load-balancer-internal: "true"

Обновляекм, проверяем:

kk get svc -l app=redis
NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP                                                                        PORT(S)                          AGE
backend-redis            LoadBalancer   172.20.178.72   internal-***-1786016015.us-east-2.elb.amazonaws.com   6379:31192/TCP,26379:32239/TCP   17s
backend-redis-headless   ClusterIP      None            <none>                                                                             6379/TCP,26379/TCP               17s
backend-redis-metrics    ClusterIP      172.20.35.163   <none>                                                                             9121/TCP                         17s

Проверим запись данных — должен пойти через Master-инстанс:

admin@bttrm-dev-app-1:~$ redis-cli -h internal-***-1786016015.us-east-2.elb.amazonaws.com -p 6379 -a blablacar SET testkey testvalue
OK

Чтение данных:

admin@bttrm-dev-app-1:~$ redis-cli -h internal-***-1786016015.us-east-2.elb.amazonaws.com -p 6379 -a blablacar GET testkey
"testvalue"

Статус репликации:

admin@bttrm-dev-app-1:~$ redis-cli -h internal-***-1786016015.us-east-2.elb.amazonaws.com -p 6379 -a blablacar info replication
Replication
role:master
connected_slaves:1
slave0:ip=10.3.50.119,port=6379,state=online,offset=144165,lag=1
...

И статус Sentinel:

admin@bttrm-dev-app-1:~$ redis-cli -h internal-***-1786016015.us-east-2.elb.amazonaws.com -p 26379 -a blablacar info sentinel
Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.3.33.107:6379,slaves=1,sentinels=2

Хотя вообще, запись на Мастер должна выполняться через получения адреса Мастера у одного из Сентинелов:

admin@bttrm-dev-app-1:~$ redis-cli -h internal-***-1786016015.us-east-2.elb.amazonaws.com -p 26379 -a blablacar sentinel get-master-addr-by-name mymaster
1) "10.3.33.107"
2) "6379"

См. документацию тут>>>.

Готово.