kubernetes入门到入坟


准入控制器概述

  • API Server中的准入控制器同样以插件形式存在,它们会拦截所有已完成认证的,且与资源创建、更新和删除操作相关的请求,以强制实现控制器中定义的功能,包括执行对象的语义验证和设置缺失字段的默认值,具体功能取决于API Server的启用的插件。目前kubernetes内置了30多个准入控制器
  • 但是准入控制器的相关代码必须要由管理员编译进kube-apiserver中才能实现,实现方式缺乏灵活性。于是,kubernetes自1.7版本引入了Initializers和External Admissio Webhooks来尝试突破此限制,而自1.9版本起,External Admission Webhooks又被分为MutatingAdmissionWebhook和ValidatingAdmissionWebhook两种类型,分别用于在API种执行对象配置的”变异”和”验证”操作,前一种类型的控制器会”改动”和”验证”资源规范,而后一种类型仅”验证”资源规范是否合规
  • 在具体的代码实现上,一个准入控制器可以是验证型、变异型或兼具此两项功能。例如,LimiRanger准入控制器可以使用默认资源请求和限制(变异阶段)来扩展Pod,也能够校验有着显式资源需求定义的Pod是否超出LimitRange对象(验证阶段)的定义。而在具体运行时,准入控制器也会根据准入控制器类型分阶段运行,第一阶段串行运行各种变异型控制器,第二阶段则串行运行各种验证型控制器。如下图所示。在此过程中,任何控制器拒绝请求都将导致整个请求被即刻拒绝,并将错误信息返回给客户端

    1. ![](https://cdn.nlark.com/yuque/0/2021/png/2937658/1631615268417-64e6ceb6-10b3-4a6b-a947-49a38c87ae35.png#align=left&display=inline&height=261&margin=%5Bobject%20Object%5D&originHeight=261&originWidth=734&size=0&status=done&style=none&width=734)
  • 值得一提的是,kubernetes集群内置功能的某些方面实际上就是由准入控制器控制的,例如,删除名称空间并进入Terminating状态时,NamespaceLifecycle准入控制器将会阻止在该名称空间中创建任何新的资源对象。甚至于,必须启用准入控制器才能使用kubernetes集群的某些更高级的安全功能,例如在整个名称空间上强制实施安全配置基线的Pod安全策略等

  • API Server默认就会启用部分准入控制器,它支持通过--enable-admission-plugins选项指定要加载的准入控制器,使用--disable-admission-plugins选项指定要禁用的准入控制器
  • kubernetes正是依赖LimitRange资源和相应的LimitRange准入控制器、ResourceQuota资源和同名的准入控制器,以及PodSecurityPolicy资源和同名的准入控制器为多租户或多项目的集群环境提供了基础的安全策略框架

LimitRange

  • 尽管用户可以为容器或Pod资源指定资源需求及资源限制,但是这并非强制性要求,那些未明确定义资源限制的容器应用很可能会因程序Bug或真实需求而吞掉本地工作节点伤到所有可用计算资源。因此,妥当的做法是,使用LimitRange资源在每个名称空间中限制每个容器的最小及最大计算资源用量,以及为定义计算资源使用区间的容器设置默认的计算资源需求和计算资源限制。一旦在名称空间上定义了LimitRange对象,客户端创建或修改资源对象的操作必将受到LimitRange控制器的”验证”,任何违反LimitRange对象定义的资源最大用量的请求都将被直接拒绝
  • LimitRange支持在Pod级别与容器级别分别设置CPU和内存两种计算资源的可用范围,它们对应的范围资源限制类型为Pod和Container。一旦在名称空间上启用LimitRange,该名称空间中的Pod或容器的requests和limits的各项属性值必须在对应的可用资源范围内,否则将会被拒绝,这是验证型准入控制器的功能。以Pod级别的CPU资源为例,若某个LimitRange资源为其设定了[0.5,4]的区间,则相应名称空间下任何Pod资源的requests.cpu的属性值必须要大于等于500m,同时,limits.cpu的属性值也必须要小于等于4。而未指定request和limit属性的容器,将会从LimitRange资源上分别自动机场相应的默认设置,这是变异型准入控制器的功能。如下图所示

    1. ![](https://cdn.nlark.com/yuque/0/2021/png/2937658/1631615283502-4ffce6b6-abd5-4739-a4e9-ef6a962ddb73.png#align=left&display=inline&height=384&margin=%5Bobject%20Object%5D&originHeight=384&originWidth=572&size=0&status=done&style=none&width=572)
  • 另外,LimitRange也支持在PersistentVolumeClaim资源级别设置存储空间的范围限制,它用于限制相应名称空间中创建的PVC对象请求使用的存储空间不能超过指定的范围。未指定requests和limits属性的PVC规范,将在创建时自动继承LimitRange上配置的默认值

  • 下面的资源清单(limitrange-demo.yaml)分别为dev名称空间中的Pod、container和PersistentVolumeClaim资源定义了各自的资源范围,并为后两者指定了相应可用资源规范的limits和requests属性的默认值。其中用到的各配置属性中,default用于定义limits的默认值,defaultRequests定义requests的默认值,min定义最小资源量,而最大资源用量可以使用max给出固定值,也可以使用maxLimitRequestRatio设定最小用量的指定背熟,同时定义二者时,其意义要相符
  1. apiVersion: v1
  2. kind: LimitRange
  3. metadata:
  4. name: resources-limits
  5. namespace: dev
  6. spec:
  7. limits:
  8. - type: Pod
  9. max:
  10. cpu: "4"
  11. memory: "2Gi"
  12. min:
  13. cpu: "500m"
  14. memory: "16Mi"
  15. - type: Container
  16. max:
  17. cpu: "4"
  18. memory: "1Gi"
  19. min:
  20. cpu: "100m"
  21. memory: "4Mi"
  22. default:
  23. cpu: "2"
  24. memory: "512Mi"
  25. defaultRequest:
  26. cpu: "500m"
  27. memory: "64Mi"
  28. maxLimitRequestRatio:
  29. cpu: "4"
  30. - type: PersistentVolumeClaim
  31. max:
  32. storage: "20Gi"
  33. min:
  34. storage: "1Gi"
  35. default:
  36. storage: "1Gi"
  37. defaultRequest:
  38. storage: "1Gi"
  39. maxLimitRequestRatio:
  40. storage: "10"
  • LimitRange仅在Container资源类型上可以为CPU与内存设置default和defaultrequest,Pod资源类型不支持
  • LimitRange资源的详细描述会以非常直观,清晰的方式输出相关的资源限制及默认值的定义,将上面的配置清单中的LimitRange资源创建到集群上,然后使用describe查看
  1. [root@jenkins rbac]# kubectl apply -f limit-range.yaml
  2. limitrange/resources-limits created
  3. [root@jenkins rbac]#
  4. [root@jenkins rbac]# kubectl describe limitranges/resources-limits -n dev
  5. Name: resources-limits
  6. Namespace: dev
  7. Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
  8. ---- -------- --- --- --------------- ------------- -----------------------
  9. Pod cpu 500m 4 - - -
  10. Pod memory 16Mi 2Gi - - -
  11. Container cpu 100m 4 500m 2 4
  12. Container memory 4Mi 1Gi 64Mi 512Mi -
  13. PersistentVolumeClaim storage 1Gi 20Gi 1Gi 1Gi 10
  14. [root@jenkins rbac]#
  • 资源创建完成之后,我们可以在dev名称空间中创建Pod对象与PVC对象用于测试各限制的边界和默认值的效果进行多维度测试
  • 先创建一个仅包含一个容器且没有默认系统资源需求和限制的Pod对象
  1. [root@jenkins rbac]# kubectl run testpod1 --image="harbor.hub.aaa.com/youzi/prod-youzi:v0.0.9" -n dev
  2. pod/testpod1 created
  3. [root@jenkins rbac]#
  • Pod对象testpod1资源规范中被自动添加了CPU和内存资源的requests和limits属性,它的值来自于limitranges/resource-limits中的定义。如下面的命令结果所示
  1. [root@jenkins rbac]# kubectl get pods testpod1 -n dev -o yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. annotations:
  6. kubernetes.io/limit-ranger: 'LimitRanger plugin set: cpu, memory request for container
  7. testpod1; cpu, memory limit for container testpod1'
  8. ...
  9. ...
  10. ...
  11. spec:
  12. containers:
  13. - image: harbor.hub.aaa.com/youzi/prod-youzi:v0.0.9
  14. imagePullPolicy: IfNotPresent
  15. name: testpod1
  16. resources:
  17. limits:
  18. cpu: "2"
  19. memory: 512Mi
  20. requests:
  21. cpu: 500m
  22. memory: 64Mi
  23. ...
  24. ...
  • 若Pod对象设定的CPU或者内存的requests属性值小于LimitRange中相应资源的下限,或limits属性值大于设定的相应资源的上限,就会触发limitrange准入控制器拒绝相关的请求。例如下面的创建Pod的命令中,仅querests.memroy一个属性值为了limitrange/resource-limits中的定义,但请求一样会被拒绝
  1. [root@jenkins rbac]# kubectl run testpod2 --image="harbor.hub.shzhanmeng.com/youzi/prod-youzi:v0.0.9" -n dev \
  2. > --limits='cpu=2,memory=1Gi' \
  3. > --requests='cpu=1,memory=8Mi'
  4. Error from server (Forbidden): pods "testpod2" is forbidden: minimum memory usage per Pod is 16Mi, but request is 8388608
  5. [root@jenkins rbac]#
  • 类似的,在dev名称空间中创建的PVC对象的可用存储空间也将受到LimitRange资源中定义的限制,这里就不再给出具体的过程,感兴趣的朋友可以自行测试一下
  • 值得注意的是,Limitrange生效与名称空间级别,它需要定义在每个名称空间之上;另外,定义的限制仅对该资源创建后的Pod和PVC资源创建请求有效,对之前已然存在的资源无效;再者,不建议在生效于同一名称空间的多个LimitRaneg资源上,对同一个计算资源限制进行分别定义,以免产生歧义或者导致冲突

ResourceQuota

  • 尽管LimitRange资源能够在名称空间上限制单个容器、Pod或PVC相关的系统资源用量,但用户依然可以创建出无数的资源对象,进而侵占集群上所有的可用系统资源。于是kubernetes就提供了ResourceQuota资源用于定义名称空间级别的资源配额,从而在名称空间上限制聚合资源消耗的边界,它支持以资源类型来限制用户可在本地名称空间中创建的相关资源对象的数量,以及这些资源对象可消耗的计算资源总量等
  • 而同名的ResourceQuota准入控制器负责观察传入的请求,并确保它没有违反相应名称空间中ResourceQuota资源定义的任何约束。ResourceQuota准入控制器属于”验证”类型的控制器,它会跟踪使用情况以确保它不超过相应ResourceQuota对象中定义的系统资源限制,用户创建或更新资源的操作违反配额约束时将会被拒绝,API Server会响应一个403的HTTP状态码,并显示一条消息以提示违反的约束条件。不过,在名称空间上启用了CPU和内存等系统资源的配额后,用户创建Pod对象时必须制定资源需求或资源限制,否则,会触发ResourceQuota准入控制器拒绝执行相应的操作
  • ResourceQuota资源可限制名称空间中处于非终止状态的所有Pod对象的计算需求及计算资源限制总量
    • cpu或request.cpu:CPU资源相关的请求的总量限额
    • memroy或request.memroy:内存资源相关请求的总量限额
    • limits.cpu: CPU资源相关限制的总量限额
    • limits.memroy: 内存资源相关限制的总量限额
  • ResourceQuota也支持限制特定名称空间中可以使用的PVC数量和这些PVC资源的空间大小总量,以及特定名称空间中可在指定的storageclass上使用的PVC数量和这些PVC资源的总数。换句话说就是,它能够分别从名称空间中的全部PVC、隶属于特定存储类的PVC以及基于本地临时存储的PVC分别进行定义:
    • request.storage:所有PVC存储需求的总量限额
    • persistentvolumeclaims:可以创建的PVC总数限额
    • <storage-class-name>.storageclass.storage.k8s.io/requests.storage: 指定存储类上可使用的所有PVC存储需求的总量限额
    • <storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims: 指定存储类上可使用的PVC总数
    • <requests.ephemeral-storage>: 所有Pod可以使用的本地临时存储资源的请求总量
    • <limits.ephemeral-storage>: 所有Pod可用的本地临时存储资源的最大总量
  • 在1.9版本之前的kubernetes系统上,resourcequota仅支持在有限的几种资源集上设定对象计算配额,如pods、services和configmaps等。而从1.9版本起开始支持以count/<resource>.<group>的格式对所有资源类型对象的计算配额,例如count/deployments.appscount/deployments.extensionscount/services
  • 下面的资源清单(resourcequota-demo.yaml)在dev名称空间中定义了一个Resourcequota资源对象,它定义了计算资源与存储资源分别在requests和limits维度的限额,也定义了部署资源类型中的可用对象数量
  1. apiVersion: v1
  2. kind: ResourceQuota
  3. metadata:
  4. name: resourcequota-demo
  5. namespace: dev
  6. spec:
  7. hard:
  8. pods: 5
  9. count/services: 5
  10. count/configmaps: 5
  11. count/secrets: 5
  12. count/cronjobs.batch: 2
  13. count/deployments.apps: 2
  14. count/deployments.extensions: 2
  15. count/statefulsets: 2
  16. persistentvolumeclaims: 6
  17. requests.storage: "20Gi"
  18. glusterfs.storageclass.storage.k8s.io/requests.storage: "20Gi"
  19. glusterfs.storageclass.storage.k8s.io/persistentvolumeclaims: 6
  20. requests.cpu: 2
  21. requests.memory: "4Gi"
  22. limits.cpu: 4
  23. limits.memory: "8Gi"
  • 与limitrange不同的是,resourcequota会计入指定名称空间内,先前的资源对象对系统和资源对象的限额占用情况,因此将resourcequota-demo创建到集群上之后,dev空间中现有的资源会立即分区限额内的一部分可用空间,这在resourcequota资源的详细描述中会有直观展示
  1. [root@jenkins rbac]# kubectl apply -f resourcequota-demo.yaml
  2. resourcequota/resourcequota-demo created
  3. [root@jenkins rbac]#
  4. [root@jenkins rbac]# kubectl describe resourcequota/resourcequota-demo -n dev
  5. Name: resourcequota-demo
  6. Namespace: dev
  7. Resource Used Hard
  8. -------- ---- ----
  9. count/configmaps 0 5
  10. count/cronjobs.batch 0 2
  11. count/deployments.apps 0 2
  12. count/deployments.extensions 0 2
  13. count/secrets 1 5
  14. count/services 0 5
  15. count/statefulsets 0 2
  16. glusterfs.storageclass.storage.k8s.io/persistentvolumeclaims 0 6
  17. glusterfs.storageclass.storage.k8s.io/requests.storage 0 20Gi
  18. limits.cpu 2 4
  19. limits.memory 512Mi 8Gi
  20. persistentvolumeclaims 0 6
  21. pods 1 5
  22. requests.cpu 500m 2
  23. requests.memory 64Mi 4Gi
  24. requests.storage 0 20Gi
  25. [root@jenkins rbac]#
  • 通过上面第二条命令的结果可以很明显的看到各个资源的最高限制配额和已经使用的配额。随后,我们可以在dev名称空间中创建Pod时,指定Pod
    的计算资源用来验证resourcequota限制范围是否有效
  1. [root@jenkins rbac]# kubectl run testpod2 --image="harbor.hub.shzhanmeng.com/youzi/prod-youzi:v0.0.9" -n dev --limits='cpu=2,memory=1Gi' --requests='cpu=2,memory=1Gi'
  2. Error from server (Forbidden): pods "testpod2" is forbidden: exceeded quota: resourcequota-demo, requested: requests.cpu=2, used: requests.cpu=500m, limited: requests.cpu=2
  3. [root@jenkins rbac]#
  • 通过上面的命令结果可以看出,由于CPU超限,所以导致Pod创建失败
  • 每个ResourceQuota资源对象上还支持定义一组作用域,用于定义资源上的配额仅生效于这组作用域交集范围的对象。目前适用范围包括TerminatingNotTerminatingBestEffortNotBestEffort
    • Terminating:匹配.spec.activeDeadlineSeconds的属性值大于等0的所有Pod对象
    • NotTerminating:匹配.spec.activeDeadlineSeconds的属性值为空的所有pod对象
    • BestEffort:匹配所有位于BestEffort Qos类别的Pod对象
    • NotBestEffort:匹配所有非BestEffort Qos类别的Pod对象
  • 另外,kubernetes自1.8版本起支持管理员设置不同的优先级类别(PriorityClass)创建Pod对象,而且自1.11版本起还吃对每个Priority对象分别设定资源限额。于是,管理员还可以在ResourceQuota资源上使用scopeSelector字段定义其生效的作用域,它支持基于Pod对象的优先级来控制Pod对系统资源的消耗

PodSecurityPolicy

  • PodSecurityPolicy(简称PSP)是集群级别的资源类型,用于控制用在配置Pod资源的期望状态时可以设定的特权类的属性,如是否可以使用特权容器、根命名空间和主机文件系统,以及可使用的主机网络和端口、卷类型和Linux Capabilities等。不过,PSP对象定义的策略本身并不会直接发生作用,它们需要经由PodSecurityPolicy准入控制器检查并强制生效
  • 我们知道,Pod和容器规范中允许用户使用securityContext字段定义安全相关的配置,但允许用户随意以特权模式运行容器或者使用任意的Linux内核能力等,显然存在着难以预料的安全风险。API Server提供了PodSecurityPolicy资源让管理员在集群全局定义或限定用户在Pod和容器上启用及禁用的安全配置,例如,是否可以使用特权容器、根命名空间和主机文件系统,以及可使用的主机网络和端口、卷类型和Linux Capabilities等。因此,本质上来说,PSP资源就是集群全局范围内定义的Pod资源可用的安全上下文策略。同名的PodSecurityPolicy准入控制器负责观察集群范围内的Pod资源的运行属性,并确保它没有违反PodSecurityPolicy资源定义的约束条件
  • 默认情况下,PSP准入控制器是未启用的状态,原因是在于未创建任何PSP对象的情况下启用此准入控制器将阻止集群中创建任何Pod对象,PSP准入控制器会根据定义的PSP资源中的安全策略判定允许哪种Pod资源的创建操作,若无任何可用的安全策略,将会组织所有的Pod资源创建。不过,PSP资源的API接口policy/v1beat1/podsecuritypolicy独立于PSP准入控制器,因此,管理员可用先定义好必要的Pod安全策略,再设置kube-apiserver启用PSP准入控制器。如果Pod的安全策略配置不当的话,将会产生难以预料的副作用,因此轻确保所添加的任何PSP对象都经过了充分的测试
  • PodSecurityPolicy是标准的API资源类型,它隶属于policy群组,在spec字段中嵌套多种安全规则来定义期望的目标,资源规范及简要的使用说明如下:
  1. apiVersion: policy/v1beta1
  2. kind: PodSecurityPolicy
  3. metadata:
  4. name: <string> #资源名称
  5. spec:
  6. allowPrivilegeEscalation: <bollean> #是否允许权限升级
  7. allowedCSIDrivers: <[]object> #内联CSI驱动程序列表,必须在Pod规范显式定义
  8. allowedCapabilities: <[]string> #允许使用的内核能力列表,"*"表示所有
  9. allowedFlexVolumes: <[]obejct> #允许使用的Flexvolume列表,空值表示所有
  10. allowedHostPaths: <[]object> #允许使用的主机路径列表,空值表示所有
  11. allowedProcMountTypes: <[]string> #允许使用的ProcMountType列表,控制表示默认
  12. allowedUnsafeSysctls: <[]string> #允许使用的非安全sysctl参数,控制表示不允许
  13. defaultAddCapabilities: <[]string> #默认添加到Pod对象的内核能力,可被drop
  14. defaultAllowPrivilegeEscalation: <bollean> #是否默认允许内核权限升级
  15. forbiddenSysctls: <[]string> #禁止使用的sysctl参数,空值表示不禁用
  16. fsGroup: <Object> #允许在SecurityContext中使用的fsgroup,必选字段
  17. rule: <string> #允许使用的FSGroup规则,支持RunAsAny和MustRunAs
  18. ranges: <[]object> #允许使用的组ID范围,需要与MustRunAs规则一同使用
  19. max: <integer> #最大组ID号
  20. min: <integer> #最小组ID号
  21. hostIPC: <boolean> #是否允许Pod使用HostIPC
  22. hostNetwork: <boolean> #是否允许Pod使用hostNetwork
  23. hostPID: <boolean> #是否允许POD使用hostpid
  24. hostPorts: <[]object> #允许Pod使用的主机端口暴漏其服务的范围
  25. max: <integer> #最大端口号,必选字段
  26. min: <integer> #最小端口号,必选字段
  27. privileged: <boolean> #是否允许允许特权Pod
  28. readOnlyRootFilesystem: <boolean> #是否设定容器的根文件系统为"只读"
  29. requiredDropCapabilities: <[]string> #必须要禁用的内核能力列表
  30. runAsGroup: <Object> #允许Pod在runAsGroup中使用的列表值,未定义表示不限制
  31. runAsUser: <Object> #允许Pod在runAsUser中使用的列表值,必选字段
  32. ranges: <[]Object> #允许使用的组ID范围,需要根MustRunAs规则一同使用
  33. max: <integer> #最大组ID号
  34. min: <integer> #最小组ID号
  35. rule: <string> #支持RunAsAny、MustRunAs和MustRunAsNonRoot
  36. runtimeClass: <Object> #允许Pod使用的运行类,未定义表示不限制
  37. allowedRuntimeClassNames: <[]string> #允许使用的runtimeClass列表,"*"表示所有
  38. defaultRuntimeClassName: <string> #默认使用的runtimeClass
  39. seLinux: <Object> #允许Pod使用的selinux标签,必选字段
  40. rule: <string> #MustRunAs表示使用selinuxOptions定义的值,RunAsAny表示可以使用任意值
  41. seLinuxOptions: <Object> #自定义selinux选项对象,与MustRunAs写作生效
  42. supplementalGroups: <Object> #允许Pod在SecurityContext中附加组,必选字段
  43. volumes: <[]string> #允许Pod使用的存储卷列表插件,空表示禁用,"*"表示所有
  • 启用PSP准入控制器后要部署任何Pod对象,相关的UserAccount及ServiceAccount必须全部获得了恰当的Pod安全策略授权。以常规用户的身份直接创建Pod对象时,PSP准入控制器将根据该账户被授权时使用的Pod安全策略验证其凭据,若无任何安全策略约束该Pod对象的安全性,则创建操作将会被拒绝。基于控制器(例如Deployment)创建Pod对象时,PSP准入控制器会根据Pod对象的ServiceAccount被授权使用的Pod安全策略验证其凭据,若不存在支持该Pod对象的安全性要求的安全策略,则Pod控制器资源自身能成功创建,但Pod对象不能
  • 事实上,即便是在启用了PSP准入控制器的情况下创建的PSP对象也依然不会发生效用,而是管理员借助授权插件(如RBAC)将”use”操作权限授权给特定的role或clusterrole,再为相关的UseAccount或ServiceAccount分配这些角色才能让PSP策略真正生效
  • 该资源的使用方式,这里就不演示了,PodSecurityPolicy如果不熟悉使用方法的话,最好还是慎用,因为之前有提到过,如果稍微操作不慎,可能会产生一定的副作用,严重的话可能导致资源创建失败等等