zoukankan      html  css  js  c++  java
  • 全网最详细的 K8s Service 不能访问排查流程

    对于新安装的 Kubernetes,经常出现的一个问题是 Service 没有正常工作。如果您已经运行了 Deployment 并创建了一个 Service,但是当您尝试访问它时没有得到响应,希望这份文档能帮助您找出问题所在。

    先来熟悉下Service工作逻辑:

    为了完成本次演练的目的,我们先运行几个 Pod。

    $ 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
    问题1:Service 存在吗?

    细心的读者会注意到我们还没有真正创建一个 Service - 其实这是我们有意的。这是一个有时会被遗忘的步骤,也是第一件要检查的事情。

    那么,如果我试图访问一个不存在的 Service,会发生什么呢?假设您有另一个 Pod,想通过名称使用这个 Service,您将得到如下内容:

    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 细节。

    $ kubectl expose deployment hostnames --port=80 --target-port=9376
    service/hostnames exposed

    再查询一遍,确定一下:

    $ 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 一样:

    apiVersion: v1
    kind: Service
    metadata:
      name: hostnames
    spec:
      selector:
        app: hostnames
      ports:
      - name: default
        protocol: TCP
        port: 80
        targetPort: 9376

    现在您可以确认 Service 存在。

    问题2:Service 是否通过 DNS 工作?

    从相同 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 名称。

    问题3: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。

    问题4:Service 能够通过 IP 访问么?

    假设我们可以确认 DNS 工作正常,那么接下来要测试的是您的 Service 是否工作正常。从集群中的一个节点,访问 Service 的 IP(从上面的 kubectl get 命令获取)。

    u@node$ curl 10.0.1.175:80
    hostnames-0uton

    u@node$ curl 10.0.1.175:80
    hostnames-yp2kp

    u@node$ curl 10.0.1.175:80
    hostnames-bvc05

    如果 Service 是正常的,您应该得到正确的响应。如果没有,有很多可能出错的地方,请继续。

    问题5: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 的一样吗?

    问题6:Service 有端点吗?

    如果您已经走到了这一步,我们假设您已经确认 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。

    问题7:Pod 正常工作吗?

    到了这步,我们知道您的 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 获取更多信息。

    问题8:kube-proxy 正常工作吗?

    如果您到了这里,那么 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),然后重试。

    问题9:kube-proxy 是否在写 iptables 规则?

    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),你会得到类似上面的结果。

    如果走到这一步还没解决!那只有烧香拜佛了!

  • 相关阅读:
    多测师讲解html _伪类选择器17_高级讲师肖sir
    多测师讲解html _后代选择器16_高级讲师肖sir
    多测师讲解html _组合选择器_高级讲师肖sir
    多测师讲解html _标签选择器14_高级讲师肖sir
    前端 HTML form表单标签 input标签 type属性 重置按钮 reset
    前端 HTML form表单标签 textarea标签 多行文本
    前端 HTML form表单标签 input标签 type属性 file 上传文件
    前端 HTML form表单标签 input标签 type属性 radio 单选框
    前端 HTML form表单标签 input标签 type属性 checkbox 多选框
    前端 HTML form表单目录
  • 原文地址:https://www.cnblogs.com/cheyunhua/p/13343313.html
Copyright © 2011-2022 走看看