Docker 容器搭建及 Redis 叢集原理

JaneWorld發表於2019-06-15

Docker 的主要用途,目前有三大類。
(1)提供一次性的環境。比如,本地測試他人的軟體、持續整合的時候提供單元測試和構建的環境。
(2)提供彈性的雲服務。因為 Docker 容器可以隨開隨關,很適合動態擴容和縮容。
(3)組建微服務架構。通過多個容器,一臺機器可以跑多個服務,因此在本機就可以模擬出微服務架構。

~Centos 安裝docker~

1、更新update到最新的版本
yum  update

2、解除安裝老版本docker
yum  remove docker  docker-common docker-selinux  docker-engine

3、安裝需要的軟體包
yum install -y yum-utils  device-mapper-persistent-data lvm2

4、設定yum源
yum-config-manager --add-repo https://download.docker.com/linux/centos/d...

5、檢視docker版本
 yum list docker-ce --showduplicates|sort -r  

6、安裝docker
yum  install  docker-ce-18.03.1.ce -y

7、啟動docker
systemctl start docker

8、加入開機自啟
systemctl enable docker

9、配置國內映象
vi /etc/docker/daemon.json
{
    "registry-mirrors": ["http://hub-mirror.c.163.com"]
}

Docker 指令
查詢docker命令:docker --help
1. 啟動
2. 刪除
 停止所容器
docker stop  $(docker ps -aq)
docker rm  $(docker ps -aq)
檢視當前映象:docker image
檢視當前docker版本:docker -v
建立映象:docker build -t redis-test . (建立一個名叫redis-test的映象) .:代表當前上下文路徑、必須有
docker build --no-cache -t redis2 .
指定網段:172.10.0.0/16 並命名為mynetwork,指令如下:
docker network create  --subnet=172.10.0.0/16  mynetwork
通過docker命令分別進入到redis容器當中,主從複製檢視另外一個文件
構建容器:docker run -it  --name  redis-master  --net mynetwork  -p 6380:6379  --ip 172.10.0.2  redis
構建容器:docker run -it  --name  redis-slave  --net mynetwork  -p 6381:6379  --ip 172.10.0.3  redis

檢視所有容器:docker ps -a (一個容器可以建立多個映象) 或者 docker container
檢視網路型別:docker network ls
檢視容器IP地址:docker network inspect mynetwork
進入容器:docker exec -it redis(容器名) bash sudo docker exec -it 775c7c9ee1e1 /bin/bash
執行容器:docker run -ti redis-master(容器名) /bin/bash
退出容器:exit
檢視docker版本:yum list installed | grep docker
解除安裝docker安裝包:yum remove docker-ce.x86_64 ddocker-ce-cli.x86_64 -y
刪除映象/容器等:rm -rf /var/lib/docker
清除快取並重新構建yum 源: yum clean all yum makecache

配置DOCKER_HOST:sudo vim /etc/profile.d/docker.sh
docker
https://www.cnblogs.com/SH170706/p/1024250...

容器外面和裡面公用一個資料夾:VOLUME /usr/docker/config
構建容器的時候-v建立公用資料夾:docker run -itd --name redis-maser2 -v /usr/docker/config:/config --net mynetwork -p 6383:6379 --ip 172.10.0.4 redis2
在從伺服器裡,vi /etc/redis.conf
配置bind 0.0.0.0
關閉保護模式proteced model no
slaveof 主節點IP 6379
info replication(在從節點客戶端下,檢視角色)

報錯Could not connect to Redis at 127.0.0.1:6379: Connection refused
主容器裡啟動redis服務:redis-server ./redis-master.conf &
從容器裡啟動redis服務:redis-server ./redis-slave.conf &
哨兵容器裡啟動redis服務:redis-sentinel ./redis-sentinel.conf &
檢視伺服器日誌:vi /var/log/redis/redis.log
vi /var/log/redis/sentinel.log
停止全部容器:docker stop $(docker ps -aq)

RDB快照和AOF快照

檢視獲取 redis 服務的配置引數:CONFIG GET maxmemory(主從記憶體數量)
檢視伺服器執行id(runid):info server
增加延遲5秒同步:tc qdisc add dev eth0 root netem delay 5000ms
刪除: del

