Kubernetes development [5] - estrutura de agendamento para criar plug-ins de agendamento personalizados

Índice

informação ambiental

fundo

prefácio

quadro principal

O plug-in obtém clientSet/informer

Definir parâmetros de agendamento do plug-in

Resumir


informação ambiental

kubernetes:1.22

centos:7.9

apiVersion: kubescheduler.config.k8s.io/v1beta2 (será obsoleto em 1.25)

fundo

Para criar um pod, conhecemos o seguinte processo

Em diferentes estágios de agendamento/vinculação, podemos injetar nossos plug-ins personalizados para intervir no processo de agendamento.

A estrutura de agendamento Scheduling Framework é apenas uma ferramenta para realizar essa função.

Como o escalonador envolve o algoritmo de escalonamento, este artigo não se aprofunda nesse aspecto, mas apenas registra a construção e o uso simples do Scheduling Framework

prefácio

Para a descrição básica de diferentes plugins, é retirado da documentação oficial

Pré-Filtro

Esses plug-ins são usados ​​para pré-processar informações sobre pods ou para verificar determinadas condições que devem ser atendidas pelo cluster ou pods. Se o plug-in PreFilter retornar um erro, o ciclo de despacho será encerrado.

Filtro

Esses plug-ins são usados ​​para filtrar nós que não podem executar o pod. Para cada nó, o agendador chamará esses plug-ins de filtro na ordem em que forem configurados. Se algum plug-in de filtro marcar um nó como inviável, os plug-ins de filtro restantes não serão chamados para esse nó. Os nós podem ser avaliados simultaneamente.

PostFilter

Esses plug-ins são invocados após a fase de filtro, mas somente quando não há nós viáveis ​​para esse pod. Os plug-ins são invocados na ordem em que são configurados. Se algum plug-in PostFilter marcar um nó como "Schedulable", o restante dos plug-ins não será chamado. Uma implementação típica de PostFilter é preemptiva, tentando tornar o Pod programável por meio da preempção de recursos de outros Pods.

PreScore

Esses plug-ins são usados ​​para realizar um trabalho de "pré-pontuação", ou seja, para gerar um estado compartilhável para uso do plug-in Score. Se o plug-in PreScore retornar um erro, o ciclo de agendamento será encerrado.

Pontuação

Esses plug-ins são usados ​​para classificar os nós que passam pelo estágio de filtro. O planejador chamará cada plug-in de pontuação para cada nó. Haverá um intervalo bem definido de números inteiros representando as pontuações mínima e máxima. Após a fase de pontuação normalizada, o agendador combinará as pontuações de nó de todos os plug-ins de acordo com os pesos de plug-in configurados.

quadro principal

Primeiro, consulte o código de amostra oficial

GitHub - kubernetes-sigs/scheduler-plugins: Repositório para plug-ins do agendador fora da árvore com base na estrutura do agendador.

Construímos main.go

package main

import (
	"fmt"
	"k8s.io/component-base/logs"
	"k8s.io/kubernetes/cmd/kube-scheduler/app"
	"myscheduler/lib"
	"os"
)

func main() {
	//来自/blob/master/cmd/scheduler/main.go
	command := app.NewSchedulerCommand(
        //可变参数——需要注入的插件列表
		app.WithPlugin(lib.TestSchedulingName, lib.NewTestScheduling),
	)
	logs.InitLogs()
	defer logs.FlushLogs()

	if err := command.Execute(); err != nil {
		_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}

Entre eles, precisamos definir o nome e a implementação relacionada do plug-in, então a seguinte estrutura de código é introduzida

//出自/pkg/capacityscheduling 只留了主体框架,简化了大部分

const TestSchedulingName = "test-scheduling" //记住这个调度器名称

type TestScheduling struct {}

func (*TestScheduling) Name() string { //实现framework.Plugin的接口方法
	return TestSchedulingName
}

func NewTestScheduling(configuration runtime.Object, f framework.Handle) (framework.Plugin, error) {
	return &TestScheduling{}, nil
}

método de plug-in

Os plug-ins de diferentes estágios são, na verdade, interfaces diferentes no pacote do framework. Se quisermos injetar os plug-ins do estágio correspondente, devemos implementar o método de interface correspondente. Aqui, consulte o método oficial para gerar rapidamente a interface método de preFilter através de goland.

var _ framework.PreFilterPlugin = &TestScheduling{}

Dois métodos de interface gerados

//业务方法
func PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) *framework.Status
//这个方法是在生成pod或删除pod时产生一些需要评估的内容,返回值同样是个接口,返回自身并快速生成接口方法即可
func PreFilterExtensions() framework.PreFilterExtensions

Basta percebermos a intervenção que nele queremos realizar.

agendador de registro

