从HTTP到K8s探针:彻底搞懂HTTPGet健康检查
一、HTTP 通信的本质是什么?
不管是浏览器访问网站,还是 curl 发请求,甚至是 K8s 的 kubelet 做健康检查,本质上都是客户端给服务端发 HTTP 请求,服务端给客户端返回 HTTP 响应,这两个东西都有固定的格式
1.1 HTTP 请求的完整结构
举个最常见的例子:你要访问容器里的健康检查接口http://127.0.0.1:80/health,你发出去的 HTTP 请求,实际长这样:
# 第一行:请求行,固定格式 GET /health HTTP/1.1 # 接下来的部分:请求头,都是「键: 值」的格式,用来传递额外信息 Host: 127.0.0.1:80 User-Agent: curl/7.68.0 Accept: */* Custom-Header: Awesome # 这里必须有一个空行,用来分隔「请求头」和「请求体」 # 最后是请求体:GET 请求一般没有内容,POST 提交表单的时候才会有我们逐行拆解,搞懂每一部分的作用:
请求行:这是请求的第一行,告诉服务端最核心的信息
GET:请求方法,意思是我要从服务端获取数据,健康检查一般都用 GET 方法。/health:我要访问的服务端的路径,也就是你要调用的接口地址。HTTP/1.1:我们用的 HTTP 协议的版本,目前大部分服务都用这个版本。
请求头:这就是我们常说的请求头,每一行都是一个键值对,用来给服务端传递额外的附加信息,比如:
Host:告诉服务端,我要访问的 IP 和端口是什么,用来区分同一个服务器上的不同站点。User-Agent:告诉服务端,我用的是什么客户端,比如是 curl 还是 Chrome 浏览器。Accept:告诉服务端,我能接收什么格式的返回内容,比如是 JSON 还是 HTML。Custom-Header: Awesome:这就是自定义的请求头,是我们额外加给服务端的信息,用来适配服务的特殊要求。
空行:这是 HTTP 协议规定必须有的,用来告诉服务端:「我的请求头已经发完了,接下来如果有内容就是请求体了」,没有这个空行,服务端就没办法正确解析你的请求。
请求体:GET 请求一般不需要带额外的请求数据,所以这里是空的,只有 POST 提交表单、上传文件的时候,才会把要提交的内容放在这里。
1.2 HTTP 响应:状态码到底是什么?
服务端收到你的请求之后,会给你返回一个 HTTP 响应,格式和请求非常像:
# 第一行:响应行 HTTP/1.1 200 OK # 接下来的部分:响应头,也是键值对,给客户端传递额外信息 Date: Mon, 13 Apr 2026 08:00:00 GMT Server: Apache/2.4.41 (Ubuntu) Content-Length: 2 Content-Type: text/plain; charset=utf-8 # 同样的空行,分隔响应头和响应体 # 最后是响应体:服务端返回给你的具体内容 ok这里最关键的,就是响应行里的状态码:
上面例子里的
200就是状态码,OK是它的文字描述,用来标记这个请求的处理结果。状态码是 HTTP 协议里规定好的,有明确的分类,不同的范围代表不同的结果:
状态码范围 | 含义 | 常见例子 |
1xx | 临时信息,请求还在处理中 | 100 Continue(服务端说你继续发剩下的内容) |
2xx | 请求成功,服务端正常处理了你的请求 | 200 OK(请求成功)、201 Created(资源创建成功) |
3xx | 重定向,需要你跳去别的地址访问 | 301 永久重定向、302 临时重定向 |
4xx | 客户端错误,你发的请求有问题 | 404 Not Found(你访问的路径不存在)、403 Forbidden(你没权限访问) |
5xx | 服务端错误,服务端自己出问题了 | 500 Internal Server Error(服务内部出错了) |
二、K8s 的 HTTPGet 探针:它到底在做什么?
搞懂了上面的 HTTP 基础,你再看 HTTPGet 探针,就会发现它的逻辑非常简单:kubelet 做的事情,和你在终端里用 curl 命令发 HTTP 请求,是完全一模一样的
具体来说,它的工作流程是这样的:
它会先拿到你容器的 IP 地址,然后按照你配置的端口、路径,给容器发一个 HTTP GET 请求,就相当于你在终端里执行了这么一条命令:
curl http://容器的IP:你配置的端口/你配置的路径然后它会等服务端返回响应,拿到响应的状态码。
最后它做判断:
如果状态码在
200~399之间,就说明请求成功了,服务端正常处理了,那容器就是健康的。如果是 4xx 或者 5xx,就说明请求出问题了,那检测就失败了。
这就是 HTTPGet 探针的全部逻辑,没有任何黑科技,本质上就是 kubelet 帮你自动定时 curl 一下你的健康接口而已。
三、自定义请求头:curl 的 - H 和 K8s 的 httpHeaders 是同一个东西
很多人搞不懂的httpHeaders配置,其实也非常好理解:它和你用 curl 的时候加的-H "xxx: xxx"参数,是完全一模一样的东西。
举个例子,如果你在 K8s 的探针里这么配置:
livenessProbe: httpGet: path: /health port: 80 httpHeaders: - name: Custom-Header value: Awesome
那 kubelet 发出去的 HTTP 请求,就会自动带上Custom-Header: Awesome这个请求头,就和你在终端里执行下面这条 curl 命令的效果,100% 完全一样:
curl -H "Custom-Header: Awesome" http://容器IP:80/health
为什么我们要加自定义请求头?
你可能会问,好好的为什么要加这个东西? 因为有些服务的健康检查接口,做了特殊的限制: 比如有些服务要求,只有带了X-Health-Check: true这个请求头的请求,才能访问健康接口,不然就会返回 403 没权限。 如果你不加这个请求头,kubelet 发的默认请求,就会被服务端拒绝,返回 403,探针就会误以为检测失败,然后把你的容器重启,这就造成了误判。 所以我们就需要配置httpHeaders,把这个要求的请求头加上,让 kubelet 的请求能正常访问健康接口。
四、实战案例:从 404 错误看探针的工作流程
我们用一个实际的测试案例,把上面的所有知识点串起来,你就能彻底搞懂了。
我们做这么一个测试:用 httpd 镜像(一个常用的 Web 服务器),配置存活探针,访问一个不存在的路径/httppage,看看会发生什么。
我们的配置文件
apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-http spec: containers: - name: liveness image: httpd imagePullPolicy: IfNotPresent args: - /bin/sh - -c - while true; do echo ok > /usr/local/apache2/htdocs/index.html; sleep 1; done ports: - containerPort: 80 livenessProbe: httpGet: path: /httppage port: 80 httpHeaders: - name: Custom-Header value: Awesome initialDelaySeconds: 3 periodSeconds: 3 restartPolicy: OnFailure整个流程的拆解
容器启动之后,过了 3 秒,kubelet 开始第一次探测。
kubelet 给容器的 80 端口,发了一个 GET 请求,访问
/httppage,并且带上了我们配置的Custom-Header: Awesome请求头。httpd 服务收到请求之后,发现自己根本没有
/httppage这个路径,所以它返回了一个 404 的状态码。kubelet 拿到响应,发现状态码是 404,属于 4xx 的客户端错误,不在 200~399 的成功范围内,所以它判定:探针检测失败了。
连续几次检测都失败之后,K8s 就把这个容器杀掉,然后重启它。
最后我们看 Pod 的事件,就能看到这个提示:
Warning Unhealthy 9m2s (x9 over 10m) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404这个提示正好对应了我们上面的整个流程:因为状态码是 404,所以探针失败了。
五、总结
HTTP 请求和响应都有固定的格式,请求头是用来传递附加信息的键值对,状态码是用来标记请求处理结果的。
K8s 的 HTTPGet 探针,本质上就是 kubelet 帮你定时 curl 你的健康接口,和你自己用 curl 发请求没有任何区别。
httpHeaders配置,和 curl 的-H参数是同一个东西,用来给请求加自定义的请求头,适配服务的特殊要求。探针会根据响应的状态码判断健康状态,200~399 算成功,4xx、5xx 算失败。
