Skip to the content.

pod 无法访问集群 ingress slb 绑定的公网域名

pod 请求 slb 域名失败的问题总结文档。

问题背景

在使用 cert-managerhttp-01 方式自动生成证书的时候发现 Certificate 一直是 pending 状态,故进行检查 Challenges 的状态,发现如下错误:

$ kubectl describe challenge example-com-2745722290-4391602865-0
...
Waiting for http-01 challenge propagation: failed to perform self check GET request ‘http://example-com/.well-known/acme-challenge/Rjx7CiiA8GdIhXWRqa_s5XVswaHlD3y0XGMa1LJIIps’: Get http://example-com/.well-known/acme-challenge/Rjx7CiiA8GdIhXWRqa_s5XVswaHlD3y0XGMa1LJIIps: dial tcp example-com:80: connect: connection refused
...

从错误信息上看出来是无法访问 http://example-com/.well-known/acme-challenge/Rjx7CiiA8GdIhXWRqa_s5XVswaHlD3y0XGMa1LJIIps ,这个链接是 cert-manager 自动生成用来验证域名使用权的,访问正常会返回一串 token 字符串,验证成功就可以颁发证书,但是如果无法访问也就无法创建证书了,难道是链接访问出错?故在自己的机器上尝试请求。

$ curl http://example-com/.well-known/acme-challenge/Rjx7CiiA8GdIhXWRqa_s5XVswaHlD3y0XGMa1LJIIps

但是上述步骤是可以正常返回拿到 200 返回码和 证书验证 token 串的。

那么为何 cert-manager 进行验证的时候会出错呢,和本地访问的区别在哪? cer-manager 是通过集群内部的 pod 进行访问的,那么会不会和这个有关系呢?

启动临时 pod 进行验证:

 $ kubectl run -it --rm --restart=Never curl --image=radial/busyboxplus:curl sh
 $ curl http://example-com/.well-known/acme-challenge/Rjx7CiiA8GdIhXWRqa_s5XVswaHlD3y0XGMa1LJIIps

得到错误:curl: (7) Failed to connect to example-com port 80: Connection refused

结论:集群内部无法正常访问 http://example-com ,进一步发现只要是和集群 slb 进行绑定的域名都无法访问,如 http://abc.example-com 等。

原因分析

通过对相关内部 pod 无法访问集群域名的一番查找,将问题锁定在了 externalTrafficPolicy 这个参数下,这个参数一般用于决定是否保留客户端 IP,说明如下:

查看了 ingress servicespec 字段,果然 externalTrafficPolicyLocal ,这就导致了不会跳到别的节点,那么这是什么意思呢,具体来说,设置 service.spec.externalTrafficPolicy 的值为 Local ,请求就只会被代理到本地 endpoints 而不会被转发到其它节点。这样就保留了最初的源 IP 地址。如果没有本地 endpoints,发送到这个节点的数据包将会被丢弃。这样在应用到数据包的任何包处理规则下,你都能依赖这个正确的 source-ip 使数据包通过并到达 endpoint。

举例来说,如果你的集群有两个节点,node1 上有一个 pod 提供服务,你使用了 NodePort (这个规则同样适用于 LoadBalancer) 类型的 svc 暴露出这个服务。如果你分别对两个 node 进行请求,那么,你只会从 endpoint pod 运行的那个节点得到了一个回复,这个回复有正确的客户端 IP。

发生了什么:

用图表示:

        client
       ^ /   \
      / /     \
     / v       X
   node 1     node 2
    ^ |
    | |
    | v
 endpoint

问题总结

那么 cert-manager 为什么无法访问集群域名呢?原因在于集群域名对应的 ipLoadBalancer 类型的 svcip , 而 svc 对应的 pod(在 node1 上) 和 cert-managerpod(在 node2 上) 不在一个 node 上,那么按照上面的理论,不会进行节点的转发,数据包被丢弃,请求自然失败。

发生了什么:

解决方法

参考文档