服务对服务的调用模式--使用Anthos服务网的多集群

143 阅读4分钟

服务对服务的调用模式--使用Anthos服务网的多集群

目标架构

我的目标架构是这样的:

两个不同的应用被托管在不同可用区的两个独立的Kubernetes集群上,一个集群的服务(称为 "调用者")调用另一个集群的服务(称为 "生产者")。

创建集群和配置Anthos服务网格

在这里托管了一个gist,其中包含了建立的步骤:

  • 在us-west1-a和us-central1-a区建立2个GKE集群
  • 在每个集群上安装Anthos服务网格
  • 将集群注册为集中管理的集群

服务安装

假设现在有两个GKE集群,第一个集群持有呼叫器和一个入口网关,以使用户能够访问呼叫器的用户界面。这是通过一个部署描述符来实现的,呼叫器的部署描述符看起来是这样的。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-caller-v1
  labels:
    app: sample-caller
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-caller
      version: v1
  template:
    metadata:
      labels:
        app: sample-caller
        version: v1
    spec:
      serviceAccountName: sample-caller-sa
      containers:
        - name: sample-caller
          image: us-docker.pkg.dev/sample/docker-repo/sample-caller:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          securityContext:
            runAsUser: 1000
          resources:
            requests:
              memory: "256Mi"
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 3
            periodSeconds: 3
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080

我复制了整个yaml文件,只是为了演示,文件中没有什么值得注意的地方。

按照同样的思路,生产者应用程序被部署到第二个集群上。

呼叫者对生产者的调用--笨拙的方法

现在,两个服务都到位了,它们如何相互调用。让我们从 "调用者 "试图使用 "样本-生产者 "的主机名(样本-生产者恰好是生产者应用程序安装在第二个集群中的Kubernetes服务的名称)来访问 "生产者 "开始。这失败了,信息看起来像这样:

这是合理的,因为服务 "sample-producer "不存在于集群1,只存在于集群2。

我的下一个想法是,如果我人为地添加一个名为 "sample-producer "的Kubernetes服务,检查其行为:

apiVersion: v1
kind: Service
metadata:
  name: sample-producer
  labels:
    app: sample-producer
    service: sample-producer
spec:
  ports:
    - port: 8080
      name: http
  selector:
    app: sample-producer

令人惊讶的是,在 "集群1 "中添加这样的服务确实有效,而且sample-producer也能干净地解析,尽管运行 "Producer "的pods在集群2中!此外,为从 "呼叫者 "到 "生产者 "的调用添加一个虚拟服务和目标规则,超时和断路器也干净地工作。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sample-producer-dl
  namespace: istio-apps
spec:
  host: sample-producer.istio-apps.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
    outlierDetection:
      consecutive5xxErrors: 3
      interval: 15s
      baseEjectionTime: 15s
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sample-producer-route
  namespace: istio-apps
spec:
  hosts:
    - "sample-producer.istio-apps.svc.cluster.local"
  http:
    - timeout: 5s
      route:
        - destination:
            host: sample-producer
            port:
              number: 8080

为什么这样做对我来说仍然是个谜。现在让我来谈谈使之工作的正确方法。

呼叫者对生产者的呼叫--正确的方法

正确的方法是使用Anthos的一个功能,即多集群服务,并在这篇博文这篇如何使用的文章 中作了详细的描述,以便让服务之间的调用在集群中工作。

简而言之,如果在集群2中定义了一个 "ServiceExport "资源,并且在集群1中存在相同的命名空间,那么该服务将使用 "service-name.namespace.svc.clubsterset.local "形式的主机名进行解析,在我的例子中,这个主机名映射为 "sample-producer.istio-apps.svc.clubsterset.local"!。ServiceExport资源看起来像这样。

kind: ServiceExport
apiVersion: net.gke.io/v1
metadata:
  namespace: istio-apps
  name: sample-producer

这是我对调用者所做的唯一改变,它不再使用 "sample-producer "来调用Producer,而是使用 "sample-producer.istio-apps.svc.clubsterset.local "的主机名,一切都解决得很干净,调用继续在集群中运行。

从调用者处查看:

来自生产者的观点:

结论

我希望这在一定程度上澄清了服务与服务之间的调用是如何在多个集群中实现的,甚至是跨区域的。Anthos服务网处理了所有的底层机制,从调用者的角度来看,所改变的只是调用服务的主机名。

有一些小问题,例如,为了让相互TLS在各集群间工作,必须创建特定的资源,我将把它作为一个练习,如果觉得难以处理,请联系我们。