之前,您使用该kubectl expose
命令创建了一个配置负载均衡器的服务,以便您可以与运行在您的 pod 中的应用程序通信。您现在将采用不同的方法。出于开发、测试和调试目的,您可能希望直接与特定 pod 通信,而不是使用将连接转发到随机选择的 pod 的服务。
您已经了解到,每个 pod 都分配有自己的 IP 地址,集群中的每个其他 pod 都可以访问它。此 IP 地址通常位于集群内部。您无法从本地计算机访问它,除非 Kubernetes 以特定方式部署 - 例如,在没有 VM 的情况下使用 kind 或 Minikube 创建集群时。
通常,要访问 pod,您必须使用以下部分中描述的方法之一。
首先,让我们确定 pod 的 IP 地址。
您可以通过检索 pod 的完整 YAML 并搜索podIP
该部分中的字段来获取 pod 的 IP 地址 status
。或者,您可以使用 kubectl describe
获取IP,但最简单的方法是使用 kubectl get -o wide
选项:
kubectl get pod kiada -o wide
# output
NAME READY STATUS RESTARTS AGE IP NODE ...
kiada 1/1 Running 0 35m 10.244.2.4 worker2 ...
如 IP 列所示,我的 pod 的 IP 是 10.244.2.4
. 现在我需要确定应用程序正在侦听的端口号。
监听端口
如果我不是应用程序的作者,我将很难找出应用程序侦听的端口。我可以检查它的源代码或容器映像的 Dockerfile,因为通常在那里指定端口,但我可能无权访问任何一个。如果其他人创建了 pod,我怎么知道它正在侦听哪个端口?
幸运的是,您可以在 pod 定义本身中指定端口列表。没有必要指定任何端口,但最好还是手动指定端口列表。
为什么要在 POD 定义中指定容器端口
在 pod 定义中指定端口纯粹是为了提供信息。它们的省略对客户端是否可以连接到 pod 的端口没有影响。如果容器允许通过绑定到其 IP 地址的端口接受连接,则任何人都可以连接到它,即使该端口未在 pod 规范中明确指定,或者您指定了错误的端口号。
尽管如此,始终指定端口是一个好主意,这样任何有权访问您的集群的人都可以看到每个 pod 公开了哪些端口。通过显式定义端口,您还可以为每个端口分配一个名称,这在您通过服务公开 pod 时非常有用。
pod 清单(kubectl get po kiada -o yaml
)表明容器使用端口 8080,因此您现在拥有与应用程序通信所需的一切。
Kubernetes 网络模型规定每个 pod 都可以从任何其他 pod 访问,并且每个*节点*都可以访问集群中任何节点上的任何 pod。
因此,与您的 pod 通信的一种方法是登录到您的一个工作节点并从那里与 pod 对话。您已经了解到登录节点的方式取决于您用于部署集群的方式。如果您使用的是 kind,请运行 docker exec -it kind-worker bash
,如果您使用的是 Minikube,请运行 minikube ssh
。
登录到节点后,使用 curl
带有 pod 的 IP 和端口的命令来访问您的应用程序。我的 pod 的 IP 是 10.244.2.4,端口是 8080,所以我运行以下命令:
curl 10.244.2.4:8080
# output
Kiada version 0.1. Request processed by "kiada". Client IP: ::ffff:10.244.2.1
通常你不会使用这种方法与你的 pod 通信,但是如果有通信问题并且你想通过首先尝试最短的通信路径来找到原因,你可能需要使用它。在这种情况下,最好登录到 pod 所在的节点并从那里运行 curl
。它与 pod 之间的通信发生在本地,因此这种方法始终具有最高的成功机会。
测试应用程序连接性的第二种方法是 curl
在您专门为此任务创建的另一个 pod 中运行。使用此方法测试其他 pod 是否能够访问您的 pod。即使网络完美运行,情况也可能并非如此。后续,您将学习如何通过将 Pod 彼此隔离来锁定网络。在这样的系统中,一个 pod 只能与它被允许的 pod 对话。
kubectl run --image=curlimages/curl -it --restart=Never --rm client-pod curl 10.244.2.4:8080
# output
Kiada version 0.1. Request processed by "kiada". Client IP: ::ffff:10.244.2.5
pod "client-pod" deleted
curlimages/curl
此命令运行一个带有从映像创建的单个容器的 pod 。您还可以使用任何其他提供 curl
二进制可执行文件的图像。-it
选项将您的控制台附加到容器的标准输入和输出,该 --restart=Never
选项确保当 curl
命令及其容器终止时该 pod 被视为已完成,并且 --rm
选项在最后删除该 pod。pod 的名称是 client-pod
,在其容器中执行的命令是 curl 10.244.2.4:8080
.
提示:
您还可以修改命令以
bash
在客户端 pod 中运行 shell,然后从 shell 中运行curl
。
当您专门测试 pod 到 pod 的连接性时,创建一个 pod 以查看它是否可以访问另一个 pod 很有用。
在开发过程中,与运行在您的 pod 中的应用程序通信的最简单方法是使用 kubectl port-forward
命令,该命令允许您通过绑定到本地计算机上的网络端口的代理与特定的 pod 通信,如下图所示。
要打开与 pod 的通信路径,您甚至不需要查找 pod 的 IP,因为您只需要指定其名称和端口即可。以下命令启动一个代理,将您计算机的本地端口转发 8080
到 kiada
pod 的 8080
端口。
kubectl port-forward kiada 8080
# output
... Forwarding from 127.0.0.1:8080 -> 8080
... Forwarding from [::1]:8080 -> 8080
-----------------------------------------
curl localhost:8080
# output
Kiada version 0.1. Request processed by "kiada". Client IP: ::ffff:127.0.0.1
如您所见,curl
已连接到本地代理并收到来自 pod 的响应。虽然该 port-forward
命令是在开发和故障排除期间与特定 pod 通信的最简单方法,但就底层发生的情况而言,它也是最复杂的方法。通信通过几个组件,因此如果通信路径中有任何问题,您将无法与 pod 通信,即使 pod 本身可以通过常规通信通道访问。
提示
kubectl port-forward
命令还可以将连接转发到服务而不是 pod,并具有其他一些有用的功能。运行kubectl port-forward --help
以了解更多信息。
如图所示,curl
进程连接代理,代理连接API服务器,API服务器再连接Pod所在节点上的 Kubelet,Kubelet 再通过 Pod 的回环设备连接容器(通俗一点,通过 localhost 地址),通过本地主机地址)。我相信你也会认为他们通信的流程过于长了。
注意
容器中的应用程序必须绑定到环回设备上的端口,以便 Kubelet 访问它。如果它只侦听 pod 的
eth0
网络接口,您将无法使用kubectl port-forward
命令访问它。
您的 Node.js 应用程序将其日志写入标准输出流。容器化应用程序通常不会将日志写入文件,而是将日志记录到标准输出 ( stdout ) 和标准错误流 ( stderr )。这允许容器运行时拦截输出,将其存储在一致的位置(通常 /var/log/containers
)并提供对日志的访问,而无需知道每个应用程序存储其日志文件的位置。
当您使用 Docker 在容器中运行应用程序时,您可以使用 docker logs <container-id>
显示其日志当您在 Kubernetes 中运行应用程序时,您可以使用 登录到托管 pod 的节点并显示其日志 docker logs
,但 Kubernetes 提供了一种更简单的方法来执行此操作用 kubectl logs
命令。
要查看您的 pod 的日志(更具体地说,是容器的日志),请运行以下命令:
kubectl logs kiada
# output
Kiada - Kubernetes in Action Demo Application
---------------------------------------------
Kiada 0.1 starting...
Local hostname is kiada
Listening on port 8080
Received request for / from ::ffff:10.244.2.1
Received request for / from ::ffff:10.244.2.5
Received request for / from ::ffff:127.0.0.1
如果您想实时流式传输应用程序日志以查看每个请求,您可以使用以下 --follow
选项(或更短的版本 -f
)运行命令:
kubectl logs kiada -f
现在向应用程序发送一些额外的请求并查看日志。完成后按 ctrl-C 停止流式传输日志。
您可能已经注意到我们忘记在日志语句中包含时间戳。没有时间戳的日志可用性有限。幸运的是,容器运行时会将当前时间戳附加到应用程序生成的每一行。您可以使用以下 --timestamps=true
选项显示这些时间戳:
kubectl logs kiada --timestamps=true
# output
2020-02-01T09:44:40.954641934Z Kiada - Kubernetes in Action Demo Application
2020-02-01T09:44:40.954843234Z ---------------------------------------------
2020-02-01T09:44:40.955032432Z Kiada 0.1 starting...
2020-02-01T09:44:40.955123432Z Local hostname is kiada
2020-02-01T09:44:40.956435431Z Listening on port 8080
2020-02-01T09:50:04.978043089Z Received request for / from ...
2020-02-01T09:50:33.640897378Z Received request for / from ...
2020-02-01T09:50:44.781473256Z Received request for / from ...
提示
您可以通过仅键入
--timestamps
不带值的方式来显示时间戳。对于布尔选项,只需指定选项名称即可将选项设置为true
. 这适用于所有采用布尔值并默认为false
.
如果您运行的第三方应用程序在其日志输出中不包含时间戳,则前面的功能非常棒,但是每一行都带有时间戳的事实给我们带来了另一个好处:按时间过滤日志行。Kubectl 提供了两种按时间过滤日志的方法。
第一个选项是当您只想显示过去几秒、几分钟或几小时的日志时。例如,要查看最近两分钟产生的日志,请运行:
kubectl logs kiada --since=2m
另一个选项是使用该选项显示在特定日期和时间之后生成的日 志--since-time
。要使用的时间格式是 RFC3339。例如,以下命令用于打印 2020 年 2 月 1 日上午 9:50 之后生成的日志:
kubectl logs kiada --since-**time**=2020-02-01T09:50:00Z
您还可以指定要显示的日志末尾的行数,而不是使用时间来限制输出。要显示最后十行,请尝试:
kubectl logs kiada --tail=10
提示
接受值的 Kubectl 选项可以用等号或空格指定。代替
--tail=10
,您也可以键入--tail 10
。
Kubernetes 为每个容器保留一个单独的日志文件。它们通常存储在运行容器的 Node 上的 /var/log/containers
目录。为每个容器创建一个单独的文件。如果容器重新启动,它的日志将被写入一个新文件。因此,如果在您使用 kubectl logs -f
跟踪其日志时重新启动容器,该命令将终止,您需要再次运行它以流式传输新容器的日志。
该 kubectl logs
命令只显示当前容器的日志。要查看前一个容器的日志,请使用 --previous
(或 -p
)选项。
注意
根据您的集群配置,当日志文件达到一定大小时,它们也可能会被轮转切割(rotated)。在这种情况下,
kubectl logs
只会显示当前的日志文件。流式传输日志时,您必须重新启动命令以在日志轮换(rotated)时切换到新文件。
当你删除一个 pod 时,它的所有日志文件也会被删除。要使 pod 的日志永久可用,您需要设置一个集中的集群日志记录系统。
如果您的应用程序将其日志写入文件而不是标准输出,您可能想知道如何访问该文件。理想情况下,您应该配置集中式日志记录系统来收集日志,以便您可以在集中的查看它们。但有时您只想保持简单,不介意手动访问日志。后续的文章中,您将学习如何将日志和其他文件从容器复制到您的计算机,或者从计算机复制到容器,以及如何在正在运行的容器中运行命令。您可以使用任何一种方法来显示日志文件或容器内的任何其他文件。
有时您可能希望将文件添加到正在运行的容器或从中检索文件。在运行的容器中修改文件不是你通常会做的事情,至少在生产中不会,但它在开发过程中很有用。
Kubectl 提供了 cp
命令,将文件或目录从本地计算机复制到任何 pod 的容器或从容器复制到计算机的命令。例如,如果您想修改 kiada
pod 提供的 HTML 文件,可以使用以下命令将其复制到本地文件系统:
kubectl cp kiada:html/index.html /tmp/index.html
此命令将文件 /html/index.html
文件从命名的 kiada
pod 复制到您计算机上的文件中。您现在可以在本地 /tmp/index.html
编辑文件。对更改感到满意后,使用以下命令将文件复制回容器:
kubectl cp /tmp/index.html kiada:html/
注意
kubectl cp
命令要求tar
命令存在于您的容器中,但此要求将来可能会发生变化。
在调试容器中运行的应用程序时,可能需要从内部检查容器及其环境。Kubectl 也提供了这个功能。您可以使用 kubectl exec
命令执行容器文件系统中存在的任何二进制文件。
例如,您可以通过运行以下命令列出 kiada
pod 中容器中运行的进程:
kubectl exec kiada -- ps aux
# output
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 1.3 812860 27356 ? Ssl 11:54 0:00 node app.js
root 120 0.0 0.1 17500 2128 ? Rs 12:22 0:00 ps aux
这是 Kubernetes 等效的 Docker 命令,它允许您在任何 pod 中远程运行命令,而无需登录到托管 pod 的节点。如果您曾经使用 ssh
在远程系统上执行过命令,您会发现这 kubectl exec
并没有太大的不同。
您 curl
在一次性客户端 pod 中执行了命令以向您的应用程序发送请求,但您也可以在 kiada
pod 本身内部运行命令:
kubectl exec kiada -- curl -s localhost:8080
# output
Kiada version 0.1. Request processed by "kiada". Client IP: ::1
为什么在 KUBECTL EXEC 命令中使用双破折号?
命令中的双破折号 (
--
) 将 kubectl 参数与要在容器中执行的命令分隔开。如果命令没有以破折号开头的参数,则不需要使用双破折号。如果您在前面的示例中省略双破折号,该-s
选项将被解释为 的选项kubectl exec
并导致以下误导性错误:> kubectl exec kiada curl -s localhost:8080 > # output > The connection to the server localhost:8080 was refused – did you specify the right host or port? > ``` > > 这可能看起来像 Node.js 服务器拒绝接受连接,但问题出在其他地方。curl 命令永远不会执行。`kubectl `当它尝试与位于 `localhost:8080` 的 Kubernetes API 服务器进行通信时,它会自行报告该错误 ,而该服务器不在服务器所在的位置。如果您运行该`kubectl options`命令,您将看到该 `-s` 选项可用于指定 Kubernetes API 服务器的地址和端口。kubectl 没有将该选项传递给 curl,而是将其作为自己的。添加双破折号可以防止这种情况。 > > 幸运的是,为了防止出现这种情况,如果您忘记了双破折号,较新版本的 kubectl 设置为返回错误。 #### 在容器中运行交互式 shell 前面的两个示例展示了如何在容器中执行单个命令。命令完成后,您将返回到您的 shell。如果要在容器中运行多个命令,可以在容器中运行一个shell,如下所示: ```sh kubectl exec -it kiada -- bash # output root@kiada:/#
-it
是两个选项的缩写:-i(stdin)
和 -t(Teletype,TTY)
,表示您希望 bash
通过将标准输入(-i
)传递给容器并将其标记为终端 (-t
) 以交互方式执行命令。
您现在可以通过在 shell 中执行命令来探索容器的内部。例如,您可以通过运行 ls -la
查看容器中的文件,使用 ip link
查看其网络接口,或使用 ping
测试其连接性。您可以运行容器中可用的任何工具。
应用程序的容器映像包含许多重要的调试工具,但并非每个容器映像都如此。为了使镜像更小并提高容器中的安全性,生产中使用的大多数容器不包含除容器主进程所需的二进制文件之外的任何二进制文件。这显着减少了攻击面,但也意味着您无法在生产容器中运行 shell 或其他工具。幸运的是,一个名为*临时容器(ephemeral containers)*的新 Kubernetes 功能允许您通过共享命名空间的方式来调试正在运行的容器。(https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/ephemeral-containers/)
kubectl attach
命令是与正在运行的容器交互的另一种方式。它将自己附加到容器中运行的主进程的标准输入、输出和错误流。通常,您只使用它与从标准输入读取的应用程序进行交互。
如果应用程序不从标准输入读取,则 kubectl attach
命令只是流式传输应用程序日志的另一种方式,因为这些通常写入标准输出和错误流,并且 attach
命令像 kubectl logs -f
命令一样流式传输它们。
kubectl attach kiada
# output
Defaulting container name to kiada.
Use 'kubectl describe pod/kiada -n default' to see all of the containers in this pod.
If you don't see a command prompt, try pressing enter.
现在,当您使用另一个终端向应用程序发送新的 HTTP 请求时 curl
,您将看到应用程序记录到标准输出的行也打印在执行 kubectl attach
命令的终端中。
# pod.kiada-stdin.yaml
apiVersion: v1
kind: Pod
metadata:
name: kiada-stdin
spec:
containers:
- name: kiada
image: luksa/kiada:0.2
stdin: true
ports:
- containerPort: 8080
正如您在清单中看到的,如果在 pod 中运行的应用程序想要从标准输入中读取,您必须通过将 stdin
容器定义中的字段设置为 来在 pod 清单中指明这一点 true
。这告诉 Kubernetes 为标准输入流分配一个缓冲区,否则应用程序在尝试从缓冲区中读取时总是会收到一个 EOF
。
使用 kubectl apply
构建 Pod
kubectl apply -f pod.kiada-stdin.yaml
# output
pod/kiada-stdin created
要启用与应用程序的通信,请再次使用 kubectl port-forward
命令,但由于 8080
先前执行的 port-forward
命令仍在使用本地端口,您必须终止它或选择不同的本地端口转发到新的 pod。您可以按如下方式执行此操作:
kubectl port-forward kiada-stdin 8888:8080
# output
Forwarding from 127.0.0.1:8888 -> 8080
Forwarding from [::1]:8888 -> 8080
您现在可以通过 http://localhost:8888 访问应用程序:
curl localhost:8888
# output
Kiada version 0.2. Request processed by "kiada-stdin". Client IP: ::ffff:127.0.0.1
让我们通过 kubectl attach
写入应用程序的标准输入流来设置状态消息。运行以下命令:
kubectl attach -i kiada-stdin
注意命令中附加选项的使用 -i
。它指示 kubectl
将其标准输入传递给容器。
提示
和
kubectl exec
命令一样,kubectl attach
也支持--tty
or-t
选项,表示标准输入是终端(TTY),但容器必须配置为通过tty
容器定义中的字段分配终端。
您现在可以将状态消息输入终端并按 ENTER 键。例如,键入以下消息:This is my custom status message.
应用程序将新消息打印到标准输出:
Status message set to: This is my custom status message.
要查看应用程序现在是否在其对 HTTP 请求的响应中包含该消息,请重新执行该 curl
命令或在 Web 浏览器中刷新页面:
curl localhost:8888
# output
Kiada version 0.2. Request processed by "kiada-stdin". Client IP: ::ffff:127.0.0.1
This is my custom status message.
您可以通过在运行该 kubectl attach
命令的终端中键入另一行来再次更改状态消息。要退出 attach
命令,请按 Control-C 或等效键。
提示
容器定义中的一个附加字段
stdinOnce
确定在附加会话结束时是否关闭标准输入通道。它默认设置为false
,允许您在每个kubectl attach
会话中使用标准输入。如果将其设置为true
,则标准输入仅在第一个会话期间保持打开状态。
好好学习,天天向上