Dubbo与Kubernetes集成
2019-11-18杂谈搜奇网44°c
A+ A-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<URL> registryURLs,
Map<String, String> 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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5efc1f17fba0 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-alpine
ADD app.jar app.jar
ENV 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
这时刻就变成主机地点了。
docker run --name zkserver --rm -p 42181:2181 -d zookeeper:3.4.9
看一下zk起来了
niuxinli@niuxinli-B450M-DS3H:~/docker_dubbo_demo/dubbo-samples/dubbo-samples-docker$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5efc1f17fba0 zookeeper:3.4.9 "/docker-entrypoint.…" 4 seconds ago Up 2 seconds 2888/tcp, 3888/tcp, 0.0.0.0:42181->2181/tcp zkserver
如今启动dubbo provider,运用link的体式格局衔接zk,我的本机地点是192.168.1.8,provider本身暴露的端口为20880,主机暴露端口改成28888。
在Kubernetes中运用Dubbo
当在Kubernetes中启动多个副本的时刻,指定详细的IP和详细的端口,都是不可行的,由于每一个机械的IP都不一样,不能写很多个yaml文件,而且一旦指定了详细端口,那这台主机的这个端口就被占用了。
我们能够经由历程建立Service,运用NodePort的体式格局,把端口牢固住,如许端口的题目就处理了。由于是对外效劳,所以运用ClusterIP肯定是不行了,IP有两种处理方法:
(1)运用Kubernetes的downward api动态的传入主机的ip。
(2)传牢固的loadbalancer的地点,比方在所有的node以外有一个F5。
不论哪一种要领,都是一种让步的方法,很不“云原生”,我演示一下运用downward api动态传入主机地点,并运用nodeport牢固端口的体式格局。
我的kubernetes集群以下:
角色 | 地点 |
---|---|
master | 192.168.174.50 |
node1 | 192.168.174.51 |
node2 | 192.168.174.52 |
node3 | 192.168.174.53 |
zk的地点是192.168.1.8,它与集群的主机互通。
我没有建private镜像堆栈,把我之前打好的dubbo-demo直接push到docker-hub上了,名字是nxlhero/dubbo-demo。
建立Service,运用的NodePort为30001,建立4个副本,如许3台机械上正好有一台起两个pod。
apiVersion: v1
kind: Service
metadata:
name: dubbo-docker
labels:
run: dubbo
spec:
type: NodePort
ports:
- port: 20880
targetPort: 20880
nodePort: 30001
selector:
run: dubbo-docker
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dubbo-docker
spec:
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。