1. 程式人生 > 程式設計 >基於Docker搭建Redis一主兩從三哨兵

基於Docker搭建Redis一主兩從三哨兵

這段時間正在學習Redis和容器相關的內容,因此想通過docker搭建一套redis主從系統來加深理解。看這篇文章可能你需要一定的docker基礎,以及對redis主從和哨兵機制有所瞭解。

這次實驗準備了三臺雲主機,系統為Debian,ip分別為:35.236.172.131 ,35.201.200.251,34.80.172.42。

首先分別在這三臺主機上安裝docker,然後每臺主機上啟動一個redis容器,執行redis-server服務,其中35.236.172.131作為master,另外兩臺機器作為slave,最後在三臺主機上再分別啟動一個redis容器,執行redis-sentinel。為什麼還是redis容器呢?因為sentinel實際上還是一個redis-server,只不過是以sentinel模式執行,只能處理sentinel需要的一些命令。

安裝docker

docker的安裝有很多種方法,這裡就不介紹了。本次使用指令碼安裝docker,Debian系統指令碼安裝如下,其他系統可以參考Docker官網的安裝方法:docs.docker.com/install/lin…。不過下面的命令在官網命令的基礎上修改映象源為阿里雲,因為國內映象往往會快一些。

指令碼安裝docker

在物理主機或者雲虛擬主機上執行下面的命令就可以完成docker安裝了,當然我是在Debian系統上,其他系統相應參考官網上的方法。

$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
複製程式碼

啟動docker CE

docker是以客戶端和伺服器模型執行的,因此需要先執行docker的伺服器,伺服器以daemon的形式執行。docker CE是docker的社群版本。

$ sudo systemctl enable docker
$ sudo systemctl start docker
複製程式碼

驗證docker是否安裝成功

下面的這條命令是從docker的官方倉庫拉取一個名為hello-world的映象,並通過這個映象啟動一個容器。

$ docker run hello-world
複製程式碼

如果執行結果如下,出現了Hello from Docker!,說明docker安裝成功了

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete 
Digest: sha256:6540fc08ee6e6b7b63468dc3317e3303aae178cb8a45ed3123180328bcc1d20f
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message,Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client,which sent it
    to your terminal.

To try something more ambitious,you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images,automate workflows,and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas,visit:
 https://docs.docker.com/get-started/
複製程式碼

啟動容器搭建主從

docker安裝成功後,可以開始部署redis服務了。先從docker官方公共倉庫拉取redis映象,然後修改redis服務的配置檔案,最後啟動容器,啟動redis伺服器。在多臺機器上執行redis伺服器,並建立主從關係。

redis的主從是實現redis叢集和redis哨兵高可用的基礎,redis的主從結構使從可以複製主上的資料,如果從與主之間網路斷開,從會自動重連到主上。

獲取Redis映象

下面的命令會拉取最新的官方版本的redis映象

$ docker pull redis
複製程式碼

檢視映象

$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
redis               latest              bb0ab8a99fe6        7 days ago          95MB
hello-world         latest              fce289e99eb9        6 months ago        1.84kB
複製程式碼

獲取並修改redis配置檔案

redis官方提供了一個配置檔案樣例,通過wget工具下載下來。我用的root使用者,就直接下載到/root目錄裡了。

$ wget http://download.redis.io/redis-stable/redis.conf
複製程式碼

開啟下載下來的檔案後,可以看到配置有很多。我只是搭建服務進行試驗所以只修改必要的幾項。如果要運用到線上,那必須所有的配置都按需求進行修改。

其中redis伺服器的master和slave角色使用的配置檔案還會有些不同,下面分別進行說明。

對於master而言,配置檔案修改以下幾項

# 註釋這一行,表示Redis可以接受任意ip的連線
# bind 127.0.0.1 

# 關閉保護模式
protected-mode no 

# 讓redis服務後臺執行
daemonize yes 

# 設定密碼(可選,如果這裡開啟了密碼要求,slave的配置裡就要加這個密碼. 只是練習配置,就不使用密碼認證了)
# requirepass masterpassword 

# 配置日誌路徑,為了便於排查問題,指定redis的日誌檔案目錄
logfile "/var/log/redis/redis.log"
複製程式碼

對於slave而言,配置檔案修改以下幾項:

