Go遊戲伺服器開發的一些思考(二十五):Redis的Docker Swarm部署
痛點
在redis 4出來之前,redis哨兵、redis叢集 是無法通過簡單的方式支援Docker Swarm的。原因是Docker Swarm的NAT埠對映機制,會導致redis無法正確獲取宿主IP、埠。
在redis 4之後,官方通過在redis.conf 、redis_sentinel.conf中,新增了slave-announce-ip、slave-announce-port、sentinel announce-ip、sentinel announce-port欄位,讓使用者通過配置填寫,來告知redis,其在NAT模式下時的宿主IP、埠。從而為redis支援Docker Swarm鋪平道路。
此外,還有一個事實,redis容器A開在宿主A上,在宿主A上儲存了持久化資料。之後,若宿主A故障後,redis容器A是不可以轉移到其他宿主上去的。隨便的轉移重開服務,會導致持久化資料的丟失。
這與Docker Swarm預設的故障轉移機制是衝突的。
指定具體宿主節點
Docker Swarm的編排指令碼,提供了一種可以根據條件來選擇宿主節點的方法:
- 在宿主機上設定唯一標籤
- 編排指令碼的deploy - placement - constraints 中,設定該服務只能在某一標籤的宿主機上可以開啟
這樣,就可以變相的關閉掉故障轉移的功能
如何設定、應用標籤
下面以ubuntu系統為例:
編輯 /etc/docker/daemon.json,增加lables欄位,類似如下:
{ "labels": [ "host_ip=192.168.1.4" ] }
這裡增加了一個標籤host_ip。標籤的命名、個數等都是根據使用需求自定義的。
這裡需要唯一標識一臺宿主機,因此定義一個host_ip標籤重啟docker,使標籤生效
service docker restart
編排指令碼類似如下:
services: redis1: image: redis deploy: placement: constraints: [engine.labels
以上。redis1的容器就只會被開在ip為192.168.1.4的宿主機上。
redis主從編排指令碼示例
編排指令碼docker-stack-redis.yml,內容如下:
version: "3.3"
services:
redis1:
image: redis
deploy:
placement:
constraints: [engine.labels.host_ip==IP1]
endpoint_mode: vip
replicas: 1
restart_policy:
condition: on-failure
delay: 60s
ports:
- "16379:6379"
networks:
- net
command: redis-server --save "" --maxmemory 5gb --timeout 0 --tcp-keepalive 60 --bind 0.0.0.0 --slave-announce-ip IP1 --slave-announce-port 16379
redis2:
image: redis
deploy:
placement:
constraints: [engine.labels.host_ip==IP2]
endpoint_mode: vip
replicas: 1
restart_policy:
condition: on-failure
delay: 60s
ports:
- "26379:6379"
networks:
- net
command: redis-server --save "" --maxmemory 5gb --timeout 0 --tcp-keepalive 60 --bind 0.0.0.0 --slave-announce-ip IP2 --slave-announce-port 26379 --slaveof redis1 6379
redis3:
image: redis
deploy:
placement:
constraints: [engine.labels.host_ip==IP3]
endpoint_mode: vip
replicas: 1
restart_policy:
condition: on-failure
delay: 60s
ports:
- "36379:6379"
networks:
- net
command: redis-server --save "" --maxmemory 5gb --timeout 0 --tcp-keepalive 60 --bind 0.0.0.0 --slave-announce-ip IP3 --slave-announce-port 36379 --slaveof redis1 6379
networks:
net:
啟動指令碼install-redis.sh,內容如下:
#!/bin/bash
[ "$HOST_IP1" ] || HOST_IP1="192.168.1.4"
[ "$HOST_IP2" ] || HOST_IP2="192.168.1.4"
[ "$HOST_IP3" ] || HOST_IP3="192.168.1.4"
cp -f docker-stack-redis.yml docker-stack-redis.yml.temp
sed -i 's/IP1/'$HOST_IP1'/g' docker-stack-redis.yml.temp
sed -i 's/IP2/'$HOST_IP2'/g' docker-stack-redis.yml.temp
sed -i 's/IP3/'$HOST_IP3'/g' docker-stack-redis.yml.temp
docker stack deploy -c ./docker-stack-redis.yml.temp redis
rm -rf ./docker-stack-redis.yml.temp
redis哨兵編排指令碼示例
編排指令碼docker-stack-redis-sentinel.yml,內容如下:
version: "3"
services:
sentinel1:
image: redis
deploy:
placement:
constraints: [engine.labels.host_ip==IP1]
replicas: 1
restart_policy:
condition: on-failure
ports:
- "46379:26379"
volumes:
- data1:/data
networks:
- net
command: /bin/bash -c "touch /data/redis_sentinel.conf && redis-server /data/redis_sentinel.conf --sentinel monitor NAME IP PORT NUM --sentinel down-after-milliseconds NAME 5000 --sentinel parallel-syncs NAME 1 --sentinel failover-timeout NAME 180000 --sentinel announce-ip IP1 --sentinel announce-port 46379"
sentinel2:
image: redis
deploy:
placement:
constraints: [engine.labels.host_ip==IP2]
replicas: 1
restart_policy:
condition: on-failure
ports:
- "46380:26379"
volumes:
- data2:/data
networks:
- net
command: /bin/bash -c "touch /data/redis_sentinel.conf && redis-server /data/redis_sentinel.conf --sentinel monitor NAME IP PORT NUM --sentinel down-after-milliseconds NAME 5000 --sentinel parallel-syncs NAME 1 --sentinel failover-timeout NAME 180000 --sentinel announce-ip IP2 --sentinel announce-port 46380"
sentinel3:
image: redis
deploy:
placement:
constraints: [engine.labels.host_ip==IP3]
replicas: 1
restart_policy:
condition: on-failure
ports:
- "46381:26379"
volumes:
- data3:/data
networks:
- net
command: /bin/bash -c "touch /data/redis_sentinel.conf && redis-server /data/redis_sentinel.conf --sentinel monitor NAME IP PORT NUM --sentinel down-after-milliseconds NAME 5000 --sentinel parallel-syncs NAME 1 --sentinel failover-timeout NAME 180000 --sentinel announce-ip IP3 --sentinel announce-port 46381"
networks:
net:
volumes:
data1:
data2:
data3:
啟動指令碼install-redis-sentinel.sh,內容如下:
#!/bin/bash
[ "$SENTINEL_NAME" ] || SENTINEL_NAME="mysentinel"
[ "$MASTER_IP" ] || MASTER_IP="192.168.1.4"
[ "$MASTER_PORT" ] || MASTER_PORT="16379"
[ "$QUORUM" ] || QUORUM="2"
[ "$HOST_IP1" ] || HOST_IP1="192.168.1.4"
[ "$HOST_IP2" ] || HOST_IP2="192.168.1.4"
[ "$HOST_IP3" ] || HOST_IP3="192.168.1.4"
cp -f docker-stack-redis-sentinel.yml docker-stack-redis-sentinel.yml.temp
sed -i 's/IP1/'$HOST_IP1'/g' docker-stack-redis-sentinel.yml.temp
sed -i 's/IP2/'$HOST_IP2'/g' docker-stack-redis-sentinel.yml.temp
sed -i 's/IP3/'$HOST_IP3'/g' docker-stack-redis-sentinel.yml.temp
sed -i 's/NAME/'$SENTINEL_NAME'/g' docker-stack-redis-sentinel.yml.temp
sed -i 's/IP/'$MASTER_IP'/g' docker-stack-redis-sentinel.yml.temp
sed -i 's/PORT/'$MASTER_PORT'/g' docker-stack-redis-sentinel.yml.temp
sed -i 's/NUM/'$QUORUM'/g' docker-stack-redis-sentinel.yml.temp
docker stack deploy -c ./docker-stack-redis-sentinel.yml.temp redis-sentinel
rm -rf ./docker-stack-redis-sentinel.yml.temp
redis叢集編排指令碼示例
redis叢集配置啟動方式,只是最後多了一步,敲一個命令使redis(s)組成叢集。
基本屬於重複勞動,等用到時再提供之。
指令碼來源
以上指令碼均可在下面github上找到,且均經過測試,失效、主從切換測試等。
地址如下:
總結
在遊戲運維階段,使用一款視覺化的redis管理工具,自然時最佳選擇。特別是,運維的redis數量眾多的情況。
這裡之所以編寫Redis的Docker Swarm部署,主要有以下幾個原因:
練習Docker Swarm編排指令碼。
redis這種應用可歸為一類別,即練習了一堆這種性質的應用開發時,一時用到,先去部署一款運維工具,顯然太重了。
本文主要驗證了redis是完整可以使用Docker Swarm方式,啟動、並接受Docker Swarm管理的!