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

若是您以爲本文不錯,歡迎關注個人微信公衆號,您的關注是我堅持的動力!

相關文章
相關標籤/搜索