使用operator实现kubernetes的sidecar管理主要参考阿里开源的openkruise项目,github地址:https://github.com/openkruise/kruise。
openkruise目前提供了5个工作负载控制器:
目前我只需要参考SidecarSet管理功能。
sidecarSet := &appsv1alpha1.SidecarSet{}
err := r.Get(context.TODO(), request.NamespacedName, sidecarSet)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
selector, err := metav1.LabelSelectorAsSelector(sidecarSet.Spec.Selector)
if err != nil {
return reconcile.Result{}, err
}
matchedPods := &corev1.PodList{}
if err := r.List(context.TODO(), &client.ListOptions{LabelSelector: selector}, matchedPods); err != nil {
return reconcile.Result{}, err
}
var filteredPods []*corev1.Pod
for i := range matchedPods.Items {
pod := &matchedPods.Items[i]
podCreateBeforeSidecarSet, err := isPodCreatedBeforeSidecarSet(sidecarSet, pod)
if err != nil {
return reconcile.Result{}, err
}
if controllerutil.IsPodActive(pod) && !isIgnoredPod(pod) && !podCreateBeforeSidecarSet {
filteredPods = append(filteredPods, pod)
}
}
isPodCreatedBeforeSidecarSet通过Annotations判断pod是否在sidecarSet前创建。
IsPodActive通过Pod.Status.Phase和Pod.DeletionTimestamp判断pod的存活状态。
isIgnoredPod通过检查Pod的namespace,会过滤掉k8s默认namespace下的pod。
status, err := calculateStatus(sidecarSet, filteredPods)
if err != nil {
return reconcile.Result{}, err
}
err = r.updateSidecarSetStatus(sidecarSet, status)
if err != nil {
return reconcile.Result{}, err
}
calculateStatus计算各个状态pod的数量,返回SidecarSet的Status。
appsv1alpha1.SidecarSetStatus{
ObservedGeneration: sidecarSet.Generation,
MatchedPods: matchedPods,
UpdatedPods: updatedPods,
ReadyPods: readyPods,
}
updateSidecarSetStatus更新SidecarSet的Status。
func (r *ReconcileSidecarSet) updateSidecarSetStatus(sidecarSet *appsv1alpha1.SidecarSet, status *appsv1alpha1.SidecarSetStatus) error {
if !inconsistentStatus(sidecarSet, status) {
return nil
}
sidecarSetClone := sidecarSet.DeepCopy()
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
sidecarSetClone.Status = *status
updateErr := r.Status().Update(context.TODO(), sidecarSetClone)
if updateErr == nil {
return nil
}
key := types.NamespacedName{
Name: sidecarSetClone.Name,
}
if err := r.Get(context.TODO(), key, sidecarSetClone); err != nil {
klog.Errorf("error getting updated sidecarset %s from client", sidecarSetClone.Name)
}
return updateErr
})
return err
}
这个函数里有两点值得我借鉴:
在SidecarSet的status更新完成之后,就是一系列的业务逻辑判断
1. check if sidecarset paused, if so, then quit
2. check if fields other than image in sidecarset had changed, if so, then quit
3. check unavailable pod number, if > 0, then quit(maxUnavailable=1)
4. find out pods need update
5. update one pod(maxUnavailable=1)
最终对pod的更新逻辑其实非常简单,找到需要更新的pod之后,遍历pod的container,匹配到需要更新的container之后,直接更新container的image,借助k8s本身的功能,即可完成sidecar的升级。
看完之后,有一种“虎头蛇尾”的感觉。原本以为k8s的sidecar升级很难,现在看来比我想象的要简单一些,处理好业务逻辑,即可完成sidecar管理。