滴滴在11月27日晚间出现了大面积的故障,坊间传言是因为运维将k8s从1.20降级成了1.12导致pod全部被kill,但又无法重启,因此探究一下k8s容器重启的相关细节。

首先来对比一下函数HashContainer在1.12和1.20两个版本的差异,变化就是在于1.20是将container对象转换成json字符串再进行hash,这样的好处就是可以省略nil或者empty的字段,如果不省略的话,当后续的版本增加了一些新字段,那么老的pod容器就会重启。

https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/kubelet/container/helpers.go
func HashContainer(container *v1.Container) uint64 {
	hash := fnv.New32a()
	// Omit nil or empty field when calculating hash value
	// Please see https://github.com/kubernetes/kubernetes/issues/53644
	containerJSON, _ := json.Marshal(container)
	hashutil.DeepHashObject(hash, containerJSON)
	return uint64(hash.Sum32())
}



https://github.com/kubernetes/kubernetes/blob/release-1.12/pkg/kubelet/container/helpers.go#L93
func HashContainer(container *v1.Container) uint64 {
	hash := fnv.New32a()
	hashutil.DeepHashObject(hash, *container)
	return uint64(hash.Sum32())
}

当从1.20降级到1.12的时候,是有可能导致同一个容器的hash值计算不一样,从而导致重启,坊间传闻至少从源码角度是可能成立的。

重启计算的相关逻辑在SyncPod函数中,主要有以下几个步骤

  1. 调用computePodActions(pod, podStatus),来计算需要进行的动作,可能是kill、create、restart
  2. 遍历podContainerChanges.ContainersToStart中的结果,调用m.startContainer进行重启
  3. 最终调用的是 m.runtimeService.StartContainer(ctx, containerID)

再留几个思考点:

  1. m.runtimeService.StartContainer(ctx, containerID)究竟执行了什么,是异步还是同步?