Kubernetes Pod调度原理介绍

最近两周一直没有抽出时间写点Kubernetes的东西,这篇学习一下Kubernetes对Pod的调度。我们先来复习一下Kubernetes的一些基本概念。

Kubernetes的基本概念

Kubernetes是一个基于容器技术的分布式架构平台,它首先是一个开源的容器集群管理系统,又是一个分布式系统开发、运维和支撑平台。 Kubernetes为容器应用提供了服务注册和发现、负载均衡、服务部署和运行、服务滚动升级、在线扩容和缩容、资源调度、资源配额管理等功能。 可以说Kubernetes具备完备的集群管理能力,贯串分布式系统开发、测试、部署、运维监控各个环节。

Kubernetes中的绝大部分概念都抽象成Kubernetes管理的一种资源对象,下面我们一起复习一下这些基本概念:

1、Master:Master节点是Kubernetes集群的控制节点,负责整个集群的管理和控制。Master节点上包含以下组件:

  • kube-apiserver:集群控制的入口,提供HTTP REST服务
  • kube-controller-manager:Kubernetes集群中所有资源对象的自动化控制中心
  • kube-scheduler:负责Pod的调度,我们本篇将主要学一下kube-scheduler的调度功能

2、Node: Node节点是Kubernetes集群中的工作节点,Node上的工作负载由Master节点分配,工作负载主要是运行容器应用。Node节点上包含以下组件:

  • kubelet:负责Pod的创建、启动、监控、重启、销毁等工作,同时与Master节点协作,实现集群管理的基本功能。
  • kube-proxy:实现Kubernetes Service的通信和负载均衡
  • 运行运行容器化(Pod)应用

3、Pod: Pod是Kubernetes最基本的部署调度单元。每个Pod可以由一个或多个业务容器和一个根容器(Pause容器)组成。一个Pod表示某个应用的一个实例

4、ReplicaSet:是Pod副本的抽象,用于解决Pod的扩容和伸缩

5、Deployment:Deployment表示部署,在内部使用ReplicaSet来实现。可以通过Deployment来生成相应的ReplicaSet完成Pod副本的创建

6、Service:Service是Kubernetes最重要的资源对象。Kubernetes中的Service对象可以对应微服务架构中的微服务。Service定义了服务的访问入口,服务的调用者Pod通过这个地址访问Service后端的Pod副本实例。 Service通过Label Selector同后端的Pod副本建立关系,Deployment保证后端Pod副本的数量,也就是保证服务的伸缩性

kube-scheduler调度过程

Master节点上的kube-scheduler负责Pod的调度,kube-scheduler将Pod安置到目标Node上,之后将Pod交给目标Node上的kubelet,Pod生命周期后续的部分由kubelet接管。

kube-scheduler使用特定的调度算法和调度策略将等待调度的Pod调度到某个合适的Node上。等待调度的Pod包含使用API创建的Pod,也包含ControllerManager为补足副本而创建的Pod。具体过程为kube-scheduler会从待调度Pod列表中取出每个Pod,并根据调度算法和调度策略从Node列表中选出一个最合适的Node,将Pod和目标Node绑定(Binding),同时将绑定信息写入到etcd中。目标Node上的kubelet通过kube-apiserver监听到kube-scheduler触发的Pod和目标Node的绑定事件,就会pull镜像和启动容器。

预选(Predicates)和优选(Priorites)步骤

kube-scheduler当前提供的调度过程包含预选(Predicates)和优选(Priorites)两步:

  • 预选(Predicates):将根据配置的预选策略(Predicates Policies)过滤掉不满足这些策略的Node,剩下的Node将作为候选Node成为优选过程的输入。
  • 优选(Priorites):根据配置的优选策略(Priorities Policies)计算出每个候选Node的积分,按积分排名,得分最高的Node胜出,Pod会和该Node绑定。

kube-scheduler进程的–algorithm-provider参数用于指定调度算法,当前Kubernetes版本1.6默认配置的是DefaultProvider。 plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go中包含了默认的Predicates Policies和Priorities Policies。 Scheduler Algorithm in Kubernetes中包含全部的Predicates Policies和Priorities Policies。

另外kube-scheduler可以通过–policy-config-file参数指定想要启用的Predicates Policies和Priorities Policies。例如:

