对于新安装的 Kubernetes,经常出现的一个问题是 Service
没有正常工作。如果您已经运行了 Deployment
并创建了一个 Service
,但是当您尝试访问它时没有得到响应,希望这份文档能帮助您找出问题所在。
先来熟悉下Service工作逻辑:
为了完成本次演练的目的,我们先运行几个 Pod
。
$ kubectl run hostnames --image=k8s.gcr.io/serve_hostname \
确认您的 Pods
是运行状态:
$ kubectl get pods -l app=hostnames
问题1:Service 存在吗?
细心的读者会注意到我们还没有真正创建一个 Service
- 其实这是我们有意的。这是一个有时会被遗忘的步骤,也是第一件要检查的事情。
那么,如果我试图访问一个不存在的 Service
,会发生什么呢?假设您有另一个 Pod
,想通过名称使用这个 Service
,您将得到如下内容:
u@pod$ wget -O- hostnames
因此,首先要检查的是 Service
是否确实存在:
$ kubectl get svc hostnames
我们已经有一个罪魁祸首了,让我们来创建 Service
。就像前面一样,这里的内容仅仅是为了步骤的执行 - 在这里您可以使用自己的 Service
细节。
$ kubectl expose deployment hostnames --port=80 --target-port=9376
再查询一遍,确定一下:
$ kubectl get svc hostnames
与前面相同,这与您使用 YAML 启动的 Service
一样:
apiVersion: v1
现在您可以确认 Service
存在。
问题2:Service 是否通过 DNS 工作?
从相同 Namespace
下的 Pod
中运行:
u@pod$ nslookup hostnames
如果失败,那么您的 Pod
和 Service
可能位于不同的 Namespace
中,请尝试使用限定命名空间的名称:
u@pod$ nslookup hostnames.default
如果成功,那么需要调整您的应用,使用跨命名空间的名称去访问服务,或者,在相同的 Namespace
中运行应用和 Service
。如果仍然失败,请尝试一个完全限定的名称:
u@pod$ nslookup hostnames.default.svc.cluster.local
注意这里的后缀:”default.svc.cluster.local”。”default” 是我们正在操作的 Namespace
。”svc” 表示这是一个 Service
。”cluster.local” 是您的集群域,在您自己的集群中可能会有所不同。
您也可以在集群中的 Node 上尝试此操作:
注意: 10.0.0.10 是我的 DNS
Service
,您的可能不同)
u@node$ nslookup hostnames.default.svc.cluster.local 10.0.0.10
如果您能够使用完全限定的名称查找,但不能使用相对名称,则需要检查 /etc/resolv.conf
文件是否正确。
u@pod$ cat /etc/resolv.conf
nameserver
行必须指示您的集群的 DNS Service
,它通过 --cluster-dns
标志传递到 kubelet
。
search
行必须包含一个适当的后缀,以便查找 Service
名称。在本例中,它在本地 Namespace
(default.svc.cluster.local
)、所有 Namespace
中的 Service
(svc.cluster.local
)以及集群(cluster.local
)中查找服务。根据您自己的安装情况,可能会有额外的记录(最多 6 条)。集群后缀通过 --cluster-domain
标志传递给 kubelet
。本文档中,我们假定它是 “cluster.local”,但是您的可能不同,这种情况下,您应该在上面的所有命令中更改它。
options
行必须设置足够高的 ndots
,以便 DNS 客户端库考虑搜索路径。在默认情况下,Kubernetes 将这个值设置为 5,这个值足够高,足以覆盖它生成的所有 DNS 名称。
问题3:DNS 是否可以解析默认服务?
如果上面仍然失败 - DNS 查找不到您需要的 Service
- 我们可以后退一步,看看还有什么不起作用。Kubernetes 主 Service
应该一直是工作的:
u@pod$ nslookup kubernetes.default
如果失败,您可能需要转到这个文档的 kube-proxy 部分,或者甚至回到文档的顶部重新开始,但不是调试您自己的 Service
,而是调试 DNS。
问题4:Service 能够通过 IP 访问么?
假设我们可以确认 DNS 工作正常,那么接下来要测试的是您的 Service
是否工作正常。从集群中的一个节点,访问 Service
的 IP(从上面的 kubectl get
命令获取)。
u@node$ curl 10.0.1.175:80
如果 Service
是正常的,您应该得到正确的响应。如果没有,有很多可能出错的地方,请继续。
问题5:Service 是对的吗?
这听起来可能很愚蠢,但您应该加倍甚至三倍检查 Service
是否正确,并且与您的 Pod
匹配。查看 Service
并验证它:
$ kubectl get service hostnames -o json
spec.ports[]
中描述的是您想要尝试访问的端口吗?targetPort
对您的 Pod
来说正确吗(许多 Pod
选择使用与 Service
不同的端口)?如果您想把它变成一个数字端口,那么它是一个数字(9376)还是字符串 “9376”?如果您想把它当作一个指定的端口,那么您的 Pod
是否公开了一个同名端口?端口的 protocol
和 Pod
的一样吗?
问题6:Service 有端点吗?
如果您已经走到了这一步,我们假设您已经确认 Service
存在,并能通过 DNS 解析。现在,让我们检查一下,您运行的 Pod
确实是由 Service
选择的。
早些时候,我们已经看到 Pod
是运行状态。我们可以再检查一下:
$ kubectl get pods -l app=hostnames
“AGE” 列表明这些 Pod
已经启动一个小时了,这意味着它们运行良好,而不是崩溃。
-l app=hostnames
参数是一个标签选择器 - 就像我们的 Service
一样。在 Kubernetes 系统中有一个控制循环,它评估每个 Service
的选择器,并将结果保存到 Endpoints
对象中。
$ kubectl get endpoints hostnames
这证实 endpoints 控制器已经为您的 Service
找到了正确的 Pods
。如果 hostnames
行为空,则应检查 Service
的 spec.selector
字段,以及您实际想选择的 Pods
的 metadata.labels
的值。常见的错误是输入错误或其他错误,例如 Service
想选择 run=hostnames
,但是 Deployment
指定的是 app=hostnames
。
问题8:Pod 正常工作吗?
到了这步,我们知道您的 Service
存在并选择了 Pods
。让我们检查一下 Pod
是否真的在工作 - 我们可以绕过 Service
机制,直接进入 Pod
。
注意: 这些命令使用的是
Pod
端口(9376),而不是Service
端口(80)。
u@pod$ wget -qO- 10.244.0.5:9376
我们期望的是 Endpoints
列表中的每个 Pod
返回自己的主机名。如果这没有发生(或者您自己的 Pod
的正确行为没有发生),您应该调查发生了什么。您会发现 kubectl logs
这个时候非常有用,或者使用 kubectl exec
直接进入到您的 Pod
,并从那里检查服务。
另一件要检查的事情是,您的 Pod 没有崩溃或正在重新启动。频繁的重新启动可能会导致断断续续的连接问题。
$ kubectl get pods -l app=hostnames
如果重新启动计数很高,请查阅有关如何调试 pods 获取更多信息。
问题9:kube-proxy 正常工作吗?
如果您到了这里,那么 Service
正在运行,也有 Endpoints
,而您的 Pod
实际上也正在服务。在这一点上,整个 Service
代理机制是否正常就是可疑的了。我们来确认一下,一部分一部分来。
确认 kube-proxy
正在您的 Nodes
上运行。您应该得到如下内容:
u@node$ ps auxw | grep kube-proxy
下一步,确认它并没有出现明显的失败,比如连接主节点失败。要做到这一点,您必须查看日志。访问日志取决于您的 Node
操作系统。在某些操作系统是一个文件,如 /var/log/messages kube-proxy.log,而其他操作系统使用 journalctl
访问日志。您应该看到类似的东西:
I1027 22:14:53.995134 5063 server.go:200] Running in resource-only container "/kube-proxy"
如果您看到有关无法连接主节点的错误消息,则应再次检查节点配置和安装步骤。
kube-proxy
无法正确运行的可能原因之一是找不到所需的 conntrack
二进制文件。在一些 Linux 系统上,这也是可能发生的,这取决于您如何安装集群,例如,您正在从头开始安装 Kubernetes。如果是这样的话,您需要手动安装 conntrack
包(例如,在 Ubuntu 上使用 sudo apt install conntrack
),然后重试。
问题10:kube-proxy 是否在写 iptables 规则?
kube-proxy
的主要职责之一是写实现 Services
的 iptables
规则。让我们检查一下这些规则是否已经被写好了。
kube-proxy 可以在 “userspace” 模式、 “iptables” 模式或者 “ipvs” 模式下运行。如果您正在使用 “iptables” 模式或者 “ipvs” 模式。您应该看到以下情况之一。
Iptables
u@node$ iptables-save | grep hostnames
KUBE-SERVICES
中应该有 1 条规则,KUBE-SVC-(hash)
中每个端点有 1 或 2 条规则(取决于 SessionAffinity
),每个端点中应有 1 条 KUBE-SEP-(hash)
链。准确的规则将根据您的确切配置(包括节点、端口组合以及负载均衡器设置)而有所不同。
IPVS
u@node$ ipvsadm -ln
IPVS 代理将为每个服务器地址(例如集群 IP、外部 IP、节点端口 IP、负载均衡 IP等)创建虚拟服务器,并为服务的端点创建一些相应的真实服务器(如果有)。在这个例子中,服务器主机(10.0.1.175:80
)有 3 个端点(10.244.0.5:9376
, 10.244.0.6:9376
, 10.244.0.7:9376
),你会得到类似上面的结果。
如果走到这一步还没解决!那只有烧香拜佛了!