As etapas de compilação do código e empacotamento da imagem são omitidas~

Como o pod do agendador precisa acessar o apiserver, você precisa especificar a conta de serviço e as permissões de ligação

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test-scheduling-clusterrole
rules:
  - apiGroups:
      - ""
    resources:
      - endpoints
      - events
    verbs:
      - create
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - pods
    verbs:
      - delete
      - get
      - list
      - watch
      - update
  - apiGroups:
      - ""
    resources:
      - bindings
      - pods/binding
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - pods/status
    verbs:
      - patch
      - update
  - apiGroups:
      - ""
    resources:
      - replicationcontrollers
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - apps
      - extensions
    resources:
      - replicasets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - apps
    resources:
      - statefulsets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - policy
    resources:
      - poddisruptionbudgets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - persistentvolumeclaims
      - persistentvolumes
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - namespaces
      - configmaps
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "storage.k8s.io"
    resources: ['*']
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "coordination.k8s.io"
    resources:
      - leases
    verbs:
      - create
      - get
      - list
      - update
  - apiGroups:
      - "events.k8s.io"
    resources:
      - events
    verbs:
      - create
      - patch
      - update

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: test-scheduling-sa
  namespace: kube-system
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test-scheduling-clusterrolebinding
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: test-scheduling-clusterrole
subjects:
  - kind: ServiceAccount
    name: test-scheduling-sa
    namespace: kube-system

Quando o agendador é iniciado, ele precisa consultar o arquivo de configuração correspondente para registrar o tipo de plug-in, definir os parâmetros etc. Montamos a configuração no contêiner por meio do configMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-scheduling-config
  namespace: kube-system
data:
   config.yaml: |
    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    leaderElection:
      leaderElect: false
    profiles:
      - schedulerName: test-scheduling
        plugins:
          preFilter:
            enabled:
            - name: "test-scheduling"

Em seguida é a definição do agendador, executado fixando o nó e montando o executável (somente para teste)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-scheduling
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-scheduling
  template:
    metadata:
      labels:
        app: test-scheduling
    spec:
      nodeName: master-01
      serviceAccount: test-scheduling-sa
      containers:
        - name: tests-cheduling
          image: alpine:3.12
          imagePullPolicy: IfNotPresent
          command: ["/app/test-scheduling"]
          args:
            - --config=/etc/kubernetes/config.yaml
            - --v=3
          volumeMounts:
            - name: config
              mountPath: /etc/kubernetes
            - name: app
              mountPath: /app
      volumes:
        - name: config
          configMap:
            name: test-scheduling-config
        - name: app
          hostPath:
             path: /root/schedular

 Verifique o status do agendador. Após configurá-lo para Running, você pode especificar o agendador no item schedulerName na configuração do Pod ao criar a carga.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: testngx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: testngx
  template:
    metadata:
      labels:
        app: testngx
    spec:
      schedulerName: test-scheduling
      containers:
        - image: nginx:1.18-alpine
          imagePullPolicy: IfNotPresent
          name: testngx
          ports:
            - containerPort: 80

Observando o log do escalonador, pode-se constatar que a intervenção foi bem-sucedida

kubectl logs test-scheduling-54fd7c585f-gmbb6 -n kube-system -f

I1117 08:51:46.567953 1 eventhandlers.go:123] "Adicionar evento para pod não agendado" pod="default/testngx-7cd55446f7-4cmgv"

I1117 08:51:46.568030 1 scheduler.go:516] "Tentativa de agendar pod" pod="default/testngx-7cd55446f7-4cmgv"

I1117 08:51:46.568094 1 agendamento de teste.go:57] pré-filtragem

O plug-in obtém clientSet/informer

A partir desta seção, é apresentada a prática comum em vários plugins;

 A primeira é a aquisição do go-client, cenário de exemplo: no plug-in do filtro, nós de filtro com rótulos xx.

Observamos que no construtor da estrutura do plug-in existe o seguinte parâmetro de entrada:

f framework.Handle

Este tipo de Handle pode nos ajudar a obter clientSet ou informante; aqui tomamos como exemplo a obtenção de informante.

Em seguida, há novas variáveis ​​de membro e construtores

type TestScheduling struct {
	fac  informers.SharedInformerFactory
}
func NewTestScheduling(configuration runtime.Object, f framework.Handle) (framework.Plugin, error) {
	return &TestScheduling{
		fac:  f.SharedInformerFactory(),
	}, nil //注入informer工厂
}

 Então você pode implementar essa lógica no método de interface do plug-in

func (s *TestScheduling) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
	klog.V(3).Infof("过滤节点")
	for k, v := range nodeInfo.Node().Labels {
		if k == "scheduling" && v != "true" {
			return framework.NewStatus(framework.Unschedulable, "设置了不可调度的标签")
		}
	}
	return framework.NewStatus(framework.Success)
}