全量複製:儘量避免主節點給子節點造成全量複製,造成一次傳送大量資料。
部分複製:主從複製中因網路閃斷原因造成資料丟失場景,從節點再次連上主節點後,如果條件允許,主節點會補發丟失資料給從節點。補發的資料遠遠小於全量複製,有效避免全量複製的過高開銷。特別注意的是,網路中斷時間過長,造成主節點沒能夠完整儲存中斷期間執行的寫命令,則無法進行部分複製,仍使用全量複製。
Redis全量複製的過程如下:
1、Redis 內部會發出一個同步命令,剛開始是 Psync 命令,Psync ? -1表示要求 master 主機同步資料
2、主機會向從機傳送 runid 和 offset,因為 slave 並沒有對應的 offset,所以是全量複製
3、從機 slave 會儲存 主機master 的基本資訊 save masterInfo
4、主節點收到全量複製的命令後,執行bgsave(非同步執行),在後臺生成RDB檔案(快照),並使用一個緩衝區(稱為複製緩衝區)記錄從現在開始執行的所有寫命令
5、主機send RDB 傳送 RDB 檔案給從機
6、傳送緩衝區資料
7、重新整理舊的資料,從節點在載入主節點的資料之前要先將老資料清除
8、載入 RDB 檔案將資料庫狀態更新至主節點執行bgsave時的資料庫狀態和緩衝區資料的載入。
全量複製開銷,主要有以下幾項:
bgsave 時間
RDB 檔案網路傳輸時間
從節點清空資料的時間
從節點載入 RDB 的時間

部分複製

部分複製是 Redis 2.8 以後出現的,之所以要加入部分複製,是因為全量複製會產生很多問題,比如像上面的時間開銷大、無法隔離等問題, Redis 希望能夠在 master 出現抖動(相當於斷開連線)的時候,可以有一些機制將複製的損失降低到最低。
1、如果網路抖動(連線斷開 connection lost)
2、主機master 還是會寫 replbackbuffer(複製緩衝區)
3、從機slave 會繼續嘗試連線主機
4、從機slave 會把自己當前 runid 和偏移量傳輸給主機 master,並且執行 pysnc 命令同步
5、如果 master 發現你的偏移量是在緩衝區的範圍內,就會返回 continue 命令
6、同步了 offset 的部分資料,所以部分複製的基礎就是偏移量 offset。
 注意:

正常情況下redis是如何決定是全量複製還是部分複製

從節點將offset傳送給主節點後,主節點根據offset和緩衝區大小決定能否執行部分複製:
如果offset偏移量之後的資料,仍然都在複製積壓緩衝區裡,則執行部分複製;
如果offset偏移量之後的資料已不在複製積壓緩衝區中(資料已被擠出),則執行全量複製。

伺服器執行ID(runid)

每個Redis節點(無論主從),在啟動時都會自動生成一個隨機ID(每次啟動都不一樣),由40個隨機的十六進位制字元組成;runid用來唯一識別一個Redis節點。 通過info server命令,可以檢視節點的runid:

主從節點初次複製時,主節點將自己的runid傳送給從節點,從節點將這個runid儲存起來;當斷線重連時,從節點會將這個runid傳送給主節點;主節點根據runid判斷能否進行部分複製:

如果從節點儲存的runid與主節點現在的runid相同,說明主從節點之前同步過,主節點會繼續嘗試使用部分複製(到底能不能部分複製還要看offset和複製積壓緩衝區的情況)

如果從節點儲存的runid與主節點現在的runid不同,說明從節點在斷線前同步的Redis節點並不是當前的主節點,只能進行全量複製。

*主從複製進階常見問題解決**

1、讀寫分離
2、主從配置不一致
3、規避全量複製
4、規避複製風暴

設定從節點個數:CONFIG set min-slaves-to-write 2
檢視設定記憶體:CONFIG get relbacklogsize
redis的主節點重啟:debug reload(重啟後,主節點的runid和offset都不受影響)

Sentinel 的核心配置:
sentinel monitor mymaster 127.0.0.1 7000 2
監控的主節點的名字、IP 和埠,最後一個2的意思是有幾臺 Sentinel 發現有問題,就會發生故障轉移,例如 配置為2,代表至少有2個 Sentinel 節點認為主節點不可達,那麼這個不可達的判定才是客觀的。對於設定的越小,那麼達到下線的條件越寬鬆,反之越嚴格。一般建議將其設定為 Sentinel 節點的一半加1

