1. 程式人生 > >Go遊戲伺服器開發的一些思考(二十五):Redis的Docker Swarm部署

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的編排指令碼,提供了一種可以根據條件來選擇宿主節點的方法:

  1. 在宿主機上設定唯一標籤
  2. 編排指令碼的deploy - placement - constraints 中,設定該服務只能在某一標籤的宿主機上可以開啟

這樣,就可以變相的關閉掉故障轉移的功能

如何設定、應用標籤

下面以ubuntu系統為例:

  1. 編輯 /etc/docker/daemon.json,增加lables欄位,類似如下:

    {
      "labels": [ "host_ip=192.168.1.4" ]
    }

    這裡增加了一個標籤host_ip。標籤的命名、個數等都是根據使用需求自定義的。
    這裡需要唯一標識一臺宿主機,因此定義一個host_ip標籤

  2. 重啟docker,使標籤生效

    service docker restart
  3. 編排指令碼類似如下:

    services:
    redis1:
      image: redis
      deploy:
        placement:
          constraints: [engine.labels
    .host_ip==192.168.1.4]

以上。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管理的!