docker 搭建zookper集群,快照虚拟机多机模拟

发布于:2025-08-31 ⋅ 阅读:(13) ⋅ 点赞:(0)

网上有很多zk集群搭建的,发现踩了很多坑,记录一下自己的搭建:

虚拟机配置

在这里插入图片描述
快照克隆:
在这里插入图片描述
在这里插入图片描述
关闭防火墙。

zk集群正式搭建

查询虚拟机ip地址:

ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1

挂载文件地址:

mkdir -p /mydata/zookeeper/data # 数据挂载目录
mkdir -p /mydata/zookeeper/conf # 配置挂载目录
mkdir -p /mydata/zookeeper/logs # 日志挂载目录

关键点,修改配置:

echo 1 > /mydata/zookeeper/conf/myid 

这里是指定zk id,选举需要使用,单机不需要

vim /mydata/zookeeper/conf/zoo.cfg

zoo.cfg创建zk配置,配置如下:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data
dataLogDir=/logs
clientPort=2181
server.1=172.30.123.67:2888:3888
server.2=172.30.123.60:2888:3888
server.3=172.30.115.33:2888:3888

网上很多都是hostname:sudo hostnamectl set-hostname zk2
或者修改/etc/hosts这个文件,通过名称映射ip,这个我也失败了,解析不了。


docker 执行命令:

docker run -d --name zk \
  --network host \
  --restart=unless-stopped \
  -v /mydata/zookeeper/data:/data \
  -v /mydata/zookeeper/logs:/logs \
  -v /mydata/zookeeper/conf:/conf \
  -e ZOO_MY_ID=$(cat /mydata/zookeeper/data/myid) \
  zookeeper:3.5.7

这个有点坑,我试了其他博主的博客,大多都是直接-p去映射,但是我这边尝试一直链接不是,采用 --network host。
–network host 让容器直接使用宿主机网络,端口 2181/2888/3888 不用再 -p 映射。

集群验证:

docker exec zk zkServer.sh status

显示:

ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower

follower就是选举角色,到这里就成功了,其他的虚拟机上还有leader。

简单使用java连接集群

pom.xml:
添加依赖

<dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>5.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>5.9.0</version>
        </dependency>

生产客户端bean:

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ZookeeperConfig {

    @Value("${curator.connectString}")
    private String connectString;

    @Value("${curator.sessionTimeoutMs}")
    private int sessionTimeoutMs;

    @Value("${curator.connectionTimeoutMs}")
    private int connectionTimeoutMs;

    @Value("${curator.retryCount}")
    private int retryCount;

    @Value("${curator.elapsedTimeMs}")
    private int elapsedTimeMs;

    @Bean(initMethod = "start", destroyMethod = "close")
    public CuratorFramework curatorFramework() {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(elapsedTimeMs, retryCount);
        return CuratorFrameworkFactory.builder()
                .connectString(connectString)
                .sessionTimeoutMs(sessionTimeoutMs)
                .connectionTimeoutMs(connectionTimeoutMs)
                .retryPolicy(retryPolicy)
                .build();
    }
}

注册服务bean:

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.nodes.PersistentNode;
import org.apache.zookeeper.CreateMode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class ZookeeperServiceRegistry {

    @Autowired
    private CuratorFramework curatorFramework;

    /**
     * 注册服务节点
     * @param serviceName 服务名
     * @param serviceAddress 实例地址,如 127.0.0.1:8080
     * @return PersistentNode 句柄,方便后续关闭
     */
    public PersistentNode registerService(String serviceName, String serviceAddress) throws Exception {
        // 1. 先保证父节点存在
        String parent = "/services/" + serviceName;
        Stat stat = curatorFramework.checkExists().forPath(parent);
        if (stat == null) {
            curatorFramework.create()
                    .creatingParentContainersIfNeeded()
                    .withMode(CreateMode.PERSISTENT)
                    .forPath(parent);
        }

        // 2. 子节点路径,使用 EPHEMERAL 即可,不需要 SEQUENTIAL
        String childPath = parent + "/" + serviceAddress;

        // 3. 创建临时节点
        PersistentNode node = new PersistentNode(
                curatorFramework,
                CreateMode.EPHEMERAL,   // 会话断后自动删除
                false,                  // false 表示不监听子节点
                childPath,
                serviceAddress.getBytes());

        node.start();
        if (!node.waitForInitialCreate(10, TimeUnit.SECONDS)) {
            throw new IllegalStateException("Failed to create node at " + childPath);
        }
        return node;
    }

    /**
     * 注销服务节点(若仍持有 PersistentNode,先关闭再删除)
     */
    public void unregisterService(String serviceName, String serviceAddress) throws Exception {
        String path = "/services/" + serviceName + "/" + serviceAddress;
        // 如果之前保存了 PersistentNode,先关闭
        // node.close();
        curatorFramework.delete().forPath(path);
    }
}

使用spring CommandLineRunner 钩子,主动注册

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class ServiceRegistrationRunner implements CommandLineRunner {

    @Autowired
    private ZookeeperServiceRegistry serviceRegistry;

    @Override
    public void run(String... args) throws Exception {
        serviceRegistry.registerService("my-service", "127.0.0.1:8080");
        System.out.println("Service registered successfully.");
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到