最近两周一直没有抽出时间写点Kubernetes的东西,这篇学习一下Kubernetes对Pod的调度。我们先来复习一下Kubernetes的一些基本概念。
Kubernetes是一个基于容器技术的分布式架构平台,它首先是一个开源的容器集群管理系统,又是一个分布式系统开发、运维和支撑平台。 Kubernetes为容器应用提供了服务注册和发现、负载均衡、服务部署和运行、服务滚动升级、在线扩容和缩容、资源调度、资源配额管理等功能。 可以说Kubernetes具备完备的集群管理能力,贯串分布式系统开发、测试、部署、运维监控各个环节。
Kubernetes中的绝大部分概念都抽象成Kubernetes管理的一种资源对象,下面我们一起复习一下这些基本概念:
1、Master:Master节点是Kubernetes集群的控制节点,负责整个集群的管理和控制。Master节点上包含以下组件:
2、Node: Node节点是Kubernetes集群中的工作节点,Node上的工作负载由Master节点分配,工作负载主要是运行容器应用。Node节点上包含以下组件:
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副本的数量,也就是保证服务的伸缩性
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镜像和启动容器。
kube-scheduler当前提供的调度过程包含预选(Predicates)和优选(Priorites)两步:
优选(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
}
接下来我们先通过几个例子来学习一下基于预选策略(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是一个预选策略,用于判断候选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是一个优选策略,是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