【问题标题】:How to setup zookeeper cluster on docker swarm如何在 docker swarm 上设置 zookeeper 集群
【发布时间】:2017-06-23 02:07:38
【问题描述】:

环境:6个服务器docker swarm集群(2个master和4个worker)

要求:我们需要在现有的 docker swarm 上建立一个 zookeeper 集群。

Blocked on :要在集群中设置 zookeeper,我们需要在每个服务器配置中提供所有 zk 服务器,并在 myid 文件中提供唯一 ID。

问题:当我们在 docker swarm 中创建 zookeeper 的副本时,我们如何为每个副本提供唯一的 ID。另外,我们如何使用每个 zookeeper 容器的 ID 更新 zoo.cfg 配置文件。

【问题讨论】:

    标签: docker apache-zookeeper docker-swarm


    【解决方案1】:

    目前这不是一个容易的问题。当每个集群成员都需要唯一的身份和存储卷时,完全可扩展的有状态应用程序集群就很棘手。

    今天,在 Docker Swarm 上,最好的建议是在 compose 文件中将每个集群成员作为单独的服务运行(参见 31z4/zookeeper-docker):

    version: '2'
    services:
        zoo1:
            image: 31z4/zookeeper
            restart: always
            ports:
                - 2181:2181
            environment:
                ZOO_MY_ID: 1
                ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
    
        zoo2:
            image: 31z4/zookeeper
            restart: always
            ports:
                - 2182:2181
            environment:
                ZOO_MY_ID: 2
                ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
    ..
    ..
    

    对于最先进(但仍在不断发展)的解决方案,我建议查看 Kubernetes:

    Statefulsets 的新概念提供了很多希望。我预计 Docker Swarm 会随着时间的推移发展出类似的能力,其中每个容器实例都被分配一个唯一且“粘性”的主机名,可以用作唯一标识符的基础。

    【讨论】:

      【解决方案2】:

      我们创建了一个扩展官方镜像的 docker 镜像,它确实做到了这一点。 entrypoint.sh 已被修改,以便在每个容器启动时,它会自动发现其余的 zookeeper 节点并适当地配置当前节点。

      您可以在docker store 和我们的github 中找到图片。

      注意:目前不处理重新创建容器导致失败等情况。

      编辑(2018 年 6 月 11 日)

      最新的镜像支持重新配置zookeeper集群的情况如下:

      • 扩展 docker 服务(添加更多容器)
      • 缩减 docker 服务(移除容器)
      • 容器被 docker swarm 重新调度导致失败(分配了新的 IP)

      【讨论】:

      • 我在三周前编写了相同的脚本(:/唯一的区别是我使用ip route get ${NODE_IP} | grep -c "dev lo" 进行自己的 IP 检查。对我来说似乎更强大。)我也被困住了-创建。您是否考虑过使用 Zookeeper 3.5 的新重新配置功能?
      • @Caesar,我们正在努力使用新的重新配置 API 应用增强功能。它将在扩展/缩减后自动配置,以防容器出现故障。
      • 我假设你把 the reconfiguration 关掉了?可能想更新你的答案。
      【解决方案3】:

      我一直在尝试以 docker swarm 模式部署 Zookeeper 集群。

      我已经部署了 3 台机器连接到 docker swarm 网络。我的要求是,尝试在每个节点上运行 3 个 Zookeeper 实例,从而形成整体。 浏览过这个帖子,对如何在 docker swarm 中部署 Zookeeper 了解甚少。

      正如@junius 所建议的,我已经创建了 docker compose 文件。 当 docker swarm 忽略它时,我已经删除了约束。参考https://forums.docker.com/t/docker-swarm-constraints-being-ignored/31555

      我的 Zookeeper docker compose 文件如下所示

      version: '3.3'
      
      services:
          zoo1:
              image: zookeeper:3.4.12
              hostname: zoo1
              ports:
                  - target: 2181
                    published: 2181
                    protocol: tcp
                    mode: host
                  - target: 2888
                    published: 2888
                    protocol: tcp
                    mode: host
                  - target: 3888
                    published: 3888
                    protocol: tcp
                    mode: host
              networks:
                  - net
              deploy:
                  restart_policy:
                      condition: on-failure
              environment:
                  ZOO_MY_ID: 1
                  ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
              volumes:
                  - /home/zk/data:/data
                  - /home/zk/datalog:/datalog
                  - /etc/localtime:/etc/localtime:ro
          zoo2:
              image: zookeeper:3.4.12
              hostname: zoo2
              ports:
                  - target: 2181
                    published: 2181
                    protocol: tcp
                    mode: host
                  - target: 2888
                    published: 2888
                    protocol: tcp
                    mode: host
                  - target: 3888
                    published: 3888
                    protocol: tcp
                    mode: host
              networks:
                  - net
              deploy:
                  restart_policy:
                      condition: on-failure
              environment:
                  ZOO_MY_ID: 2
                  ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=0.0.0.0:2888:3888 server.3=zoo3:2888:3888
              volumes:
                  - /home/zk/data:/data
                  - /home/zk/datalog:/datalog
                  - /etc/localtime:/etc/localtime:ro
          zoo3:
              image: zookeeper:3.4.12
              hostname: zoo3
              ports:
                  - target: 2181
                    published: 2181
                    protocol: tcp
                    mode: host
                  - target: 2888
                    published: 2888
                    protocol: tcp
                    mode: host
                  - target: 3888
                    published: 3888
                    protocol: tcp
                    mode: host
              networks:
                  - net
              deploy:
                  restart_policy:
                      condition: on-failure
              environment:
                  ZOO_MY_ID: 3
                  ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=0.0.0.0:2888:3888
              volumes:
                  - /home/zk/data:/data
                  - /home/zk/datalog:/datalog
                  - /etc/localtime:/etc/localtime:ro
      networks:
          net:
      

      使用 docker stack 命令部署。

      docker stack deploy -c zoo3.yml zk 创建网络 zk_net 创建服务 zk_zoo3 创建服务 zk_zoo1 创建服务 zk_zoo2

      Zookeeper 服务运行良好,每个节点中的每个服务都没有任何问题。

      docker 堆栈服务 zk ID 名称模式副本图像端口 rn7t5f3tu0r4 zk_zoo1 复制 1/1 zookeeper:3.4.12 0.0.0.0:2181->2181/tcp, 0.0.0.0:2888->2888/tcp, 0.0.0.0:3888->3888/tcp u51r7bjwwm03 zk_zoo2 复制 1/1 zookeeper:3.4.12 0.0.0.0:2181->2181/tcp, 0.0.0.0:2888->2888/tcp, 0.0.0.0:3888->3888/tcp zlbcocid57xz zk_zoo3 复制 1/1 zookeeper:3.4.12 0.0.0.0:2181->2181/tcp, 0.0.0.0:2888->2888/tcp, 0.0.0.0:3888->3888/tcp

      当我停止并再次启动 zookeeper 堆栈时,我已经复制了这里讨论的这个问题。

      码头工人堆栈rm zk docker stack deploy -c zoo3.yml zk

      这次 Zookeeper 集群没有形成。 docker 实例记录了以下内容

      ZooKeeper JMX enabled by default
      Using config: /conf/zoo.cfg
      2018-11-02 15:24:41,531 [myid:2] - WARN  [WorkerSender[myid=2]:QuorumCnxManager@584] - Cannot open channel to 1 at election address zoo1/10.0.0.4:3888
      java.net.ConnectException: Connection refused (Connection refused)
              at java.net.PlainSocketImpl.socketConnect(Native Method)
              at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
              at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
              at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
              at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
              at java.net.Socket.connect(Socket.java:589)
              at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectOne(QuorumCnxManager.java:558)
              at org.apache.zookeeper.server.quorum.QuorumCnxManager.toSend(QuorumCnxManager.java:534)
              at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.process(FastLeaderElection.java:454)
              at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.run(FastLeaderElection.java:435)
              at java.lang.Thread.run(Thread.java:748)
      2018-11-02 15:24:41,538 [myid:2] - WARN  [WorkerSender[myid=2]:QuorumCnxManager@584] - Cannot open channel to 3 at election address zoo3/10.0.0.2:3888
      java.net.ConnectException: Connection refused (Connection refused)
              at java.net.PlainSocketImpl.socketConnect(Native Method)
              at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
              at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
              at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
              at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
              at java.net.Socket.connect(Socket.java:589)
              at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectOne(QuorumCnxManager.java:558)
              at org.apache.zookeeper.server.quorum.QuorumCnxManager.toSend(QuorumCnxManager.java:534)
              at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.process(FastLeaderElection.java:454)
              at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.run(FastLeaderElection.java:435)
              at java.lang.Thread.run(Thread.java:748)
      2018-11-02 15:38:19,146 [myid:2] - WARN  [QuorumPeer[myid=2]/0.0.0.0:2181:Learner@237] - Unexpected exception, tries=1, connecting to /0.0.0.0:2888
      java.net.ConnectException: Connection refused (Connection refused)
              at java.net.PlainSocketImpl.socketConnect(Native Method)
              at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
              at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:204)
              at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
              at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
              at java.net.Socket.connect(Socket.java:589)
              at org.apache.zookeeper.server.quorum.Learner.connectToLeader(Learner.java:229)
              at org.apache.zookeeper.server.quorum.Follower.followLeader(Follower.java:72)
              at org.apache.zookeeper.server.quorum.QuorumPeer.run(QuorumPeer.java:981)
      2018-11-02 15:38:20,147 [myid:2] - WARN  [QuorumPeer[myid=2]/0.0.0.0:2181:Learner@237] - Unexpected exception, tries=2, connecting to /0.0.0.0:2888
      java.net.ConnectException: Connection refused (Connection refused)
              at java.net.PlainSocketImpl.socketConnect(Native Method)
              at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
              at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:204)
              at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
              at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
              at java.net.Socket.connect(Socket.java:589)
              at org.apache.zookeeper.server.quorum.Learner.connectToLeader(Learner.java:229)
              at org.apache.zookeeper.server.quorum.Follower.followLeader(Follower.java:72)
              at org.apache.zookeeper.server.quorum.QuorumPeer.run(QuorumPeer.java:981)
      

      仔细观察发现,当我第一次部署此堆栈时,具有 id: 2 的 ZooKeeper 实例在节点 1 上运行。这创建了一个值为 2 的 myid 文件。

      cat /home/zk/data/myid 2

      当我停止并再次启动堆栈时,我发现这一次,ID:3 的 ZooKeeper 实例在节点 1 上运行。

      码头工人ps 容器 ID 图像命令创建状态端口名称 566b68c11c8b zookeeper:3.4.12 "/docker-entrypoin..." 6 分钟前 上升 6 分钟 0.0.0.0:2181->2181/tcp, 0.0.0.0:2888->2888/tcp, 0.0.0.0:3888-> 3888/tcp zk_zoo3.1.7m0hq684pkmyrm09zmictc5bm

      但是 myid 文件的值仍然是 2,它是由之前的实例设置的。

      因此日志显示 [myid:2] 并尝试连接到 id 为 1 和 3 的实例并失败。

      进一步调试发现docker-entrypoint.sh文件包含如下代码

      # Write myid only if it doesn't exist
      if [[ ! -f "$ZOO_DATA_DIR/myid" ]]; then
          echo "${ZOO_MY_ID:-1}" > "$ZOO_DATA_DIR/myid"
      fi
      

      这对我来说是个问题。我已经用以下内容编辑了 docker-entrypoint.sh,

      if [[ -f "$ZOO_DATA_DIR/myid" ]]; then
          rm "$ZOO_DATA_DIR/myid"
      fi
      
      echo "${ZOO_MY_ID:-1}" > "$ZOO_DATA_DIR/myid"
      

      并将 docker-entrypoint.sh 挂载到我的撰写文件中。

      通过此修复,我能够多次停止和启动我的堆栈,并且每次我的 zookeeper 集群能够形成集成而不会遇到连接问题。

      我的docker-entrypoint.sh文件如下

      #!/bin/bash
      
      set -e
      
      # Allow the container to be started with `--user`
      if [[ "$1" = 'zkServer.sh' && "$(id -u)" = '0' ]]; then
          chown -R "$ZOO_USER" "$ZOO_DATA_DIR" "$ZOO_DATA_LOG_DIR"
          exec su-exec "$ZOO_USER" "$0" "$@"
      fi
      
      # Generate the config only if it doesn't exist
      if [[ ! -f "$ZOO_CONF_DIR/zoo.cfg" ]]; then
          CONFIG="$ZOO_CONF_DIR/zoo.cfg"
      
          echo "clientPort=$ZOO_PORT" >> "$CONFIG"
          echo "dataDir=$ZOO_DATA_DIR" >> "$CONFIG"
          echo "dataLogDir=$ZOO_DATA_LOG_DIR" >> "$CONFIG"
      
          echo "tickTime=$ZOO_TICK_TIME" >> "$CONFIG"
          echo "initLimit=$ZOO_INIT_LIMIT" >> "$CONFIG"
          echo "syncLimit=$ZOO_SYNC_LIMIT" >> "$CONFIG"
      
          echo "maxClientCnxns=$ZOO_MAX_CLIENT_CNXNS" >> "$CONFIG"
      
          for server in $ZOO_SERVERS; do
              echo "$server" >> "$CONFIG"
          done
      fi
      
      if [[ -f "$ZOO_DATA_DIR/myid" ]]; then
          rm "$ZOO_DATA_DIR/myid"
      fi
      
      echo "${ZOO_MY_ID:-1}" > "$ZOO_DATA_DIR/myid"
      
      exec "$@"
      

      我的docker compose文件如下

      version: '3.3'
      
      services:
          zoo1:
              image: zookeeper:3.4.12
              hostname: zoo1
              ports:
                  - target: 2181
                    published: 2181
                    protocol: tcp
                    mode: host
                  - target: 2888
                    published: 2888
                    protocol: tcp
                    mode: host
                  - target: 3888
                    published: 3888
                    protocol: tcp
                    mode: host
              networks:
                  - net
              deploy:
                  restart_policy:
                      condition: on-failure
              environment:
                  ZOO_MY_ID: 1
                  ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
              volumes:
                  - /home/zk/data:/data
                  - /home/zk/datalog:/datalog
                  - /home/zk/docker-entrypoint.sh:/docker-entrypoint.sh
                  - /etc/localtime:/etc/localtime:ro
          zoo2:
              image: zookeeper:3.4.12
              hostname: zoo2
              ports:
                  - target: 2181
                    published: 2181
                    protocol: tcp
                    mode: host
                  - target: 2888
                    published: 2888
                    protocol: tcp
                    mode: host
                  - target: 3888
                    published: 3888
                    protocol: tcp
                    mode: host
              networks:
                  - net
              deploy:
                  restart_policy:
                      condition: on-failure
              environment:
                  ZOO_MY_ID: 2
                  ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=0.0.0.0:2888:3888 server.3=zoo3:2888:3888
              volumes:
                  - /home/zk/data:/data
                  - /home/zk/datalog:/datalog
                  - /home/zk/docker-entrypoint.sh:/docker-entrypoint.sh
                  - /etc/localtime:/etc/localtime:ro
          zoo3:
              image: zookeeper:3.4.12
              hostname: zoo3
              ports:
                  - target: 2181
                    published: 2181
                    protocol: tcp
                    mode: host
                  - target: 2888
                    published: 2888
                    protocol: tcp
                    mode: host
                  - target: 3888
                    published: 3888
                    protocol: tcp
                    mode: host
              networks:
                  - net
              deploy:
                  restart_policy:
                      condition: on-failure
              environment:
                  ZOO_MY_ID: 3
                  ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=0.0.0.0:2888:3888
              volumes:
                  - /home/zk/data:/data
                  - /home/zk/datalog:/datalog
                  - /home/zk/docker-entrypoint.sh:/docker-entrypoint.sh
                  - /etc/localtime:/etc/localtime:ro
      networks:
          net:
      

      有了这个,我可以使用 swarm 模式在 docker 中启动并运行 zookeeper 实例,而无需在 compose 文件中对任何主机名进行硬编码。如果我的一个节点出现故障,则会在 swarm 上的任何可用节点上启动服务,而不会出现任何问题。

      谢谢

      【讨论】:

      • 所以这其实是一个很不正确的做法。您看到的问题是因为您为所有动物园管理员使用相同的目录,这样做的目的是什么?在这种情况下,您可以使用一个 Zookeeper 实例,该实例在每次崩溃时都会重新启动。正确的解决方法是为每个实例使用不同的目录。这样,zookeeper 实际上就像它应该的那样工作,通过相互通信来保持数据日志的清洁。
      • 抱歉,我没有为所有 Zookeeper 实例使用同一个目录。这是一个 docker swarm yaml。我有 3 个节点的 swarm 集群,当我加载这个 compose 文件时,Zookeeper 的 3 个实例将在 3 个节点上运行,而不是在单个节点上。他们没有共享目录。
      • 但是您不会对它们的运行位置施加任何限制。当您部署它们时,docker swarm 将决定它们应该在哪里运行,因此可能会发生 2 个实例在同一台机器上运行。即使当您重新启动服务时它们都在 3 个不同的节点上运行(幸运),它们也可能在之前运行的不同节点上运行。所以 ID 为 1 的 Zookeeper 在节点 2 上启动,它会遇到 ID 为 2 的 id 文件。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多