- Scheduling 调度 5%
- 使用label选择器来调度pods
- 理解Daemonset的角色
- 理解resource limit 会如何影响pod 调度
- 理解如何运行多个调度器, 以及如何配置pod使用它们
- 不使用调度器, 手动调度一个pod
- 查看和显示调度事件events
- 知道如何配置kubernetes scheduler
- 查看节点信息
kubectl get nodes
输出如下:
NAME STATUS AGE VERSION
worker0 Ready 1d v1.6.0+fff5156
worker1 Ready 1d v1.6.0+fff5156
worker2 Ready 1d v1.6.0+fff5156
- 选择一个节点, 并给这个节点打上label
kubectl label nodes <your-node-name> disktype=ssd
- 验证节点上是否有成功打上对应label
kubectl get nodes --show-labels
输出结果类似于
NAME STATUS AGE VERSION LABELS
worker0 Ready 1d v1.6.0+fff5156 ...,disktype=ssd,kubernetes.io/hostname=worker0
worker1 Ready 1d v1.6.0+fff5156 ...,kubernetes.io/hostname=worker1
worker2 Ready 1d v1.6.0+fff5156 ...,kubernetes.io/hostname=worker2
- 创建一个pod, 调度到这个node上 在pod的配置里, 要指定nodeSelector, 比如disktype=ssd。 这意味着pod启动后会调度到打上了disktype=ssd标签的node上
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
- 验证pod启动后是否调度到指定节点上
kubectl get pods --output=wide
输出类似于
AME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 13s 10.200.0.4 worker0
daemonset会在每一个节点上运行一个pod的副本,当有node加入集群时,会自动在新节点上启动pod副本, 当node被移出节点时, 上面的pod副本会被自动删除。 使用场景:
- 运行storage进程,如 glusterd, ceph等
- 进行日志收集进程fluentd, logstash , filebeat等
- 监控进程,比如prometheus node exporter, collectd, datadog agent, new relic agent, ganglia gmond等
apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
containers:
- name: fluentd-elasticsearch
image: gcr.io/google-containers/fluentd-elasticsearch:1.20
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
如果只想调度到某些主机上, 可以指定nodeSelector的值。
通常情况下pod要运行的机器是由scheduler来选择的的。但是DaemonSet controller创建的pod要运行的机器是已经被选择好的(pod在创建的时候.spec.nodeName字段就指定了, 因此会被scheduler忽略)
因此:
- unschedulable字段对daemonset controller创建的pod不起作用,当你尝试kubectl drain <node_name> 时, 驱赶不了daemonset创建的pod
- 即使scheduler没有起来, daemonset创建的pod也可以正常调度,因为它的调度不依赖scheduler
但是如果node被标记为以下的两种状态: node.alpha.kubernetes.io/notReady node.alpha.kubernetes.io/unreachable
daemon pods就不会调度到上面。
- 使用hostPort
- 创建headless service(指定pod 的选择器标签),使用dns解析得到一堆endpoint
- 创建service(指定pod 的选择器标签), 用service 的cluster ip或者域名进行连接
node的label改变时,daemonset会自动将pod启动在新匹配上的节点,或者把pod移除出不匹配的节点
当你用kubectl删除daemonset时,指定参数--cascade=false, 这时daemonset会被删除, 但是pod会被保留, 重新使用新的模板创建daemonset, 新的daemonset会识别和匹配到已经存在于node上的pods, 但是不会修改和删除它们, 你需要手动执行删除这些pod, 才能让新的pod能够创建。
值得注意的是, 在kubernetes 1.6版本之后, daemonset可以执行滚动更新了。 具体操作可以参考https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/
- init, upstartd, 和systemd
但是daemonset的好处在于 可以像k8s上的应用一样, 监控和记录日志 可以像k8s上的应用一样使用模板进行编排,比如Pod Template,并使用k8s的工具, 比如kubectl 在未来版本中, 可以和node的升级工作流和daemonset创建的pod进行整合 可以通过资源限制, 更好和应用的容器做隔离 可以方便的进行滚动升级 安装部署方便, 而且有新的节点移入移除时,进程的添加和删除都是自动执行的
- bare pods
在pod中指定特定的节点运行。 daemonset的方式优于这种方式, 它会自动替换和删除有故障主机上的pod, 或者处于维护中(比如内核升级)的节点上的pod
- static pods
static pod不能被 kubectl和其它的kubernetes 的api client。 static pods 有可能在未来的版本中被废弃。
- Deployments
deployment更适合有滚动升级, 扩缩容需求的无状态服务 daemonset适合那些需要运行在所有或特定主机上的服务, 或者需要先于其它pod启动的服务
- 打包自定义的scheduler镜像
- 为你的scheduler制作deployment
apiVersion: apps/v1beta1
kind: Deployment
metadata:
labels:
component: scheduler
tier: control-plane
name: my-scheduler
namespace: kube-system
spec:
replicas: 1
template:
metadata:
labels:
component: scheduler
tier: control-plane
version: second
spec:
containers:
- command:
- /usr/local/bin/kube-scheduler
- --address=0.0.0.0
- --leader-elect=false
- --scheduler-name=my-scheduler
image: gcr.io/my-gcp-project/my-kube-scheduler:1.0
livenessProbe:
httpGet:
path: /healthz
port: 10251
initialDelaySeconds: 15
name: kube-second-scheduler
readinessProbe:
httpGet:
path: /healthz
port: 10251
resources:
requests:
cpu: '0.1'
securityContext:
privileged: false
volumeMounts: []
hostNetwork: false
hostPID: false
volumes: []
这个scheduler pod启动的时候, 要指定--scheduler-name, 这个名称是要唯一的。另外其它pod要指定调度器的时候用的是这里指定的调度器名称。 3. 在集群中运行scheduler
kubectl create -f my-scheduler.yaml
如果要让多个调度器之间进行选主操作, 要修改pod yaml里的启动参数, 增加如下参数
- --leader-elect=true
- --lock-object-namespace=lock-object-namespace
- --lock-object-name=lock-object-name
如果你开启了RBAC, 要更新你的cluster role - system:kube-scheduler. 在resourceNames:那一行,加上你的调度器名称
- pod启动时指定调度器
apiVersion: v1
kind: Pod
metadata:
name: annotation-second-scheduler
labels:
name: multischeduler-example
spec:
schedulerName: my-scheduler
containers:
- name: pod-with-second-annotation-container
image: gcr.io/google_containers/pause:2.0
spec.schedulerName指定的调度器的名称要和 scheduler的启动参数--scheduler-name里指定的名称一致,才能正常调度。
- 验证pod有没有被期望的调度器调度
kubectl get events
从events可以看出调度行为
使用static pod可以实现手动调度一个pod。
static pod的特点总结如下:
- 不通过apiserver watch, 由本机的kubelet进程执行watch,并拉起
- 只在本机进行调度
- 没有健康检查
- 不被controller manager调度
- 因为apiserver里有pod的镜像信息, 通过apiserver还是可以看到pod的信息
方法一: kubelet启动时要指定参数kubelet --pod-manifest-path=,这里的the directory要放置static pod的编排文件的目录。 把static pod的编排文件放到此目录下, kubelet可以监听到变化, 并根据编排文件创建pod。
方法二: 指定--manifest-url=, kubelet会从这个URL下载编排文件, 并创建pod
当我们直接用kubectl的命令kubectl delete pod删除一个静态pod时, 我们会发现static pod过一段时间又会被kubelet拉起。
要完全删除一个static pod, 可以移除把该static pod的编排文件, 从--pod-manifest-path=所指定的目录里移除即可。
因为static pod有一个特性是我们的应用的docker 容器被删除,或者pod被kubectl误删了之后, static pod还能被kubelet进程拉起。通过这种方式保证了应用的可用性。 有点相当于systemd的功能, 但比systemd好的一点是, static pod的镜像信息会在apiserver中注册。 这样的话, 我们就可以统一对部署信息进行可视化管理。 另外static调度了的是容器, 无需拷贝二进制文件到主机上, 应用封装在镜像里也保证了环境的一致性, 无论是应用的编排文件还是应用的镜像都方便进行版本管理和分发。
实际生产中, static pod可以用来部署kubernetes的kube-proxy, kube-scheduler, kube-controller-manager, kube-apiserver等进程。
当我们要部署一个有状态的应用, 有几种方式:
- 使用statefulset
- 给主机打上label,只允许主机在这几台主机上调度
- 通过挂载ceph盘来存储有状态数据(要求master和node节点上都要安装rbd工具)
- 使用static pod ,但是这种方式适合那些无滚动升级需求, 无扩缩容需求,不频繁进行升级的应用