liveness和readiness做爲k8s的探針,能夠對應用進行健康探測。
兩者支持的探測方式相同。主要的探測方式支持http探測,執行命令探測,以及tcp探測。
探測均是由kubelet執行。node
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命令。獲取返回值。docker
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 }
kubelet是根據執行命令的退出碼來決定是否探測成功。當執行命令的退出碼爲0時,認爲執行成功,不然爲執行失敗。若是執行超時,則狀態爲Unknown。後端
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探測是經過kubelet請求容器的指定url,並根據response來進行判斷。
當返回的狀態碼在200到400(不含400)之間時,也就是狀態碼爲2xx和3xx是,認爲探測成功。不然認爲失敗。網絡
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探測是經過探測指定的端口。若是能夠鏈接,則認爲探測成功,不然認爲失敗。app
執行命令探測失敗的緣由主要多是容器未成功啓動,或者執行命令失敗。固然也可能docker或者docker-shim存在故障。socket
因爲http和tcp都是從kubelet自node節點上發起的,向容器的ip進行探測。
因此探測失敗的緣由除了應用容器的問題外,還多是從node到容器ip的網絡不通。tcp
探測方式相同,那麼liveness與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 } ......
liveness主要用來肯定什麼時候重啓容器。liveness探測的結果會存儲在livenessManager中。
kubelet在syncPod時,發現該容器的liveness探針檢測失敗時,會將其加入待啓動的容器列表中,在以後的操做中會從新建立該容器。url
readiness主要來肯定容器是否已經就緒。只有當Pod中的容器都處於就緒狀態,也就是pod的condition裏的Ready爲true時,kubelet纔會認定該Pod處於就緒狀態。而pod是否處於就緒狀態的做用是控制哪些Pod應該做爲service的後端。若是Pod處於非就緒狀態,那麼它們將會被從service的endpoint中移除。code
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檢查結果會經過SetContainerReadiness函數,設置到pod的status中,從而更新pod的ready condition。
liveness和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 }
liveness的初始值爲成功。這樣防止在應用尚未成功啓動前,就被誤殺。若是在規定時間內還未成功啓動,纔將其設置爲失敗,從而觸發容器重建。
而readiness的初始值爲失敗。這樣防止應用尚未成功啓動前就嚮應用進行流量的導入。若是在規定時間內啓動成功,纔將其設置爲成功,從而將流量嚮應用導入。
liveness與readiness兩者做用不能相互替代。
例如只配置了liveness,那麼在容器啓動,應用尚未成功就緒以前,這個時候pod是ready的(由於容器成功啓動了)。那麼流量就會被引入到容器的應用中,可能會致使請求失敗。雖然在liveness檢查失敗後,重啓容器,此時pod的ready的condition會變爲false。可是前面會有一些流量由於錯誤狀態導入。
固然只配置了readiness是沒法觸發容器重啓的。
由於兩者的做用不一樣,在實際使用中,能夠根據實際的需求將兩者進行配合使用。