对于新安装的 Kubernetes,常常出现的一个问题是 Service 没有正常工做。若是您已经运行了 Deployment 并建立了一个 Service,可是当您尝试访问它时没有获得响应,但愿这份文档能帮助您找出问题所在。node
先来熟悉下Service工做逻辑:json
为了完成本次演练的目的,咱们先运行几个 Pod。api
$ kubectl run hostnames --image=k8s.gcr.io/serve_hostname \ --labels=app=hostnames \ --port=9376 \ --replicas=3 deployment.apps/hostnames created
确认您的 Pods 是运行状态:服务器
$ kubectl get pods -l app=hostnames NAME READY STATUS RESTARTS AGE hostnames-632524106-bbpiw 1/1 Running 0 2m hostnames-632524106-ly40y 1/1 Running 0 2m hostnames-632524106-tlaok 1/1 Running 0 2m
细心的读者会注意到咱们尚未真正建立一个 Service - 其实这是咱们有意的。这是一个有时会被遗忘的步骤,也是第一件要检查的事情。session
那么,若是我试图访问一个不存在的 Service,会发生什么呢?假设您有另外一个 Pod,想经过名称使用这个 Service,您将获得以下内容:app
u@pod$ wget -O- hostnames Resolving hostnames (hostnames)... failed: Name or service not known. wget: unable to resolve host address 'hostnames'
所以,首先要检查的是 Service 是否确实存在:负载均衡
$ kubectl get svc hostnames No resources found. Error from server (NotFound): services "hostnames" not found
咱们已经有一个罪魁祸首了,让咱们来建立 Service。就像前面同样,这里的内容仅仅是为了步骤的执行 - 在这里您可使用本身的 Service 细节。dom
$ kubectl expose deployment hostnames --port=80 --target-port=9376 service/hostnames exposed
再查询一遍,肯定一下:curl
$ kubectl get svc hostnames NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hostnames ClusterIP 10.0.1.175 <none> 80/TCP 5s
与前面相同,这与您使用 YAML 启动的 Service 同样:tcp
apiVersion: v1 kind: Service metadata: name: hostnames spec: selector: app: hostnames ports: - name: default protocol: TCP port: 80 targetPort: 9376
如今您能够确认 Service 存在。
从相同 Namespace 下的 Pod 中运行:
u@pod$ nslookup hostnames Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local Name: hostnames Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
若是失败,那么您的 Pod 和 Service 可能位于不一样的 Namespace 中,请尝试使用限定命名空间的名称:
u@pod$ nslookup hostnames.default Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local Name: hostnames.default Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
若是成功,那么须要调整您的应用,使用跨命名空间的名称去访问服务,或者,在相同的 Namespace 中运行应用和 Service。若是仍然失败,请尝试一个彻底限定的名称:
u@pod$ nslookup hostnames.default.svc.cluster.local Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local Name: hostnames.default.svc.cluster.local Address 1: 10.0.1.175 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 Server: 10.0.0.10 Address: 10.0.0.10#53 Name: hostnames.default.svc.cluster.local Address: 10.0.1.175
若是您可以使用彻底限定的名称查找,但不能使用相对名称,则须要检查 /etc/resolv.conf 文件是否正确。
u@pod$ cat /etc/resolv.conf nameserver 10.0.0.10 search default.svc.cluster.local svc.cluster.local cluster.local example.com options ndots:5
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 名称。
若是上面仍然失败 - DNS 查找不到您须要的 Service - 咱们能够后退一步,看看还有什么不起做用。Kubernetes 主 Service 应该一直是工做的:
u@pod$ nslookup kubernetes.default Server: 10.0.0.10 Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local Name: kubernetes.default Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local
若是失败,您可能须要转到这个文档的 kube-proxy 部分,或者甚至回到文档的顶部从新开始,但不是调试您本身的 Service,而是调试 DNS。
假设咱们能够确认 DNS 工做正常,那么接下来要测试的是您的 Service 是否工做正常。从集群中的一个节点,访问 Service 的 IP(从上面的 kubectl get 命令获取)。
u@node$ curl 10.0.1.175:80 hostnames-0utonu@node$ curl 10.0.1.175:80 hostnames-yp2kp u@node$ curl 10.0.1.175:80 hostnames-bvc05
若是 Service 是正常的,您应该获得正确的响应。若是没有,有不少可能出错的地方,请继续。
这听起来可能很愚蠢,但您应该加倍甚至三倍检查 Service 是否正确,而且与您的 Pod 匹配。查看 Service 并验证它:
$ kubectl get service hostnames -o json { "kind": "Service", "apiVersion": "v1", "metadata": { "name": "hostnames", "namespace": "default", "selfLink": "/api/v1/namespaces/default/services/hostnames", "uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc", "resourceVersion": "347189", "creationTimestamp": "2015-07-07T15:24:29Z", "labels": { "app": "hostnames" } }, "spec": { "ports": [ { "name": "default", "protocol": "TCP", "port": 80, "targetPort": 9376, "nodePort": 0 } ], "selector": { "app": "hostnames" }, "clusterIP": "10.0.1.175", "type": "ClusterIP", "sessionAffinity": "None" }, "status": { "loadBalancer": {} } }
spec.ports[] 中描述的是您想要尝试访问的端口吗?targetPort 对您的 Pod 来讲正确吗(许多 Pod 选择使用与 Service 不一样的端口)?若是您想把它变成一个数字端口,那么它是一个数字(9376)仍是字符串 “9376”?若是您想把它看成一个指定的端口,那么您的 Pod 是否公开了一个同名端口?端口的 protocol 和 Pod 的同样吗?
若是您已经走到了这一步,咱们假设您已经确认 Service 存在,并能经过 DNS 解析。如今,让咱们检查一下,您运行的 Pod 确实是由 Service 选择的。
早些时候,咱们已经看到 Pod 是运行状态。咱们能够再检查一下:
$ kubectl get pods -l app=hostnames NAME READY STATUS RESTARTS AGE hostnames-0uton 1/1 Running 0 1h hostnames-bvc05 1/1 Running 0 1h hostnames-yp2kp 1/1 Running 0 1h
“AGE” 列代表这些 Pod 已经启动一个小时了,这意味着它们运行良好,而不是崩溃。
-l app=hostnames 参数是一个标签选择器 - 就像咱们的 Service 同样。在 Kubernetes 系统中有一个控制循环,它评估每一个 Service 的选择器,并将结果保存到 Endpoints 对象中。
$ kubectl get endpoints hostnames NAME ENDPOINTS hostnames 10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376
这证明 endpoints 控制器已经为您的 Service 找到了正确的 Pods。若是 hostnames 行为空,则应检查 Service 的 spec.selector 字段,以及您实际想选择的 Pods 的 metadata.labels 的值。常见的错误是输入错误或其余错误,例如 Service 想选择 run=hostnames,可是 Deployment 指定的是 app=hostnames。
到了这步,咱们知道您的 Service 存在并选择了 Pods。让咱们检查一下 Pod 是否真的在工做 - 咱们能够绕过 Service 机制,直接进入 Pod。
注意:这些命令使用的是 Pod 端口(9376),而不是 Service 端口(80)。
u@pod$ wget -qO- 10.244.0.5:9376 hostnames-0uton pod $ wget -qO- 10.244.0.6:9376 hostnames-bvc05 u@pod$ wget -qO- 10.244.0.7:9376 hostnames-yp2kp
咱们指望的是 Endpoints 列表中的每一个 Pod 返回本身的主机名。若是这没有发生(或者您本身的 Pod 的正确行为没有发生),您应该调查发生了什么。您会发现 kubectl logs 这个时候很是有用,或者使用 kubectl exec 直接进入到您的 Pod,并从那里检查服务。
另外一件要检查的事情是,您的 Pod 没有崩溃或正在从新启动。频繁的从新启动可能会致使断断续续的链接问题。
$ kubectl get pods -l app=hostnames NAME READY STATUS RESTARTS AGE hostnames-632524106-bbpiw 1/1 Running 0 2m hostnames-632524106-ly40y 1/1 Running 0 2m hostnames-632524106-tlaok 1/1 Running 0 2m
若是从新启动计数很高,请查阅有关如何调试 pods 获取更多信息。
若是您到了这里,那么 Service 正在运行,也有 Endpoints,而您的 Pod 实际上也正在服务。在这一点上,整个 Service 代理机制是否正常就是可疑的了。咱们来确认一下,一部分一部分来。
确认 kube-proxy 正在您的 Nodes 上运行。您应该获得以下内容:
u@node$ ps auxw | grep kube-proxy root 4194 0.4 0.1 101864 17696 ? Sl Jul04 25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2
下一步,确认它并无出现明显的失败,好比链接主节点失败。要作到这一点,您必须查看日志。访问日志取决于您的 Node 操做系统。在某些操做系统是一个文件,如 /var/log/messages kube-proxy.log,而其余操做系统使用 journalctl 访问日志。您应该看到相似的东西:
I1027 22:14:53.995134 5063 server.go:200] Running in resource-only container "/kube-proxy" I1027 22:14:53.998163 5063 server.go:247] Using iptables Proxier. I1027 22:14:53.999055 5063 server.go:255] Tearing down userspace rules. Errors here are acceptable. I1027 22:14:54.038140 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53] I1027 22:14:54.038164 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53] I1027 22:14:54.038209 5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443] I1027 22:14:54.038238 5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master I1027 22:14:54.040048 5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP I1027 22:14:54.040154 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP I1027 22:14:54.040223 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP
若是您看到有关没法链接主节点的错误消息,则应再次检查节点配置和安装步骤。
kube-proxy 没法正确运行的可能缘由之一是找不到所需的 conntrack 二进制文件。在一些 Linux 系统上,这也是可能发生的,这取决于您如何安装集群,例如,您正在从头开始安装 Kubernetes。若是是这样的话,您须要手动安装 conntrack 包(例如,在 Ubuntu 上使用 sudo apt install conntrack),而后重试。
kube-proxy 的主要职责之一是写实现 Services 的 iptables 规则。让咱们检查一下这些规则是否已经被写好了。
kube-proxy 能够在 “userspace” 模式、 “iptables” 模式或者 “ipvs” 模式下运行。若是您正在使用 “iptables” 模式或者 “ipvs” 模式。您应该看到如下状况之一。
Iptables u@node$ iptables-save | grep hostnames -A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set\-xmark 0x00004000/0x00004000 -A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376 -A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set\-xmark 0x00004000/0x00004000 -A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376 -A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set\-xmark 0x00004000/0x00004000 -A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376 -A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3 -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3 -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
KUBE-SERVICES 中应该有 1 条规则,KUBE-SVC-(hash) 中每一个端点有 1 或 2 条规则(取决于 SessionAffinity),每一个端点中应有 1 条 KUBE-SEP-(hash) 链。准确的规则将根据您的确切配置(包括节点、端口组合以及负载均衡器设置)而有所不一样。
IPVS u@node$ ipvsadm -ln Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn ... TCP 10.0.1.175:80 rr -> 10.244.0.5:9376 Masq 1 0 0 -> 10.244.0.6:9376 Masq 1 0 0 -> 10.244.0.7:9376 Masq 1 0 0...
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),你会获得相似上面的结果。
若是走到这一步还没解决!那只有烧香拜佛了!
注:全文来自官方文档翻译而成