基于 istio 的 k8s 多集群实践

2023-04-05 17:00:34 +08:00
 GopherDaily

假设我们在阿里云上有一个托管的 k8s 集群, cn-shanghai, 在线下的某个机房有一个自己维护的 k8s 集群, wuxi. 现在我们希望通过基于 Istio 的 Mesh 来实现这两个集群之间的互联互通.

效果展示

对最终效果, 我们希望:

我们先在 cn-shanghai 部署 helloworld-v1, 随后在 wuxi 尝试调用 helloworld.

✗ kubectl --context cn-shanghai get pods | grep helloworld
helloworld-v1-776f57d5f6-sxjtw                            2/2     Running            0               162m
✗ kubectl --context wuxi exec -ti sleep-5597f78777-hq7vk -- curl helloworld.dev:5000/hello
Hello version: v1, instance: helloworld-v1-776f57d5f6-sxjtw

而当我们在 wuxi 部署 helloworld-v2 时, 从 wuxi 发布的调用均匀分布在两个集群.

✗ kubectl --context wuxi get pods | grep helloworld
helloworld-v2-7bd9f44595-db94r   2/2     Running   0          14s
✗ kubectl --context wuxi exec -ti sleep-5597f78777-hq7vk -- curl helloworld.dev:5000/hello
Hello version: v1, instance: helloworld-v1-776f57d5f6-sxjtw
✗ kubectl --context wuxi exec -ti sleep-5597f78777-hq7vk -- curl helloworld.dev:5000/hello
Hello version: v2, instance: helloworld-v2-7bd9f44595-db94r

显然, 当本集群存在被调用方时, 我们更希望调用尽量集中在集群内. 通过选择合适的 LocalityLoadBalancerSetting, 很轻松就可以实现类似需求.

✗ kubectl --context wuxi apply -f demo-dr.yaml
destinationrule.networking.istio.io/helloworld created
✗ kubectl --context wuxi exec -ti sleep-5597f78777-hq7vk -- curl helloworld.dev:5000/hello
Hello version: v2, instance: helloworld-v2-7bd9f44595-db94r
✗ kubectl --context wuxi exec -ti sleep-5597f78777-hq7vk -- curl helloworld.dev:5000/hello
Hello version: v2, instance: helloworld-v2-7bd9f44595-db94r
✗ kubectl --context wuxi exec -ti sleep-5597f78777-hq7vk -- curl helloworld.dev:5000/hello
Hello version: v2, instance: helloworld-v2-7bd9f44595-db94r

Istio 的多集群方案

Istio 在文档中介绍了多种多集群的方案, 我们选择的是网络隔离下的多主, Install Multi-Primary on different networks.

多主架构中, 每个集群都拥有独立的 control plane (istiod) 来管理本集群的 mesh. 相较于单集群架构的主要区别在于:

对熟悉 Istio 的同学来说, 上图的架构应该是非常容易理解的, 唯一需要深入探究的应该是 Network Gateway 相关内容. Istio 通过几个方面实现了网络隔离下的跨集群调用:

wuxi 的 NetworkGateway 监听了 15443 端口并针对相关服务做路由.

k --context wuxi get -n ingress-cluster pods -l app=cluster-wuxi
NAME                            READY   STATUS    RESTARTS   AGE
cluster-wuxi-6fdbb84466-zgwwr   1/1     Running   0          3h7m
✗ istioctl --context wuxi proxy-config listener cluster-wuxi-6fdbb84466-zgwwr.ingress-cluster --port 15443 | grep helloworld
0.0.0.0 15443 SNI: outbound_.5000_._.helloworld.dev.svc.cluster.local; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2                       Cluster: outbound_.5000_._.helloworld.dev.svc.cluster.local

cn-shanghai 内 Envoy 接收到 Endpoint 中, wuxi 机房 Pod 的 IP 都被替换为 NetworkGateway 的对外地址.

✗ kubectl --context wuxi get -n ingress-cluster svc cluster-wuxi
NAME           TYPE       CLUSTER-IP      EXTERNAL-IP    PORT(S)                                                           AGE
cluster-wuxi   NodePort   172.23.234.78   10.18.10.196   15021:32125/TCP,15443:30880/TCP,15012:30500/TCP,15017:32230/TCP   3h50m
✗ istioctl --context cn-shanghai proxy-config endpoints sleep-75b85f5796-f6k89 --cluster "outbound|5000||helloworld.dev.svc.cluster.local"
ENDPOINT                STATUS      OUTLIER CHECK     CLUSTER
10.18.10.196:30880      HEALTHY     OK                outbound|5000||helloworld.dev.svc.cluster.local
172.20.171.185:5000     HEALTHY     OK                outbound|5000||helloworld.dev.svc.cluster.local

Envoy LB 相关的配置中, 支持 priority 和 weight 两个属性, 当指定本地优先时, Istio 在向 Envoy 推送信息时会将集群内 Endpoint 的 priority 设置为 1, 进而实现本地优先.

istioctl --context wuxi proxy-config endpoints sleep-5597f78777-hq7vk --cluster "outbound|5000||helloworld.dev.svc.cluster.local" -o json | jq '.[].hostStatuses[] | [.address, .priority]'
[
  {
    "socketAddress": {
      "address": "172.22.0.30",
      "portValue": 5000
    }
  },
  null
]
[
  {
    "socketAddress": {
      "address": "10.0.139.33",
      "portValue": 32652
    }
  },
  1
]

坑与妥协

我们线下和云上的两个集群, Node 之间是互通的, 所以并不一定需要将 Network Gateway 的 Service 设置为 LoadBalancer. 当你选择直接使用 NodePort 时, Istio 允许你通过注解 traffic.istio.io/nodeSelector 来申明对应的节点. 但是需要注意两点:

ExternalIP 这个要求基本使得这个方案不需要任何的操作性. 换一个思路, 我们选择了部分固定的节点, 将对应 IP 直接设置为 Service 的 externalIPs, 这时候将 traffic.istio.io/nodeSelector 设置为 {} 即可.

和大部分 k8s 使用者一样, 我们依赖 CoreDNS 提供的域名解析服务, 而 Istio 并没有直接解决这个问题. 已有的一些解决方案在复杂度和成熟度上并没有特别令人满意, 所以我们直接土法炼钢, 在 CI/CD 流程中直接同步 Service 到所有集群.

1620 次点击
所在节点    Kubernetes
1 条回复
GopherDaily
2023-04-06 13:08:39 +08:00
up 下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/930019

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX