一、前言
问题排查过程,源码部分均由我的开发同事排查和记录;在征得其同意后,由我发表在此。
二、问题
某天接到客户反馈,pod的事件中出现大量的 warning event: Readiness probe failed: OCI runtime exec failed: exec failed: EOF: unknown。但不影响客户访问该服务。
三、环境
特别说明:客户在负责运行业务的k8s节点上坚持开启了cpu-manager
组件 | 版本 |
---|
四、排查
1、接到客户反馈后,检查该pod所在节点的kubelet日志,如下:
probe的错误类型为failure,对应代码如下:图片2、再查看docker日志,如下:

虽然从Docker日志中显示是 stream copy error,但实际上是底层的 runc 返回了 EOF,导致返回了 error。3、因为日志中显示 probe 类型为 Failure,因此 e.CombinedOutPut() 的 err != nil,并且 ExitStatus 不为 0,data 的值为 OCI runtime exec failed: exec failed: unexpected EOF: unknown,最终会调用到 RunInContainer 方法
ExecSync 是通过 GRPC 调用了 dockershim 的 ExecSync
dockershim 最终调用到 ExecInContainer 方法,并且该方法的返回了 exitcode 不为 0 的 error。
ExecInContainer 做了以下几件事:
那么日志中打印的报错就是 response stream 传递过来的字符流。也就是说,dockerd 的 response 中包含了错误值。
此时去 docker 代码中查找原因,ExecStart 会调用到 dockerd 的以下代码:
根据上面 docker 的日志,err 的错误信息为:OCI runtime exec failed: exec failed: EOF: unknown。也就是说 ContainerExecStart 返回了错误。ContainerExecStart 会调用到 containerd.Exec,也就是 dockerd 和 containerd 之间进行通信
这里 new 了一个 FIFOSet,而 reading from a closed fifo 仅出现在 fifo 被 close 掉时,仍然在读取的情况。即 f.Close() 发生在 f.Read() 前面。在外层可以看到
p.Start 调用到下面的代码,通过 GRPC 和 containerd 通信。
这个 GRPC 调用会到达 containerd 以下的代码:
Exec 的代码如下:
因此是 runc 在运行后输出了 exec failed: EOF: unknown 这个错误。
将 runc 指令循环执行,可少量复现。经过排查,发现 runc exec 在运行期间会读取 container 的 state.JSON,并使用 json decode 时出现异常。
此时联想到开启 kubelet cpu-manager 后,会 update container,也就是更新这个 state.json 文件。导致 runc 读到了部分 cpu-manager 更新的内容。从而导致 json decode 失败。此时排查 runc EOF 和 kubelet cpu-manager update container(默认每 10s 更新一次) 的时间,发现时间点刚好吻合,验证猜想。
查看 runc 是否有修复,发现了这个 pr:。 修复思路是将 saveState 变成原子操作,这样就不会出现读取 state.json 时,读到部分写入的内容,导致 unexpected EOF (或 EOF)的问题
五、解决
关闭cpu-manager
升级runc
如何在ubuntu上安装docker
Docker 是 dotCloud 最近几个月刚宣布的开源引擎,旨在提供一种应用程序的自动化部署解决方案,简单的说就是,在 Linux 系统上迅速创建一个容器(类似虚拟机)并在容器上部署和运行应用程序,并通过配置文件可以轻松实现应用程序的自动化安装、部署和升级,非常方便。 因为使用了容器,所以可以很方便的把生产环境和开发环境分开,互不影响,这是 docker 最普遍的一个玩法。 更多的玩法还有大规模 web 应用、数据库部署、持续部署、集群、测试环境、面向服务的云计算、虚拟桌面 VDI 等等。 注意:由于Docker需要在Linux Kernel 3.8及以上才可以很好的工作【本人在ubuntu12.04 lts 内核3.2也正常安装】,官方更是推荐Ubuntu系统,这里有两种选择:Ubuntu 12.04 LTS或最新的Ubuntu 13.10 而本文比较喜欢倾向LTS,幸好有办法解决Kernel版本问题。 1、更新Ubuntu内核使用如下命令行更新内核至3.8.0-25sudo apt-get install linux-image-3.8.0-25-genericsudo apt-get install linux-headers-3.8.0-25-generic完成后重启电脑,通过命令 “uname -r” 来查看内核是否成功更新。
windows下安装的docker虚拟机有什么用
下面是安装 Docker 客户端并在上面运行容器的简单步骤。 1. 下载 Boot2Docker在我们开始安装之前,我们需要 Boot2Docker 的可执行文件。 可以从 它的 Github 下载最新版本的 Boot2Docker。 在这篇指南中,我们从网站中下载版本 v1.6.1。 我们从那网页中用我们喜欢的浏览器或者下载管理器下载了名为 的文件。 2. 安装 Boot2Docker现在我们运行安装文件,它会安装 Window Docker 客户端、用于 Windows 的 Git(MSYS-git)、VirtualBox、Boot2Docker Linux ISO 以及 Boot2Docker 管理工具,这些对于开箱即用地运行全功能的 Docker 引擎都至关重要。 3. 运行 Boot2Docker安装完成必要的组件之后,我们从桌面上的“Boot2Docker Start”快捷方式启动 Boot2Docker。 它会要求你输入以后用于验证的 SSH 密钥。 然后会启动一个配置好的用于管理在虚拟机中运行的 Docker 的 unix shell。 为了检查是否正确配置,运行下面的 docker version 命令。 docker version
如何查看docker本地镜像文件
我们可以利用docker images 查看本地镜像信息,方便我们使用[Root@docker ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED VIRTUAL /ubuntu latest afec24b 39 hours ago 187.9 MB[root@docker ~]#列出的信息有 镜像名、标签、镜像id、创建的时间、 大小。
发表评论