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

Kubernetes的基本概念

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

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

  • Master:Master节点是Kubernetes集群的控制节点,负责整个集群的管理和控制。Master节点上包含以下组件:
    • kube-apiserver:集群控制的入口,提供HTTP REST服务
    • kube-controller-manager:Kubernetes集群中所有资源对象的自动化控制中心
    • kube-scheduler:负责Pod的调度,我们本篇将主要学一下kube-scheduler的调度功能
  • Node: Node节点是Kubernetes集群中的工作节点,Node上的工作负载由Master节点分配,工作负载主要是运行容器应用。Node节点上包含以下组件:
    • kubelet:负责Pod的创建、启动、监控、重启、销毁等工作,同时与Master节点协作,实现集群管理的基本功能。
    • kube-proxy:实现Kubernetes Service的通信和负载均衡
    • 运行运行容器化(Pod)应用
  • Pod: Pod是Kubernetes最基本的部署调度单元。每个Pod可以由一个或多个业务容器和一个根容器(Pause容器)组成。一个Pod表示某个应用的一个实例
  • ReplicaSet:是Pod副本的抽象,用于解决Pod的扩容和伸缩
  • Deployment:Deployment表示部署,在内部使用ReplicaSet来实现。可以通过Deployment来生成相应的ReplicaSet完成Pod副本的创建
  • 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。例如:

 1{
 2"kind" : "Policy",
 3"apiVersion" : "v1",
 4"predicates" : [
 5	{"name" : "PodFitsHostPorts"},
 6	{"name" : "PodFitsResources"},
 7	{"name" : "NoDiskConflict"},
 8	{"name" : "NoVolumeZoneConflict"},
 9	{"name" : "MatchNodeSelector"},
10	{"name" : "HostName"}
11	],
12"priorities" : [
13	{"name" : "LeastRequestedPriority", "weight" : 1},
14	{"name" : "BalancedResourceAllocation", "weight" : 1},
15	{"name" : "ServiceSpreadingPriority", "weight" : 1},
16	{"name" : "EqualPriority", "weight" : 1}
17	],
18"hardPodAffinitySymmetricWeight" : 10
19}

Pod调度入门

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

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

1192.168.61.11 node1
2192.168.61.12 node2
3192.168.61.13 node3
4192.168.61.14 node4

MatchNodeSelector

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

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

1kubectl get nodes --show-labels
2NAME      STATUS    AGE       VERSION   LABELS
3node1     Ready     34d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node1
4node2     Ready     32d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node2
5node3     Ready     32d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node3
6node4     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

 1apiVersion: apps/v1beta1
 2kind: Deployment
 3metadata:
 4  name: nginx-deployment
 5spec:
 6  replicas: 2
 7  template:
 8    metadata:
 9      labels:
10        app: nginx
11    spec:
12      containers:
13      - name: nginx
14        image: nginx
15        imagePullPolicy: IfNotPresent
16        ports:
17        - containerPort: 80
18      nodeSelector:
19        kubernetes.io/hostname: node4
 1kubectl create -f nginx-deployment.yaml
 2deployment "nginx-deployment" created
 3
 4kubectl get deploy
 5NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
 6nginx-deployment   2         2         2            2           1m
 7
 8kubectl get pod -o wide
 9NAME                                READY     STATUS        RESTARTS   AGE       IP           NODE
10nginx-deployment-1270158812-9qh1v   1/1       Running       0          1m        10.244.3.5   node4
11nginx-deployment-1270158812-f5k7h   1/1       Running       0          1m        10.244.3.6   node4

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

1kubectl delete deploy nginx-deployment

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

 1kubectl label node node1 disktype=ssd apptype=web
 2kubectl label node node2 disktype=ssd
 3kubectl label node node3 disktype=ssd apptype=web
 4kubectl label node node4 apptype=web
 5
 6kubectl get nodes --show-labels
 7NAME      STATUS    AGE       VERSION   LABELS
 8node1     Ready     34d       v1.6.2    apptype=web,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node1
 9node2     Ready     32d       v1.6.2    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node2
10node3     Ready     32d       v1.6.2    apptype=web,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node3
11node4     Ready     29d       v1.6.2    apptype=web,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node4

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

 1apiVersion: apps/v1beta1
 2kind: Deployment
 3metadata:
 4  name: nginx-deployment
 5spec:
 6  replicas: 2
 7  template:
 8    metadata:
 9      labels:
10        app: nginx
11    spec:
12      containers:
13      - name: nginx
14        image: nginx
15        imagePullPolicy: IfNotPresent
16        ports:
17        - containerPort: 80
18      nodeSelector:
19        disktype: ssd
20        apptype: web
 1kubectl create -f nginx-deployment.yaml
 2deployment "nginx-deployment" created
 3
 4kubectl get deploy
 5NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
 6nginx-deployment   2         2         2            0           45s
 7
 8kubectl get pod -o wide
 9NAME                               READY     STATUS    RESTARTS   AGE       IP            NODE
10nginx-deployment-908661896-pzs2z   1/1       Running   0          2m        10.244.0.69   node1
11nginx-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的要求。

例如:

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: with-pod-affinity
 5spec:
 6  affinity:
 7    podAffinity:
 8      requiredDuringSchedulingIgnoredDuringExecution:
 9      - labelSelector:
10          matchExpressions:
11          - key: security
12            operator: In
13            values:
14            - S1
15        topologyKey: failure-domain.beta.kubernetes.io/zone
16    podAntiAffinity:
17      preferredDuringSchedulingIgnoredDuringExecution:
18      - weight: 100
19        podAffinityTerm:
20          labelSelector:
21            matchExpressions:
22            - key: security
23              operator: In
24              values:
25              - S2
26          topologyKey: kubernetes.io/hostname
27  containers:
28  - name: with-pod-affinity
29    image: gcr.io/google_containers/pause:2.0

参考