k8s scheduler不同namespace同名node節點調度異常

      上一篇文章中說到k8s是一個主從架構,接口管理和調度模塊都在主節點上,node節點作爲kubernetes的從節點,負責實際的pod

管理。在創建Pod時決定Pod創建在那個node節點上則是由scheduler模塊來進行調度。



       目前工作中用的kubernetes版本爲1.5版本,在使用過程中發現存在同名節點導致調度異常的問題,這周仔細研究了下kubernetes

 scheduler模塊的源碼,發現在對node節點進行管理時,kubernetes維持了一個cache,cache採用Map<name,nodeInfo>對node節點的

信息進行緩存,這裏的name只是存了node節點的名稱,並沒有加入node的namespace,這個就會產生問題,在存在不同namespace

下同名的node時,在緩存中只會隨機存入一個node節點的信息,scheduler只會感知一個node,所有pod都會調度到緩存的節點上,當這

個node節點資源不足時則調度失敗,而這時另一個namespace下的同名node沒有被調度。

func (cache *schedulerCache) AddNode(node *v1.Node) error {
	cache.mu.Lock()
	defer cache.mu.Unlock()

	n, ok := cache.nodes[node.Name]
	if !ok {
		n = NewNodeInfo()
		cache.nodes[node.Name] = n
	}
	return n.SetNode(node)
}

   node節點作爲kubernetes的從節點,通過納管流程將普通的計算節點納管到kubermetes中進行管理,納管時kubernetes會在計算節點

上裝上kubelet和kube-proxy,用來在節點上管理Pod和Pod的路由。node節點納管進kubernetes後,會在etcd中存入相應節點信息,同時

scheduler也會在cache中調用addNode在Map<name,nodeInfo>加入nodeInfo。用戶調用接口創建Pod之後,scheduler watch到這個動

作,會對Pod進行調度,主要是執行gennic_scheduler.go中的Scherdule()方法,找到合適的node進行具體創建這個Pod。


func (g *genericScheduler) Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister) (string, error) {
	trace := utiltrace.New(fmt.Sprintf("Scheduling %s/%s", pod.Namespace, pod.Name))
	defer trace.LogIfLong(100 * time.Millisecond)

	nodes, err := nodeLister.List()
	if err != nil {
		return "", err
	}
	if len(nodes) == 0 {
		return "", ErrNoNodesAvailable
	}

	// Used for all fit and priority funcs.
	err = g.cache.UpdateNodeNameToInfoMap(g.cachedNodeInfoMap)
	if err != nil {
		return "", err
	}

	trace.Step("Computing predicates")
	filteredNodes, failedPredicateMap, err := findNodesThatFit(pod, g.cachedNodeInfoMap, nodes, g.predicates, g.extenders, g.predicateMetaProducer, g.equivalenceCache)
	if err != nil {
		return "", err
	}

	if len(filteredNodes) == 0 {
		return "", &FitError{
			Pod:              pod,
			FailedPredicates: failedPredicateMap,
		}
	}

	trace.Step("Prioritizing")
	metaPrioritiesInterface := g.priorityMetaProducer(pod, g.cachedNodeInfoMap)
	priorityList, err := PrioritizeNodes(pod, g.cachedNodeInfoMap, metaPrioritiesInterface, g.prioritizers, filteredNodes, g.extenders)
	if err != nil {
		return "", err
	}

	trace.Step("Selecting host")
	return g.selectHost(priorityList)
}


  schedule方法先通過listNodeNamespace方法從etcd獲取到node節點,然後通過UpdateNodeNameToInfoMap方法更新cache中的node信息,之後通過一系列算法進行節點的選擇,都沒有涉及到namespace的區分。


解決方法:

  在cache的node操作方法中都用namespace+"/"+name作爲node的緩存鍵值,在獲取nodeList時只獲取pod當前namespace和default namespace下的node節點,對應的在NodeNameToInfoMap中也只獲取當前namespace下的node,後面的預測和選取都針對當前namespace下的節點操作,達到namespace的空間區分的目的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章