主從樹狀結構:是哨兵和叢集的基礎,是高可用的基礎。
一主兩從三哨兵,客戶端訪問哨兵叢集。
kill掉主節點後,哨兵會幫助,從節點中選舉出新的主節點。
docker network create --subnet=172.10.0.0/16 mytest
主節點容器:docker run -itd --name redis-master --net mytest -p 6380:6379 --ip 172.10.0.2 redis2 Master
從節點容器:docker run -itd --name redis-slave2 --net mytest -p 6384:6379 --ip 172.10.0.6 redis2 Slave
從節點容器:docker run -itd --name redis-slave3 --net mytest -p 6385:6379 --ip 172.10.0.7 redis2 Slave
哨兵節點容器:docker run -itd --name redis-sentinel1 --net mytest -p 22530:22530 --ip 172.10.0.9 redis2 Sentinel1
哨兵節點容器:docker run -itd --name redis-sentinel2 --net mytest -p 22531:22531 --ip 172.10.0.10 redis2 Sentinel2
哨兵節點容器:docker run -itd --name redis-sentinel3 --net mytest -p 22532:22532 --ip 172.10.0.11 redis2 Sentinel3

啟動主容器: redis-server ./config/redis-master.conf
檢視主從節點資訊:SENTINEL masters
檢視從節點資訊:SENTENEL slaves mymaster
檢視節點資訊:docker inspect mytest
redis-sentinel /etc/redis-sentinel.conf &
檢視連結資訊:vi /var/log/redis/sentinel.log(可看到哨兵的redis埠號)
切換到哨兵redis的客戶端:redis-cli -p 26379(埠號)
哨兵redis的客戶端,獲取當前主機的連線和master:SENTINEL get-master-addr-by-name mymaster

關掉正在使用該檔案的程式

快照的生成,會fork一個子程式出來。
取消主從複製:slaveof no one
重啟從伺服器的複製:SLAVEOF 172.10.0.2 6379(主節點的IP)
安裝網路延遲:yum install iproute -y
安裝swoole:pecl install swoole

redis文件:
http://redisdoc.com/
https://blog.csdn.net/q1035331653/article/...
docker文件:
http://www.runoob.com/docker/docker-run-co...

http://www.dockerinfo.net/docker%E5%AE%B9%...
使用Dockerfile定製映象:
1.FROM 指定基礎映象
2.RUN 執行命令(最新docker最多接受run 127層左右,用&&或\可避免)
FROM centos:latest
RUN groupadd -r redis && useradd -r -g redis redis\
RUN yum -y update && yum -y install epel-release && yum -y install redis && yum -y install net-tools\
EXPOSE 6379(暴露埠)

docker 編排工具搭建叢集
docker-composer.yaml
image:公用映象
container_name:容器名稱
working_dir:公共工作目錄
environment:環境變數
ports:外部的linux雲伺服器埠和容器的埠對映
networks:ipv4_address:172.50.0.3
stdin_open:輸入
tty:終端
privileged:true 許可權的提取
volunums:宿主機跟容器之間的共享目錄
entrypoint:容器啟動後會啟動的命令

1、docker-compose構建cluster叢集
2、Redis-cluster叢集伸縮原理

redis的Ruby工具快遞建立叢集(redis-trib.rb是採用Ruby實現Redis的叢集管理工具)

啟動好6個節點之後,使用 redis-trib.rb create 命令完成節點握手和槽分配過程

redis-trib.rb create --replicas 1 47.98.147.49:6391  47.98.147.49:6392  47.98.147.49:6393 47.98.147.49:6394 47.98.147.49:6395 47.98.147.49:6396
報錯:-bash: gem: command not found
解決連結:https://blog.csdn.net/zhaoraolin/article/d...
https://blog.csdn.net/Hello_World_QWP/arti...

在當前Dockerfile的目錄下構建:docker build -t redis-cluster .
編排指令:docker-compose -p redis-cluster up -d
env
檢視工作目錄:echo $PWD
檢視埠:echo $PORT
echo /config/nodes-${PWD}.conf
進入節點:redis-cli -p 6394
檢視叢集狀態:cluster info
檢視節點資訊:cluster nodes
cluster meet 116.255.176.221 6391
通過叢集模式設計客戶端:redis-cli -c -h 116.255.176.221 -p 6394 set tls 123

