OpenKruise入门:原地升级的原理
概要
k8s的哲学是不要把Pod当成宠物,因此每次deployment的更新,都会导致Pod的删除和重建,对于一些场景而言,Pod的重建是完全不必要的,因此需要一种办法,在特定情况,例如只是更新镜像时,让Pod不要重建,而是重启,并且对业务无损,OpenKruise实现了这个功能。
详情
k8s核心原理
原地升级即k8s的Pod重启
每个 Node 上的 Kubelet,会针对本机上所有 Pod.spec.containers 中的每个 container 计算一个 hash 值,当hash变化时,例如image发生了改变,kubelet会重启容器,从而实现Pod的原地升级。
func HashContainer(container *v1.Container) uint64 { hash := fnv.New32a() containerJSON, _ := json.Marshal(container) hashutil.DeepHashObject(hash, containerJSON) return uint64(hash.Sum32()) }
pod spec 修改校验:
// pkg/apis/core/validation/validation.go func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList
status上报image
func (kl *Kubelet) convertToAPIContainerStatuses(pod *v1.Pod, podStatus *kubecontainer.PodStatus, previousStatus []v1.ContainerStatus, containers []v1.Container, hasInitContainers, isInitContainer bool) []v1.ContainerStatus func (kl *Kubelet) convertStatusToAPIStatus(pod *v1.Pod, podStatus *kubecontainer.PodStatus, oldPodStatus v1.PodStatus) *v1.PodStatus func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodStatus func (kl *Kubelet) syncPod(ctx context.Context, updateType kubetypes.SyncPodType, pod, mirrorPod *v1.Pod, podStatus *kubecontainer.PodStatus) (isTerminal bool, err error) kl.statusManager.SetPodStatus(pod, apiPodStatus) // 更新pod status到apiserver func (m *manager) syncPod(uid types.UID, status versionedPodStatus) newPod, patchBytes, unchanged, err := statusutil.PatchPodStatus(m.kubeClient, pod.Namespace, pod.Name, pod.UID, pod.Status, mergedStatus)
kubelet判断Pod Ready(ReadinessGates)
func GeneratePodReadyCondition(spec *v1.PodSpec, conditions []v1.PodCondition, containerStatuses []v1.ContainerStatus, podPhase v1.PodPhase) v1.PodCondition { unreadyMessages := []string{} for _, rg := range spec.ReadinessGates { _, c := podutil.GetPodConditionFromList(conditions, rg.ConditionType) if c == nil { unreadyMessages = append(unreadyMessages, fmt.Sprintf("corresponding condition of pod readiness gate %q does not exist.", string(rg.ConditionType))) } else if c.Status != v1.ConditionTrue { unreadyMessages = append(unreadyMessages, fmt.Sprintf("the status of pod readiness gate %q is not \"True\", but %v", string(rg.ConditionType), c.Status)) } } }
Pod 更新限制和校验
func ValidateTolerations(tolerations []core.Toleration, fldPath *field.Path) field.ErrorList
kruise原理
pkg/util/inplaceupdate/inplace_update.go
定义了原地更新的控制面接口和实现,pkg/util/inplaceupdate/inplace_update_defaults.go
定义了如何把更新patch到Pod上等方法,例如defaultPatchUpdateSpecToPod
// Interface for managing pods in-place update. type Interface interface { CanUpdateInPlace(oldRevision, newRevision *apps.ControllerRevision, opts *UpdateOptions) bool Update(pod *v1.Pod, oldRevision, newRevision *apps.ControllerRevision, opts *UpdateOptions) UpdateResult Refresh(pod *v1.Pod, opts *UpdateOptions) RefreshResult }
参考
揭秘:如何为 Kubernetes 实现原地升级