TOC

准入控制器(Admission Controllers)

    对于Kubernetes来讲,我们的APIServer是整个访问集群的唯一入口, 这里所谓的访问是对Kubernetes所谓的资源管理控制类的访问,这个时候APIServer通畅被公认为整个集群系统的GATEWAY,用户对于我们的Kubernetes之上跑的应用的访问,是借助于我们的节点的NodePort或者Ingress来访问的,那么这些请求是不会非得经过APIServer的,对于整个Kubernetes来讲,我们用户分为两类,第一类对Kubernetes之上资源本身的各种各样属性管理类的访问,访问方式无非就是命令行的访问,基于WEB UI或者编程接口类的访问,APIServer除了接收来自于集群外部的请求之外,还有向集群内部的请求,比如向CoreOS这样管理类的应用,甚至每一个Pod都需要与APIServer进行交互,至少Pod需要获取自己相关的信息的时候,需要连接到APIServer进行交互,为此,APIServer在内部内嵌了有三个重要的功能插件,分别是认证、授权、准入控制;
    至于这些核心功能都是以插件的方式进行部署,因此对于认证、授权、准入控制就有了各式各样的插件,并且多个模块他们是可以同时启用的,如果都不启用表示我们的Kubernetes不支持对应的功能了,比如认证,比较典型的几个认证机制,第一个是客户端证书认证,第二种基于用户名和密码认证,第三种是令牌认证,一般而言每一种认证方式,通常都需要通过http协议的首部来承载相关的认证信息,尤其是令牌;
    对于授权而言,也一样支持多个授权插件同时启用,到今天为止,我们的Kubernetes所支持的四种授权模块,主要分四种,分别是Node、ABAC、RBAC、Webhook,认证和授权的工作逻辑很想象,他们都基于短路的方式进行工作,而且是一票通过,也就意味着任何一个对应的插件只要反馈这个用户认证或者授权通过了,那么它就通过了APIServer的认证或者授权的校验,后续其他认证模块将不会再进行检查;
    准入控制是一个比较独特的工作逻辑,准入控制的模块分为两类,一类是便易型模块,它主要的作用是按照模块的规定修改用户所提交的关于资源的定义的属性值的提供,第二种是校验型模块,它主要是用于校验用户给定的配置信息是否符合我们系统之上已有的或者用户定义的某种规范,这种叫做校验型的准入控制,所有的准入控制代码我们称之为准入控制器;
    准入控制器有很多种,其中便易类的准入控制器一般不会拒绝用户的操作,因为他们只是用来修改用户所提交的资源,尤其是提交了新的属性定义,或者新资源,这些定义如果不符合规范,比如缺少了字段,或者字段的值描述方式有误,那么它会自动将它进行补全或者修正或者填充,这种我们称之为便易型准入控制器,而校验型的准入控制器,则是校验用户提交的修改操作是否被准入的,属于被准入的那么这次检查表示通过,如果用户请求不能合乎准入控制器所定义的规范,请求就会被拒绝;
    准入控制器的工作逻辑是一票否决,准入控制器只对写操作有效,因为无论便易型或者是校验型它只对你新增的字段或者新修改的值有关系,如果某一次的检测过程定义完了开始进行校验,一旦发生被拒绝这种情况,那么这次请求就完全被拒绝了,因此它仅对修改类的操作有效,并且工作为一票否决模式,而Kubernetes的准入控制器有许多种;
