使用ASM东西向网关实现多集群跨网络互通

阿里云ASM支持将多个ACK集群整合在一个ASM实例中,为分布广泛的服务提供统一的管理和运维平台。ASM东西向网关提供了更为灵活的多集群网络互联方案,本文将介绍如何在多集群场景下利用东西向网关打通集群通信。

背景信息

ASM支持多集群模式,即用户可以在同一个ASM实例中加入多个ACK集群。 您可以在ASM中加入多个不同网络下的ACK集群,若多集群的网络不具备打通三层网络的条件(设施限制、CIDR冲突、费用等原因),则可以利用ASM网关打通集群网络,使用ASM东西向网关可以灵活使用公、私有网络将集群网络联通,无感解决地址冲突问题,实现多集群统一的流量治理、安全防护以及全链路可观测。本文将以Sleep应用跨集群访问httpbin为例介绍如何在ASM环境下利用东西向网关配置跨网络多集群互通。

image

能力优势

ASM 1.22及以上版本提供的东西向网关完整实现了七层客户端负载均衡,跨集群调用场景下,所有路由能力与非跨集群负载均衡完全对齐。

前提条件

  • 已创建ASM实例,版本不低于1.22。具体操作,请参见创建ASM实例

  • 已添加多个集群到ASM实例。具体操作,请参见添加集群到ASM实例。(本文示例为两个)。

  • 已开启ASM自动注入。具体操作,请参见启用自动注入

  • 服务之间跨集群访问需要满足以下两个条件之一:

    • 开启了ASM的在ASM中使用DNS代理(推荐)。

    • 手动在客户端所在的集群创建一个和服务端集群相同的目标Service。

步骤一:为ASM控制面绑定公网IP

参考为ASM控制平面绑定或解绑EIP,为ASM控制面绑定EIP。

步骤二:为集群指定网络配置,并启用东西向网关

您可以为每个集群指定一个逻辑网络。同一个逻辑网络之间的服务可以直接互相访问,不同逻辑网络之间的服务需要通过东西向网关才可以访问。

  1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

  2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择集群与工作负载管理 > Kubernetes集群

  3. 单击多集群网络配置按钮,参考如下方式配置多集群网络。

    • 为ACK-1指定所属逻辑网络为network1。

    • 为ACK-2指定所属逻辑网络为network2,并且在ACK-2中启用东西向网关访问

    image

应用上述配置后,ASM会为您在ACK-2中创建一个默认东西向网关,该网关具有公网IP。ACK-1中的服务将会自动通过这个东西向网关访问到ACK-2中的服务,并且这条链路默认会启用mTLS加密。

您可以通过ASM集群的kubeconfig查看东西向网关的定义,东西向网关有一个特殊的名称:asm-cross-network-${ACK ID}。您可以根据自己的需求,自行调节网关的资源和副本数等配置。

说明

目前东西向网关是一个TCP Proxy,无法进行L7负载均衡。可能在某些情况下存在负载不均衡的情况。

步骤三:验证东西向流量

