摘要:節點對不會有影響,查詢處于狀態并一直保持。根據上一節描述,此時已經有正確的在其他節點,此時故障節點恢復后,執行優雅刪除,刪除舊的。會從狀態變為狀態,執行優雅刪除,,然后執行重新調度與重建操作。會從狀態直接變成狀態,不涉及重建。
節點離線后的 pod 狀態
在 kubernetes 使用過程中,根據集群的配置不同,往往會因為如下情況的一種或幾種導致節點 NotReady:
kubelet 進程停止
apiserver 進程停止
etcd 進程停止
kubernetes 管理網絡 Down
當出現這種情況的時候,會出現節點 NotReady,進而當kube-controller-manager 中的--pod-eviction-timeout定義的值,默認 5 分鐘后,將觸發 Pod eviction 動作。
對于不同類型的 workloads,其對應的 pod 處理方式因為 controller-manager 中各個控制器的邏輯不通而不同。總結如下:
deployment: 節點 NotReady 觸發 eviction 后,pod 將會在新節點重建(如果有 nodeSelector 或者親和性要求,會處于 Pending 狀態),故障節點的 Pod 仍然會保留處于 Unknown 狀態,所以此時看到的 pod 數多于副本數。
statefulset: 節點 NotReady 同樣會對 StatefulSet 觸發 eviction 操作,但是用戶看到的 Pod 會一直處于 Unknown 狀態沒有變化。
daemonSet: 節點 NotReady 對 DaemonSet 不會有影響,查詢 pod 處于 NodeLost 狀態并一直保持。
這里說到,對于 deployment 和 statefulSet 類型資源,當節點 NotReady 后顯示的 pod 狀態為 Unknown。 這里實際上 etcd 保存的狀態為 NodeLost,只是顯示時做了處理,與 daemonSet 做了區分。對應代碼中的邏輯為:
### node controller // 觸發 NodeEviction 操作時會 DeletePods,這個刪除為 GracefulDelete, // apiserver rest 接口對 PodObj 添加了 DeletionTimestamp func DeletePods(kubeClient clientset.Interface, recorder record.EventRecorder, nodeName, nodeUID string, daemonStore extensionslisters.DaemonSetLister) (bool, error) { ... for _, pod := range pods.Items { ... // Set reason and message in the pod object. if _, err = SetPodTerminationReason(kubeClient, &pod, nodeName); err != nil { if apierrors.IsConflict(err) { updateErrList = append(updateErrList, fmt.Errorf("update status failed for pod %q: %v", format.Pod(&pod), err)) continue } } // if the pod has already been marked for deletion, we still return true that there are remaining pods. if pod.DeletionGracePeriodSeconds != nil { remaining = true continue } // if the pod is managed by a daemonset, ignore it _, err := daemonStore.GetPodDaemonSets(&pod) if err == nil { // No error means at least one daemonset was found continue } glog.V(2).Infof("Starting deletion of pod %v/%v", pod.Namespace, pod.Name) recorder.Eventf(&pod, v1.EventTypeNormal, "NodeControllerEviction", "Marking for deletion Pod %s from Node %s", pod.Name, nodeName) if err := kubeClient.CoreV1().Pods(pod.Namespace).Delete(pod.Name, nil); err != nil { return false, err } remaining = true } ... } ### staging apiserver REST 接口 // 對于優雅刪除,到這里其實已經停止,不再進一步刪除,剩下的交給 kubelet watch 到變化后去做 delete func (e *Store) Delete(ctx genericapirequest.Context, name string, options *metav1.DeleteOptions) (runtime.Object, bool, error) { ... if graceful || pendingFinalizers || shouldUpdateFinalizers { err, ignoreNotFound, deleteImmediately, out, lastExisting = e.updateForGracefulDeletionAndFinalizers(ctx, name, key, options, preconditions, obj) } // !deleteImmediately covers all cases where err != nil. We keep both to be future-proof. if !deleteImmediately || err != nil { return out, false, err } ... } // stagging/apiserver中的 rest 接口調用,設置了 DeletionTimestamp 和 DeletionGracePeriodSeconds func (e *Store) updateForGracefulDeletionAndFinalizers(ctx genericapirequest.Context, name, key string, options *metav1.DeleteOptions, preconditions storage.Preconditions, in runtime.Object) (err error, ignoreNotFound, deleteImmediately bool, out, lastExisting runtime.Object) { ... if options.GracePeriodSeconds != nil { period := int64(*options.GracePeriodSeconds) if period >= *objectMeta.GetDeletionGracePeriodSeconds() { return false, true, nil } newDeletionTimestamp := metav1.NewTime( objectMeta.GetDeletionTimestamp().Add(-time.Second * time.Duration(*objectMeta.GetDeletionGracePeriodSeconds())). Add(time.Second * time.Duration(*options.GracePeriodSeconds))) objectMeta.SetDeletionTimestamp(&newDeletionTimestamp) objectMeta.SetDeletionGracePeriodSeconds(&period) return true, false, nil } ... } ### node controller // SetPodTerminationReason 嘗試設置 Pod狀態和原因到 Pod 對象中 func SetPodTerminationReason(kubeClient clientset.Interface, pod *v1.Pod, nodeName string) (*v1.Pod, error) { if pod.Status.Reason == nodepkg.NodeUnreachablePodReason { return pod, nil } pod.Status.Reason = nodepkg.NodeUnreachablePodReason pod.Status.Message = fmt.Sprintf(nodepkg.NodeUnreachablePodMessage, nodeName, pod.Name) var updatedPod *v1.Pod var err error if updatedPod, err = kubeClient.CoreV1().Pods(pod.Namespace).UpdateStatus(pod); err != nil { return nil, err } return updatedPod, nil } ### 命令行輸出 // 打印輸出時狀態的切換,如果 "DeletionTimestamp 不為空" 且 "podStatus 為 NodeLost 狀態"時, // 顯示的狀態為 Unknown func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1alpha1.TableRow, error) { ... if pod.DeletionTimestamp != nil && pod.Status.Reason == node.NodeUnreachablePodReason { reason = "Unknown" } else if pod.DeletionTimestamp != nil { reason = "Terminating" } ... }節點恢復 Ready 后 pod 狀態
當節點恢復后,不同的 workload 對應的 pod 狀態變化也是不同的。
deployment: 根據上一節描述,此時 pod 已經有正確的 pod 在其他節點 running,此時故障節點恢復后,kubelet 執行優雅刪除,刪除舊的 PodObj。
statefulset: statefulset 會從Unknown 狀態變為 Terminating 狀態,執行優雅刪除,detach PV,然后執行重新調度與重建操作。
daemonset: daemonset 會從 NodeLost 狀態直接變成 Running 狀態,不涉及重建。
我們往往會考慮下面兩個問題,statefulset 為什么沒有重建? 如何保持單副本 statefulset 的高可用呢?
關于為什么沒重建
首先簡單介紹下 statefulset 控制器的邏輯。
Statefulset 控制器通過 StatefulSetControl 以及 StatefulPodControl 2個模塊協調完成對 statefulSet 類型 workload 的狀態管理(StatefulSetStatusUpdater)和擴縮控制(StatefulPodControl)。實際上,StatefulsetControl是對 StatefulPodControl 的調用來增刪改 Pod。
StatefulSet 在 podManagementPolicy 為默認值 OrderedReady 時,會按照整數順序單調遞增的依次創建 Pod,否則在 Parallel時,雖然是按整數,但是 Pod 是同時調度與創建。
具體的邏輯在核心方法 UpdateStatefulSet 中,見圖:
我們看到的 Stateful Pod 一直處于 Unknown 狀態的原因就是因為這個控制器屏蔽了對該 Pod 的操作。因為在第一節介紹了,NodeController 的 Pod Eviction 機制已經把 Pod 標記刪除,PodObj 中包含的 DeletionTimestamp 被設置,StatefulSet Controller 代碼檢查 IsTerminating 符合條件,便直接 return 了。
// updateStatefulSet performs the update function for a StatefulSet. This method creates, updates, and deletes Pods in // the set in order to conform the system to the target state for the set. The target state always contains // set.Spec.Replicas Pods with a Ready Condition. If the UpdateStrategy.Type for the set is // RollingUpdateStatefulSetStrategyType then all Pods in the set must be at set.Status.CurrentRevision. // If the UpdateStrategy.Type for the set is OnDeleteStatefulSetStrategyType, the target state implies nothing about // the revisions of Pods in the set. If the UpdateStrategy.Type for the set is PartitionStatefulSetStrategyType, then // all Pods with ordinal less than UpdateStrategy.Partition.Ordinal must be at Status.CurrentRevision and all other // Pods must be at Status.UpdateRevision. If the returned error is nil, the returned StatefulSetStatus is valid and the // update must be recorded. If the error is not nil, the method should be retried until successful. func (ssc *defaultStatefulSetControl) updateStatefulSet( ... for i := range replicas { ... // If we find a Pod that is currently terminating, we must wait until graceful deletion // completes before we continue to make progress. if isTerminating(replicas[i]) && monotonic { glog.V(4).Infof( "StatefulSet %s/%s is waiting for Pod %s to Terminate", set.Namespace, set.Name, replicas[i].Name) return &status, nil } ... } } // isTerminating returns true if pod"s DeletionTimestamp has been set func isTerminating(pod *v1.Pod) bool { return pod.DeletionTimestamp != nil }
那么如何保證單副本高可用?
往往應用中有一些 pod 沒法實現多副本,但是又要保證集群能夠自愈,那么這種某個節點 Down 掉或者網卡壞掉等情況,就會有很大影響,要如何能夠實現自愈呢?
對于這種 Unknown 狀態的 Stateful Pod ,可以通過 force delete 方式去刪除。關于 ForceDelete,社區是不推薦的,因為可能會對唯一的標志符(單調遞增的序列號)產生影響,如果發生,對 StatefulSet 是致命的,可能會導致數據丟失(可能是應用集群腦裂,也可能是對 PV 多寫導致)。
kubectl delete pods
但是這樣刪除仍然需要一些保護措施,以 Ceph RBD 存儲插件為例,當執行force delete 前,根據經驗,用戶應該先設置 ceph osd blacklist,防止當遷移過程中網絡恢復后,容器繼續向 PV 寫入數據將文件系統弄壞。因為 force delete 是將 PodObj 直接從 ETCD 強制清理,這樣 StatefulSet Controller 將會新建新的 Pod 在其他節點, 但是故障節點的 Kubelet 清理這個舊容器需要時間,此時勢必存在 2 個容器mount 了同一塊 PV(故障節點Pod 對應的容器與新遷移Pod 創建的容器),但是如果此時網絡恢復,那么2 個容器可能同時寫入數據,后果將是嚴重的
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/32992.html
摘要:核心概念是最小的調度單元,可以由一個或者多個容器組成。該模式會跟云服務商有關,比如可以通過等創建一個外部的負載均衡器,將請求轉發到對應的服務組。而可以提供外部服務可訪問的負載均衡器等。 概述 Kubernetes 有各類資源對象來描述整個集群的運行狀態。這些對象都需要通過調用 kubernetes api 來進行創建、修改、刪除,可以通過 kubectl 命令工具,也可以直接調用 k8...
摘要:此文已由作者劉超授權網易云社區發布。五更加適合微服務和的設計好了,說了本身,接下來說說的理念設計,為什么這么適合微服務。相關閱讀為什么天然適合微服務為什么天然適合微服務為什么天然適合微服務文章來源網易云社區 此文已由作者劉超授權網易云社區發布。 歡迎訪問網易云社區,了解更多網易技術產品運營經驗 四、Kubernetes 本身就是微服務架構 基于上面這十個設計要點,我們再回來看 Kube...
摘要:二總結使用的和的,能夠很好的支持這樣的有狀態服務部署到集群上。部署方式有待優化本次試驗中使用靜態方式部署集群,如果節點變遷時,需要執行等命令手動配置集群,嚴重限制了集群自動故障恢復擴容縮容的能力。 一. 概述 kubernetes通過statefulset為zookeeper、etcd等這類有狀態的應用程序提供完善支持,statefulset具備以下特性: 為pod提供穩定的唯一的...
摘要:二總結使用的和的,能夠很好的支持這樣的有狀態服務部署到集群上。部署方式有待優化本次試驗中使用靜態方式部署集群,如果節點變遷時,需要執行等命令手動配置集群,嚴重限制了集群自動故障恢復擴容縮容的能力。 一. 概述 kubernetes通過statefulset為zookeeper、etcd等這類有狀態的應用程序提供完善支持,statefulset具備以下特性: 為pod提供穩定的唯一的...
摘要:在中,被用來管理有狀態應用的對象。并行管理并行管理告訴控制器以并行的方式啟動或者終止所有的。如果設置為,則控制器將會刪除和重建中的每一。在大部分的情況下,不會使用分隔當希望進行金絲雀發布,或者執行階段發布時,分隔是很有用的。 在Kubernetes中,StatefulSet被用來管理有狀態應用的API對象。StatefulSets在Kubernetes 1.9版本才穩定。Statefu...
閱讀 729·2021-11-24 10:19
閱讀 1106·2021-09-13 10:23
閱讀 3428·2021-09-06 15:15
閱讀 1777·2019-08-30 14:09
閱讀 1683·2019-08-30 11:15
閱讀 1837·2019-08-29 18:44
閱讀 934·2019-08-29 16:34
閱讀 2456·2019-08-29 12:46