- 你所不知道的事
-
Service与其后端Pod副本集群之间则是通过Label Selector来实现“无缝对接”的
· 没有指定targetPort,则默认targetPort与port相同
· Cluster IP无法被Ping,因为没有一个“实体网络对象”来响应。 -
statefulSet (抄的)
· 在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet和Job都面向无状态的服务。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Akka集群、ZooKeeper集群等,这些应用集群有4个共同点:
(1)每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并通信。
(2)集群的规模是比较固定的,集群规模不能随意变动。
(3)集群中的每个节点都是有状态的,通常会持久化数据到永久存储中。
(4)如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。
· 如果通过RC或Deployment控制Pod副本数量来实现上述有状态的集群,就会发现第1点是无法满足的,因为Pod的名称是随机产生的,Pod的IP地址也是在运行期才确定且可能有变动的,我们事先无法为每个Pod都确定唯一不变的ID。另外,为了能够在其他节点上恢复某个失败的节点,这种集群中的Pod需要挂接某种共享存储,为了解决这个问题,Kubernetes从1.4版本开始引入了PetSet这个新的资源对象,并且在1.5版本时更名为StatefulSet,StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种,它有如下特性。- StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名称为kafka,那么第1个Pod叫kafka-0,第2个叫kafka-1,以此类推。
- StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
- StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。
· StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,即在每个StatefulSet定义中都要声明它属于哪个Headless Service。Headless Service与普通Service的关键区别在于,它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例都创建了一个DNS域名,这个域名的格式为:
${podname}.${headless service name}
· 比如一个3节点的Kafka的StatefulSet集群对应的Headless Service的名称为kafka,StatefulSet的名称为kafka,则StatefulSet里的3个Pod的DNS名称分别为kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,这些DNS名称可以直接在集群的配置文件中固定下来。 -
pod调度过程
-
rbac
饭粒1:RoleBinding将在default命名空间中把pod-reader角色授予用户jane,这一操作可以让jane读取default命名空间中的Podkind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: read-pods namespace: default subjects: - kind: User name: jane apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
饭粒2:虽然secret-reader是一个集群角色,但是因为使用了RoleBinding,所以dave只能读取development命名空间中的secret
kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: read-secrets namespace: development subjects: - kind: User name: dave apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: secret-reader apiGroup: rbac.authorization.k8s.io
饭粒3:允许manager组的用户读取任意Namespace中的secret
kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: read-secrets-global subjects: - kind: Group name: manager apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: secret-reader apiGroup: rbac.authorization.k8s.io
饭粒4:Pod是一个命名空间内的资源,log就是一个下级资源。要在一个RBAC角色中体现,就需要用斜线“/”来分隔资源和下级资源。若想授权让某个主体同时能够读取Pod和Pod log,则可以配置resources为一个数组
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: pod-and-pod-logs-reader rules: - apiGroups: [""] resources: ["pods", "pods/log"] verbs: ["get", "list"]
饭粒5:让一个主体只能对一个ConFigmap进行get和update操作
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: configmap-updater rules: - apiGroups: [""] resources: ["configmap"] resourceNames: ["my-configmap"] verbs: ["update", "get"]
常用的rbac角色示例
- 允许读取核心API组的Pod资源
rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"]
- 允许读写extensions和apps两个API组中的deployment资源
rules: - apiGroups: ["extensions", "apps"] resources: ["deployments"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- 允许读取pods及读写jobs
rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] - apiGroups: ["batch", "extensions"] resources: ["jobs"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- 允许读取一个名为my-config的ConfigMap(必须绑定到一个RoleBinding来限制到一个Namespace下的ConfigMap)
rules: - apiGroups: [""] resources: ["configmaps"] resourceNames: ["my-config"] verbs: ["get"]
- 读取核心组的Node资源(Node属于集群级的资源,所以必须存在于ClusterRole中,并使用ClusterRoleBinding进行绑定)
rules: - apiGroups: [""] resources: ["node"] verbs: ["get", "list", "watch"]
- 允许对非资源端点“/healthz”及其所有子路径进行GET和POST操作(必须使用ClusterRole和ClusterRoleBinding)
rules: - nonResourceURLs: ["/healthz", "/healthz/*"] verbs: ["get", "post"]
常见的rbac角色绑定
- 用户饭粒alice@example.com
subjects: - kind: User name: "alice@example.com" apiGroup: rbac.authorization.k8s.io
- 用户组饭粒frontend-admins
subjects: - kind: Group name: "frontend-admins" apiGroup: rbac.authorization.k8s.io
- kube-system命名空间中的默认Service Account
subjects: - kind: ServiceAccount name: default namespace: kube-system
- qa命名空间中的所有Service Account
subjects: - kind: Group name: system:serviceaccounts:qa apiGroup: rbac.authorization.k8s.io
- 所有Service Account
subjects: - kind: Group name: system:serviceaccounts apiGroup: rbac.authorization.k8s.io
- 所有认证用户(Kubernetes 1.5以上版本)
subjects: - kind: Group name: system:authenticated apiGroup: rbac.authorization.k8s.io
- 所有未认证用户(Kubernetes 1.5以上版本)
subjects: - kind: Group name: system:unauthenticated apiGroup: rbac.authorization.k8s.io
- 全部用户(Kubernetes 1.5以上版本)
subjects: - kind: Group name: system:authenticated apiGroup: rbac.authorization.k8s.io - kind: Group name: system:unauthenticated apiGroup: rbac.authorization.k8s.io
可以在运行时进行调整,无须重新启动API Server。
-
abac
-
常见ABAC授权示例
- 允许用户alice对所有资源做任何操作
{ "apiVersion":"abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "alice", "namespace": "*", "resource": "*", "apiGroup": "*"} }
- kubelet可以读取任意Pod
{ "apiVersion":"abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "pods", "readonly": true} }
- kubelet可以读写Event对象
{ "apiVersion":"abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "events"} }
- 用户bob只能读取projectCaribou中的Pod
{ "apiVersion":"abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "bob", "namespace": "projectCaribou", "resource": "pod", "readonly": true} }
- 任何用户都可以对非资源类路径进行只读请求:
{ "apiVersion":"abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:authenticated", "readonly": true, "nonResourcePath": "*"} } { "apiVersion":"abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:unauthenticated", "readonly": true, "nonResourcePath": "*"} }
如果添加了新的ABAC策略,则需要重启API Server以使其生效。
-
Service Account也是一种账号,但它并不是给Kubernetes集群的用户(系统管理员、运维人员、租户用户等)用的,而是
给运行在Pod里的进程用的
,它为Pod里的进程提供了必要的身份证明。 -
Docker支持的4类网络模式:
- host模式:使用–net=host指定。
- container模式:使用–net=container:NAME_or_ID指定。
- none模式:使用–net=none指定。
- bridge模式:使用–net=bridge指定,为默认设置。
在Kubernetes管理模式下通常只会使用bridge模式。
docker默认网桥模型:
在bridge模式下,Docker Daemon第1次启动时会创建一个虚拟的网桥,默认的名称是docker0,然后按照RPC1918的模型在私有网络空间中给这个网桥分配一个子网。针对由Docker创建的每一个容器,都会创建一个虚拟的以太网设备(Veth设备对),其中一端关联到网桥上,另一端使用Linux的网络命名空间技术,映射到容器内的eth0设备,然后从网桥的地址段内给eth0接口分配一个IP地址。 -
API Server可能返回的状态码
-
kubernetes集群管理
· 设置节点不可调度:kubectl patch node [node_name] -p '{"spec":{"unschedulable":true}}'
kubectl cordon [node_name]
将某个Node脱离调度范围时,在其上运行的Pod并不会自动停止,管理员需要手动停止在该Node上运行的Pod。
· 集群加入新节点:
在Kubernetes集群中,一个新Node的加入是非常简单的。在新的Node上安装Docker、kubelet和kube-proxy服务,然后配置kubelet和kube-proxy的启动参数,将Master URL指定为当前Kubernetes集群Master的地址,最后启动这些服务。通过kubelet默认的自动注册机制,新的Node将会自动加入现有的Kubernetes集群中。
· 标签管理:- 添加标签
kubectl label pod [pod_name] [label_key]=[label_value]
eg: kubectl label pod myweb-gcq4v user=mine
kubectl label node [node_name] [label_key]=[label_value] - 删除标签(key后面加 -)
kubectl label pod [pod_name] [label_key]-
- 修改标签(–overwrite)
kubectl label pod [pod_name] [label_key]=[label_value]--overwrite
- 查看标签(–show-labels / -L[label_key])
kubectl get po --show-labels
eg: kubectl get po -Luser
· 资源管理 Requests/Limits
CPU 是可压缩资源(当使用量超过限制值不会立刻杀死容器),而内存是不可压缩资源(只要使用量一超过阈值马上杀死容器)
· QoS
Kubernetes 支持了三种 QoS 级别,分别为BestEffort(尽力而为、不太可靠的)、Burstable(弹性波动、较可靠的) 和 Guranteed(完全可靠的)- BestEffort表示 Pod 中没有一个容器设置了 Requests 或 Limits,它的
优先级最低
;(在容器未定义Limits时,Limits值默认等于节点资源容量的上限) - Burstable表示 Pod 中每个容器至少定义了 CPU 或 Memory 的 Requests,或者 Requests 和 Limits 不相等,它属于
中等优先级
; - Guranteed则表示 Pod 中每个容器 Requests 和 Limits 都相等,这类 Pod 的运行
优先级最高
。简单来说就是cpu.limits = cpu.requests,memory.limits = memory.requests。容器可以不定义Requests,因为Requests值在未定义时默认等于Limits。
当节点中某些Pod 使用的 CPU 和 Memory 越来越多,或者宿主机某些进程(例如 Kubelet、Docker)占用了 CPU 和 Memory,这个时候Kubernetes 就会根据 QoS 的优先级来选择 Kill 掉一部分 Pod。优先级最低的BestEffort类型的 Pod,占用的资源越多越优先被 Kill 掉。从另外一个角度来看,BestEffort Pod由于没有设置资源Limits,所以在资源充足时,它们可以充分使用所有的闲置资源。
饭粒1:Burstable - OOMapiVersion: v1 kind: Pod metadata: name: memory-burstable-demo namespace: demo spec: containers: - name: memory-demo image: polinux/stress resources: requests: memory: "50Mi" limits: memory: "100Mi" command: ["stress"] args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
饭粒2:Burstable - Out of CPU
apiVersion: v1 kind: Pod metadata: name: cpu-burstable-demo namespace: demo spec: containers: - name: cpu-demo image: vish/stress resources: limits: cpu: "1" requests: cpu: "0.5" args: - -cpus - "2"
· LimitRange 设置默认资源防线
apiVersion: v1 kind: LimitRange metadata: name: mem-limit-range namespace: example spec: limits: - default: # 默认 limit memory: 512Mi cpu: 2 defaultRequest: # 默认 request memory: 256Mi cpu: 0.5 max: # 最大 limit,pod容器可以设置的最大 Limits,default 字段不能高于此值 memory: 800Mi cpu: 3 min: # 最小 request,Pod 中容器可以设置的最小 Requests,defaulRequest 字段不能低于此值 memory: 100Mi cpu: 0.3 maxLimitRequestRatio: # limit/request 的最大比率 memory: 2 cpu: 2 type: Container # 支持 Container / Pod / PersistentVolumeClaim 三种类型
说明:
- 如果设置了max而又没有设置 default,那么所有未显式设置这些值的容器都将使用此处的最大值作为 Limits。
- 如果设置了min而又没有设置 defaulRequest,那么所有未显式设置这些值的容器都将使用此处的最小值作为 Requests。
- 参数必须满足以下关系:Min ≤ Default Request ≤ Default Limit ≤ Max
- 官文参考:
如何配置每个命名空间最小和最大的 CPU 约束
如何配置每个命名空间最小和最大的内存约束
如何配置每个命名空间默认的 CPU 申请值和限制值
如何配置每个命名空间默认的内存申请值和限制值
如何配置每个命名空间最小和最大存储使用量
· ResourceQuota 设置资源总量限制
资源配额可以通过在kube-apiserver的--admission-control
参数值中添加ResourceQuota参数进行开启apiVersion: v1 kind: ResourceQuota metadata: name: compute-resources namespace: demo #在demo空间下 spec: hard: requests.cpu: "10" #cpu预配置10 requests.memory: 100Gi #内存预配置100Gi limits.cpu: "40" #cpu最大不超过40 limits.memory: 200Gi #内存最大不超过200Gi configmaps: "10" #最多10个configmap pods: "20" #最多20个pod persistentvolumeclaims: "10" #最多10个pvc replicationcontrollers: "20" #最多20个rc secrets: "10" #最多10个secrets services: "10" #最多10个service services.loadbalancers: "10" #最多10个lb类型的service requests.nvidia.com/gpu: 10 #最多10个GPU
-
Kubernetes的GC机制(垃圾回收) 官文
Kubernetes 默认开启了 GC 的能力,由kubelet负责,可以回收内部的各种资源对象,还可以回收 kubelet 节点上的冗余镜像以及退出的容器。
· 容器镜像的 GC 参数:
--minimum-image-ttl-duration
表示一个镜像在清理前的最小存活时间;
--image-gc-high-threshold
表示磁盘使用率的上限阈值,默认值是 90%,即当磁盘使用率达到 90% 的时候会触发对镜像的 GC 操作;
--image-gc-low-threshold
表示磁盘使用率的下限阈值,默认值是 80%,即当磁盘使用率降到 80% 的时候,GC 操作结束。· 对容器的 GC 参数:
--minimum-container-ttl-duration
表示已停止的容器在被清理之前最小的存活时间,默认值是 1 分钟,即容器停止超过 1 分钟才会被标记可被 GC 清理;
--maximum-dead-containers-per-container
表示一个 Pod 内可以保留的已停止的容器数量,默认值是 2。Kubernetes 是以 Pod 为单位进行容器管理的。有时候 Pod 内运行失败的容器,比如容器自身的问题,或者健康检查失败,会被 kubelet 自动重启,这将产生一些停止的容器;
--maximum-dead-containers
表示在本节点上可以保留的已停止容器的最大数量,默认值是240。毕竟这些容器也会消耗额外的磁盘空间,所以超过这个上限阈值后,就会触发 Kubelet 的 GC 操作,来帮你自动清理这些已停止的容器,释放磁盘空间。
注意:在有些场景中,容器的日志需要保留在本地,如果直接清理掉这些容器会丢失日志。所以这里我强烈建议你将–maximum-dead-containers-per-container设置为一个足够大的值,以便每个容器至少有一个退出的实例。· Kubernetes 内部对象的 GC
*) 查看资源对象的从属关系:metadata.ownerReferences
如:Deployment(owner) -> ReplicaSet (dependent) -> Pod (dependent)
*) GC方式:
通过deleteOptions.propagationPolicy
这个字段,来控制删除的策略.- 级联删除
后台(Background)模式:Kuberentes 会立即删除主对象,比如 Deployment,之后 Kubernetes 会在后台 GC 其附属的对象,比如 ReplicaSet。
前台(Foreground)模式:先删除其所属的对象,然后再删除主对象。$ kubectl proxy --port=8080 $ curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \ -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \ -H "Content-Type: application/json"
注意:ownerReference.blockOwnerDeletion为true时才会组织删除主对象。$ kubectl proxy --port=8080 $ curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-replicaset \ -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \ -H "Content-Type: application/json"
- 非级联删除(孤立对象Orphaned)
kubectl 命令行在删除操作的时候,默认是进行级联删除的,如果你不想级联删除,可以这么操作:$ kubectl proxy --port=8080 $ curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \ -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \ -H "Content-Type: application/json"
$ kubectl delete replicaset my-repset --cascade=false
- 级联删除
-
pod抢占式资源调度(资源不足时优先级高的把优先级低的挤下线)
PriorityClass 定义优先级apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: "This priority class should be used for XYZ service pods only."
注意:
这个对象是个集群级别的定义,并不属于任何 namespace,可以被全局使用,即各个 namespace 中创建的 Pod 都能使用 PriorityClass。
globalDefault 用来表明是否将该 PriorityClass 的数值作为默认值,并将其应用在所有未设置 priorityClassName 的 Pod 上。如果没有任何一个 PriorityClass 对象的 globalDefault 被设置为 true 的话,就意味着所有未设置 spec.priorityClassName 的 Pod 的优先级为 0。即便你新建了某个 PriorityClass 对象,并将其 globalDefault 设置为 true,那么也不不影响集群中的存量 Pod,只会对新建的 Pod 生效。k8s定义了两个优先级值
- 自定义 PriorityClass 的时候,定义的优先级值是不能高于 HighestUserDefinablePriority 值 1000000000
- Kubernetes 在初始化的时候就自带了两个 PriorityClasses,即 system-cluster-critical 和 system-node-critical,优先级值为 2000000000,可以通过
kubectl get priorityclass
查看$ kubectl get priorityclass NAME VALUE GLOBAL-DEFAULT AGE system-cluster-critical 2000000000 false 59d system-node-critical 2000001000 false 59d
pod设置系统优先级饭粒:
apiVersion: apps/v1 kind: Deployment metadata: ... name: coredns namespace: kube-system ... spec: ... template: ... spec: ... priorityClassName: system-cluster-critical ... status: ...
$ kubectl describe pod coredns -n kube-system Name: coredns Namespace: kube-system Priority: 2000000000 Priority Class Name: system-cluster-critical ...
注意:在使用的时候不允许自己去设置 spec.priority 的数值,我们只能通过 spec.priorityClassName 来设置优先级。
设置非抢占式,只优先调度不抢占(不推荐):- PriorityClass定义时,在与globalDefault同级加上配置
preemptionPolicy: Never
- kube-scheduler 的抢占能力是通过
disablePreemption
这个参数来控制的,该标志默认为 false。如果你还是坚持禁用抢占,可以将 disablePreemption 设置为true
提高集群的资源利用率最常见的做法就是采用优先级的方案。在实际使用的时候,要避免恶意用户创建高优先级的 Pod,这样会导致其他 Pod 被驱逐或者无法调度,严重影响集群的稳定性和安全性。集群管理员可以为特定用户创建特定优先级级别,来防止他们恶意使用高优先级的 PriorityClass。