您能否为 Kubernetes 活动端点提供专用连接,当常规连接池耗尽时可用?

问题描述

当我们的 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 工作线程。

基本上我们看到的是:

  1. 应用程序无法连接到某些资源。某些网络、Memcached、DB 或其他服务开始超时。等待超时会导致线程非常长,我们很快就会用完它们。
  2. Readiness/Liveness probs 没有及时响应,Kubernetes 重新启动 POD(或者,在我们移除 liveness probe 后,使用 readiness 的 LB 将它们从负载均衡中移除,效果基本相同)。
  3. 根本原因问题已解决(以某种方式),但现在负载平衡中剩下的(非)POD 太少了。当一个 POD 返回时,它会受到所有流量的影响,变得筋疲力尽,并且因为它再次在准备探测中太慢而从 LB 中删除
  4. 我们现在发现很难摆脱这种状态......(到目前为止它发生了两次,我们基本上不得不切断 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