docker环境,搭建redis cluster集群

本文分享如何在docker环境中搭建redis cluster集群,并在搭建过程当中分享一些docker的经常使用知识。html

准备镜像

关于bin/ubuntu:16.04镜像的构建请参考docker基础环境搭建node

构建一个bin/redis:5.0.7镜像,该镜像基于bin/ubuntu:16.04,使用源码安装redis-5.0.7(请先下载redis-5.0.7.tar.gz)。
Dockerfile以下:linux

FROM bin/ubuntu:16.04

RUN apt-get update &&  apt install -yqq   make gcc 

WORKDIR /var/lib

COPY redis-5.0.7.tar.gz .
RUN tar -xzvf redis-5.0.7.tar.gz && rm redis-5.0.7.tar.gz

WORKDIR /var/lib/redis-5.0.7
RUN make install

构建一个bin/redis-server镜像,Dockerfile以下redis

FROM bin/redis:5.0.7

COPY docker-entrypoint.sh /usr/local/bin

RUN groupadd -r redis && useradd -r -g redis redis \
&& chmod 777  /usr/local/bin/docker-entrypoint.sh

VOLUME /usr/local/redis/data/
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["--port 6379"]

CMD 和 ENTRYPOINT 指令都是用来指定容器启动时运行的命令。而使用RUN命令启动容器时也能够指定容器启动时运行的命令。他们区别以下sql

  1. 不存在ENTRYPOINT指令时,CMD指令能够指定默认命令。若是使用RUN启动容器时指定了命令,会覆盖CMD指令,而若是没有指定,则执行CMD中的默认命令。
  2. 存在ENTRYPOINT指令,ENTRYPOINT指定的命令不能够被RUN覆盖(除非使用--entrypoint参数),但CMD/RUN能够指定参数(做为ENTRYPOINT指定命令的参数)。若是RUN中指定参数,覆盖CMD中的参数。若是没有指定,则使用CMD中的默认参数。
  3. CMD和ENTRYPOINT指令能够exec模式,如ENTRYPOINT ["docker-entrypoint.sh","--port 6379"],也可使用shell模式,如ENTRYPOINT docker-entrypoint.sh --port 6379,但这时docker会在指定命令前加/bin/sh -c,这样可能会致使一些错误,推荐使用exec模式。

上面Dockerfile配置了容器启动命令为docker-entrypoint.sh,默认参数为--port 6379,也能够在RUN命令中指定参数。
(这里说的RUN命令是启动容器的命令,不是Dockerfile中的RUN指令)docker

docker-entrypoint.sh负责修改配置,启动redisshell

#!/bin/bash

mkdir -p /etc/redis/ /var/log/redis/ /usr/local/redis/data/
chown -R redis:redis /var/log/redis/ /usr/local/redis/data/

cat>>/etc/redis/redis.conf<<EOF
protected-mode no
appendonly yes
logfile /var/log/redis/redis.log
dir /usr/local/redis/data/
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
EOF

exec gosu redis redis-server  /etc/redis/redis.conf  $@
exec是 bash 的内置命令,exec用被执行的命令替换掉当前的shell进程,且exec命令后的其余命令将再也不执行。使用exec和gosu启动redis,可让redis成为PID等于1的进程,保证SIGTERM等信号正常工做。
CMD/ENTRYPOINT指令的exce模式也有这个做用。

启动容器

启动全部的redis容器数据库

for i in `seq 1 6`; do 
     sudo docker run -d --name redis-$i bin/redis-server
done

注意,这里run命令没有linux命令参数,则容器启动后执行ENTRYPOINT指定命令和CMD默认参数 docker-entrypoint.sh '--port 6379'ubuntu

查看进程bash

$ sudo docker top redis-1

能够经过logs目录查看日志

$ sudo docker logs -f  redis-1

(正常使用下redis可能没有日志输出到前台)

docker容器运行时应该尽可能不进行写数据操做(不然删除容器后数据也被删除了),对于数据库这类须要保存动态数据的应用,其数据文件应该保存于卷(volume)中。
VOLUME指令就是用于挂载卷的。
简单来讲,就是将docker目录(/usr/local/redis/data/)挂载到宿主机的目录(docker会在宿主机/var/lib/docker/volumes下建立一个子目录),这样docker输出到该docker目录的文件实际保存到宿主机目录。
若是咱们修改宿主目录的文件,docker也会立刻知道到,这样咱们将代码文件放在宿主机上(方便咱们修改代码),而后让docker容器经过卷来读取文件。
卷还能够实现数据共享, 经过在run命令中使用--volumes-from选项。

Dockerfile中使用了VOLUME指令挂载了一个卷保存redis的AOF文件,这样容器被删除后,文件还保留在宿主机内。
经过docker inspect 能够查看Volume卷的挂载信息

