Podにおけるコンテナ再起動プロセス

5144 ワード

背景


テストの場合、通常はPodのcontainerを頻繁に殺し、再起動する必要があります.この過程でPodの状態がCrashLoopBackOffになることが多く、containerが再起動する時間が長くなります.

ぶんせき


containerが頻繁にrestartを回避するために、k 8 sはcontainer restartプロセスを制限し、back-offを使用する方法で、公式ドキュメントでは次のように説明されています.
Failed containers that are restarted by Kubelet, are restarted with an exponential back-off delay, the delay is in multiples of sync-frequency 0, 1x, 2x, 4x, 8x … capped at 5 minutes and is reset after 10 minutes of successful execution.
  • https://kubernetes-v1-4.github.io/docs/user-guide/pod-states/

  • ここではまず直接結論を出します.
  • Podでrestart containerを実行する場合(具体的なタイミングは、周期的にSyncPod()を実行する場合)、Podは自身のStatus構造を通じて現在のこのcontainer(Podに複数のcontainerがある可能性があるため)が前回終了した時間をts
  • と記す.
  • 最初のrestartである場合、containerを直接再起動し、KubeletのbackOff.perItemBackoff(map構造、keyはcontainerと存在podオブジェクトから算出されたid)に次回backoffの時間(初期値10 s、その後指数成長、最大5 min)
  • を記録する
  • 最初のrestart、すなわちKubeletのbackOffでない場合.perItemBackoffにはすでにこのcontainerのbackOffレコードがあり、backoffとしてカウントされています.
  • now() - ts < backoffの場合、待ち時間が足りないことを示し、CrashLoopBackOff Event(次のSyncPodのサイクルが来るまで待って、この値を再比較)
  • を投げ出す
  • それ以外の場合、説明はbackoffの時間を待っているので、restartできます.このときbackOffが実行されます.Next()は、容器に対応するbackoffを2倍にしてrestart動作
  • を実行する.

    4 . ステップ3でbackoffを計算する過程で、前回containerが終了したときの現在の時間距離の間隔もチェックし、2 * MaxContainerBackOff = 10 minutesより大きい場合、このcontainerに対応するbackoffを初期値10 sにリセットする

    ソースの詳細


    1. kubernetes/pkg/kubelet/kubelet.go


    ソースコードにより、kubernetes/pkg/kubelet/kubelet.goファイルには2つの定数があることがわかりました.
    MaxContainerBackOff = 300 * time.Second
    backOffPeriod = time.Second * 10
    

    この2つの変数を使用してBackOffオブジェクトを構築しました.これはkubeletのプロパティで、node上のすべてのpodに適用されます.
    klet.backOff = flowcontrol.NewBackOff(backOffPeriod, MaxContainerBackOff)
    

    BackOff構造は次のとおりです.
    type Backoff struct {
        sync.Mutex
        Clock           clock.Clock
        defaultDuration time.Duration
        maxDuration     time.Duration
        perItemBackoff  map[string]*backoffEntry
    }
    

    そしてSyncPodメソッドでこのオブジェクトを使用します
    // Call the container runtime's SyncPod callback
    result := kl.containerRuntime.SyncPod(pod, apiPodStatus, podStatus, pullSecrets, kl.backOff)
    

    2. kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go


    SyncPodが具体的にやっていることは次のとおりです.
    // SyncPod syncs the running pod into the desired pod by executing following steps:
    //
    //  1. Compute sandbox and container changes.
    //  2. Kill pod sandbox if necessary.
    //  3. Kill any containers that should not be running.
    //  4. Create sandbox if necessary.
    //  5. Create init containers.
    //  6. Create normal containers.
    func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, _ v1.PodStatus, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
    

    同じようにこのファイルには、重要な関数があります.
    // If a container is still in backoff, the function will return a brief backoff error and
    // a detailed error message.
    func (m *kubeGenericRuntimeManager) doBackOff(pod *v1.Pod, container *v1.Container, podStatus *kubecontainer.PodStatus, backOff *flowcontrol.Backoff) (bool, string, error) {
        var cStatus *kubecontainer.ContainerStatus
        for _, c := range podStatus.ContainerStatuses {
            if c.Name == container.Name && c.State == kubecontainer.ContainerStateExited {
                cStatus = c
                break
            }
        }
    
        if cStatus == nil {
            return false, "", nil
        }
    
        glog.Infof("checking backoff for container %q in pod %q", container.Name, format.Pod(pod))
        // Use the finished time of the latest exited container as the start point to calculate whether to do back-off.
        ts := cStatus.FinishedAt
        // backOff requires a unique key to identify the container.
        key := getStableKey(pod, container)
        if backOff.IsInBackOffSince(key, ts) {
            if ref, err := kubecontainer.GenerateContainerRef(pod, container); err == nil {
                m.recorder.Eventf(ref, v1.EventTypeWarning, events.BackOffStartContainer, "Back-off restarting failed container")
            }
            err := fmt.Errorf("Back-off %s restarting failed container=%s pod=%s", backOff.Get(key), container.Name, format.Pod(pod))
            glog.Infof("%s", err.Error())
            return true, err.Error(), kubecontainer.ErrCrashLoopBackOff
        }
    
        backOff.Next(key, ts)
        return false, "", nil
    }
    

    ここでbackOff.次のように定義します.
    // move backoff to the next mark, capping at maxDuration
    func (p *Backoff) Next(id string, eventTime time.Time) {
        p.Lock()
        defer p.Unlock()
        entry, ok := p.perItemBackoff[id]
        if !ok || hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
            entry = p.initEntryUnsafe(id)
        } else {
            delay := entry.backoff * 2 // exponential
            entry.backoff = time.Duration(integer.Int64Min(int64(delay), int64(p.maxDuration)))
        }
        entry.lastUpdate = p.Clock.Now()
    }