{
"kind" : "Policy",
"apiVersion" : "v1",
"predicates" : [
    {"name" : "PodFitsHostPorts"},
    {"name" : "PodFitsResources"},
    {"name" : "NoDiskConflict"},
    {"name" : "NoVolumeZoneConflict"},
    {"name" : "MatchNodeSelector"},
    {"name" : "HostName"}
    ],
"priorities" : [
    {"name" : "LeastRequestedPriority", "weight" : 1},
    {"name" : "BalancedResourceAllocation", "weight" : 1},
    {"name" : "ServiceSpreadingPriority", "weight" : 1},
    {"name" : "EqualPriority", "weight" : 1}
    ],
"hardPodAffinitySymmetricWeight" : 10
}

Pod调度入门

接下来我们先通过几个例子来学习一下基于预选策略(Predicates Policies)和优选策略(Priorities Policies)实现的Pod调度。 我们可以使用这些策略实现将Pod调度到某个或某些特别的Node上。

我们使用前面在Kubernetes 1.6 高可用集群部署部署的集群作为试验环境。

192.168.61.11 node1
192.168.61.12 node2
192.168.61.13 node3
192.168.61.14 node4

MatchNodeSelector

MatchNodeSelector是一个预选策略,用于判断候选Node是否包含Pod的spec.nodeSelector指定的标签。

我们先来看看当前集群中的Node具有的标签:

kubectl get nodes --show-labels
NAME      STATUS    AGE       VERSION   LABELS
node1     Ready     34d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node1
node2     Ready     32d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node2
node3     Ready     32d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node3
node4     Ready     29d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node4

可以看到每个Node上都有kubernetes.io/hostname=xxx这个label,我们可以使用Pod的spec.nodeSelector指定这个label将Pod调度具体的某个Node上。例如我们创建一个如下的Deployment,nginx-deployment.yaml:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
      nodeSelector:
        kubernetes.io/hostname: node4
kubectl create -f nginx-deployment.yaml
deployment "nginx-deployment" created

kubectl get deploy
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           1m

kubectl get pod -o wide
NAME                                READY     STATUS        RESTARTS   AGE       IP           NODE
nginx-deployment-1270158812-9qh1v   1/1       Running       0          1m        10.244.3.5   node4
nginx-deployment-1270158812-f5k7h   1/1       Running       0          1m        10.244.3.6   node4

可以看到这个Pod的两个副本都被调度到node4上。下面继续试验,假设我们要将Pod调度到磁盘类型为ssd并且专门用来跑Web应用的Node上。 我们先删除前面创建的Deployment:

kubectl delete deploy nginx-deployment

我们给node1, node2, node3标记如下:

kubectl label node node1 disktype=ssd apptype=web
kubectl label node node2 disktype=ssd
kubectl label node node3 disktype=ssd apptype=web
kubectl label node node4 apptype=web

kubectl get nodes --show-labels
NAME      STATUS    AGE       VERSION   LABELS
node1     Ready     34d       v1.6.2    apptype=web,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node1
node2     Ready     32d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node2
node3     Ready     32d       v1.6.2    apptype=web,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node3
node4     Ready     29d       v1.6.2    apptype=web,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node4

修改前面的nginx-deployment.yaml文件:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
      nodeSelector:
        disktype: ssd
        apptype: web
kubectl create -f nginx-deployment.yaml
deployment "nginx-deployment" created

kubectl get deploy
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            0           45s

kubectl get pod -o wide
NAME                               READY     STATUS    RESTARTS   AGE       IP            NODE
nginx-deployment-908661896-pzs2z   1/1       Running   0          2m        10.244.0.69   node1
nginx-deployment-908661896-wvd9p   1/1       Running   0          2m        10.244.2.51   node3

可以看到Pod被调度到了node1和node3上,而node1和node3上我们前面标记了disktype=ssd和apptype=web。

NodeAffinityPriority

NodeAffinityPriority是一个优选策略,是Kubernetes 1.2开始提供的Kubernetes调度中的亲和性机制。NodeAffinityPriority的Node选择器不再限于对Node Label的精确匹配,而支持多种操作符(如In, NotIn, Exists, DoesNotExist, Gt, Lt)。支持两种类型的选择器,一种是requiredDuringSchedulingIgnoredDuringExecution,它保证所选的Node必须满足Pod对Node的所有要求,这种更像前面的MatchNodeSelector;另外一种是preferresDuringSchedulingIgnoredDuringExecution,它对kube-scheduler提出需求,kube-scheduler会尽量但不保证满足NodeSelector的要求。

例如:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: gcr.io/google_containers/pause:2.0
标签:Kubernetes 发布于:2019-11-16 10:08:46