Spring Cloud Kubernetes服务发现


Geecon 2018年, Mauricio Salatino谈到了Spring Cloud Kubernetes项目。这个项目有很多好主意。令人印象最深刻的是Spring Cloud K8如何进行服务发现。它的工作原理如下:

  • 在K8s集群中,没有必要拥有Eureka。K8s中的ETCD拥有所有必要的信息。
  • 您的应用程序将通过指定的K8s服务名称联系K8s API服务器以获取端点信息。
  • 然后可以通过Feign调用返回的服务。
  • 要让DiscoveryClient正常运行,您需要做的 就是将Kubernetes服务名称与spring.application.name属性对齐。

听起来很简单,所以让我们测试吧!

服务发现,相同的命名空间不同的pod
让我们看看那一个Spring Cloud Application:

package com.example.service2.mvc;

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    public class Controller {

        @GetMapping(path = "/service2")
        public String respondPoint() {
            return
"Hello I'm service2";
        }
    }

配置中:spring.application.name.

spring.application.name=service2-chart
server.port=8081

现在让我们构建并将应用程序安装到Kubernetes集群中。 为了使它变得美观和流畅,准备了helm chart.

  • $ eval $(minikube docker-env)
  • $ mvn clean install
  • $ helm install -n service2-chart service2-chart-0.1.0.tgz

helm chart应该安装一切,但让我们检查一下服务:
$ kubectl -n default get services
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          58d
service2-chart   NodePort    10.100.48.206   <none>        8081:30467/TCP   18s

好的,我们将Kubernetes服务名称与spring.application.name对齐。 

准备另外一个Spring Boot调用service2-chart app/K8s service.
首先,使用Feign客户端,请注意我们只需要知道K8s服务名称,就是这样:

 package com.example.service1.mvc;

    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;

    @FeignClient(name = "service2-chart")
    public interface Service2Client {

        @GetMapping(
"/service2")
        String invokeService2();
    }

现在调用这个 service2-chart K8s 远程服务:

  package com.example.service1.mvc;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController 
    public class Controller {

        @Autowired
        private Service2Client service2Client;

        @GetMapping(path = "/service1")
        public String respondPoint() {
            final String service2Output = service2Client.invokeService2();
            return
"Hello I'm Service1 ->"+service2Output;
        }
    }

一切准备就绪,让我们测试一下:

  • $ eval $(minikube docker-env)
  • $ mvn clean install
  • $ helm install -n service1-chart service1-chart-0.1.0.tgz

Helm应该再次安装所有内容,这是所需的输出:

$ kubectl -n default get services  
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          58d
service1-chart   NodePort    10.101.7.4      <none>        8082:32391/TCP   3m
service2-chart   NodePort    10.100.48.206   <none>        8081:30467/TCP   17h

Helm charts 已经安装:

$ helm list
NAME            REVISION    UPDATED                     STATUS      CHART                   APP VERSION NAMESPACE
service1-chart  1           Sun Dec 23 18:03:30 2018    DEPLOYED    service1-chart-0.1.0    1.0         default  
service2-chart  1           Sun Dec 23 01:05:17 2018    DEPLOYED    service2-chart-0.1.0    1.0         default

现在调用/service1 端点,这个端点会调用service2-chart K8s服务:

$ minikube service service1-chart --url
http://192.168.99.100:32391

tomask79:spring-service1 tomask79$ curl http:
//192.168.99.100:32391/service1
{
"timestamp":"2018-12-23T19:57:12.020+0000",
"status":500,"error":"Internal Server Error",
"message":"Error creating bean with name 'ribbhttps://github.com/fabric8io/kubernetes-client]fabric8 kubernetes客户端[/url] 无法在默认命名空间中检索端点数据。
要理解为什么让我们重复关于Kubernetes的基本规则:
<ul>
<li>在pod内运行的每个进程都在[url=https:
//kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/]服务帐户[/url]标识下运行。
<li>如果未指定,则每个pod都在“default”服务账户下运行。
<li>为了使pod获得K8s端点元数据,对应的默认服务账户的特权 需要获得至少是“view”clusterrole。 
</ul>
只要
"view" readonly [url=https://kubernetes.io/docs/reference/access-authn-authz/rbac/user-facing-roles]clusterrole[/url] 就好:

[code]$ kubectl create rolebinding default-view-binding --clusterrole=view --serviceaccount=default:default --namespace=default
rolebinding.rbac.authorization.k8s.io/default-view-binding created

再次curl调用/service1 端点:

$ curl http://192.168.99.100:32391/service1
Hello I'm Service1 ->Hello I'm service2

成功,我们通过一个K8s服务的名字调用在同一名称空间内的服务!

服务发现,不同的命名空间,不同的pods
这一次让我们通过知道它的名字来尝试调用生活在分离命名空间中的kubernetes服务 。只需很少的编码更改即可支持此功能!

首先让我们创建名为“test”的新命名空间:

 $ kubectl create namespace test

现在让我们将service2-chart安装到新的命名空间中(这里你需要helm ):

$ helm install -n service2-chart --namespace test service2-chart-0.1.0.tgz

下一步是告诉service1中的ribbon在哪里查找刚刚安装的service2-chart服务。 
默认情况下,它列出当前名称空间中的端点。要告诉他在另一个名称空间中搜索service2-chart, 您需要将以下属性添加到application.properties中:

service2-chart.ribbon.KubernetesNamespace=test

现在让我们重建并重新安装service1应用程序到K8s集群,默认命名空间:

  • $ mvn clean install
  • $ helm package ./service1-chart --debug
  • $ helm install -n service1-chart service1-chart-0.1.0.tgz

这是测试之前所需的输出(注意命名空间):

$ helm list
NAME            REVISION    UPDATED                     STATUS      CHART                   APP VERSION NAMESPACE
service1-chart  1           Sun Dec 23 22:45:24 2018    DEPLOYED    service1-chart-0.1.0    1.0         default  
service2-chart  1           Sun Dec 23 22:09:00 2018    DEPLOYED    service2-chart-0.1.0    1.0         test

好的,让我们尝试调用service1-chart服务,该服务在另一个命名空间中调用service2-chart:

$ minikube service service1-chart --url
http://192.168.99.100:30176
tomask79:spring-service1 tomask79$ curl http:
//192.168.99.100:30176/service1
{
"timestamp":"2018-12-23T21:47:00.649+0000","status":500,"error":"Internal Server Error",
"message":"Error creating bean with name 'ribbview --serviceaccount=default:default
clusterrolebinding.rbac.authorization.k8s.io/test-view created

现在让我们再次调用service1-chart:

$ curl http://192.168.99.100:30176/service1
Hello I'm Service1 ->Hello I'm service2


好的,这次我们通过知道它的名字来调用生活在分离命名空间中的kubernetes服务 。我喜欢这种方法,因为您可以在不更改代码的情况下将您的微服务服务发现机制迁移 到kubernetes。

点击标题见原文!