~二、Redis-cluster原理~

    ## **~1、~****~節點通訊~**
    在分散式儲存中需要提供維護節點後設資料資訊的機制,所謂後設資料是指:節點負責哪些資料,是否出現故障等狀態資訊,Redis 叢集採用  Gossip(流言)協議,Gossip 協議工作原理就是節點彼此不斷通訊交換資訊,一段時間後所有的節點都會知道叢集完整的資訊,這種方式類似流言傳播

叢集中的每個節點都會單獨開闢一個 TCP 通道,用於節點之間彼此通訊,通訊埠號在基礎埠上加10000。

2)每個節點在固定週期內通過特定規則選擇幾個節點傳送 ping 訊息。

3)接收到 ping 訊息的節點用 pong 訊息作為響應。

叢集中每個節點通過一定規則挑選要通訊的節點,每個節點可能知道全部節點,也可能僅知道部分節點,只要這些節點彼此可以正常通訊,最終它們會達到一致的狀態。當節點出故障、新節點加入、主從角色變化、槽資訊變更等事件發生時,通過不斷的 ping/pong 訊息通訊,經過一段時間後所有的節點都會知道整個叢集全部節點的最新狀態,從而達到叢集狀態同步的目的

## **~2、叢集伸縮~**

Redis 叢集提供了靈活的節點擴容和收縮方案。在不影響叢集對外服務的情況下,可以為叢集新增節點進行擴容也可以下線部分節點進行縮容。

    #### 2.1、**槽和資料與節點的對應關係**

當主節點分別維護自己負責的槽和對應的資料,如果希望加入1個節點實現叢集擴容時,需要通過相關命令把一部分槽和資料遷移給新節點。
每個節點負責的槽和資料相比之前變少了從而達到了叢集擴容的目的,叢集伸縮=槽和資料在節點之間的移動。

    ### **2.2、擴容操作**

擴容是分散式儲存最常見的需求,Redis 叢集擴容操作可分為如下步驟:
1)準備新節點。
2)加入叢集。
3)遷移槽和資料。

2.2.1、準備新節點

        需要提前準備好新節點並執行在叢集模式下,新節點建議跟叢集內的節點配置保持一致,便於管理統一。
        ### **2.2.2、加入叢集**
        通過 redis-trib.rb  add-node  127.0.0.1:6397  127.0.0.1:6391 實現節點新增
        要加入的節點  叢集中的節點
        docker run  -itd  --name redis-master4  -v  /usr/docker/redis/config:/config --net redis-cluster_redis-master -e PORT=6397 -p  6397:6397 -p 16397:16397   --ip  172.50.0.5  redis-cluster

    3. **遷移槽和資料**

加入叢集后需要為新節點遷移槽和相關資料,槽在遷移過程中叢集可以正常提供讀寫服務,遷移過程是叢集擴容最核心的環節,下面詳細講解。

槽是 Redis 叢集管理資料的基本單位,首先需要為新節點制定槽的遷移計劃,確定原有節點的哪些槽需要遷移到新節點。遷移計劃需要確保每個節點負責相似數量的槽,從而保證各節點的資料均勻,比如之前是三個節點,現在是四個節點,把節點槽分佈在四個節點上。

資料遷移過程是逐個槽進行的
流程說明:
1)對目標節點傳送匯入命令,讓目標節點準備匯入槽的資料。
2)對源節點傳送匯出命令,讓源節點準備遷出槽的資料。
3)源節點迴圈執行遷移命令,將槽跟資料遷移到目標節點。

redis-trib 提供了槽重分片功能,命令如下:
redis-trib.rb reshard host:port --from --to --slots --yes --timeout --pipeline

### **3.1、遷移操作**

redis-trib.rb reshard 127.0.0.1:6379
列印出叢集每個節點資訊後,reshard 命令需要確認遷移的槽數量,這裡我們根據節點個數輸入對應的值:
docker

輸入某個節點的節點 ID 作為目標節點,目標節點只能指定一個:
What is the receiving node ID xxxx
docker

之後輸入源節點的 ID,這裡分別輸入相應的節點 ID 最後用 done 表示結束:
資料遷移之前會列印出所有的槽從源節點到目標節點的計劃,確認計劃無誤後輸入 yes 執行遷移工作
redis-trib 工具會列印出每個槽遷移的進度:

