这可能是所有Kubernetes管理员都遇到过的问题:他们的容器无法启动,又不知道原因。可能通过容器退出码查处一点蛛丝马迹,但仅凭该信息并不足以找出容器没有按预期启动的根本原因。另外,由于Kubernetes不会在容器启动或意外终止失败时自动通知管理员,所以我们并不能立即发现问题。幸运的是,可以做到持续跟踪容器状态,并获得关于启动失败和终止的特定信息。但是你需要知道去哪里找这些信息,而这些信息并没有你想象的那么明显。在本文中,我们将解释在K8s中跟踪容器重启和终止的方法,并指出可以用于实现持续“监控容器重启”的示例代码。K8S中容器和Pod状态
我们不想在这里说得太早,所以让我们先解释容器和pod在Kubernetes中如何运行的基础知识开始。当你想在Kubernetes中部署一个应用程序时,你需要部署一个Pod。Pod承载一个或多个容器。理想情况下,当你告诉Kubernetes启动Pod时,所有的容器都将成功启动。在这种情况下,你最终得到的是一个在k8s中称之为成功状态的Pod。但如果一个或多个容器由于某种原因没有启动,Pod就会进入Failed状态。在这种情况下,您的应用程序将无法运行,因为它所依赖的Pod已经失败。追中Pod为何失败
由于容器终止失败,Pods可能最终处于Failed状态,原因有很多。常见的根本原因包括未能获取容器镜像,因为镜像不可用、应用程序代码中的错误或Pod的YAML中的错误配置。但仅仅知道一个Pod失败了并不意味着你就知道失败的原因。除非深入研究,否则你只能知道它处于Failed状态。容器退出码
深入挖掘的一种方法是查看容器退出码。容器退出码是数值类型,给出了容器停止运行的原因。你可以通过运行以下命令获取Pod中容器的退出代码:kubectl get pod termination-demo
不幸的是,容器退出码并不总是提供可用的信息。原因是在某些情况下,退出码是由在容器中运行的应用程序决定的,它可能使用与容器运行时不同的退出码。因此,例如,你可能会得到一个143退出码,这在理论上表示容器得捕获了一个SIGTERM信号。但是如果代码是由你的应用程序生成的,那么143可能意味着完全不同的东西。这里的关键点是,虽然容器退出代码有时很有用,但它不是定位容器失败原因的主要来源。K8S API
你可能会倾向于使用Kubernetes API来研究容器终止原因。该API允许跟踪与集群状态更改相关的某些类型的“事件”。不幸的是,由于API目前没有包含用于容器重启或终止的事件类型,因此它对这个目的不是很有用。ContainerStatus结构体
获取关于容器重启信息的最好方法是查看ContainerStatus结构体,它包含在相关Pod的PodSpec中。// k8s.io/api/core/v1/types.go
type ContainerStatus struct {
Name string
State ContainerState
LastTerminationState
ContainerState
Ready bool
RestartCount int32
Image string
ImageID string
ContainerID string
Started *bool
}
本文感兴趣的是LastTerminationState字段,如果容器成功运行,该字段将为空。每当容器重新启动时,该字段将被覆盖。通过使用cache.ListWatch来监视Pod事件更新,比较新的和旧的Pod的ContainerStatus结构体,确定容器是否已经重启。使用这种方式,你可以在容器重启事件发生时识别出来。至于找出事件发生的原因,你可以使用ContainerStateTerminated结构体,如下所示:
type ContainerStateTerminated struct {
ExitCode int32
Signal int32
Reason string
Message string
StartedAt metav1.Time
FinishedAt metav1.Time
ContainerID string
}
此结构体提供了容器重启原因所需的所有内容,独立于退出代码。使用这个结构体,在github中已有人开发了一个工具,用来监控和记录Kubernetes中的容器重启事件。地址:https://github.com/groundcover-com/blog/tree/main/blog_k8s_containers_restarts。实时Kubernetes容器重启监控
我们上面描述的方法能够在容器重启和终止发生时立即获得有关它们的特定信息。这是K8s管理员能够解决令人沮丧和烦恼的问题的一种方法——容器不能按预期运行,原因很难通过查看容器退出码或Kubernetes事件来找出。