You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2022/07/15 07:32:30 UTC

[dubbo] branch 3.1 updated: [3.1] Add new modules kubernetes and xds (#10325)

This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.1
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.1 by this push:
     new 17a0aa2730 [3.1] Add new modules kubernetes and xds (#10325)
17a0aa2730 is described below

commit 17a0aa2730e9a2f3d92963921d43603e21bec3bd
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Jul 15 15:32:19 2022 +0800

    [3.1] Add new modules kubernetes and xds (#10325)
    
    * add modules, kubernetes and xds
    
    * xds components configuration
---
 dubbo-kubernetes/pom.xml                           |  81 +++++
 .../kubernetes/KubernetesMeshEnvListener.java      | 197 ++++++++++
 .../KubernetesMeshEnvListenerFactory.java          |  28 +-
 .../registry/kubernetes/KubernetesRegistry.java    |  12 +-
 .../kubernetes/KubernetesRegistryFactory.java      |   6 +-
 .../kubernetes/KubernetesServiceDiscovery.java     | 404 +++++++++++++++++++++
 .../KubernetesServiceDiscoveryFactory.java         |  18 +-
 .../dubbo/registry/kubernetes/MeshConstant.java    |  43 +++
 .../kubernetes/NopKubernetesMeshEnvListener.java   |  22 +-
 .../kubernetes/util/KubernetesClientConst.java     |  78 ++++
 .../kubernetes/util/KubernetesConfigUtils.java     | 118 ++++++
 .../org.apache.dubbo.registry.RegistryFactory      |   1 +
 ...g.apache.dubbo.registry.client.ServiceDiscovery |   1 +
 ...e.dubbo.registry.client.ServiceDiscoveryFactory |   1 +
 ...luster.router.mesh.route.MeshEnvListenerFactory |   1 +
 .../kubernetes/KubernetesServiceDiscoveryTest.java | 198 ++++++++++
 .../org.mockito.plugins.MockMaker                  |   1 +
 dubbo-registry/pom.xml                             |   1 -
 .../dubbo-registry-xds => dubbo-xds}/pom.xml       |  12 +-
 .../dubbo/registry/xds/XdsCertificateSigner.java   |   0
 .../java/org/apache/dubbo/registry/xds/XdsEnv.java |   0
 .../org/apache/dubbo/registry/xds/XdsRegistry.java |   0
 .../dubbo/registry/xds/XdsRegistryFactory.java     |   0
 .../dubbo/registry/xds/XdsServiceDiscovery.java    |   0
 .../registry/xds/XdsServiceDiscoveryFactory.java   |   0
 .../xds/istio/IstioCitadelCertificateSigner.java   |   0
 .../dubbo/registry/xds/istio/IstioConstant.java    |   0
 .../apache/dubbo/registry/xds/istio/IstioEnv.java  |   0
 .../dubbo/registry/xds/util/NodeBuilder.java       |   0
 .../dubbo/registry/xds/util/PilotExchanger.java    |   0
 .../apache/dubbo/registry/xds/util/XdsChannel.java |   0
 .../xds/util/protocol/AbstractProtocol.java        |   0
 .../registry/xds/util/protocol/DeltaResource.java  |   0
 .../registry/xds/util/protocol/XdsProtocol.java    |   0
 .../xds/util/protocol/delta/DeltaEndpoint.java     |   0
 .../xds/util/protocol/delta/DeltaListener.java     |   0
 .../xds/util/protocol/delta/DeltaRoute.java        |   0
 .../xds/util/protocol/impl/EdsProtocol.java        |   0
 .../xds/util/protocol/impl/LdsProtocol.java        |   0
 .../xds/util/protocol/impl/RdsProtocol.java        |   0
 .../xds/util/protocol/message/Endpoint.java        |   0
 .../xds/util/protocol/message/EndpointResult.java  |   0
 .../xds/util/protocol/message/ListenerResult.java  |   0
 .../xds/util/protocol/message/RouteResult.java     |   0
 .../src/main/proto/ca.proto                        |   0
 .../org.apache.dubbo.registry.RegistryFactory      |   0
 ...g.apache.dubbo.registry.client.ServiceDiscovery |   0
 ...e.dubbo.registry.client.ServiceDiscoveryFactory |   0
 ....apache.dubbo.registry.xds.XdsCertificateSigner |   0
 pom.xml                                            |   2 +
 50 files changed, 1177 insertions(+), 48 deletions(-)

diff --git a/dubbo-kubernetes/pom.xml b/dubbo-kubernetes/pom.xml
new file mode 100644
index 0000000000..f5250b30a3
--- /dev/null
+++ b/dubbo-kubernetes/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-parent</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>dubbo-kubernetes</artifactId>
+    <name>${project.artifactId}</name>
+    <description>The Kubernetes Integration</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.fabric8</groupId>
+            <artifactId>kubernetes-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.fabric8</groupId>
+            <artifactId>kubernetes-server-mock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <version>3.8.0</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.jupiter</groupId>
+                    <artifactId>junit-jupiter-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+
+</project>
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
new file mode 100644
index 0000000000..1a0c1fa6b2
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.kubernetes;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
+
+import com.google.gson.Gson;
+import io.fabric8.kubernetes.api.model.ListOptionsBuilder;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class KubernetesMeshEnvListener implements MeshEnvListener {
+    public static final Logger logger = LoggerFactory.getLogger(KubernetesMeshEnvListener.class);
+    private volatile static boolean usingApiServer = false;
+    private volatile static KubernetesClient kubernetesClient;
+    private volatile static String namespace;
+
+    private final Map<String, MeshAppRuleListener> appRuleListenerMap = new ConcurrentHashMap<>();
+
+    private final Map<String, Watch> vsAppWatch = new ConcurrentHashMap<>();
+    private final Map<String, Watch> drAppWatch = new ConcurrentHashMap<>();
+
+    private final Map<String, String> vsAppCache = new ConcurrentHashMap<>();
+    private final Map<String, String> drAppCache = new ConcurrentHashMap<>();
+
+    public static void injectKubernetesEnv(KubernetesClient client, String configuredNamespace) {
+        usingApiServer = true;
+        kubernetesClient = client;
+        namespace = configuredNamespace;
+    }
+
+    @Override
+    public boolean isEnable() {
+        return usingApiServer;
+    }
+
+    @Override
+    public void onSubscribe(String appName, MeshAppRuleListener listener) {
+        appRuleListenerMap.put(appName, listener);
+        logger.info("Subscribe Mesh Rule in Kubernetes. AppName: " + appName);
+
+        // subscribe VisualService
+        subscribeVs(appName);
+
+        // subscribe DestinationRule
+        subscribeDr(appName);
+
+        // notify for start
+        notifyOnce(appName);
+    }
+
+    private void subscribeVs(String appName) {
+        if (vsAppWatch.containsKey(appName)) {
+            return;
+        }
+
+        try {
+            Watch watch = kubernetesClient
+                .customResource(
+                    MeshConstant.getVsDefinition())
+                .watch(namespace, appName, null, new ListOptionsBuilder().build(), new Watcher<String>() {
+                    @Override
+                    public void eventReceived(Action action, String resource) {
+                        logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+
+                        if (action == Action.ADDED || action == Action.MODIFIED) {
+                            Map drRuleMap = new Gson().fromJson(resource, Map.class);
+                            String vsRule = new Yaml(new SafeConstructor()).dump(drRuleMap);
+                            vsAppCache.put(appName, vsRule);
+                            if (drAppCache.containsKey(appName)) {
+                                notifyListener(vsRule, appName, drAppCache.get(appName));
+                            }
+                        } else {
+                            appRuleListenerMap.get(appName).receiveConfigInfo("");
+                        }
+                    }
+
+                    @Override
+                    public void onClose(WatcherException cause) {
+                        // ignore
+                    }
+                });
+            vsAppWatch.put(appName, watch);
+            try {
+                Map<String, Object> vsRule = kubernetesClient
+                    .customResource(
+                        MeshConstant.getVsDefinition())
+                    .get(namespace, appName);
+                vsAppCache.put(appName, new Yaml(new SafeConstructor()).dump(vsRule));
+            } catch (Throwable ignore) {
+
+            }
+        } catch (IOException e) {
+            logger.error("Error occurred when listen kubernetes crd.", e);
+        }
+    }
+
+    private void notifyListener(String vsRule, String appName, String drRule) {
+        String rule = vsRule + "\n---\n" + drRule;
+        logger.info("Notify App Rule Listener. AppName: " + appName + " Rule:" + rule);
+
+        appRuleListenerMap.get(appName).receiveConfigInfo(rule);
+    }
+
+    private void subscribeDr(String appName) {
+        if (drAppWatch.containsKey(appName)) {
+            return;
+        }
+
+        try {
+            Watch watch = kubernetesClient
+                .customResource(
+                    MeshConstant.getDrDefinition())
+                .watch(namespace, appName, null, new ListOptionsBuilder().build(), new Watcher<String>() {
+                    @Override
+                    public void eventReceived(Action action, String resource) {
+                        logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+
+                        if (action == Action.ADDED || action == Action.MODIFIED) {
+                            Map drRuleMap = new Gson().fromJson(resource, Map.class);
+                            String drRule = new Yaml(new SafeConstructor()).dump(drRuleMap);
+
+                            drAppCache.put(appName, drRule);
+                            if (vsAppCache.containsKey(appName)) {
+                                notifyListener(vsAppCache.get(appName), appName, drRule);
+                            }
+                        } else {
+                            appRuleListenerMap.get(appName).receiveConfigInfo("");
+                        }
+                    }
+
+                    @Override
+                    public void onClose(WatcherException cause) {
+                        // ignore
+                    }
+                });
+            drAppWatch.put(appName, watch);
+            try {
+                Map<String, Object> drRule = kubernetesClient
+                    .customResource(
+                        MeshConstant.getDrDefinition())
+                    .get(namespace, appName);
+                drAppCache.put(appName, new Yaml(new SafeConstructor()).dump(drRule));
+            } catch (Throwable ignore) {
+
+            }
+        } catch (IOException e) {
+            logger.error("Error occurred when listen kubernetes crd.", e);
+        }
+    }
+
+    private void notifyOnce(String appName) {
+        if (vsAppCache.containsKey(appName) && drAppCache.containsKey(appName)) {
+            notifyListener(vsAppCache.get(appName), appName, drAppCache.get(appName));
+        }
+    }
+
+    @Override
+    public void onUnSubscribe(String appName) {
+        appRuleListenerMap.remove(appName);
+
+        if (vsAppWatch.containsKey(appName)) {
+            vsAppWatch.remove(appName).close();
+        }
+        vsAppCache.remove(appName);
+
+        if (drAppWatch.containsKey(appName)) {
+            drAppWatch.remove(appName).close();
+        }
+        drAppCache.remove(appName);
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
similarity index 52%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
index 41eba21a70..9d1c6d06cf 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
@@ -14,27 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
 
-import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
-import org.apache.dubbo.registry.client.ServiceDiscovery;
-import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory;
 
-public class XdsServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
+import java.util.concurrent.atomic.AtomicBoolean;
 
-    private static final Logger logger = LoggerFactory.getLogger(XdsServiceDiscoveryFactory.class);
+public class KubernetesMeshEnvListenerFactory implements MeshEnvListenerFactory {
+    public static final Logger logger = LoggerFactory.getLogger(KubernetesMeshEnvListenerFactory.class);
+    private final AtomicBoolean initialized = new AtomicBoolean(false);
+    private MeshEnvListener listener = null;
 
     @Override
-    protected ServiceDiscovery createDiscovery(URL registryURL) {
-        XdsServiceDiscovery xdsServiceDiscovery = new XdsServiceDiscovery(ApplicationModel.defaultModel() ,registryURL);
+    public MeshEnvListener getListener() {
         try {
-            xdsServiceDiscovery.doInitialize(registryURL);
-        } catch (Exception e) {
-            logger.error("Error occurred when initialize xDS service discovery impl.", e);
+            if (initialized.compareAndSet(false, true)) {
+                listener = new NopKubernetesMeshEnvListener();
+            }
+        } catch (Throwable t) {
+            logger.info("Current Env not support Kubernetes.");
         }
-        return xdsServiceDiscovery;
+        return listener;
     }
 }
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
similarity index 81%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
index a4bd51e6b1..b221c89c2b 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
@@ -14,20 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.support.FailbackRegistry;
 
 /**
- * Empty implements for xDS <br/>
- * xDS only support `Service Discovery` mode register <br/>
+ * Empty implements for Kubernetes <br/>
+ * Kubernetes only support `Service Discovery` mode register <br/>
  * Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
- * {@link XdsServiceDiscovery} is the real implementation of xDS
+ * {@link KubernetesServiceDiscovery} is the real implementation of Kubernetes
  */
-public class XdsRegistry extends FailbackRegistry {
-    public XdsRegistry(URL url) {
+public class KubernetesRegistry extends FailbackRegistry {
+    public KubernetesRegistry(URL url) {
         super(url);
     }
 
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
similarity index 87%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
index 8fa130b197..fe0e0477d7 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.support.AbstractRegistryFactory;
 
-public class XdsRegistryFactory extends AbstractRegistryFactory {
+public class KubernetesRegistryFactory extends AbstractRegistryFactory {
 
     @Override
     protected String createRegistryCacheKey(URL url) {
@@ -29,6 +29,6 @@ public class XdsRegistryFactory extends AbstractRegistryFactory {
 
     @Override
     protected Registry createRegistry(URL url) {
-        return new XdsRegistry(url);
+        return new KubernetesRegistry(url);
     }
 }
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
new file mode 100644
index 0000000000..e0f47f1f95
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesConfigUtils;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelUtil;
+
+import com.alibaba.fastjson.JSONObject;
+import io.fabric8.kubernetes.api.model.EndpointAddress;
+import io.fabric8.kubernetes.api.model.EndpointPort;
+import io.fabric8.kubernetes.api.model.EndpointSubset;
+import io.fabric8.kubernetes.api.model.Endpoints;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+public class KubernetesServiceDiscovery extends AbstractServiceDiscovery {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private KubernetesClient kubernetesClient;
+
+    private String currentHostname;
+
+    private final URL registryURL;
+
+    private final String namespace;
+
+    private final boolean enableRegister;
+
+    public final static String KUBERNETES_PROPERTIES_KEY = "io.dubbo/metadata";
+
+    private final static ConcurrentHashMap<String, Watch> SERVICE_WATCHER = new ConcurrentHashMap<>(64);
+
+    private final static ConcurrentHashMap<String, Watch> PODS_WATCHER = new ConcurrentHashMap<>(64);
+
+    private final static ConcurrentHashMap<String, Watch> ENDPOINTS_WATCHER = new ConcurrentHashMap<>(64);
+
+    private final static ConcurrentHashMap<String, AtomicLong> SERVICE_UPDATE_TIME = new ConcurrentHashMap<>(64);
+
+    public KubernetesServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
+        super(applicationModel, registryURL);
+        Config config = KubernetesConfigUtils.createKubernetesConfig(registryURL);
+        this.kubernetesClient = new DefaultKubernetesClient(config);
+        this.currentHostname = System.getenv("HOSTNAME");
+        this.registryURL = registryURL;
+        this.namespace = config.getNamespace();
+        this.enableRegister = registryURL.getParameter(KubernetesClientConst.ENABLE_REGISTER, true);
+
+        boolean availableAccess;
+        try {
+            availableAccess = kubernetesClient.pods().withName(currentHostname).get() != null;
+        } catch (Throwable e) {
+            availableAccess = false;
+        }
+        if (!availableAccess) {
+            String message = "Unable to access api server. " +
+                "Please check your url config." +
+                " Master URL: " + config.getMasterUrl() +
+                " Hostname: " + currentHostname;
+            logger.error(message);
+        } else {
+            KubernetesMeshEnvListener.injectKubernetesEnv(kubernetesClient, namespace);
+        }
+    }
+
+    @Override
+    public void doDestroy() throws Exception {
+        SERVICE_WATCHER.forEach((k, v) -> v.close());
+        SERVICE_WATCHER.clear();
+
+        PODS_WATCHER.forEach((k, v) -> v.close());
+        PODS_WATCHER.clear();
+
+        ENDPOINTS_WATCHER.forEach((k, v) -> v.close());
+        ENDPOINTS_WATCHER.clear();
+
+        kubernetesClient.close();
+    }
+
+    @Override
+    public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {
+        if (enableRegister) {
+            kubernetesClient
+                .pods()
+                .inNamespace(namespace)
+                .withName(currentHostname)
+                .edit(pod ->
+                    new PodBuilder(pod)
+                        .editOrNewMetadata()
+                        .addToAnnotations(KUBERNETES_PROPERTIES_KEY, JSONObject.toJSONString(serviceInstance.getMetadata()))
+                        .endMetadata()
+                        .build());
+            if (logger.isInfoEnabled()) {
+                logger.info("Write Current Service Instance Metadata to Kubernetes pod. " +
+                    "Current pod name: " + currentHostname);
+            }
+        }
+    }
+
+    /**
+     * Comparing to {@link AbstractServiceDiscovery#doUpdate(ServiceInstance)}, unregister() is unnecessary here.
+     */
+    @Override
+    public void doUpdate(ServiceInstance serviceInstance) throws RuntimeException {
+        reportMetadata(serviceInstance.getServiceMetadata());
+        this.doRegister(serviceInstance);
+    }
+
+    @Override
+    public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {
+        if (enableRegister) {
+            kubernetesClient
+                .pods()
+                .inNamespace(namespace)
+                .withName(currentHostname)
+                .edit(pod ->
+                    new PodBuilder(pod)
+                        .editOrNewMetadata()
+                        .removeFromAnnotations(KUBERNETES_PROPERTIES_KEY)
+                        .endMetadata()
+                        .build());
+            if (logger.isInfoEnabled()) {
+                logger.info("Remove Current Service Instance from Kubernetes pod. Current pod name: " + currentHostname);
+            }
+        }
+    }
+
+    @Override
+    public Set<String> getServices() {
+        return kubernetesClient
+            .services()
+            .inNamespace(namespace)
+            .list()
+            .getItems()
+            .stream()
+            .map(service -> service.getMetadata().getName())
+            .collect(Collectors.toSet());
+    }
+
+    @Override
+    public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+        Endpoints endpoints =
+            kubernetesClient
+                .endpoints()
+                .inNamespace(namespace)
+                .withName(serviceName)
+                .get();
+
+        return toServiceInstance(endpoints, serviceName);
+    }
+
+    @Override
+    public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
+        listener.getServiceNames().forEach(serviceName -> {
+            SERVICE_UPDATE_TIME.put(serviceName, new AtomicLong(0L));
+
+            // Watch Service Endpoint Modification
+            watchEndpoints(listener, serviceName);
+
+            // Watch Pods Modification, happens when ServiceInstance updated
+            watchPods(listener, serviceName);
+
+            // Watch Service Modification, happens when Service Selector updated, used to update pods watcher
+            watchService(listener, serviceName);
+        });
+    }
+
+    private void watchEndpoints(ServiceInstancesChangedListener listener, String serviceName) {
+        Watch watch = kubernetesClient
+            .endpoints()
+            .inNamespace(namespace)
+            .withName(serviceName)
+            .watch(new Watcher<Endpoints>() {
+                @Override
+                public void eventReceived(Action action, Endpoints resource) {
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("Received Endpoint Event. Event type: " + action.name() +
+                            ". Current pod name: " + currentHostname);
+                    }
+
+                    notifyServiceChanged(serviceName, listener);
+                }
+
+                @Override
+                public void onClose(WatcherException cause) {
+                    // ignore
+                }
+            });
+
+        ENDPOINTS_WATCHER.put(serviceName, watch);
+    }
+
+    private void watchPods(ServiceInstancesChangedListener listener, String serviceName) {
+        Map<String, String> serviceSelector = getServiceSelector(serviceName);
+        if (serviceSelector == null) {
+            return;
+        }
+
+        Watch watch = kubernetesClient
+            .pods()
+            .inNamespace(namespace)
+            .withLabels(serviceSelector)
+            .watch(new Watcher<Pod>() {
+                @Override
+                public void eventReceived(Action action, Pod resource) {
+                    if (Action.MODIFIED.equals(action)) {
+                        if (logger.isDebugEnabled()) {
+                            logger.debug("Received Pods Update Event. Current pod name: " + currentHostname);
+                        }
+
+                        notifyServiceChanged(serviceName, listener);
+                    }
+                }
+
+                @Override
+                public void onClose(WatcherException cause) {
+                    // ignore
+                }
+            });
+
+        PODS_WATCHER.put(serviceName, watch);
+    }
+
+    private void watchService(ServiceInstancesChangedListener listener, String serviceName) {
+        Watch watch = kubernetesClient
+            .services()
+            .inNamespace(namespace)
+            .withName(serviceName)
+            .watch(new Watcher<Service>() {
+                @Override
+                public void eventReceived(Action action, Service resource) {
+                    if (Action.MODIFIED.equals(action)) {
+                        if (logger.isDebugEnabled()) {
+                            logger.debug("Received Service Update Event. Update Pods Watcher. " +
+                                "Current pod name: " + currentHostname);
+                        }
+
+                        if (PODS_WATCHER.containsKey(serviceName)) {
+                            PODS_WATCHER.get(serviceName).close();
+                            PODS_WATCHER.remove(serviceName);
+                        }
+                        watchPods(listener, serviceName);
+                    }
+                }
+
+                @Override
+                public void onClose(WatcherException cause) {
+                    // ignore
+                }
+            });
+
+        SERVICE_WATCHER.put(serviceName, watch);
+    }
+
+    private void notifyServiceChanged(String serviceName, ServiceInstancesChangedListener listener) {
+        long receivedTime = System.nanoTime();
+
+        ServiceInstancesChangedEvent event;
+
+        event = new ServiceInstancesChangedEvent(serviceName, getInstances(serviceName));
+
+        AtomicLong updateTime = SERVICE_UPDATE_TIME.get(serviceName);
+        long lastUpdateTime = updateTime.get();
+
+        if (lastUpdateTime <= receivedTime) {
+            if (updateTime.compareAndSet(lastUpdateTime, receivedTime)) {
+                listener.onEvent(event);
+                return;
+            }
+        }
+
+        if (logger.isInfoEnabled()) {
+            logger.info("Discard Service Instance Data. " +
+                "Possible Cause: Newer message has been processed or Failed to update time record by CAS. " +
+                "Current Data received time: " + receivedTime + ". " +
+                "Newer Data received time: " + lastUpdateTime + ".");
+        }
+    }
+
+    @Override
+    public URL getUrl() {
+        return registryURL;
+    }
+
+    private Map<String, String> getServiceSelector(String serviceName) {
+        Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get();
+        if (service == null) {
+            return null;
+        }
+        return service.getSpec().getSelector();
+    }
+
+    private List<ServiceInstance> toServiceInstance(Endpoints endpoints, String serviceName) {
+        Map<String, String> serviceSelector = getServiceSelector(serviceName);
+        if (serviceSelector == null) {
+            return new LinkedList<>();
+        }
+        Map<String, Pod> pods = kubernetesClient
+            .pods()
+            .inNamespace(namespace)
+            .withLabels(serviceSelector)
+            .list()
+            .getItems()
+            .stream()
+            .collect(
+                Collectors.toMap(
+                    pod -> pod.getMetadata().getName(),
+                    pod -> pod));
+
+        List<ServiceInstance> instances = new LinkedList<>();
+        Set<Integer> instancePorts = new HashSet<>();
+
+        for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+            instancePorts.addAll(
+                endpointSubset.getPorts()
+                    .stream().map(EndpointPort::getPort)
+                    .collect(Collectors.toSet()));
+        }
+
+        for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+            for (EndpointAddress address : endpointSubset.getAddresses()) {
+                Pod pod = pods.get(address.getTargetRef().getName());
+                String ip = address.getIp();
+                if (pod == null) {
+                    logger.warn("Unable to match Kubernetes Endpoint address with Pod. " +
+                        "EndpointAddress Hostname: " + address.getTargetRef().getName());
+                    continue;
+                }
+
+                instancePorts.forEach(port -> {
+                    ServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, ip, port, ScopeModelUtil.getApplicationModel(getUrl().getScopeModel()));
+
+                    String properties = pod.getMetadata().getAnnotations().get(KUBERNETES_PROPERTIES_KEY);
+                    if (StringUtils.isNotEmpty(properties)) {
+                        serviceInstance.getMetadata().putAll(JSONObject.parseObject(properties, Map.class));
+                        instances.add(serviceInstance);
+                    } else {
+                        logger.warn("Unable to find Service Instance metadata in Pod Annotations. " +
+                            "Possibly cause: provider has not been initialized successfully. " +
+                            "EndpointAddress Hostname: " + address.getTargetRef().getName());
+                    }
+                });
+            }
+        }
+
+        return instances;
+    }
+
+    /**
+     * UT used only
+     */
+    @Deprecated
+    public void setCurrentHostname(String currentHostname) {
+        this.currentHostname = currentHostname;
+    }
+
+    /**
+     * UT used only
+     */
+    @Deprecated
+    public void setKubernetesClient(KubernetesClient kubernetesClient) {
+        this.kubernetesClient = kubernetesClient;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
similarity index 67%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
index 8fa130b197..7d11dfaaa8 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
@@ -14,21 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.registry.Registry;
-import org.apache.dubbo.registry.support.AbstractRegistryFactory;
-
-public class XdsRegistryFactory extends AbstractRegistryFactory {
-
-    @Override
-    protected String createRegistryCacheKey(URL url) {
-        return url.toFullString();
-    }
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
 
+public class KubernetesServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
     @Override
-    protected Registry createRegistry(URL url) {
-        return new XdsRegistry(url);
+    protected ServiceDiscovery createDiscovery(URL registryURL) {
+        return new KubernetesServiceDiscovery(applicationModel, registryURL);
     }
 }
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
new file mode 100644
index 0000000000..813bdd8259
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.kubernetes;
+
+import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
+
+public class MeshConstant {
+    public static CustomResourceDefinitionContext getVsDefinition() {
+        // TODO cache
+        return new CustomResourceDefinitionContext.Builder()
+            .withGroup("service.dubbo.apache.org")
+            .withVersion("v1alpha1")
+            .withScope("Namespaced")
+            .withName("virtualservices.service.dubbo.apache.org")
+            .withPlural("virtualservices")
+            .withKind("VirtualService").build();
+    }
+
+    public static CustomResourceDefinitionContext getDrDefinition() {
+        // TODO cache
+        return new CustomResourceDefinitionContext.Builder()
+            .withGroup("service.dubbo.apache.org")
+            .withVersion("v1alpha1")
+            .withScope("Namespaced")
+            .withName("destinationrules.service.dubbo.apache.org")
+            .withPlural("destinationrules")
+            .withKind("DestinationRule").build();
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
similarity index 64%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
index 8fa130b197..1238efb579 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
@@ -14,21 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.registry.Registry;
-import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
 
-public class XdsRegistryFactory extends AbstractRegistryFactory {
+public class NopKubernetesMeshEnvListener implements MeshEnvListener {
 
     @Override
-    protected String createRegistryCacheKey(URL url) {
-        return url.toFullString();
+    public boolean isEnable() {
+        return false;
     }
 
     @Override
-    protected Registry createRegistry(URL url) {
-        return new XdsRegistry(url);
+    public void onSubscribe(String appName, MeshAppRuleListener listener) {
+
+    }
+
+    @Override
+    public void onUnSubscribe(String appName) {
+
     }
 }
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
new file mode 100644
index 0000000000..d4591a249e
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.kubernetes.util;
+
+public class KubernetesClientConst {
+    public static final String DEFAULT_MASTER_PLACEHOLDER = "DEFAULT_MASTER_HOST";
+    public static final String DEFAULT_MASTER_URL = "https://kubernetes.default.svc";
+
+    public final static String ENABLE_REGISTER = "enableRegister";
+
+    public final static String TRUST_CERTS = "trustCerts";
+
+    public final static String USE_HTTPS = "useHttps";
+
+    public static final String HTTP2_DISABLE = "http2Disable";
+
+    public final static String NAMESPACE = "namespace";
+
+    public final static String API_VERSION = "apiVersion";
+
+    public final static String CA_CERT_FILE = "caCertFile";
+
+    public final static String CA_CERT_DATA = "caCertData";
+
+    public final static String CLIENT_CERT_FILE = "clientCertFile";
+
+    public final static String CLIENT_CERT_DATA = "clientCertData";
+
+    public final static String CLIENT_KEY_FILE = "clientKeyFile";
+
+    public final static String CLIENT_KEY_DATA = "clientKeyData";
+
+    public final static String CLIENT_KEY_ALGO = "clientKeyAlgo";
+
+    public final static String CLIENT_KEY_PASSPHRASE = "clientKeyPassphrase";
+
+    public final static String OAUTH_TOKEN = "oauthToken";
+
+    public final static String USERNAME = "username";
+
+    public final static String PASSWORD = "password";
+
+    public final static String WATCH_RECONNECT_INTERVAL = "watchReconnectInterval";
+
+    public final static String WATCH_RECONNECT_LIMIT = "watchReconnectLimit";
+
+    public final static String CONNECTION_TIMEOUT = "connectionTimeout";
+
+    public final static String REQUEST_TIMEOUT = "requestTimeout";
+
+    public final static String ROLLING_TIMEOUT = "rollingTimeout";
+
+    public final static String LOGGING_INTERVAL = "loggingInterval";
+
+    public final static String HTTP_PROXY = "httpProxy";
+
+    public final static String HTTPS_PROXY = "httpsProxy";
+
+    public final static String PROXY_USERNAME = "proxyUsername";
+
+    public final static String PROXY_PASSWORD = "proxyPassword";
+
+    public final static String NO_PROXY = "noProxy";
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
new file mode 100644
index 0000000000..81505ca647
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.kubernetes.util;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+
+import java.util.Base64;
+
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.API_VERSION;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_ALGO;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_PASSPHRASE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CONNECTION_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_PLACEHOLDER;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_URL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP2_DISABLE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTPS_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.LOGGING_INTERVAL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NAMESPACE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NO_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.OAUTH_TOKEN;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PASSWORD;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_PASSWORD;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_USERNAME;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.REQUEST_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.ROLLING_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.TRUST_CERTS;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USERNAME;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USE_HTTPS;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_INTERVAL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_LIMIT;
+
+public class KubernetesConfigUtils {
+
+    public static Config createKubernetesConfig(URL url) {
+        // Init default config
+        Config base = Config.autoConfigure(null);
+
+        // replace config with parameters if presents
+        return new ConfigBuilder(base) //
+            .withMasterUrl(buildMasterUrl(url)) //
+            .withApiVersion(url.getParameter(API_VERSION, base.getApiVersion())) //
+            .withNamespace(url.getParameter(NAMESPACE, base.getNamespace())) //
+            .withUsername(url.getParameter(USERNAME, base.getUsername())) //
+            .withPassword(url.getParameter(PASSWORD, base.getPassword())) //
+
+            .withOauthToken(url.getParameter(OAUTH_TOKEN, base.getOauthToken())) //
+
+            .withCaCertFile(url.getParameter(CA_CERT_FILE, base.getCaCertFile())) //
+            .withCaCertData(url.getParameter(CA_CERT_DATA, decodeBase64(base.getCaCertData()))) //
+
+            .withClientKeyFile(url.getParameter(CLIENT_KEY_FILE, base.getClientKeyFile())) //
+            .withClientKeyData(url.getParameter(CLIENT_KEY_DATA, decodeBase64(base.getClientKeyData()))) //
+
+            .withClientCertFile(url.getParameter(CLIENT_CERT_FILE, base.getClientCertFile())) //
+            .withClientCertData(url.getParameter(CLIENT_CERT_DATA, decodeBase64(base.getClientCertData()))) //
+
+            .withClientKeyAlgo(url.getParameter(CLIENT_KEY_ALGO, base.getClientKeyAlgo())) //
+            .withClientKeyPassphrase(url.getParameter(CLIENT_KEY_PASSPHRASE, base.getClientKeyPassphrase())) //
+
+            .withConnectionTimeout(url.getParameter(CONNECTION_TIMEOUT, base.getConnectionTimeout())) //
+            .withRequestTimeout(url.getParameter(REQUEST_TIMEOUT, base.getRequestTimeout())) //
+            .withRollingTimeout(url.getParameter(ROLLING_TIMEOUT, base.getRollingTimeout())) //
+
+            .withWatchReconnectInterval(url.getParameter(WATCH_RECONNECT_INTERVAL, base.getWatchReconnectInterval())) //
+            .withWatchReconnectLimit(url.getParameter(WATCH_RECONNECT_LIMIT, base.getWatchReconnectLimit())) //
+            .withLoggingInterval(url.getParameter(LOGGING_INTERVAL, base.getLoggingInterval())) //
+
+            .withTrustCerts(url.getParameter(TRUST_CERTS, base.isTrustCerts())) //
+            .withHttp2Disable(url.getParameter(HTTP2_DISABLE, base.isTrustCerts())) //
+
+            .withHttpProxy(url.getParameter(HTTP_PROXY, base.getHttpProxy())) //
+            .withHttpsProxy(url.getParameter(HTTPS_PROXY, base.getHttpsProxy())) //
+            .withProxyUsername(url.getParameter(PROXY_USERNAME, base.getProxyUsername())) //
+            .withProxyPassword(url.getParameter(PROXY_PASSWORD, base.getProxyPassword())) //
+            .withNoProxy(url.getParameter(NO_PROXY, base.getNoProxy())) //
+            .build();
+    }
+
+    private static String buildMasterUrl(URL url) {
+        if (DEFAULT_MASTER_PLACEHOLDER.equalsIgnoreCase(url.getHost())) {
+            return DEFAULT_MASTER_URL;
+        }
+        return (url.getParameter(USE_HTTPS, true) ?
+            "https://" : "http://")
+            + url.getHost() + ":" + url.getPort();
+    }
+
+    private static String decodeBase64(String str) {
+        return StringUtils.isNotEmpty(str) ?
+            new String(Base64.getDecoder().decode(str)) :
+            null;
+    }
+}
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000000..94177d81ea
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesRegistryFactory
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000000..3e1b88e44c
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000000..4301ab8b4b
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory
new file mode 100644
index 0000000000..4dfae84b80
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesMeshEnvListenerFactory
diff --git a/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java b/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
new file mode 100644
index 0000000000..6b1a1b0318
--- /dev/null
+++ b/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//package org.apache.dubbo.registry.kubernetes;
+//
+//import org.apache.dubbo.common.URL;
+//import org.apache.dubbo.registry.client.DefaultServiceInstance;
+//import org.apache.dubbo.registry.client.ServiceInstance;
+//import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+//import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+//import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+//import org.apache.dubbo.rpc.model.ApplicationModel;
+//import org.apache.dubbo.rpc.model.ScopeModelUtil;
+//
+//import io.fabric8.kubernetes.api.model.Endpoints;
+//import io.fabric8.kubernetes.api.model.EndpointsBuilder;
+//import io.fabric8.kubernetes.api.model.Pod;
+//import io.fabric8.kubernetes.api.model.PodBuilder;
+//import io.fabric8.kubernetes.api.model.Service;
+//import io.fabric8.kubernetes.api.model.ServiceBuilder;
+//import io.fabric8.kubernetes.client.Config;
+//import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+//import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
+//import org.junit.jupiter.api.AfterEach;
+//import org.junit.jupiter.api.Assertions;
+//import org.junit.jupiter.api.BeforeEach;
+//import org.junit.jupiter.api.Test;
+//import org.junit.jupiter.api.extension.ExtendWith;
+//import org.mockito.ArgumentCaptor;
+//import org.mockito.Mockito;
+//import org.mockito.junit.jupiter.MockitoExtension;
+//
+//import java.util.HashMap;
+//import java.util.HashSet;
+//import java.util.Map;
+//
+//@ExtendWith({MockitoExtension.class})
+//public class KubernetesServiceDiscoveryTest {
+//    public KubernetesServer mockServer = new KubernetesServer(false, true);
+//
+//    private NamespacedKubernetesClient mockClient;
+//
+//    private ServiceInstancesChangedListener mockListener = Mockito.mock(ServiceInstancesChangedListener.class);
+//
+//    private URL serverUrl;
+//
+//    private Map<String, String> selector;
+//
+//    @BeforeEach
+//    public void setUp() {
+//        mockServer.before();
+//        mockClient = mockServer.getClient();
+//
+//        serverUrl = URL.valueOf(mockClient.getConfiguration().getMasterUrl())
+//            .setProtocol("kubernetes")
+//            .addParameter(KubernetesClientConst.USE_HTTPS, "false")
+//            .addParameter(KubernetesClientConst.HTTP2_DISABLE, "true");
+//        serverUrl.setScopeModel(ApplicationModel.defaultModel());
+//
+//        System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
+//        System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
+//
+//        selector = new HashMap<>(4);
+//        selector.put("l", "v");
+//        Pod pod = new PodBuilder()
+//            .withNewMetadata().withName("TestServer").withLabels(selector).endMetadata()
+//            .build();
+//
+//        Service service = new ServiceBuilder()
+//            .withNewMetadata().withName("TestService").endMetadata()
+//            .withNewSpec().withSelector(selector).endSpec().build();
+//
+//        Endpoints endPoints = new EndpointsBuilder()
+//            .withNewMetadata().withName("TestService").endMetadata()
+//            .addNewSubset()
+//            .addNewAddress().withIp("ip1")
+//            .withNewTargetRef().withUid("uid1").withName("TestServer").endTargetRef().endAddress()
+//            .addNewPort("Test", "Test", 12345, "TCP").endSubset()
+//            .build();
+//
+//        mockClient.pods().create(pod);
+//        mockClient.services().create(service);
+//        mockClient.endpoints().create(endPoints);
+//    }
+//
+//    @AfterEach
+//    public void destroy() {
+//        mockServer.after();
+//    }
+//
+//    @Test
+//    public void testEndpointsUpdate() throws Exception {
+//
+//        KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+//        serviceDiscovery.initialize(serverUrl);
+//
+//        serviceDiscovery.setCurrentHostname("TestServer");
+//        serviceDiscovery.setKubernetesClient(mockClient);
+//
+//        ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+//        serviceDiscovery.register(serviceInstance);
+//
+//        HashSet<String> serviceList = new HashSet<>(4);
+//        serviceList.add("TestService");
+//        Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+//        Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+//
+//        serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+//        mockClient.endpoints().withName("TestService")
+//            .edit(endpoints ->
+//                new EndpointsBuilder(endpoints)
+//                    .editFirstSubset()
+//                    .addNewAddress()
+//                    .withIp("ip2")
+//                    .withNewTargetRef().withUid("uid2").withName("TestServer").endTargetRef()
+//                    .endAddress().endSubset()
+//                    .build());
+//
+//        Thread.sleep(5000);
+//        ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+//            ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+//        Mockito.verify(mockListener, Mockito.times(2)).onEvent(eventArgumentCaptor.capture());
+//        Assertions.assertEquals(2, eventArgumentCaptor.getValue().getServiceInstances().size());
+//
+//        serviceDiscovery.unregister(serviceInstance);
+//
+//        serviceDiscovery.destroy();
+//    }
+//
+//    @Test
+//    public void testPodsUpdate() throws Exception {
+//
+//        KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+//        serviceDiscovery.initialize(serverUrl);
+//
+//        serviceDiscovery.setCurrentHostname("TestServer");
+//        serviceDiscovery.setKubernetesClient(mockClient);
+//
+//        ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+//        serviceDiscovery.register(serviceInstance);
+//
+//        HashSet<String> serviceList = new HashSet<>(4);
+//        serviceList.add("TestService");
+//        Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+//        Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+//
+//        serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+//
+//        serviceInstance = new DefaultServiceInstance("TestService", "Test12345", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+//        serviceDiscovery.update(serviceInstance);
+//
+//        Thread.sleep(5000);
+//        ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+//            ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+//        Mockito.verify(mockListener, Mockito.times(1)).onEvent(eventArgumentCaptor.capture());
+//        Assertions.assertEquals(1, eventArgumentCaptor.getValue().getServiceInstances().size());
+//
+//        serviceDiscovery.unregister(serviceInstance);
+//
+//        serviceDiscovery.destroy();
+//    }
+//
+//    @Test
+//    public void testGetInstance() throws Exception {
+//        KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+//        serviceDiscovery.initialize(serverUrl);
+//
+//        serviceDiscovery.setCurrentHostname("TestServer");
+//        serviceDiscovery.setKubernetesClient(mockClient);
+//
+//        ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+//        serviceDiscovery.register(serviceInstance);
+//
+//        serviceDiscovery.update(serviceInstance);
+//
+//        Assertions.assertEquals(1, serviceDiscovery.getServices().size());
+//        Assertions.assertEquals(1, serviceDiscovery.getInstances("TestService").size());
+//
+//        Assertions.assertEquals(serviceInstance, serviceDiscovery.getLocalInstance());
+//
+//        serviceDiscovery.unregister(serviceInstance);
+//
+//        serviceDiscovery.destroy();
+//    }
+//}
diff --git a/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000000..ca6ee9cea8
--- /dev/null
+++ b/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
\ No newline at end of file
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
index 8cb6984f2f..b72880197a 100644
--- a/dubbo-registry/pom.xml
+++ b/dubbo-registry/pom.xml
@@ -35,7 +35,6 @@
         <module>dubbo-registry-zookeeper</module>
         <module>dubbo-registry-nacos</module>
         <module>dubbo-registry-multiple</module>
-        <module>dubbo-registry-xds</module>
     </modules>
 
     <dependencies>
diff --git a/dubbo-registry/dubbo-registry-xds/pom.xml b/dubbo-xds/pom.xml
similarity index 93%
rename from dubbo-registry/dubbo-registry-xds/pom.xml
rename to dubbo-xds/pom.xml
index eb6e3798de..d162c2e163 100644
--- a/dubbo-registry/dubbo-registry-xds/pom.xml
+++ b/dubbo-xds/pom.xml
@@ -18,16 +18,20 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.dubbo</groupId>
-        <artifactId>dubbo-registry</artifactId>
+        <artifactId>dubbo-parent</artifactId>
         <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
-    <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>dubbo-registry-xds</artifactId>
+    <artifactId>dubbo-xds</artifactId>
     <name>${project.artifactId}</name>
-    <description>The xDS registry module of Dubbo project</description>
+    <description>The xDS Integration</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
 
     <dependencies>
 
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/proto/ca.proto b/dubbo-xds/src/main/proto/ca.proto
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/proto/ca.proto
rename to dubbo-xds/src/main/proto/ca.proto
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
diff --git a/pom.xml b/pom.xml
index 5fdea1c96b..3b69aee256 100644
--- a/pom.xml
+++ b/pom.xml
@@ -157,6 +157,8 @@
         <module>dubbo-spring-boot</module>
         <module>dubbo-native</module>
         <module>dubbo-test</module>
+        <module>dubbo-kubernetes</module>
+        <module>dubbo-xds</module>
         <module>dubbo-native-plugin</module>
     </modules>