千家信息网

Dubbo应用迁移到Kubernetes

发表于:2025-12-03 作者:千家信息网编辑
千家信息网最后更新 2025年12月03日,Dubbo应用迁移到docker的问题Dubbo是阿里开源的一套服务治理与rpc框架,服务的提供者通过zookeeper把自己的服务发布上去,然后服务调用方通过zk获取服务的ip和端口,dubbo客户
千家信息网最后更新 2025年12月03日Dubbo应用迁移到Kubernetes

Dubbo应用迁移到docker的问题

Dubbo是阿里开源的一套服务治理与rpc框架,服务的提供者通过zookeeper把自己的服务发布上去,然后服务调用方通过zk获取服务的ip和端口,dubbo客户端通过自己的软负载功能自动选择服务提供者并调用,整个过程牵涉到的三方关系如下图所示。

在正常的情况下,这三方都在同一个互通的网段,provider提供给zk的就是获取到的本机地址,consumer能访问到这个地址。

但是假如服务放在docker容器中,而调用者并不在docker中,它们的网段是不一样的。

这个时候就出现问题了,consumer无法访问到provider了。

Dubbo提供的解决方案

新版的Dubbo提供了四个配置来指定与注册服务相关的地址和端口。

DUBBO_IP_TO_REGISTRY: 要发布到注册中心上的地址DUBBO_PORT_TO_REGISTRY: 要发布到注册中心上的端口DUBBO_IP_TO_BIND: 要绑定的服务地址(监听的地址)DUBBO_PORT_TO_BIND: 要绑定的服务端口

