多集群故障排除
本页介绍如何解决部署到多个集群和/或网络的 Istio 问题。在阅读本页之前,请完成多集群安装步骤,并阅读部署模型指南。
跨集群负载均衡
多网络安装中最常见也是最广泛的问题是跨集群负载均衡不起作用。通常情况下,这会表现为只看到服务集群本地实例的响应。
$ for i in $(seq 10); do kubectl --context=$CTX_CLUSTER1 -n sample exec curl-dd98b5f48-djwdw -c curl -- 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
...
按照验证多集群安装指南进行操作时,我们应该期望看到 v1
和 v2
响应,这表示流量已发送到两个集群。
此问题可能有许多原因。
连接和防火墙问题
在某些环境中,可能无法明显地发现防火墙阻止了集群之间的流量。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 边车的 Pod 执行手动流量测试。
在每个集群中,为该测试创建一个新的命名空间。不要启用边车注入。
$ 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/curl/curl.yaml -n uninjected-sample
$ kubectl apply --context="${CTX_CLUSTER2}" \
-f samples/curl/curl.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
curl-557747455f-jdsd8 1/1 Running 0 41s 10.100.0.2 node-2 <none> <none>
helloworld-v2-54df5f84b-z28p5 1/1 Running 0 43s 10.100.0.1 node-1 <none> <none>
记下 helloworld
的 IP
列。在本例中,它是 10.100.0.1
。
$ REMOTE_POD_IP=10.100.0.1
接下来,尝试将流量从 cluster1
中的 curl
Pod 直接发送到此 Pod IP。
$ kubectl exec --context="${CTX_CLUSTER1}" -n uninjected-sample -c curl \
"$(kubectl get pod --context="${CTX_CLUSTER1}" -n uninjected-sample -l \
app=curl -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 配置范围内。
本地负载均衡
区域负载均衡可用于使客户端优先将流量发送到最近的目标。如果集群位于不同的区域(区域/区域),区域负载均衡将优先考虑本地集群,并且工作正常。如果禁用了区域负载均衡,或者集群位于同一区域,则可能存在其他问题。
信任配置
跨集群流量与集群内流量一样,依赖于代理之间共有的信任根。默认的 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}')
如果根证书不匹配,或者根本不存在该机密,则可以按照插入 CA 证书指南进行操作,确保对每个集群都运行这些步骤。
分步诊断
如果您已经完成了上述部分,但仍然遇到问题,则需要深入调查。
以下步骤假设您按照HelloWorld 验证进行操作。在继续之前,请确保 helloworld
和 curl
都已部署到每个集群中。
从每个集群中查找 curl
服务针对 helloworld
的端点。
$ istioctl --context $CTX_CLUSTER1 proxy-config endpoint curl-dd98b5f48-djwdw.sample | grep helloworld
故障排除信息因流量源集群而异
$ istioctl --context $CTX_CLUSTER1 proxy-config endpoint curl-dd98b5f48-djwdw.sample | grep helloworld
10.0.0.11:5000 HEALTHY OK outbound|5000||helloworld.sample.svc.cluster.local
仅显示一个端点,表示控制平面无法从远程集群读取端点。验证是否已正确配置远程机密。
$ kubectl get secrets --context=$CTX_CLUSTER1 -n istio-system -l "istio/multiCluster=true"
- 如果缺少机密,请创建它。
- 如果机密存在
- 查看机密中的配置。确保集群名称用作远程
kubeconfig
的数据键。 - 如果机密看起来正确,请检查
istiod
的日志,以查找连接或权限问题,这些问题会导致无法访问远程 Kubernetes API 服务器。日志消息可能包含Failed to add remote cluster from secret
以及错误原因。
- 查看机密中的配置。确保集群名称用作远程
$ istioctl --context $CTX_CLUSTER2 proxy-config endpoint curl-dd98b5f48-djwdw.sample | grep helloworld
10.0.1.11:5000 HEALTHY OK outbound|5000||helloworld.sample.svc.cluster.local
仅显示一个端点,表示控制平面无法从远程集群读取端点。验证是否已正确配置远程机密。
$ kubectl get secrets --context=$CTX_CLUSTER1 -n istio-system -l "istio/multiCluster=true"
- 如果缺少机密,请创建它。
- 如果机密存在,并且端点是主集群中的 Pod
- 查看机密中的配置。确保集群名称用作远程
kubeconfig
的数据键。 - 如果机密看起来正确,请检查
istiod
的日志,以查找连接或权限问题,这些问题会导致无法访问远程 Kubernetes API 服务器。日志消息可能包含Failed to add remote cluster from secret
以及错误原因。
- 查看机密中的配置。确保集群名称用作远程
- 如果机密存在,并且端点是远程集群中的 Pod
- 代理从远程集群内的 istiod 读取配置。当远程集群具有集群内 istiod 时,它只用于边车注入和 CA。您可以通过查找
istio-system
命名空间中的名为istiod-remote
的服务来验证是否是这种情况。如果缺少,请重新安装,确保设置values.global.remotePilotAddress
。
- 代理从远程集群内的 istiod 读取配置。当远程集群具有集群内 istiod 时,它只用于边车注入和 CA。您可以通过查找
主集群和远程集群的步骤仍然适用于多网络,但多网络有一个额外的案例。
$ istioctl --context $CTX_CLUSTER1 proxy-config endpoint curl-dd98b5f48-djwdw.sample | grep helloworld
10.0.5.11:5000 HEALTHY OK outbound|5000||helloworld.sample.svc.cluster.local
10.0.6.13:5000 HEALTHY OK outbound|5000||helloworld.sample.svc.cluster.local
在多网络中,我们期望端点 IP 之一与远程网络的东-西网关公共 IP 相匹配。看到多个 Pod IP 表示以下两种情况之一:
- 无法确定远程网络的网关地址。
- 无法确定客户端或服务器 Pod 的网络。
无法确定远程网络的网关地址
在无法访问的远程集群中,检查服务是否具有外部 IP。
$ kubectl -n istio-system get service -l "istio=eastwestgateway"
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-eastwestgateway LoadBalancer 10.8.17.119 <PENDING> 15021:31781/TCP,15443:30498/TCP,15012:30879/TCP,15017:30336/TCP 76m
如果 EXTERNAL-IP
停留在 <PENDING>
状态,则该环境可能不支持 LoadBalancer
服务。在这种情况下,可能需要自定义服务的 spec.externalIPs
部分,以手动为网关提供从集群外部可访问的 IP。
如果存在外部 IP,请检查服务是否包含一个名为 topology.istio.io/network
的标签,该标签具有正确的 value。如果该值不正确,请重新安装网关,并确保在生成脚本上设置 –network
标志。
无法确定客户端或服务器的网络。
在源 Pod 上,检查代理元数据。
$ kubectl get pod $CURL_POD_NAME \
-o jsonpath="{.spec.containers[*].env[?(@.name=='ISTIO_META_NETWORK')].value}"
$ kubectl get pod $HELLOWORLD_POD_NAME \
-o jsonpath="{.metadata.labels.topology\.istio\.io/network}"
如果这两个值未设置,或者设置的值不正确,istiod 可能将源代理和客户端代理视为位于同一网络上,并发送网络本地端点。如果这两个值未设置,请检查在安装过程中是否已正确设置 values.global.network
,或注入 Webhook 是否配置正确。
Istio 使用注入过程中设置的 topology.istio.io/network
标签来确定 Pod 的网络。对于未注入的 Pod,Istio 依赖于集群中系统命名空间上设置的 topology.istio.io/network
标签。
在每个集群中,检查网络
$ kubectl --context="${CTX_CLUSTER1}" get ns istio-system -ojsonpath='{.metadata.labels.topology\.istio\.io/network}'
如果上面的命令没有输出预期的网络名称,请设置该标签。
$ kubectl --context="${CTX_CLUSTER1}" label namespace istio-system topology.istio.io/network=network1