"Mounts": [
    {
        "Type": "volume",
        "Name": "025f5fff7a31e61f8a068b7b6de38731d16b5efac7e96ac0c01892a4139e8d83",
        "Source": "/var/lib/docker/volumes/025f5fff7a31e61f8a068b7b6de38731d16b5efac7e96ac0c01892a4139e8d83/_data",
        "Destination": "/usr/local/redis/data",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],

run命令能够经过-v参数挂载卷,而且能够指定宿主机目录,VOLUME指令没法作到这点。

sudo docker run -d  -v /home/binecy/redis/data/:/usr/local/redis/data/ bin/redis-server

建立集群

获取全部的docker容器ip

$ for i in `seq 1 6`; do 
>    echo `sudo docker inspect -f '{{ .NetworkSettings.IPAddress}}'  redis-$i`
> done
172.17.0.2
172.17.0.3
172.17.0.4
172.17.0.5
172.17.0.6
172.17.0.7

链接到redis-1容器,执行如下命令,建立redis cluster

$ sudo docker exec -it redis-1 /bin/bash
root$ redis-cli --cluster create  172.17.0.2:6379 172.17.0.3:6379 172.17.0.4:6379 172.17.0.5:6379 172.17.0.6:6379 172.17.0.7:6379  --cluster-replicas 1

docker网络

下面重点来看看docker网络

bridge模式

上面例子我使用的是docker默认的网络模式,即bridge模式

Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。

当Docker 进程启动时,会自动在主机上建立一个 docker0 虚拟网桥,默认分配网段172.17.0.0/16,实际上就是一个 Linux bridge 网桥,能够理解为一个软件交换机,附加在其上的任何网卡之间都能自动转发数据包。

在bridge模式下,docker会为每个容器建立一个Network Namespace。建立一个容器时,容器从docker0中的子网分配一个IP地址,并建立一对veth虚拟网络设备,其中一个设备在容器中做为容器的网卡,另外一个设备桥接在宿主机docker0上,可经过命令brctl show 查看(名称为vethXXX),经过这样的桥接方法宿主机上的全部容器都处于同一个二层网络中,这样使得容器与容器以及容器与宿主机之间可以互相通讯。(但不能跨宿主机通讯)。

veth 是 Virtual ETHernet 的缩写,是一种虚拟网络设备。当老是以两张虚拟网卡(Veth peer)形式被建立,而且在一个网卡上的数据包可直接转发给另外一个网卡上,即便这两个网卡不在同一个namespace中。

咱们也能够自定义了一个网桥

docker network create redis-net

自定义网桥和默认的docker0网桥最大区别是自定义网桥能够经过--network-alias指定容器的网络别名,容器间能够经过网络别名通讯。

for i in `seq 1 6`; do 
     sudo docker run -d --name  redis-$i --network redis-net --network-alias redis-$i bin/redis-server
done

这样,就能够在redis-1容器中,能够ping redis-2 ping ping通redis-2容器,也能够经过 redis-cli-h redis-2 链接到redis-2容器的redis。

Mysql MGR,Zookeeper等分布式系统,须要在应用启动前将集群内其余成员的ip信息写入配置文件,这时很适合使用网络别名。
(redis-cli --cluster create命令没法使用网络别名)

使用自定义网络后,能够经过如下命令获取docker容器ip

sudo docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'  redis-1
docker inspect -f能够支持go template语法,这里使用range循环遍历全部的.NetworkSettings.Networks,并取其下的IPAddress变量。

不过这样建立的redis cluster只有宿主机能够访问,外部机器(非宿主机)没法访问,由于docker中的redis端口没有映射到宿主机上。
咱们也能够在run命令上使用-p参数映射端口(-P能够映射Dockerfile中EXPOSE指定的端口):

for i in `seq 1 6`; do 
     sudo docker run -d --name redis-$i -p 6379 -p 16379 bin/redis-server
done

但即便这样外部机器仍是没法访问redis cluster,由于redis cluster内部通讯用的docker容器ip(就是redis-cli --cluster create命令中的ip),外部机器访问redis cluster时,redis cluster会将这些ip返回给外部机器,并让外部机器经过它们来访问redis cluster,但外部机器没法访问docker容器ip,因此这种方式只能在宿主机上访问redis cluster。

run命令中的-p选项映射端口是经过NAT协议实现的,可使用iptables命令查看。

host模式

下面说一下host模式
host模式下容器共享宿主机的Network Namespace,容器内启动的端口直接是宿主机的端口,而且容器不会建立网卡和IP,直接使用宿主机的网卡和IP

​下面在host模式下搭建redis cluster集群。
启动redis 应用

for port in `seq 7000 7005`; do 
    sudo docker run -d  --name redis-${port} --net=host  bin/redis-server "--port  ${port}" 
done

这里RUN命令指定了参数,容器启动后执行ENTRYPOINT指定命令和RUN参数docker-entrypoint.sh '--port ${port}'

注意,这里容器启动的端口就是宿主机的端口,要保证宿主机端口不会冲突。

经过宿主机IP192.168.0.102启动redis cluster

$ sudo docker exec -it redis-7000 /bin/bash
root$ redis-cli --cluster create  192.168.0.102:7000 192.168.0.102:7002 192.168.0.102:7002 192.168.0.102:7003 192.168.0.102:7004 192.168.0.102:7005  --cluster-replicas 1

这样外部机器就能够经过宿主机IP192.168.0.102访问redis cluster。

关于docker网络,这有一篇很好的文章 -- docker网络
Dockerfile优化 -- 如何编写最佳的Dockerfile

本文说了docker CMD/ENTRYPOINT,VOLUME,inspect,网络等知识点,基本知足咱们平常使用docker了。

若是您以为本文不错,欢迎关注个人微信公众号,您的关注是我坚持的动力!

相关文章
相关标签/搜索