CoreDNS 惊现诡异 bug,导致服务大面积中断

Sealos 公众号已接入了 GPT-4,完全免费!欢迎前来调戏👇

499d4c3614fbdaa387e9a5abcc13a3f4.png

原文链接:https://juejin.cn/post/7277471908417110053

我是 LEE,老李,一个在 IT 行业摸爬滚打 17 年的技术老兵。

事件背景

某天凌晨被一个急促的电话叫醒,电话中传来了急促的声音:“老李在不在,现在 Kubernetes 中 Pod  相互调用全部中断了,全盘中断了,快点起来帮忙修复下。”,吓得我一激灵从床上起来,在企微中默默看完了事情的前因后果,随后登录上故障  Kubernetes 集群上,看到 Pod 中的日志都平凡出现无法建联,无法解析域名的错误,这个时候我才意识到这个问题的严重性。

通过监控大盘,我发现 Kubernetes 集群中的 CoreDNS 服务的 ReadinessProbe 端口 8181 端口异常,导致服务无法正常启动,进而导致 Pod 之间无法相互调用,业务全盘中断。

现象获取

我登录到 Kubernetes 集群中 NetworkDebug 容器中,对运行的 CoreDNS 的 8080,8181,9153 联通性测试,发现 8080 和 9153 端口工作均正常,只有 8181 端口工作异常。

CoreDNS 运行端口功能如下表:

##端口号描述
18080用于查询 CoreDNS 的状态
28181用于检测 CoreDNS 是否正常工作
39153用于 Prometheus 监控

CoreDNS Pod 都在 0/1 Running 状态。

548377d1f2992486a2b52f870a0d7bff.jpeg

kubectl 直接 describe CoreDNS Pod,显示 ReadinessProbe 探针异常,导致 Pod 无法正常启动。

a056599327e0018e1eece0ae6797b23b.jpeg
Readiness probe failed: Get "http://x.x.x.x:8181/ready": dial tcp x.x.x.x:8181: connect: connection refused

由于 ReadinessProbe 探针主要是确认 Pod 能否进入转发列表,也就是说 Pod 的 IP 和 Port 能否成为 Service 的 Endpoint 中的一个。

如果 ReadinessProbe 探针失败,那么 Pod 就不会被加入到 Service 的 Endpoint 中,这样就会导致  Service 无法正常工作。我也对 CoreDNS 的 Service 也做了检查,发现 CoreDNS 的 Service 中的  Endpoint 为空,这就是为什么 Pod 之间无法相互调用的原因。

6b7f56a564ccf9caaa5816735ee76809.jpeg

确认了故障触发逻辑以后,想要解决问题就简单了,只要解决 CoreDNS 的 ReadinessProbe 探针异常,恢复 CoreDNS Pod 就行了。

原理分析

为什么小伙伴完成执行变更操作,重启 CoreDNS Pod 以后,ReadinessProbe 探针依然异常呢?要能解答这个疑问,就必须要了解 CoreDNS 中 8181 这个端口是如何工作的,直接看源代码。

coredns/plugin/ready/setup.go

// 注册 setup 函数, 命名为 ready
func init() { plugin.Register("ready", setup) }

func setup(c *caddy.Controller) error {
 addr, err := parse(c) // 解析 caddy 配置文件
 if err != nil {
  return plugin.Error("ready", err)
 }
 rd := &ready{Addr: addr} // 创建 ready 对象, Addr 是 8181 端口

 uniqAddr.Set(addr, rd.onStartup)
 c.OnStartup(func() error { uniqAddr.Set(addr, rd.onStartup); return nil })  // 启动一个 Http Server 相应 8181 端口的请求

 ... // 省略部分代码

 return nil
}

// 解析 caddy 配置文件
func parse(c *caddy.Controller) (string, error) {
 addr := ":8181"
 i := 0
 for c.Next() {
  if i > 0 {
   return "", plugin.ErrOnce
  }
  i++
  args := c.RemainingArgs()

  switch len(args) {
  case 0:
  case 1:
   addr = args[0] // 这里可能就是应发现 8181 端口异常的原因,传入的参数是 IP:Port 的格式,但是 net.SplitHostPort 解析没有报错,导致 addr 变量的值是空的或者别的值
   if _, _, e := net.SplitHostPort(addr); e != nil {
    return "", e
   }
  default:
   return "", c.ArgErr()
  }
 }
 return addr, nil
}

coredns/plugin/ready/ready.go