上述步骤中的网络配置需要在业务Pod启动时生效。如果修改网络配置之前已经启动了业务Pod,则需要重启业务Pod。

  1. 在ACK-1中创建Sleep应用,示例YAML配置如下:

    sleep.yaml

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: sleep
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sleep
      labels:
        app: sleep
        service: sleep
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: sleep
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sleep
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sleep
      template:
        metadata:
          labels:
            app: sleep
        spec:
          terminationGracePeriodSeconds: 0
          serviceAccountName: sleep
          containers:
          - name: sleep
            image: registry.cn-hangzhou.aliyuncs.com/acs/curl:8.1.2
            command: ["/bin/sleep", "infinity"]
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - mountPath: /etc/sleep/tls
              name: secret-volume
          volumes:
          - name: secret-volume
            secret:
              secretName: sleep-secret
              optional: true
    ---
  2. 在ACK-2中创建httpbin应用,示例YAML配置如下:

    httpbin.yaml

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: httpbin
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin
      labels:
        app: httpbin
        service: httpbin
    spec:
      ports:
      - name: http
        port: 8000
        targetPort: 80
      selector:
        app: httpbin
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: httpbin
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: httpbin
          version: v1
      template:
        metadata:
          labels:
            app: httpbin
            version: v1
        spec:
          serviceAccountName: httpbin
          containers:
          - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/httpbin:0.1.0
            imagePullPolicy: IfNotPresent
            name: httpbin
            ports:
            - containerPort: 80
  3. 从Sleep对应的Pod中访问httpbin应用(使用ACK-1的kubeconfig)。

    1. 获取Sleep Pod名称。

      kubectl get pod | grep sleep
    2. 在Sleep中使用curl访问httpbin。

      kubectl exec ${sleep pod名称} -- curl httpbin:8000/status/418

      此时可以看到访问成功,输出如下所示:

        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                       Dload  Upload   Total   Spent    Left  Speed
      100   135  100   135    0     0  16075      0 --:--:-- --:--:-- --:--:-- 16875
      
          -=[ teapot ]=-
      
             _...._
           .'  _ _ `.
          | ."` ^ `". _,
          \_;`"---"`|//
            |       ;/
            \_     _/
              `"""`
  4. 验证Sleep通过东西向网关访问了httpbin。

    1. 查看Sleep Pod的日志(使用ACK-1的kubeconfig)。

      kubectl logs ${sleep pod名称} -c istio-proxy | tail -1

      输出如下所示:

      {"authority_for":"httpbin:8000","bytes_received":"0","bytes_sent":"135","downstream_local_address":"xxx.xxx.xxx.xx:8000","downstream_remote_address":"xx.x.xxx.xxx:xxxxx","duration":"7","istio_policy_status":"-","method":"GET","path":"/status/418","protocol":"HTTP/1.1","request_id":"08dc43e9-60c8-4f2f-910a-b727172ce311","requested_server_name":"-","response_code":"418","response_flags":"-","route_name":"default","start_time":"2024-05-23T10:06:27.289Z","trace_id":"-","upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local","upstream_host":"xxx.xx.xxx.xxx:15443","upstream_local_address":"xx.x.xxx.xxx:60248","upstream_response_time":"7","upstream_service_time":"7","upstream_transport_failure_reason":"-","user_agent":"curl/8.1.2","x_forwarded_for":"-"}

      其中的upstream_host字段标识Sleep Pod直接访问的目标,可以看到访问的端口是1544315443是东西向网关专用的端口。

    2. 查看东西向网关的日志(使用ACK-2的kubeconfig)。

      首先获取东西向网关Pod。

      kubectl -n istio-system get pod | grep asm-cross-network
      
      istio-asm-cross-network-c0859be51XXX   1/1     Running   0          20h
      istio-asm-cross-network-c0859be51XXX   1/1     Running   0          20h

      可以看到默认有两个东西向网关的Pod。您可以分别查看这两个Pod的日志,可以看到类似的访问日志。

       kubectl logs istio-asm-cross-network-c0859be51XXX -n istio-system  | tail -1
      
      {"authority_for":"-","bytes_received":"xxxx","bytes_sent":"xxxx","downstream_local_address":"xx.xx.x.xx:15443","downstream_remote_address":"xx.xx.xx.xx:xxxxx","duration":"1568569","istio_policy_status":"-","method":"-","path":"-","protocol":"-","request_id":"-","requested_server_name":"outbound_.8000_._.httpbin.default.svc.cluster.local","response_code":"0","response_flags":"-","route_name":"-","start_time":"2024-05-23T08:41:16.618Z","trace_id":"-","upstream_cluster":"outbound_.8000_._.httpbin.default.svc.cluster.local","upstream_host":"xx.xx.xx.xxx:80","upstream_local_address":"xx.x.xx.xx:xxxxx","upstream_response_time":"-","upstream_service_time":"-","upstream_transport_failure_reason":"-","user_agent":"-","x_forwarded_for":"-"}