【编者的话】本文介绍了如何使用Docker搭建Redis集群,很多读者都在问Docker能带来哪些实质性的好处,我想本文就是一个很好的例子。不使用Docker你也可以搭建Redis集群,那使用Docker后会有怎么样的优势了?我想可以用两个词总结:快速和复用。
我们经常会遇到这样一个问题:当我们想进行一个完整的测试的时候,往往缺少硬件或者其它资源。为了满足需求,我可能需要三台服务,或者说三个虚拟机。但是我发现我们没有时间来创建它们,并且如果要物理机的话我们也没有那么多资源。这也是为什么我对Docker如此感兴趣,因为它可以解决我的问题。
我想在Ubuntu上创建三个运行Redis的Docker容器,并把它们连接起来,然后我就可以自由的在测试和开发过程中水平的扩展了, 接下来我就给你们展示我是怎么做的,以及这样做的优势。
1. 下载和配置基础镜像
我使用的是非常优秀的phusion镜像作为基础镜像,它增加了很多Docker忽略的特性, 比如按序启动服务等等, 关于这个镜像的更多信息,可以点击这里了解。
首先,让我们使用Docker来pullphusion镜像(译者注:建议使用高版本的Docker下载,低版本会有问题)。
docker pull phusion/baseimage
下载完成之后, 你可以通过docker images命令看到最新下载的镜像。
docker images
REPOSITORY TAG IMAGE ID CREATE D SIZE
docker.io/phusion/baseimage latest 166cfc3f6974 5 mont hs ago 209 MB
docker.io/curiousby/centos-ssh-root-java-tomcat-maven-nexus-svn original 358f17fad349 8 mont hs ago 1.17 GB
docker.io/curiousby/centos-ssh-root-java-tomcat-maven-nexus original eb7293ef9028 8 mont hs ago 1.01 GB
这个镜像非常好用,你可以在容器启动的时候指定特定服务的启动顺序, 在这里我想给这个镜像加一个SSH密码登录的功能,而不是使用SSH key。因为是在本地运行,所以不用太担心安全的问题。我们需要创建一个phusion的实例, 然后通过SSH key登录, 并且修改配置,重启SSH,之后我们就可以使用root登录了。
首先,用phusion基础镜像创建一个新的容器。
docker run -d --name redis phusion/baseimage /sbin/my_init --enable-insecure-key
/sbin/my_init是允许phusion在容器启动的时候启动你的服务。 enable-insecure-key允许我们使用‘insecure key‘ssh进新的容器。
现在我们已经部署了容器, 接下来需要得到它的IP地址,可以使用docker inspect命令
docker inspect redis | grep IPA
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAMConfig": null,
"IPAddress": "172.17.0.2",
接下来,下载’insecure key‘并使用它登录这个容器。
curl -o insecure_key -fSL https://github.com/takei-yuya/insecure_public_key/blob/master/insecure_key.pub
下载insecure_key到当前目录后,修改权限
chmod 600 insecure_key
[root@svn sam]# ls -l
总用量 64
-rw------- 1 root root 64127 7月 22 18:20 insecure_key
[root@svn sam]# ifconfig
docker0: flags=4163 mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::42:39ff:fef4:bed8 prefixlen 64 scopeid 0x20
ether 02:42:39:f4:be:d8 txqueuelen 0 (Ethernet)
RX packets 33 bytes 2076 (2.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 21 bytes 2228 (2.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens192: flags=4163 mtu 1500
inet 10.0.100.210 netmask 255.255.255.0 broadcast 10.0.100.255
inet6 fe80::8815:e9b3:1b5a:faee prefixlen 64 scopeid 0x20
ether 00:50:56:ae:73:2c txqueuelen 1000 (Ethernet)
RX packets 821955 bytes 143646869 (136.9 MiB)
RX errors 0 dropped 72 overruns 0 frame 0
TX packets 557848 bytes 41166593 (39.2 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73 mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10
loop txqueuelen 1 (Local Loopback)
RX packets 2 bytes 196 (196.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2 bytes 196 (196.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth9e19302: flags=4163 mtu 1500
inet6 fe80::d4a2:7dff:fe37:fe07 prefixlen 64 scopeid 0x20
ether d6:a2:7d:37:fe:07 txqueuelen 0 (Ethernet)
RX packets 13 bytes 882 (882.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 13 bytes 922 (922.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
virbr0: flags=4099 mtu 1500
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether 52:54:00:e9:c4:8a txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@svn sam]# ssh -i insecure_key [email protected]
The authenticity of host '10.0.100.210 (10.0.100.210)' can't be established.
ECDSA key fingerprint is SHA256:LRjdi8K/ZLedi3BtvuLkY8E7QOQs5kz7/DUeglehNc0.
ECDSA key fingerprint is MD5:66:1c:7f:93:06:d7:d9:4a:5a:d8:1d:eb:77:6b:9f:8c.
Are you sure you want to continue connecting (yes/no)?
[root@svn sam]# ssh -i insecure_key [email protected]
Enter passphrase for key 'insecure_key':
Enter passphrase for key 'insecure_key':
Enter passphrase for key 'insecure_key':
[email protected]'s password:
Last login: Sun Jul 22 16:54:11 2018 from 10.0.10.69
祝贺你, 你现在SSH进入容器了!下面你需要修改SSH,从而我们不再需要’insecure key‘了,为了做到这个,打开’/etc/ssh/sshd_config‘找到下面这一行, 并去掉注释
PermitRootLogin yes
保存文件, 我们再设置root的密码。
passwd
更改用户 root 的密码 。
新的 密码:
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
[root@svn ~]#
以上我们做了一些准备工作,接下来我们将在这个容器中安装Redis。
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8e044161c998 phusion/baseimage "/sbin/my_init --e..." 37 minutes ago Up 37 minutes redis
c1e4909a8f60 curiousby/centos-ssh-root-java-tomcat-maven-nexus-svn:original "/usr/local/nexus/..." 4 months ago Exited (255) About an hour ago 0.0.0.0:3690->3690/tcp, 22/tcp, 0.0.0.0:8080-8081->8080-8081/tcp docker1
首先启动我们刚才创建的容器
docker start 8e044161c998
进入容器中
docker exec -it 8e044161c998 /bin/bash
下载redis到容器中
wget https://github.com/antirez/redis/archive/3.0.0-rc1.tar.gz
bash: wget: command not found
发现wget这个命令不存在,这个时候,我们需要升级系统,然后安装wget命令
apt-get update #更新系统
apt-get upgrade #升级系统
apt-get install wget gcc make#安装wget、gcc、make
然后我们在下载Redis 3.0.0 RC1
wget https://github.com/antirez/redis/archive/3.0.0-rc1.tar.gz
--2018-07-22 10:46:46-- https://github.com/antirez/redis/archive/3.0.0-rc1.tar.gz
Resolving github.com (github.com)... 13.229.188.59, 13.250.177.223, 52.74.223.119
Connecting to github.com (github.com)|13.229.188.59|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/antirez/redis/tar.gz/3.0.0-rc1 [following]
--2018-07-22 10:46:49-- https://codeload.github.com/antirez/redis/tar.gz/3.0.0-rc1
Resolving codeload.github.com (codeload.github.com)... 13.250.162.133, 54.251.140.56, 13.229.189.0
Connecting to codeload.github.com (codeload.github.com)|13.250.162.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: ‘3.0.0-rc1.tar.gz’
3.0.0-rc1.tar.gz [ <=> ] 1.27M 649KB/s in 2.0s
2018-07-22 10:46:53 (649 KB/s) - ‘3.0.0-rc1.tar.gz’ saved [1335857]
移动刚下载的包到home目录下
ls
3.0.0-rc1.tar.gz bin dev home lib64 mnt proc run srv tmp var
bd_build boot etc lib media opt root sbin sys usr
root@8e044161c998:/# mv 3.0.0-rc1.tar.gz home/
root@8e044161c998:/# cd home/
root@8e044161c998:/home# ls
3.0.0-rc1.tar.gz
解压这个包并make。
tar -zxvf 3.0.0-rc1.tar.gz
进入到刚刚解压的目录
cd redis-3.0.0-rc1/
root@8e044161c998:/home/redis-3.0.0-rc1#
执行make
make
最后, 我们把编译好的代码中的几个可执行命令移动到/usr/bin/下面
root@e3919192d9e3:/home/redis-3.0.0-rc1# cd src
root@e3919192d9e3:/home/redis-3.0.0-rc1/src# mv redis-cli redis-server redis-sentinel /usr/bin/
root@e3919192d9e3:/home/redis-3.0.0-rc1/src# cd ..
root@e3919192d9e3:/home/redis-3.0.0-rc1# mkdir -p /etc/redis/
root@e3919192d9e3:/home/redis-3.0.0-rc1# cp redis.conf /etc/redis/redis.conf
现在打开文件/etc/redis/redis.conf, 找到‘daemonize no’改为‘daemonize yes‘,然后启动它!
/etc/redis/redis.conf
在这个配置文件中我们只要找到‘salveof’这一行,然后去掉注释,修改为node1的ip地址,像下面这样:
slaveof 172.17.0.2 6379
root@e3919192d9e3:/home/redis-3.0.0-rc1/src# redis-server /etc/redis/redis.conf root@e3919192d9e3:/home/redis-3.0.0-rc1/src#
好了, Redis现在已经安装好了,并且在容器里面运行了,使用的配置文件是/etc/redis/redis.conf。
3. 让Docker在启动容器的时候启动Redis服务
现在我们的容器正在运行Redis,并且也可以使用SSH登录了,我们还需要让它在容器启动的时候自动启动Redis服务,使用phusion基础镜像来实现这点相当的容易。首先,因为我们启动容器的时候使用了/sibn/my_init, 它会去运行任何我们放在/etc/service/*下面的程序。所以,对于我们来说,我们只要去创建一个目录以及在这个目录里面再创建一个叫run的文件,像下面这样:
vim redis.run
在这个run文件里面,我们加入下面的内容:
#!/bin/shset -e exec /usr/bin/redis-server /etc/redis/redis.conf
最后,记得给run文件添加可执行权限。
4. 提交镜像以便于重用
现在我们的redis容器运行良好, 我们想要把它保存为伪模板,以便在Docker上重复部署。做到这个非常简单,我们只要使用‘docker commit ...’这个镜像到我们本地的库就可以了,像下面这样:
现在把这个镜像推送到阿里云的镜像仓库里面
首页登录镜像仓库
docker login --username=zyf1985121 registry.cn-qingdao.aliyuncs.com
为镜像打个tag
docker tag eaa36147691f registry.cn-qingdao.aliyuncs.com/ylcaifu/docker:redis-cluster-node
提交镜像
docker push registry.cn-qingdao.aliyuncs.com/ylcaifu/docker:redis-cluster-node
查看
5. 部署新镜像的3个实例
为了解释清楚,我会删除之前创建的容器,以免后面搞晕。首先使用docker stop redis,之后再docker rm redis:
[root@svn zntg]# docker stop redis
redis
[root@svn zntg]# docker rm redis
redis
[root@svn zntg]#
很简单,下面让我们部署新镜像的3个实例,我们使用了Docker的端口映射机制,从而我们就可以使用Host服务器的IP访问这些实例,我们给这些实例关联的端口如下:
- node1 - hostip:7001
- node2 - hostip:7002
- node3 - hostip:7003
所以我们运行redis-cli -h 192.168.0.2 -p 7001,它将重定向到172.17.0.x -p 6379,例如下面我们部署了三个实例:
[root@svn zntg]# docker run -d --name node1 -p 7001:6379 redis-cluster-node /sbin/my_init
36ff5e40d09325599cdf2122a63065622ab36fea8ea2fb09621c83a966afbf2b
[root@svn zntg]# docker run -d --name node2 -p 7002:6379 redis-cluster-node /sbin/my_init
c0b3876f293ebe306de86b1b499d2614fe6228a2ba8cbb6fec5d730a2ef7ac45
[root@svn zntg]# docker run -d --name node3 -p 7003:6379 redis-cluster-node /sbin/my_init
73b1e766eb8081017a8d83d3aced043763ff61550a6bf368aa858234e4d0fe43
[root@svn zntg]#
现在我们可以运行docker ps -as来查看我们运行的三个Redis容器:
[root@svn zntg]# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
73b1e766eb80 redis-cluster-node "/sbin/my_init" 47 seconds ago Up 46 seconds 0.0.0.0:7003->6379/tcp node3 18.8 kB (virtual 456 MB)
c0b3876f293e redis-cluster-node "/sbin/my_init" About a minute ago Up About a minute 0.0.0.0:7002->6379/tcp node2 18.8 kB (virtual 456 MB)
36ff5e40d093 redis-cluster-node "/sbin/my_init" About a minute ago Up About a minute 0.0.0.0:7001->6379/tcp node1 18.8 kB (virtual 456 MB)
c1e4909a8f60 curiousby/centos-ssh-root-java-tomcat-maven-nexus-svn:original "/usr/local/nexus/..." 4 months ago Exited (255) 3 hours ago 0.0.0.0:3690->3690/tcp, 22/tcp, 0.0.0.0:8080-8081->8080-8081/tcp docker1 0 B
[root@svn zntg]#
现在我们看到了3个容器,分别名字为node1、node2和node3,并且有一个专门的端口与Redis 服务的端口相映射。为了测试这样的映射是正确的,可以在另外一台机器使用redis-cli登录到各个Redis服务器上面:
root@36ff5e40d093:/home/redis-3.0.0-rc1/src# redis-cli -h 10.0.100.210 -p 7002
10.0.100.210:7002> quit
root@36ff5e40d093:/home/redis-3.0.0-rc1/src# redis-cli -h 10.0.100.210 -p 7003
10.0.100.210:7003> quit
root@36ff5e40d093:/home/redis-3.0.0-rc1/src# redis-cli -h 10.0.100.210 -p 7004
Could not connect to Redis at 10.0.100.210:7004: Connection refused
not connected> quit
root@36ff5e40d093:/home/redis-3.0.0-rc1/src# redis-cli -h 10.0.100.210 -p 7001
10.0.100.210:7001>
正如你看到的, 我们可以使用 hostip+port登录到对应的Redis服务器上, 当我们尝试一个不正确的端口时,却不行。
6. 配置Redis的从节点
我们有了三个独立的Redis服务器, 现在我们想把它们连接到一起, 从而我们可以测试这个集群的扩展性,对集群的监控,或者是做其它的事情。
我们把node1作为主节点,把node2和node3配置为它的从节点,这可以简单的通过修改/etc/redis/redis.conf这个文件来实现。SSH进入node2和node3节点,修改配置文件,然后重启容器:
进入node2容器里面
修改配置文件
/etc/redis/redis.conf
在这个配置文件中我们只要找到‘salveof’这一行,然后去掉注释,修改为node1的ip地址,像下面这样:
slaveof 172.17.0.2 6379
node3执行同样的操作
最后使用命令docker restart node2重启容器, 现在它就是node1的一个从节点了。
为了验证这个, 使用redis-cli分别登录到node1和node2, 在node1上运行命令set hello world, 然后在node2上运行get hello,如果配置正确的话会得到下面的结果:
进入node1容器
docker exec -it node1 /bin/bash
cd /home/redis-3.0.0-rc1/src#
root@36ff5e40d093:/usr/bin# redis-cli -h 10.0.100.210 -p 7001
在node1容器里面设置一个key
set hello world
发现报错了
root@36ff5e40d093:/home/redis-3.0.0-rc1/src# redis-cli -h 10.0.100.210 -p 7001
10.0.100.210:7001> keys *
Error: Connection reset by peer
解决这个错误是修改/etc/redis/redis.conf配置文件
将bind 127.0.0.1改为bind0.0.0.0
然后重启redis
root@36ff5e40d093:/usr/bin# redis-server /etc/redis/redis.conf
在执行命令
root@36ff5e40d093:/usr/bin# redis-cli -h 10.0.100.210 -p 7001
10.0.100.210:7001> set hello world
OK
10.0.100.210:7001>
发现已经成功了,接下来我们在node2中取获取这个hello,看是否可以得到值
[root@svn sam]# docker exec -it node2 /bin/bash
root@c0b3876f293e:/# vim etc/redis/redis.conf
root@c0b3876f293e:/# cd /usr/bin/
root@c0b3876f293e:/usr/bin# redis-server /etc/redis/redis.conf
root@c0b3876f293e:/usr/bin# redis-cli -h 10.0.100.210 -p 7002
10.0.100.210:7002> get hello
"world"
10.0.100.210:7002>
正如我们期望的那样,在node2这个容器中,我们成功的拿到了node1里面设置的key的值
用reids客户端也可以成功连接
[root@svn sam]# docker exec -it node3 /bin/bash
root@73b1e766eb80:/# vim /etc/redis/redis.conf
root@73b1e766eb80:/# cd /usr/bin/
root@73b1e766eb80:/usr/bin# redis-se
redis-sentinel redis-server
root@73b1e766eb80:/usr/bin# redis-server /et/redis/redis.conf
1655:C 22 Jul 14:32:15.043 # Fatal error, can't open config file '/et/redis/redis.conf'
root@73b1e766eb80:/usr/bin# redis-server /etc/redis/redis.conf
root@73b1e766eb80:/usr/bin# redis-cli -h 10.0.100.210 -p 7003
10.0.100.210:7003> get hello
"world"
10.0.100.210:7003>
恭喜,基于Docker的、拥有三个节点且可水平扩展的Redis集群就这样搭好了。
接下来的下一篇博客中,我将给大家展示如何使用Opsview去监控Redis集群,你可以看到一个像下面这样展示你的集群统计信息的可视化界面: