честно спизжено отсюда:
http://devmind.ru/k8s/razvorachivaem-ha-cluster-redis-in-kubernetes
Задача: развернуть отказоустойчивый экземпляр redis. У redis есть 2 варианта:
Replica - схема master-slave. В мастер можно писать/читать, со слейва можно только читать. На всех нодах одинаковые данные. При падении мастера, любой слейв сможет стать мастером.
Cluster- Тут все серьезно. Кластер умеет Sharding. Годиться для очень больших проектов.
Собирать будем по схеме Replica. За основу берем готовый helm от Bitnami и корректируем настройки под нас.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm pull bitnami/redis
tar -xvf redis-12.8.3.tgz
cd redis/
vim values.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#Задаем пароль global: redis: password: "myredispassword" #Включаем сборщик метрик metrics: enabled: true serviceMonitor: enabled: true namespace: "monitoring" #Запрещаем мастеру и слейву создавать файловые стораджи. master: persistence: enabled: false slave: persistence: enabled: false # Включаем sentinel, увеличиваем количество слейвов и указываем имя нашего кластера. sentinel: enabled: true downAfterMilliseconds: 600 cluster: enabled: true slaveCount: 3 clusterDomain: cluster.local |
Sentinel нам необходим для автоматического переключения slave в master, при падении последнего. 3 ноды это минимум ,который нужен для кворума sentinel.
узнать имя кластера можно командой:
kubectl config view
у меня оно осталось по умолчанию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[root@kub-master-1 ~]# kubectl config view apiVersion: v1 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://192.168.1.201:6443 name: cluster.local contexts: - context: cluster: cluster.local user: admin-cluster.local name: admin-cluster.local current-context: admin-cluster.local kind: Config preferences: {} users: - name: admin-cluster.local user: client-certificate-data: REDACTED client-key-data: REDACTED |
ставим:
helm install redis-cluster redis/ -f redis/values.yaml -n redis
Дожидаемся запуска подов, проверям:
1 2 3 4 5 |
[root@kub-master-1 ~]# kubectl get pod -n redis NAME READY STATUS RESTARTS AGE redis-cluster-node-0 3/3 Running 0 16m redis-cluster-node-1 3/3 Running 0 15m redis-cluster-node-2 3/3 Running 0 15m |
1 2 3 4 5 |
[root@kub-master-1 ~]# kubectl get service -n redis NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE redis-cluster ClusterIP 10.100.203.196 <none> 6379/TCP,26379/TCP 17m redis-cluster-headless ClusterIP None <none> 6379/TCP,26379/TCP 17m redis-cluster-metrics ClusterIP 10.100.29.95 <none> 9121/TCP 17m |
В принципе на этом все, если ваши приложения умеют работать с несколькими нодами redis и определять мастера. Обращаться ваши приложения уже могут по headless адресам нод
1 2 3 |
redis-cluster-node-0.redis-cluster-headless:6379 redis-cluster-node-1.redis-cluster-headless:6379 redis-cluster-node-2.redis-cluster-headless:6379 |
мы сделаем единую точку входа:
Узнать кто является мастером, можно у самих нод, заходим в контейнер:
[root@kub-master-1 ~]# kubectl exec -ti redis-cluster-node-1 -n redis bash
и теперь опрашиваем наши поды:
I have no name!@redis-cluster-node-1:/$ redis-cli -h redis-cluster-node-0.redis-cluster-headless -a myredispassword info Replication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
I have no name!@redis-cluster-node-1:/$ redis-cli -h redis-cluster-node-0.redis-cluster-headless -a myredispassword info Replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:slave master_host:10.0.214.20 master_port:6379 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:1481209 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:8817a08da830eaadeb996ab83441c136286072d2 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:1481209 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:504183 repl_backlog_histlen:977027 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
I have no name!@redis-cluster-node-1:/$ redis-cli -h redis-cluster-node-1.redis-cluster-headless -a myredispassword info Replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:slave master_host:10.0.214.20 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:1484927 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:8817a08da830eaadeb996ab83441c136286072d2 master_replid2:9009ea42aeb6a76877a7a7998c0d8ede4b4b11b6 master_repl_offset:1484927 second_repl_offset:501959 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:436352 repl_backlog_histlen:1048576 |
как видим поды 0 и 1 являются слейвами теперь сделаем запрос к под 2
I have no name!@redis-cluster-node-1:/$ redis-cli -h redis-cluster-node-2.redis-cluster-headless -a myredispassword info Replication
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Replication role:master connected_slaves:2 slave0:ip=10.0.94.18,port=6379,state=online,offset=1500578,lag=0 slave1:ip=10.0.214.19,port=6379,state=online,offset=1500578,lag=0 master_replid:8817a08da830eaadeb996ab83441c136286072d2 master_replid2:9009ea42aeb6a76877a7a7998c0d8ede4b4b11b6 master_repl_offset:1500851 second_repl_offset:501959 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:452276 repl_backlog_histlen:1048576 |
как видим он является мастером.
Для дальнейшей работы, за основу возьмем haproxy, который будет опрашивать каждую из нод на предмет ее статуса и переключать запросы на мастера. Конфиг самого haproxy:
[root@kub-master-1 ~]# cat redis/haproxy.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
frontend redis_frontend mode tcp bind 0.0.0.0:6379 default_backend redis-backend backend redis-backend mode tcp option tcplog option tcp-check tcp-check send AUTH\ myredispassword\r\n tcp-check expect string +OK tcp-check send PING\r\n tcp-check expect string +PONG tcp-check send info\ replication\r\n tcp-check expect string role:master tcp-check send QUIT\r\n tcp-check expect string +OK server redis_node1_app01 redis-cluster-node-0.redis-cluster-headless:6379 maxconn 4096 check inter 1s server redis_node2_app02 redis-cluster-node-1.redis-cluster-headless:6379 maxconn 4096 check inter 1s server redis_node3_app03 redis-cluster-node-2.redis-cluster-headless:6379 maxconn 4096 check inter 1s |
в этом конфиге не забываем указывать аутентификационные данные а именно myredispassword
Заливаем его на кластер:
[root@kub-master-1 ~]# kubectl create configmap redis-haproxy-config --from-file=redis/haproxy.cfg -n redis
[root@kub-master-1 ~]# cat redis/deployment-hapr.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
apiVersion: apps/v1 kind: Deployment metadata: name: redis-haproxy-deployment namespace: redis labels: app: redis-haproxy spec: replicas: 2 selector: matchLabels: app: redis-haproxy template: metadata: labels: app: redis-haproxy spec: containers: - name: redis-haproxy image: haproxy:lts-alpine volumeMounts: - name: redis-haproxy-config-volume mountPath: /usr/local/etc/haproxy/haproxy.cfg subPath: haproxy.cfg ports: - containerPort: 6379 volumes: - name: redis-haproxy-config-volume configMap: name: redis-haproxy-config items: - key: haproxy.cfg path: haproxy.cfg |
[root@kub-master-1 ~]# cat redis/service-hapr.yaml
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: v1 kind: Service metadata: name: redis-haproxy-balancer namespace: redis spec: type: ClusterIP selector: app: redis-haproxy ports: - port: 6379 targetPort: 6379 |
применяем:
[root@kub-master-1 ~]# kubectl apply -f redis/deployment-hapr.yaml -f redis/service-hapr.yaml
проверяем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[root@kub-master-1 ~]# kubectl get pod -n redis NAME READY STATUS RESTARTS AGE redis-cluster-node-0 3/3 Running 0 145m redis-cluster-node-1 3/3 Running 0 144m redis-cluster-node-2 3/3 Running 0 143m redis-haproxy-deployment-56cb4b7bd6-mc6tk 1/1 Running 0 28s redis-haproxy-deployment-56cb4b7bd6-v295k 1/1 Running 0 28s [root@kub-master-1 ~]# kubectl get service -n redis NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE redis-cluster ClusterIP 10.100.203.196 <none> 6379/TCP,26379/TCP 145m redis-cluster-headless ClusterIP None <none> 6379/TCP,26379/TCP 145m redis-cluster-metrics ClusterIP 10.100.29.95 <none> 9121/TCP 145m redis-haproxy-balancer ClusterIP 10.100.231.70 <none> 6379/TCP 49s |
если нужен будет доступ из вне кластера то открываем порт наружу:
[root@kub-master-1 ~]# cat redis/configmap.yaml
1 2 3 4 5 6 7 |
apiVersion: v1 kind: ConfigMap metadata: name: tcp-services namespace: ingress-nginx data: 6379: "redis/redis-haproxy-balancer:6379" |
В нём порт в начале 6379 должен быть прокинут наружу,
redis - это наш неймспей
redis-haproxy-balancer - это наш сервис
6379 - это порт на котором слушает сервис
применим его:
[root@kub-master-1 ~]# kubectl apply -f redis/configmap.yaml
Правим сервис ingress
kubectl edit service -n ingress-nginx ingress-nginx
А именно добавляем новый порт 6379
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
spec: clusterIP: 10.100.31.49 ports: - name: http port: 80 protocol: TCP targetPort: 80 - name: https port: 443 protocol: TCP targetPort: 443 - name: prometheus port: 10254 protocol: TCP targetPort: 10254 - name: redis-port port: 6379 protocol: TCP targetPort: 6379 |
После этого правим deployment ingress в нашем случае это daemonsets
kubectl edit daemonsets.apps -n ingress-nginx ingress-nginx-controller
Добавляем наш порт:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ports: - containerPort: 80 hostPort: 80 name: http protocol: TCP - containerPort: 443 hostPort: 443 name: https protocol: TCP - containerPort: 6379 hostPort: 6379 name: redis protocol: TCP |
проверим что на воркер ноде (на которой у нас ingress) данный порт доступен:
1 2 3 |
[root@kub-worker-2 ~]# netstat -ntpl | grep 6379 tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 2643/nginx: worker tcp6 0 0 :::6379 :::* LISTEN 2643/nginx: worker |
Теперь при обращении к внешнему адреу 192.168.1.205:6379 нам всегда будет отвечать мастер-сервер redis:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
redis-cli -h 192.168.1.205 -a myredispassword info Replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:master connected_slaves:2 slave0:ip=10.0.94.18,port=6379,state=online,offset=1846730,lag=0 slave1:ip=10.0.214.19,port=6379,state=online,offset=1846594,lag=1 master_replid:8817a08da830eaadeb996ab83441c136286072d2 master_replid2:9009ea42aeb6a76877a7a7998c0d8ede4b4b11b6 master_repl_offset:1846730 second_repl_offset:501959 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:798155 repl_backlog_histlen:1048576 |
Временно останавливаем текущего мастера:
redis-cli -h 192.168.1.205 -a myredispassword DEBUG sleep 300
и проверим что у нас теперь подключённых слейвов - 1.
Через несколько секунд нам уже отвечает другой под, и сообщает что в кластере осталось лишь двое.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
redis-cli -h 192.168.1.205 -a myredispassword info Replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:master connected_slaves:1 slave0:ip=10.0.94.18,port=6379,state=online,offset=1885948,lag=0 master_replid:3cfe621374523d906cd93235ef5b8f851a72b235 master_replid2:8817a08da830eaadeb996ab83441c136286072d2 master_repl_offset:1886085 second_repl_offset:1883316 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:837510 repl_backlog_histlen:1048576 |
для более наглядной проверке мы можем удалить мастер под и проверить что наш кластер будет доступен, проверим что 0 под является мастером:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
I have no name!@redis-cluster-node-1:/$ redis-cli -h redis-cluster-node-0.redis-cluster-headless -a myredispassword info Replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:master connected_slaves:2 slave0:ip=10.0.94.18,port=6379,state=online,offset=1955465,lag=1 slave1:ip=10.0.214.20,port=6379,state=online,offset=1955451,lag=1 master_replid:3cfe621374523d906cd93235ef5b8f851a72b235 master_replid2:8817a08da830eaadeb996ab83441c136286072d2 master_repl_offset:1955465 second_repl_offset:1883316 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:906890 repl_backlog_histlen:1048576 |
удалим его и проверим непосредственно по айпишнику:
[root@kub-master-1 ~]# kubectl delete pod -n redis redis-cluster-node-0
как видим всё отработало:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
I have no name!@redis-cluster-node-1:/$ redis-cli -h 192.168.1.205 -a myredispassword info Replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:master connected_slaves:1 slave0:ip=10.0.214.20,port=6379,state=online,offset=1986417,lag=0 master_replid:5a2c9d9d55ddee9dbcc29daa968efb637ad4b5d9 master_replid2:3cfe621374523d906cd93235ef5b8f851a72b235 master_repl_offset:1986552 second_repl_offset:1984209 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:937977 repl_backlog_histlen:1048576 |
и после того как под поднялся, его роль в кластере стала слейвом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
I have no name!@redis-cluster-node-1:/$ redis-cli -h redis-cluster-node-0.redis-cluster-headless -a myredispassword info Replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:slave master_host:10.0.94.18 master_port:6379 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:1996407 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:5a2c9d9d55ddee9dbcc29daa968efb637ad4b5d9 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:1996407 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1987787 repl_backlog_histlen:8621 |
====================================================================================================
Dashboard + пароль в секретах.
kubectl create ns redis
helm repo add bitnami https://charts.bitnami.com/bitnami
helm pull bitnami/redis
tar -xvf redis-12.8.3.tgz
cd redis/
echo -n "password-for-redis-cluster-Asergsdg2345KHJ" | base64
cGFzc3dvcmQtZm9yLXJlZGlzLWNsdXN0ZXItQXNlcmdzZGcyMzQ1S0hK
cat secret-password.yaml
1 2 3 4 5 6 7 8 9 |
apiVersion: v1 kind: Secret metadata: name: redis-password namespace: redis type: Opaque data: redis-password: cGFzc3dvcmQtZm9yLXJlZGlzLWNsdXN0ZXItQXNlcmdzZGcyMzQ1S0hK |
kubectl apply -f secret-password.yaml
vim values.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
## Use password authentication usePassword: true password: "" ## Use existing secret (ignores previous password) existingSecret: redis-password ## Prometheus Exporter / Metrics ## metrics: enabled: true # Enable this if you're using https://github.com/coreos/prometheus-operator serviceMonitor: enabled: true ## Specify a namespace if needed namespace: monitoring master: persistence: enabled: false slave: persistence: enabled: false sentinel: enabled: true downAfterMilliseconds: 600 clusterDomain: test.local cluster: enabled: true slaveCount: 3 |
В следующем файле в строке tcp-check send AUTH\ myredispassword\r\n нам надо указать наш пароль от redis
[root@prod-vsrv-kubemaster1 redis]# cat haproxy.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
frontend redis_frontend mode tcp bind 0.0.0.0:6379 default_backend redis-backend backend redis-backend mode tcp option tcplog option tcp-check tcp-check send AUTH\ password-for-redis-cluster-Asergsdg2345KHJ\r\n tcp-check expect string +OK tcp-check send PING\r\n tcp-check expect string +PONG tcp-check send info\ replication\r\n tcp-check expect string role:master tcp-check send QUIT\r\n tcp-check expect string +OK server redis_node1_app01 redis-cluster-node-0.redis-cluster-headless:6379 maxconn 4096 check inter 1s server redis_node2_app02 redis-cluster-node-1.redis-cluster-headless:6379 maxconn 4096 check inter 1s server redis_node3_app03 redis-cluster-node-2.redis-cluster-headless:6379 maxconn 4096 check inter 1s |
[root@prod-vsrv-kubemaster1 redis]# cat deployment-haproxy-redis.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
apiVersion: apps/v1 kind: Deployment metadata: name: redis-haproxy-deployment namespace: redis labels: app: redis-haproxy spec: replicas: 2 selector: matchLabels: app: redis-haproxy template: metadata: labels: app: redis-haproxy spec: containers: - name: redis-haproxy image: haproxy:lts-alpine volumeMounts: - name: redis-haproxy-config-volume mountPath: /usr/local/etc/haproxy/haproxy.cfg subPath: haproxy.cfg ports: - containerPort: 6379 volumes: - name: redis-haproxy-config-volume configMap: name: redis-haproxy-config items: - key: haproxy.cfg path: haproxy.cfg |
[root@prod-vsrv-kubemaster1 redis]# cat service-haproxy-redis.yaml
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: v1 kind: Service metadata: name: redis-haproxy-balancer namespace: redis spec: type: ClusterIP selector: app: redis-haproxy ports: - port: 6379 targetPort: 6379 |
Всё, можно запускать
cd ../
helm install redis-cluster redis/ -f redis/values.yaml -n redis
[root@prod-vsrv-kubemaster1 redis]# kubectl create configmap redis-haproxy-config --from-file=redis/haproxy.cfg -n redis
[root@prod-vsrv-kubemaster1 redis]# kubectl apply -f redis/deployment-haproxy-redis.yaml -f redis/service-haproxy-redis.yaml
Посмотреть пароль можно командой:
kubectl get secret --namespace redis redis-password -o jsonpath="{.data.redis-password}" | base64 --decode
Подключаться к сервису можно командой:
redis-cli -h redis-cluster -p 6379 -a $REDIS_PASSWORD # Read only operations
redis-cli -h redis-cluster -p 26379 -a $REDIS_PASSWORD # Sentinel access
Подключаться из вне можно командой:
redis-cli -h 127.0.0.1 -p 6379 -a $REDIS_PASSWORD
Прокидываем порт наружу:
[root@prod-vsrv-kubemaster1 redis]# cat /root/ConfigMap.yml
1 2 3 4 5 6 7 8 9 |
apiVersion: v1 kind: ConfigMap metadata: name: tcp-services namespace: ingress-nginx data: 5050: "m-logstash-megabuilder/logstash-logstash:5044" 5672: "rabbitmq/rabbitmq:5672" 6379: "redis/redis-haproxy-balancer:6379" |
Доступ изнутри будет выглядеть вот так:
redis-cluster-headless.redis.svc.test.local:6379
теперь поставим дашборд
git clone https://github.com/joeferner/redis-commander.git
нас интересует:
https://github.com/joeferner/redis-commander/tree/master/k8s/helm-chart
правим файл:
redis-commander/values.yaml
указываем параметры подключения пароль и хост:
1 2 3 4 5 6 7 |
redis: # Specifies a single Redis host host: "redis-cluster-headless.redis.svc.test.local" # Specifies redis password password: "password-for-redis-cluster-Asergsdg2345KHJ" |
настраиваем базовую авторизацию:
1 2 3 4 5 |
httpAuth: # Specify http basic username and password to protect access to redis commander web ui username: "admin" password: "admin" |
хост ingress
1 2 3 4 5 6 7 8 9 |
ingress: enabled: true annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: redis.prod.test.ru paths: ["/"] |
ну и всё устанавливаем его:
helm upgrade --install redis-web -n redis --values redis-commander/values.yaml redis-commander/