Índice
O plug-in obtém clientSet/informer
Definir parâmetros de agendamento do plug-in
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
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.