# 註釋這一行,表示Redis可以接受任意ip的連線
# bind 127.0.0.1 

# 關閉保護模式
protected-mode no 

# 讓redis服務後臺執行
daemonize yes 

# 設定密碼(可選,如果這裡開啟了密碼要求,slave的配置裡就要加這個密碼)
requirepass masterpassword 

# 設定主庫的密碼,用於認證,如果主庫開啟了requirepass選項這裡就必須填相應的密碼
masterauth <master-password>

# 設定master的IP和埠號,redis配置檔案中的預設埠號是6379
# 低版本的redis這裡會是slaveof,意思是一樣的,因為slave是比較敏感的詞彙,所以在redis後面的版本中不在使用slave的概念,取而代之的是replica
# 將35.236.172.131做為主,其餘兩臺機器做從。ip和埠號按照機器和配置做相應修改。
replicaof 35.236.172.131 6379

# 配置日誌路徑,為了便於排查問題,指定redis的日誌檔案目錄
logfile "/var/log/redis/redis.log"
複製程式碼

啟動容器

分別在主機和從機上按照上面的方法建立好配置檔案,檢查無誤後就可以開始啟動容器了。

我們在三臺機器上分別將容器別名指定為redis-1,redis-2,redis-3,這樣便於區分與說明,docker通過--name引數來指定容器的別名。redis-1是master上容器的別名,redis-2和redis-3是兩個slave上的別名。

下面以執行redis-3容器為例說明容器的啟動過程。另外兩臺機器上的容器redis-1和redis-2操作是相同的,只是要注意master的配置檔案和slave不同。不過首先要啟動主伺服器,也就是redis-1容器。然後再啟動redis-2和redis-3。

# 首先以後臺模式執行容器
$ docker run -it --name redis-3 -v /root/redis.conf:/usr/local/etc/redis/redis.conf -d -p 6379:6379 redis /bin/bash
# 容器成功啟動後,會列印一個長串的容器ID
a3952342094dfd5a56838cb6becb5faa7a34f1dbafb7e8c506e9bd7bb1c2951b
# 通過ps命令檢視容器的狀態,可以看到redis-3已經啟動
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
a3952342094d        redis               "docker-entrypoint.s…"   8 minutes ago       Up 8 minutes        0.0.0.0:6379->6379/tcp   redis-3
複製程式碼

上面已經啟動了容器,接下來進入容器裡啟動redis伺服器。

# 以互動模式進入容器redis-3
$ docker exec -it redis-3 bash

# 建立日誌檔案目錄
$ mkdir /var/log/redis/
$ touch /var/log/redis/redis.log

# 啟動redis伺服器,如果沒有任何輸出,就說明成功了
$ redis-server /usr/local/etc/redis/redis.conf

# 在容器裡啟動一個redis客戶端
$ redis-cli 

# 執行info命令,檢視伺服器狀態
127.0.0.1:6379> info
...
# 如果是主,這裡的role的值會是master,如果是從,這裡的role的值會是slave
role:slave
# 對於slave,還要檢視master_link_status這個屬性值。slave上這個屬性值為up就說明主從複製是OK的,否者就有問題。如果從機狀態不為up,首先排查主機的埠是否被限,然後檢視redis日誌排查原因
master_link_status:up
...

# 最後退出容器
$ exit
複製程式碼

驗證主從複製

主從搭建成功後,可以通過在master上寫入一個key-value值,檢視是否會同步到slave上,來驗證主從同步是否能成功。

# 以互動模式進入容器redis-1中
$ docker exec -it redis-1 bash
複製程式碼

執行一個redis-cli,向test_key寫入一個值

$ redis-cli
127.0.0.1:6379> set test_key hello-world
OK
複製程式碼

在任意slave機器上進入容器,也執行一個redis-cli,查詢這個key的值。如果能查詢到這個值,且與主機上的值相同,說明主從同步成功。經測試,主動同步成功。

127.0.0.1:6379> get test_key 
"hello-world"
複製程式碼

新增哨兵

主從結構搭建成功了,系統的可用性變高了,但是如果主發生故障,需要人工手動切換從機為主機。這種切換工作不僅浪費人力資源,更大的影響是主從切換期間這段時間redis是無法對外提供服務的。因此,哨兵系統被開發出來了,哨兵可以在主發生故障後,自動進行故障轉移,從從機裡選出一臺升級為主機,並持續監聽著原來的主機,當原來的主機恢復後,會將其作為新主的從機。

