经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Kubernetes » 查看文章
Dubbo与Kubernetes集成
来源:cnblogs  作者:牛哥的博客  时间:2019/11/4 12:00:30  对本文有异议

Dubbo应用迁移到docker的问题

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

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

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

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

Dubbo提供的解决方案

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

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

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

  1. /**
  2. * Register & bind IP address for service provider, can be configured separately.
  3. * Configuration priority: environment variables -> java system properties -> host property in config file ->
  4. * /etc/hosts -> default network address -> first available network address
  5. *
  6. * @param protocolConfig
  7. * @param registryURLs
  8. * @param map
  9. * @return
  10. */
  11. private static String findConfigedHosts(ServiceConfig<?> sc,
  12. ProtocolConfig protocolConfig,
  13. List<URL> registryURLs,
  14. Map<String, String> map) {
  15. boolean anyhost = false;
  16. String hostToBind = getValueFromConfig(protocolConfig, DUBBO_IP_TO_BIND);
  17. if (hostToBind != null && hostToBind.length() > 0 && isInvalidLocalHost(hostToBind)) {
  18. throw new IllegalArgumentException("Specified invalid bind ip from property:" + DUBBO_IP_TO_BIND + ", value:" + hostToBind);
  19. }
  20. // if bind ip is not found in environment, keep looking up
  21. if (StringUtils.isEmpty(hostToBind)) {
  22. hostToBind = protocolConfig.getHost();
  23. if (sc.getProvider() != null && StringUtils.isEmpty(hostToBind)) {
  24. hostToBind = sc.getProvider().getHost();
  25. }
  26. if (isInvalidLocalHost(hostToBind)) {
  27. anyhost = true;
  28. try {
  29. logger.info("No valid ip found from environment, try to find valid host from DNS.");
  30. hostToBind = InetAddress.getLocalHost().getHostAddress();
  31. } catch (UnknownHostException e) {
  32. logger.warn(e.getMessage(), e);
  33. }
  34. if (isInvalidLocalHost(hostToBind)) {
  35. if (CollectionUtils.isNotEmpty(registryURLs)) {
  36. for (URL registryURL : registryURLs) {
  37. if (MULTICAST.equalsIgnoreCase(registryURL.getParameter("registry"))) {
  38. // skip multicast registry since we cannot connect to it via Socket
  39. continue;
  40. }
  41. try (Socket socket = new Socket()) {
  42. SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
  43. socket.connect(addr, 1000);
  44. hostToBind = socket.getLocalAddress().getHostAddress();
  45. break;
  46. } catch (Exception e) {
  47. logger.warn(e.getMessage(), e);
  48. }
  49. }
  50. }
  51. if (isInvalidLocalHost(hostToBind)) {
  52. hostToBind = getLocalHost();
  53. }
  54. }
  55. }
  56. }
  57. map.put(BIND_IP_KEY, hostToBind);
  58. // registry ip is not used for bind ip by default
  59. String hostToRegistry = getValueFromConfig(protocolConfig, DUBBO_IP_TO_REGISTRY);
  60. if (hostToRegistry != null && hostToRegistry.length() > 0 && isInvalidLocalHost(hostToRegistry)) {
  61. throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
  62. } else if (StringUtils.isEmpty(hostToRegistry)) {
  63. // bind ip is used as registry ip by default
  64. hostToRegistry = hostToBind;
  65. }
  66. map.put(ANYHOST_KEY, String.valueOf(anyhost));
  67. return hostToRegistry;
  68. }

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

  1. public static String getSystemProperty(String key) {
  2. String value = System.getenv(key);
  3. if (StringUtils.isEmpty(value)) {
  4. value = System.getProperty(key);
  5. }
  6. return value;
  7. }

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

  1. 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端口。

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

看一下zk起来了

  1. niuxinli@niuxinli-B450M-DS3H:~/dubbo-samples-docker$ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 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

  1. FROM openjdk:8-jdk-alpine
  2. ADD app.jar app.jar
  3. ENV JAVA_OPTS=""
  4. ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar

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

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

正常启动镜像

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

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

传入环境变量重新启动,

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

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

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

看一下zk起来了

  1. niuxinli@niuxinli-B450M-DS3H:~/docker_dubbo_demo/dubbo-samples/dubbo-samples-docker$ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 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。

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: dubbo-docker
  5. labels:
  6. run: dubbo
  7. spec:
  8. type: NodePort
  9. ports:
  10. - port: 20880
  11. targetPort: 20880
  12. nodePort: 30001
  13. selector:
  14. run: dubbo-docker
  15. ---
  16. apiVersion: apps/v1
  17. kind: Deployment
  18. metadata:
  19. name: dubbo-docker
  20. spec:
  21. selector:
  22. matchLabels:
  23. run: dubbo
  24. replicas: 4
  25. template:
  26. metadata:
  27. labels:
  28. run: dubbo
  29. spec:
  30. containers:
  31. - name: dubbo-docker
  32. image: nxlhero/dubbo-demo
  33. env:
  34. - name: DUBBO_IP_TO_REGISTRY
  35. valueFrom:
  36. fieldRef:
  37. fieldPath: status.hostIP
  38. - name: DUBBO_PORT_TO_REGISTRY
  39. value: "30001"
  40. tty: true
  41. ports:
  42. - containerPort: 20880

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

  1. env:
  2. - name: DUBBO_IP_TO_REGISTRY
  3. valueFrom:
  4. fieldRef:
  5. fieldPath: status.hostIP
  6. - name: DUBBO_PORT_TO_REGISTRY
  7. value: "30001"

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

原文链接:http://www.cnblogs.com/nxlhero/p/11789006.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号