准入控制器种类
AlwaysAdmmit(deprecated):总是允许,检查一下,但是不拒绝用户的任何请求;
AlwaysPullImages:无论Pod定义imagePullPolicy的什么值,如果使用了这个准入插件校验,那么Pod每次进行变动都会重新pull镜像,强制避免使用本地镜像;
AlwaysDeny(deprecated):总是拒绝,无论正确与否都拒绝;
DefaultStorageClass:默认存储类,也就意味着当我们创建了一个不属于StorageClass的PV或者PVC的时候,自动给他分配一个StorageClass;
LimitRanger:限制范围,在定义Pod资源限制的时候可以定义Pod resources,也就是说一个Pod的容器可以使用多少CPU或者内存资源,在生产环境当中每一个Pod都应该合理的定义它的需求和限制而不应该留空,但是我们不期望每一次定义Pod清单都去显示定义Limit或者requests,那么就可以使用LimitRanger,它允许我们在某一个名称空间上,创建一个LimitRanger的资源,这个是一个Kubernetes标准资源,一旦把它定义在名称空间之后,就相当于可以为这个名称空间所有创建的Pod限制,必须有资源下限和上限的定意思,如果没有LimitRanger可以定义默认值,如果定义了也可以限制于不能低于或者不能高于某个值,如果不符合条件就拒绝,这个拒绝是靠LimitRanger来拒绝了的,所以在启动APIServer的时候必须显示给定开启LimitRanger插件才会生效;
ResoucreQuota:资源配额,我们可以在每一个或者某一个名称空间上定义一个ResoucreQuota资源类型,它也是标准的Kubernetes资源,ResoucreQuota主要用来定义整个名称空间最多使用的资源总量,它可以定义在一个名称空间上能创建多少个资源,可以明确指定这个名称空间10个Pod、3个Service、3个Deployment、3个pv等都可以定义,也可以名称指定所有Pod加起来所消耗的CPU不能超过10核,内存不能超过20G;
ServiceAccount:我们没创建一个Pod,内部都会有一个Volumes,并且被挂在到Pod,其实说白了每创建一个Pod都会附加一个ServiceAccount这个ServiceAccount的名称叫default,每一个ServiceAccount都应该有一个secrets,这个secrets主要认证到APIServer,因此每一个Pod的默认的ServiceAccount的secrets是被作为存储卷的方式自动关联到Pod上去的,而这一切都是依靠ServiceAccount准入控制器自动实现的;
PodSecurityPoliy:Pod安全策略,定义Pod时,可以在Pod级别或者容器级别定义SecurityContext以确保我们定义的Pod不会违反定义的安全策略带来危险,为了避免用户没有定义SecurityContext的Pod带来的危险问题,我们可以在名称空间级别启用PodSecurityPoliy准入控制器,可以通过PodSecurityPoliy限制说所有运行在某一名称空间上的每一个Pod都必须明确符合我们在PodSecurityPoliy所定义的Pod安全策略,但是一旦启用了PodSecurityPoliy就必须要准入控制器,任何用户所创建的Pod行为必须有一个PodSecurityPoliy资源定义了这个Pod允许被创建,如果没有定义,那么此次请求会被拒绝,所以PodSecurityPoliy默认没启用就是这个原因;
DefaultTolerationSeconds:
DenyEscalatingExec:
查看默认启用的准入控制器
[root@node1 ~]# grep 'admission' /etc/kubernetes/manifests/kube-apiserver.yaml 
    - --enable-admission-plugins=NodeRestriction
LimitRanger
    定义一个namespace内部,在Pod级别或者容器级别配置可使用的资源配额;
Container Limits/Pod Limits
    容器级别的Limits,可以限制CPU或者内存的最大最小的使用范围,当然也可以只定义requests或者只定义limits;
Min::最小资源限制,可以定义一个具体的值也可以指定一个比例;
Max:最大资源限制,可以定义一个具体的值也可以指定一个比例;
MaxLimitRequestRatio:允许的最大倍数,也就是说Max最大比Min大多少被,注意,是最大,所以不是说一定要多少倍;
LimitRanger配置示例
apiVersion: v1
kind: LimitRange
metadata:
  name: core-resource-limits
spec:
  limits:
  - type: Pod
    max:
      cpu: 2
      memory: 1Gi
    min:
      cpu: 200m
      memory: 6Mi
  - type: Container
    max:
      cpu: 2
      memory: 1Gi
    min:
      cpu: 200m
      memory: 6Mi
    default:  # 如果某一容器在定义时,没有定义请求上限,可以用default定义
      cpu: 300m
      memory: 200Mi
    defaultRequest: # 如果某一容器在定义时,没有定义请求下限,可以用default定义
      cpu: 200m
      memory: 100Mi
    maxLimitRequestRatio: # 上限最多不能是下限的多少倍
      cpu: 10