哨兵先監聽主,通過對主傳送info命令,獲取到從的資訊,然後也會監聽到從。另外哨兵都會像主訂閱__sentinel__:hello頻道,當有新的哨兵加入時,會向這個頻道傳送一條資訊,這條資訊包含了該哨兵的IP和埠等資訊,那麼其他已經訂閱了該頻道的哨兵就會收到這條資訊,就知道有一個新的哨兵加入。這些哨兵會與新加入和哨兵建立連線,選主是需要通過這個連線來進行投票。這個關係可以用下面這個圖來描述

獲取並修改sentinel配置檔案

通過wget命令獲取sentinel的配置檔案

wget http://download.redis.io/redis-stable/sentinel.conf
複製程式碼

修改配置檔案以下幾項

# 讓sentinel服務後臺執行
daemonize yes 

# 修改日誌檔案的路徑
logfile "/var/log/redis/sentinel.log"

# 修改監控的主redis伺服器
# 最後一個2表示,兩臺機器判定主被動下線後,就進行failover(故障轉移)
sentinel monitor mymaster 35.236.172.131 6379 2
複製程式碼

啟動容器

與啟動redis容器類似,啟動一個別名為sentinel的容器

$ docker run -it --name sentinel -p 26379:26379 -v /root/sentinel.conf:/usr/local/etc/redis/sentinel.conf -d redis /bin/bash
複製程式碼

執行哨兵

# 進入容器
$ docker exec -it sentinel bash

# 建立日誌目錄和檔案
$ mkdir /var/log/redis
$ touch /var/log/redis/sentinel.log

# 啟動哨兵
redis-sentinel /usr/local/etc/redis/sentinel.conf 

# 檢視日誌,哨兵成功監聽到一主和兩從的機器
18:X 11 Jul 2019 13:25:55.416 # +monitor master mymaster 35.236.172.131 6379 quorum 2
18:X 11 Jul 2019 13:25:55.418 * +slave slave 35.201.200.251:6379 35.201.200.251 6379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 13:25:55.421 * +slave slave 34.80.172.42:6379 34.80.172.42 6379 @ mymaster 35.236.172.131 6379
複製程式碼

在另外兩臺機器上按照同樣的方法在一個容器中執行sentinel,sentinel都使用相同的配置檔案。

驗證failover(故障轉移)

為了驗證哨兵機制下的自動主從切換,我們將主上的redis程式kill掉。

稍等幾秒鐘後,就有另外一臺從升級為主機,實驗時是第三臺機器,也就是redis-3升級為了主,用info命令查詢可以看到redis-3伺服器的角色變成的master。說明自動主從切換成功。

127.0.0.1:6379> info
...
# Replication
role:master
...
複製程式碼

然後重新啟動之前被kill掉的master伺服器,啟動後用info命令檢視,可以發現其變成了redis-3的從伺服器。

下面這段日誌,描述了35.236.172.131作為主啟動,執行故障轉移的master sentinel選舉,執行故障轉移,建立新的主從關係。

