问题描述
当我们的 POD 耗尽并且可能需要进行一些扩展时,Kubernetes 将这种耗尽与死亡混淆并重新启动我们的 POD:s。当然,这会产生相反的效果,剩余的 POD:s 会得到更多的负载......所以我的问题来了,你能用专用的、非耗尽的连接为 Kubernetes 和 LB 活跃度和就绪端点提供服务吗?
我们有一个在 Kubernetes 中运行的旧系统,每个 POD 中捆绑了一个 Apache httpd 和一个 tomcat。负载均衡由 Kubernetes 在不同的 POD:s 之间完成,而不是在 httpd 中。 Httpd 正在运行 mpm_event+mod_jk 并且有一个到 Tomcat 的 AJP 1.3 连接。 Httpd 还从光盘提供一些没有 Tomcat 的静态资源。当出现故障时,我们很快就会耗尽 AJP 线程和 HTTPD 工作线程。
基本上我们看到的是:
- 应用程序无法连接到某些资源。某些网络、Memcached、DB 或其他服务开始超时。等待超时会导致线程非常长,我们很快就会用完它们。
- Readiness/Liveness probs 没有及时响应,Kubernetes 重新启动 POD(或者,在我们移除 liveness probe 后,使用 readiness 的 LB 将它们从负载均衡中移除,效果基本相同)。
- 根本原因问题已解决(以某种方式),但现在负载平衡中剩下的(非)POD 太少了。当一个 POD 返回时,它会受到所有流量的影响,变得筋疲力尽,并且因为它再次在准备探测中太慢而从 LB 中删除。
- 我们现在发现很难摆脱这种状态......(到目前为止它发生了两次,我们基本上不得不切断 Cloudflare WAF 上的所有流量,直到足够多的 POD:s 重新启动/负载平衡...... )
我对解决方案的想法:
我想我可以从 httpd->tomcat 为 liveness 和 readiness 端点打开一个优先的 fastlane,见下文。但是,我可以以某种方式将 httpd (mpm_event) 中的工作人员专用于这些端点吗?否则,当我用完 httpd 工作人员时,我猜我的快车道不会提供任何帮助。或者关于如何确保只要 tomcat 还活着,即使它已经筋疲力尽,我们始终可以提供活性/就绪性的任何其他想法?
这是我当前的 httpd worker 设置:
<IfModule mpm_event_module>
StartServers 3
ServerLimit 36
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 900
MaxConnectionsPerChild 0
</IfModule>
也许需要一个工作人员来分析请求并找出 URI... :-/ 或者我可以以某种方式将特定的工作人员池专门用于活跃和准备工作???
我的 httpd->tomcat fastlane:
我正在尝试与 tomcat 的第二个 AJP 连接,专门用于准备和活跃端点。乍一看,它似乎有效。
在 server.xml 中,我在端口 8008 上添加了一个连接器:
<Connector
port="8009"
protocol="AJP/1.3"
redirectPort="8443"
connectionTimeout="60000"
minSpareThreads="2"
maxThreads="20"
acceptorThreadCount="2"
URIEncoding="UTF-8"
address="127.0.0.1"
secretrequired="false" />
<!--
This is the prioritized connector used for health checks.
-->
<Connector
port="8008"
protocol="AJP/1.3"
redirectPort="8443"
connectionTimeout="-1"
keepAliveTimeout="-1"
acceptorThreadPriority="6"
minSpareThreads="2"
maxThreads="5"
acceptorThreadCount="1"
URIEncoding="UTF-8"
address="127.0.0.1"
secretrequired="false" />
在我的 workers.properties
(JkWorkersFile)中,我添加了新连接并将其命名为 ajp13prio
:
worker.list=ajp13,ajp13prio
worker.ajp13.type=ajp13
worker.ajp13.port=8009
worker.ajp13.host=127.0.0.1
worker.ajp13.lbfactor=1
worker.ajp13prio.type=ajp13
worker.ajp13prio.port=8008
worker.ajp13prio.host=127.0.0.1
worker.ajp13prio.lbfactor=1
在我的 httpd conf 中,我将探针配置为使用新的连接器:
<VirtualHost *:80>
...
# health checks (readiness and liveness probes) are prioritized
JkMount /api/v2/health/* ajp13prio
# All requests go to worker1 by default
JkMount /* ajp13
...
</VirtualHost>
解决方法
如果其他人在这里结束,在 kubernetes 中运行旧的 Apache 设置。
最后,我向 Tomcat 添加了第二个连接器,它是 HTTP,而不是 AJP。这个连接器的端口从容器中暴露出来并被 LB 和 Kubernetes 使用。因此,HTTPD 被完全绕过以进行健康检查。然后这个端口(默认情况下)在 LB 中被阻止以供外部访问。
server.xml
<Connector
port="8080"
address="0.0.0.0"
protocol="HTTP/1.1"
enableLookups="false"
maxThreads="5"
minSpareThreads="2"
acceptorThreadCount="1"
acceptorThreadPriority="9"
connectionTimeout="2000"
keepAliveTimeout="-1"
disableUploadTimeout="false"
connectionUploadTimeout="3600000"
redirectPort="8443"
URIEncoding="UTF-8"
maxPostSize="1" />
deployment.yaml
tomcat container:
readinessProbe:
httpGet:
path: /health/readiness
port: 8080
initialDelaySeconds: 120
livenessProbe:
httpGet:
path: /health/liveness
port: 8080
ports:
- name: ajp
containerPort: 8009
protocol: TCP
- name: http-prio
containerPort: 8080
protocol: TCP
service.yaml:
apiVersion: v1
kind: Service
metadata:
...
annotations:
cloud.google.com/backend-config: '{"default": "app-backendconfig"}'
spec:
ports:
- name: http
port: 80
- name: http-prio
port: 8080
...
backendconfig.yaml
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
...
spec:
...
healthCheck:
type: HTTP
requestPath: /health/readiness
port: 8080