LimitRanger实战示例

    通过Pod的方式来测试LimitRanger资源配额的限制效果;
首先给定一个LimitRange规则
# 在cce名称空间创建一个limitrange资源,表示这个名称空间的所有Pod或者Container都会受到这个limitrange准入控制器的检测
[root@node1 ~]# cat limitrange.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: cce
---
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
  namespace: cce
spec:
  limits:
  - type: Container
    default:  # 如果某一容器在定义时,没有定义请求上限,可以用default定义
      cpu: 1000m
    defaultRequest: # 如果某一容器在定义时,没有定义请求下限,可以用default定义
      cpu: 1000m
    min: # 最小下限
      cpu: 500m
    max: # 最大上限
      cpu: 2000m
    maxLimitRequestRatio:  # 允许最大倍数,最大允许max是min的多少倍,如果max和maxLimitRequestRatio同时定义那么就得符合标准,min*maxLimitRequestRatio<=max
      cpu: 4
[root@node1 ~]# kubectl describe limitranges -n cce cpu-limit-range 
Name:       cpu-limit-range
Namespace:  cce
Type        Resource  Min   Max  Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---   ---  ---------------  -------------  -----------------------
Container   cpu       500m  2    1                1              4
当Pod没有定义资源配额的时候
# 创建一个自主式Pod,不定义资源上下限,查看limitrange是否会默认附加预定义的配置
[root@node1 ~]# cat cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-limit-range
  namespace: cce
spec:
  containers:
  - name: test
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
[root@node1 ~]# 
[root@node1 ~]# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-limit-range
  namespace: cce
spec:
  containers:
  - name: test
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
[root@node1 ~]# kubectl apply -f pod.yaml 
pod/test-limit-range created
# 可以看到已经添加了默认值
[root@node1 ~]# kubectl describe pod -n cce test-limit-range |egrep -A 1 '(Limits|Requests)'
    Limits:
      cpu:  1
    Requests:
      cpu:        1
当Pod定义的资源配置不符合我们的LimitRange规则的时候
# 假设我们自己定义了资源请求不符合的我们的LimitRange准入控制器的要求就会拒绝
 [root@node1 ~]# cat limitrange2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: test2-limit-range
  namespace: cce
spec:
  containers:
  - name: test
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
    resources:
      requests:
        cpu: 200m  # 请求CPU不符合我们定义的LimitRange的最小500m
      limits:
        cpu: 300m
# 已经显式拒绝了
[root@node1 ~]# kubectl apply -f limitrange2.yaml 
Error from server (Forbidden): error when creating "limitrange2.yaml": pods "test2-limit-range" is forbidden: minimum cpu usage per Container is 500m, but request is 200m

ResoucreQuota

    ResoucreQuota可以在某一个指定的名称空间上限制整个名称空间可使用的资源总额,限制所有非终止状态的Pod的资源总和;
cpu:如果不设定上下限,那么直接定义cpu即可;
limits.cpu:CPU最大不能超过多少,计算方式是,所有非终止状态的Pod的cpu.limits.cpu的上限只和不能超过这个总额;
limits.memory:内存最大不能超过多少,计算方式是,所有非终止状态的Pod的cpu.limits.memory的上限只和不能超过这个总额;;
memory:如果不设定上下限,那么直接定义memory即可;
requests.cpu:CPU最小不能超过多少,计算方式是,所有非终止状态的Pod的cpu.requests.cpu的上限只和不能超过这个总额;
requests.memory:内存最小不能超过多少,计算方式是,所有非终止状态的Pod的cpu.requests.memory的上限只和不能超过这个总额;
requests.storage:指定名称空间的所有Pod的pvc的总量不能超过这个值;
persistentvolumeclaims:限制在指定名称空间的pvc的数量总和不能超过这个值;
Kubernetes 1.9之后新特性
    在Kubernetes 1.9之后允许对所有资源类型做数量限制,比如pv、pvc、pod等,具体配置如下;
