k 8 sの中のlivenseとreadinessの原理と違いを詳しく説明します.

5556 ワード

livenessとreadinessのプローブの作業方法のソースコード解析
livenseとreadinessはk 8 sのプローブとして、応用に対して健康診断を行うことができます.両者がサポートする探知方式は同じです.主な探知方式はhttp探知に対応し、命令探知を実行し、tcp探知を実行する.探測はいずれもクベルが行います.
コマンド検出を実行
func (pb *prober) runProbe(p *v1.Probe, pod *v1.Pod, status v1.PodStatus, container v1.Container, containerID kubecontainer.ContainerID) (probe.Result, string, error) {
.....        
        command := kubecontainer.ExpandContainerCommandOnlyStatic(p.Exec.Command, container.Env)
        return pb.exec.Probe(pb.newExecInContainer(container, containerID, command, timeout))
......
        
func (pb *prober) newExecInContainer(container v1.Container, containerID kubecontainer.ContainerID, cmd []string, timeout time.Duration) exec.Cmd {
    return execInContainer{func() ([]byte, error) {
        return pb.runner.RunInContainer(containerID, cmd, timeout)
    }}
}
        
......
func (m *kubeGenericRuntimeManager) RunInContainer(id kubecontainer.ContainerID, cmd []string, timeout time.Duration) ([]byte, error) {
    stdout, stderr, err := m.runtimeService.ExecSync(id.ID, cmd, 0)
    return append(stdout, stderr...), err
}
kubeletから、CRIインターフェースのExecSyncインターフェースを通じて、対応するコンテナ内で綴り付けられたcmdコマンドを実行します.戻り値を取得します
func (pr execProber) Probe(e exec.Cmd) (probe.Result, string, error) {
    data, err := e.CombinedOutput()
    glog.V(4).Infof("Exec probe response: %q", string(data))
    if err != nil {
        exit, ok := err.(exec.ExitError)
        if ok {
            if exit.ExitStatus() == 0 {
                return probe.Success, string(data), nil
            } else {
                return probe.Failure, string(data), nil
            }
        }
        return probe.Unknown, "", err
    }
    return probe.Success, string(data), nil
}
クベレットは、実行コマンドのログアウトコードに基づいて、検出が成功したかどうかを決定します.実行コマンドの終了コードが0の場合、実行に成功したと見なします.そうでなければ実行に失敗します.タイムアウトを実行すると、状態はアンロックです.
http検出
func DoHTTPProbe(url *url.URL, headers http.Header, client HTTPGetInterface) (probe.Result, string, error) {
    req, err := http.NewRequest("GET", url.String(), nil)
    ......
    if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
        glog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
        return probe.Success, body, nil
    }
    ......
http探査はクベルトが容器の指定を要求し、レスポンスによって判断します.戻りの状態コードが200から400(400を含まない)の間にある場合、すなわち状態コードは2 xxと3 xxであり、探知に成功したと考えられている.失敗すると思う.
tcp検出
func DoTCPProbe(addr string, timeout time.Duration) (probe.Result, string, error) {
    conn, err := net.DialTimeout("tcp", addr, timeout)
    if err != nil {
        // Convert errors to failures to handle timeouts.
        return probe.Failure, err.Error(), nil
    }
    err = conn.Close()
    if err != nil {
        glog.Errorf("Unexpected error closing TCP probe socket: %v (%#v)", err, err)
    }
    return probe.Success, "", nil
}
tcp検出は指定されたポートを検出することによって行われる.接続が可能であれば、プローブが成功したと見なされ、失敗したと見なされます.
失敗の原因を探る
コマンド検出の実行に失敗した理由は、コンテナが正常に起動されていないか、コマンドの実行に失敗したことが主です.もちろん、dockerやdocker-shimにも障害があるかもしれません.
httpとtcpはいずれもクbeletからnodeノードによって開始されたので、容器のipを検出します.そのため、検出に失敗した原因は、アプリケーション容器の問題以外に、nodeから容器ipまでのネットワークが不通だった可能性があります.
livenessとreadinessの原理の違い
探知方式が同じなら、livenseとreadinessの違いは何ですか?まず、両者が果たす役割は違っています.
func (m *kubeGenericRuntimeManager) computePodContainerChanges(pod *v1.Pod, podStatus *kubecontainer.PodStatus) podContainerSpecChanges {
        ......
        liveness, found := m.livenessManager.Get(containerStatus.ID)
        if !found || liveness == proberesults.Success {
            changes.ContainersToKeep[containerStatus.ID] = index
            continue
        }
        ......
livenseは主にいつ容器を再起動するかを確認するために使われます.リベンジの結果は、リベンジManagerに格納されます.kubeletは、syncPodにおいて、この容器のlivensプローブが検出に失敗した場合、起動するコンテナリストに追加し、その後の操作でこの容器を新たに作成します.
readinessは主に容器が準備されているかどうかを確認します.Podの容器がすべて準備完了状態、つまりpodのconditionのReadyがtrueである場合にのみ、kubeletはこのPodが準備完了状態と認定します.podがレディ状態にあるかどうかの役割は、どのPodがserviceの後端として機能するべきかを制御することである.Podがレディ状態でない場合、それらはserviceのendpointから取り除かれる.
func (m *manager) SetContainerReadiness(podUID types.UID, containerID kubecontainer.ContainerID, ready bool) {
        ......
        containerStatus.Ready = ready
        ......
        readyCondition := GeneratePodReadyCondition(&pod.Spec, status.ContainerStatuses, status.Phase)
        ......
        m.updateStatusInternal(pod, status, false)
}
readiness検査の結果は、SetContinerReadiness関数により、podのstatusに設定され、podのready conditionを更新します.
livenseとreadinessは最終的な役割の違い以外に、もう一つ大きな違いはそれらの初期値の違いです.
    switch probeType {
    case readiness:
        w.spec = container.ReadinessProbe
        w.resultsManager = m.readinessManager
        w.initialValue = results.Failure
    case liveness:
        w.spec = container.LivenessProbe
        w.resultsManager = m.livenessManager
        w.initialValue = results.Success
    }
リベンジの初期値は成功です.このように応用が成功していない前に、誤殺されることを防止します.規定時間内に起動に失敗した場合は、容器の再構築をトリガします.
readinessの初期値は失敗です.このように、アプリケーションの起動が成功していない前に、アプリケーションにトラフィックの導入を行います.規定時間内に起動が成功したら、それを成功に設定して、流量をアプリケーションに導入します.
livenseとreadinessの2つの役割は互いに取って代わることができません.
例えば、livenseのみを配置していると、コンテナが起動し、アプリケーションが完了していない前に、その時点でpodはreadyです.流量は容器の応用に導入され、要求が失敗する可能性がある.livense検査に失敗した後、容器を再起動すると、podのreadyのconditionがfalseになります.しかし、前の方にいくつかの流量があります.エラー状態のために導入されます.
もちろんreadinessだけでは容器の再起動をトリガできません.
両者の役割は違っていますので、実際の使用では、実際の需要に応じて両者を合わせて使うことができます.