多集群下的故障排除

本页介绍如何将 Istio 部署到多个集群和/或多个网络的问题。 开始之前,请确保您已经完成了多集群安装 的要求并且已经阅读了部署模型指南。

跨集群负载均衡

多网络安装最常见同时也是最广泛的问题是无法实现跨集群负载均衡。 通常表现为仅看到来自服务的集群本地实例(cluster-local instance)的响应:

$ for i in $(seq 10); do kubectl --context=$CTX_CLUSTER1 -n sample exec sleep-dd98b5f48-djwdw -c sleep -- curl -s helloworld:5000/hello; done
Hello version: v1, instance: helloworld-v1-578dd69f69-j69pf
Hello version: v1, instance: helloworld-v1-578dd69f69-j69pf
Hello version: v1, instance: helloworld-v1-578dd69f69-j69pf
...

按照验证多集群安装指南操作完毕后, 我们期待同时看到 v1v2 响应,这表示流量同时到达了两个集群。

造成响应仅来自集群本地实例的原因有很多:

连接和防火墙问题

在某些环境中,防火墙阻止集群之间的流量可能并不明显。可能出现 ICMP(ping)流量成功, 但 HTTP 和其他类型的流量失败的情况。这可能表现为超时,或者在某些情况下表现为更令人困惑的错误, 例如:

upstream connect error or disconnect/reset before headers. reset reason: local reset, transport failure reason: TLS error: 268435612:SSL routines:OPENSSL_internal:HTTP_REQUEST

虽然 Istio 提供了服务发现功能简化了这个问题, 但如果每个集群中的 Pod 位于没有 Istio 的单个网络上,跨集群流量仍然应该成功。 要排除 TLS/mTLS 问题,您可以使用不带 Istio Sidecar 的 Pod 进行手动测试连通性。

在每个集群中,为此测试创建一个新的命名空间。要启用 Sidecar 注入:

$ kubectl create --context="${CTX_CLUSTER1}" namespace uninjected-sample
$ kubectl create --context="${CTX_CLUSTER2}" namespace uninjected-sample

然后部署验证多集群安装中使用的应用程序:

$ kubectl apply --context="${CTX_CLUSTER1}" \
    -f samples/helloworld/helloworld.yaml \
    -l service=helloworld -n uninjected-sample
$ kubectl apply --context="${CTX_CLUSTER2}" \
    -f samples/helloworld/helloworld.yaml \
    -l service=helloworld -n uninjected-sample
$ kubectl apply --context="${CTX_CLUSTER1}" \
    -f samples/helloworld/helloworld.yaml \
    -l version=v1 -n uninjected-sample
$ kubectl apply --context="${CTX_CLUSTER2}" \
    -f samples/helloworld/helloworld.yaml \
    -l version=v2 -n uninjected-sample
$ kubectl apply --context="${CTX_CLUSTER1}" \
    -f samples/sleep/sleep.yaml -n uninjected-sample
$ kubectl apply --context="${CTX_CLUSTER2}" \
    -f samples/sleep/sleep.yaml -n uninjected-sample

使用 -o wide 参数验证 cluster2 集群中是否有一个正在运行的 helloworld Pod, 这样我们就可以获得 Pod IP:

$ kubectl --context="${CTX_CLUSTER2}" -n uninjected-sample get pod -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
helloworld-v2-54df5f84b-z28p5    1/1     Running   0          43s   10.100.0.1   node-1   <none>           <none>
sleep-557747455f-jdsd8           1/1     Running   0          41s   10.100.0.2   node-2   <none>           <none>

记下 helloworldIP 地址列。在本例中,它是 10.100.0.1

$ REMOTE_POD_IP=10.100.0.1

接下来,尝试从 cluster1 中的 sleep Pod 直接向此 Pod IP 发送流量:

$ kubectl exec --context="${CTX_CLUSTER1}" -n uninjected-sample -c sleep \
    "$(kubectl get pod --context="${CTX_CLUSTER1}" -n uninjected-sample -l \
    app=sleep -o jsonpath='{.items[0].metadata.name}')" \
    -- curl -sS $REMOTE_POD_IP:5000/hello
Hello version: v2, instance: helloworld-v2-54df5f84b-z28p5

正常的情况下,应该只有来自 helloworld-v2 的响应。重复这些步骤, 将流量从 cluster2 发送到 cluster1

如果成功,则可以排除连接问题。如果失败,则问题的原因可能超出了 Istio 配置的范围。

地域负载均衡

地域负载均衡 会引导客户端访问最近的服务。如果集群分布于不同地理位置(地区/区域),本地负载均衡将优先选用本地实例提供服务, 这与预期相符。而如果禁用了本地负载均衡或者是集群处于同一地理位置,那就可能还存在其他问题。

受信配置

与集群内通信一样,跨集群通信依赖于代理之间公共的且可信任的根证书颁发机构(root)。 默认情况下 Istio 使用自身单独生成的根证书颁发机构。对于多集群的情况, 我们必须手动配置公共的且可信任的根证书颁发机构。阅读下面的插入式证书章节或者参考 身份和信任模型了解更多细节。

插入式证书

您可以通过比较每个集群中的根证书的方式来验证受信配置是否正确:

$ diff \
   <(kubectl --context="${CTX_CLUSTER1}" -n istio-system get secret cacerts -ojsonpath='{.data.root-cert\.pem}') \
   <(kubectl --context="${CTX_CLUSTER2}" -n istio-system get secret cacerts -ojsonpath='{.data.root-cert\.pem}')

如果根证书不匹配或 Secret 根本不存在, 您可以按照插件 CA 证书指南进行操作, 确保对每个集群都执行这些步骤。

逐步分析

如果您已经阅读了上面的章节,但问题仍没有解决,那么可能需要进行更深入的探讨。

下面这些步骤假定您已经完成了 HelloWorld 认证指南, 并且确保 helloworldsleep 服务已经在每个集群中被正确部署。

针对每个集群,找到 sleep 服务对应的 helloworldendpoints

$ istioctl --context $CTX_CLUSTER1 proxy-config endpoint sleep-dd98b5f48-djwdw.sample | grep helloworld

故障诊断信息因流量来源的集群不同而不同:

$ istioctl --context $CTX_CLUSTER1 proxy-config endpoint sleep-dd98b5f48-djwdw.sample | grep helloworld
10.0.0.11:5000                   HEALTHY     OK                outbound|5000||helloworld.sample.svc.cluster.local

仅显示一个 endpoints,表示控制平面无法从从集群读取 endpoints。 验证远程 Secret 是否配置正确。

$ kubectl get secrets --context=$CTX_CLUSTER1 -n istio-system -l "istio/multiCluster=true"
  • 如果缺失 Secret,则创建一个。
  • 如果存在 Secret,则:
    • 查看配置,确保使用集群名作为远程 kubeconfig 的数据键(data key)。
    • 如果 Secret 看起来没问题,检查 istiod 的日志,以确定是连接还是权限问题导致无法连接远程 Kubernetes API。该日志可能包括 Failed to add remote cluster from secret 信息和对应的错误原因。
这些信息有用吗?
您是否有更多建议和改进意见?

感谢您的反馈!