From 01027380d5f439b49df24342824fddaafb35e8c0 Mon Sep 17 00:00:00 2001 From: jiajie Date: Thu, 2 Jul 2020 17:37:40 +0800 Subject: [PATCH] Update debug-service.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新文档内容从 release1.16 到 master --- .../debug-service.md | 1381 ++++++++--------- 1 file changed, 611 insertions(+), 770 deletions(-) diff --git a/content/zh/docs/tasks/debug-application-cluster/debug-service.md b/content/zh/docs/tasks/debug-application-cluster/debug-service.md index 76cccc17d3..39f08d78da 100644 --- a/content/zh/docs/tasks/debug-application-cluster/debug-service.md +++ b/content/zh/docs/tasks/debug-application-cluster/debug-service.md @@ -19,180 +19,93 @@ title: Debug Services -对于新安装的 Kubernetes,经常出现的一个问题是 `Service` 没有正常工作。如果您已经运行了 `Deployment` 并创建了一个 `Service`,但是当您尝试访问它时没有得到响应,希望这份文档能帮助您找出问题所在。 - - +对于新安装的 Kubernetes,经常出现的问题是服务无法正常运行。 您已经通过 +Deployment(或其他工作负载控制器)运行了 Pod,并创建服务,但是 +当您尝试访问它时,没有任何响应。 该文档有望对您有所帮助并找出问题所在。 - - -## 约定 - -在整个文档中,您将看到各种可以运行的命令。有些命令需要在 `Pod` 中运行,有些命令需要在 Kubernetes `Node` 上运行,还有一些命令可以在您拥有 `kubectl` 和集群凭证的任何地方运行。为了明确预期的效果,本文档将使用以下约定。 - -如果命令 "COMMAND" 期望在 `Pod` 中运行,并且产生 "OUTPUT": - -```shell -u@pod$ COMMAND -OUTPUT -``` - -如果命令 "COMMAND" 期望在 `Node` 上运行,并且产生 "OUTPUT": - -```shell -u@node$ COMMAND -OUTPUT -``` - -如果命令是 "kubectl ARGS": - -```shell -$ kubectl ARGS -OUTPUT -``` - ## 在 pod 中运行命令 -对于这里的许多步骤,您可能希望知道运行在集群中的 `Pod` 看起来是什么样的。最简单的方法是运行一个交互式的 busybox `Pod`: - +对于这里的许多步骤,您可能希望知道运行在集群中的 Pod 看起来是什么样的。最简单的方法是运行一个交互式的 alpine Pod: ```none -$ kubectl run -it --rm --restart=Never busybox --image=busybox sh -如果你没有看到命令提示符,请尝试按 Enter 键。 -/ # +$ kubectl run -it --rm --restart=Never alpine --image=alpine sh +If you don't see a command prompt, try pressing enter. ``` + +{{< note >}} +如果你没有看到命令提示符,请尝试按 Enter 键。 +{{< /note >}} + +如果您已经有了您想使用的正在运行的 Pod,则可以运行以下命令去进入: ```shell -$ kubectl exec -c -- +kubectl exec -c -- ``` + ## 设置 -为了完成本次演练的目的,我们先运行几个 `Pod`。因为可能正在调试您自己的 `Service`,所以,您可以使用自己的详细信息进行替换,或者,您也可以跟随并开始下面的步骤来获得第二个数据点。 +为了完成本次实践的任务,我们先运行几个 Pod。由于您可能正在调试自己的服务,所以,您可以使用自己的信息进行替换,或者,您也可以跟随并开始下面的步骤来获得第二个数据点。 ```shell -$ kubectl run hostnames --image=k8s.gcr.io/serve_hostname \ - --labels=app=hostnames \ - --port=9376 \ - --replicas=3 +$ kubectl create deployment hostnames --image=k8s.gcr.io/serve_hostname +``` +```none deployment.apps/hostnames created ``` + `kubectl` 命令将打印创建或变更的资源的类型和名称,它们可以在后续命令中使用。 -{{< note >}} -这与您使用以下 YAML 启动 `Deployment` 相同: +让我们将这个 deployment 的副本数扩至 3。 +```shell +kubectl scale deployment hostnames --replicas=3 +``` +```none +deployment.apps/hostnames scaled +``` + + +请注意这与您使用以下 YAML 方式启动 Deployment 类似: ```yaml apiVersion: apps/v1 kind: Deployment metadata: + labels: + app: hostnames name: hostnames spec: selector: @@ -207,185 +120,259 @@ spec: containers: - name: hostnames image: k8s.gcr.io/serve_hostname - ports: - - containerPort: 9376 - protocol: TCP ``` -{{< /note >}} -确认您的 `Pods` 是运行状态: + + +"app" 标签是 `kubectl create deployment` 根据 Deployment 名称自动设置的。 + +确认您的 Pods 是运行状态: ```shell -$ kubectl get pods -l app=hostnames +kubectl get pods -l app=hostnames +``` +```none 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 ``` + +您还可以确认您的 Pod 是否正在运行。您可以获取 Pod IP 地址列表并直接对其进行测试。 + +```shell +kubectl get pods -l app=hostnames \ + -o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}' +``` +```none +10.244.0.5 +10.244.0.6 +10.244.0.7 +``` + + + +用于本教程的示例容器仅通过 HTTP 在端口 9376 上提供其自己的主机名,但是如果要调试自己的应用程序,则需要使用您的 Pod 正在侦听的端口号。 + +在 pod 内运行: + +```shell +for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do + wget -qO- $ep +done +``` + +输出类似这样: + +``` +hostnames-632524106-bbpiw +hostnames-632524106-ly40y +hostnames-632524106-tlaok +``` + + +如果此时您没有收到期望的响应,则您的 Pod 状态可能不健康,或者可能没有在您认为正确的端口上进行监听。 +您可能会发现 `kubectl logs` 命令对于查看正在发生的事情很有用,或者您可能需要通过`kubectl exec` 直接进入 Pod 中并从那里进行调试。 + +假设到目前为止一切都已按计划进行,那么您可以开始调查为何您的服务无法正常工作。 + -## Service 存在吗? +## Service 是否存在? -细心的读者会注意到我们还没有真正创建一个 `Service` - 其实这是我们有意的。这是一个有时会被遗忘的步骤,也是第一件要检查的事情。 +细心的读者会注意到我们实际上尚未创建`Service`-这是有意而为之。 这一步有时会被遗忘,这是首先要检查的步骤。 -那么,如果我试图访问一个不存在的 `Service`,会发生什么呢?假设您有另一个 `Pod`,想通过名称使用这个 `Service`,您将得到如下内容: +那么,如果我尝试访问不存在的 ‘服务’ 会怎样? 假设您有另一个`Pod`通过名称匹配到`Service`,您将得到类似结果: ```shell -u@pod$ wget -O- hostnames +wget -O- hostnames +``` +```none Resolving hostnames (hostnames)... failed: Name or service not known. wget: unable to resolve host address 'hostnames' ``` - -因此,首先要检查的是 `Service` 是否确实存在: + +首先要检查的是该服务是否真实存在: ```shell -$ kubectl get svc hostnames +kubectl get svc hostnames +``` +```none No resources found. Error from server (NotFound): services "hostnames" not found ``` -我们已经有一个罪魁祸首了,让我们来创建 `Service`。就像前面一样,这里的内容仅仅是为了步骤的执行 - 在这里您可以使用自己的 `Service` 细节。 - -```shell -$ kubectl expose deployment hostnames --port=80 --target-port=9376 -service/hostnames exposed -``` - -再查询一遍,确定一下: - -```shell -$ kubectl get svc hostnames -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -hostnames ClusterIP 10.0.1.175 80/TCP 5s -``` - -与前面相同,这与您使用 YAML 启动的 `Service` 一样: - -```yaml -apiVersion: v1 -kind: Service -metadata: - name: hostnames -spec: - selector: - app: hostnames - ports: - - name: default - protocol: TCP - port: 80 - targetPort: 9376 -``` - -现在您可以确认 `Service` 存在。 - +让我们创建 '服务'。 和以前一样,在这次实践中 - 您可以在此处使用自己的 `Service` 的内容。 +```shell +kubectl expose deployment hostnames --port=80 --target-port=9376 +``` +```none +service/hostnames exposed +``` -From a `Pod` in the same `Namespace`: + +重新运行查询命令,确认没有问题: ```shell -u@pod$ nslookup hostnames +kubectl get svc hostnames +``` +```none +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +hostnames ClusterIP 10.0.1.175 80/TCP 5s +``` + + +现在您知道了服务确实存在。 + +就像之前通过 YAML 方式启动 '服务'一样: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: hostnames +spec: + selector: + app: hostnames + ports: + - name: default + protocol: TCP + port: 80 + targetPort: 9376 +``` + +为了突出配置范围的完整性,您在此处创建的服务使用的端口号与 Pods 不同。对于许多真实的服务,这些值可以是相同的。 + + + +## Service 是否通过 DNS 工作? + +通常客户端通过 DNS 名称来匹配到 Service。 + +从相同命名空间下的 Pod 中运行以下命令: +```shell +nslookup hostnames +``` +```none 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 ``` - -If this fails, perhaps your `Pod` and `Service` are in different -`Namespaces`, try a namespace-qualified name: + +如果失败,那么您的 Pod 和 Service 可能位于不同的命名空间中,请尝试使用限定命名空间的名称(同样在 Pod 内运行): ```shell -u@pod$ nslookup hostnames.default +nslookup hostnames.default +``` +```none 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 ``` + +如果成功,那么需要调整您的应用,使用跨命名空间的名称去访问服务,或者,在相同的命名空间中运行应用和 Service。如果仍然失败,请尝试一个完全限定的名称: ```shell -u@pod$ nslookup hostnames.default.svc.cluster.local +nslookup hostnames.default.svc.cluster.local +``` +```none 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" 是我们正在操作的命名空间。"svc" 表示这是一个Service。"cluster.local" 是您的集群域,在您自己的集群中可能会有所不同。 + +您也可以在集群中的节点上尝试此操作: + +{{< note >}} +10.0.0.10 是我的 DNS 服务 IP,您的可能有所不同。 {{< /note >}} ```shell -u@node$ nslookup hostnames.default.svc.cluster.local 10.0.0.10 +nslookup hostnames.default.svc.cluster.local 10.0.0.10 +``` +```none Server: 10.0.0.10 Address: 10.0.0.10#53 @@ -393,105 +380,68 @@ Name: hostnames.default.svc.cluster.local Address: 10.0.1.175 ``` + +如果您能够使用完全限定的名称查找,但不能使用相对名称,则需要检查您 Pod 中的 `/etc/resolv.conf` 文件是否正确。在 Pod 中运行以下命令: ```shell -u@pod$ cat /etc/resolv.conf +cat /etc/resolv.conf +``` + +您应该可以看到类似这样的输出: + +``` nameserver 10.0.0.10 search default.svc.cluster.local svc.cluster.local cluster.local example.com options ndots:5 ``` + -## Service 是否通过 DNS 工作? +`nameserver` 行必须指示您的集群的 DNS Service,它通过 `--cluster-dns` 标志传递到 kubelet。 -从相同 `Namespace` 下的 `Pod` 中运行: - -```shell -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` 中,请尝试使用限定命名空间的名称: - -```shell -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`。如果仍然失败,请尝试一个完全限定的名称: - -```shell -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 上尝试此操作: - -{{< note >}} -10.0.0.10 是我的 DNS `Service`,您的可能不同). -{{< /note >}} - -```shell -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` 文件是否正确。 - -```shell -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”,但是您的可能不同,这种情况下,您应该在上面的所有命令中更改它。 +`search` 行必须包含一个适当的后缀,以便查找服务名称。在本例中,它在本地命名空间(`default.svc.cluster.local`)、所有命名空间中的 `Service`(`svc.cluster.local`)最后是集群(`cluster.local`)中查找服务的名称。根据您自己的安装情况,可能会有额外的记录(最多 6 条)。 +集群后缀通过 `--cluster-domain` 标志传递给 `kubelet`。 本文档中,我们假定后缀是 “cluster.local”。您的集群配置可能不同,这种情况下,您应该在上面的所有命令中更改它。 `options` 行必须设置足够高的 `ndots`,以便 DNS 客户端库考虑搜索路径。在默认情况下,Kubernetes 将这个值设置为 5,这个值足够高,足以覆盖它生成的所有 DNS 名称。 +### Service 是否通过 DNS 名称来工作?{#does-any-service-exist-in-dns} + +如果上面的方式仍然失败,DNS 查找不到您需要的服务名,您可以后退一步,看看还有什么其它东西没有正常工作。Kubernetes 主服务应该一直是工作的。在 Pod 中运行如下命令: ```shell -u@pod$ nslookup kubernetes.default +nslookup kubernetes.default +``` +```none Server: 10.0.0.10 Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local @@ -499,121 +449,59 @@ Name: kubernetes.default Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local ``` -If this fails, you might need to go to the kube-proxy section of this doc, or -even go back to the top of this document and start over, but instead of -debugging your own `Service`, debug DNS. + -### DNS 中是否存在任何服务? - -如果上面仍然失败 - DNS 查找不到您需要的 `Service` - 我们可以后退一步,看看还有什么不起作用。Kubernetes 主 `Service` 应该一直是工作的: - -```shell -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。 +如果失败,您可能需要转到这个文档的 [kube-proxy](#is-the-kube-proxy-working) 部分,或者甚至回到文档的顶部重新开始,但不是调试您自己的服务,而是调试 DNS。 ### Service 能够通过 IP 访问么? -假设我们可以确认 DNS 工作正常,那么接下来要测试的是您的 `Service` 是否工作正常。从集群中的一个节点,访问 `Service` 的 IP(从上面的 `kubectl get` 命令获取)。 +假设您已经确认 DNS 工作正常,那么接下来要测试的是您的能否通过它的 IP 正常访问。从集群中的一个 Pod,尝试访问服务的 IP(从上面的 `kubectl get` 命令获取)。 ```shell -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 +for i in $(seq 1 3); do + wget -qO- 10.0.1.175:80 +done ``` -如果 `Service` 是正常的,您应该得到正确的响应。如果没有,有很多可能出错的地方,请继续。 + +输出应该类似这样: + +``` +hostnames-632524106-bbpiw +hostnames-632524106-ly40y +hostnames-632524106-tlaok +``` +如果服务状态是正常的,您应该得到正确的响应。如果没有,有很多可能出错的地方,请继续阅读。 + + -## Service 是对的吗? +## Service 的配置是否正确? -这听起来可能很愚蠢,但您应该加倍甚至三倍检查您的 `Service` 是否正确,并且与您的 `Pod` 匹配。查看您的 `Service` 并验证它: +这听起来可能很愚蠢,但您应该两次甚至三次检查您的服务配置是否正确,并且与您的 Pod 匹配。查看您的服务配置并验证它: ```shell -$ kubectl get service hostnames -o json +kubectl get service hostnames -o json ``` ```json { @@ -622,7 +510,6 @@ $ kubectl get service hostnames -o json "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", @@ -652,174 +539,176 @@ $ kubectl get service hostnames -o json } } ``` - -`spec.ports[]` 中描述的是您想要尝试访问的端口吗?`targetPort` 对您的 `Pod` 来说正确吗(许多 `Pod` 选择使用与 `Service` 不同的端口)?如果您想把它变成一个数字端口,那么它是一个数字(9376)还是字符串 “9376”?如果您想把它当作一个指定的端口,那么您的 `Pod` 是否公开了一个同名端口?端口的 `protocol` 和 `Pod` 的一样吗? + +* 您想要访问的端口是否在 `spec.ports[]` 中列出? +* `targetPort` 对您的 Pod 来说正确吗(许多 Pod 使用与 Service 不同的端口)? +* 如果您想使用数值型端口,那么它的类型是一个数值(9376)还是字符串 “9376”? +* 如果您想使用名称型端口,那么您的 Pod 是否暴露了一个同名端口? +* 端口的 `protocol` 和 Pod 的是否对应? ## Service 有端点吗? -如果您已经走到了这一步,我们假设您已经确认您的 `Service` 存在,并能通过 DNS 解析。现在,让我们检查一下,您运行的 `Pod` 确实是由 `Service` 选择的。 +如果您已经走到了这一步,您已经确认您的服务被正确定义,并能通过 DNS 解析。现在,让我们检查一下,您运行的 Pod 确实是由 Service 选择的。 -早些时候,我们已经看到 `Pod` 是运行状态。我们可以再检查一下: +早些时候,我们已经看到 Pod 是运行状态。我们可以再检查一下: ```shell -$ 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 +kubectl get pods -l app=hostnames ``` +```none +NAME READY STATUS RESTARTS AGE +hostnames-632524106-bbpiw 1/1 Running 0 1h +hostnames-632524106-ly40y 1/1 Running 0 1h +hostnames-632524106-tlaok 1/1 Running 0 1h +``` + +`-l app=hostnames` 参数是一个标签选择器 - 和我们 Service 中的一样。 + +"AGE" 列表明这些 Pod 已经启动一个小时了,这意味着它们运行良好,而不是崩溃。 + +"RESTARTS" 列表明 Pod 没有经常崩溃或重启。经常性崩溃可能导致间歇性连接问题。如果重启数过大,通过[调试 pod](/docs/tasks/debug-application-cluster/debug-pod-replication-controller/#debugging-pods)了解更多。 + +在 Kubernetes 系统中有一个控制循环,它评估每个 Service 的选择器,并将结果保存到 Endpoints 对象中。 ```shell -$ kubectl get endpoints hostnames +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`。 + +这证实端点控制器已经为您的服务找到了正确的 Pods。如果 `端点` 列的值为 ``,则应检查 `Service` 的 `spec.selector` 字段,以及您实际想选择的 `Pods` 的 `metadata.labels` 的值。常见的错误是输入错误或其他错误,例如 Service 想选择 `app=hostnames`,但是 Deployment 指定的是 `run=hostnames`。在 1.18之前的版本中 `kubectl run` 也可以被用来创建 Deployment。 ## Pod 正常工作吗? -到了这步,我们知道您的 `Service` 存在并选择了您的 `Pods`。让我们检查一下 `Pod` 是否真的在工作 - 我们可以绕过 `Service` 机制,直接进入 `Pod`。 +至此,您知道您的服务已存在,并且已匹配到您的Pod。在本实践的开始,您验证了 Pod 本身。 +让我们再次检查 Pod 是否确实在工作-您可以绕过 Service 机制并直接转到 Pod,如上面的端点所示。 {{< note >}} -这些命令使用的是 `Pod` 端口(9376),而不是 `Service` 端口(80)。 +这些命令使用的是 Pod 端口(9376),而不是 Service 端口(80)。 {{< /note >}} -```shell -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 没有崩溃或正在重新启动。频繁的重新启动可能会导致断断续续的连接问题。 +在 Pod 中运行: ```shell -$ 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 +for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do + wget -qO- $ep +done ``` -如果重新启动计数很高,请查阅有关如何[调试 pods](/docs/tasks/debug-application-cluster/debug-pod-replication-controller/#debugging-pods) 获取更多信息。 + +输出应该类似这样: +``` +hostnames-632524106-bbpiw +hostnames-632524106-ly40y +hostnames-632524106-tlaok +``` + + +您希望端点列表中的每个 Pod 都返回自己的主机名。 如果这不是发生的情况(或您自己的 Pod 的正确行为是什么),您应调查那里发生了什么。 ## kube-proxy 正常工作吗? -如果您到了这里,那么您的 `Service` 正在运行,也有 `Endpoints`,而您的 `Pod` 实际上也正在服务。在这一点上,整个 `Service` 代理机制是否正常就是可疑的了。我们来确认一下,一部分一部分来。 +如果您到达这里,则说明您的服务正在运行,拥有端点,Pod 真正在运行。 此时此刻,整个服务代理机制是可疑的。 让我们一步一步地确认它没问题。 + +服务的默认实现(在大多数集群上应用的)是kube-proxy。这是一个在每个节点上运行的程序,并配置一小组用于提供服务抽象的机制之一。如果您的集群不使用kube-proxy,则以下各节将不适用,您将必须检查您正在使用的服务的实现方式。 +### kube-proxy 正常运行吗? + +确认 `kube-proxy` 正在节点上运行。 在节点上直接运行,您将会得到类似以下的输出: ```shell -u@node$ ps auxw | grep kube-proxy +ps auxw | grep kube-proxy +``` +```none 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 ``` + +下一步,确认它并没有出现明显的失败,比如连接主节点失败。要做到这一点,您必须查看日志。访问日志取决于您节点的操作系统。在某些操作系统是一个文件,如 /var/log/messages kube-proxy.log,而其他操作系统使用 `journalctl` 访问日志。您应该看到类似的输出: ```none I1027 22:14:53.995134 5063 server.go:200] Running in resource-only container "/kube-proxy" @@ -834,8 +723,9 @@ I1027 22:14:54.040154 5063 proxier.go:294] Adding new service "kube-system/ku 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 在运行吗? - -确认 `kube-proxy` 正在您的 `Nodes` 上运行。您应该得到如下内容: - -```shell -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` 访问日志。您应该看到类似的东西: - -```none -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 可以在这些模式之一中运行。在上述日志中,`Using iptables Proxier` 行表示 kube-proxy 在 "iptables" 模式下运行。最常见的另一种模式是 "ipvs"。先前的 "userspace" +模式已经被这些所代替。 -#### Userspace +#### Iptables 模式 + +在 "iptables" 模式中, 您应该可以在节点上看到如下输出: ```shell -u@node$ iptables-save | grep hostnames +iptables-save | grep hostnames +``` +```none +-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 +``` + + +For each port of each Service, there should be 1 rule in `KUBE-SERVICES` and +one `KUBE-SVC-` chain. For each Pod endpoint, there should be a small +number of rules in that `KUBE-SVC-` and one `KUBE-SEP-` chain with +a small number of rules in it. The exact rules will vary based on your exact +config (including node-ports and load-balancers). + +#### IPVS mode + +In "ipvs" mode, you should see something like the following on a Node: + +```shell +ipvsadm -ln +``` +```none +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 +... +``` + + +对于每个服务的每个端口,还有 NodePort,外部 IP 和 +负载平衡器 IP,kube-proxy 将创建一个虚拟服务器。 对于每个 Pod 端点,它将创建相应的真实服务器。 在此示例中,服务主机名(`10.0.1.175:80`)拥有 3 个端点(`10.244.0.5:9376`, +`10.244.0.6:9376`, `10.244.0.7:9376`)。 + +#### Userspace 模式 + +在少数情况下,您可能会用到 "userspace" 模式,在您的节点上运行: + +```shell +iptables-save | grep hostnames +``` +```none -A KUBE-PORTALS-CONTAINER -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j REDIRECT --to-ports 48577 -A KUBE-PORTALS-HOST -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j DNAT --to-destination 10.240.115.247:48577 ``` -There should be 2 rules for each port on your `Service` (just one in this -example) - a "KUBE-PORTALS-CONTAINER" and a "KUBE-PORTALS-HOST". If you do -not see these, try restarting `kube-proxy` with the `-V` flag set to 4, and -then look at the logs again. + -### kube-proxy 是否在写 iptables 规则? -`kube-proxy` 的主要职责之一是写实现 `Services` 的 `iptables` 规则。让我们检查一下这些规则是否已经被写好了。 - -kube-proxy 可以在 "userspace" 模式、 "iptables" 模式或者 "ipvs" 模式下运行。 -希望您正在使用 "iptables" 模式或者 "ipvs" 模式。您应该看到以下情况之一。 - -#### Userpace - -```shell -u@node$ iptables-save | grep hostnames --A KUBE-PORTALS-CONTAINER -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j REDIRECT --to-ports 48577 --A KUBE-PORTALS-HOST -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j DNAT --to-destination 10.240.115.247:48577 -``` - -您的 `Service` 上的每个端口应该有两个规则(本例中只有一个)- "KUBE-PORTALS-CONTAINER" 和 "KUBE-PORTALS-HOST"。如果您没有看到这些,请尝试将 `-V` 标志设置为 4 之后重新启动 `kube-proxy`,然后再次查看日志。 - -几乎没有人应该再使用 "userspace" 模式了,所以我们不会在这里花费更多的时间。 - - -#### Iptables - -```shell -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 - -```shell -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`),你会得到类似上面的结果。 - - - +对于服务(本例中只有一个)的每个端口,应当有 2 条规则: 一个 "KUBE-PORTALS-CONTAINER" 和一个 "KUBE-PORTALS-HOST"。 + +几乎没有人应该再使用 "userspace" 模式,因此您在这里不会花更多的时间。 + +### kube-proxy 是否在运行? + +假设您确实遇到上述情况之一,请重试从节点上通过 IP 访问您的服务: ```shell -u@node$ curl 10.0.1.175:80 -hostnames-0uton +curl 10.0.1.175:80 +``` +```none +hostnames-632524106-bbpiw ``` + -### kube-proxy 在执行代理操作么? +如果失败,并且您正在使用用户空间代理,则可以尝试直接访问代理。 如果您使用的是 iptables 代理,请跳过本节。 -假设您确实看到了上述规则,请再次尝试通过 IP 访问您的 `Service`: +回顾上面的 `iptables-save` 输出,并提取 `kube-proxy` 用于您的服务的端口号。在上面的例子中,它是 “48577”。现在试着连接它: ```shell -u@node$ curl 10.0.1.175:80 -hostnames-0uton +curl localhost:48577 ``` - -如果失败了,并且您正在使用 userspace 代理,您可以尝试直接访问代理。如果您使用的是 iptables 代理,请跳过本节。 - -回顾上面的 `iptables-save` 输出,并提取 `kube-proxy` 用于您的 `Service` 的端口号。在上面的例子中,它是 “48577”。现在连接到它: - -```shell -u@node$ curl localhost:48577 -hostnames-yp2kp +```none +hostnames-632524106-tlaok ``` -如果仍然失败,请查看 `kube-proxy` 日志中的特定行,如: - -```shell -Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376] -``` - -如果您没有看到这些,请尝试将 `-V` 标志设置为 4 并重新启动 `kube-proxy`,然后再查看日志。 - +如果仍然失败,请查看 `kube-proxy` 日志中的特定行,如: + +```none +Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376] +``` + + +如果您没有看到这些,请尝试将 `-V` 标志设置为 4 并重新启动 `kube-proxy`,然后再查看日志。 + +### 边缘案例: 一个 Pod 无法通过服务 IP 连接到它本身{#a-pod-fails-to-reach-itself-via-the-service-ip}。 + +这听起来似乎不太可能,但是确实发生了,并且应该可行。 + +如果网络没有为“发夹模式”流量生成正确配置,通常当 `kube-proxy` 以 `iptables` 模式运行,并且 Pod 与桥接网络连接时,就会发生这种情况。`Kubelet`暴露了 `hairpin-mode`[标志](/docs/admin/kubelet/),如果服务的端点尝试访问自己的服务VIP,则该端点可以把流量负载均衡回来到它们自身。 +`hairpin-mode` 标志必须被设置为 `hairpin-veth` 或者`promiscuous-bridge`。 + +解决此问题的常见步骤如下: + +* 确认 `hairpin-mode` 被设置为 `hairpin-veth` 或 `promiscuous-bridge`. +您应该可以看到下面这样。本例中 `hairpin-mode` 被设置为 +`promiscuous-bridge` 。 ```shell -u@node$ ps auxw|grep kubelet +ps auxw | grep kubelet +``` +```none root 3392 1.1 0.8 186804 65208 ? Sl 00:51 11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0 - ``` + -```shell + +* 确认有效的 `hairpin-mode`。要做到这一点,您必须查看 kubelet 日志。访问日志取决于节点的操作系统。在一些操作系统上,它是一个文件,如 /var/log/kubelet.log,而其他操作系统则使用 `journalctl` 访问日志。请注意,由于兼容性,有效的 `hairpin-mode` 可能不匹配 `--hairpin-mode` 标志。在 kubelet.log 中检查是否有带有关键字 `hairpin` 的日志行。应该有日志行指示有效的 `hairpin-mode`,就像下面这样。 + +```none I0629 00:51:43.648698 3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge" ``` + -### Pod 无法通过 Service IP 访问自己 - -如果网络没有为“发夹模式”流量生成正确配置,通常当 `kube-proxy` 以 `iptables` 模式运行,并且 Pod 与桥接网络连接时,就会发生这种情况。`Kubelet` 公开了一个 `hairpin-mode` 标志,如果 pod 试图访问它们自己的 Service VIP,就可以让 Service 的端点重新负载到他们自己身上。`hairpin-mode` 标志必须设置为 `hairpin-veth` 或者 `promiscuous-bridge`。 - -解决这一问题的常见步骤如下: - -* 确认 `hairpin-mode` 被设置为 `hairpin-veth` 或者 `promiscuous-bridge`。您应该看到下面这样的内容。在下面的示例中,`hairpin-mode` 被设置为 `promiscuous-bridge`。 - -```shell -u@node$ ps auxw|grep kubelet -root 3392 1.1 0.8 186804 65208 ? Sl 00:51 11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0 - -``` - -* 确认有效的 `hairpin-mode`。要做到这一点,您必须查看 kubelet 日志。访问日志取决于节点的操作系统。在一些操作系统上,它是一个文件,如 /var/log/kubelet.log,而其他操作系统则使用 `journalctl` 访问日志。请注意,由于兼容性,有效的 `hairpin-mode` 可能不匹配 `--hairpin-mode` 标志。在 kubelet.log 中检查是否有带有关键字 `hairpin` 的日志行。应该有日志行指示有效的 `hairpin-mode`,比如下面的内容。 -```shell -I0629 00:51:43.648698 3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge" -``` - -* 如果有效的发夹模式是 `hairpin-veth`,请确保 `Kubelet` 具有在节点上的 `/sys` 中操作的权限。如果一切正常工作,您应该看到如下内容: +* 如果有效的发卡模式是 `hairpin-veth`, 保证 `Kubelet` 有操作节点上 `/sys` 的权限。如果一切正常,您将会看到如下输出: ```shell for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done +``` +```none 1 1 1 1 ``` -* 如果有效的发夹模式是 `promiscuous-bridge`,则请确保 `Kubelet` 拥有在节点上操纵 Linux 网桥的权限。如果正确使用和配置了 cbr0 网桥,您应该看到: - -```shell -u@node$ ifconfig cbr0 |grep PROMISC -UP BROADCAST RUNNING PROMISC MULTICAST MTU:1460 Metric:1 - -``` - -* 如果上述任何一项都没有效果,请寻求帮助。 - +* 如果有效的发卡模式是 `promiscuous-bridge`, 保证 `Kubelet` 有操作节点上 linux bridge 的权限。如果 `cbr0` 桥正在被使用且被正确设置,您将会看到如下输出: + +```shell +ifconfig cbr0 |grep PROMISC +``` +```none +UP BROADCAST RUNNING PROMISC MULTICAST MTU:1460 Metric:1 +``` + + +* 如果以上步骤都不能解决问题,请寻求帮助。 + ## 寻求帮助 -如果您走到这一步,那么就真的是奇怪的事情发生了。您的 `Service` 正在运行,有 `Endpoints`,您的 `Pods` 也确实在服务中。您的 DNS 正常,`iptables` 规则已经安装,`kube-proxy` 看起来也正常。然而 `Service` 不起作用。这种情况下,您应该让我们知道,这样我们可以帮助调查! - -使用 [Slack](/docs/troubleshooting/#slack) 或者 [Forum](https://discuss.kubernetes.io) 或者 [GitHub](https://github.com/kubernetes/kubernetes) 联系我们。 +如果您走到这一步,那么就真的是奇怪的事情发生了。您的 Service 正在运行,有 Endpoints ,您的 Pods 也确实在服务中。您的 DNS 正常,`iptables` 规则已经安装,`kube-proxy` 看起来也正常。然而服务还是没有正常工作。这种情况下,请告诉我们,这样我们可以帮助调查! +通过 +[Slack](/docs/troubleshooting/#slack) 或者 +[Forum](https://discuss.kubernetes.io) 或者 +[GitHub](https://github.com/kubernetes/kubernetes) +联系我们。 ## {{% heading "whatsnext" %}} - -访问[故障排查文档](/docs/troubleshooting/)获取更多信息。 - - - +访问 [故障排查文档](/docs/troubleshooting/) 获取更多信息。