Redis 是一个高性能的key-value数据库。 为了达到最佳性能, Redis 使用 内存中的数据集。通过定期将数据集转储到磁盘 或将每个命令附加到基于磁盘的日志来持久化您的数据。
Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉
我们遵循一主两从的模式进行部署,三台物理节点,一个节点上部署六个redis节点。
1.编写Dockerfile,6.0需要注意的地方需要自己对配置文件赋权。
#基础镜像
FROM redis:6.2.6
#修复时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
#环境变量
ENV REDIS_PORT 8000
#ENV REDIS_PORT_NODE 18000
#暴露变量
EXPOSE $REDIS_PORT
#EXPOSE $REDIS_PORT_NODE
#复制
COPY entrypoint.sh /usr/local/bin/
COPY redis.conf /usr/local/etc/
#for config rewrite
RUN chmod -R 777 /usr/local/etc/redis.conf
RUN chmod +x /usr/local/bin/entrypoint.sh
#入口
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
#命令
CMD ["redis-server", "/usr/local/etc/redis.conf"]
2.编写shell文件 entrypoint.sh,脚本主要作用是通过**$REDIS_PORT**变量对对应的容器端口进行开放,具体的变量赋值会在docker-compose文件中
#!/bin/sh
#只作用于当前进程,不作用于其创建的子进程
set -e
#$0--Shell本身的文件名 $1--第一个参数 $@--所有参数列表
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
sed -i 's/REDIS_PORT/'$REDIS_PORT'/g' /usr/local/etc/redis.conf
chown -R redis . #改变当前文件所有者
exec gosu redis "$0" "$@" #gosu是sudo轻量级”替代品”
fi
exec "$@"
3.编写redis.conf
port REDIS_PORT
cluster-enabled no
cluster-config-file nodes.conf
cluster-node-timeout 15000
appendonly no
save ""
maxmemory 10737418240
maxmemory-policy allkeys-lru
activedefrag yes
tcp-backlog 511
logfile "redis.log"
# masterauth
# requirepass
requirepass和masterauth不能启用,否则redis-trib创建集群失败。
protected-mode 保护模式是禁止公网访问,但是不能设置密码和bind ip。
4.构建镜像
[root@tv0-business-65]# docker build -t ycloud/redis:6.2.5 . #构建
[root@tv0-business-65]# docker save 04d818646945 -o krc6.tar ycloud/redis:6.2.5 #打包并附名
1.编写docker-compose.yaml文件
version: '3'
services:
redis1:
image: ycloud/redis:6.2.5
network_mode: host
restart: always
volumes:
- /data/redis/8001/:/data
environment:
- REDIS_PORT=8001
redis2:
image: ycloud/redis:6.2.5
network_mode: host
restart: always
volumes:
- /data/redis/8002/:/data
environment:
- REDIS_PORT=8002
redis3:
image: ycloud/redis:6.2.5
network_mode: host
restart: always
volumes:
- /data/redis/8003/:/data
environment:
- REDIS_PORT=8003
redis4:
image: ycloud/redis:6.2.5
network_mode: host
restart: always
volumes:
- /data/redis/8004/:/data
environment:
- REDIS_PORT=8004
redis6.2.5:
image: ycloud/redis:6.2.5
network_mode: host
restart: always
volumes:
- /data/redis/8006.2.5/:/data
environment:
- REDIS_PORT=8005
redis6:
image: ycloud/redis:6.2.5
network_mode: host
restart: always
volumes:
- /data/redis/8006/:/data
environment:
- REDIS_PORT=8006
network使用的是host模式,容器直接使用宿主机的IP和端口。基础薄弱的同学可以了解容器网络的相关知识,这里不做过多讲解
2.启动容器
$docker-compose up -d
$docker-compose ps
[root@wjfh-189-6-188 redis]# docker-compose -f dc6.yml ps
Name Command State Ports
----------------------------------------------------------------
redis_redis1_1 /usr/local/bin/entrypoint. ... Up
redis_redis2_1 /usr/local/bin/entrypoint. ... Up
redis_redis3_1 /usr/local/bin/entrypoint. ... Up
redis_redis4_1 /usr/local/bin/entrypoint. ... Up
redis_redis5_1 /usr/local/bin/entrypoint. ... Up
redis_redis6_1 /usr/local/bin/entrypoint. ... Up
三个节点都启动成功之后,接下来就要组件redis集群
3.组建集群
docker run --rm -it inem0o/redis-trib create --replicas 2 \
10.189.6.188:8001 10.189.6.189:8001 10.189.6.190:8001 \
10.189.6.188:8002 10.189.6.189:8002 10.189.6.190:8002 \
10.189.6.188:8003 10.189.6.189:8003 10.189.6.190:8003 \
10.189.6.188:8004 10.189.6.189:8004 10.189.6.190:8004 \
10.189.6.188:8005 10.189.6.189:8005 10.189.6.190:8005 \
10.189.6.188:8006 10.189.6.189:8006 10.189.6.190:8006
redis-trib是redis官方提供的一个组件集群的工具
–replicas 表示一个主节点创建两个从节点,其他参数值得是地址的集合
>>> Performing hash slots allocation on 18 nodes...
Master[0] -> Slots 0 - 2730
Master[1] -> Slots 2731 - 5460
Master[2] -> Slots 5461 - 8191
Master[3] -> Slots 8192 - 10922
Master[4] -> Slots 10923 - 13652
Master[5] -> Slots 13653 - 16383
·····
·····
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
显示提示集群组件成功
4.自定义主从匹配节点
这是原cluster的主从分配,无法确定master分配的slave,我们这里书写脚本,让每个节点的80018002为master,从80038004,8005~8006中,各取一个为slave。
#!/usr/bin/env python3
# coding:utf-8
import time
import sys
import os
class host_info:
def __init__(self):
self.ip = ''
self.port = 0
self.id = ''
master_list = []
slave_list = []
def slave_exec(do):
for node in slave_list:
ip = node.ip
port = node.port
# do something
cmd = '/usr/local/bin/redis-cli -h ' + ip + \
' -p ' + str(port) + ' ' + do
print(cmd)
las = os.popen(cmd).read()
print(las)
time.sleep(5)
def master_exec(do):
for node in master_list:
ip = node.ip
port = node.port
# do something
cmd = '/usr/local/bin/redis-cli -h ' + ip + \
' -p ' + str(port) + ' ' + do
print(cmd)
las = os.popen(cmd).read()
print(las)
time.sleep(5)
def get_redis_nodes(ip, port):
cmd = '/usr/local/bin/redis-cli -h ' + ip + \
' -p ' + str(port) + ' cluster nodes'
las = os.popen(cmd).read()
nodes = las.split("\n")
for node in nodes:
if len(node) < 2:
continue
nt = node.split(" ")
host_ip = nt[1]
at = host_ip.find("@")
colon = host_ip.find(":")
if at == -1 or colon == -1:
print('at or colon not found', host_ip)
continue
host_tmp = host_info()
host_tmp.ip = host_ip[:colon]
host_tmp.port = host_ip[colon + 1:at]
host_tmp.id = nt[0]
if nt[2].find('master') != -1:
print("master---:", nt[2])
master_list.append(host_tmp)
if nt[2].find('slave') != -1:
print("slave---:", nt[2])
# master id
host_tmp.id = nt[3]
slave_list.append(host_tmp)
return
def set_replicate(ip, port, master_ip, master_port):
s = ip + ":" + str(port) + ' is replicate of ' + \
master_ip + ":" + str(master_port)
print(s)
for ins in master_list:
if ins.ip == master_ip and ins.port == str(master_port) and len(ins.id) > 0:
cmd = '/usr/local/bin/redis-cli -h ' + ip + ' -p ' + \
str(port) + ' cluster replicate ' + ins.id
print(cmd)
tmp = os.popen(cmd).readlines()
print(tmp)
time.sleep(2)
return True
print("ip:%s,port:%d not found", ip, port)
def sub_group():
ip_d_l = ['10.189.11.222','10.189.11.223','10.189.11.224']
for i in range(len(ip_d_l)):
# print(ip_d_l[i])
if (i + 1) % 3 == 1:
for port in range(8003, 8004):
set_replicate(ip_d_l[i], port, ip_d_l[i + 1], port - 8)
for port in range(8005, 8006):
set_replicate(ip_d_l[i], port, ip_d_l[i + 2], port - 16)
if (i + 1) % 3 == 2:
for port in range(8003, 8004):
set_replicate(ip_d_l[i], port, ip_d_l[i - 1], port - 8)
for port in range(8005, 8006):
set_replicate(ip_d_l[i], port, ip_d_l[i + 1], port - 16)
if (i + 1) % 3 == 0:
for port in range(8003, 8004):
set_replicate(ip_d_l[i], port, ip_d_l[i - 2], port - 8)
for port in range(8005, 8006):
set_replicate(ip_d_l[i], port, ip_d_l[i - 1], port - 16)
def is_contains(ip, port):
for ins in master_list:
if ins.ip == ip and ins.port == str(port):
print("ip:%s,port:%d,id:%s is found", ip, port, ins.id)
return True
print("ip:%s,port:%d not found", ip, port)
# 检查各个集群是否是每台服务器8001~8008为主
def check_masters():
ip_d_l = ['10.189.11.222','10.189.11.223','10.189.11.224']
port_d_l = [8001, 8002]
ins_num = len(ip_d_l) * len(port_d_l)
if ins_num != len(master_list):
print("ins_num:%d, master_list_num:%d", ins_num, len(master_list))
for ip in ip_d_l:
for port in port_d_l:
is_contains(ip, port)
def check_group_slave(ip, port):
mid = ''
mip = ''
mport = ''
for mins in master_list:
if mins.ip == ip and mins.port == str(port):
mid = mins.id
mip = mins.ip
break
if len(mid) < 1:
print("ip:%s, port:%d not found!!!", ip, port)
return
print("----------------master ins %s:%d", ip, port)
for sins in slave_list:
if sins.id == mid:
print("slave ip:%s,port:%s", sins.ip, sins.port)
if sins.ip == mins.ip:
print("+++++++++++++error!!!+++++++++++++")
print("----------------")
# 检查集群的最终配置是否为三服务器一组,互为主备
def check_group():
ip_d_l = ['10.189.11.222','10.189.11.223','10.189.11.224']
port_d_l = [8001, 8002]
ins_num = len(ip_d_l) * len(port_d_l)
if ins_num != len(master_list):
print("ins_num:%d, master_list_num:%d", ins_num, len(master_list))
for ip in ip_d_l:
for port in port_d_l:
check_group_slave(ip, port)
def main():
ip = '10.189.11.223'
port = 8001
get_redis_nodes(ip, port)
check_group()
return
if __name__ == "__main__":
sys.exit(main())
redis-migrate-tool是唯品会的一个方便实用redis间数据迁移工具。
先安装 automake、libtool、autoconf 和 bzip2。
$ cd redis-migrate-tool
$ autoreconf -fvi
$ ./configure
$ make
$ src/redis-migrate-tool -h
[source]
type: redis cluster
servers:
- 10.189.6.188:8001
[target]
type: redis cluster
servers:
- 10.189.6.199:8001
[common]
listen: 0.0.0.0:8888
src/redis-migrate-tool -c rmt.conf -o log -d
$src/redis-migrate-tool -c rmt.conf -o log -C redis_check
Check job is running...
Checked keys: 1000
Inconsistent value keys: 0
Inconsistent expire keys : 0
Other check error keys: 0
Checked OK keys: 1000
All keys checked OK!
Check job finished, used 1.041s
redis-cli --cluster call 10.189.11.222:8001 CONFIG SET masterauth "zKxNbyHxFRjokTc7mzWzHEp7m"
redis-cli --cluster call 10.189.11.222:8001 CONFIG SET requirepass "zKxNbyHxFRjokTc7mzWzHEp7m"
redis-cli --cluster call 10.177.43.93:8001 -a zKxNbyHxFRjokTc7mzWzHEp7m CONFIG REWRITE
config rewrite可以将临时配置的密钥写入配置文件,方式重启后密钥丢失
Redis Manager 是 Redis 一站式管理平台,支持集群的监控、安装、管理、告警以及基本的数据操作功能。 集群监控:支持监控 Memory、Clients 等 Redis 重要指标 。
docker run -d --net=host --name redis-manager -e DATASOURCE_DATABASE='redis_manager' -e DATASOURCE_URL='jdbc:mysql://10.177.42.49:3306/redis_manager?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8' -e DATASOURCE_USERNAME='root' -e DATASOURCE_PASSWORD='BowtpqLZ5jUqvfg' reasonduan/redis-manager
需提前准备好db,部署好之后访问web,默认用户密码admin:admin
–>import cluster 添加集群,可在dashboard中查看相关信息
参考文献
https://github.com/vipshop/redis-migrate-tool
https://www.jianshu.com/p/813a79ddf932