Redis Master-Slave-Sentinel for Windows Container
Master-Slave-Sentinel 本機測試
我們先在本機進行 Redis for Windows 的 Master-Slave-Sentinel 架構測試。由於微軟官方已經不再繼續維護 Redis for Windows,目前還有一位 tporadowski fork 後持繼在更新與維護。讓我們謝謝他。
本機 Master
Master 組態參考:https://github.com/tporadowski/redis/blob/develop/redis.conf
redis.docker-master.conf 修改處:
bind 0.0.0.0
protected-mode no
#requirepass "yourpass"
dir "c:\\redis_data"
appendonly yes
預設沒有加上 requirepass
等於是任何人都能連線,如果設定了,其他 Salve 或 Sentinel 也要設置。另外,我們啟用 AOF,將資料備份本實體機上,這樣就算實體機重開機也能讓資料回復。
啟動:
redis-server.exe redis.docker-master.conf
測試:
redis-cli.exe -h localhost -a {yourpass} info
redis-cli.exe -h localhost -a {yourpass} info replication
本機 Slave
Slave 組態參考:https://github.com/tporadowski/redis/blob/develop/redis.conf
redis.docker-slave.conf 修改處:
bind 0.0.0.0
protected-mode no
port 6380
dir "c:\\redis_data"
slaveof 127.0.0.1 6379
#masterauth <master-yourpass>
slaveof
是重點所在,也是晚點要處理的重點,也就是,在容器環境,基本上取得的 IP 位置會是隨機的,所以要一開始就規劃好的 Master 網路的 IP 位置。這樣在 Slave 容器起來之後,才知道要如何找到 Master 連線與同步。
執要與測試:
redis-server.exe redis.docker-slave.conf
redis-cli.exe -h localhost -p 6380 info replication
在 Master 輸入:redis-cli.exe -h localhost -a yourpass set mykey myvalue
在 Slave 輸入:redis-cli.exe -h localhost -p 6380 get mykey
由於 Master-Slave 兩個執行個體都是同一主機,本機測試應該很順利。
本機 Sentinel
Sentinel 組態參考:https://github.com/tporadowski/redis/blob/develop/sentinel.conf
redis.docker-sentinel.conf 修改處:
# Add Setting For Docker
bind 0.0.0.0
protected-mode no
# Sentinel
port 26379
dir "c:\\redis_data"
sentinel monitor master1 127.0.0.1 6379 1
sentinel down-after-milliseconds master1 30000
sentinel failover-timeout master1 180000
sentinel parallel-syncs master1 1
#sentinel auth-pass master1 foobared
sentinel monitor master1 127.0.0.1 6379 1
指要監控那一台 Master,一個 Sentinel 可以監控多個,一個 Master 可以被多個 Sentinel 監控。只要重覆那 4+1 個 sentinel
組態,並指到另一 Master 即可。組態 6379 後面的 1 是指幾個 Sentinel 判定的失敗就是失敗,預設是 2,目前我們只啟動一台,所以改為 1。
執行:
redis-server.exe redis.docker-sentinel.conf --sentinel
Master-Slave-Sentinel 容器建置
有了組態檔之後,我們可以開始來打包所需要 Redis for Windows 容器。
我們採用PowerShell 1909 已經發行了。mcr.microsoft.com/windows/nanoserver:1909
來當基礎映像檔,但 nanoserver:1909 沒有 PowerShell,而 mcr.microsoft.com/powershell 1909 版本一直建置不出來,所以 Dockerfile 部分我們採用下載 Redis 程式再複製的方式,而不是全自動 Redis 容器的建置過程。
網路配置
前面提到,我們在本機或 VM 環境,要取得 IP 位置相對簡單,但容器在啟動時,本身是動態配置 IP 區段與位置的。而且如果你有注意的話,在 Slave 與 Sentinel 的組態裡,都是用 127.0.0.1
去連接 Master,這有二個問題:一、IP 位置寫死。二、這樣的組態在容器是不會有效的。
Slave 本身如果是用 Docker Compose 來啟動,有找個一個解法。查詢 redis-server.exe --help
有提供一個 --slaveof
參數,利用 links:
之後能利用名稱來指定,算是解了一半。
redis-server.exe --slaveof redis-master 6379
另一種方式,就是先設計好網路架構,並且在啟動時指定好 Master 的 IP 位置,如此一來,就不怕不知道 Master 的 IP 位置,如何對 Slave 與 Sentinel 進行組態而傷腦筋。
docker network create -d nat --subnet=10.10.10.0/24 --gateway 10.10.10.1 --ipam-driver windows --opt com.docker.network.windowsshim.networkname=redis-net redis-net
Build Redis Images
Dockerfile-Master
# escape=`
FROM mcr.microsoft.com/windows/nanoserver:1909
LABEL maintainer="kkbruce"
RUN mkdir c:\redis_data
COPY Redis /Redis
ADD redis.docker-master-6379.conf /Redis
WORKDIR /Redis
EXPOSE 6379
CMD ["redis-server.exe", "redis.docker-master-6379.conf"]
Dockerfile-Slave
# escape=`
FROM mcr.microsoft.com/windows/nanoserver:1909
LABEL maintainer="kkbruce"
RUN mkdir c:\redis_data
COPY Redis /Redis
ADD redis.docker-slave-6380.conf /Redis
WORKDIR /Redis
EXPOSE 6380
CMD ["redis-server.exe", "redis.docker-slave-6380.conf"]
Dockerfile-Sentinel
# escape=`
FROM mcr.microsoft.com/windows/nanoserver:1909
LABEL maintainer="kkbruce"
RUN mkdir c:\redis_data
COPY Redis /Redis
ADD redis.docker-sentinel-6379.conf /Redis
WORKDIR /Redis
EXPOSE 26379
CMD ["redis-server.exe", "redis.docker-sentinel-6379.conf", "--sentinel"]
Build Image
docker build -t kkbruce/redis-master:4.0.14.2-aof-nanoserver-1909 -f .\Dockerfile-Master .
docker build -t kkbruce/redis-slave:4.0.14.2-aof-nanoserver-1909 -f .\Dockerfile-Slave .
docker build -t kkbruce/redis-sentinel:4.0.14.2-aof-nanoserver-1909 -f .\Dockerfile-Sentinel .
Run Test:
docker run -d -p 6379:6379 --name redis-master --rm -v C:\redis_data\:c:\redis_data --network redisnet --ip=10.10.10.10 kkbruce/redis:master-4.0.14.2-aof-nanoserver-1909
docker run -d -p 6380:6380 --name redis-slave --rm -v C:\redis_data\:c:\redis_data kkbruce/redis:slave-4.0.14.2-aof-nanoserver-1909
docker run -d -p 26379:26379 --name redis-sentinel --rm -v C:\redis_data\:c:\redis_data kkbruce/redis:sentinel-4.0.14.2-aof-nanoserver-1909
Docker Compose
將上面最後的測試指定翻成 Docker Compose 的 YAML 組態:
version: '3.7'
services:
master:
image: "kkbruce/redis:master-4.0.14.2-aof-nanoserver-1909"
ports:
- 6379:6379
hostname: redis-master
volumes:
- redis-data:c:\redis_data
networks:
redis-net:
ipv4_address: 10.10.10.10
restart: unless-stopped
slave:
image: "kkbruce/redis:slave-4.0.14.2-aof-nanoserver-1909"
ports:
- 6380:6380
hostname: redis-slave
volumes:
- redis-data:c:\redis_data
networks:
- redis-net
restart: unless-stopped
depends_on:
- master
sentinel:
image: "kkbruce/redis:sentinel-4.0.14.2-aof-nanoserver-1909"
ports:
- 26379:26379
hostname: redis-sentinel
volumes:
- redis-data:c:\redis_data
networks:
- redis-net
restart: unless-stopped
depends_on:
- master
- slave
networks:
redis-net:
external: true
name: redis-net
volumes:
redis-data:
這樣就完了我們第一個 Redis for Windows 的 Master-Slave-Sentinel 架構的容器。
以上架構有個缺點,就是 Master-Slave-Sentinel 都只能有一個實體。因為我們繫結了主機的 Port Mapping。您可以試著把 slave 的 Port 組態註解後,進行
scale
來測試。
docker-compose.exe up -d
docker-compose.exe stop master
docker-compose.exe start master
docker-compose.exe stop slave
docker-compose.exe start slave
C# with Redis Sentinel
架構好 Redis Master-Slave-Sentinel 之後,我們希望程式可以在主機出錯時自動切換連線主機。在進入程式前另外注意,我在測試時從 Logs 裡發現:
sentinel_1 | [1160] 10 Feb 17:41:06.752 # Next failover delay: I will not start a failover before Mon Feb 10 17:47:07 2020
在我不斷 stop
、start
Master、Slave 測試時,Sentinel 本身會有延遲啟動的作業,也就是,短時間大量中斷測試時會突然發現切換作業沒有正常動作了,就是這原因。
sentinel failover-timeout master1 180000
可以調整這個值來延長或縮短。
在程式方面,我們採用最通用的 C# 套件 StackExchange.Redis 來實作業,而且很幸運作的在 Basic Usage 與 Configuration 就很快找到相關設定與組態。
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379");
我們實作一下連接程式:
public static class RedisHaFactory
{
private static readonly Lazy<ConnectionMultiplexer> Connection;
/// <summary>
/// Use EndPoint to connection.
/// </summary>
static RedisHaFactory()
{
ConfigurationOptions options = new ConfigurationOptions
{
EndPoints ={{ "127.0.0.1", 6379 },{ "127.0.0.1", 6380 }},
DefaultDatabase = 10
};
Connection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(options));
}
public static ConnectionMultiplexer GetConnection => Connection.Value;
public static IDatabase RedisDB => GetConnection.GetDatabase();
}
public static class RedisFactory6380
{
private static readonly Lazy<ConnectionMultiplexer> Connection;
/// <summary>
/// Use connectionString to connection.
/// </summary>
static RedisFactory6380()
{
//var connectionString = "127.0.0.1:6379,127.0.0.1:6380,DefaultDatabase=10";
var connectionString = "127.0.0.1:6380,defaultDatabase=10";
var options = ConfigurationOptions.Parse(connectionString);
Connection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(options));
}
public static ConnectionMultiplexer GetConnection => Connection.Value;
public static IDatabase RedisDB => GetConnection.GetDatabase();
}
RedisHaFactory
是用ConfigurationOptions
來設定EndPoints
。RedisFactory6380
是用字串來設定。等一下測試要用的程式碼。
我們在一個 ASP.NET Core Web API 裡去進行測試:
[Route("poc/[action]")]
[ApiController]
public class PocController : ControllerBase
{
/// <summary>
/// Write data to Master.
/// </summary>
/// <returns>string</returns>
public IActionResult Demo1()
{
RedisHaFactory.RedisDB.StringSet("6379", "Write string value to 6379.");
var stringGet = RedisHaFactory.RedisDB.StringGet("6379");
return Ok(stringGet.ToString());
}
/// <summary>
/// Write data to Slave(readonly), It will throw exception.
/// </summary>
/// <returns></returns>
public IActionResult Demo2()
{
RedisFactory6380.RedisDB.StringSet("6380", "Write string value to 6380.");
var stringGet = RedisFactory6380.RedisDB.StringGet("6380");
return Ok(stringGet.ToString());
}
/// <summary>
/// Shitdown Master and write to Slave(sentinel enable).
/// </summary>
/// <returns>string</returns>
public IActionResult Demo3()
{
RedisHaFactory.RedisDB.StringSet("6379down", "Write string value to 6380.");
var stringGet = RedisHaFactory.RedisDB.StringGet("6379down");
return Ok(stringGet.ToString());
}
}
Demo1
正常寫入。Demo2
寫入 Slave 會失敗,因為它是唯讀。進行 Demo3
之前,把 Master 服務停用,讓 Sentinel 去切換,執行後會發現,正常寫入 6380 Port 的主機了,因為現在它被 Sentinel 切換為 Master 角色。
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。