标签: 监控

  • 一个 HTTP 请求头引发的 72 小时崩溃循环

    有一阵子 beta.mazter.cn 一直打不开,返回 404。当时没太在意,以为是 WordPress 初始化慢。三天后去看 Pod,才发现它已经重启了 5 次,一直卡在崩溃循环里。

    这篇文章记录完整的排查链条和根因——它有个挺有意思的特点:每一个组件都在按设计工作,组合起来却产生了死循环

    现象

    $ kubectl get pods -n beta
    NAME                         READY   STATUS    RESTARTS   AGE
    wordpress-5c79d8dc94-xxxxx   0/1     Running   5          3d22h

    同一套配置在 prod 命名空间一切正常(0 次重启),所以第一反应不是配置错,而是”beta 环境有什么特殊的”。这个判断后来被证明是错的——prod 只是运气好。

    看 Events

    kubectl describe pod 是这类问题唯一的正确答案来源:

    Events:
      Warning  Unhealthy  liveness probe failed: HTTP probe failed with statuscode: 503
      Warning  Unhealthy  Readiness probe failed: HTTP probe failed with statuscode: 503
      Normal   Killing    Container failed liveness probe, will be restarted

    还有一条很关键的:Probe terminated redirects。探针跟随重定向,然后被终止了。

    还原死循环

    把整条链路串起来是这样:

    • 探针按配置去请求 http://<PodIP>:80/wp-login.php
    • WordPress 收到的是明文 HTTP,而站点地址配置的是 HTTPS,于是它回一个 302,让客户端跳到 https://beta.mazter.cn/wp-login.php
    • 这个 HTTPS 请求出去之后,经过 Traefik Ingress 又被路由回来——目标是同一个 WordPress Service。
    • 而此刻这个 Pod 还没通过 readiness 检查,后端池里没有可用端点,Traefik 返回 503
    • 探针拿到 503,判定存活失败,kubelet 杀掉容器重启。
    • 重启后一切重演。

    为什么 WordPress 坚持要跳 HTTPS?因为官方镜像的 wp-config.php 里有一段条件判断,只有在检测到代理头 X-Forwarded-Proto: https 时,才会把 $_SERVER['HTTPS'] 置为 on。探针直连 Pod,不带这个头,于是 WordPress 认为”你现在是 HTTP,不安全”,坚持把你推向 HTTPS。

    修复

    给探针补上这个头即可,readiness 和 liveness 都要改:

    readinessProbe:
      httpGet:
        path: /wp-login.php
        port: 80
        httpHeaders:
          - name: X-Forwarded-Proto
            value: https
      initialDelaySeconds: 30
      periodSeconds: 10
    livenessProbe:
      httpGet:
        path: /wp-login.php
        port: 80
        httpHeaders:
          - name: X-Forwarded-Proto
            value: https
      initialDelaySeconds: 60
      periodSeconds: 15

    改完 apply 之后 Pod 一次就 Ready 了,beta.mazter.cn/wp-login.php 恢复 200。

    为什么 prod 没炸

    这是最值得警惕的部分。prod 之所以正常,是因为它的 Pod 已经跑了很久、早就处于 Ready 状态,重定向绕回来时能命中一个健康的后端,拿到 200 而不是 503。

    换句话说:prod 不是没有问题,只是还没被触发。任何一次重启(节点重启、镜像更新、资源不足被驱逐)都可能让 prod 走进同一个循环。所以修复同时打到了两个环境。

    更根本的解法

    补请求头是把 WordPress 的行为”哄”对了。更稳妥的做法是让探针打一个不会重定向的端点,比如在站点根放一个空的 healthz.html,探它即可。这样探针不依赖应用层的 HTTPS 判断逻辑,也不受代理头约定变化的影响。

    两种方案都行,重要的是别让健康检查去请求一个”可能会重定向”的地址——重定向会让探针的失败原因变得极难解释。