kube-scheduler负责分配调度Pod到集群内的节点上,它监听kube-apiserver,查询还未分配Node的Pod,然后根据调度策略为这些Pod分配节点(更新Pod的NodeName字段)。
调度器:
kube-scheduler调度分为两个阶段,predicate和priority:
Predicates plugin工作原理:一层层运行插件并过滤,最后剩可用的节点集合,然后就会往Priority策略
工作原理:根据每一个插件会有权重打分机制,然后最后权重分数高的择优
CPU
内存
示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: 1Gi
cpu: 1
requests:
memory: 256Mi
cpu: 100m
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
spec:
limits:
- default:
memory: 512Mi
defaultRequest:
memory: 256Mi
type: Container
磁盘资源需求
Init Container的资源需求
kubectl label nodes 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
NodeAffinity目前支持两种:
比如下面的例子代表调度到包含标签Kubernetes.io/e2e-az-name 并且值为e2e-az1或e2e-az2的Node上,并且优选还带有标签another-node-label-key=another-node-label-value的 Node。
podAffinity基于Pod的标签来选择Node,仅调度到满足条件Pod所在的Node上,支持podAffinity和podAntiAffinity。这个功能比较绕,以下面的例子为例:
如果一个“Node所在Zone中包含至少一个带有 security=S1标签且运行中的Pod”,那么可以调度到该Node,不调度到“包含至少一个带有 security=S2标签且运行中Pod”的Node上。
Taints和Tolerations 用于保证Pod不被调度到不合适的Node上,其中Taint应用于Node上,而Toleration则应用于Pod上。
Taint node
kubectl taint nodes cadmin for-special-user=cadmin:NoSchedule
(kubectl taint nodes nodename key=value:policy)
Untaint node
kubectl taint nodes cadmin for-special-user=cadmin:NoSchedule-
tolerations:
- key: "for-special-user"
operator: "Equal"
value: "cadmin"
effect: "NoSchedule"
tolerationSeconds: 100
目前支持的Taint类型:
然而,当Pod的Tolerations 匹配Node的所有Taints的时候可以调度到该Node上;当Pod是已经运行的时候,也不会被删除(evicted)。另外对于NoExecute,如果Pod增加了一个tolerationSeconds,则会在该时间之后才删除 Pod。
从v1.8开始,kube-scheduler支持定义Pod的优先级,从而保证高优先级的Pod 优先调度。开启方法为:
多调度器
如果默认的调度器不满足要求,还可以部署自定义的调度器。并且,在整个集群中还可以同时运行多个调度器实例,通过podSpec.schedulerName 来选择使用哪一个调度器(默认使用内置的调度器)。 —— (自定义设置容忍时间)
Controller Manager 由 kube-controller-manager 和 cloud-controller-manager 组成,是 Kubernetes 的大脑,它通过 apiserver 监控整个集群的状态,并确保集群处于预期的工作状态。
kubectl exec -it kube-controller-manager-cadmin -- kube-controller-manager -h
kubectl get controllerrevision
Informer的内部机制
Reflector会解析对象的key。
代码本地的Thread Safe Store会存储对象和键值,即存储namespace
控制器的协同工作原理
Controller Manager
把结果写到API Server
通用Controller
Job
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
parallelism: 2
completions: 5
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: OnFailure
StatefulSet:管理有状态应用的工作负载Api对象
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-ss
spec:
serviceName: nginx-ss
replicas: 1
selector:
matchLabels:
app: nginx-ss
template:
metadata:
labels:
app: nginx-ss
spec:
containers:
- name: nginx-ss
image: nginx
---
apiVersion: v1
kind: Service
metadata:
name: nginx-ss
labels:
app: nginx-ss
spec:
ports:
- port: 80
clusterIP: None
selector:
app: nginx-ss
DaemonSet:日志场景
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ds
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ReplicaSet:kubernetes中的一种副本控制器,主要作用是控制由其管理的pod,使pod副本的数量始终维持在预设的个数。
Garbage Controller:watch集群里所有对象,ownerReference
控制垃圾收集器删除附属
当你删除对象时,可以指定该对象的附属是否也自动删除。 自动删除附属的行为也称为 级联删除(Cascading Deletion) 。 Kubernetes 中有两种 级联删除 模式:后台(Background) 模式和 前台(Foreground) 模式。
如果删除对象时,不自动删除它的附属,这些附属被称作 孤立对象(Orphaned)。
Kubernetes 会自动设置 ownerReference 的值。 例如,当创建一个 ReplicaSet 时,Kubernetes 自动设置 ReplicaSet 中每个 Pod 的 ownerReference 字段值。 在 Kubernetes 1.8 版本,Kubernetes 会自动为某些对象设置 ownerReference 的值。 这些对象是由 ReplicationController、ReplicaSet、StatefulSet、DaemonSet、Deployment、 Job 和 CronJob 所创建或管理的。
你也可以通过手动设置 ownerReference 的值,来指定属主和附属之间的关系。
apiVersion: v1
kind: Pod
metadata:
...
ownerReferences:
- apiVersion: apps/v1
controller: true
blockOwnerDeletion: true
kind: ReplicaSet
name: my-repset
uid: d9607e19-f88f-11e6-a518-42010a800195
...
确保scheduler和controller的高可用
Leader election加锁,能让主控制器通过心跳去获取控制器
kubelet架构
每个节点上都运行一个kubelet服务进程,默认监听10250端口。
节点管理
节点管理主要是节点自注册和节点状态更新:
Pod管理
获取Pod清单:
--config
指定的配置目录下的文件(默认/etc/Kubernetes/manifests/)。该文件每20秒重新检查一次(可配置)。--manifest-url
设置。每20秒检查一次这个端点(可配置)。sandbox容器是一个pause容器,永远sleep的一个进程。作为一个pod的底座,为了稳定性,让网络挂载上去。
cAdvisor:cAdvisor对Node机器上的资源及容器进行实时监控和性能数据采集,包括CPU使用情况、内存使用情况、网络吞吐量及文件系统使用情况,采集资源指标并上报,cAdvisor集成在Kubelet中,当kubelet启动时会自动启动cAdvisor,即一个cAdvisor仅对一台Node机器进行监控。kubelet的启动参数–cadvisor-port可以定义cAdvisor对外提供服务的端口,默认为4194。
1.cAvisor简介:
cAdvisor是Google开源的容器资源监控和性能分析工具,它是专门为容器而生,在Kubernetes中,我们不需要单独去安装,cAdvisor作为kubelet内置的一部分程序可以直接使用,也就是我们可以直接使用cadvisor采集数据,可以采集到和容器运行相关的所有指标,单独安装cAdvisor时的数据路径为/api/v1/nodes/[节点名称]/proxy/metrics/cadvisor,如果cadvisor集成到kubelet,采集数据的路径是https://127.0.0.1:10250/metrics/cadvisor
2.查看cadvisor监控指标,在k8s-master节点操作
kubectl create ns monitor-sa #创建一个monitor-sa的名称空间
kubectl create serviceaccount monitor -n monitor-sa #创建一个sa账号
kubectl create clusterrolebinding monitor-clusterrolebinding -n monitor-sa --clusterrole=cluster-admin --serviceaccount=monitor-sa:monitor
#把sa账号monitor通过clusterrolebing绑定到clusterrole上
kubectl get secret -n monitor-sa #查看monitor-sa名称空间下的secret密钥
kubectl describe secret monitor-token-j4jwf -n monitor-sa #可看到token相关的内容如下所示
eyJhbGciOiJSUzI1NiIsImtpZCI6IkV5VUZuUmlPa0pMSF9sSFdUYktjdWdGVk9CR3owMlZhUDg4UzdVQWtveEEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJtb25pdG9yLXNhIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im1vbml0b3ItdG9rZW4tajRqd2YiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibW9uaXRvciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImQ5NzJiNDA1LWEzZTYtNDJiYS04YzU3LTA2MjE2YmE3Nzk1MCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDptb25pdG9yLXNhOm1vbml0b3IifQ.U0fMb34xlIcMrC5g_v3jeTMwxg3L3VkAD6lUa84Ke3kor3aB9tT092PM4N5_8cVPRJkHkh5UXx3A7mWOErjftgux41azA2N1Zkuqt-7VXkvvBCOBAmv-95mRz9FPEbzbR9gG5EudcCFeJypYOO3n7Oipr1MS4YxGLYVjUTQ46f5GIMJli9Uw6MYkij9HwuoD8qbLulAq6W540qvJfK4Bd20kvjqzZQveD2Ej-hmUlHR2cqshgD64VgBOIAJJir4bQ04JthLqgpC9peTTYo2hJ8XK-Y5OCx2v419syb0xPC2jrzwcZabvTBG_QCB4Ly8BRAxjEDB4ox3R6EMw8Ie68A
通过下面命令可以获取到cadvisor采集的指标数据
curl https://127.0.0.1:10250/metrics/cadvisor -k -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkV5VUZuUmlPa0pMSF9sSFdUYktjdWdGVk9CR3owMlZhUDg4UzdVQWtveEEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJtb25pdG9yLXNhIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im1vbml0b3ItdG9rZW4tajRqd2YiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibW9uaXRvciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImQ5NzJiNDA1LWEzZTYtNDJiYS04YzU3LTA2MjE2YmE3Nzk1MCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDptb25pdG9yLXNhOm1vbml0b3IifQ.U0fMb34xlIcMrC5g_v3jeTMwxg3L3VkAD6lUa84Ke3kor3aB9tT092PM4N5_8cVPRJkHkh5UXx3A7mWOErjftgux41azA2N1Zkuqt-7VXkvvBCOBAmv-95mRz9FPEbzbR9gG5EudcCFeJypYOO3n7Oipr1MS4YxGLYVjUTQ46f5GIMJli9Uw6MYkij9HwuoD8qbLulAq6W540qvJfK4Bd20kvjqzZQveD2Ej-hmUlHR2cqshgD64VgBOIAJJir4bQ04JthLqgpC9peTTYo2hJ8XK-Y5OCx2v419syb0xPC2jrzwcZabvTBG_QCB4Ly8BRAxjEDB4ox3R6EMw8Ie68A"
3.cadvisor中获取到的典型监控指标如下:
指标名称 类型 含义
container_cpu_load_average_10s gauge 过去10秒容器CPU的平均负载
container_cpu_usage_seconds_total counter 容器在每个CPU内核上的累积占用时间 (单位:秒)
container_cpu_system_seconds_total counter System CPU累积占用时间(单位:秒)
container_cpu_user_seconds_total counter User CPU累积占用时间(单位:秒)
container_fs_usage_bytes gauge 容器中文件系统的使用量(单位:字节)
container_fs_limit_bytes gauge 容器可以使用的文件系统总量(单位:字节)
container_fs_reads_bytes_total counter 容器累积读取数据的总量(单位:字节)
container_fs_writes_bytes_total counter 容器累积写入数据的总量(单位:字节)
container_memory_max_usage_bytes gauge 容器的最大内存使用量(单位:字节)
container_memory_usage_bytes gauge 容器当前的内存使用量(单位:字节
container_spec_memory_limit_bytes gauge 容器的内存使用量限制
machine_memory_bytes gauge 当前主机的内存总量
container_network_receive_bytes_total counter 容器网络累积接收数据总量(单位:字节)
container_network_transmit_bytes_total counter 容器网络累积传输数据总量(单位:字节)
4.当能够正常采集到cAdvisor的样本数据后,可以通过以下表达式计算容器的CPU使用率:
(1)sum(irate(container_cpu_usage_seconds_total{image!=""}[1m])) without (cpu)
容器CPU使用率
(2)container_memory_usage_bytes{image!=""}
查询容器内存使用量(单位:字节):
(3)sum(rate(container_network_receive_bytes_total{image!=""}[1m])) without (interface)
查询容器网络接收量(速率)(单位:字节/秒):
(4)sum(rate(container_network_transmit_bytes_total{image!=""}[1m])) without (interface)
容器网络传输量 字节/秒
(5)sum(rate(container_fs_reads_bytes_total{image!=""}[1m])) without (device)
容器文件系统读取速率 字节/秒
(6)sum(rate(container_fs_writes_bytes_total{image!=""}[1m])) without (device)
容器文件系统写入速率 字节/秒
5.cadvisor 常用容器监控指标
(1)网络流量
sum(rate(container_network_receive_bytes_total{name=~".+"}[1m])) by (name)
##容器网络接收的字节数(1分钟内),根据名称查询 name=~".+"
sum(rate(container_network_transmit_bytes_total{name=~".+"}[1m])) by (name)
##容器网络传输的字节数(1分钟内),根据名称查询 name=~".+"
(2)容器 CPU相关
sum(rate(container_cpu_system_seconds_total[1m]))
###所用容器system cpu的累计使用时间(1min钟内)
sum(irate(container_cpu_system_seconds_total{image!=""}[1m])) without (cpu)
###每个容器system cpu的使用时间(1min钟内)
sum(rate(container_cpu_usage_seconds_total{name=~".+"}[1m])) by (name) * 100
#每个容器的cpu使用率
sum(sum(rate(container_cpu_usage_seconds_total{name=~".+"}[1m])) by (name) * 100)
#总容器的cpu使用率
kubelet启动pod的时候是通过CRI进行启动。
CRI:
容器运行时(Container Runtime),运行于Kubernetes(k8s)集群的每个节点中,负责容器的整个生命周期。其中Docker是目前应用最广的。随着容器云的发展,越来越多的容器运行时涌现。为了解决这些容器运行时和Kubernetes的集成问题,在Kubernetes 1.5版本中,社区推出了CRI(
Container Runtime Interface,容器运行时接口)以支持更多的容器运行时。
CRI是Kubernetes定义的一组gRPC服务。kubelet作为客户端,基于gRPC框架,通过Socket和容器运行时通信。它包括两类服务:镜像服务(Image Service)和运行时服务(Runtime Service)。
Kubernetes网络模型设计的基础原则是:
Kubernetes的集群里,IP地址是以Pod为单位进行分配的,每个Pod都拥有一个独立的IP地址。一个Pod内部的所有容器共享一个网络栈,即宿主机上的一个网络命名空间,包括它们的IP地址、网络设备、配置等都是共享的。也就是说,Pod 里面的所有容器能通过localhost:port来连接对方。在Kubernetes中,提供了一个轻量的通用容器网络接口CNI(Container Network Interface),专门用于设置和删除容器的网络连通性。容器运行时通过CNI 调用网络插件来完成容器的网络设置。
容器运行时在启动时会从CNI的配置目录中读取JSON格式的配置文件,文件后缀为".conf”“.conflist””.json"。如果配置目录中包含多个文件,一般情况下,会以名字排序选用第一个配置文件作为默认的网络配置,并加载获取其中指定的CNI插件名称和配置参数。
插件设计考量
原理:启动一个Pod,Container RunTime会去调用CNI,CNI会去调用IPAM插件,然后为Pod分配一个IP,然后会配置带宽,把配置结果告诉Container RunTime,Container RunTime就会把这个IP带给kubelet, kubelet会把状态上报到API Server,然后Pod的IP就会更新到Pod的状态里,这个Pod就有IP了
关于容器网络管理,容器运行时一般需要配置两个参数–cni-bin-dir和–cni-conf-dir。有一种特殊情况,kubelet内置的Docker作为容器运行时,是由kubelet来查找CNI插件的,运行插件来为容器设置网络,这两个参数应该配置在kubelet处:
原理:启动一个Pod,Container RunTime会去调用CNI,CNI会去调用IPAM插件,然后为Pod分配一个IP,然后会配置带宽,把配置结果告诉Container RunTime,Container RunTime就会把这个IP带给kubelet, kubelet会把状态上报到API Server,然后Pod的IP就会更新到Pod的状态里,这个Pod就有IP了
CNI插件外,Kubernetes还需要标准的CNI插件lo,最低版本为0.2.0版本。网络插件除支持设置和清理Pod网络接口外,该插件还需要支持Iptables。如果Kube-proxy 工作在lptables模式,网络插件需要确保容器流量能使用lptables转发。例如,如果网络插件将容器连接到Linux网桥,必须将net/bridge/bridge-nf-call-iptables参数sysctt 设置为1,网桥上数据包将遍历Iptables规则。如果插件不使用Linux桥接器(而是类似Open vSwitch或其他某种机制的插件),则应确保容器流量被正确设置了路由。
kubectl get crd
kubectl get ippools.crd.projectcalico.org -o yaml
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-ipv4-ippool
spec:
blockSize: 26
cidr: 192.168.0.0/16
ipipMode: Never
natOutgoing: true
nodeSelector: all()
vxlanMode: CrossSubnet
容器运行时存储
K8s支持以插件的形式来实现对不同存储的支持和扩展。
out-of-tree CSI插件
CSI通过RPC与存储驱动进行交互。
在设计CSI的时候,Kubernetes对CSI存储驱动的打包和部署要求很少,主要定义了Kubernetes的两个相关模块。
kube-controller-manager:
kubelet:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
常见的半持久化存储主要是hostPath卷。hostPath卷能将主机节点文件系统上的文件或目录挂载到指定Pod中。对普通用户而言一般不需要这样的卷,但是对很多需要获取节点系统信息的Pod而言,却是非常必要的。
例如,hostPath的用法举例如下:
使用hostPath的时候,除设置必需的path属性外,用户还可以有选择性地为hostPath 卷指定类型,支持类型包含目录、字符设备、块设备等。
hostPath注意点
- 使用同一个目录的Pod可能会由于调度到不同的节点,导致目录中的内容有所不同。
- Kubernetes在调度时无法顾及由hostPath使用的资源。
- Pod被删除后,如果没有特别处理,那么hostPath上写的数据会遗留到节点上,占用磁盘空间(pod的生命周期与hostPath是解耦的)。
支持持久化的存储是所有分布式系统所必备的特性。针对持久化存储,Kubernetes引入了 StorageClass、Volume、PVC(Persistent Volume Claim)、PV(Persitent Volume) 的概念,将存储独立于Pod的生命周期来进行管理。
Kuberntes目前支持的持久化存储包含各种主流的块存储和文件存储,譬如awsElasticBlockStore、azureDisk、cinder、NFS、cephfs、iscsi等,在大类上可以将其分为网络存储和本地存储两种类型。
pv.yaml (可用的持久化存储卷)
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 100Mi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
pvc.yaml (用户需要使用的话需要创建pvc声明去使用哪个存储卷)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
StorageClass
StorageClass 用于指示存储的类型,不同的存储类型可以通过不同的StorageClass来为用户提供服务。
StorageClass主要包含存储插件 provisioner、卷的创建和mount参数等字段。
PVC:由用户创建,代表用户对存储需求的声明,主要包含需要的存储大小、存储卷的访问模式、StroageClass 等类型,其中存储卷的访问模式必须与存储的类型一致
PV:由集群管理员提前创建,或者根据PVC的申请需求动态地创建,它代表系统后端的真实的存储空间,可以称之为卷空间。
存储对象关系
用户通过创建PVC来申请存储。控制器通过PVC的StorageClass和请求的大小声明来存储后端创建卷,进而创建PV,Pod 通过指定PVC来引用存储。
多容器之间共享存储,最简方案是emptyDir
带来的挑战: