5.5 pod就绪后发出信号
还有一件关于Service和Ingress的事情需要考虑。已经了解到,如果pod的标签与服务的pod选择器相匹配,那么pod就将作为服务的后端。只要创建了具有适当标签的新pod,它就成为服务的一部分,并且请求开始被重定向到pod。但是,如果pod没有准备好,如何处理服务请求呢?
该pod可能需要时间来加载配置或数据,或者可能需要执行预热过程以防止第一个用户请求时间太长影响了用户体验。在这种情况下,不希望该pod立即开始接收请求,尤其是在运行的实例可以正确快速地处理请求的情况下。不要将请求转发到正在启动的pod中,直到完全准备就绪。
5.5.1 介绍就绪探针
在之前的章节中,了解了存活探针,以及它们如何通过确保异常容器自动重启来保持应用程序的正常运行。与存活探针类似,Kubernetes还允许为容器定义准备就绪探针。
就绪探测器会定期调用,并确定特定的pod是否接收客户端请求。当容器的准备就绪探测返回成功时,表示容器已准备好接收请求。
这个准备就绪的概念显然是每个容器特有的东西。Kubernetes只能检查在容器中运行的应用程序是否响应一个简单的GET/请求,或者它可以响应特定的URL路径(该URL导致应用程序执行一系列检查以确定它是否准备就绪)。考虑到应用程序的具体情况,这种确切的准备就绪的判定是应用程序开发人员的责任。
就绪探针的类型
像存活探针一样,就绪探针有三种类型:
- Exec探针,执行进程的地方。容器的状态由进程的退出状态代码确定。
- HTTP GET探针,向容器发送HTTP GET请求,通过响应的HTTP状态代码判断容器是否准备好。
- TCP socket探针,它打开一个TCP连接到容器的指定端口。如果连接已建立,则认为容器已准备就绪。
了解就绪探针的操作
启动容器时,可以为Kubernetes配置一个等待时间,经过等待时间后才可以执行第一次准备就绪检查。之后,它会周期性地调用探针,并根据就绪探针的结果采取行动。如果某个pod报告它尚未准备就绪,则会从该服务中删除该pod。如果pod再次准备就绪,则重新添加pod。
与存活探针不同,如果容器未通过准备检查,则不会被终止或重新启动。这是存活探针与就绪探针之间的重要区别。存活探针通过杀死异常的容器并用新的正常容器替代它们来保持pod正常工作,而就绪探针确保只有准备好处理请求的pod才可以接收它们(请求)。这在容器启动时最为必要,当然在容器运行一段时间后也是有用的。如果一个容器的就绪探测失败,则将该容器从端点对象中移除。连接到该服务的客户端不会被重定向到pod。这和pod与服务的标签选择器完全不匹配的效果相同。
了解就绪探针的重要性
设想一组pod(例如,运行应用程序服务器的pod)取决于另一个pod(例如,后端数据库)提供的服务。如果任何一个前端连接点出现连接问题并且无法再访问数据库,那么就绪探针可能会告知Kubernetes该pod没有准备好处理任何请求。如果其他pod实例没有遇到类似的连接问题,则它们可以正常处理请求。就绪探针确保客户端只与正常的pod交互,并且永远不会知道系统存在问题。
5.5.2 向pod添加就绪探针
接下来,将通过修改Replication Controller的pod模板来为现有的pod添加就绪探针。
向pod template添加就绪探针
可以通过kubectl edit命令来向已存在的ReplicationController中的pod模板添加探针。
$ kubectl edit rs kubia
当在文本编辑器中打开ReplicationController的YAML时,在pod模板中查找容器规格,并将以下就绪探针定义添加到spec.template.spec.containers下的第一个容器。YAML看起来应该就像下面的代码清单。
代码清单5.17 RC创建带有就绪探针的pod:kubia-rc-readinessprobe.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
labels:
app: kubia
tier: frontend
spec:
# modify replicas according to your case
replicas: 3
selector:
matchLabels:
app: kubia #简单的matchLabels选择器
template:
metadata:
labels:
app: kubia
spec: #期望Pod实现的功能(即在pod中部署)
containers: #生成container,与docker中的container是同一种
- name: kubia #container的名称
image: luksa/kubia:1.0
readinessProbe:
exec:
command:
- ls
- /var/ready
就绪探针将定期在容器内执行 ls /var/ready
命令。如果文件存在,则ls命令返回退出码0,否则返回非零的退出码。如果文件存在,则就绪探针将成功;否则,它会失败。
定义这样一个奇怪的就绪探针的原因是,可以通过创建或删除有问题的文件来触发结果。该文件尚不存在,所以所有的pod现在应该报告没有准备好,是这样的吗?其实并不完全是,正如在前面章节中了解的那样,更改ReplicationController的pod模板对现有的pod没有影响。
换句话说,现有的所有pod仍没有定义准备就绪探针。可以通过使用kubectl get pods列出pod并查看READY列。需要删除pod并让它们通过ReplicationController重新创建。新的pod将进行就绪检查会一直失败,并且不会将其作为服务的端点,直到在每个pod中创建 /var/ready
文件。
观察并修改pod就绪状态
再次列出pod并检查它们是否准备好:
$ kubectl get po
READY列显示出没有一个容器准备好。现在通过创建 /var/ready
文件使其中一个文件的就绪探针返回成功,该文件的存在可以模拟就绪探针成功:
$ kubectl exec kubia-2r1qb -- touch /var/ready
使用 kubectl exec
命令在kubia-2r1qb的pod容器内执行touch命令。如果文件尚不存在,touch命令会创建该文件。就绪探针命令现在应该返回退出码0,这意味着探测成功,并且现在应该显示pod已准备就绪。现在去查看其状态:
$ kubectl get po kubia-2r1qb
该pod还没有准备好。有什么不对或者这是预期的结果吗?用 kubectl describe
kube来获得更详细的关于pod的信息。输出应该包含以下内容:
Readiness exec [ls /var/ready] delay=0s timeout=1s period=10s #success=1 #failure=3
准备就绪探针会定期检查——默认情况下每10秒检查一次。由于尚未调用就绪探针,因此容器未准备好。但是最晚10秒钟内,该pod应该已经准备就绪,其IP应该列为service的endpoint(运行 kubectl get endpoint kubialoadbalancer
来确认)。
服务打向单独的pod
现在可以点击几次服务网址,查看每个请求都被重定向到这个pod:
$ curl http://130.211.53.173
即使有三个pod正在运行,但只有一个pod报告已准备好,因此是唯一的pod接收请求。如果现在删除该文件,则将再次从该服务中删除该容器。
5.5.3 了解就绪探针的实际作用
此模拟就绪探针仅用于演示就绪探针的功能。在实际应用中,应用程序是否可以(并且希望)接收客户端请求,决定了就绪探测应该返回成功或失败。
应该通过删除pod或更改pod标签而不是手动更改探针来从服务中手动移除pod。
提示 如果想要从某个服务中手动添加或删除pod,请将 enabled=true
作为标签添加到pod,以及服务的标签选择器中。当想要从服务中移除pod时,删除标签。
务必定义就绪探针
在总结本节之前,有两个关于就绪探针的要点,需要强调。首先,如果没有将就绪探针添加到pod中,它们几乎会立即成为服务端点。如果应用程序需要很长时间才能开始监听传入连接,则在服务启动但尚未准备好接收传入连接时,客户端请求将被转发到该pod。因此,客户端会看到“连接被拒绝”类型的错误。
提示 应该始终定义一个就绪探针,即使它只是向基准URL发送HTTP请求一样简单。
不要将停止pod的逻辑纳入就绪探针中
需要提及的另一件事情涉及pod生命周期结束(pod关闭),并且也与客户端出现连接错误相关。
当一个容器关闭时,运行在其中的应用程序通常会在收到终止信号后立即停止接收连接。因此,可能认为只要启动关机程序,就需要让就绪探针返回失败,以确保从所有服务中删除该pod。但这不是必需的,因为只要删除该容器,Kubernetes就会从所有服务中移除该容器。
网友评论