滴滴在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
函数中,主要有以下几个步骤
- 调用
computePodActions(pod, podStatus)
,来计算需要进行的动作,可能是kill、create、restart - 遍历podContainerChanges.ContainersToStart中的结果,调用m.startContainer进行重启
- 最终调用的是 m.runtimeService.StartContainer(ctx, containerID)
再留几个思考点:
- m.runtimeService.StartContainer(ctx, containerID)究竟执行了什么,是异步还是同步?