root@4355ca3260c5:/var/log/redis# cat sentinel.log 
17:X 11 Jul 2019 13:25:55.395 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
17:X 11 Jul 2019 13:25:55.395 # Redis version=5.0.5,bits=64,commit=00000000,modified=0,pid=17,just started
17:X 11 Jul 2019 13:25:55.395 # Configuration loaded
18:X 11 Jul 2019 13:25:55.398 * Running mode=sentinel,port=26379.
18:X 11 Jul 2019 13:25:55.398 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
18:X 11 Jul 2019 13:25:55.416 # Sentinel ID is 7d9a7877d4cffb6fec5877f605b975e00e7953c1
18:X 11 Jul 2019 13:25:55.416 # +monitor master mymaster 35.236.172.131 6379 quorum 2
18:X 11 Jul 2019 13:25:55.418 * +slave slave 35.201.200.251:6379 35.201.200.251 6379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 13:25:55.421 * +slave slave 34.80.172.42:6379 34.80.172.42 6379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 13:26:25.460 # +sdown slave 35.201.200.251:6379 35.201.200.251 6379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:04:23.390 * +sentinel sentinel 09aa7d2098ad2dc52e6e07d7bc6670f00f5ff3e3 172.17.0.3 26379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:04:25.418 * +sentinel-invalid-addr sentinel 09aa7d2098ad2dc52e6e07d7bc6670f00f5ff3e3 172.17.0.3 26379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:04:25.418 * +sentinel sentinel 7d9a7877d4cffb6fec5877f605b975e00e7953c1 172.17.0.3 26379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:04:25.456 * +sentinel-address-switch master mymaster 35.236.172.131 6379 ip 172.17.0.3 port 26379 for 09aa7d2098ad2dc52e6e07d7bc6670f00f5ff3e3
18:X 11 Jul 2019 14:08:34.338 * +sentinel-invalid-addr sentinel 09aa7d2098ad2dc52e6e07d7bc6670f00f5ff3e3 172.17.0.3 26379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:08:34.338 * +sentinel sentinel 28d3c0e636fa29ac9fb5c3cc2be00432c1b0ead9 172.17.0.3 26379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:08:36.236 * +sentinel-address-switch master mymaster 35.236.172.131 6379 ip 172.17.0.3 port 26379 for 09aa7d2098ad2dc52e6e07d7bc6670f00f5ff3e3
18:X 11 Jul 2019 14:11:12.151 # +sdown master mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:11:12.214 # +odown master mymaster 35.236.172.131 6379 #quorum 4/2
18:X 11 Jul 2019 14:11:12.214 # +new-epoch 1
18:X 11 Jul 2019 14:11:12.214 # +try-failover master mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:11:12.235 # +vote-for-leader 7d9a7877d4cffb6fec5877f605b975e00e7953c1 1
18:X 11 Jul 2019 14:11:12.235 # 7d9a7877d4cffb6fec5877f605b975e00e7953c1 voted for 7d9a7877d4cffb6fec5877f605b975e00e7953c1 1
18:X 11 Jul 2019 14:11:12.235 # 28d3c0e636fa29ac9fb5c3cc2be00432c1b0ead9 voted for 7d9a7877d4cffb6fec5877f605b975e00e7953c1 1
18:X 11 Jul 2019 14:11:12.235 # 09aa7d2098ad2dc52e6e07d7bc6670f00f5ff3e3 voted for 7d9a7877d4cffb6fec5877f605b975e00e7953c1 1
18:X 11 Jul 2019 14:11:12.294 # +elected-leader master mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:11:12.294 # +failover-state-select-slave master mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:11:12.394 # -failover-abort-no-good-slave master mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:11:12.453 # Next failover delay: I will not start a failover before Thu Jul 11 14:17:12 2019
18:X 11 Jul 2019 14:11:13.050 # +config-update-from sentinel 28d3c0e636fa29ac9fb5c3cc2be00432c1b0ead9 172.17.0.3 26379 @ mymaster 35.236.172.131 6379
18:X 11 Jul 2019 14:11:13.050 # +switch-master mymaster 35.236.172.131 6379 34.80.172.42 6379
18:X 11 Jul 2019 14:11:13.050 * +slave slave 35.201.200.251:6379 35.201.200.251 6379 @ mymaster 34.80.172.42 6379
18:X 11 Jul 2019 14:11:13.050 * +slave slave 35.236.172.131:6379 35.236.172.131 6379 @ mymaster 34.80.172.42 6379
18:X 11 Jul 2019 14:11:43.077 # +sdown slave 35.236.172.131:6379 35.236.172.131 6379 @ mymaster 34.80.172.42 6379
18:X 11 Jul 2019 14:11:43.077 # +sdown slave 35.201.200.251:6379 35.201.200.251 6379 @ mymaster 34.80.172.42 6379
18:X 12 Jul 2019 01:54:05.142 # -sdown slave 35.236.172.131:6379 35.236.172.131 6379 @ mymaster 34.80.172.42 6379
18:X 12 Jul 2019 01:54:15.087 * +convert-to-slave slave 35.236.172.131:6379 35.236.172.131 6379 @ mymaster 34.80.172.42 6379
複製程式碼

總結

redis通過主從複製來實現高可用,但是發生故障時需要人工進行主從切換,效率低下。哨兵機制實現了redis主從的自動切換,提高了redis叢集的可用性,提高了redis叢集的故障轉移效率。