Você também precisa habilitar este plugin no arquivo de configuração do agendador

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-scheduling-config
  namespace: kube-system
data:
   config.yaml: |
    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    leaderElection:
      leaderElect: false
    profiles:
      - schedulerName: test-scheduling
        plugins:
          preFilter:
            enabled:
            - name: "test-scheduling"
          filter:
            enabled:
            - name: "test-scheduling"

Rotule um nó no cluster de acordo

kubectl label node node-01 scheduling=false

Em seguida, crie uma carga e observe que o pod está no estado pendente 

testngx-677b6896b-nqsk8 0/1 Pendente 0 5s

Verifique o evento do pod e observe que o nó foi filtrado porque o rótulo não programável está definido

Eventos:

  Digite Motivo Idade da Mensagem

  ---- ------ ---- ---- -------

  Warning FailedScheduling 28s test-scheduling 0/3 nós estão disponíveis: 1 nó(s) não correspondeu à afinidade/seletor de nó do pod, 1 nó(s) tinha mancha {node-role.kubernetes.io/master: }, que o pod não tolerou, 1 conjunto de rótulo não programável.

Até agora, a demonstração de exemplo de aquisição do conjunto de clientes/informador e falha de agendamento no agendador está concluída.

Vale ressaltar que se o load yaml corrigir o nó por meio de nodeName, o agendador configurado por shcedulerName não afetará o agendamento do pod, mesmo que a lógica no plug-in do filtro filtre o nó a ser corrigido.

Definir parâmetros de agendamento do plug-in

Esta seção demonstra que o agendador pode ler dinamicamente definindo os parâmetros do arquivo de configuração.

Um cenário de exemplo é que, se o número de pods no namespace em que a carga é criada exceder n, uma falha de agendamento será retornada. Como o estágio de filtragem do nó não está envolvido, é melhor implementá-lo no plug-in de pré-filtro.

O arquivo de configuração do agendador possui a seguinte configuração:

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-scheduling-config
  namespace: kube-system
data:
   config.yaml: |
    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    leaderElection:
      leaderElect: false
    profiles:
      - schedulerName: test-scheduling
        plugins:
          preFilter:
            enabled:
            - name: "test-scheduling"
          filter:
            enabled:
            - name: "test-scheduling"
        pluginConfig:
          - name: test-scheduling
            args:
              maxPods: 5

O número máximo de pods é definido como 5.

Adicione este parâmetro como uma variável de membro à estrutura do agendador

type TestScheduling struct {
	fac  informers.SharedInformerFactory
	args *Args
}

type Args struct {
	MaxPods int `json:"maxPods,omitempty"`
}

No construtor, existe um parâmetro de entrada:

tempo de execução de configuração.Object

Isso contém nossa configuração no arquivo de configuração, reverta-a em nossa estrutura de configuração e atribua-a no construtor

func NewTestScheduling(configuration runtime.Object, f framework.Handle) (framework.Plugin, error) {
	args := &Args{}
	if err := frameworkruntime.DecodeInto(configuration, args); err != nil { //由配置文件注入参数,并通过configuration获取
		return nil, err
	}
	return &TestScheduling{
		fac:  f.SharedInformerFactory(),
		args: args,
	}, nil //注入informer工厂
}

Pode ser obtido diretamente na função de interface do plug-in do filtro.

func (s *TestScheduling) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) *framework.Status {
	klog.V(3).Infof("预过滤")
	pods, err := s.fac.Core().V1().Pods().Lister().Pods(p.Namespace).List(labels.Everything())
	if err != nil {
		return framework.NewStatus(framework.Error, err.Error())
	}
	if len(pods) > s.args.MaxPods { 
		return framework.NewStatus(framework.Unschedulable, "pod数量超过了最大限制")
	}
	return framework.NewStatus(framework.Success)
}

As observações para falhas de agendamento são semelhantes às da seção anterior e foram omitidas aqui.

Resumir

 Até agora, concluímos algumas jogabilidades simples no plug-in de filtro rígido. Posteriormente, demonstraremos algumas operações básicas no plug-in de pré-pontuação/pontuação do plug-in de pontuação suave, incluindo a captura e o armazenamento de dados brutos no plug-in de pré-pontuação pré-score, se esses dados forem passados ​​para os plug-ins de pré-pontuação e pontuação , e como normalizar por meio do Normalize It é otimizado para fazer pontuação de vários plug-ins de forma colaborativa, para que a pontuação final caia dentro de um intervalo calibrado.

Acho que você gosta

Origin blog.csdn.net/kingu_crimson/article/details/127917631
Recomendado
Clasificación