以IP地址为例,Dubbo先找是不是有DUBBO_IP_TO_BIND这个配置,如果有使用配置的地址,如果没有就取本机地址。然后继续找DUBBO_IP_TO_REGISTRY,如果有了配置,使用配置,否则就使用DUBBO_IP_TO_BIND。具体代码如下:

        /**         * Register & bind IP address for service provider, can be configured separately.         * Configuration priority: environment variables -> java system properties -> host property in config file ->         * /etc/hosts -> default network address -> first available network address         *         * @param protocolConfig         * @param registryURLs         * @param map         * @return         */        private static String findConfigedHosts(ServiceConfig sc,                                                ProtocolConfig protocolConfig,                                                List registryURLs,                                                Map map) {            boolean anyhost = false;            String hostToBind = getValueFromConfig(protocolConfig, DUBBO_IP_TO_BIND);            if (hostToBind != null && hostToBind.length() > 0 && isInvalidLocalHost(hostToBind)) {                throw new IllegalArgumentException("Specified invalid bind ip from property:" + DUBBO_IP_TO_BIND + ", value:" + hostToBind);            }            // if bind ip is not found in environment, keep looking up            if (StringUtils.isEmpty(hostToBind)) {                hostToBind = protocolConfig.getHost();                if (sc.getProvider() != null && StringUtils.isEmpty(hostToBind)) {                    hostToBind = sc.getProvider().getHost();                }                if (isInvalidLocalHost(hostToBind)) {                    anyhost = true;                    try {                        logger.info("No valid ip found from environment, try to find valid host from DNS.");                        hostToBind = InetAddress.getLocalHost().getHostAddress();                    } catch (UnknownHostException e) {                        logger.warn(e.getMessage(), e);                    }                    if (isInvalidLocalHost(hostToBind)) {                        if (CollectionUtils.isNotEmpty(registryURLs)) {                            for (URL registryURL : registryURLs) {                                if (MULTICAST.equalsIgnoreCase(registryURL.getParameter("registry"))) {                                    // skip multicast registry since we cannot connect to it via Socket                                    continue;                                }                                try (Socket socket = new Socket()) {                                    SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());                                    socket.connect(addr, 1000);                                    hostToBind = socket.getLocalAddress().getHostAddress();                                    break;                                } catch (Exception e) {                                    logger.warn(e.getMessage(), e);                                }                            }                        }                        if (isInvalidLocalHost(hostToBind)) {                            hostToBind = getLocalHost();                        }                    }                }            }            map.put(BIND_IP_KEY, hostToBind);            // registry ip is not used for bind ip by default            String hostToRegistry = getValueFromConfig(protocolConfig, DUBBO_IP_TO_REGISTRY);            if (hostToRegistry != null && hostToRegistry.length() > 0 && isInvalidLocalHost(hostToRegistry)) {                throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);            } else if (StringUtils.isEmpty(hostToRegistry)) {                // bind ip is used as registry ip by default                hostToRegistry = hostToBind;            }            map.put(ANYHOST_KEY, String.valueOf(anyhost));            return hostToRegistry;        }

然后我们看这个getValueFromConfig(),它调用了下面的函数,可以看到,它是先找环境变量,再找properties。

    public static String getSystemProperty(String key) {        String value = System.getenv(key);        if (StringUtils.isEmpty(value)) {            value = System.getProperty(key);        }        return value;    }

所以我们通过环境变量,就能修改Dubbo发布到zookeeper上的地址和端口。假如我们通过docker镜像启动了一个dubbo provider,并且它的服务端口是8888,假设主机地址为192.168.1.10,那么我们通过下面的命令,

docker run -e DUBBO_IP_TO_REGISTRY=192.168.1.10 -e DUBBO_PORT_TO_REGISTRY=8888 -p 8888:8888 dubbo_image

就能让内部的服务以192.168.1.10:8888的地址发布。

我们通过官方的实例来演示一下,因为官方提供的案例都很久了,所以我自己重新搞了一个示例,代码在https://github.com/XinliNiu/dubbo-docker-sample.git 。

先启动一个zookeeper,暴露2181端口。

docker run --name zkserver --rm -p 2181:2181  -d zookeeper:3.4.9

看一下zk起来了

niuxinli@niuxinli-B450M-DS3H:~/dubbo-samples-docker$ docker psCONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                         NAMES5efc1f17fba0        zookeeper:3.4.9     "/docker-entrypoint.…"   4 seconds ago       Up 2 seconds        2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp   zkserver

把代码导入IDE,修改dubbo-docker-provide.xml,把地址改成刚发布到zk的地址和端口,我的地址是192.168.1.8。

运行DubboApplication,这时候可以看到在zk上注册了服务。

修改dubbo-docker-consumer.xml里的zk地址,执行单元测试,能正常访问。

把DubboApplication导出成可以执行的jar包,名字叫app.jar,创建如下Dockerfile

FROM openjdk:8-jdk-alpineADD app.jar app.jarENV JAVA_OPTS=""ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar

创建dubbo-demo镜像,在同样的目录里执行docker build。

docker build --no-cache -t dubbo-demo .

正常启动镜像

docker run  -p 20880:20880  -it --rm dubbo-demo

发现是172.16.0.3的地址,这个是访问不了的。

传入环境变量重新启动,

docker run  -e DUBBO_IP_TO_REGISTRY=192.168.1.8 -e DUBBO_PORT_TO_REGISTRY=20880 -p 20880:20880  -it --rm dubbo-demo

这时候就变成主机地址了。

在Kubernetes中使用Dubbo

当在Kubernetes中启动多个副本的时候,指定具体的IP和具体的端口,都是不可行的,因为每个机器的IP都不一样,不能写很多个yaml文件,而且一旦指定了具体端口,那这台主机的这个端口就被占用了。

我们可以通过创建Service,使用NodePort的方式,把端口固定住,这样端口的问题就解决了。因为是对外服务,所以使用ClusterIP肯定是不行了,IP有两种解决办法:

(1)使用Kubernetes的downward api动态的传入主机的ip。

(2)传固定的loadbalancer的地址,例如在所有的node之外有一个F5。

不管哪种方法,都是一种妥协的办法,很不"云原生",我演示一下使用downward api动态传入主机地址,并使用nodeport固定端口的方式。

我的kubernetes集群如下:

角色地址
master192.168.174.50
node1192.168.174.51
node2192.168.174.52
node3192.168.174.53

zk的地址是192.168.1.8,它与集群的主机互通。

我没有建private镜像仓库,把我之前打好的dubbo-demo直接push到docker-hub上了,名字是nxlhero/dubbo-demo。

创建Service,使用的NodePort为30001,创建4个副本,这样3台机器上正好有一台起两个pod。

apiVersion: v1kind: Servicemetadata:  name: dubbo-docker  labels:    run: dubbospec:  type: NodePort  ports:  - port: 20880    targetPort: 20880    nodePort: 30001  selector:    run: dubbo-docker---apiVersion: apps/v1kind: Deploymentmetadata:  name: dubbo-dockerspec:  selector:    matchLabels:      run: dubbo  replicas: 4  template:    metadata:      labels:        run: dubbo    spec:      containers:      - name: dubbo-docker        image: nxlhero/dubbo-demo        env:        - name: DUBBO_IP_TO_REGISTRY          valueFrom:            fieldRef:              fieldPath: status.hostIP        - name: DUBBO_PORT_TO_REGISTRY          value: "30001"        tty: true        ports:        - containerPort: 20880

这个yaml最关键的地方就是环境变量,主机IP通过downward apid传入,端口使用固定的nodeport。

        env:        - name: DUBBO_IP_TO_REGISTRY          valueFrom:            fieldRef:              fieldPath: status.hostIP        - name: DUBBO_PORT_TO_REGISTRY          value: "30001"

创建Service,启动后可以看到zookeeper上的地址都是主机的地址和nodeport。

地址 端口 服务 主机 配置 变量 环境 镜像 代码 问题 三方 副本 办法 动态 名字 官方 就是 提供者 方式 时候 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 数据库镜像同步 软件开发公司起什么名字好听 武汉企赋网络技术有限公司 安卓怎么清理软件登录数据库 附近软件开发交流群 神通数据库创建用户和模式 挽竹服务器的皮肤怎么弄 虹口区本地网络技术厂家价格 贵阳定位软件开发 合肥参考网络技术咨询优势 用户对数据库进行操作的界面 腾讯模拟器老是连接服务器失败 从服务器或工作站到接口的连接 体脂秤软件开发 萧山科技城有互联网公司吗 网络技术与社会发展的关系 莱芜企业erp软件开发 组织国家网络安全宣传周 网络安全教育班会笔记 网络技术的优点和 缺点 数据库死锁怎么 神通数据库创建用户和模式 怎么查看服务器本地编码 chns的数据库 数据库创建添加存储过程 数据库系统一般有哪些成员构成 网络安全汇总模版 一个服务器多个用户是什么模式 幻塔安卓星岛服务器怎么下载 5e平台csgo服务器选择
0