func (rd *ready) onStartup() error {
 ln, err := reuseport.Listen("tcp", rd.Addr)
 if err != nil {
  return err
 }

 rd.Lock()
 rd.ln = ln
 rd.mux = http.NewServeMux()
 rd.done = true
 rd.Unlock()

    // 8181 端口健康检测的处理方法,主要相应 /ready 的请求
 rd.mux.HandleFunc("/ready", func(w http.ResponseWriter, _ *http.Request) {
  rd.Lock()
  defer rd.Unlock()
  if !rd.done {
   w.WriteHeader(http.StatusServiceUnavailable)
   io.WriteString(w, "Shutting down")
   return
  }
  ok, todo := plugins.Ready() // 判断当前组件是否准备就绪
  if ok {                    // 如果准备就绪,返回 200
   w.WriteHeader(http.StatusOK)
   io.WriteString(w, http.StatusText(http.StatusOK))
   return
  }
  log.Infof("Still waiting on: %q", todo)
  w.WriteHeader(http.StatusServiceUnavailable) // 如果没有准备就绪,返回 503
  io.WriteString(w, todo)
 })

 go func() { http.Serve(rd.ln, rd.mux) }() // 启动一个 Http Server 相应 8181 端口的请求

 return nil
}

实际我看完了整个 ready 组件的代码,我也百思不得其解,因为之前我们在 CoreDNS 的 Configmap  中从来没有启动或者配置过 ready 组件,那个时候的 CoreDNS 的 ReadinessProbe 探针是正常的,为什么现在就不正常了呢?

  1. 会不会自动启动的 ready 组件一直不能进入就绪状态,导致 ReadinessProbe 探针异常?

  2. 会不会是传入的参数不正确导致的,但是为什么 net.SplitHostPort 解析没有报错呢?

各种问题在我脑袋里面翻滚,但是没有一个能够解释的通的,最后我只能通过最快和最有把握的方式来解决问题。

如果要最快的方式解决:我想是不是在 parse 函数让 args[0] 传入一个 :8181 的格式,也许就不会有问题了。

当然真正的问题,我们团队还在排查中,如果有结果,我会在文章中更新。

处理方法

假设 CoreDNS ready 组件自动启动有问题,那我们可不可以手动的方式启动呢?通过官方文档中的描述,我们可以在 CoreDNS  的配置文件中添加一个 ready 插件,来解决这个问题。相当通过手动设置 ready 插件的端口,来解决 ReadinessProbe  探针异常的问题。

https://coredns.io/plugins/ready/

1d621a270c4a88f306f00b309ed5421f.jpeg

具体配置如下:

apiVersion: v1
kind: ConfigMap
metadata:
    name: coredns
    namespace: kube-system
data:
    Corefile: |
        .:53 {
            errors
            health
            kubernetes cluster.local in-addr.arpa ip6.arpa {
               pods insecure
               fallthrough in-addr.arpa ip6.arpa
            }
            prometheus :9153
            ready :8181  ### 增加此行
            forward . /etc/resolv.conf {
              max_concurrent 1000
            }
            log
            cache 30
            loop
            reload
            loadbalance
        }

最后重启 CoreDNS Pod,问题解决。

当然后面我们通过 CoreDNS 的 Github 中 Issue 查找,发现了一个和我们类似的问题,也是 ReadinessProbe 探针异常(8181 端口),也是通过配置中添加 ready 插件解决的问题。

https://github.com/coredns/coredns/issues/3403

有兴趣的小伙伴可以自行移步查看。

d330b11820e593782e7245a12125a48d.jpeg

最终效果

CoreDNS Pod 运行正常,ReadinessProbe 探针正常,业务恢复正常。

7e2f2f2594f2fa0a80a8fcebe69a598d.jpeg

557eb095e7af17bf3d8d77baa6010904.png

Sealos 社区长期征稿,欢迎 Sealos 终端用户与开发者前来投稿,奖金💰丰厚!详情可查看文章👇

c3535b839b55100ce74dbf7846d4bffe.jpeg

爱 Sealos?来投稿!有奖金,还没有截止日期...


加入 Sealos 开源社区

体验像个人电脑一样简单的云操作系统

🏠官网链接

https://sealos.io

🐙GitHub 地址

https://github.com/labring/sealos

📑访问 Sealos 文档

https://sealos.io/zh-Hans/docs/Intro

🏘️逛逛论坛

https://forum.laf.run/

往期推荐

30 秒使用 VaultWarden 搭建个人密码管理器

2023-09-13

0bb4c841ade5e7071e820d7d8fabcdb5.jpeg

一键部署 Umami 统计个人网站访问数据,跟 Google Analytics 说再见

2023-08-10

5e2b05dfd9c211890aa3c5e8b024b888.jpeg

3 分钟将免费无限制的 Claude 2.0 接入任意 GPT 套壳应用,太香了!

2023-07-28

c27659aad2e87e630b62fd5c3a08e912.jpeg

关于 Sealos

Sealos 是一款以 Kubernetes 为内核的云操作系统发行版。它以云原生的方式,抛弃了传统的云计算架构,转向以 Kubernetes 为云内核的新架构,使企业能够像使用个人电脑一样简单地使用云。

关注 Sealos 公众号与我们一同成长👇👇👇

82bbff1b3eac16c52d832b3c3f385b44.jpeg

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值