语法:count/<resource>.group
例子:
    service数量限制:count/services
    deployment数量限制:count/deployment.apps
    pvc限制:count/persistentvolumeclaims
    任意类型的资源:count/*
    ...
限额生效范围
    这种配额只对哪些种类型的Pod生效,可以限制指定类别的Pod,而不是所有类别,当然,默认是所有类别;
Terminating:只让他生效在终止状态的Pod上面;
NotTerminating:只让他生效在非终止状态的Pod上面;
BestEffort:只让他生效在NotBestEffort状态的Pod上面;
NotBestEffort:只让他生效在非NotBestEffort的状态的Pod上面;
示例
apiVersion: v1
kind: ResoucreQuota
metadata:
  name: core-object-counts  # 核心资源技术
  namespace: cce # 指定生效的namespace
spec:
  hard: # 硬限制
    configmaps: 10 # configmaps资源不能超过10个
    persistentvolumeclaims: 4 # pvc不能超过4个
    replicationcontrollers: 20 # replicationcontrollers控制器不能超过20个
    secrets: 10 # secrets不能超过10个
    services: 10  # services不能超过10个
---
apiVersion: v1
kind: ResoucreQuota
metadata:
  name: compute-resources # 计算资源限制
  namespace: cce # 指定生效的namespace
spec:
  hard: # 硬限制
    pods: 10 # pod不能超过10个
    requests.cpu: 4 # 所有Pod请求的CPU不能超过4个
    requests.memory: 1Gi # 所有Pod请求的memory不能超过1G
    limits.cpu: 2 # 所有Pod最大使用的CPU总数不能超过2核心
    limits.memory: 2Gi # 所有Pod最大使用的memory总数不能超过2核心
实战示例
# 创建一个ResourceQuota资源
[root@node1 ~]# vi quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources # 计算资源限制
  namespace: cce # 指定生效的namespace
spec:
  hard: # 硬限制
    services: 1 # 该名称空间最大之能有一个services
    pods: 1 # pod不能超过10个
    requests.cpu: 1 # 所有Pod请求的CPU不能超过4个
    requests.memory: 1Gi # 所有Pod请求的memory不能超过1G
    limits.cpu: 1 # 所有Pod最大使用的CPU总数不能超过2核心
    limits.memory: 1Gi # 所有Pod最大使用的memory总数不能超过2核心
    count/deployments.apps: 1  # 这个名称空间最多之能有一个apps群组的deployment控制器
    count/deployments.extensions: 1 # 新版本默认使用apps
    persistentvolumeclaims: 2                                                                                                    
[root@node1 ~]# kubectl apply -f !$
kubectl apply -f quota.yaml
# 查看配置
[root@node1 ~]# kubectl describe -n cce resourcequotas compute-resources 
Name:                         compute-resources
Namespace:                    cce
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        0     1
count/deployments.extensions  0     1
limits.cpu                    1     1
limits.memory                 0     1Gi
persistentvolumeclaims        0     2
pods                          1     1
requests.cpu                  1     1
requests.memory               0     1Gi
services                      1     1
# 测试资源显示是否生效
[root@node1 ~]# kubectl create service clusterip c1 --tcp=81 -n cce
service/c1 created
# 可以看到已经在namespace级别限制了services的数量
[root@node1 ~]# kubectl create service clusterip c2 --tcp=82 -n cce      
Error from server (Forbidden): services "c2" is forbidden: exceeded quota: compute-resources, requested: services=1, used: services=1, limited: services=1

PodSecurityPoliy示例

[root@node1 chapter10]# cat psp-privileged.yaml 
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'
[root@node1 chapter10]# cat psp-restricted.yaml 
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
spec:
  privileged: false
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - ALL
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    rule: 'MustRunAsNonRoot'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      - min: 1
        max: 65535
  readOnlyRootFilesystem: false

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注