docker

檢視節點的資訊cluster-node
docker

輸入 redis-trib.rb rebalance ip:port
docker

### **3.2、主從節點設定**

擴容之初我們把6397、6398節點加入到叢集,節點6397遷移了部分槽和資料作為主節點,但相比其他主節點目前還沒有從節點,因此該節點不具備故障轉移的能力。

這時需要把節點6398作為6397的從節點,從而保證整個叢集的高可用。使用 cluster replicate{masterNodeId}命令為主節點新增對應從節點。(進入6398 的客戶端,進行新增 redis-cli -h 116.255.176.221 -p 6398)

## **~4、收縮叢集~**

收縮叢集意味著縮減規模,需要從現有叢集中安全下線部分節點,下線節點過程如下:
1)首先需要確定下線節點是否有負責的槽,如果是,需要把槽遷移到其他節點,保證節點下線後整個叢集槽節點對映的完整性。

2)當下線節點不再負責槽或者本身是從節點時,就可以通知叢集內其他節點忘記下線節點,當所有的節點忘記該節點後可以正常關閉。

### 4.1、**下線遷移槽**

下線節點需要把自己負責的槽遷移到其他節點,原理與之前節點擴容的遷移槽過程一致,但是過程收縮正好和擴容遷移方向相反,下線節點變為源節點,其他主節點變為目標節點,源節點需要把自身負責的4096個槽均勻地遷移到其他主節點上。

使用 redis-trib.rb reshard 命令完成槽遷移。由於每次執行 reshard 命令只能有一個目標節點,因此需要執行3次 reshard 命令

**刪除節點**:redis-trib.rb del-none 116.255.176.223:6397
**節點槽資料遷移**:redis-trib.rb  rebalance 116.255.176.223:6379

### **4.2、忘記節點**

由於叢集內的節點不停地通過 Gossip 訊息彼此交換節點狀態,因此需要通過一種健壯的機制讓叢集內所有節點忘記下線的節點。也就是說讓其他節點不再與要下線節點進行 Gossip 訊息交換。Redis 提供了 cluster forget{downNodeId}命令實現該功能,會把這個節點資訊放入黑名單,但是60s之後會恢復。

生產環境當中使用redis-trib.rb del-node {host:port}{downNodeId}命令進行相關操作
注意: redis-trib  rebalance 命令選擇

        適用於節點的槽不平衡的狀態,有槽的節點
1、預設節點加入,要先做節點槽的遷移
2、節點已經遷移了所有的槽資訊,並且已經從叢集刪除後,才可以使用平衡

檢視當前節點的槽位:cluster slots

Docker Machine 是 Docker 官方編排專案之一,負責在多種平臺上快速安裝 Docker 環境。

Docker Machine 是一個工具,它允許你在虛擬宿主機上安裝 Docker Engine ,並使用 docker-machine 命令管理這些宿主機。你可以使用 Machine 在你本地的 Mac 或 Windows box、公司網路、資料中心、或像 阿里雲 或 華為雲這樣的雲提供商上建立 Docker 宿主機。

使用 docker-machine 命令,你可以啟動、審查、停止和重新啟動託管的宿主機、升級 Docker 客戶端和守護程式、並配置 Docker 客戶端與你的宿主機通訊。

1、為什麼要使用它?

在沒有Docker Machine之前,你可能會遇到以下問題:
1、你需要登入主機,按照主機及作業系統特有的安裝以及配置步驟安裝Docker,使其能執行Docker容器。
2、你需要研發一套工具管理多個Docker主機並監控其狀態。

Docker Machine的出現解決了以上問題。
      1、Docker Machine簡化了部署的複雜度,無論是在本機的虛擬機器上還是在公有云平臺,只需要一條命令便可搭建好Docker主機
      2、Docker Machine提供了多平臺多Docker主機的集中管理部署
      3、Docker Machine 使應用由本地遷移到雲端變得簡單,只需要修改一下環境變數即可和任意Docker主機通訊部署應用。

Docker的組成:
1、Docker daemon
2、一套與 Docker daemon 互動的 REST API
3、一個命令列客戶端

哨兵監控redis主從,當主redis掛掉,在哨兵選舉主伺服器時,資料無法寫入進去。寫進去資料可能丟失了,客戶端根據錯誤做有限次數的重試。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章