CoreDNS cache TTL 的行为

355 阅读2分钟

CoreDNS TTL and Cache Behavior: A New Perspective

问题背景

最近在工作中遇到了一个有趣的情况。我们的客户的 Kubernetes 集群中有三个 master 节点全部宕机了,但令人惊讶的是,CoreDNS 依然能正常工作,依然能成功处理 DNS 请求。这让我重新审视了 CoreDNS 的 TTL 和缓存机制,特别是关于缓存如何刷新以及 CoreDNS 如何与 Kubernetes API 进行交互的部分。

在此之前,我一直认为 CoreDNS 会在后台每秒重新计算 TTL,然后自动刷新缓存。然而,实际情况并非如此,TTL 并不会在每次请求时都进行计算。相反,TTL 是在每次接收到 DNS 请求后才开始计算,并且当缓存 TTL 超过限制时,CoreDNS 会返回缓存结果给客户端,同时启动一个异步的 doRefresh 操作,去请求 Kubernetes 插件查询 API server 以更新缓存。

TTL 和缓存刷新机制

通过查阅源码,我发现 CoreDNS 的缓存刷新机制并不像我想象的那样自动进行。当缓存的 TTL 超时,CoreDNS 会将缓存结果直接返回给客户端,并触发异步操作去更新缓存。具体而言,如果 CoreDNS 和 API server 断开连接,缓存就永远不会被更新,导致缓存一直有效,直到缓存失效或被其他因素影响。

verifystale 参数的作用

还有一个关键的开关就是 verifystale 参数。如果开启了这个开关,CoreDNS 会同步请求 Kubernetes 插件,查询 API server 获取最新的 DNS 信息,并更新缓存。如果关闭,直接返回缓存,然后异步请求api-server 更新缓存。

TTL 处理源码解析

以下是 CoreDNS 中处理 TTL 和缓存刷新的相关源码:

ttl = i.ttl(now)
if ttl < 0 {
	// serve stale behavior
	if c.verifyStale {
		crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, cd: cd}
		cw := newVerifyStaleResponseWriter(crr)
		ret, err := c.doRefresh(ctx, state, cw)
		if cw.refreshed {
			return ret, err
		}
	}

	// Adjust the time to get a 0 TTL in the reply built from a stale item.
	now = now.Add(time.Duration(ttl) * time.Second)
	if !c.verifyStale {
		cw := newPrefetchResponseWriter(server, state, c)
		go c.doPrefetch(ctx, state, cw, i, now)
	}
	servedStale.WithLabelValues(server, c.zonesMetricLabel, c.viewMetricLabel).Inc()
} else if c.shouldPrefetch(i, now) {
	cw := newPrefetchResponseWriter(server, state, c)
	go c.doPrefetch(ctx, state, cw, i, now)
}

if i.wildcard != "" {
	// Set wildcard source record name to metadata
	metadata.SetValueFunc(ctx, "zone/wildcard", func() string {
		return i.wildcard
	})
}

if c.keepttl {
	// If keepttl is enabled we fake the current time to the stored
	// one so that we always get the original TTL
	now = i.stored
}
resp := i.toMsg(r, now, do, ad)
w.WriteMsg(resp)
return dns.RcodeSuccess, nil