美文网首页Docker容器
docker环境,搭建redis cluster集群

docker环境,搭建redis cluster集群

作者: binecy | 来源:发表于2020-03-14 21:35 被阅读0次

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

    准备镜像

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

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

    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如下

    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命令启动容器时也可以指定容器启动时运行的命令。他们区别如下

    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-entrypoint.sh负责修改配置,启动redis

    #!/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'

    查看进程

    $ 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了。

    如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!


    相关文章

      网友评论

        本文标题:docker环境,搭建redis cluster集群

        本文链接:https://www.haomeiwen.com/subject/growfhtx.html