Red Hat Training

A Red Hat training course is available for OpenShift Container Platform

クラスター管理

OpenShift Container Platform 3.11

OpenShift Container Platform 3.11 集群管理

摘要

OpenShift 集群管理主题包含用于管理 OpenShift 集群的日常任务和其他高级配置主题。

第 1 章 概述

 
这些群集管理主题涵盖管理 OpenShift Container Platform 集群的日常任务和其他高级配置主题。

第 2 章 管理节点

2.1. 概述

您可以使用 CLI 管理实例中的节点

在执行节点管理操作时,CLI 与代表实际节点主机的节点对象交互主控机使用节点对象的信息通过 健康检查来验证节点

2.2. 列出节点

列出 master 已知的所有节点:

$ oc get nodes

输出示例

NAME                   STATUS    ROLES     AGE       VERSION
master.example.com     Ready     master    7h        v1.9.1+a0ce1bc657
node1.example.com      Ready     compute   7h        v1.9.1+a0ce1bc657
node2.example.com      Ready     compute   7h        v1.9.1+a0ce1bc657

使用节点信息,列出包含项目 pod 部署信息的所有节点:

$ oc get nodes -o wide

输出示例

NAME                           STATUS    ROLES     AGE       VERSION           EXTERNAL-IP      OS-IMAGE                                      KERNEL-VERSION          CONTAINER-RUNTIME
ip-172-18-0-39.ec2.internal    Ready     infra     1d        v1.10.0+b81c8f8   54.172.185.130   Red Hat Enterprise Linux Server 7.5 (Maipo)   3.10.0-862.el7.x86_64   docker://1.13.1
ip-172-18-10-95.ec2.internal   Ready     master    1d        v1.10.0+b81c8f8   54.88.22.81      Red Hat Enterprise Linux Server 7.5 (Maipo)   3.10.0-862.el7.x86_64   docker://1.13.1
ip-172-18-8-35.ec2.internal    Ready     compute   1d        v1.10.0+b81c8f8   34.230.50.57     Red Hat Enterprise Linux Server 7.5 (Maipo)   3.10.0-862.el7.x86_64   docker://1.13.1

要只列出单个节点的信息,使用完整节点名称替换 <node>

$ oc get node <node>

这些命令输出中的 STATUS 列可以显示具有以下条件的节点:

表 2.1. 节点状况

状况描述

Ready

节点通过返回 StatusOK 从 master 传递健康检查。

NotReady

节点没有从 master 传递执行的健康检查。

SchedulingDisabled

无法调度 Pod 放置到节点上

注意

如果 CLI 无法找到任何节点状况,STATUS 列也可以显示节点的 Unknown

要获取有关特定节点的详细信息,包括造成当前状况的原因:

$ oc describe node <node>

例如:

$ oc describe node node1.example.com

输出示例

Name:               node1.example.com 1
Roles:              compute 2
Labels:             beta.kubernetes.io/arch=amd64 3
                    beta.kubernetes.io/os=linux
                    kubernetes.io/hostname=m01.example.com
                    node-role.kubernetes.io/compute=true
                    node-role.kubernetes.io/infra=true
                    node-role.kubernetes.io/master=true
                    zone=default
Annotations:        volumes.kubernetes.io/controller-managed-attach-detach=true  4
CreationTimestamp:  Thu, 24 May 2018 11:46:56 -0400
Taints:             <none>   5
Unschedulable:      false
Conditions:                  6
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  OutOfDisk        False   Tue, 17 Jul 2018 11:47:30 -0400   Tue, 10 Jul 2018 15:45:16 -0400   KubeletHasSufficientDisk     kubelet has sufficient disk space available
  MemoryPressure   False   Tue, 17 Jul 2018 11:47:30 -0400   Tue, 10 Jul 2018 15:45:16 -0400   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Tue, 17 Jul 2018 11:47:30 -0400   Tue, 10 Jul 2018 16:03:54 -0400   KubeletHasNoDiskPressure     kubelet has no disk pressure
  Ready            True    Tue, 17 Jul 2018 11:47:30 -0400   Mon, 16 Jul 2018 15:10:25 -0400   KubeletReady                 kubelet is posting ready status
  PIDPressure      False   Tue, 17 Jul 2018 11:47:30 -0400   Thu, 05 Jul 2018 10:06:51 -0400   KubeletHasSufficientPID      kubelet has sufficient PID available
Addresses:                   7
  InternalIP:  192.168.122.248
  Hostname:    node1.example.com
Capacity:                    8
 cpu:            2
 hugepages-2Mi:  0
 memory:         8010336Ki
 pods:           40
Allocatable:
 cpu:            2
 hugepages-2Mi:  0
 memory:         7907936Ki
 pods:           40
System Info:                 9
 Machine ID:                         b3adb9acbc49fc1f9a7d6
 System UUID:                        B3ADB9A-B0CB-C49FC1F9A7D6
 Boot ID:                            9359d15aec9-81a20aef5876
 Kernel Version:                     3.10.0-693.21.1.el7.x86_64
 OS Image:                           OpenShift Enterprise
 Operating System:                   linux
 Architecture:                       amd64
 Container Runtime Version:          docker://1.13.1
 Kubelet Version:                    v1.10.0+b81c8f8
 Kube-Proxy Version:                 v1.10.0+b81c8f8
ExternalID:                          node1.example.com
Non-terminated Pods:                 (14 in total)       10
  Namespace                          Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------                          ----                                  ------------  ----------  ---------------  -------------
  default                            docker-registry-2-w252l               100m (5%)     0 (0%)      256Mi (3%)       0 (0%)
  default                            registry-console-2-dpnc9              0 (0%)        0 (0%)      0 (0%)           0 (0%)
  default                            router-2-5snb2                        100m (5%)     0 (0%)      256Mi (3%)       0 (0%)
  kube-service-catalog               apiserver-jh6gt                       0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-service-catalog               controller-manager-z4t5j              0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                        master-api-m01.example.com            0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                        master-controllers-m01.example.com    0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                        master-etcd-m01.example.com           0 (0%)        0 (0%)      0 (0%)           0 (0%)
  openshift-ansible-service-broker   asb-1-hnn5t                           0 (0%)        0 (0%)      0 (0%)           0 (0%)
  openshift-node                     sync-dvhvs                            0 (0%)        0 (0%)      0 (0%)           0 (0%)
  openshift-sdn                      ovs-zjs5k                             100m (5%)     200m (10%)  300Mi (3%)       400Mi (5%)
  openshift-sdn                      sdn-zr4cb                             100m (5%)     0 (0%)      200Mi (2%)       0 (0%)
  openshift-template-service-broker  apiserver-s9n7t                       0 (0%)        0 (0%)      0 (0%)           0 (0%)
  openshift-web-console              webconsole-785689b664-q7s9j           100m (5%)     0 (0%)      100Mi (1%)       0 (0%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ------------  ----------  ---------------  -------------
  500m (25%)    200m (10%)  1112Mi (14%)     400Mi (5%)
Events:                                                  11
  Type     Reason                   Age                From                      Message
  ----     ------                   ----               ----                      -------
  Normal   NodeHasSufficientPID     6d (x5 over 6d)    kubelet, m01.example.com  Node m01.example.com status is now: NodeHasSufficientPID
  Normal   NodeAllocatableEnforced  6d                 kubelet, m01.example.com  Updated Node Allocatable limit across pods
  Normal   NodeHasSufficientMemory  6d (x6 over 6d)    kubelet, m01.example.com  Node m01.example.com status is now: NodeHasSufficientMemory
  Normal   NodeHasNoDiskPressure    6d (x6 over 6d)    kubelet, m01.example.com  Node m01.example.com status is now: NodeHasNoDiskPressure
  Normal   NodeHasSufficientDisk    6d (x6 over 6d)    kubelet, m01.example.com  Node m01.example.com status is now: NodeHasSufficientDisk
  Normal   NodeHasSufficientPID     6d                 kubelet, m01.example.com  Node m01.example.com status is now: NodeHasSufficientPID
  Normal   Starting                 6d                 kubelet, m01.example.com  Starting kubelet.
 ...

1
节点的名称。
2
节点的角色,可以是 mastercomputeinfra
3
4
应用到节点的注解。
5
6
7
节点的 IP 地址和主机名。
8
9
节点主机的相关信息。
10
节点上的 pod。
11

2.3. 查看节点

您可以显示节点的用量统计,这些统计信息为容器提供了运行时环境。这些用量统计包括 CPU、内存和存储的消耗。

查看用量统计:

$ oc adm top nodes

输出示例

NAME       CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
node-1     297m         29%       4263Mi          55%
node-0     55m          5%        1201Mi          15%
infra-1    85m          8%        1319Mi          17%
infra-0    182m         18%       2524Mi          32%
master-0   178m         8%        2584Mi          16%

查看具有标签的节点的用量统计信息:

$ oc adm top node --selector=''

您必须选择过滤所基于的选择器(标签查询)。支持 ===!=

注意

您必须有 cluster-reader 权限才能查看用量统计。

注意

必须安装 metrics-server 才能查看用量统计。请参阅使用 Horizontal Pod Autoscalers 的要求

2.4. 添加主机

您可以通过运行scaleup.yml playbook 添加新主机到集群。此 playbook 查询 master,为新主机生成和发布新证书,然后仅在新主机上运行配置 playbook。在运行scaleup.yml playbook 之前,请完成所有必备的主机准备步骤

重要

scaleup.yml playbook 仅配置新主机。它不会更新 master服务的 NO_PROXY,也不会重启 master 服务。

您必须有一个现有的清单文件,如/etc/ansible/hosts ,它代表当前集群配置,才能运行scaleup.yml playbook。如果您之前使用 atomic-openshift-installer 命令来运行安装,您可以检查~/.config/openshift/hosts 中安装程序生成的最后一个清单文件,并将该文件用作清单文件。您可以根据需要修改此文件。然后,当运行 ansible-playbook 时,您必须使用 -i 指定文件位置。

流程

  1. 通过更新 openshift-ansible 软件包来确保您有最新的 playbook:

    # yum update openshift-ansible
  2. 编辑/etc/ansible/hosts 文件,并将 new_<host_type> 添加到 [OSEv3:children] 部分。例如,要添加新节点主机,请添加 new_nodes

    [OSEv3:children]
    masters
    nodes
    new_nodes

    若要添加新的 master 主机,可添加 new_masters

  3. 创建一个 [new_<host_type>] 部分来为新主机指定主机信息。将此部分格式化为现有部分,如下例所示:

    [nodes]
    master[1:3].example.com
    node1.example.com openshift_node_group_name='node-config-compute'
    node2.example.com openshift_node_group_name='node-config-compute'
    infra-node1.example.com openshift_node_group_name='node-config-infra'
    infra-node2.example.com openshift_node_group_name='node-config-infra'
    
    [new_nodes]
    node3.example.com openshift_node_group_name='node-config-infra'

    如需了解更多选项,请参阅配置主机变量

    在添加新 master 时,将主机添加到 [new_masters] 部分和 [new_nodes] 部分,以确保新 master 主机是 OpenShift SDN 的一部分:

    [masters]
    master[1:2].example.com
    
    [new_masters]
    master3.example.com
    
    [nodes]
    master[1:2].example.com
    node1.example.com openshift_node_group_name='node-config-compute'
    node2.example.com openshift_node_group_name='node-config-compute'
    infra-node1.example.com openshift_node_group_name='node-config-infra'
    infra-node2.example.com openshift_node_group_name='node-config-infra'
    
    [new_nodes]
    master3.example.com
    重要

    如果您使用 node-role.kubernetes.io/infra=true 标签标记 master 主机,且没有其他专用基础架构节点,则必须通过将 openshift_schedulable=true 添加到条目来明确将该主机标记为可以调度。否则,registry 和路由器 Pod 将无法放置到任何节点。

  4. 更改到 playbook 目录,再运行openshift_node_group.yml playbook。如果您的清单文件位于/etc/ansible/hosts 默认以外的位置,请使用 -i 选项指定位置:

    $ cd /usr/share/ansible/openshift-ansible
    $ ansible-playbook [-i /path/to/file] \
      playbooks/openshift-master/openshift_node_group.yml

    这会为新节点组创建 ConfigMap,并最终为主机上的节点配置文件。

    注意

    运行 openshift_node_group.yaml playbook 只会更新新节点。无法运行它来更新集群中的现有节点。

  5. 运行scaleup.yml playbook。如果您的清单文件位于默认/etc/ansible/hosts 以外的位置,请使用 -i 选项指定位置。

    • 对于额外的节点:

      $ ansible-playbook [-i /path/to/file] \
          playbooks/openshift-node/scaleup.yml
    • 对于额外的 master:

      $ ansible-playbook [-i /path/to/file] \
          playbooks/openshift-master/scaleup.yml
  6. 如果您在集群中部署了 EFK 堆栈,请将节点标签设置为 logging-infra-fluentd=true

    # oc label node/new-node.example.com logging-infra-fluentd=true
  7. 在 playbook 运行后,验证安装
  8. 将您在 [new_<host_type>] 部分定义的任何主机移动到它们的相应部分。通过移动这些主机,后续的 playbook 运行使用此清单文件正确处理节点。您可以保留空 [new_<host_type>] 部分。例如,添加新节点时:

    [nodes]
    master[1:3].example.com
    node1.example.com openshift_node_group_name='node-config-compute'
    node2.example.com openshift_node_group_name='node-config-compute'
    node3.example.com openshift_node_group_name='node-config-compute'
    infra-node1.example.com openshift_node_group_name='node-config-infra'
    infra-node2.example.com openshift_node_group_name='node-config-infra'
    
    [new_nodes]

2.5. 删除节点

当您使用 CLI 删除节点时,节点对象会从 Kubernetes 中删除,但节点本身存在的 pod 不会被删除。任何未由复制控制器支持的裸机 pod 都无法被 OpenShift Container Platform 访问,由复制控制器支持的 pod 将被重新调度到其他可用的节点,并且需要手动删除本地清单 pod

从 OpenShift Container Platform 集群中删除节点:

  1. 从您要删除的节点中撤离 pod
  2. 删除节点对象:

    $ oc delete node <node>
  3. 检查节点是否已从节点列表中移除:

    $ oc get nodes

    现在应该只为处于 Ready 状态的剩余节点调度 Pod。

  4. 如果要从节点主机中卸载所有 OpenShift Container Platform 内容,包括所有 pod 和容器,请继续卸载节点 ,并使用uninstall.yml playbook 按照以下步骤进行操作。该流程假定使用 Ansible 对集群安装过程的一般了解

2.6. 更新节点上的标签

在节点上添加或更新标签

$ oc label node <node> <key_1>=<value_1> ... <key_n>=<value_n>

查看更详细的用法:

$ oc label -h

2.7. 列出节点上的 pod

列出一个或多个节点上的所有或选定 pod:

$ oc adm manage-node <node1> <node2> \
    --list-pods [--pod-selector=<pod_selector>] [-o json|yaml]

列出选定节点上的所有或选定 pod:

$ oc adm manage-node --selector=<node_selector> \
    --list-pods [--pod-selector=<pod_selector>] [-o json|yaml]

2.8. 将节点标记为不可调度或可以调度

默认情况下,带有 Ready 状态的健康节点被标记为可以调度,即允许在该节点上放置新的 pod。如果手动将节点标记为不可调度,则会阻止在该节点上调度任何新的 pod。节点上的现有 pod 不受影响。

将一个或多个节点标记为不可调度:

$ oc adm manage-node <node1> <node2> --schedulable=false

例如:

$ oc adm manage-node node1.example.com --schedulable=false

输出示例

NAME                 LABELS                                        STATUS
node1.example.com    kubernetes.io/hostname=node1.example.com      Ready,SchedulingDisabled

将当前不可调度的一个或多个节点标记为可以调度:

$ oc adm manage-node <node1> <node2> --schedulable

另外,您可以使用 --selector=<node_selector> 选项将所选节点标记为可以调度或不可调度,而不是指定具体的节点名称(如 <node1> <node2>)。

2.9. 撤离节点上的 pod

通过撤离 pod,您可以迁移给定节点中的所有或选定 pod。节点必须首先标记为不可调度,才能执行 pod 撤离。

只有由复制控制器支持的 pod 可以撤离;复制控制器在其他节点上创建新 pod,并从指定节点移除现有的 pod。裸机 pod(即不由复制控制器支持的 pod)默认情况下不受影响。您可以通过指定 pod 选择器来撤离一小部分 pod。pod 选择器基于标签,因此具有指定标签的所有 pod 将被撤离。

撤离节点上的所有或选定 pod:

$ oc adm drain <node> [--pod-selector=<pod_selector>]

您可以使用 --force 选项强制删除裸机 pod。当设置为 true 时,即使存在不由复制控制器、ReplicaSet、作业、daemonset 或 StatefulSet 管理的 pod,也会继续执行删除:

$ oc adm drain <node> --force=true

您可以使用 --grace-period 设置一个期限(以秒为单位),以便每个 pod 正常终止。如果为负,则使用 pod 中指定的默认值:

$ oc adm drain <node> --grace-period=-1

您可以使用 --ignore-daemonsets 并将其设置为 true 来忽略 daemonset 管理的 pod:

$ oc adm drain <node> --ignore-daemonsets=true

您可以使用 --timeout 设置放弃前等待的时长。0 的值设定无限时间长度:

$ oc adm drain <node> --timeout=5s

您可以使用 --delete-local-data 并将其设置为 true 以继续删除,即使有使用 emptyDir 的 pod(节点排空时会被删除的本地数据):

$ oc adm drain <node> --delete-local-data=true

要列出将要迁移的对象而不实际执行撤离,请使用 --dry-run 选项并将其设置为 true

$ oc adm drain <node> --dry-run=true

您可以使用 --selector=<node_selector> 选项撤离与选择器匹配的节点上的 pod,而不指定具体的节点名称。

2.10. 重新引导节点

要重新引导节点而不导致平台上运行的应用的中断,务必要先撤离 pod。对于由路由层提供高可用性的 pod,不需要执行其他操作。对于需要存储的其他 pod(通常是数据库),务必要确保它们能够在一个 pod 临时下线时仍然保持运作。虽然为有状态 pod 实施弹性对每个应用程序都是不同的,但在所有情况下,务必要将调度程序配置为使用节点反关联性,以确保 pod 正确分布在可用节点中。

另一个挑战是如何处理运行关键基础架构的节点,比如路由器或 registry。相同的节点撤离过程同样适用于这类节点,但必须要了解某些边缘情况。

2.10.1. 基础架构节点

基础架构节点是标记为运行 OpenShift Container Platform 环境组成部分的节点。目前,管理节点重新引导的最简单方法是确保至少有三个节点可用于运行基础架构。以下情景演示了一个常见的错误,只有两个节点可用时,它可能会导致 OpenShift Container Platform 上运行的应用程序发生服务中断。

  • 节点 A 标记为不可调度,所有 pod 都被撤离。
  • 该节点上运行的 registry pod 现在重新部署到节点 B 上。也就是说,节点 B 现在同时运行两个 registry pod。
  • 节点 B 现在标记为不可调度,并且被撤离。
  • 在节点 B 上公开两个 pod 端点的服务在短时间内失去所有端点,直到它们被重新部署到节点 A。

使用三个基础架构节点的同一流程不会导致服务中断。但由于 pod 调度的原因,最后一个节点在撤离并返回到轮转后,会保留为运行零个 registry。其他两个节点将分别运行两个 registry 和一个 registry。最好的解决方法是借助 pod 反关联性。Kubernetes 中有一个 alpha 功能,现在可用于测试,但尚不支持用于生产环境工作负载。

2.10.2. 使用 pod 反关联性

Pod 反关联性与 节点反关联性稍有不同。如果没有其他适当的位置来部署 pod,则可以违反节点反关联性。Pod 反关联性可以设置为必要的或偏好的。

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-antiaffinity
spec:
  affinity:
    podAntiAffinity: 1
      preferredDuringSchedulingIgnoredDuringExecution: 2
      - weight: 100 3
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: docker-registry 4
              operator: In 5
              values:
              - default
          topologyKey: kubernetes.io/hostname
1
用于配置 pod 反关联性的小节。
2
定义偏好规则。
3
为偏好规则指定权重。优先选择权重最高的节点。
4
描述用来决定何时应用反关联性规则的 pod 标签。指定标签的键和值。
5
运算符表示现有 pod 上的标签和新 pod 规格中 matchExpression 参数的值集合之间的关系。可以是 InNotInExistsDoesNotExist

本例假定容器镜像 registry pod 具有标签 docker-registry=default。Pod 反关联性可以使用任何 Kubernetes 匹配表达式。

最后一步是在/etc/origin/master/scheduler.json 中启用 MatchInterPodAffinity 调度程序 predicate。在这个版本中,如果只有两个基础架构节点可用,且一个节点被重新引导,容器镜像 registry Pod 将无法在另一个节点上运行。oc get pods 将 pod 报告为 unready,直到有合适的节点可用。一旦某个节点可用,并且所有 pod 恢复到就绪状态,下一个节点就可以重启。

2.10.3. 处理运行路由器的节点

在大多数情况下,运行 OpenShift Container Platform 路由器的 pod 将公开主机端口。PodFitsPorts 调度程序 predicate 确保没有使用相同端口的路由器 pod 在同一节点上运行,并实现 pod 反关联性。如果路由器依赖于 IP 故障转移来实现高可用性,则不需要任何其他操作。如果路由器 pod 依赖 AWS Elastic Load Balancing 等外部服务来实现高可用性,则由该服务负责响应路由器 pod 重启。

在个别情况下,路由器 pod 可能没有配置主机端口。在这些情况下,务必要遵循推荐的基础架构节点的重启流程

2.11. 修改节点

在安装过程中,OpenShift Container Platform 为每种节点组在 openshift-node 项目中创建一个 configmap:

  • node-config-master
  • node-config-infra
  • node-config-compute
  • node-config-all-in-one
  • node-config-master-infra

若要对现有节点进行配置更改,请编辑相应的配置映射。各个节点上的同步 pod 监视配置映射的变化。在安装过程中,使用同步守护进程创建同步 pod,并将节点配置参数所在的 /etc/origin/node/node-config.yaml 文件添加到每个节点。当同步 pod 检测到配置映射更改时,它会在该节点组的所有节点上更新node-config.yaml,并在适当的节点上重启 atomic-openshift-node.service

$ oc get cm -n openshift-node

输出示例

NAME                       DATA      AGE
node-config-all-in-one     1         1d
node-config-compute        1         1d
node-config-infra          1         1d
node-config-master         1         1d
node-config-master-infra   1         1d

node-config-compute 组的配置映射示例

apiVersion: v1
authConfig:      1
  authenticationCacheSize: 1000
  authenticationCacheTTL: 5m
  authorizationCacheSize: 1000
  authorizationCacheTTL: 5m
dnsBindAddress: 127.0.0.1:53
dnsDomain: cluster.local
dnsIP: 0.0.0.0               2
dnsNameservers: null
dnsRecursiveResolvConf: /etc/origin/node/resolv.conf
dockerConfig:
  dockerShimRootDirectory: /var/lib/dockershim
  dockerShimSocket: /var/run/dockershim.sock
  execHandlerName: native
enableUnidling: true
imageConfig:
  format: registry.reg-aws.openshift.com/openshift3/ose-${component}:${version}
  latest: false
iptablesSyncPeriod: 30s
kind: NodeConfig
kubeletArguments: 3
  bootstrap-kubeconfig:
  - /etc/origin/node/bootstrap.kubeconfig
  cert-dir:
  - /etc/origin/node/certificates
  cloud-config:
  - /etc/origin/cloudprovider/aws.conf
  cloud-provider:
  - aws
  enable-controller-attach-detach:
  - 'true'
  feature-gates:
  - RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true
  node-labels:
  - node-role.kubernetes.io/compute=true
  pod-manifest-path:
  - /etc/origin/node/pods  4
  rotate-certificates:
  - 'true'
masterClientConnectionOverrides:
  acceptContentTypes: application/vnd.kubernetes.protobuf,application/json
  burst: 40
  contentType: application/vnd.kubernetes.protobuf
  qps: 20
masterKubeConfig: node.kubeconfig
networkConfig:   5
  mtu: 8951
  networkPluginName: redhat/openshift-ovs-subnet  6
servingInfo:                   7
  bindAddress: 0.0.0.0:10250
  bindNetwork: tcp4
  clientCA: client-ca.crt 8
volumeConfig:
  localQuota:
    perFSGroup: null
volumeDirectory: /var/lib/origin/openshift.local.volumes

1
身份验证和授权配置选项.
2
附加到 pod /etc/resolv.conf 的 IP 地址。
3
直接传递给与 Kubelet 命令行参数匹配的 Kubelet 的键值对
4
pod 清单文件或目录的路径。目录必须包含一个或多个清单文件。OpenShift 容器平台使用清单文件在节点上创建 pod。
5
节点上的 pod 网络设置。
6
软件定义型网络(SDN)插件.为 ovs-subnet 插件设置 redhat/openshift-ovs-subnet ;ovs-multitenant 插件设置为 redhat/openshift-ovs-multitenant ;对于 ovs-networkpolicy 插件,设置为 redhat/openshift-ovs-networkpolicy
7
节点的证书信息。
8
可选: PEM 编码的证书捆绑包。如果设置,则必须根据指定文件中的证书颁发机构显示并验证有效的客户端证书,然后才能检查请求标头中的用户名。
注意

不要手动修改/etc/origin/node/node-config.yaml 文件。

2.11.1. 配置节点资源

您可以通过在节点配置映射中添加 kubelet 参数来配置节点资源。

  1. 编辑配置映射:

    $ oc edit cm node-config-compute -n openshift-node
  2. 添加 kubeletArguments 部分并指定您的选项:

    kubeletArguments:
      max-pods: 1
        - "40"
      resolv-conf: 2
        - "/etc/resolv.conf"
      image-gc-high-threshold: 3
        - "90"
      image-gc-low-threshold: 4
        - "80"
      kube-api-qps: 5
        - "20"
      kube-api-burst: 6
        - "40"
    1
    2
    用作容器 DNS 解析配置基础的解析器配置文件。
    3
    始终运行镜像垃圾回收之后的磁盘用量百分比。默认:90%
    4
    从不运行镜像垃圾回收前的磁盘用量百分比。垃圾收集的磁盘使用最低,默认:80%
    5
    与 Kubernetes API 服务器交互时要使用的每秒查询(QPS)。
    6
    与 Kubernetes API 服务器对话时要使用的突发。

    查看所有可用 kubelet 选项:

    $ hyperkube kubelet -h

2.11.2. 每个节点设置最大 pod

/etc/origin/node/node-config.yaml 文件中,两个参数控制可调度到节点的 pod 的最大数量: pods-per-coremax-pods。当两个选项都被使用时,这两个选项中的较小的限制为节点上的 pod 数量。超过这些值可导致:

  • OpenShift Container Platform 和 Docker 的 CPU 使用率增加。
  • 减慢 pod 调度的速度。
  • 潜在的内存不足情况(取决于节点中的内存量)。
  • 耗尽 IP 地址池。
  • 资源过量使用,导致用户应用程序性能变差。
注意

在 Kubernetes 中,包含单个容器的 pod 实际使用两个容器。第二个容器用来在实际容器启动前设置联网。因此,运行 10 个 pod 的系统实际上会运行 20 个容器。

pods-per-core 根据节点上的处理器内核数,设置节点可运行的 pod 数量。例如:如果在有 4 个处理器内核的节点上将 pods-per-core 设置为 10,则该节点上允许的最大 pod 数量为 40。

kubeletArguments:
  pods-per-core:
    - "10"
注意

pods-per-core 设置为 0 可禁用这个限制。

max-pods 将节点可以运行的 pod 数量设置为固定值,而不考虑节点的属性。集群限制记录 max-pods 的最大支持值。

kubeletArguments:
  max-pods:
    - "250"

使用上例,pods-per-core 的默认值是 10max-pods 的默认值是 250。这意味着,除非节点有 25 个或更多内核,否则 pods-per-core 将是限制因素。

2.12. 重置 Docker 存储

当您下载容器镜像并运行和删除容器时,Docker 并不总是释放映射的磁盘空间。因此,随着时间推移,节点上可能会耗尽空间,这可能会阻止 OpenShift Container Platform 创建新 pod,或者导致 pod 创建需要几分钟。

例如,下面显示了 6 分钟后仍然处于 ContainerCreating 状态的 pod,事件日志会显示一个 FailedSync 事件

$ oc get pod

输出示例

NAME                               READY     STATUS              RESTARTS   AGE
cakephp-mysql-persistent-1-build   0/1       ContainerCreating   0          6m
mysql-1-9767d                      0/1       ContainerCreating   0          2m
mysql-1-deploy                     0/1       ContainerCreating   0          6m

$ oc get events

输出示例

LASTSEEN   FIRSTSEEN   COUNT     NAME                               KIND                    SUBOBJECT                     TYPE      REASON                         SOURCE                                                 MESSAGE
6m         6m          1         cakephp-mysql-persistent-1-build   Pod                                                   Normal    Scheduled                      default-scheduler                                      Successfully assigned cakephp-mysql-persistent-1-build to ip-172-31-71-195.us-east-2.compute.internal
2m         5m          4         cakephp-mysql-persistent-1-build   Pod                                                   Warning   FailedSync                     kubelet, ip-172-31-71-195.us-east-2.compute.internal   Error syncing pod
2m         4m          4         cakephp-mysql-persistent-1-build   Pod                                                   Normal    SandboxChanged                 kubelet, ip-172-31-71-195.us-east-2.compute.internal   Pod sandbox changed, it will be killed and re-created.

解决这个问题的一种方法是重置 Docker 存储,以移除 Docker 不需要的构件。

在您要重启 Docker 存储的节点中:

  1. 运行以下命令将节点标记为不可调度:

    $ oc adm manage-node <node> --schedulable=false
  2. 运行以下命令关闭 Docker 和 atomic-openshift-node 服务:

    $ systemctl stop docker atomic-openshift-node
  3. 运行以下命令删除本地卷目录:

    $ rm -rf /var/lib/origin/openshift.local.volumes

    此命令会清除本地镜像缓存。因此,镜像(包括 ose-* 镜像)需要重新拉取。这会导致镜像存储恢复时 pod 启动时间较慢。

  4. 删除/var/lib/docker 目录:

    $ rm -rf /var/lib/docker
  5. 运行以下命令来重置 Docker 存储:

    $ docker-storage-setup --reset
  6. 运行以下命令重新创建 Docker 存储:

    $ docker-storage-setup
  7. 重新创建/var/lib/docker 目录:

    $ mkdir /var/lib/docker
  8. 运行以下命令来重启 Docker 和 atomic-openshift-node 服务:

    $ systemctl start docker atomic-openshift-node
  9. 通过重启主机来重启节点服务:

    # systemctl restart atomic-openshift-node.service
  10. 运行以下命令将节点标记为可以调度:

    $ oc adm manage-node <node> --schedulable=true

第 3 章 恢复 OpenShift Container Platform 组件

3.1. 概述

在 OpenShift Container Platform 中,您可以通过从单独的存储重新创建集群元素(包括节点和应用程序)来恢复集群及其组件

要恢复集群,您必须首先备份它

重要

以下过程描述了恢复应用程序和 OpenShift Container Platform 集群的通用方法。它不能考虑自定义要求。您可能需要采取额外的操作来恢复集群。

3.2. 恢复集群

要恢复集群,首先重新安装 OpenShift Container Platform。

流程

  1. 按照您原先安装 OpenShift Container Platform 的方式重新安装 OpenShift Container Platform。
  2. 运行所有自定义安装后步骤,例如更改 OpenShift Container Platform 控制之外的服务或安装额外的服务,如监控代理。

3.3. 恢复 master 主机备份

在创建了重要 master 主机文件的备份后,如果它们被破坏或意外删除,您可以通过将文件复制到 master,确保文件包含正确的内容并重新启动受影响的服务来恢复文件。

流程

  1. 恢复 /etc/origin/master/master-config.yaml 文件:

    # MYBACKUPDIR=*/backup/$(hostname)/$(date +%Y%m%d)*
    # cp /etc/origin/master/master-config.yaml /etc/origin/master/master-config.yaml.old
    # cp /backup/$(hostname)/$(date +%Y%m%d)/origin/master/master-config.yaml /etc/origin/master/master-config.yaml
    # master-restart api
    # master-restart controllers
    警告

    重新启动主控服务可能会导致停机。但是,您可以从高可用性负载均衡器池中移除 master 主机,然后执行恢复操作。正确恢复该服务后,您可以将 master 主机重新添加到负载均衡器池中。

    注意

    执行完全重启受影响的实例,以恢复 iptables 配置。

  2. 如果因为缺少软件包而无法重启 OpenShift Container Platform,请重新安装软件包。

    1. 获取当前安装的软件包列表:

      $ rpm -qa | sort > /tmp/current_packages.txt
    2. 查看软件包列表之间的区别:

      $ diff /tmp/current_packages.txt ${MYBACKUPDIR}/packages.txt
      
      > ansible-2.4.0.0-5.el7.noarch
    3. 重新安装缺少的软件包:

      # yum reinstall -y <packages> 1
      1
      使用软件包列表之间不同的软件包替换 <packages>
  3. 通过将证书复制到 /etc/pki/ca-trust/source/anchors/ 目录并执行 update-ca-trust 来恢复系统证书:

    $ MYBACKUPDIR=*/backup/$(hostname)/$(date +%Y%m%d)*
    $ sudo cp ${MYBACKUPDIR}/etc/pki/ca-trust/source/anchors/<certificate> /etc/pki/ca-trust/source/anchors/ 1
    $ sudo update-ca-trust
    1
    使用要恢复的系统证书的文件名替换 <certificate>
    注意

    始终确保在文件重新复制时恢复用户 ID 和组 ID,以及 SELinux 上下文。

3.4. 恢复节点主机备份

创建重要节点主机文件的备份后,如果它们被破坏或意外删除,您可以通过复制 文件来恢复文件,确保该文件包含正确的内容并重新启动受影响的服务。

流程

  1. 恢复 /etc/origin/node/node-config.yaml 文件:

    # MYBACKUPDIR=/backup/$(hostname)/$(date +%Y%m%d)
    # cp /etc/origin/node/node-config.yaml /etc/origin/node/node-config.yaml.old
    # cp /backup/$(hostname)/$(date +%Y%m%d)/etc/origin/node/node-config.yaml /etc/origin/node/node-config.yaml
    # reboot
警告

重新启动服务可能会导致停机。如需有关如何简化流程的提示,请参阅节点维护

注意

执行完全重启受影响的实例,以恢复 iptables 配置。

  1. 如果因为缺少软件包而无法重启 OpenShift Container Platform,请重新安装软件包。

    1. 获取当前安装的软件包列表:

      $ rpm -qa | sort > /tmp/current_packages.txt
    2. 查看软件包列表之间的区别:

      $ diff /tmp/current_packages.txt ${MYBACKUPDIR}/packages.txt
      
      > ansible-2.4.0.0-5.el7.noarch
    3. 重新安装缺少的软件包:

      # yum reinstall -y <packages> 1
      1
      使用软件包列表之间不同的软件包替换 <packages>
  2. 通过将证书复制到 /etc/pki/ca-trust/source/anchors/ 目录并执行 update-ca-trust 来恢复系统证书:

    $ MYBACKUPDIR=*/backup/$(hostname)/$(date +%Y%m%d)*
    $ sudo cp ${MYBACKUPDIR}/etc/pki/ca-trust/source/anchors/<certificate> /etc/pki/ca-trust/source/anchors/
    $ sudo update-ca-trust
    使用要恢复的系统证书的文件名替换 <certificate>
    注意

    始终确保在文件返回时恢复正确的用户 ID 和组群 ID,以及 SELinux 上下文。

3.5. 恢复 etcd

3.5.1. 恢复 etcd 配置文件

如果 etcd 主机已损坏,且 /etc/etcd/etcd.conf 文件丢失,请按照以下流程恢复该文件:

  1. 访问 etcd 主机:

    $ ssh master-0 1
    1
    使用 etcd 主机的名称替换 master-0
  2. 将备份 etcd.conf 文件复制到 /etc/etcd/

    # cp /backup/etcd-config-<timestamp>/etcd/etcd.conf /etc/etcd/etcd.conf
  3. 在文件中设置所需的权限和 selinux 上下文:

    # restorecon -RvF /etc/etcd/etcd.conf

在这个示例中,备份文件保存在 /backup/etcd-config-<timestamp>/etcd/etcd.conf 路径中,它可以用作外部 NFS 共享、S3 存储桶或其他存储解决方案。

恢复 etcd 配置文件后,您必须重启静态 pod。这是在恢复 etcd 数据后完成的。

3.5.2. 恢复 etcd 数据

在静态 pod 中恢复 etcd 前:

  • etcdctl 二进制文件必须可用,或者,在容器化安装中,rhel7/etcd 容器必须可用。

    您可以运行以下命令在 etcd 软件包中安装 etcdctl 二进制文件:

    # yum install etcd

    该软件包还会安装 systemd 服务。禁用并屏蔽该服务,使其在 etcd 在静态 pod 中运行时不会作为 systemd 服务运行。通过禁用并屏蔽该服务,确保您不会意外启动该服务,并防止它在重启系统时自动重启该服务。

    # systemctl disable etcd.service
    # systemctl mask etcd.service

在静态 pod 上恢复 etcd:

  1. 如果 pod 正在运行,通过将 pod 清单 YAML 文件移到另一个目录中来停止 etcd pod:

    # mkdir -p /etc/origin/node/pods-stopped
    # mv /etc/origin/node/pods/etcd.yaml /etc/origin/node/pods-stopped
  2. 移动所有旧数据:

    # mv /var/lib/etcd /var/lib/etcd.old

    您可以使用 etcdctl 在恢复 pod 的节点中重新创建数据。

  3. 将 etcd 快照恢复到 etcd pod 的挂载路径:

    # export ETCDCTL_API=3
    # etcdctl snapshot restore /etc/etcd/backup/etcd/snapshot.db \
    	 --data-dir /var/lib/etcd/ \
    	 --name ip-172-18-3-48.ec2.internal \
    	 --initial-cluster "ip-172-18-3-48.ec2.internal=https://172.18.3.48:2380" \
    	 --initial-cluster-token "etcd-cluster-1" \
    	 --initial-advertise-peer-urls https://172.18.3.48:2380 \
    	 --skip-hash-check=true

    etcd.conf 文件获取集群的适当值。

  4. 在数据目录中设置所需的权限和 selinux 上下文:

    # restorecon -RvF /var/lib/etcd/
  5. 通过将 pod 清单 YAML 文件移到所需目录中来重启 etcd pod:

    # mv /etc/origin/node/pods-stopped/etcd.yaml /etc/origin/node/pods/

3.6. 添加 etcd 节点

恢复 etcd 后,您可以在集群中添加更多 etcd 节点。您可以使用 Ansible playbook 或手动步骤添加 etcd 主机。

3.6.1. 使用 Ansible 添加新 etcd 主机

流程
  1. 在 Ansible 清单文件中,创建一个名为 [new_etcd] 的新组,再添加新主机。然后,将 new_etcd 组添加为 [OSEv3] 组的子组:

    [OSEv3:children]
    masters
    nodes
    etcd
    new_etcd 1
    
    ... [OUTPUT ABBREVIATED] ...
    
    [etcd]
    master-0.example.com
    master-1.example.com
    master-2.example.com
    
    [new_etcd] 2
    etcd0.example.com 3
    1 2 3
    添加以下行:
  2. 在安装 OpenShift Container Platform 并托管 Ansible 清单文件的主机中,切换到 playbook 目录并运行 etcd scaleup playbook:

    $ cd /usr/share/ansible/openshift-ansible
    $ ansible-playbook  playbooks/openshift-etcd/scaleup.yml
  3. 在 playbook 运行后,通过将新 etcd 主机从 [new_etcd] 组移到 [etcd] 组来修改清单文件,使其反映当前的状态:

    [OSEv3:children]
    masters
    nodes
    etcd
    new_etcd
    
    ... [OUTPUT ABBREVIATED] ...
    
    [etcd]
    master-0.example.com
    master-1.example.com
    master-2.example.com
    etcd0.example.com
  4. 如果使用 Flannel,修改位于 /etc/sysconfig/flanneld 的每个 OpenShift Container Platform 主机上的 flanneld 服务配置,使其包含新的 etcd 主机:

    FLANNEL_ETCD_ENDPOINTS=https://master-0.example.com:2379,https://master-1.example.com:2379,https://master-2.example.com:2379,https://etcd0.example.com:2379
  5. 重启 flanneld 服务:

    # systemctl restart flanneld.service

3.6.2. 手动添加新 etcd 主机

如果您没有以静态 pod 用户身份在 master 节点上运行 etcd,您可能需要添加另一个 etcd 主机。

流程
修改当前的 etcd 集群

要创建 etcd 证书,请运行 openssl 命令,将值替换为您环境中的值。

  1. 创建一些环境变量:

    export NEW_ETCD_HOSTNAME="*etcd0.example.com*"
    export NEW_ETCD_IP="192.168.55.21"
    
    export CN=$NEW_ETCD_HOSTNAME
    export SAN="IP:${NEW_ETCD_IP}, DNS:${NEW_ETCD_HOSTNAME}"
    export PREFIX="/etc/etcd/generated_certs/etcd-$CN/"
    export OPENSSLCFG="/etc/etcd/ca/openssl.cnf"
    注意

    用作 etcd_v3_ca_* 的自定义 openssl 扩展包括 $SAN 环境变量作为 subjectAltName。如需更多信息,请参阅 /etc/etcd/ca/openssl.cnf

  2. 创建用于存储配置和证书的目录:

    # mkdir -p ${PREFIX}
  3. 创建服务器证书请求并为其签名:(server.csr server.crt

    # openssl req -new -config ${OPENSSLCFG} \
        -keyout ${PREFIX}server.key  \
        -out ${PREFIX}server.csr \
        -reqexts etcd_v3_req -batch -nodes \
        -subj /CN=$CN
    
    # openssl ca -name etcd_ca -config ${OPENSSLCFG} \
        -out ${PREFIX}server.crt \
        -in ${PREFIX}server.csr \
        -extensions etcd_v3_ca_server -batch
  4. 创建对等证书请求并将其签名:(peer.csr peer.crt

    # openssl req -new -config ${OPENSSLCFG} \
        -keyout ${PREFIX}peer.key \
        -out ${PREFIX}peer.csr \
        -reqexts etcd_v3_req -batch -nodes \
        -subj /CN=$CN
    
    # openssl ca -name etcd_ca -config ${OPENSSLCFG} \
      -out ${PREFIX}peer.crt \
      -in ${PREFIX}peer.csr \
      -extensions etcd_v3_ca_peer -batch
  5. 从当前节点复制当前的 etcd 配置和 ca.crt 文件,作为稍后修改的示例:

    # cp /etc/etcd/etcd.conf ${PREFIX}
    # cp /etc/etcd/ca.crt ${PREFIX}
  6. 当仍处于存活的 etcd 主机上时,将新主机添加到集群中。要在集群中添加额外的 etcd 成员,您必须首先调整第一个成员的 peerURLs 值中的默认 localhost 对等点:

    1. 使用 member list 命令获取第一个成员的成员 ID:

      # etcdctl --cert-file=/etc/etcd/peer.crt \
          --key-file=/etc/etcd/peer.key \
          --ca-file=/etc/etcd/ca.crt \
          --peers="https://172.18.1.18:2379,https://172.18.9.202:2379,https://172.18.0.75:2379" \ 1
          member list
      1
      请确定在 --peers 参数值中只指定活跃的 etcd 成员的 URL。
    2. 获取 etcd 侦听集群对等点的 IP 地址:

      $ ss -l4n | grep 2380
    3. 使用 etcdctl member update 命令更新 peerURLs 的值,方法是传递从上一步中获取的成员 ID 和 IP 地址:

      # etcdctl --cert-file=/etc/etcd/peer.crt \
          --key-file=/etc/etcd/peer.key \
          --ca-file=/etc/etcd/ca.crt \
          --peers="https://172.18.1.18:2379,https://172.18.9.202:2379,https://172.18.0.75:2379" \
          member update 511b7fb6cc0001 https://172.18.1.18:2380
    4. 重新运行 member list 命令并确保对等 URL 不再包含 localhost
  7. 将新主机添加到 etcd 集群。请注意,新主机还没有配置,因此在您配置新主机前,状态会保持为 unstarted

    警告

    您必须添加每个成员并一次在线。当您在集群中添加每个额外成员时,您必须为当前同级调整 peerURLs 列表。peerURLs 列表会为每个添加的成员生成一个。etcdctl member add 命令输出在etcd.conf 文件中添加每个成员时必须设置的值,如下说明所示。

    # etcdctl -C https://${CURRENT_ETCD_HOST}:2379 \
      --ca-file=/etc/etcd/ca.crt     \
      --cert-file=/etc/etcd/peer.crt     \
      --key-file=/etc/etcd/peer.key member add ${NEW_ETCD_HOSTNAME} https://${NEW_ETCD_IP}:2380 1
    
    Added member named 10.3.9.222 with ID 4e1db163a21d7651 to cluster
    
    ETCD_NAME="<NEW_ETCD_HOSTNAME>"
    ETCD_INITIAL_CLUSTER="<NEW_ETCD_HOSTNAME>=https://<NEW_HOST_IP>:2380,<CLUSTERMEMBER1_NAME>=https:/<CLUSTERMEMBER2_IP>:2380,<CLUSTERMEMBER2_NAME>=https:/<CLUSTERMEMBER2_IP>:2380,<CLUSTERMEMBER3_NAME>=https:/<CLUSTERMEMBER3_IP>:2380"
    ETCD_INITIAL_CLUSTER_STATE="existing"
    1
    在这个行中,10.3.9.222 是 etcd 成员的标签。您可以指定主机名、IP 地址或简单名称。
  8. 更新示例 ${PREFIX}/etcd.conf 文件。

    1. 将以下值替换为上一步中生成的值:

      • ETCD_NAME
      • ETCD_INITIAL_CLUSTER
      • ETCD_INITIAL_CLUSTER_STATE
    2. 使用上一步中输出中的新主机 IP 修改以下变量:您可以使用 ${NEW_ETCD_IP} 作为值。

      ETCD_LISTEN_PEER_URLS
      ETCD_LISTEN_CLIENT_URLS
      ETCD_INITIAL_ADVERTISE_PEER_URLS
      ETCD_ADVERTISE_CLIENT_URLS
    3. 如果您之前使用 member 系统作为 etcd 节点,您必须覆盖/etc/etcd/etcd.conf 文件中的当前值。
    4. 检查文件中的语法错误或缺少 IP 地址,否则 etcd 服务可能会失败:

      # vi ${PREFIX}/etcd.conf
  9. 在托管安装文件的节点上,更新/etc/ansible/hosts 清单文件中的 [etcd] 主机组。删除旧的 etcd 主机并添加新主机。
  10. 创建一个包含证书、示例配置文件和 catgz 文件并将其复制到新主机上:

    # tar -czvf /etc/etcd/generated_certs/${CN}.tgz -C ${PREFIX} .
    # scp /etc/etcd/generated_certs/${CN}.tgz ${CN}:/tmp/
修改新的 etcd 主机
  1. 安装 iptables-services 以提供 iptables 工具为 etcd 打开所需的端口:

    # yum install -y iptables-services
  2. 创建 OS_FIREWALL_ALLOW 防火墙规则以允许 etcd 进行通信:

    • 客户端的端口 2379/tcp
    • 端口 2380/tcp 用于对等通信

      # systemctl enable iptables.service --now
      # iptables -N OS_FIREWALL_ALLOW
      # iptables -t filter -I INPUT -j OS_FIREWALL_ALLOW
      # iptables -A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m tcp --dport 2379 -j ACCEPT
      # iptables -A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m tcp --dport 2380 -j ACCEPT
      # iptables-save | tee /etc/sysconfig/iptables
      注意

      在本例中,一个新的链 OS_FIREWALL_ALLOW 被创建,它是 OpenShift Container Platform 安装程序用于防火墙规则的标准命名。

      警告

      如果环境托管在 IaaS 环境中,请修改实例的安全组,以允许传入到这些端口的流量。

  3. 安装 etcd:

    # yum install -y etcd

    确定安装了版本 etcd-2.3.7-4.el7.x86_64 或更高版本,

  4. 通过删除 etcd pod 定义来确保 etcd 服务没有运行:

    # mkdir -p /etc/origin/node/pods-stopped
    # mv /etc/origin/node/pods/* /etc/origin/node/pods-stopped/
  5. 删除所有 etcd 配置和数据:

    # rm -Rf /etc/etcd/*
    # rm -Rf /var/lib/etcd/*
  6. 提取证书和配置文件:

    # tar xzvf /tmp/etcd0.example.com.tgz -C /etc/etcd/
  7. 在新主机上启动 etcd:

    # systemctl enable etcd --now
  8. 验证主机是否是集群的一部分以及当前的集群健康状况:

    • 如果使用 v2 etcd api,请运行以下命令:

      # etcdctl --cert-file=/etc/etcd/peer.crt \
                --key-file=/etc/etcd/peer.key \
                --ca-file=/etc/etcd/ca.crt \
                --peers="https://*master-0.example.com*:2379,\
                https://*master-1.example.com*:2379,\
                https://*master-2.example.com*:2379,\
                https://*etcd0.example.com*:2379"\
                cluster-health
      member 5ee217d19001 is healthy: got healthy result from https://192.168.55.12:2379
      member 2a529ba1840722c0 is healthy: got healthy result from https://192.168.55.8:2379
      member 8b8904727bf526a5 is healthy: got healthy result from https://192.168.55.21:2379
      member ed4f0efd277d7599 is healthy: got healthy result from https://192.168.55.13:2379
      cluster is healthy
    • 如果使用 v3 etcd api,请运行以下命令:

      # ETCDCTL_API=3 etcdctl --cert="/etc/etcd/peer.crt" \
                --key=/etc/etcd/peer.key \
                --cacert="/etc/etcd/ca.crt" \
                --endpoints="https://*master-0.example.com*:2379,\
                  https://*master-1.example.com*:2379,\
                  https://*master-2.example.com*:2379,\
                  https://*etcd0.example.com*:2379"\
                  endpoint health
      https://master-0.example.com:2379 is healthy: successfully committed proposal: took = 5.011358ms
      https://master-1.example.com:2379 is healthy: successfully committed proposal: took = 1.305173ms
      https://master-2.example.com:2379 is healthy: successfully committed proposal: took = 1.388772ms
      https://etcd0.example.com:2379 is healthy: successfully committed proposal: took = 1.498829ms
修改每个 OpenShift Container Platform master
  1. 修改每个 master 上 /etc/origin/master/master-config.yaml 文件的 etcClientInfo 部分中的 master 配置。将新的 etcd 主机添加到 OpenShift Container Platform 用来存储数据的 etcd 服务器列表中,并删除所有失败的 etcd 主机:

    etcdClientInfo:
      ca: master.etcd-ca.crt
      certFile: master.etcd-client.crt
      keyFile: master.etcd-client.key
      urls:
        - https://master-0.example.com:2379
        - https://master-1.example.com:2379
        - https://master-2.example.com:2379
        - https://etcd0.example.com:2379
  2. 重启 master API 服务:

    • 在每个 master 上:

      # master-restart api
      # master-restart controllers
      警告

      etcd 节点的数量必须是奇数,因此您必须至少添加两个主机。

  3. 如果使用 Flannel,修改每个 OpenShift Container Platform 主机上的 flanneld 服务配置 /etc/sysconfig/flanneld,使其包含新的 etcd 主机:

    FLANNEL_ETCD_ENDPOINTS=https://master-0.example.com:2379,https://master-1.example.com:2379,https://master-2.example.com:2379,https://etcd0.example.com:2379
  4. 重启 flanneld 服务:

    # systemctl restart flanneld.service

3.7. 使 OpenShift Container Platform 服务恢复在线

完成更改后,重新在线 OpenShift Container Platform。

流程

  1. 在每个 OpenShift Container Platform master 上,从备份中恢复 master 和节点配置,并启用并重启所有相关服务:

    # cp ${MYBACKUPDIR}/etc/origin/node/pods/* /etc/origin/node/pods/
    # cp ${MYBACKUPDIR}/etc/origin/master/master.env /etc/origin/master/master.env
    # cp ${MYBACKUPDIR}/etc/origin/master/master-config.yaml.<timestamp> /etc/origin/master/master-config.yaml
    # cp ${MYBACKUPDIR}/etc/origin/node/node-config.yaml.<timestamp> /etc/origin/node/node-config.yaml
    # cp ${MYBACKUPDIR}/etc/origin/master/scheduler.json.<timestamp> /etc/origin/master/scheduler.json
    # master-restart api
    # master-restart controllers
  2. 在每个 OpenShift Container Platform 节点上,根据需要更新节点配置映射,然后启用并重启 atomic-openshift-node 服务:

    # cp /etc/origin/node/node-config.yaml.<timestamp> /etc/origin/node/node-config.yaml
    # systemctl enable atomic-openshift-node
    # systemctl start atomic-openshift-node

3.8. 恢复项目

要恢复项目,请创建新项目,然后运行 oc create -f <file_name> 来恢复任何导出的文件。

流程

  1. 创建项目:

    $ oc new-project <project_name> 1
    1
    这个 <project_name> 值必须与备份的项目的名称匹配。
  2. 导入项目对象:

    $ oc create -f project.yaml
  3. 导入您在备份项目时导出的任何其他资源,如角色绑定、secret、服务帐户和持久性卷声明:

    $ oc create -f <object>.yaml

    如果某些资源需要存在另一个对象,则可能无法导入。如果发生了这种情况,请查看错误消息以确定必须首先导入哪些资源。

警告

某些资源(如 pod 和默认服务帐户)可能无法创建。

3.9. 恢复应用程序数据

您可以使用 oc rsync 命令恢复应用程序数据,假设 rsync 已安装在容器镜像中。红帽 rhel7 基础镜像包含 rsync。因此,所有基于 rhel7 的镜像也都包含它。请参阅对 CLI 操作进行故障排除和调试 - rsync

警告

这是应用程序数据的一种通用恢复,没有考虑特定于应用程序的备份程序,例如数据库系统的特殊导出和导入程序。

可能存在其他恢复方式,具体取决于您使用的持久卷类型,如 Cinder、NFS 或 Gluster。

流程

恢复 Jenkins 部署的应用数据示例

  1. 验证备份:

    $ ls -la /tmp/jenkins-backup/
    total 8
    drwxrwxr-x.  3 user     user   20 Sep  6 11:14 .
    drwxrwxrwt. 17 root     root 4096 Sep  6 11:16 ..
    drwxrwsrwx. 12 user     user 4096 Sep  6 11:14 jenkins
  2. 使用 oc rsync 工具将数据复制到正在运行的 pod 中:

    $ oc rsync /tmp/jenkins-backup/jenkins jenkins-1-37nux:/var/lib
    注意

    根据应用程序,您可能需要重新启动应用程序。

  3. 另外,还可使用新数据重启应用程序:

    $ oc delete pod jenkins-1-37nux

    另外,您可以将部署缩减为 0,然后再次向上扩展:

    $ oc scale --replicas=0 dc/jenkins
    $ oc scale --replicas=1 dc/jenkins

3.10. 恢复持久性卷声明

本主题描述了两种恢复数据的方法。第一个步骤涉及删除 文件,然后将文件重新放到预期位置。第二个示例演示了迁移持久卷声明。当后端存储不再存在时,迁移会在需要移动的存储或灾难场景中发生。

检查特定应用程序的恢复程序,了解将数据恢复到应用程序所需的任何步骤。

3.10.1. 将文件恢复到现有 PVC

流程
  1. 删除该文件:

    $ oc rsh demo-2-fxx6d
    sh-4.2$ ls */opt/app-root/src/uploaded/*
    lost+found  ocp_sop.txt
    sh-4.2$ *rm -rf /opt/app-root/src/uploaded/ocp_sop.txt*
    sh-4.2$ *ls /opt/app-root/src/uploaded/*
    lost+found
  2. 替换包含 pvc 中文件的 rsync 备份的服务器中的文件:

    $ oc rsync uploaded demo-2-fxx6d:/opt/app-root/src/
  3. 使用 oc rsh 连接到 pod 并查看目录中的内容,验证该文件是否在 pod 上恢复:

    $ oc rsh demo-2-fxx6d
    sh-4.2$ *ls /opt/app-root/src/uploaded/*
    lost+found  ocp_sop.txt

3.10.2. 将数据恢复到新 PVC

以下步骤假定创建了新的 pvc

流程
  1. 覆盖当前定义的 claim-name

    $ oc set volume dc/demo --add --name=persistent-volume \
    		--type=persistentVolumeClaim --claim-name=filestore \ --mount-path=/opt/app-root/src/uploaded --overwrite
  2. 验证 pod 是否在使用新 PVC:

    $ oc describe dc/demo
    Name:		demo
    Namespace:	test
    Created:	3 hours ago
    Labels:		app=demo
    Annotations:	openshift.io/generated-by=OpenShiftNewApp
    Latest Version:	3
    Selector:	app=demo,deploymentconfig=demo
    Replicas:	1
    Triggers:	Config, Image(demo@latest, auto=true)
    Strategy:	Rolling
    Template:
      Labels:	app=demo
    		deploymentconfig=demo
      Annotations:	openshift.io/container.demo.image.entrypoint=["container-entrypoint","/bin/sh","-c","$STI_SCRIPTS_PATH/usage"]
    		openshift.io/generated-by=OpenShiftNewApp
      Containers:
       demo:
        Image:	docker-registry.default.svc:5000/test/demo@sha256:0a9f2487a0d95d51511e49d20dc9ff6f350436f935968b0c83fcb98a7a8c381a
        Port:	8080/TCP
        Volume Mounts:
          /opt/app-root/src/uploaded from persistent-volume (rw)
        Environment Variables:	<none>
      Volumes:
       persistent-volume:
        Type:	PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
        *ClaimName:	filestore*
        ReadOnly:	false
    ...omitted...
  3. 现在,部署配置使用新的 pvc,运行 oc rsync 将文件放在新的 pvc 中:

    $ oc rsync uploaded demo-3-2b8gs:/opt/app-root/src/
    sending incremental file list
    uploaded/
    uploaded/ocp_sop.txt
    uploaded/lost+found/
    
    sent 181 bytes  received 39 bytes  146.67 bytes/sec
    total size is 32  speedup is 0.15
  4. 使用 oc rsh 连接到 pod 并查看目录中的内容,验证该文件是否在 pod 上恢复:

    $ oc rsh demo-3-2b8gs
    sh-4.2$ ls /opt/app-root/src/uploaded/
    lost+found  ocp_sop.txt

第 4 章 替换 master 主机

您可以替换失败的 master 主机。

首先,从集群中删除失败的 master 主机,然后添加替换的 master 主机。如果失败的 master 主机运行 etcd,请通过在新的 master 主机上添加 etcd 来扩展 etcd。

重要

您必须完成本主题的所有部分。

4.1. 弃用 master 主机

Master 主机运行重要的服务,如 OpenShift Container Platform API 和控制器服务。为了弃用 master 主机,必须停止这些服务。

OpenShift Container Platform API 服务是一个主动/主动服务,因此只要请求发送到单独的 master 服务器,停止该服务不会影响环境。但是,OpenShift Container Platform 控制器服务是一个主动/被动服务,服务使用 etcd 来决定活跃的 master。

在多主控机架构中弃用 master 主机包括从负载均衡器池中移除 master,以避免尝试使用该 master 的新连接。这个过程很大程度上依赖于使用的负载均衡器。以下步骤显示从 haproxy 中删除 master 的详情。如果 OpenShift Container Platform 在云供应商上运行或使用 F5 设备,请查看特定的产品文档来从轮转中删除 master。

流程

  1. 删除 /etc/haproxy/haproxy.cfg 配置文件中的 backend 部分。例如,如果使用 haproxy 弃用名为 master-0.example.com 的 master,请确保从以下内容中删除主机名:

    backend mgmt8443
        balance source
        mode tcp
        # MASTERS 8443
        server master-1.example.com 192.168.55.12:8443 check
        server master-2.example.com 192.168.55.13:8443 check
  2. 然后,重启 haproxy 服务。

    $ sudo systemctl restart haproxy
  3. 从负载均衡器中删除 master 后,通过将定义文件移出静态 pod dir /etc/origin/node/pods 来禁用 API 和控制器服务:

    # mkdir -p /etc/origin/node/pods/disabled
    # mv /etc/origin/node/pods/controller.yaml /etc/origin/node/pods/disabled/:
    +
  4. 由于 master 主机是一个可调度的 OpenShift Container Platform 节点,因此请按照弃用节点主机部分中的步骤进行操作
  5. /etc/ansible/hosts Ansible 清单文件中的 [masters][nodes] 组中删除 master 主机,以便在使用该清单文件运行任何 Ansible 任务时避免出现问题。

    警告

    弃用 Ansible 清单中列出的第一个 master 主机需要额外的举措。

    /etc/origin/master/ca.serial.txt 文件只在 Ansible 主机清单中列出的第一个 master 上生成。如果您弃用了第一个 master 主机,请在进程前将 /etc/origin/master/ca.serial.txt 文件复制到其余 master 主机中。

    重要

    在运行多个 master 的 OpenShift Container Platform 3.11 集群中,其中一个 master 节点包括 /etc/origin/master/etc/etcd/ca/etc/etcd/generated_certs 中的额外 CA 证书。这些是应用程序节点和 etcd 节点扩展操作所必需的,如果 CA 主机 master 已被弃用,则必须在另一个 master 节点上恢复。

  6. kubernetes 服务将 master 主机 IP 作为端点包含在内。要验证 master 已被正确弃用,请查看 kubernetes 服务输出并查看已弃用的 master 是否已被删除:

    $ oc describe svc kubernetes -n default
    Name:			kubernetes
    Namespace:		default
    Labels:			component=apiserver
    			provider=kubernetes
    Annotations:		<none>
    Selector:		<none>
    Type:			ClusterIP
    IP:			10.111.0.1
    Port:			https	443/TCP
    Endpoints:		192.168.55.12:8443,192.168.55.13:8443
    Port:			dns	53/UDP
    Endpoints:		192.168.55.12:8053,192.168.55.13:8053
    Port:			dns-tcp	53/TCP
    Endpoints:		192.168.55.12:8053,192.168.55.13:8053
    Session Affinity:	ClientIP
    Events:			<none>

    主机成功弃用后,可以安全地删除之前运行主控机的主机。

4.2. 添加主机

您可以通过运行scaleup.yml playbook 添加新主机到集群。此 playbook 查询 master,为新主机生成和发布新证书,然后仅在新主机上运行配置 playbook。在运行scaleup.yml playbook 之前,请完成所有必备的主机准备步骤

重要

scaleup.yml playbook 仅配置新主机。它不会更新 master服务的 NO_PROXY,也不会重启 master 服务。

您必须有一个现有的清单文件,如/etc/ansible/hosts ,它代表当前集群配置,才能运行scaleup.yml playbook。如果您之前使用 atomic-openshift-installer 命令来运行安装,您可以检查~/.config/openshift/hosts 中安装程序生成的最后一个清单文件,并将该文件用作清单文件。您可以根据需要修改此文件。然后,当运行 ansible-playbook 时,您必须使用 -i 指定文件位置。

流程

  1. 通过更新 openshift-ansible 软件包来确保您有最新的 playbook:

    # yum update openshift-ansible
  2. 编辑/etc/ansible/hosts 文件,并将 new_<host_type> 添加到 [OSEv3:children] 部分。例如,要添加新节点主机,请添加 new_nodes

    [OSEv3:children]
    masters
    nodes
    new_nodes

    若要添加新的 master 主机,可添加 new_masters

  3. 创建一个 [new_<host_type>] 部分来为新主机指定主机信息。将此部分格式化为现有部分,如下例所示:

    [nodes]
    master[1:3].example.com
    node1.example.com openshift_node_group_name='node-config-compute'
    node2.example.com openshift_node_group_name='node-config-compute'
    infra-node1.example.com openshift_node_group_name='node-config-infra'
    infra-node2.example.com openshift_node_group_name='node-config-infra'
    
    [new_nodes]
    node3.example.com openshift_node_group_name='node-config-infra'

    如需了解更多选项,请参阅配置主机变量

    在添加新 master 时,将主机添加到 [new_masters] 部分和 [new_nodes] 部分,以确保新 master 主机是 OpenShift SDN 的一部分:

    [masters]
    master[1:2].example.com
    
    [new_masters]
    master3.example.com
    
    [nodes]
    master[1:2].example.com
    node1.example.com openshift_node_group_name='node-config-compute'
    node2.example.com openshift_node_group_name='node-config-compute'
    infra-node1.example.com openshift_node_group_name='node-config-infra'
    infra-node2.example.com openshift_node_group_name='node-config-infra'
    
    [new_nodes]
    master3.example.com
    重要

    如果您使用 node-role.kubernetes.io/infra=true 标签标记 master 主机,且没有其他专用基础架构节点,则必须通过将 openshift_schedulable=true 添加到条目来明确将该主机标记为可以调度。否则,registry 和路由器 Pod 将无法放置到任何节点。

  4. 更改到 playbook 目录,再运行openshift_node_group.yml playbook。如果您的清单文件位于/etc/ansible/hosts 默认以外的位置,请使用 -i 选项指定位置:

    $ cd /usr/share/ansible/openshift-ansible
    $ ansible-playbook [-i /path/to/file] \
      playbooks/openshift-master/openshift_node_group.yml

    这会为新节点组创建 ConfigMap,并最终为主机上的节点配置文件。

    注意

    运行 openshift_node_group.yaml playbook 只会更新新节点。无法运行它来更新集群中的现有节点。

  5. 运行scaleup.yml playbook。如果您的清单文件位于默认/etc/ansible/hosts 以外的位置,请使用 -i 选项指定位置。

    • 对于额外的节点:

      $ ansible-playbook [-i /path/to/file] \
          playbooks/openshift-node/scaleup.yml
    • 对于额外的 master:

      $ ansible-playbook [-i /path/to/file] \
          playbooks/openshift-master/scaleup.yml
  6. 如果您在集群中部署了 EFK 堆栈,请将节点标签设置为 logging-infra-fluentd=true

    # oc label node/new-node.example.com logging-infra-fluentd=true
  7. 在 playbook 运行后,验证安装
  8. 将您在 [new_<host_type>] 部分定义的任何主机移动到它们的相应部分。通过移动这些主机,后续的 playbook 运行使用此清单文件正确处理节点。您可以保留空 [new_<host_type>] 部分。例如,添加新节点时:

    [nodes]
    master[1:3].example.com
    node1.example.com openshift_node_group_name='node-config-compute'
    node2.example.com openshift_node_group_name='node-config-compute'
    node3.example.com openshift_node_group_name='node-config-compute'
    infra-node1.example.com openshift_node_group_name='node-config-infra'
    infra-node2.example.com openshift_node_group_name='node-config-infra'
    
    [new_nodes]

4.3. 扩展 etcd

您可以通过在 etcd 主机中添加更多资源或通过添加更多 etcd 主机来纵向扩展 etcd 集群。

注意

由于 etcd 使用的投票系统,集群必须始终包含奇数个成员数。

拥有奇数 etcd 主机的集群可考虑容错。etcd 主机数量为奇数不会改变仲裁所需的数量,但会增加故障的容错能力。例如,对于包含三个成员的群集,仲裁为 2,保留一个失败容错能力。这可确保群集在两个成员健康时继续运行。

建议具有三个 etcd 主机的生产中集群。

新主机需要全新的红帽企业 Linux 版本 7 专用主机。etcd 存储应位于 SSD 磁盘上,以实现最高性能和挂载到 /var/lib/etcd 中的专用磁盘中。

先决条件

  1. 在添加新的 etcd 主机前,请备份 etcd 配置和数据以防止数据丢失
  2. 检查当前的 etcd 集群状态,以避免添加新主机到不健康的集群。运行这个命令:

    # ETCDCTL_API=3 etcdctl --cert="/etc/etcd/peer.crt" \
              --key=/etc/etcd/peer.key \
              --cacert="/etc/etcd/ca.crt" \
              --endpoints="https://*master-0.example.com*:2379,\
                https://*master-1.example.com*:2379,\
                https://*master-2.example.com*:2379"
                endpoint health
    https://master-0.example.com:2379 is healthy: successfully committed proposal: took = 5.011358ms
    https://master-1.example.com:2379 is healthy: successfully committed proposal: took = 1.305173ms
    https://master-2.example.com:2379 is healthy: successfully committed proposal: took = 1.388772ms
  3. 在运行 scaleup playbook 前,请确保新主机已注册到正确的红帽软件频道中:

    # subscription-manager register \
        --username=*<username>* --password=*<password>*
    # subscription-manager attach --pool=*<poolid>*
    # subscription-manager repos --disable="*"
    # subscription-manager repos \
        --enable=rhel-7-server-rpms \
        --enable=rhel-7-server-extras-rpms

    etcd 托管在 rhel-7-server-extras-rpms 软件频道中。

  4. 确保从 etcd 集群中删除所有未使用的 etcd 成员。这必须在运行 scaleup playbook 前完成。

    1. 列出 etcd 成员:

      # etcdctl --cert="/etc/etcd/peer.crt" --key="/etc/etcd/peer.key" \
        --cacert="/etc/etcd/ca.crt" --endpoints=ETCD_LISTEN_CLIENT_URLS member list -w table

      复制未使用的 etcd 成员 ID(如果适用)。

    2. 通过使用以下命令指定其 ID 来删除未使用的成员:

      # etcdctl --cert="/etc/etcd/peer.crt" --key="/etc/etcd/peer.key" \
        --cacert="/etc/etcd/ca.crt" --endpoints=ETCD_LISTEN_CLIENT_URL member remove UNUSED_ETCD_MEMBER_ID
  5. 在当前 etcd 节点上升级 etcd 和 iptables:

    # yum update etcd iptables-services
  6. 备份 etcd 主机的 /etc/etcd 配置。
  7. 如果新的 etcd 成员也是 OpenShift Container Platform 节点,请将所需的主机数量添加到集群中
  8. 此流程的其余部分假定您添加了一台主机,但是如果您添加多个主机,请在每个主机上执行所有步骤。

4.3.1. 使用 Ansible 添加新 etcd 主机

流程
  1. 在 Ansible 清单文件中,创建一个名为 [new_etcd] 的新组,再添加新主机。然后,将 new_etcd 组添加为 [OSEv3] 组的子组:

    [OSEv3:children]
    masters
    nodes
    etcd
    new_etcd 1
    
    ... [OUTPUT ABBREVIATED] ...
    
    [etcd]
    master-0.example.com
    master-1.example.com
    master-2.example.com
    
    [new_etcd] 2
    etcd0.example.com 3
    1 2 3
    添加以下行:
  2. 在安装 OpenShift Container Platform 并托管 Ansible 清单文件的主机中,切换到 playbook 目录并运行 etcd scaleup playbook:

    $ cd /usr/share/ansible/openshift-ansible
    $ ansible-playbook  playbooks/openshift-etcd/scaleup.yml
  3. 在 playbook 运行后,通过将新 etcd 主机从 [new_etcd] 组移到 [etcd] 组来修改清单文件,使其反映当前的状态:

    [OSEv3:children]
    masters
    nodes
    etcd
    new_etcd
    
    ... [OUTPUT ABBREVIATED] ...
    
    [etcd]
    master-0.example.com
    master-1.example.com
    master-2.example.com
    etcd0.example.com
  4. 如果使用 Flannel,修改位于 /etc/sysconfig/flanneld 的每个 OpenShift Container Platform 主机上的 flanneld 服务配置,使其包含新的 etcd 主机:

    FLANNEL_ETCD_ENDPOINTS=https://master-0.example.com:2379,https://master-1.example.com:2379,https://master-2.example.com:2379,https://etcd0.example.com:2379
  5. 重启 flanneld 服务:

    # systemctl restart flanneld.service

4.3.2. 手动添加新 etcd 主机

如果您没有以静态 pod 用户身份在 master 节点上运行 etcd,您可能需要添加另一个 etcd 主机。

流程
修改当前的 etcd 集群

要创建 etcd 证书,请运行 openssl 命令,将值替换为您环境中的值。

  1. 创建一些环境变量:

    export NEW_ETCD_HOSTNAME="*etcd0.example.com*"
    export NEW_ETCD_IP="192.168.55.21"
    
    export CN=$NEW_ETCD_HOSTNAME
    export SAN="IP:${NEW_ETCD_IP}, DNS:${NEW_ETCD_HOSTNAME}"
    export PREFIX="/etc/etcd/generated_certs/etcd-$CN/"
    export OPENSSLCFG="/etc/etcd/ca/openssl.cnf"
    注意

    用作 etcd_v3_ca_* 的自定义 openssl 扩展包括 $SAN 环境变量作为 subjectAltName。如需更多信息,请参阅 /etc/etcd/ca/openssl.cnf

  2. 创建用于存储配置和证书的目录:

    # mkdir -p ${PREFIX}
  3. 创建服务器证书请求并为其签名:(server.csr server.crt

    # openssl req -new -config ${OPENSSLCFG} \
        -keyout ${PREFIX}server.key  \
        -out ${PREFIX}server.csr \
        -reqexts etcd_v3_req -batch -nodes \
        -subj /CN=$CN
    
    # openssl ca -name etcd_ca -config ${OPENSSLCFG} \
        -out ${PREFIX}server.crt \
        -in ${PREFIX}server.csr \
        -extensions etcd_v3_ca_server -batch
  4. 创建对等证书请求并将其签名:(peer.csr peer.crt

    # openssl req -new -config ${OPENSSLCFG} \
        -keyout ${PREFIX}peer.key \
        -out ${PREFIX}peer.csr \
        -reqexts etcd_v3_req -batch -nodes \
        -subj /CN=$CN
    
    # openssl ca -name etcd_ca -config ${OPENSSLCFG} \
      -out ${PREFIX}peer.crt \
      -in ${PREFIX}peer.csr \
      -extensions etcd_v3_ca_peer -batch
  5. 从当前节点复制当前的 etcd 配置和 ca.crt 文件,作为稍后修改的示例:

    # cp /etc/etcd/etcd.conf ${PREFIX}
    # cp /etc/etcd/ca.crt ${PREFIX}
  6. 当仍处于存活的 etcd 主机上时,将新主机添加到集群中。要在集群中添加额外的 etcd 成员,您必须首先调整第一个成员的 peerURLs 值中的默认 localhost 对等点:

    1. 使用 member list 命令获取第一个成员的成员 ID:

      # etcdctl --cert-file=/etc/etcd/peer.crt \
          --key-file=/etc/etcd/peer.key \
          --ca-file=/etc/etcd/ca.crt \
          --peers="https://172.18.1.18:2379,https://172.18.9.202:2379,https://172.18.0.75:2379" \ 1
          member list
      1
      请确定在 --peers 参数值中只指定活跃的 etcd 成员的 URL。
    2. 获取 etcd 侦听集群对等点的 IP 地址:

      $ ss -l4n | grep 2380
    3. 使用 etcdctl member update 命令更新 peerURLs 的值,方法是传递从上一步中获取的成员 ID 和 IP 地址:

      # etcdctl --cert-file=/etc/etcd/peer.crt \
          --key-file=/etc/etcd/peer.key \
          --ca-file=/etc/etcd/ca.crt \
          --peers="https://172.18.1.18:2379,https://172.18.9.202:2379,https://172.18.0.75:2379" \
          member update 511b7fb6cc0001 https://172.18.1.18:2380
    4. 重新运行 member list 命令并确保对等 URL 不再包含 localhost
  7. 将新主机添加到 etcd 集群。请注意,新主机还没有配置,因此在您配置新主机前,状态会保持为 unstarted

    警告

    您必须添加每个成员并一次在线。当您在集群中添加每个额外成员时,您必须为当前同级调整 peerURLs 列表。peerURLs 列表会为每个添加的成员生成一个。etcdctl member add 命令输出在etcd.conf 文件中添加每个成员时必须设置的值,如下说明所示。

    # etcdctl -C https://${CURRENT_ETCD_HOST}:2379 \
      --ca-file=/etc/etcd/ca.crt     \
      --cert-file=/etc/etcd/peer.crt     \
      --key-file=/etc/etcd/peer.key member add ${NEW_ETCD_HOSTNAME} https://${NEW_ETCD_IP}:2380 1
    
    Added member named 10.3.9.222 with ID 4e1db163a21d7651 to cluster
    
    ETCD_NAME="<NEW_ETCD_HOSTNAME>"
    ETCD_INITIAL_CLUSTER="<NEW_ETCD_HOSTNAME>=https://<NEW_HOST_IP>:2380,<CLUSTERMEMBER1_NAME>=https:/<CLUSTERMEMBER2_IP>:2380,<CLUSTERMEMBER2_NAME>=https:/<CLUSTERMEMBER2_IP>:2380,<CLUSTERMEMBER3_NAME>=https:/<CLUSTERMEMBER3_IP>:2380"
    ETCD_INITIAL_CLUSTER_STATE="existing"
    1
    在这个行中,10.3.9.222 是 etcd 成员的标签。您可以指定主机名、IP 地址或简单名称。
  8. 更新示例 ${PREFIX}/etcd.conf 文件。

    1. 将以下值替换为上一步中生成的值:

      • ETCD_NAME
      • ETCD_INITIAL_CLUSTER
      • ETCD_INITIAL_CLUSTER_STATE
    2. 使用上一步中输出中的新主机 IP 修改以下变量:您可以使用 ${NEW_ETCD_IP} 作为值。

      ETCD_LISTEN_PEER_URLS
      ETCD_LISTEN_CLIENT_URLS
      ETCD_INITIAL_ADVERTISE_PEER_URLS
      ETCD_ADVERTISE_CLIENT_URLS
    3. 如果您之前使用 member 系统作为 etcd 节点,您必须覆盖/etc/etcd/etcd.conf 文件中的当前值。
    4. 检查文件中的语法错误或缺少 IP 地址,否则 etcd 服务可能会失败:

      # vi ${PREFIX}/etcd.conf
  9. 在托管安装文件的节点上,更新/etc/ansible/hosts 清单文件中的 [etcd] 主机组。删除旧的 etcd 主机并添加新主机。
  10. 创建一个包含证书、示例配置文件和 catgz 文件并将其复制到新主机上:

    # tar -czvf /etc/etcd/generated_certs/${CN}.tgz -C ${PREFIX} .
    # scp /etc/etcd/generated_certs/${CN}.tgz ${CN}:/tmp/
修改新的 etcd 主机
  1. 安装 iptables-services 以提供 iptables 工具为 etcd 打开所需的端口:

    # yum install -y iptables-services
  2. 创建 OS_FIREWALL_ALLOW 防火墙规则以允许 etcd 进行通信:

    • 客户端的端口 2379/tcp
    • 端口 2380/tcp 用于对等通信

      # systemctl enable iptables.service --now
      # iptables -N OS_FIREWALL_ALLOW
      # iptables -t filter -I INPUT -j OS_FIREWALL_ALLOW
      # iptables -A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m tcp --dport 2379 -j ACCEPT
      # iptables -A OS_FIREWALL_ALLOW -p tcp -m state --state NEW -m tcp --dport 2380 -j ACCEPT
      # iptables-save | tee /etc/sysconfig/iptables
      注意

      在本例中,一个新的链 OS_FIREWALL_ALLOW 被创建,它是 OpenShift Container Platform 安装程序用于防火墙规则的标准命名。

      警告

      如果环境托管在 IaaS 环境中,请修改实例的安全组,以允许传入到这些端口的流量。

  3. 安装 etcd:

    # yum install -y etcd

    确定安装了版本 etcd-2.3.7-4.el7.x86_64 或更高版本,

  4. 通过删除 etcd pod 定义来确保 etcd 服务没有运行:

    # mkdir -p /etc/origin/node/pods-stopped
    # mv /etc/origin/node/pods/* /etc/origin/node/pods-stopped/
  5. 删除所有 etcd 配置和数据:

    # rm -Rf /etc/etcd/*
    # rm -Rf /var/lib/etcd/*
  6. 提取证书和配置文件:

    # tar xzvf /tmp/etcd0.example.com.tgz -C /etc/etcd/
  7. 在新主机上启动 etcd:

    # systemctl enable etcd --now
  8. 验证主机是否是集群的一部分以及当前的集群健康状况:

    • 如果使用 v2 etcd api,请运行以下命令:

      # etcdctl --cert-file=/etc/etcd/peer.crt \
                --key-file=/etc/etcd/peer.key \
                --ca-file=/etc/etcd/ca.crt \
                --peers="https://*master-0.example.com*:2379,\
                https://*master-1.example.com*:2379,\
                https://*master-2.example.com*:2379,\
                https://*etcd0.example.com*:2379"\
                cluster-health
      member 5ee217d19001 is healthy: got healthy result from https://192.168.55.12:2379
      member 2a529ba1840722c0 is healthy: got healthy result from https://192.168.55.8:2379
      member 8b8904727bf526a5 is healthy: got healthy result from https://192.168.55.21:2379
      member ed4f0efd277d7599 is healthy: got healthy result from https://192.168.55.13:2379
      cluster is healthy
    • 如果使用 v3 etcd api,请运行以下命令:

      # ETCDCTL_API=3 etcdctl --cert="/etc/etcd/peer.crt" \
                --key=/etc/etcd/peer.key \
                --cacert="/etc/etcd/ca.crt" \
                --endpoints="https://*master-0.example.com*:2379,\
                  https://*master-1.example.com*:2379,\
                  https://*master-2.example.com*:2379,\
                  https://*etcd0.example.com*:2379"\
                  endpoint health
      https://master-0.example.com:2379 is healthy: successfully committed proposal: took = 5.011358ms
      https://master-1.example.com:2379 is healthy: successfully committed proposal: took = 1.305173ms
      https://master-2.example.com:2379 is healthy: successfully committed proposal: took = 1.388772ms
      https://etcd0.example.com:2379 is healthy: successfully committed proposal: took = 1.498829ms
修改每个 OpenShift Container Platform master
  1. 修改每个 master 上 /etc/origin/master/master-config.yaml 文件的 etcClientInfo 部分中的 master 配置。将新的 etcd 主机添加到 OpenShift Container Platform 用来存储数据的 etcd 服务器列表中,并删除所有失败的 etcd 主机:

    etcdClientInfo:
      ca: master.etcd-ca.crt
      certFile: master.etcd-client.crt
      keyFile: master.etcd-client.key
      urls:
        - https://master-0.example.com:2379
        - https://master-1.example.com:2379
        - https://master-2.example.com:2379
        - https://etcd0.example.com:2379
  2. 重启 master API 服务:

    • 在每个 master 上:

      # master-restart api
      # master-restart controllers
      警告

      etcd 节点的数量必须是奇数,因此您必须至少添加两个主机。

  3. 如果使用 Flannel,修改每个 OpenShift Container Platform 主机上的 flanneld 服务配置 /etc/sysconfig/flanneld,使其包含新的 etcd 主机:

    FLANNEL_ETCD_ENDPOINTS=https://master-0.example.com:2379,https://master-1.example.com:2379,https://master-2.example.com:2379,https://etcd0.example.com:2379
  4. 重启 flanneld 服务:

    # systemctl restart flanneld.service

第 5 章 管理用户

5.1. 概述

用户是与 OpenShift 容器平台 API 交互的实体。这些可以是用于开发应用的开发人员,也可以是用于管理集群的管理员。可以将用户分配到组,这样可设置应用到组所有成员的权限。例如,您可以为组提供 API 访问权限,授予组 API 的所有成员访问权限。

本节介绍用户帐户的管理,包括如何在 OpenShift Container Platform 中创建新用户帐户以及如何删除它们。

5.2. 创建用户

创建用户的过程取决于配置的身份提供程序。默认情况下,OpenShift Container Platform 使用 DenyAll 身份供应商,该供应商拒绝访问所有用户名和密码。

以下过程创建新用户,然后为用户添加一个角色:

  1. 根据您的身份提供程序,创建用户帐户。这可能取决于用作身份提供程序配置一部分的 mappingmethod
  2. 为新用户指定所需的角色:

    # oc create clusterrolebinding <clusterrolebinding_name> \
      --clusterrole=<role> --user=<user>

    其中 --clusterrole 选项是所需的集群角色。例如,要授予新用户 cluster-admin 权限,用户可以访问集群中的所有内容:

    # oc create clusterrolebinding registry-controller \
      --clusterrole=cluster-admin --user=admin

    有关角色的说明和列表,请参阅架构指南中的集群角色和本地角色部分

作为集群管理员,您还可以管理每个用户的访问级别。

注意

根据身份提供商和定义的组结构,某些角色可能会自动提供给用户。如需更多信息,请参阅使用 LDAP 同步组部分

5.3. 查看用户和身份列表

OpenShift Container Platform 用户配置存储在 OpenShift Container Platform 中的多个位置。无论身份提供程序如何,OpenShift Container Platform 都会在内部存储诸如基于角色的访问控制(RBAC)信息和组成员资格等详情。要完全删除用户信息,除用户帐户外,还必须删除此数据。

在 OpenShift Container Platform 中,两种对象类型包含身份供应商之外的用户数据: useridentity

获取当前用户列表:

$ oc get user
NAME      UID                                    FULL NAME   IDENTITIES
demo     75e4b80c-dbf1-11e5-8dc6-0e81e52cc949               htpasswd_auth:demo

获取当前的身份列表:

$ oc get identity
NAME                  IDP NAME        IDP USER NAME   USER NAME   USER UID
htpasswd_auth:demo    htpasswd_auth   demo            demo        75e4b80c-dbf1-11e5-8dc6-0e81e52cc949

请注意两个对象类型之间匹配的 UID。如果在开始使用 OpenShift Container Platform 后尝试更改身份验证供应商,重叠的用户名将无法工作,因为身份列表中的条目仍会指向旧的身份验证方法。

5.4. 创建组

用户是向 OpenShift 容器平台发出请求的实体,但可以将用户组织到由一组用户组成的一个或多个组中。组可用于一次性管理许多用户,如授权策略或一次性向多个用户授予权限。

如果您的机构使用 LDAP,您可以将任何 LDAP 记录同步到 OpenShift Container Platform,以便您可以在一个位置配置组。这假设有关您的用户的信息位于 LDAP 服务器中。如需更多信息,请参阅使用 LDAP 同步组部分

如果未使用 LDAP,您可以使用以下步骤手动创建组。

要创建新组,请执行以下操作:

# oc adm groups new <group_name> <user1> <user2>

例如,要创建 west 组并放置 johnbetty 用户:

# oc adm groups new west john betty

要验证组是否已创建并列出与该组关联的用户,请运行以下命令:

# oc get groups
NAME      USERS
west      john, betty

后续步骤:

5.5. 管理用户和组标签

为用户或组群添加标签:

$ oc label user/<user_name> <label_name>=<label_value>

例如,如果用户名是 user,且标签是 level=gold

$ oc label user/theuser level=gold

删除标签:

$ oc label user/<user_name> <label_name>-

显示用户或组群的标签:

$ oc describe user/<user_name>

5.6. 删除用户

删除用户:

  1. 删除用户记录:

    $ oc delete user demo
    user "demo" deleted
  2. 删除用户身份。

    用户的身份与您所使用的身份供应商相关。从 oc get user 中的用户记录中获取供应商名称。

    在本例中,身份提供程序名称为 htpasswd_auth。该命令为:

    # oc delete identity htpasswd_auth:demo
    identity "htpasswd_auth:demo" deleted

    如果您跳过这一步,该用户将无法再次登录。

完成这些步骤后,当用户再次登录时,OpenShift Container Platform 中会创建一个新帐户。

如果您的用意是阻止用户再次登录(例如,如果员工已离开公司并且想要永久删除帐户),您也可以将用户从您配置的身份供应商的身份验证后端(如 htpasswd、kerberos 或其他)中删除。

例如,如果您使用 htpasswd,请使用用户名和密码删除为 OpenShift Container Platform 配置的htpasswd 文件中的条目。

对于诸如轻量级目录访问协议(LDAP)或红帽身份管理(IdM)等外部识别管理,请使用用户管理工具来删除用户条目。

第 6 章 管理项目

6.1. 概述

在 OpenShift Container Platform 中,项目用于分组和隔离相关的对象。作为管理员,您可以为开发人员授予某些项目的访问权限,让他们能够自行创建项目,并授予他们个别项目内的管理权限。

6.2. 自助配置项目

您可以允许开发人员创建自己的项目。有一个端点将根据模板调配项目当开发人员创建一个新项目时,Web 控制台和 oc new-project 命令会使用此端点。

6.2.1. 为新项目修改模板

API 服务器根据由master-config.yaml 文件的 projectRequestTemplate 参数标识的模板自动置备项目。如果未定义 参数,API 服务器会创建一个默认模板,该模板将使用请求的名称创建项目,并将请求用户分配到该项目的"admin"角色。

创建自己的自定义项目模板:

  1. 从当前的默认项目模板开始:

    $ oc adm create-bootstrap-project-template -o yaml > template.yaml
  2. 使用文本编辑器通过添加对象或修改现有对象来修改template.yaml 文件。
  3. 加载模板:

    $ oc create -f template.yaml -n default
  4. 修改master-config.yaml 文件以引用载入的模板:

    ...
    projectConfig:
      projectRequestTemplate: "default/project-request"
      ...

提交项目请求时,API 会替换模板中的以下参数:

参数描述

PROJECT_NAME

项目的名称。必需。

PROJECT_DISPLAYNAME

项目的显示名称。可以为空。

PROJECT_DESCRIPTION

项目的描述。可以为空。

PROJECT_ADMIN_USER

管理用户的用户名。

PROJECT_REQUESTING_USER

请求用户的用户名。

API 访问权限被授予具有 self-provisioner 角色和 self-provisioners 集群角色绑定的开发人员。默认情况下,所有通过身份验证的开发人员都可获得此角色。

6.2.2. 禁用自助置备

您可以防止经过身份验证的用户组自助置备新项目。

  1. 以具有cluster-admin 权限的用户身份登录。
  2. 查看 self-provisionersclusterrolebinding 用法。运行以下命令,然后查看 self-provisioners 部分中的主题。

    $ oc  describe clusterrolebinding.rbac self-provisioners
    
    Name:		self-provisioners
    Labels:		<none>
    Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
    Role:
      Kind:	ClusterRole
      Name:	self-provisioner
    Subjects:
      Kind	Name				Namespace
      ----	----				---------
      Group	system:authenticated:oauth
  3. 从组 system:authenticated:oauth 中删除 self-provisioner 集群角色。

    • 如果 self-provisioners 集群角色绑定只将 self-provisioner 角色绑定到 system:authenticated:oauth 组,请运行以下命令:

      $ oc patch clusterrolebinding.rbac self-provisioners -p '{"subjects": null}'
    • 如果 self-provisioners clusterrolebinding 将 self-provisioner 角色绑定到 system:authenticated:oauth 组以外的更多用户、组或 serviceaccounts,请运行以下命令:

      $ oc adm policy remove-cluster-role-from-group self-provisioner system:authenticated:oauth
  4. master-config.yaml 文件中设置 projectRequestMessage 参数值,以指示开发人员如何请求新项目。此参数值是一个字符串,当用户尝试自助置备项目时,该字符串将在 Web 控制台中显示给用户。您可以使用以下信息之一:

    • 要申请项目,请联系您的系统管理员,地址为 projectname@example.com
    • 要请求新项目,请填写位于 https://internal.example.com/openshift-project-request 的项目请求表单。

    YAML 文件示例

    ...
    projectConfig:
      ProjectRequestMessage: "message"
      ...

  5. 编辑 self-provisioners 集群角色绑定,以防止自动更新角色。自动更新会使集群角色重置为默认状态。

    • 从命令行更新角色绑定:

      1. 运行以下命令:

        $ oc edit clusterrolebinding.rbac self-provisioners
      2. 在显示的角色绑定中,将 rbac.authorization.kubernetes.io/autoupdate 参数值设置为 false,如下例所示:

        apiVersion: authorization.openshift.io/v1
        kind: ClusterRoleBinding
        metadata:
          annotations:
            rbac.authorization.kubernetes.io/autoupdate: "false"
        ...
    • 使用单个命令更新角色绑定:

      $ oc patch clusterrolebinding.rbac self-provisioners -p '{ "metadata": { "annotations": { "rbac.authorization.kubernetes.io/autoupdate": "false" } } }'

6.3. 使用 Node Selectors

节点选择器与带标签的节点搭配使用,以控制 pod 的放置。

6.3.1. 设置集群范围的默认 Node Selector

作为集群管理员,您可以设置集群范围的默认节点选择器来限制 pod 放置到特定的节点。

编辑/etc/origin/master/master-config.yaml 中的 master 配置文件,并为默认节点选择器添加一个值。这应用到没有指定 nodeSelector 值的所有项目中创建的 pod:

...
projectConfig:
  defaultNodeSelector: "type=user-node,region=east"
...

重启 OpenShift 服务以使更改生效:

# master-restart api
# master-restart controllers

6.3.2. 设置项目范围的 Node Selector

要使用节点选择器创建单独的项目,在创建项目时使用 --node-selector 选项。例如,如果您有一个带有多个区域的 OpenShift Container Platform 拓扑,您可以使用节点选择器限制特定的 OpenShift Container Platform 项目,仅将 pod 部署到特定区域的节点。

以下命令创建一个名为 myproject 的新项目,它规定 pod 部署到标有 user-nodeeast 的节点上:

$ oc adm new-project myproject \
    --node-selector='type=user-node,region=east'

运行此命令后,这便成为指定项目中包含的所有容器集的管理员设置节点选择器。

注意

虽然 new-project 子命令分别适用于 oc admoc,集群管理员和开发人员命令则分别使用 oc adm 命令使用节点选择器创建新项目。在自助置备项目时,项目开发人员无法使用 new-project 子命令。

使用 oc adm new-project 命令,在项目中添加 annotation 部分。您可以编辑项目,并更改 openshift.io/node-selector 值来覆盖默认值:

...
metadata:
  annotations:
    openshift.io/node-selector: type=user-node,region=east
...

您还可以使用以下命令覆盖现有项目命名空间的默认值:

# oc patch namespace myproject -p \
    '{"metadata":{"annotations":{"openshift.io/node-selector":"node-role.kubernetes.io/infra=true"}}}'

如果 openshift.io/node-selector 设置为空字符串(oc adm new-project --node-selector=""),则该项目将不会有管理员设置的节点选择器,即使已经设置了群集范围的默认值。这意味着,作为集群管理员,您可以设置默认值,将开发人员项目限制为节点的子集,并且仍然启用基础架构或其他项目来调度整个集群。

6.3.3. 开发人员指定的 Node Selectors

如果 OpenShift Container Platform 开发人员希望进一步限制节点,则可以在其 pod 配置上设置节点选择器。这将是项目节点选择器的补充,这意味着您仍然可以为具有节点选择器值的所有项目指定节点选择器值。

例如,如果使用上述注解(openshift.io/node-selector: type=user-node,region=east)创建了一个项目,并且开发人员在该项目中的 pod 上设置了另一个节点选择器,例如 clearance=classified,则该 pod 只会调度到具有所有三个标签(type=user-noderegion=eastclearance=classified)的节点上。如果在 pod 中设置 region=west,其 pod 会要求具有标签 region=eastregion=west 的节点,它们无法正常工作。pod 不会被调度,因为标签只能设置为一个值。

6.4. 每个用户限制自助项目数量

给定用户请求的自助置备项目数量可使用 ProjectRequestLimit准入控制插件来限制

重要

如果在 OpenShift Container Platform 3.1 或更早版本中使用 New Projects 修改模板中所述的流程创建了项目请求模板,则生成的模板不包括用于 ProjectRequestLimitConfig 的注解 openshift.io/requester: ${PROJECT_REQUESTING_USER}。您必须添加注解。

要为用户指定限值,必须在/etc/origin/master/master-config.yaml 的 master 配置文件内为插件指定配置。插件配置取用户标签选择器和关联的最大项目请求的列表作为值。

选择器按顺序评估。第一个与当前用户匹配的将用于确定项目的最大数量。如果没有指定选择器,则限制适用于所有用户。如果没有指定最多的项目数,则允许特定选择器有无限数量的项目。

以下配置为每个用户设置 2 个项目的全局限制,同时为带有标签为 level=admin 的用户允许 10 个项目为 level=advanced 和无限项目。

admissionConfig:
  pluginConfig:
    ProjectRequestLimit:
      configuration:
        apiVersion: v1
        kind: ProjectRequestLimitConfig
        limits:
        - selector:
            level: admin 1
        - selector:
            level: advanced 2
          maxProjects: 10
        - maxProjects: 2 3
1
对于选择器 level=admin,没有指定 maxProjects。这意味着具有此标签的用户最多没有项目请求。
2
对于选择器 level=advanced,最多允许 10 个项目。
3
对于第三个条目,没有指定选择器。这意味着它将应用到任何无法满足前两条规则的用户。由于规则按顺序评估,此规则应当在最后指定。
注意

管理用户和组标签提供了有关如何为用户和组添加、删除或显示标签的进一步指导。

更改完成后,重启 OpenShift Container Platform 以使更改生效。

# master-restart api
# master-restart controllers

6.5. 启用和限制自提供的项目 Per Service Account

默认情况下,服务帐户无法创建项目。但是,管理员可以为每个服务帐户启用此功能,给定服务帐户请求的自助置备项目数量可使用 ProjectRequestLimit准入控制插件来限制

注意

如果服务帐户被允许创建项目,则您无法信任上放置的任何标签,因为项目编辑器可以操作这些标签。

  1. 如果服务帐户不存在,在项目中创建服务帐户:

    $ oc create sa <sa_name>
  2. 以具有 cluster-admin 权限的用户身份,将 self-provisioner 集群角色添加到服务帐户中:

    $ oc adm policy \
        add-cluster-role-to-user self-provisioner \
        system:serviceaccount:<project>:<sa_name>
  3. 编辑/etc/origin/master/master-config.yaml 中的 master 配置文件,并将 ProjectRequestLimit 部分中的 maxProjectsForServiceAccounts 参数值设置为任何给定的 self-provisioner-enabled 服务帐户可以创建的最大项目数。

    例如,以下配置为每个服务帐户设置三个项目的全局限值:

    admissionConfig:
      pluginConfig:
        ProjectRequestLimit:
          configuration:
            apiVersion: v1
            kind: ProjectRequestLimitConfig
            maxProjectsForServiceAccounts: 3
  4. 保存更改后,重启 OpenShift Container Platform 以使更改生效:

    # master-restart api
    # master-restart controllers
  5. 以服务帐户身份登录并创建新项目,验证是否已应用您的更改。

    1. 使用其令牌作为服务帐户登录:

      $ oc login --token <token>
    2. 创建一个新项目

      $ oc new-project <project_name>

第 7 章 管理 Pod

7.1. 概述

本主题描述了 pod 的管理,包括限制其运行一次的持续时间,以及它们可以使用的带宽量。

7.2. 查看 Pod

您可以显示 pod 的用量统计,这些统计信息为容器提供了运行时环境。这些用量统计包括 CPU、内存和存储的消耗。

查看用量统计:

$ oc adm top pods
NAME                         CPU(cores)   MEMORY(bytes)
hawkular-cassandra-1-pqx6l   219m         1240Mi
hawkular-metrics-rddnv       20m          1765Mi
heapster-n94r4               3m           37Mi

查看带有标签的 pod 的用量统计:

$ oc adm top pod --selector=''

您必须选择过滤所基于的选择器(标签查询)。支持 ===!=

注意

您必须有 cluster-reader 权限才能查看用量统计。

注意

必须安装 metrics-server 才能查看用量统计。请参阅使用 Horizontal Pod Autoscalers 的要求

7.3. 限制 Pod 运行时

OpenShift Container Platform 依赖于运行一次 pod 来执行诸如部署 pod 或执行构建等任务。当 pod 是 RestartPolicyNever 的 pod 时。OnFailure

集群管理员可以使用 RunOnceDuration 准入控制插件来强制限制这些运行时间,因为 pod 可以处于活跃状态。时间限制过期后,集群将尝试主动终止这些 pod。具有此类限制的主要原因是防止构建等任务运行过长的时间。

7.3.1. 配置 RunOnceDuration 插件

插件配置应包含运行后 pod 的默认活跃截止时间。此截止时间在全局范围内执行,但可以根据每个项目替代。

admissionConfig:
  pluginConfig:
    RunOnceDuration:
      configuration:
        apiVersion: v1
        kind: RunOnceDurationConfig
        activeDeadlineSecondsOverride: 3600 1
....
1
以秒为单位为 run-once pod 指定全局默认值。

7.3.2. 指定每个项目自定义持续时间

除了为运行一次 pod 指定全局最长持续时间外,管理员还可以添加注解(openshift.io/active-deadline-seconds-override)到特定项目来覆盖全局默认值。

  • 对于新项目,在项目 specification.yaml 文件中定义注解。

    apiVersion: v1
    kind: Project
    metadata:
      annotations:
        openshift.io/active-deadline-seconds-override: "1000" 1
      name: myproject
    1
    将运行一次 pod 的默认活跃期限秒数覆盖为 1000 秒。请注意,覆盖的值必须以字符串形式指定。
  • 对于现有项目,

    • 运行 oc edit 并在编辑器中添加 openshift.io/active-deadline-seconds-override: 1000 注解。

      $ oc edit namespace <project-name>

      或者

    • 使用 oc patch 命令:

      $ oc patch namespace <project_name> -p '{"metadata":{"annotations":{"openshift.io/active-deadline-seconds-override":"1000"}}}'

7.3.2.1. 部署 Egress Router Pod

例 7.1. Egress 路由器的 Pod 定义示例

apiVersion: v1
kind: Pod
metadata:
  name: egress-1
  labels:
    name: egress-1
  annotations:
    pod.network.openshift.io/assign-macvlan: "true"
spec:
  containers:
  - name: egress-router
    image: openshift3/ose-egress-router
    securityContext:
      privileged: true
    env:
    - name: EGRESS_SOURCE 1
      value: 192.168.12.99
    - name: EGRESS_GATEWAY 2
      value: 192.168.12.1
    - name: EGRESS_DESTINATION 3
      value: 203.0.113.25
  nodeSelector:
    site: springfield-1 4
1
集群管理员保留的节点子网上的 IP 地址,供此 pod 使用。
2
值与节点本身使用的默认网关相同。
3
与 pod 的连接会被重定向到 203.0.113.25,源 IP 地址为 192.168.12.99
4
pod 只会部署到具有标签站点 Springfield-1 的节点

pod.network.openshift.io/assign-macvlan annotation 在主网络接口上创建一个 Macvlan 网络接口,然后将它移到 pod 的网络命名空间中,然后再启动 egress-router 容器。

注意

"true" 中保留引号。省略它们将导致错误。

pod 包含一个容器,它使用 openshift3/ose-egress-router 镜像,该容器会特权运行,以便它可以配置 Macvlan 接口并设置 iptables 规则。

环境变量告知 egress-router 镜像要使用的地址,它会将 Macvlan 接口配置为使用 EGRESS_SOURCE 作为其 IP 地址,并将 EGRESS_GATEWAY 作为其网关。

设置 NAT 规则,以便将到 pod 集群 IP 地址上的任何 TCP 或 UDP 端口的连接重定向到 EGRESS_DESTINATION 上的相同端口。

如果集群中只有部分节点能够声明指定的源 IP 地址并使用指定的网关,您可以指定一个 nodeNamenodeSelector 来表示哪些节点可以接受。

7.3.2.2. 部署 Egress 路由器服务

虽然严格来说并不是必需的,但您通常希望创建指向出口路由器的服务:

apiVersion: v1
kind: Service
metadata:
  name: egress-1
spec:
  ports:
  - name: http
    port: 80
  - name: https
    port: 443
  type: ClusterIP
  selector:
    name: egress-1

您的 pod 现在可以连接到此服务。使用保留的出口 IP 地址将其连接重新指向外部服务器的对应端口。

7.3.3. 使用 Egress Firewall 限制 Pod 访问

作为 OpenShift Container Platform 集群管理员,您可以使用出口策略限制集群中有些或所有 pod 可以访问的外部地址,以便:

  • pod 只能与内部主机通信,且无法启动到公共互联网的连接。

    或者,

  • pod 只能与公共互联网通信,且无法(在集群外)启动与内部主机的连接。

    或者,

  • pod 无法访问不应该联系的特定内部子网/主机。

例如,您可以使用不同的出口策略配置项目,允许 <project A> 访问指定的 IP 范围,但拒绝相同的 <project B> 访问。

项目管理员不能创建 EgressNetworkPolicy 对象,也不能编辑您在项目中创建的对象。另外还有一些其他限制可创建 EgressNetworkPolicy

  1. default 项目(以及通过 oc adm pod-network make-projects-global使全局化的任何其他项目)不能具有出口策略。
  2. 如果您将两个项目合并在一起(通过 oc adm pod-network join-projects),则无法在任何接合的项目中使用出口策略
  3. 项目不能有多个出口策略对象。

违反这些限制将导致项目的出口策略中断,并可能导致所有外部网络流量被丢弃。

7.3.3.1. 配置 Pod 访问限制

要配置 pod 访问限制,您必须使用 oc 命令或 REST API。您可以使用 oc [create|replace|delete] 操作 EgressNetworkPolicy 对象。api/swagger-spec/oapi-v1.json 文件包含对象的实际工作方式的 API 级别详情。

配置 pod 访问限制:

  1. 导航到您要影响的项目。
  2. 为 pod 限制策略创建 JSON 文件:

    # oc create -f <policy>.json
  3. 使用策略详情配置 JSON 文件。例如:

    {
        "kind": "EgressNetworkPolicy",
        "apiVersion": "v1",
        "metadata": {
            "name": "default"
        },
        "spec": {
            "egress": [
                {
                    "type": "Allow",
                    "to": {
                        "cidrSelector": "1.2.3.0/24"
                    }
                },
                {
                    "type": "Allow",
                    "to": {
                        "dnsName": "www.foo.com"
                    }
                },
                {
                    "type": "Deny",
                    "to": {
                        "cidrSelector": "0.0.0.0/0"
                    }
                }
            ]
        }
    }

    当在项目中添加上述示例时,它允许到 IP 范围 1.2.3.0/24 和域名 www.foo.com 的流量,但拒绝访问所有其他外部 IP 地址。(流量到其他 pod 不会受到影响,因为策略仅适用于外部流量。)

    EgressNetworkPolicy 中的规则按顺序检查,第一个匹配的规则生效。如果上面示例中的三个规则被撤销,则不允许流量进行 1.2.3.0/24www.foo.com,因为首先会检查 0.0.0.0/0 规则,它会匹配并拒绝所有流量。

    域名更新在 30 分钟内有所反映。在上例中,假设 www.foo.com 解析为 10.11.12.13,但之后改为 20.21.22.23。然后,OpenShift Container Platform 最多需要 30 分钟时间来适应这些 DNS 更新。

7.4. 限制 Pod 可用的带宽

您可以对 pod 应用服务质量流量控制,有效限制其可用带宽。出口流量(从 pod 传出)按照策略来处理,仅在超出配置的速率时丢弃数据包。入口流量(传入 pod 中)通过控制已排队数据包进行处理,以便有效地处理数据。您对 pod 应用的限制不会影响其他 pod 的带宽。

限制 pod 的带宽:

  1. 编写对象定义 JSON 文件,并使用 kubernetes.io/ingress-bandwidthkubernetes.io/egress-bandwidth 注解指定数据流量速度。例如,将 pod 出口和入口带宽限制为 10M/s:

    受限 Pod 对象定义

    {
        "kind": "Pod",
        "spec": {
            "containers": [
                {
                    "image": "openshift/hello-openshift",
                    "name": "hello-openshift"
                }
            ]
        },
        "apiVersion": "v1",
        "metadata": {
            "name": "iperf-slow",
            "annotations": {
                "kubernetes.io/ingress-bandwidth": "10M",
                "kubernetes.io/egress-bandwidth": "10M"
            }
        }
    }

  2. 使用对象定义创建 pod:

    $ oc create -f <file_or_dir_path>

7.5. 设置 Pod Disruption 预算

pod 中断预算是 Kubernetes API 的一部分,可以像其他对象类型一样使用 oc 命令进行管理。它们允许在操作过程中指定 pod 的安全约束,比如为维护而清空节点。

PodDisruptionBudget 是一个 API 对象,用于指定某一时间必须保持在线的副本的最小数量或百分比。在项目中进行这些设置对节点维护(比如缩减集群或升级集群)有益,而且仅在自愿驱除(而非节点失败)时遵从这些设置。

PodDisruptionBudget 对象的配置由以下关键部分组成:

  • 标签选择器,即一组 pod 的标签查询。
  • 可用性级别,指定必须同时可用的最小 pod 数量。

以下是 PodDisruptionBudget 资源的示例:

apiVersion: policy/v1beta1 1
kind: PodDisruptionBudget
metadata:
  name: my-pdb
spec:
  selector:  2
    matchLabels:
      foo: bar
  minAvailable: 2  3
1
PodDisruptionBudgetpolicy/v1beta1 API 组的一部分。
2
对一组资源进行的标签查询。matchLabelsmatchExpressions 的结果在逻辑上是接合的。
3
必须同时可用的最小 pod 数量。这可以是整数,也可以是指定百分比的字符串(例如 20%)。

如果您使用上述对象定义创建 YAML 文件,您可以使用以下内容将其添加到项目中:

$ oc create -f </path/to/file> -n <project_name>

您可以使用以下命令来检查所有项目的 pod 中断预算:

$ oc get poddisruptionbudget --all-namespaces

NAMESPACE         NAME          MIN-AVAILABLE   SELECTOR
another-project   another-pdb   4               bar=foo
test-project      my-pdb        2               foo=bar

如果系统中至少有 minAvailable pod 运行,PodDisruptionBudget 被视为健康。超过该限制的每个 pod 都可被驱除

注意

根据 pod 优先级和抢占设置,可能会即使 pod 中断预算要求而移除较低优先级 pod。

7.6. 配置关键 Pod

有许多核心组件(如 DNS)对于集群完全正常工作至关重要,但它们在常规集群节点而不是主节点上运行。如果一个关键附加组件被驱除,集群可能会停止正常工作。您可以通过在 pod 规格中添加 scheduler.alpha.kubernetes.io/critical-pod 注解以使 pod 成为关键 pod,以便 descheduler 不会删除这些 pod。

spec:
  template:
    metadata:
      name: critical-pod
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: "true"

第 8 章 管理网络

8.1. 概述

本主题描述了整个集群网络的管理,包括项目隔离和出站流量控制。

在管理 Pod 中,会讨论 Pod 级别的网络功能,如每个 pod 带宽限制。

8.2. 管理 Pod 网络

当您的集群配置为使用 ovs-multitenant SDN 时,您可以使用管理员 CLI 管理单独的 pod 覆盖网络用于项目。如有必要,请参阅配置 SDN 部分以了解插件配置步骤。

8.2.1. 加入项目网络

将项目加入现有项目网络:

$ oc adm pod-network join-projects --to=<project1> <project2> <project3>

在上例中,<project2><project3> 中的所有 pod 和服务现在都可以访问 <project1> 中的任何 pod 和服务,反之亦然。服务可以通过 IP 或完全限定 DNS 名称(<service>.<pod_namespace>.svc.cluster.local)进行访问。例如,若要访问项目 myproject 中名为 db 的服务,可使用 db.myproject.svc.cluster.local

另外,您可以使用 --selector=<project_selector> 选项而不是指定具体的项目名称。

验证您接合的网络:

$ oc get netnamespaces

然后查看 NETID 列。同一 pod-network 中的项目将具有相同的 NetID。

8.3. 隔离项目网络

要在集群中隔离项目网络,反之亦然,请运行:

$ oc adm pod-network isolate-projects <project1> <project2>

在上例中,<project1><project2> 中的所有 pod 和服务都无法从集群中的其他非全局项目访问任何 pod 和服务,反之亦然。

另外,您可以使用 --selector=<project_selector> 选项而不是指定具体的项目名称。

8.3.1. 使项目网络全局化

允许项目访问集群中的所有 pod 和服务,反之亦然:

$ oc adm pod-network make-projects-global <project1> <project2>

在上例中,<project1><project2> 中的所有 pod 和服务现在都可以访问集群中的任何 pod 和服务,反之亦然。

另外,您可以使用 --selector=<project_selector> 选项而不是指定具体的项目名称。

8.4. 为路由和入口对象禁用主机名集合

在 OpenShift Container Platform 中,路由和入口对象的主机名冲突攻击会被默认启用。这意味着没有 cluster-admin 角色的用户只能在创建时在路由或入口对象中设置主机名,之后无法更改它。但是,您可以为部分或所有用户放松对路由和入口对象的限制。

警告

由于 OpenShift Container Platform 使用对象创建时间戳来确定给定主机名的最旧的路由或入口对象,如果旧的路由更改了主机名,或者使用了 ingress 对象,则路由或入口对象可能会劫持较新路由的主机名。

作为 OpenShift Container Platform 集群管理员,您可以在创建后编辑路由中的主机名。您还可以创建一个角色来允许特定用户这样做:

$ oc create clusterrole route-editor --verb=update --resource=routes.route.openshift.io/custom-host

然后您可以将新角色绑定到用户:

$ oc adm policy add-cluster-role-to-user route-editor user

您还可以对入口对象禁用主机名冲突检测。这样,没有 cluster-admin 角色的用户可在创建后编辑 ingress 对象的主机名。这对依赖于 Kubernetes 行为的 OpenShift Container Platform 安装很有用,包括允许编辑 ingress 对象中的主机名。

  1. master.yaml 文件中添加以下内容:

    admissionConfig:
      pluginConfig:
        openshift.io/IngressAdmission:
          configuration:
            apiVersion: v1
            allowHostnameChanges: true
            kind: IngressAdmissionConfig
          location: ""
  2. 重启 master 服务以使更改生效:

    $ master-restart api
    $ master-restart controllers

8.5. 控制出口流量

作为集群管理员,您可以在主机级别上为特定节点分配多个静态 IP 地址。如果应用开发人员需要其应用服务的专用 IP 地址,可以在他们用于询问防火墙访问的过程中请求一个 IP 地址。然后,他们可以从开发人员的项目部署出口路由器,使用部署配置中的 nodeSelector 来确保 pod 驻留在带有预分配的静态 IP 地址的主机上。

egress pod 的部署声明了其中一个源 IP、受保护服务的目标 IP,以及用于到达目的地的网关 IP。部署 pod 后,您可以创建一个服务来访问出口路由器 pod,然后将该源 IP 添加到公司防火墙中。然后,开发人员具有访问在其项目中创建的出口路由器服务的信息,例如 service.project.cluster.domainname.com

当开发人员需要访问外部的防火墙服务时,他们可以在其应用程序中调用出口路由器 pod 的服务(service.project.cluster.domainname.com)(如 JDBC 连接信息),而不是实际受保护的服务 URL。

您还可以为项目分配静态 IP 地址,确保来自指定项目的所有传出外部连接都具有可识别的来源。这与默认出口路由器不同,后者用于将流量发送到特定目的地。

如需更多信息,请参阅为外部项目流量启用修复 IP 部分。

作为 OpenShift Container Platform 集群管理员,您可以使用以下方法控制出口流量:

防火墙
通过利用出口防火墙,您可以强制执行可接受的出站流量策略,以便特定端点或 IP 范围(子网)成为 OpenShift Container Platform 中可以与之通信的动态端点(pod)的唯一可接受目标。
路由器
通过使用出口路由器,您可以创建可识别的服务将流量发送到某些目的地,确保流量被视为来自已知来源的流量。这有助于提高安全性,因为它允许您保护外部数据库,以便只有命名空间中的特定 pod 可以与将流量代理到数据库的服务(出口路由器)进行通信。
iptables
除了上述 OpenShift Container Platform 内部解决方案外,还可以创建应用于传出流量的 iptables 规则。这些规则允许比出口防火墙更多的可能性,但不能限制为特定的项目。

8.6. 使用 Egress Firewall 来限制对外部资源的访问

作为 OpenShift Container Platform 集群管理员,您可以使用出口防火墙策略限制集群内的部分或所有 Pod 可以访问的外部 IP 地址。出口防火墙策略支持以下情况:

  • pod 只能连接到内部主机,且无法启动到公共互联网的连接。
  • pod 只能连接到公共互联网,且无法启动到 OpenShift Container Platform 集群外的内部主机的连接。
  • pod 无法访问指定的内部子网或主机。

可通过以 CIDR 格式或指定 DNS 名称指定 IP 地址范围来设置出口策略。例如:您可以允许 <project_A> 访问指定的 IP 范围,但拒绝对 <project_B> 相同的访问。或者,您可以限制应用程序开发人员从(Python)pip 镜像进行更新,并强制更新仅来自批准的源。

小心

您必须具有ovs-multitenant,以便通过出口策略限制 pod 访问。

如果您使用 ovs-multitenant 插件,egress 策略仅与每个项目的一个策略兼容,且无法用于共享网络的项目,如全局项目。

项目管理员不能创建 EgressNetworkPolicy 对象,也不能编辑您在项目中创建的对象。另外还有一些其他限制可创建 EgressNetworkPolicy

  • default 项目(以及通过 oc adm pod-network make-projects-global使全局化的任何其他项目)不能具有出口策略。
  • 如果您将两个项目合并在一起(通过 oc adm pod-network join-projects),则无法在任何接合的项目中使用出口策略
  • 项目不能有多个出口策略对象。

违反这些限制会导致项目的出口策略出现问题,并可能导致所有外部网络流量被丢弃。

使用 oc 命令或 REST API 配置出口策略。您可以使用 oc [create|replace|delete] 操作 EgressNetworkPolicy 对象。api/swagger-spec/oapi-v1.json 文件包含对象的实际工作方式的 API 级别详情。

配置出口策略:

  1. 导航到您要影响的项目。
  2. 使用您要使用的策略配置创建一个 JSON 文件,如下例所示:

    {
        "kind": "EgressNetworkPolicy",
        "apiVersion": "v1",
        "metadata": {
            "name": "default"
        },
        "spec": {
            "egress": [
                {
                    "type": "Allow",
                    "to": {
                        "cidrSelector": "1.2.3.0/24"
                    }
                },
                {
                    "type": "Allow",
                    "to": {
                        "dnsName": "www.foo.com"
                    }
                },
                {
                    "type": "Deny",
                    "to": {
                        "cidrSelector": "0.0.0.0/0"
                    }
                }
            ]
        }
    }

    当上例添加到项目中时,它允许到 IP 范围 1.2.3.0/24 和域名 www.foo.com 的流量,但拒绝访问所有其他外部 IP 地址。流量到其他 pod 的流量不受影响,因为策略仅适用于外部流量

    EgressNetworkPolicy 中的规则按顺序检查,第一个匹配的规则生效。如果上面示例中的三个规则被撤销,则不允许流量进行 1.2.3.0/24www.foo.com,因为首先会检查 0.0.0.0/0 规则,它会匹配并拒绝所有流量。

    域名更新根据本地非授权服务器返回的域的 TTL(time to live)值进行轮询。pod 也应该在必要时从同一本地名称服务器解析域,否则出口网络策略控制器和 pod 感知的域的 IP 地址会有所不同,出口网络策略可能无法按预期强制执行。由于出口网络策略控制器和 pod 会异步轮询相同的本地名称服务器,因此可能会有一个竞争条件,pod 在出口控制器前可能会获取更新的 IP。由于此限制,仅建议在 EgressNetworkPolicy 中使用域名来更改 IP 地址不常用的域。

    注意

    出口防火墙始终允许 pod 访问 pod 所在的用于 DNS 解析的节点的外部接口。如果您的 DNS 解析不是由本地节点上的某个内容处理,那么如果您在 pod 中使用域名,则需要添加出口防火墙规则,以允许访问 DNS 服务器的 IP 地址。

  3. 使用 JSON 文件创建 EgressNetworkPolicy 对象:

    $ oc create -f <policy>.json
小心

通过创建路由公开服务将忽略 EgressNetworkPolicy。出口网络策略服务端点过滤在节点 kubeproxy 中完成。当涉及路由器时,会绕过 kubeproxy,且不会应用出口网络策略强制。管理员可以通过限制对创建路由的访问来防止此缺陷。

8.6.1. 使用 Egress 路由器允许外部资源识别 Pod 流量

OpenShift Container Platform 出口路由器使用一个私有源 IP 地址运行一项服务,该服务将流量重定向到指定的远程服务器。服务允许容器集与设置为仅允许从白名单 IP 地址访问的服务器进行通信。

重要

出口路由器并不适用于每个传出的连接。创建大量出口路由器可以推送网络硬件的限制。例如,为每个项目或应用创建出口路由器可能会超出网络接口可以处理的本地 MAC 地址数,然后再回退到软件中的 MAC 地址过滤。

重要

目前,出口路由器与 Amazon AWS、Azure Cloud 或其他不支持第 2 层操作的云平台不兼容,因为它们与 macvlan 流量不兼容。

部署注意事项

Egress 路由器向节点的主网络接口添加第二个 IP 地址和 MAC 地址。如果您没有在裸机上运行 OpenShift Container Platform,您可能需要配置虚拟机监控程序或云供应商来允许额外的地址。

Red Hat OpenStack Platform

如果要在 Red Hat OpenStack Platform 上部署 OpenShift Container Platform,则需要将 OpenStack 环境中的 IP 和 MAC 地址列入白名单,否则通信将失败

neutron port-update $neutron_port_uuid \
  --allowed_address_pairs list=true \
  type=dict mac_address=<mac_address>,ip_address=<ip_address>
Red Hat Enterprise Virtualization
如果您使用 Red Hat Enterprise Virtualization,您应该将 EnableMACAntiSpoofingFilterRules 设置为 false
VMware vSphere
如果您使用 VMware vSphere,请参阅 VMWare 文档来保证 vSphere 标准交换器的安全。通过从 vSphere Web 客户端中选择主机的虚拟交换机来查看和更改 VMWare vSphere 默认设置。

具体来说,请确保启用了以下功能:

出口路由器模式

出口路由器可以三种不同的模式运行:重定向模式、HTTP 代理模式和 DNS 代理模式。重定向模式可用于除 HTTP 和 HTTPS 以外的所有服务。对于 HTTP 和 HTTPS 服务,请使用 HTTP 代理模式。对于使用 IP 地址或域名的基于 TCP 的服务,请使用 DNS 代理模式。

8.6.1.1. 以重定向模式部署 Egress Router Pod

在重定向模式中,出口路由器设置 iptables 规则,将流量从其自身 IP 地址重定向到一个或多个目标 IP 地址。希望使用保留源 IP 地址的客户端 pod 必须修改来连接到出口路由器,而不是直接连接到目标 IP。

  1. 使用以下内容创建 pod 配置:

    apiVersion: v1
    kind: Pod
    metadata:
      name: egress-1
      labels:
        name: egress-1
      annotations:
        pod.network.openshift.io/assign-macvlan: "true" 1
    spec:
      initContainers:
      - name: egress-router
        image: registry.redhat.io/openshift3/ose-egress-router
        securityContext:
          privileged: true
        env:
        - name: EGRESS_SOURCE 2
          value: 192.168.12.99/24
        - name: EGRESS_GATEWAY 3
          value: 192.168.12.1
        - name: EGRESS_DESTINATION 4
          value: 203.0.113.25
        - name: EGRESS_ROUTER_MODE 5
          value: init
      containers:
      - name: egress-router-wait
        image: registry.redhat.io/openshift3/ose-pod
      nodeSelector:
        site: springfield-1 6
    1
    在主网络接口上创建一个 Macvlan 网络接口,并将它移到 pod 的网络项目中,然后再启动 egress-router 容器。在 "true" 中保留引号。省略它们会导致错误。要在主接口以外的网络接口上创建 Macvlan 接口,请将注解值设置为该接口的名称。例如: eth1
    2
    节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。另外,您可以包括子网长度 /24 后缀,以便可以设置到本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用 EGRESS_GATEWAY 变量指定的主机,子网中没有其他主机。
    3
    值与节点使用的默认网关相同。
    4
    用于将流量定向到的外部服务器。使用这个示例,到 pod 的连接会被重定向到 203.0.113.25,源 IP 地址为 192.168.12.99。
    5
    这将告知出口路由器镜像,它被部署为"init 容器"。以前的 OpenShift Container Platform 版本(和出口路由器镜像)不支持这个模式,它必须作为普通容器运行。
    6
    pod 仅部署到带有 site=springfield-1 标签的节点。
  2. 使用上述定义创建 pod:

    $ oc create -f <pod_name>.json

    检查 pod 是否已创建:

    $ oc get pod <pod_name>
  3. 通过创建服务指向出口路由器,确保其他 pod 可以找到 pod 的 IP 地址:

    apiVersion: v1
    kind: Service
    metadata:
      name: egress-1
    spec:
      ports:
      - name: http
        port: 80
      - name: https
        port: 443
      type: ClusterIP
      selector:
        name: egress-1

    您的 pod 现在可以连接到此服务。使用保留的出口 IP 地址将其连接重新指向外部服务器的对应端口。

出口路由器设置由从 openshift3/ose-egress-router 镜像创建的"init 容器"执行,该容器则特权运行,以便它配置 Macvlan 接口并设置 iptables 规则。在设置 iptables 规则后,它会退出,在 pod 被终止前,openshift3/ose-pod 容器会运行(不做任何操作)。

环境变量告知 egress-router 镜像要使用的地址,它会将 Macvlan 接口配置为使用 EGRESS_SOURCE 作为其 IP 地址,并将 EGRESS_GATEWAY 作为其网关。

设置 NAT 规则,以便将到 pod 集群 IP 地址上的任何 TCP 或 UDP 端口的连接重定向到 EGRESS_DESTINATION 上的相同端口。

如果集群中只有部分节点能够声明指定的源 IP 地址并使用指定的网关,您可以指定一个 nodeNamenodeSelector 来表示哪些节点可以接受。

8.6.1.2. 重定向至多个目标

在上例中,任何端口上的出口 pod(或其对应服务)的连接都会重定向到单个目标 IP。您还可以根据端口配置不同的目标 IP:

apiVersion: v1
kind: Pod
metadata:
  name: egress-multi
  labels:
    name: egress-multi
  annotations:
    pod.network.openshift.io/assign-macvlan: "true"
spec:
  initContainers:
  - name: egress-router
    image: registry.redhat.io/openshift3/ose-egress-router
    securityContext:
      privileged: true
    env:
    - name: EGRESS_SOURCE 1
      value: 192.168.12.99/24
    - name: EGRESS_GATEWAY
      value: 192.168.12.1
    - name: EGRESS_DESTINATION 2
      value: |
        80   tcp 203.0.113.25
        8080 tcp 203.0.113.26 80
        8443 tcp 203.0.113.26 443
        203.0.113.27
    - name: EGRESS_ROUTER_MODE
      value: init
  containers:
  - name: egress-router-wait
    image: registry.redhat.io/openshift3/ose-pod
1
节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。另外,您可以包括子网长度 /24 后缀,以便可以设置到本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用 EGRESS_GATEWAY 变量指定的主机,子网中没有其他主机。
2
EGRESS_DESTINATION 将 YAML 语法用作其值,可以是多行字符串。如需更多信息,请参阅以下内容。

EGRESS_DESTINATION 的每一行可以是以下三种类型之一:

  • <port> <protocol> <IP_address> - 这表示,到给定 <port> 的传入连接应该被重新定向到给定的 <IP_address> 上的同一端口。<protocol>tcpudp。在上例中,第一行将流量从本地端口 80 重定向到 203.0.113.25 上的端口 80。
  • <port> <protocol> <IP_address> <remote_port> - 如上所示,除了连接被重新定向到 <IP_address> 上的不同 <remote_port> 外。在上例中,第二行和第三行将本地端口 8080 和 8443 重定向到 203.0.113.26 上的远程端口 80 和 443。
  • <fallback_IP_address> - 如果 EGRESS_DESTINATION 的最后一行是一个 IP 地址,则其它端口上的所有连接都将被重定向到该 IP 地址上的对应端口(例如,上例中的 203.0.113.27)。如果没有回退 IP 地址,则其他端口上的连接将直接被拒绝。)

8.6.1.3. 使用 ConfigMap 指定 EGRESS_DESTINATION

对于大量或经常更改的目标映射集合,您可以使用 ConfigMap 外部维护列表,并让出口路由器 pod 从那里读取它。这具有项目管理员能够编辑 ConfigMap 的优点,而他们可能无法直接编辑 Pod 定义,因为它包含特权容器。

  1. 创建包含 EGRESS_DESTINATION 数据的文件:

    $ cat my-egress-destination.txt
    # Egress routes for Project "Test", version 3
    
    80   tcp 203.0.113.25
    
    8080 tcp 203.0.113.26 80
    8443 tcp 203.0.113.26 443
    
    # Fallback
    203.0.113.27

    请注意,您可以将空行和注释放在此文件中

  2. 从文件创建 ConfigMap 对象:

    $ oc delete configmap egress-routes --ignore-not-found
    $ oc create configmap egress-routes \
      --from-file=destination=my-egress-destination.txt

    此处 egress-routes 是所创建的 ConfigMap 对象的名称,my-egress-destination.txt 是从中读取数据的文件的名称。

  3. 如上所示创建出口路由器 pod 定义,但在 environment 部分中为 EGRESS_DESTINATION 指定 ConfigMap:

        ...
        env:
        - name: EGRESS_SOURCE 1
          value: 192.168.12.99/24
        - name: EGRESS_GATEWAY
          value: 192.168.12.1
        - name: EGRESS_DESTINATION
          valueFrom:
            configMapKeyRef:
              name: egress-routes
              key: destination
        - name: EGRESS_ROUTER_MODE
          value: init
        ...
    1
    节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。另外,您可以包括子网长度 /24 后缀,以便可以设置到本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用 EGRESS_GATEWAY 变量指定的主机,子网中没有其他主机。
注意

当 ConfigMap 更改时,出口路由器不会自动更新。重新启动容器集以获取更新。

8.6.1.4. 部署 Egress Router HTTP 代理 Pod

HTTP 代理模式中,出口路由器作为 HTTP 代理在端口 8080 上运行。这只适用于与基于 HTTP 或基于 HTTPS 的服务进行通信的客户端,但通常需要较少的更改才能使客户端容器集正常工作。通过设置环境变量,可以告知程序使用 HTTP 代理。

  1. 使用以下示例创建 pod:

    apiVersion: v1
    kind: Pod
    metadata:
      name: egress-http-proxy
      labels:
        name: egress-http-proxy
      annotations:
        pod.network.openshift.io/assign-macvlan: "true" 1
    spec:
      initContainers:
      - name: egress-router-setup
        image: registry.redhat.io/openshift3/ose-egress-router
        securityContext:
          privileged: true
        env:
        - name: EGRESS_SOURCE 2
          value: 192.168.12.99/24
        - name: EGRESS_GATEWAY 3
          value: 192.168.12.1
        - name: EGRESS_ROUTER_MODE 4
          value: http-proxy
      containers:
      - name: egress-router-proxy
        image: registry.redhat.io/openshift3/ose-egress-http-proxy
        env:
        - name: EGRESS_HTTP_PROXY_DESTINATION 5
          value: |
            !*.example.com
            !192.168.1.0/24
            *
    1
    在主网络接口上创建一个 Macvlan 网络接口,然后将它移到 pod 的网络项目中,然后再启动 egress-router 容器。在 "true" 中保留引号。省略它们会导致错误。
    2
    节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。另外,您可以包括子网长度 /24 后缀,以便可以设置到本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用 EGRESS_GATEWAY 变量指定的主机,子网中没有其他主机。
    3
    值与节点本身使用的默认网关相同。
    4
    这会告知出口路由器镜像,它被部署为 HTTP 代理的一部分,因此不应设置 iptables 重定向规则。
    5
    一个字符串或 YAML 多行字符串指定如何配置代理。请注意,这作为 HTTP 代理容器中的环境变量指定,而不是与 init 容器中的其他环境变量指定。

    您可以为 EGRESS_HTTP_PROXY_DESTINATION 值指定以下任意一项。您还可以使用 *,即"允许连接到所有远程目的地"。配置中的每行都指定允许或者拒绝的连接组:

    • IP 地址(例如 192.168.1.1)允许连接到该 IP 地址。
    • CIDR 范围(如 192.168.1.0/24)允许连接到那个 CIDR 范围。
    • 主机名(如 www.example.com)允许代理到该主机。
    • 前面带有 *. (例如 *.example.com)的域名允许代理到那个域及其所有子域。
    • ! 后跟上述任意一个连接,而不是允许连接
    • 如果最后一行是 *,则允许任何未被拒绝的任何内容。否则,未被允许的任何内容都将被拒绝。
  2. 通过创建服务指向出口路由器,确保其他 pod 可以找到 pod 的 IP 地址:

    apiVersion: v1
    kind: Service
    metadata:
      name: egress-1
    spec:
      ports:
      - name: http-proxy
        port: 8080 1
      type: ClusterIP
      selector:
        name: egress-1
    1
    确保 http 端口始终设置为 8080
  3. 通过设置 http_proxyhttps_proxy 变量,将客户端 pod(而不是出口代理 pod)配置为使用 HTTP 代理:

        ...
        env:
        - name: http_proxy
          value: http://egress-1:8080/ 1
        - name: https_proxy
          value: http://egress-1:8080/
        ...
    1
    第 2 步中创建的服务。
    注意

    所有设置都不需要使用 http_proxyhttps_proxy 环境变量。如果以上内容没有创建可以正常工作设置,请查阅 pod 中运行的工具或软件的文档。

您还可以使用 ConfigMap 指定 EGRESS_HTTP_PROXY_DESTINATION,这与上面的重定向出口路由器示例类似

8.6.1.5. 部署 Egress Router DNS 代理 Pod

DNS 代理模式中,出口路由器作为基于 TCP 服务的 DNS 代理运行,从其自身 IP 地址到一个或多个目标 IP 地址。希望使用保留的客户端 pod,必须修改源 IP 地址以连接到出口路由器,而不是直接连接到目标 IP。这样可确保外部目的地将流量视为来自已知来源的流量。

  1. 使用以下示例创建 pod:

    apiVersion: v1
    kind: Pod
    metadata:
      name: egress-dns-proxy
      labels:
        name: egress-dns-proxy
      annotations:
        pod.network.openshift.io/assign-macvlan: "true" 1
    spec:
      initContainers:
      - name: egress-router-setup
        image: registry.redhat.io/openshift3/ose-egress-router
        securityContext:
          privileged: true
        env:
        - name: EGRESS_SOURCE 2
          value: 192.168.12.99/24
        - name: EGRESS_GATEWAY 3
          value: 192.168.12.1
        - name: EGRESS_ROUTER_MODE 4
          value: dns-proxy
      containers:
      - name: egress-dns-proxy
        image: registry.redhat.io/openshift3/ose-egress-dns-proxy
        env:
        - name: EGRESS_DNS_PROXY_DEBUG 5
          value: "1"
        - name: EGRESS_DNS_PROXY_DESTINATION 6
          value: |
            # Egress routes for Project "Foo", version 5
    
            80  203.0.113.25
    
            100 example.com
    
            8080 203.0.113.26 80
    
            8443 foobar.com 443
    1
    使用 pod.network.openshift.io/assign-macvlan annotation 在主网络接口上创建一个 Macvlan 网络接口,然后将它移到 pod 的网络命名空间中,然后再启动 egress-router-setup 容器。在 "true" 中保留引号。省略它们会导致错误。
    2
    节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。另外,您可以包括子网长度 /24 后缀,以便可以设置到本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用 EGRESS_GATEWAY 变量指定的主机,子网中没有其他主机。
    3
    值与节点本身使用的默认网关相同。
    4
    这会告知出口路由器镜像,它被部署为 DNS 代理的一部分,因此不应设置 iptables 重定向规则。
    5
    可选。设置此变量将在 stdout 上显示 DNS 代理日志输出。
    6
    这会将 YAML 语法用于多行字符串。详情请查看以下。
    注意

    EGRESS_DNS_PROXY_DESTINATION 的每一行都可通过以下两种方式之一设置:

    • <port> <remote_address> - 这表示到给定 <port> 的传入连接应该代理到给定 <remote_address> 上的同一 TCP 端口中。<remote_address> 可以是 IP 地址或 DNS 名称。如果是 DNS 名称,DNS 解析会在运行时进行。在上例中,第一行将 TCP 流量代理从本地端口 80 到 203.0.113.25 上的端口 80。第二行将 TCP 流量代理从本地端口 100 至 example.com 上的端口 100。
    • <port> <remote_address> <remote_port> - 如上所示,除了连接代理到 <remote_address> 上的不同 <remote_port> 外。在上例中,第三行代理本地端口 8080 到 203.0.113.26 上的远程端口 80,第四行将本地端口 8443 代理到 foobar.com 上的远程端口 443。
  2. 通过创建服务指向出口路由器,确保其他 pod 可以找到 pod 的 IP 地址:

    apiVersion: v1
    kind: Service
    metadata:
      name: egress-dns-svc
    spec:
      ports:
      - name: con1
        protocol: TCP
        port: 80
        targetPort: 80
      - name: con2
        protocol: TCP
        port: 100
        targetPort: 100
      - name: con3
        protocol: TCP
        port: 8080
        targetPort: 8080
      - name: con4
        protocol: TCP
        port: 8443
        targetPort: 8443
      type: ClusterIP
      selector:
        name: egress-dns-proxy

    Pod 现在可以连接至此服务。使用保留的出口 IP 地址将其连接代理到外部服务器的对应端口。

您还可以使用 ConfigMap 指定 EGRESS_DNS_PROXY_DESTINATION,这与上面的重定向出口路由器示例类似

8.6.1.6. 为 Egress Router Pod 启用故障切换

通过使用复制控制器,您可以确保始终有一个出口路由器 pod 副本以防止停机。

  1. 使用以下方法创建复制控制器配置文件:

    apiVersion: v1
    kind: ReplicationController
    metadata:
      name: egress-demo-controller
    spec:
      replicas: 1 1
      selector:
        name: egress-demo
      template:
        metadata:
          name: egress-demo
          labels:
            name: egress-demo
          annotations:
            pod.network.openshift.io/assign-macvlan: "true"
        spec:
          initContainers:
          - name: egress-demo-init
            image: registry.redhat.io/openshift3/ose-egress-router
            env:
            - name: EGRESS_SOURCE 2
              value: 192.168.12.99/24
            - name: EGRESS_GATEWAY
              value: 192.168.12.1
            - name: EGRESS_DESTINATION
              value: 203.0.113.25
            - name: EGRESS_ROUTER_MODE
              value: init
            securityContext:
              privileged: true
          containers:
          - name: egress-demo-wait
            image: registry.redhat.io/openshift3/ose-pod
          nodeSelector:
            site: springfield-1
    1
    确保 replicas 设置为 1,因为任何时候都只能使用给定的 EGRESS_SOURCE 值。这意味着,在一个标签为 site=springfield-1 的节点上,只有一个路由器副本正在运行。
    2
    节点所在的物理网络中的 IP 地址,由集群管理员保留给此 pod 使用。另外,您可以包括子网长度 /24 后缀,以便可以设置到本地子网的正确路由。如果没有指定子网长度,则出口路由器只能访问使用 EGRESS_GATEWAY 变量指定的主机,子网中没有其他主机。
  2. 使用定义创建 pod:

    $ oc create -f <replication_controller>.json
  3. 要验证,请检查是否创建了复制控制器 pod:

    $ oc describe rc <replication_controller>

8.6.2. 使用 iptables 规则限制对外部资源的访问

有些集群管理员可能想要对传出流量执行操作,这些流量不适合 EgressNetworkPolicy 或出口路由器。在某些情况下,这可以通过直接创建 iptables 规则来完成。

例如,您可以创建记录到特定目的地的流量的规则,或者防止每秒超过特定数量的传出连接。

OpenShift Container Platform 不提供自动添加自定义 iptables 规则的方法,但它提供了一个可供管理员手动添加此类规则的位置。启动时每个节点都会在 filter 表中创建一个名为 OPENSHIFT-ADMIN-OUTPUT-RULES 的空链(假设链尚不存在)。管理员添加至该链的任何规则都将应用到从 pod 到集群外目的地的所有流量(而不是任何其他流量)。

使用此功能时需要注意以下几点:

  1. 您需要确保在每个节点上创建规则 ; OpenShift Container Platform 不提供自动实现该操作的任何方法。
  2. 规则不适用于通过出口路由器退出集群的流量,它们应用 EgressNetworkPolicy 规则后运行(因此不会看到被 EgressNetworkPolicy拒绝的流量)。
  3. 处理从 pod 到节点或 pod 到 master 的连接非常复杂,因为节点同时具有"外部"IP 地址和"内部"SDN IP 地址。因此,一些 pod 到节点/master 流量可能会通过此链,但其他 pod 到节点/master 流量可能会绕过它。

8.7. 为外部项目流量启用静态 IP

作为集群管理员,您可以为项目分配特定的静态 IP 地址,以便流量可在外部轻松识别。这与默认出口路由器不同,后者用于将流量发送到特定目的地。

可识别的 IP 流量通过确保原始数据可见来提高集群安全性。启用后,来自指定项目的所有传出外部连接将共享相同的固定源 IP,这意味着任何外部资源都可以识别流量。

与出口路由器不同,这可能受 EgressNetworkPolicy 防火墙规则的约束。

注意

为集群中的项目分配静态 IP 地址需要 SDN 使用 ovs-networkpolicyovs-multitenant 网络插件。

注意

如果您以多租户模式使用 OpenShift SDN,则无法将出口 IP 地址与与其关联的项目附加到另一个命名空间的任何命名空间一起使用。例如,如果运行 oc adm pod-network join-projects --to=project1 project2 命令 project1project2 加入,则这两个项目都不能使用出口 IP 地址。如需更多信息,请参阅 BZ#1645577

启用静态源 IP:

  1. 使用所需 IP 更新 NetNamespace

    $ oc patch netnamespace <project_name> -p '{"egressIPs": ["<IP_address>"]}'

    例如,将 MyProject 项目分配给 IP 地址 192.168.1.100:

    $ oc patch netnamespace MyProject -p '{"egressIPs": ["192.168.1.100"]}'

    egressIPs 字段是一个数组。您可以将 egressIPs 设置为不同节点上的两个或多个 IP 地址,以提供高可用性。如果设置了多个出口 IP 地址,Pod 会将列表中的第一个 IP 用于出口,但如果托管该 IP 地址的节点失败,Pod 会在短暂的延迟后切换到使用列表中的下一个 IP。

  2. 手动将出口 IP 分配给所需的节点主机。在节点主机上的 HostSubnet 对象上设置 egressIPs 字段。包含尽可能多的 IP 地址,以便分配给该节点主机:

    $ oc patch hostsubnet <node_name> -p \
      '{"egressIPs": ["<IP_address_1>", "<IP_address_2>"]}'

    例如,假设 node1 应具有出口 IP 192.168.1.100、192.168.1.101 和 192.168.1.102:

    $ oc patch hostsubnet node1 -p \
      '{"egressIPs": ["192.168.1.100", "192.168.1.101", "192.168.1.102"]}'
    重要

    出口 IP 是作为额外 IP 地址在主网络接口中实现的,且必须与节点的主 IP 位于同一个子网中。此外,不应在任何 Linux 网络配置文件中配置任何外部 IP,如ifcfg-eth0。

    在主网络接口上允许额外 IP 地址可能需要在使用某些云或虚拟机解决方案时进行额外的配置。

如果为项目启用了上述内容,则该项目的所有出口流量都将路由到托管该出口 IP 的节点,然后(使用 NAT)连接到该 IP 地址。如果在 NetNamespace 中设置 egressIPs,但没有节点托管该出口 IP,则会丢弃来自该命名空间的出口流量。

8.8. 启用自动 Egress IP

与为外部项目流量启用静态 IP 类似,作为集群管理员,您可以通过将 egressIPs 参数设置为 NetNamespace 资源,将出口 IP 地址分配给命名空间。您只能将单个 IP 地址与一个项目关联。

注意

如果您以多租户模式使用 OpenShift SDN,则无法将出口 IP 地址与与其关联的项目附加到另一个命名空间的任何命名空间一起使用。例如,如果运行 oc adm pod-network join-projects --to=project1 project2 命令 project1project2 加入,则这两个项目都不能使用出口 IP 地址。如需更多信息,请参阅 BZ#1645577

使用完全自动出口 IP,您可以设置每个节点的 HostSubnet 资源的 egressCIDRs 参数,以指示可以托管的出口 IP 地址范围。请求出口 IP 地址的命名空间与可以托管那些出口 IP 地址的节点匹配,然后将出口 IP 地址分配给这些节点。

高可用性是自动的。如果托管出口 IP 地址的节点停机,且有节点可以根据 HostSubnet 资源的 egressCIDR 值托管这些出口 IP 地址,则出口 IP 地址将移到一个新节点。当原始出口 IP 地址节点恢复在线后,出口 IP 地址会自动移动,以在节点之间均衡出口 IP 地址。

重要

您不能在同一节点上使用手动分配和自动分配的出口 IP 地址,或者 IP 地址范围相同。

  1. 使用出口 IP 地址更新 NetNamespace

     $ oc patch netnamespace <project_name> -p '{"egressIPs": ["<IP_address>"]}'

    您只能为 egressIPs 参数指定单个 IP 地址。不支持使用多 IP 地址。

    例如,将 project1 分配给 IP 地址 192.168.1.100 和 project2 到 IP 地址 192.168.1.101:

    $ oc patch netnamespace project1 -p '{"egressIPs": ["192.168.1.100"]}'
    $ oc patch netnamespace project2 -p '{"egressIPs": ["192.168.1.101"]}''
  2. 设置 egressCIDRs 字段来指示哪些节点可以托管出口 IP 地址:

    $ oc patch hostsubnet <node_name> -p \
      '{"egressCIDRs": ["<IP_address_range_1>", "<IP_address_range_2>"]}'

    例如,将 node1node2 设置为托管范围为 192.168.1.0 到 192.168.1.255 的出口 IP 地址:

    $ oc patch hostsubnet node1 -p '{"egressCIDRs": ["192.168.1.0/24"]}'
    $ oc patch hostsubnet node2 -p '{"egressCIDRs": ["192.168.1.0/24"]}'
  3. OpenShift Container Platform 会自动以均衡的方式将特定的出口 IP 地址分配给可用的节点。在本例中,它会将出口 IP 地址 192.168.1.100 分配给 node1,将出口 IP 地址 192.168.1.101 分配给 node2,反之亦然。

8.9. 启用多播

重要

目前,多播最适用于低带宽协调或服务发现,而不是高带宽解决方案。

默认情况下,OpenShift Container Platform pod 之间多播流量被禁用。如果您使用 ovs-multitenantovs-networkpolicy 插件,您可以通过在项目对应的 netnamespace 对象中设置注解来启用每个项目的多播:

$ oc annotate netnamespace <namespace> \
    netnamespace.network.openshift.io/multicast-enabled=true

通过删除注解来禁用多播:

$ oc annotate netnamespace <namespace> \
    netnamespace.network.openshift.io/multicast-enabled-

使用 ovs-multitenant 插件时:

  1. 在隔离的项目中,容器集发送的多播数据包将传送到项目中的所有其他 pod。
  2. 如果您已将网络接合在一起,则需要在每个项目的 netnamespace 中启用多播,以便它在任何项目中都生效。接合网络中的 pod 发送的多播数据包将传送到所有接合网络中的所有 pod。
  3. 要在 default 项目中启用多播,还必须在 kube-service-catalog 项目和全局所有其他项目中启用它。全局项目不是多播的"全局";全局项目中的 pod 发送的多播数据包将仅传送到其他全局项目中的 pod,而非所有项目中的所有容器集。同样,全局项目中的 pod 将仅接收从其他全局项目中的 pod 发送的多播数据包,而不接收来自所有项目中所有容器集的多播数据包。

使用 ovs-networkpolicy 插件时:

  1. pod 发送的多播数据包将传送到项目中的所有其他 pod,而不考虑 NetworkPolicy 对象。(pod 或许能通过多播进行通信,即使它们无法通过单播进行通信。)
  2. 一个项目中的 pod 发送的多播数据包不会传送到任何其他项目中的 pod,即使存在允许项目间通信的 NetworkPolicy 对象。

8.10. 启用 NetworkPolicy

ovs-subnetovs-multitenant 插件具有自己的传统网络隔离模型,不支持 Kubernetes NetworkPolicy。但是,可以使用 ovs-networkpolicy 插件提供 NetworkPolicy 支持。

注意

v1 NetworkPolicy 功能仅适用于 OpenShift Container Platform。这意味着 OpenShift Container Platform 不提供出口策略类型、IPBlock 和组合 podSelectornamespaceSelector

注意

不要在默认的 OpenShift Container Platform 项目中应用 NetworkPolicy 功能,因为它们可能会破坏与集群的通信。

警告

NetworkPolicy 规则不适用于主机网络命名空间。启用主机网络的 Pod 不受 NetworkPolicy 规则的影响。

在配置为使用 ovs-networkpolicy 插件的集群中,网络隔离完全由 NetworkPolicy 对象控制。默认情况下,项目中的所有 pod 都可被其他 pod 和网络端点访问。要在一个项目中隔离一个或多个 pod,您可以在该项目中创建 NetworkPolicy 对象来指示允许的入站连接。项目管理员可以在自己的项目中创建和删除 NetworkPolicy 对象。

没有指向它们的 NetworkPolicy 对象的 Pod 可以完全访问,而具有指向它们的一个或多个 NetworkPolicy 对象的 pod 会被隔离。这些隔离的 pod 只接受至少被其 NetworkPolicy 对象之一接受的连接。

以下是支持不同场景的 NetworkPolicy 对象定义示例:

  • 拒绝所有流量

    要使项目"拒绝"添加与所有 pod 匹配但不接受任何流量的 NetworkPolicy 对象。

    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
      name: deny-by-default
    spec:
      podSelector:
      ingress: []
  • 仅接受项目中 pod 的连接

    要使 pod 接受同一项目中其他 pod 的连接,但拒绝其他项目中所有 pod 的连接:

    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
      name: allow-same-namespace
    spec:
      podSelector:
      ingress:
      - from:
        - podSelector: {}
  • 只允许基于 pod 标签的 HTTP 和 HTTPS 流量

    要对带有特定标签(以下示例role=frontend )的 pod 仅启用 HTTP 和 HTTPS 访问,请添加类似如下的 NetworkPolicy 对象:

    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
      name: allow-http-and-https
    spec:
      podSelector:
        matchLabels:
          role: frontend
      ingress:
      - ports:
        - protocol: TCP
          port: 80
        - protocol: TCP
          port: 443

NetworkPolicy 对象是可添加的,这意味着您可以组合多个 NetworkPolicy 对象来满足复杂的网络要求。

例如,对于前面的示例中定义的 NetworkPolicy 对象,您可以在同一个项目中定义 allow-same-namespaceallow-http-and-https 策略。因此,允许带有标签 role=frontend 的 pod 接受每个策略允许的任何连接。也就是说,任何端口上来自同一命名空间中的 pod 的连接,以及端口 80443 上的来自任意命名空间中 pod 的连接。

8.10.1. 高效地使用 NetworkPolicy

NetworkPolicy 通过对象,您可以在命名空间中通过标签来相互隔离不同的 pod。

NetworkPolicy 对象应用到单一命名空间中的大量 pod 效率较低。Pod 标签不存在于 IP 级别上,因此 NetworkPolicy 对象为每个使用 podSelector 选择的 pod 之间生成单独的 OVS 流规则生成一个单独的 OVS 流规则。

例如,如果 NetworkPolicy 对象中的 spec podSelectoringress podSelector 各自匹配 200 个 pod,则生成 40000(200*200)OVS 流规则。这可能会减慢机器的速度。

若要减少 OVS 流规则的数量,可使用命名空间来包含需要隔离的 pod 组。

NetworkPolicy 使用 namespaceSelectors 或空 podSelectors 选择整个命名空间的对象仅生成与命名空间的 VXLAN VNID 匹配的 OVS 流规则。

保留不需要在原始命名空间中隔离的 pod,并将需要隔离的 pod 移到一个或多个不同的命名空间中。

创建额外的目标跨命名空间策略,以允许来自隔离 pod 的特定流量。

8.10.2. NetworkPolicy 和路由器

使用 ovs-multitenant 插件时,所有命名空间中会自动允许来自路由器的流量。这是因为路由器通常位于 default 命名空间中,所有命名空间都允许来自该命名空间中的 pod 的连接。使用 ovs-networkpolicy 插件时,这不会自动发生。因此,如果您有一个默认隔离命名空间的策略,则需要执行额外的步骤来允许路由器访问命名空间。

种选择是为每个服务创建一个策略,允许从所有来源访问。例如:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-to-database-service
spec:
  podSelector:
    matchLabels:
      role: database
  ingress:
  - ports:
    - protocol: TCP
      port: 5432

这允许路由器访问该服务,但也允许其他用户命名空间中的容器集访问该服务。这不应造成任何问题,因为这些容器集通常可以使用公共路由器访问服务。

另外,您可以创建一个允许从默认命名空间中完全访问的策略,如 ovs-multitenant 插件中所示:

  1. 向 default 命名空间添加标签。

    重要

    如果您使用之前流程中的 default 标签标记了 default 项目,则跳过这一步。集群管理员角色需要向命名空间添加标签。

    $ oc label namespace default name=default
  2. 创建允许从该命名空间进行连接的策略。

    注意

    对您要允许连接的每个命名空间执行此步骤。具有 Project Administrator 角色的用户可以创建策略。

    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
      name: allow-from-default-namespace
    spec:
      podSelector:
      ingress:
      - from:
        - namespaceSelector:
            matchLabels:
              name: default

8.10.3. 为新项目设置默认 NetworkPolicy

集群管理员可修改默认项目模板,以便在创建新项目时启用默认 NetworkPolicy 对象(一个或多个)自动创建。要做到这一点:

  1. 创建自定义项目模板,并将主模板配置为使用它。
  2. 使用 default 标签标记 default 项目:

    重要

    如果您使用之前流程中的 default 标签标记了 default 项目,则跳过这一步。集群管理员角色需要向命名空间添加标签。

    $ oc label namespace default name=default
  3. 编辑模板以包含所需的 NetworkPolicy 对象:

    $ oc edit template project-request -n default
    注意

    要将 NetworkPolicy 对象包含在现有模板中,请使用 oc edit 命令。目前,无法使用 oc patch 将对象添加到 Template 资源中。

    1. objects 数组中添加每个默认策略作为元素:

      objects:
      ...
      - apiVersion: networking.k8s.io/v1
        kind: NetworkPolicy
        metadata:
          name: allow-from-same-namespace
        spec:
          podSelector:
          ingress:
          - from:
            - podSelector: {}
      - apiVersion: networking.k8s.io/v1
        kind: NetworkPolicy
        metadata:
          name: allow-from-default-namespace
        spec:
          podSelector:
          ingress:
          - from:
            - namespaceSelector:
                matchLabels:
                  name: default
      ...

8.11. 启用 HTTP 严格传输安全性

HTTP 严格传输安全性 (HSTS) 策略是一种安全增强,可确保主机上只允许 HTTPS 流量。所有 HTTP 请求都会默认丢弃。这可用于确保与网站安全交互,或提供安全应用程序让用户受益。

当 HSTS 启用时,HSTS 会添加一个 Strict Transport Security 标头到站点的 HTTPS 响应。您可以使用路由中的 insecureEdgeTerminationPolicy 值重定向到 HTTPS。但是,当启用 HSTS 时,客户端会在发送请求前将所有来自 HTTP URL 的请求更改为 HTTPS,从而消除对重定向的需求。客户端不需要支持此功能,可通过设置 max-age=0 来禁用。

重要

HSTS 仅适用于安全路由(边缘终止或重新加密)。其配置在 HTTP 或传递路由上无效。

要将 HSTS 启用到路由,请将 haproxy.router.openshift.io/hsts_header 值添加到边缘终止或重新加密路由中:

apiVersion: v1
kind: Route
metadata:
  annotations:
    haproxy.router.openshift.io/hsts_header: max-age=31536000;includeSubDomains;preload
重要

确保 haproxy.router.openshift.io/hsts_header 值中没有空格,且参数中没有其他值。只需要 max-age

所需的 max-age 参数指示 HSTS 策略生效的时间长度,以秒为单位。每当从主机收到带有 HSTS 标头的响应时,客户端会更新 max-age。当 max-age 超时时,客户端会丢弃策略。

可选的 includeSubDomains 参数告知客户端主机的所有子域的处理方式与主机相同。

如果 max-age 大于 0,可选的 preload 参数允许外部服务将此站点包括在 HSTS 预加载列表中。例如,Google 等站点可以构造一个设置了 preload 的站点列表。浏览器可以使用这些列表来确定哪些站点仅通过 HTTPS 与 通信,即使在哪些站点与站点交互之前也是如此。如果没有设置 preload,它们需要通过 HTTPS 与站点通信来获取标头。

8.12. 吞吐量问题故障排除

有时,通过 OpenShift Container Platform 部署的应用程序可能会导致网络吞吐量问题,如特定服务间的延迟异常高。

如果 pod 日志未能揭示造成问题的原因,请使用以下方法分析性能问题:

  • 使用 ping 或 tcpdump 等数据包分析器,分析 pod 与其节点之间的流量。

    例如,在每个 pod 上运行 tcpdump 工具,同时重现导致问题的行为。检查两端的捕获信息,以比较发送和接收时间戳,以分析流量与 pod/来自 pod 的延迟。如果节点接口被其他 pod、存储设备或者数据平面的流量过载,则 OpenShift Container Platform 中可能会出现延迟。

    $ tcpdump -s 0 -i any -w /tmp/dump.pcap host <podip 1> && host <podip 2> 1
    1
    podip 是 pod 的 IP 地址。运行以下命令以获取 pod 的 IP 地址:
    # oc get pod <podname> -o wide

    tcpdump /tmp/dump.pcap 中生成一个包含这两个 pod 间所有流量的文件。最好在运行分析器后立即重现问题,并在问题重现完成后马上停止分析器,从而尽量减小文件的大小。您还可以通过以下命令,在节点之间运行数据包分析器(从考量范围中剔除 SDN):

    # tcpdump -s 0 -i any -w /tmp/dump.pcap port 4789
  • 使用 iperf 等带宽测量工具来测量数据流吞吐量和 UDP 吞吐量。先从 pod 运行该工具,然后从节点运行,以尝试查找瓶颈。iperf3 工具是 RHEL 7 的一部分。

有关安装和使用 iperf3 的详情,请参考这个红帽解决方案

第 9 章 配置服务帐户

9.1. 概述

当用户使用 OpenShift Container Platform CLI 或 Web 控制台时,他们的 API 令牌会通过 OpenShift Container Platform API 进行身份验证。但是,当普通用户的凭据不可用时,组件通常会单独进行 API 调用。例如:

  • 复制控制器进行 API 调用来创建或删除容器集。
  • 容器内的应用可以发出 API 调用来进行发现。
  • 外部应用程序可以发出 API 调用来监控或集成。

服务帐户为控制 API 访问提供了灵活的方式,不需要共享常规用户的凭证。

9.2. 用户名和组

每个服务帐户都有一个关联的用户名,可以像普通用户一样被授予角色。用户名派生自其项目和名称:

system:serviceaccount:<project>:<name>

例如,将 view 角色添加到 top-secret 项目中的 robot 服务帐户:

$ oc policy add-role-to-user view system:serviceaccount:top-secret:robot
重要

如果要授予对项目中特定服务帐户的访问权限,您可以使用 -z 标志。在服务帐户所属的项目中,使用 -z 标志并指定 <serviceaccount_name>。强烈建议您这样做,因为它有助于防止拼写错误,并确保只为指定的服务帐户授予访问权限。例如:

 $ oc policy add-role-to-user <role_name> -z <serviceaccount_name>

如果没有在项目中,使用 -n 选项指定它应用到的项目命名空间,如下例所示。

每一服务帐户也是以下两个组的成员:

system:serviceaccounts
包含系统中的所有服务帐户。
system:serviceaccounts:<project>
包含指定项目中的所有服务帐户。

例如,允许所有项目中的所有服务帐户查看 top-secret 项目中的资源:

$ oc policy add-role-to-group view system:serviceaccounts -n top-secret

允许 managers 项目中的所有服务帐户编辑 top-secret 项目中的资源:

$ oc policy add-role-to-group edit system:serviceaccounts:managers -n top-secret

9.3. 管理服务帐户

服务帐户是各个项目中存在的 API 对象。要管理服务帐户,您可以使用 oc 命令和 saserviceaccount 对象类型,或使用 Web 控制台。

获取当前项目中现有服务帐户的列表:

$ oc get sa
NAME       SECRETS   AGE
builder    2         2d
default    2         2d
deployer   2         2d

要创建新服务帐户,请执行以下操作:

$ oc create sa robot
serviceaccount "robot" created

旦创建了服务帐户,会自动向其中添加两个 secret:

  • API 令牌
  • OpenShift Container Registry 的凭证

可以通过描述服务帐户来查看它们:

$ oc describe sa robot
Name:		robot
Namespace:	project1
Labels:		<none>
Annotations:	<none>

Image pull secrets:	robot-dockercfg-qzbhb

Mountable secrets: 	robot-token-f4khf
                   	robot-dockercfg-qzbhb

Tokens:            	robot-token-f4khf
                   	robot-token-z8h44

系统确保服务帐户始终具有 API 令牌和 registry 凭据。

生成的 API 令牌和 registry 凭据不会过期,但可通过删除 secret 来撤销。删除 secret 时,会自动生成一个新 secret 来代替它。

9.4. 启用服务帐户身份验证

服务帐户使用由 RSA 私钥签名的令牌向 API 进行身份验证。身份验证层使用匹配的公共 RSA 密钥验证签名。

要启用服务帐户令牌生成,更新 master 上的/etc/origin/master/master-config.yml 文件中的 serviceAccountConfig 小节以指定 privateKeyFile (用于签名)和 publicKeyFiles 列表中匹配的公钥文件:

serviceAccountConfig:
  ...
  masterCA: ca.crt 1
  privateKeyFile: serviceaccount.private.key 2
  publicKeyFiles:
  - serviceaccount.public.key 3
  - ...
1
用于验证 API 服务器的服务证书的 CA 文件。
2
私有 RSA 密钥文件(用于令牌签名)。
3
公共 RSA 密钥文件(用于令牌验证)。如果提供了私钥文件,则使用公钥组件。可以指定多个公钥文件,如果令牌可以由其中一个公钥验证,则可以接受该令牌。这允许轮转签名密钥,同时仍然接受上一签名人生成的令牌。

9.5. 受管服务帐户

每个项目都需要服务帐户来运行构建、部署和其他容器集。master 上的/etc/origin/master/master-config.yml 文件中的 managedNames 设置控制在每个项目中自动创建哪些服务帐户:

serviceAccountConfig:
  ...
  managedNames: 1
  - builder 2
  - deployer 3
  - default 4
  - ...
1
要在每个项目中自动创建的服务帐户列表。
2
构建 Pod 需要每个项目中的构建器服务帐户,并被授予 system:image-builder 角色,该角色允许使用内部容器镜像注册表将镜像推送到项目中任何镜像流。
3
部署 Pod 需要每个项目中的一个部署器服务帐户,并被授予 system:deployer 角色,允许查看和修改项目中的复制控制器和容器集。
4
所有其他容器集使用默认服务帐户,除非指定了不同的服务帐户。

项目中的所有服务帐户都会被授予 system:image-puller 角色,允许使用内部容器镜像 registry 从项目中的任何镜像流拉取镜像。

9.6. 基础架构服务帐户

几个基础架构控制器使用服务帐户凭证运行。服务器启动时在 OpenShift Container Platform 基础架构项目(openshift-infra)中创建以下服务帐户,并授予以下集群范围角色:

服务帐户描述

replication-controller

分配 system:replication-controller 角色

deployment-controller

分配 system:deployment-controller 角色

build-controller

分配 system:build-controller 角色。另外,build-controller 服务帐户也包含在特权安全上下文约束中,以便创建特权构建 Pod。

要配置创建这些服务帐户的项目,请在 master 上的/etc/origin/master/master-config.yml 文件中设置 openshiftInfrastructureNamespace 字段:

policyConfig:
  ...
  openshiftInfrastructureNamespace: openshift-infra

9.7. 服务帐户和机密

将 master 上的/etc/origin/master/master-config.yml 文件中的 limitSecretReferences 字段设置为 true,要求其服务帐户将 pod secret 引用列入白名单。将其值设为 false 以允许 Pod 在项目中引用任何 secret。

serviceAccountConfig:
  ...
  limitSecretReferences: false

第 10 章 管理基于角色的访问控制(RBAC)

10.1. 概述

您可以使用 CLI 查看 RBAC 资源,以及管理员 CLI 来管理角色和绑定

10.2. 查看角色和绑定

角色可用于授予 集群范围和 项目范围内的各种访问级别。用户和组可以同时关联或 绑定到多个角色。您可以使用 oc describe 命令查看角色及其绑定的详情。

在集群范围内绑定了cluster-admin 默认集群角色的用户可以对任何资源执行任何操作本地绑定了admin 默认集群角色的用户可以管理该项目中的角色和绑定

注意

检查 Evaluating Authorization 部分中的操作动词的完整列表。

10.2.1. 查看集群角色

查看集群角色及其关联的规则集:

$ oc describe clusterrole.rbac
Name:		admin
Labels:		<none>
Annotations:	openshift.io/description=A user that has edit rights within the project and can change the project's membership.
		rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
  Resources							Non-Resource URLs	Resource Names	Verbs
  ---------							-----------------	--------------	-----
  appliedclusterresourcequotas					[]			[]		[get list watch]
  appliedclusterresourcequotas.quota.openshift.io		[]			[]		[get list watch]
  bindings							[]			[]		[get list watch]
  buildconfigs							[]			[]		[create delete deletecollection get list patch update watch]
  buildconfigs.build.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]
  buildconfigs/instantiate					[]			[]		[create]
  buildconfigs.build.openshift.io/instantiate			[]			[]		[create]
  buildconfigs/instantiatebinary				[]			[]		[create]
  buildconfigs.build.openshift.io/instantiatebinary		[]			[]		[create]
  buildconfigs/webhooks						[]			[]		[create delete deletecollection get list patch update watch]
  buildconfigs.build.openshift.io/webhooks			[]			[]		[create delete deletecollection get list patch update watch]
  buildlogs							[]			[]		[create delete deletecollection get list patch update watch]
  buildlogs.build.openshift.io					[]			[]		[create delete deletecollection get list patch update watch]
  builds							[]			[]		[create delete deletecollection get list patch update watch]
  builds.build.openshift.io					[]			[]		[create delete deletecollection get list patch update watch]
  builds/clone							[]			[]		[create]
  builds.build.openshift.io/clone				[]			[]		[create]
  builds/details						[]			[]		[update]
  builds.build.openshift.io/details				[]			[]		[update]
  builds/log							[]			[]		[get list watch]
  builds.build.openshift.io/log					[]			[]		[get list watch]
  configmaps							[]			[]		[create delete deletecollection get list patch update watch]
  cronjobs.batch						[]			[]		[create delete deletecollection get list patch update watch]
  daemonsets.extensions						[]			[]		[get list watch]
  deploymentconfigrollbacks					[]			[]		[create]
  deploymentconfigrollbacks.apps.openshift.io			[]			[]		[create]
  deploymentconfigs						[]			[]		[create delete deletecollection get list patch update watch]
  deploymentconfigs.apps.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]
  deploymentconfigs/instantiate					[]			[]		[create]
  deploymentconfigs.apps.openshift.io/instantiate		[]			[]		[create]
  deploymentconfigs/log						[]			[]		[get list watch]
  deploymentconfigs.apps.openshift.io/log			[]			[]		[get list watch]
  deploymentconfigs/rollback					[]			[]		[create]
  deploymentconfigs.apps.openshift.io/rollback			[]			[]		[create]
  deploymentconfigs/scale					[]			[]		[create delete deletecollection get list patch update watch]
  deploymentconfigs.apps.openshift.io/scale			[]			[]		[create delete deletecollection get list patch update watch]
  deploymentconfigs/status					[]			[]		[get list watch]
  deploymentconfigs.apps.openshift.io/status			[]			[]		[get list watch]
  deployments.apps						[]			[]		[create delete deletecollection get list patch update watch]
  deployments.extensions					[]			[]		[create delete deletecollection get list patch update watch]
  deployments.extensions/rollback				[]			[]		[create delete deletecollection get list patch update watch]
  deployments.apps/scale					[]			[]		[create delete deletecollection get list patch update watch]
  deployments.extensions/scale					[]			[]		[create delete deletecollection get list patch update watch]
  deployments.apps/status					[]			[]		[create delete deletecollection get list patch update watch]
  endpoints							[]			[]		[create delete deletecollection get list patch update watch]
  events							[]			[]		[get list watch]
  horizontalpodautoscalers.autoscaling				[]			[]		[create delete deletecollection get list patch update watch]
  horizontalpodautoscalers.extensions				[]			[]		[create delete deletecollection get list patch update watch]
  imagestreamimages						[]			[]		[create delete deletecollection get list patch update watch]
  imagestreamimages.image.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]
  imagestreamimports						[]			[]		[create]
  imagestreamimports.image.openshift.io				[]			[]		[create]
  imagestreammappings						[]			[]		[create delete deletecollection get list patch update watch]
  imagestreammappings.image.openshift.io			[]			[]		[create delete deletecollection get list patch update watch]
  imagestreams							[]			[]		[create delete deletecollection get list patch update watch]
  imagestreams.image.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]
  imagestreams/layers						[]			[]		[get update]
  imagestreams.image.openshift.io/layers			[]			[]		[get update]
  imagestreams/secrets						[]			[]		[create delete deletecollection get list patch update watch]
  imagestreams.image.openshift.io/secrets			[]			[]		[create delete deletecollection get list patch update watch]
  imagestreams/status						[]			[]		[get list watch]
  imagestreams.image.openshift.io/status			[]			[]		[get list watch]
  imagestreamtags						[]			[]		[create delete deletecollection get list patch update watch]
  imagestreamtags.image.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]
  jenkins.build.openshift.io					[]			[]		[admin edit view]
  jobs.batch							[]			[]		[create delete deletecollection get list patch update watch]
  limitranges							[]			[]		[get list watch]
  localresourceaccessreviews					[]			[]		[create]
  localresourceaccessreviews.authorization.openshift.io		[]			[]		[create]
  localsubjectaccessreviews					[]			[]		[create]
  localsubjectaccessreviews.authorization.k8s.io		[]			[]		[create]
  localsubjectaccessreviews.authorization.openshift.io		[]			[]		[create]
  namespaces							[]			[]		[get list watch]
  namespaces/status						[]			[]		[get list watch]
  networkpolicies.extensions					[]			[]		[create delete deletecollection get list patch update watch]
  persistentvolumeclaims					[]			[]		[create delete deletecollection get list patch update watch]
  pods								[]			[]		[create delete deletecollection get list patch update watch]
  pods/attach							[]			[]		[create delete deletecollection get list patch update watch]
  pods/exec							[]			[]		[create delete deletecollection get list patch update watch]
  pods/log							[]			[]		[get list watch]
  pods/portforward						[]			[]		[create delete deletecollection get list patch update watch]
  pods/proxy							[]			[]		[create delete deletecollection get list patch update watch]
  pods/status							[]			[]		[get list watch]
  podsecuritypolicyreviews					[]			[]		[create]
  podsecuritypolicyreviews.security.openshift.io		[]			[]		[create]
  podsecuritypolicyselfsubjectreviews				[]			[]		[create]
  podsecuritypolicyselfsubjectreviews.security.openshift.io	[]			[]		[create]
  podsecuritypolicysubjectreviews				[]			[]		[create]
  podsecuritypolicysubjectreviews.security.openshift.io		[]			[]		[create]
  processedtemplates						[]			[]		[create delete deletecollection get list patch update watch]
  processedtemplates.template.openshift.io			[]			[]		[create delete deletecollection get list patch update watch]
  projects							[]			[]		[delete get patch update]
  projects.project.openshift.io					[]			[]		[delete get patch update]
  replicasets.extensions					[]			[]		[create delete deletecollection get list patch update watch]
  replicasets.extensions/scale					[]			[]		[create delete deletecollection get list patch update watch]
  replicationcontrollers					[]			[]		[create delete deletecollection get list patch update watch]
  replicationcontrollers/scale					[]			[]		[create delete deletecollection get list patch update watch]
  replicationcontrollers.extensions/scale			[]			[]		[create delete deletecollection get list patch update watch]
  replicationcontrollers/status					[]			[]		[get list watch]
  resourceaccessreviews						[]			[]		[create]
  resourceaccessreviews.authorization.openshift.io		[]			[]		[create]
  resourcequotas						[]			[]		[get list watch]
  resourcequotas/status						[]			[]		[get list watch]
  resourcequotausages						[]			[]		[get list watch]
  rolebindingrestrictions					[]			[]		[get list watch]
  rolebindingrestrictions.authorization.openshift.io		[]			[]		[get list watch]
  rolebindings							[]			[]		[create delete deletecollection get list patch update watch]
  rolebindings.authorization.openshift.io			[]			[]		[create delete deletecollection get list patch update watch]
  rolebindings.rbac.authorization.k8s.io			[]			[]		[create delete deletecollection get list patch update watch]
  roles								[]			[]		[create delete deletecollection get list patch update watch]
  roles.authorization.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]
  roles.rbac.authorization.k8s.io				[]			[]		[create delete deletecollection get list patch update watch]
  routes							[]			[]		[create delete deletecollection get list patch update watch]
  routes.route.openshift.io					[]			[]		[create delete deletecollection get list patch update watch]
  routes/custom-host						[]			[]		[create]
  routes.route.openshift.io/custom-host				[]			[]		[create]
  routes/status							[]			[]		[get list watch update]
  routes.route.openshift.io/status				[]			[]		[get list watch update]
  scheduledjobs.batch						[]			[]		[create delete deletecollection get list patch update watch]
  secrets							[]			[]		[create delete deletecollection get list patch update watch]
  serviceaccounts						[]			[]		[create delete deletecollection get list patch update watch impersonate]
  services							[]			[]		[create delete deletecollection get list patch update watch]
  services/proxy						[]			[]		[create delete deletecollection get list patch update watch]
  statefulsets.apps						[]			[]		[create delete deletecollection get list patch update watch]
  subjectaccessreviews						[]			[]		[create]
  subjectaccessreviews.authorization.openshift.io		[]			[]		[create]
  subjectrulesreviews						[]			[]		[create]
  subjectrulesreviews.authorization.openshift.io		[]			[]		[create]
  templateconfigs						[]			[]		[create delete deletecollection get list patch update watch]
  templateconfigs.template.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]
  templateinstances						[]			[]		[create delete deletecollection get list patch update watch]
  templateinstances.template.openshift.io			[]			[]		[create delete deletecollection get list patch update watch]
  templates							[]			[]		[create delete deletecollection get list patch update watch]
  templates.template.openshift.io				[]			[]		[create delete deletecollection get list patch update watch]


Name:		basic-user
Labels:		<none>
Annotations:	openshift.io/description=A user that can get basic information about projects.
		rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
  Resources						Non-Resource URLs	Resource Names	Verbs
  ---------						-----------------	--------------	-----
  clusterroles						[]			[]		[get list]
  clusterroles.authorization.openshift.io		[]			[]		[get list]
  clusterroles.rbac.authorization.k8s.io		[]			[]		[get list watch]
  projectrequests					[]			[]		[list]
  projectrequests.project.openshift.io			[]			[]		[list]
  projects						[]			[]		[list watch]
  projects.project.openshift.io				[]			[]		[list watch]
  selfsubjectaccessreviews.authorization.k8s.io		[]			[]		[create]
  selfsubjectrulesreviews				[]			[]		[create]
  selfsubjectrulesreviews.authorization.openshift.io	[]			[]		[create]
  storageclasses.storage.k8s.io				[]			[]		[get list]
  users							[]			[~]		[get]
  users.user.openshift.io				[]			[~]		[get]


Name:		cluster-admin
Labels:		<none>
Annotations:	authorization.openshift.io/system-only=true
		openshift.io/description=A super-user that can perform any action in the cluster. When granted to a user within a project, they have full control over quota and membership and can perform every action...
		rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
  Resources	Non-Resource URLs	Resource Names	Verbs
  ---------	-----------------	--------------	-----
  		[*]			[]		[*]
  *.*		[]			[]		[*]


Name:		cluster-debugger
Labels:		<none>
Annotations:	authorization.openshift.io/system-only=true
		rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
  Resources	Non-Resource URLs	Resource Names	Verbs
  ---------	-----------------	--------------	-----
  		[/debug/pprof]		[]		[get]
  		[/debug/pprof/*]	[]		[get]
  		[/metrics]		[]		[get]


Name:		cluster-reader
Labels:		<none>
Annotations:	authorization.openshift.io/system-only=true
		rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
  Resources							Non-Resource URLs	Resource Names	Verbs
  ---------							-----------------	--------------	-----
  								[*]			[]		[get]
  apiservices.apiregistration.k8s.io				[]			[]		[get list watch]
  apiservices.apiregistration.k8s.io/status			[]			[]		[get list watch]
  appliedclusterresourcequotas					[]			[]		[get list watch]

...

10.2.2. 查看集群角色绑定

查看当前的集群角色绑定集合,这显示绑定到各种角色的用户和组:

$ oc describe clusterrolebinding.rbac
Name:		admin
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	admin
Subjects:
  Kind			Name				Namespace
  ----			----				---------
  ServiceAccount	template-instance-controller	openshift-infra


Name:		basic-users
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	basic-user
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:authenticated


Name:		cluster-admin
Labels:		kubernetes.io/bootstrapping=rbac-defaults
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	cluster-admin
Subjects:
  Kind			Name		Namespace
  ----			----		---------
  ServiceAccount	pvinstaller	default
  Group			system:masters


Name:		cluster-admins
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	cluster-admin
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:cluster-admins
  User	system:admin


Name:		cluster-readers
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	cluster-reader
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:cluster-readers


Name:		cluster-status-binding
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	cluster-status
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:authenticated
  Group	system:unauthenticated


Name:		registry-registry-role
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:registry
Subjects:
  Kind			Name		Namespace
  ----			----		---------
  ServiceAccount	registry	default


Name:		router-router-role
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:router
Subjects:
  Kind			Name	Namespace
  ----			----	---------
  ServiceAccount	router	default


Name:		self-access-reviewers
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	self-access-reviewer
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:authenticated
  Group	system:unauthenticated


Name:		self-provisioners
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	self-provisioner
Subjects:
  Kind	Name				Namespace
  ----	----				---------
  Group	system:authenticated:oauth


Name:		system:basic-user
Labels:		kubernetes.io/bootstrapping=rbac-defaults
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	system:basic-user
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:authenticated
  Group	system:unauthenticated


Name:		system:build-strategy-docker-binding
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	system:build-strategy-docker
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:authenticated


Name:		system:build-strategy-jenkinspipeline-binding
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	system:build-strategy-jenkinspipeline
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:authenticated


Name:		system:build-strategy-source-binding
Labels:		<none>
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	system:build-strategy-source
Subjects:
  Kind	Name			Namespace
  ----	----			---------
  Group	system:authenticated


Name:		system:controller:attachdetach-controller
Labels:		kubernetes.io/bootstrapping=rbac-defaults
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	system:controller:attachdetach-controller
Subjects:
  Kind			Name			Namespace
  ----			----			---------
  ServiceAccount	attachdetach-controller	kube-system


Name:		system:controller:certificate-controller
Labels:		kubernetes.io/bootstrapping=rbac-defaults
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true
Role:
  Kind:	ClusterRole
  Name:	system:controller:certificate-controller
Subjects:
  Kind			Name			Namespace
  ----			----			---------
  ServiceAccount	certificate-controller	kube-system


Name:		system:controller:cronjob-controller
Labels:		kubernetes.io/bootstrapping=rbac-defaults
Annotations:	rbac.authorization.kubernetes.io/autoupdate=true

...

10.2.3. 查看本地角色和绑定

所有默认集群角色都可以在本地绑定到用户或组

可以创建自定义本地角色

本地角色绑定也可以查看。

查看当前的本地角色绑定集合,这显示绑定到各种角色的用户和组:

$ oc describe rolebinding.rbac

默认情况下,在查看本地角色绑定时使用当前项目。另外,可以使用 -n 标志指定项目。如果用户已经拥有 admin默认集群角色,这可用于查看另一个项目的本地角色绑定

$ oc describe rolebinding.rbac -n joe-project
Name:		admin
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	admin
Subjects:
  Kind	Name	Namespace
  ----	----	---------
  User	joe


Name:		system:deployers
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:deployer
Subjects:
  Kind			Name		Namespace
  ----			----		---------
  ServiceAccount	deployer	joe-project


Name:		system:image-builders
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:image-builder
Subjects:
  Kind			Name	Namespace
  ----			----	---------
  ServiceAccount	builder	joe-project


Name:		system:image-pullers
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:image-puller
Subjects:
  Kind	Name					Namespace
  ----	----					---------
  Group	system:serviceaccounts:joe-project

10.3. 管理角色绑定

向用户或组添加或 绑定 角色可让用户或组获得该角色授予的相关访问权限。您可以使用 oc adm policy 命令在用户和组中添加或删除角色。

在使用以下操作为本地角色绑定管理用户或组的关联角色时,可以使用 -n 标志指定项目。如果未指定,则使用当前项目。

表 10.1. 本地角色绑定操作

命令描述

$ oc adm policy who-can <verb> <resource>

指出哪些用户可以对某一资源执行某种操作。

$ oc adm policy add-role-to-user <role> <username>

将给定角色绑定到当前项目中的指定用户。

$ oc adm policy remove-role-from-user <role> <username>

从当前项目中的指定用户移除指定角色。

$ oc adm policy remove-user <username>

移除当前项目中的指定用户及其所有角色。

$ oc adm policy add-role-to-group <role> <groupname>

将给定角色绑定到当前项目中的指定组。

$ oc adm policy remove-role-from-group <role> <groupname>

从当前项目中的指定组移除给定角色。

$ oc adm policy remove-group <groupname>

移除当前项目中的指定组及其所有角色。

--rolebinding-name=

可以与 oc adm policy 命令一起使用,以保留分配给用户或组的 rolebinding 名称。

您也可以使用以下操作管理集群角色绑定。因为集群角色绑定使用没有命名空间的资源,所以这些操作没有使用 -n 标志。

表 10.2. 集群角色绑定操作

命令描述

$ oc adm policy add-cluster-role-to-user <role> <username>

将给定角色绑定到集群中所有项目的指定用户。

$ oc adm policy remove-cluster-role-from-user <role> <username>

从集群中所有项目的指定用户移除给定角色。

$ oc adm policy add-cluster-role-to-group <role> <groupname>

将给定角色绑定到集群中所有项目的指定组。

$ oc adm policy remove-cluster-role-from-group <role> <groupname>

从集群中所有项目的指定组移除给定角色。

--rolebinding-name=

可以与 oc adm policy 命令一起使用,以保留分配给用户或组的 rolebinding 名称。

例如,您可以运行以下命令,将 admin 角色添加到 joe-project 中的 alice 用户:

$ oc adm policy add-role-to-user admin alice -n joe-project

然后您可以查看本地角色绑定,并在输出中验证添加情况:

$ oc describe rolebinding.rbac -n joe-project
Name:		admin
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	admin
Subjects:
  Kind	Name	Namespace
  ----	----	---------
  User	joe


Name:		admin-0 1
Labels:		<none>
Annotations:	<none>

Role:
  Kind:  ClusterRole
  Name:  admin
Subjects:
  Kind  Name   Namespace
  ----  ----   ---------
  User  alice 2


Name:		system:deployers
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:deployer
Subjects:
  Kind			Name		Namespace
  ----			----		---------
  ServiceAccount	deployer	joe-project


Name:		system:image-builders
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:image-builder
Subjects:
  Kind			Name	Namespace
  ----			----	---------
  ServiceAccount	builder	joe-project


Name:		system:image-pullers
Labels:		<none>
Annotations:	<none>
Role:
  Kind:	ClusterRole
  Name:	system:image-puller
Subjects:
  Kind	Name					Namespace
  ----	----					---------
  Group	system:serviceaccounts:joe-project
1
创建具有默认名称的新角色绑定,根据需要递增。要指定要修改的现有角色绑定,请在向用户添加角色时使用 --rolebinding-name 选项。
2
添加了用户 alice

10.4. 创建本地角色

您可以为项目创建本地角色,然后将其绑定到用户。

  1. 要为项目创建本地角色,请运行以下命令:

    $ oc create role <name> --verb=<verb> --resource=<resource> -n <project>

    在这个命令中,指定: * <name>、本地角色的名称 * <verb>、要应用到角色 * <resource> 的以逗号分隔的操作列表,以及角色应用到 * <project>、项目名称的资源

    + 例如,要创建一个本地角色来允许用户查看 blue 项目中的 pod,请运行以下命令:

    +

    $ oc create role podview --verb=get --resource=pod -n blue
  2. 要将新角色绑定到用户,运行以下命令:
$ oc adm policy add-role-to-user podview user2 --role-namespace=blue -n blue

10.5. 创建集群角色

要创建集群角色,请运行以下命令:

$ oc create clusterrole <name> --verb=<verb> --resource=<resource>

在此命令中,指定:

  • <name>,本地角色的名称
  • <verb>,应用于角色的操作动词的逗号分隔列表
  • <resource>,角色应用到的资源

例如,要创建一个集群角色来允许用户查看 Pod,请运行以下命令:

$ oc create clusterrole podviewonly --verb=get --resource=pod

10.6. 集群和本地角色绑定

集群角色绑定是存在于集群级别的绑定。角色绑定存在于项目级别。集群角色 view 必须使用本地角色绑定来绑定到用户,以便该用户能够查看项目。只有集群角色不提供特定情形所需的权限集合时才应创建本地角色。

有些集群角色名称最初令人困惑。您可以使用本地角色绑定将 cluster-admin 绑定到用户,使它似乎此用户具有集群管理员的权限。事实并非如此。将 cluster-admin 绑定到特定项目更像是该项目的超级管理员,授予集群角色 admin 的权限,以及一些额外的权限,如编辑速率限值的能力。这可能会产生混淆,特别是通过 Web 控制台 UI,它不会列出绑定到真正集群管理员的集群角色绑定。但是,它会列出可用来本地绑定 cluster-admin 的本地角色绑定。

10.7. 更新策略定义

在集群升级过程中,在所有 master 重启时,会自动协调默认集群角色来恢复任何缺少的权限

如果您自定义默认集群角色并希望确保角色协调不会修改它们:

  1. 保护每个角色免受协调:

    $ oc annotate clusterrole.rbac <role_name> --overwrite rbac.authorization.kubernetes.io/autoupdate=false
    警告

    您必须手动更新包含此设置的角色,以便在升级后包含任何新的或所需的权限。

  2. 生成默认 bootstrap 策略模板文件:

    $ oc adm create-bootstrap-policy-file --filename=policy.json
    注意

    文件的内容因 OpenShift Container Platform 版本而异,但 文件仅包含默认策略。

  3. 更新policy.json 文件,使其包含任何集群角色自定义。
  4. 使用策略文件自动协调不受协调保护的角色和角色绑定:

    $ oc auth reconcile -f policy.json
  5. 协调安全性上下文约束:

    # oc adm policy reconcile-sccs \
        --additive-only=true \
        --confirm

第 11 章 镜像策略

11.1. 概述

您可以控制在集群中导入、标记并运行哪些镜像。为此,有两个设施:

允许导入的注册表是一种镜像策略配置,允许您将镜像来源限制为特定的外部 registry 集合。这组规则应用到要导入或标记到任何镜像流的任何镜像。因此,任何与规则集不匹配的引用 registry 的镜像都将被拒绝。

ImagePolicy 准入插件可让您指定允许在集群中运行哪些镜像。这目前被视为 beta。它允许您控制:

  • 镜像源 :可以使用哪些 registry 来拉取镜像
  • 镜像解析 :强制 pod 使用不可变摘要运行,以确保镜像不会因为重新标签而改变
  • 容器镜像标签限制 :限制或需要镜像上的标签
  • 镜像注解限制 :限制或需要集成容器镜像 registry 中的镜像的注解

11.2. 配置 registry 允许导入

您可以在 imagePolicyConfig:allowedRegistriesForImport 部分配置master-config.yaml 中允许导入的 registry,如下例所示。如果没有设置,则允许所有镜像,这是默认设置。

例 11.1. Registries 允许导入的配置示例

imagePolicyConfig:
  allowedRegistriesForImport:
  -
    domainName: registry.redhat.io 1
  -
    domainName: *.mydomain.com
    insecure: true 2
  -
    domainName: local.registry.corp:5000 3
1
允许来自指定安全注册表的任何镜像。
2
允许来自任何托管在 mydomain.com 子域中不安全 registry 的任何镜像。mydomain.com 未列入白名单。
3
允许来自给定注册表中指定端口的任何镜像。

每个规则由以下属性组成:

  • domainName: 是可选择使用 :<port> 后缀终止的主机名,其中可识别特殊的通配符字符(?*)。前者匹配包含任何长度的字符序列,而后面的字符恰好匹配一个字符。通配符字符可以在 : 分隔符前后都存在。通配符仅适用于分隔符之前或之后的部分,而不考虑分隔符的存在。
  • insecure: 是一个布尔值,用于决定在 domainName 中缺少 :<port> 部分时匹配哪些端口。如果为 true,只要导入过程中使用了不安全的标记,domainName 将匹配带有 :80 后缀或未指定端口的 registry。如果为 false,则匹配带有 :443 后缀或未指定端口的 registry。

如果规则应当与同一域的安全和不安全端口都匹配,则必须列出规则两次(使用 insecure=true 之后,一次为 insecure=false )。

在进行任何规则评估前,非限定镜像引用被授权为 docker.io。要将它们列入白名单,请使用 domainName: docker.io

domainName: * 规则与任何 registry 主机名匹配,但端口仍仅限于 443。要在任意端口上匹配任意 registry,请使用 domainName: *:*

根据 Registries 配置示例中为导入而创建的规则

  • oc tag --insecure reg.mydomain.com/app:v1 app:v1 处理 mydomain.com 规则已列入白名单
  • oc import-image --from reg1.mydomain.com:80/foo foo:latest 也将列入白名单
  • oc tag local.registry.corp/bar bar:latest 被拒绝,因为端口与第三个规则中的 5000 不匹配

被拒绝的镜像导入将生成类似以下文本的错误消息:

The ImageStream "bar" is invalid:
* spec.tags[latest].from.name: Forbidden: registry "local.registry.corp" not allowed by whitelist: "local.registry.corp:5000", "*.mydomain.com:80", "registry.redhat.io:443"
* status.tags[latest].items[0].dockerImageReference: Forbidden: registry "local.registry.corp" not allowed by whitelist: "local.registry.corp:5000", "*.mydomain.com:80", "registry.redhat.io:443"

11.3. 配置 ImagePolicy Admission 插件

要配置可以在集群中运行的镜像,在 master-config.yaml 文件中配置 ImagePolicy Admission 插件。您可以根据需要设置一个或多个规则。

  • 使用特定注解拒绝镜像

    使用此规则拒绝在其上设置特定注解的所有镜像。以下使用 images.openshift.io/deny-execution 注解拒绝所有镜像:

    - name: execution-denied
      onResources:
      - resource: pods
      - resource: builds
      reject: true
      matchImageAnnotations:
      - key: images.openshift.io/deny-execution 1
        value: "true"
      skipOnResolutionFailure: true
    1
    如果特定镜像被视为是恶意的,管理员可以设置此注解来标记这些镜像。
  • 允许用户从 Docker Hub 运行镜像

    使用此规则允许用户使用 Docker Hub 中的镜像:

    - name: allow-images-from-dockerhub
      onResources:
        - resource: pods
        - resource: builds
        matchRegistries:
        - docker.io

以下是在 master-config.yaml 文件中设置多个 ImagePolicy addmission 插件规则的示例配置:

注解的文件示例

admissionConfig:
  pluginConfig:
    openshift.io/ImagePolicy:
      configuration:
        kind: ImagePolicyConfig
        apiVersion: v1
        resolveImages: AttemptRewrite 1
        executionRules: 2
        - name: execution-denied
          # Reject all images that have the annotation images.openshift.io/deny-execution set to true.
          # This annotation may be set by infrastructure that wishes to flag particular images as dangerous
          onResources: 3
          - resource: pods
          - resource: builds
          reject: true 4
          matchImageAnnotations: 5
          - key: images.openshift.io/deny-execution
            value: "true"
          skipOnResolutionFailure: true 6
        - name: allow-images-from-internal-registry
          # allows images from the internal registry and tries to resolve them
          onResources:
          - resource: pods
          - resource: builds
          matchIntegratedRegistry: true
        - name: allow-images-from-dockerhub
          onResources:
          - resource: pods
          - resource: builds
          matchRegistries:
          - docker.io
        resolutionRules: 7
        - targetResource:
            resource: pods
          localNames: true
          policy: AttemptRewrite
        - targetResource: 8
            group: batch
            resource: jobs
          localNames: true 9
          policy: AttemptRewrite

1
尝试将镜像解析到不可变镜像摘要中,并更新 pod 中的镜像拉取规格。
2
针对传入资源进行评估的规则数组。如果您只有 reject: true 规则,则默认允许所有操作。如果您有任何接受规则,即任何规则中的 reject: false,ImagePolicy 的默认冲突会切换到 deny-all
3
指示要强制执行规则的资源。如果未指定任何内容,则默认为 pod
4
表示如果此规则匹配,则 Pod 应该被拒绝。
5
镜像对象的元数据匹配的注解列表。
6
如果您无法解析镜像,请不要失败 pod。
7
允许在 Kubernetes 资源中使用镜像流的规则数组。默认配置允许 pod、复制控制器、replicaset、有状态集、daemonset、Deployment 和作业在镜像字段中使用相同的项目镜像流标签引用。
8
标识规则应用到的组和资源。如果资源是 *,则该规则将应用到该组中的所有资源。
9
LocalNames 将允许单个片段名称(如 ruby:2.5)解释为命名空间本地镜像流标签,但前提是启用了资源或目标镜像流 local name resolution
注意

如果您通常依赖使用默认 registry 前缀(如 docker.io 或 registry.redhat.io )拉取的基础架构镜像,这些镜像将不匹配任何 matchRegistries 值,因为它们没有 registry 前缀。要确保基础架构镜像具有与镜像策略匹配的 registry 前缀,请在 master-config.yaml 文件中设置 imageConfig.format 值。

11.4. 使用 Admission Controller 来始终拉取镜像

镜像拉取到某个节点后,来自任何用户在该节点上的任何 Pod 都可以使用该镜像,而无需对镜像进行授权检查。为确保 Pod 不使用它们没有凭证的镜像,请使用 AlwaysPullImages 准入控制器。

此准入控制器修改每个新 Pod 来强制镜像拉取(pull)策略 Always,确保私有镜像只能由具有凭证的用户使用,即使 Pod 规格使用镜像拉取(pull)策略 Never

启用 AlwaysPullImages 准入控制器

  1. master-config.yaml 中添加以下内容:

    admissionConfig:
      pluginConfig:
        AlwaysPullImages: 1
          configuration:
            kind: DefaultAdmissionConfig
            apiVersion: v1
            disable: false 2
    1
    准入插件名称。
    2
    指定 false 来表示该插件应该被启用。
  2. 使用 master-restart 命令重启 control plane 静态 Pod 中运行的 master 服务:

    $ master-restart api
    $ master-restart controllers

11.5. 测试 ImagePolicy Admission 插件

  1. 使用 openshift/image-policy-check 测试您的配置。

    例如,使用以上信息,然后测试如下:

    $ oc import-image openshift/image-policy-check:latest --confirm
  2. 使用此 YAML 创建 pod。应当创建 pod。

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: test-pod
    spec:
      containers:
      - image: docker.io/openshift/image-policy-check:latest
        name: first
  3. 创建指向其他注册表的另一个 pod。pod 应该被拒绝。

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: test-pod
    spec:
      containers:
      - image: different-registry/openshift/image-policy-check:latest
        name: first
  4. 使用导入的镜像创建指向内部注册表的 pod。pod 应该被创建,如果您查看镜像规格,您应该会看到一个摘要来代替标签。

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: test-pod
    spec:
      containers:
      - image: <internal registry IP>:5000/<namespace>/image-policy-check:latest
        name: first
  5. 使用导入的镜像创建指向内部注册表的 pod。pod 应该被创建,如果您查看镜像规格,您应该会看到该标签未修改。

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: test-pod
    spec:
      containers:
      - image: <internal registry IP>:5000/<namespace>/image-policy-check:v1
        name: first
  6. oc get istag/image-policy-check:latest 获取摘要并将其用于 oc annotate images/<digest> images.openshift.io/deny-execution=true。例如:

    $ oc annotate images/sha256:09ce3d8b5b63595ffca6636c7daefb1a615a7c0e3f8ea68e5db044a9340d6ba8 images.openshift.io/deny-execution=true
  7. 再次创建此 pod,您应该会看到 pod 被拒绝:

    apiVersion: v1
    kind: Pod
    metadata:
      generateName: test-pod
    spec:
      containers:
      - image: <internal registry IP>:5000/<namespace>/image-policy-check:latest
        name: first

第 12 章 镜像签名

12.1. 概述

在 Red Hat Enterprise Linux(RHEL)系统上的容器镜像签名提供了一种方法:

  • 验证容器镜像来自的位置,
  • 检查镜像是否未被篡改,
  • 设置策略,以确定哪些经过验证的镜像可以拉取到主机。

如需更完整地了解 RHEL 系统上的容器镜像签名架构,请参阅容器镜像签名集成指南

OpenShift Container Registry 允许通过 REST API 存储签名。oc CLI 可用于验证镜像签名,并在 Web 控制台或 CLI 中显示这些签名。

12.2. 使用 Atomic CLI 签署镜像

OpenShift Container Platform 不自动执行镜像签名。签名需要开发人员的专用 GPG 密钥,通常存储在工作站上。本文档描述了该工作流。

atomic 命令行界面(CLI)版本 1.12.5 或更高版本提供签名容器镜像的命令,这些镜像可以推送到 OpenShift Container Registry。atomic CLI 可用于基于红帽的发行版本:RHEL、Centos 和 Fedora。atomic CLI 在 RHEL Atomic 主机系统中预先安装。有关在 RHEL 主机上安装 atomic 软件包的详情,请参考 启用镜像签名支持

重要

atomic CLI 使用来自 oc login 的身份验证凭证。确保在同一主机上为 atomicoc 命令使用相同的用户。例如,如果您以 sudo 身份执行 atomic CLI,请确定使用 sudo oc login 登录 OpenShift Container Platform。

要将签名附加到镜像,用户必须具有 image-signer 集群角色。集群管理员可使用以下方法添加此项:

$ oc adm policy add-cluster-role-to-user system:image-signer <user_name>

镜像可以在推送时签名:

$ atomic push [--sign-by <gpg_key_id>] --type atomic <image>

当指定了 atomic 传输类型参数时,签名会存储在 OpenShift Container Platform 中。如需更多信息,请参阅签名传输。

有关如何使用 atomic CLI 设置和执行镜像签名的详情,请查看 RHEL Atomic 主机管理容器:签名容器镜像文档或 atomic push --help 输出以了解参数详情。

使用 atomic CLI 和 OpenShift Container Registry 的具体示例工作流包括在容器镜像签名集成指南中

12.3. 使用 OpenShift CLI 验证镜像签名

您可以使用 oc adm verify-image-signature 命令验证导入到 OpenShift Container Registry 的镜像的签名。此命令通过利用公共 GPG 密钥验证签名本身,验证镜像签名中包含的镜像身份是否可以被信任,然后将所提供的预期身份与给定镜像的身份( pull spec)匹配。

默认情况下,该命令使用位于$GNUPGHOME/pubring.gpg 中的公共 GPG 密钥环,通常位于路径~/.gnupg 中。默认情况下,这个命令不会将验证结果保存到镜像对象。要做到这一点,您必须指定 --save 标记,如下所示。

注意

要验证镜像的签名,用户必须具有 image-auditor 集群角色。集群管理员可使用以下方法添加此项:

$ oc adm policy add-cluster-role-to-user system:image-auditor <user_name>
重要

在 上使用已经验证的镜像和无效 GPG 密钥或无效预期身份的 --save 标志会导致保存的验证状态和所有签名被删除,且镜像将不再验证。

为了避免误删除所有签名,您可以首先运行不带 --save 标记的命令,并检查日志是否有潜在问题。

要验证镜像签名,请使用以下格式:

$ oc adm verify-image-signature <image> --expected-identity=<pull_spec> [--save] [options]

<pull_spec> 可以通过描述镜像流来找到。<image> 可以通过描述镜像流标签来找到。请参见以下示例命令输出。

镜像签名验证示例

$ oc describe is nodejs -n openshift
Name:             nodejs
Namespace:        openshift
Created:          2 weeks ago
Labels:           <none>
Annotations:      openshift.io/display-name=Node.js
                  openshift.io/image.dockerRepositoryCheck=2017-07-05T18:24:01Z
Docker Pull Spec: 172.30.1.1:5000/openshift/nodejs
...

$ oc describe istag nodejs:latest -n openshift
Image Name:	sha256:2bba968aedb7dd2aafe5fa8c7453f5ac36a0b9639f1bf5b03f95de325238b288
...

$ oc adm verify-image-signature \
    sha256:2bba968aedb7dd2aafe5fa8c7453f5ac36a0b9639f1bf5b03f95de325238b288 \
    --expected-identity 172.30.1.1:5000/openshift/nodejs:latest \
    --public-key /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release \
    --save

注意

如果 oc adm verify-image-signature 命令返回 x509: certificate signed by unknown authority 错误,您可能需要将 registry 的证书颁发机构(CA)添加到系统中信任的 CA 列表中。您可以执行以下步骤来实现:

  1. 将 CA 证书从集群传输到客户端机器。

    例如,要为docker-registry.default.svc 添加 CA,传输位于/etc/docker/certs.d/docker-registry.default.svc\:5000/node-client-ca.crt 的文件。

  2. 将 CA 证书复制到/etc/pki/ca-trust/source/anchors/ 目录中。例如:

    # cp </path_to_file>/node-client-ca.crt \
        /etc/pki/ca-trust/source/anchors/
  3. 执行 update-ca-trust 以更新可信 CA 列表:

    # update-ca-trust

12.4. 使用 Registry API 访问镜像签名

OpenShift Container Registry 提供了一个 extensions 端点,供您编写和读取镜像签名。镜像签名通过容器镜像 registry API 存储在 OpenShift Container Platform 键值存储中。

注意

此端点是实验性的,不受上游容器镜像 registry 项目的支持。有关容器镜像 registry API 的常规信息,请参阅上游 API 文档

12.4.1. 通过 API 编写镜像签名

要向镜像添加新签名,您可以使用 HTTP PUT 方法将 JSON 有效负载发送到 extensions 端点:

PUT /extensions/v2/<namespace>/<name>/signatures/<digest>
$ curl -X PUT --data @signature.json http://<user>:<token>@<registry_endpoint>:5000/extensions/v2/<namespace>/<name>/signatures/sha256:<digest>

带有签名内容的 JSON 有效负载应具有以下结构:

{
  "version": 2,
  "type":    "atomic",
  "name":    "sha256:4028782c08eae4a8c9a28bf661c0a8d1c2fc8e19dbaae2b018b21011197e1484@cddeb7006d914716e2728000746a0b23",
  "content": "<cryptographic_signature>"
}

name 字段包含镜像签名的名称,该名称必须是唯一的,格式为 <digest>@<name><digest> 代表镜像名称,<name> 是签名的名称。签名名称长度必须为 32 个字符。<cryptographic_signature> 必须遵循容器/镜像库中记录的规格

12.4.2. 通过 API 读取镜像签名

假设已签名的镜像已被推送到 OpenShift Container Registry 中,您可以使用以下命令读取签名:

GET /extensions/v2/<namespace>/<name>/signatures/<digest>
$ curl http://<user>:<token>@<registry_endpoint>:5000/extensions/v2/<namespace>/<name>/signatures/sha256:<digest>

<namespace> 代表 OpenShift Container Platform 项目名称或 registry 存储库名称,<name> 代表镜像存储库的名称。digest 代表镜像的 SHA-256 校验和。

如果给定镜像包含签名数据,则上述命令的输出应生成以下 JSON 响应:

{
  "signatures": [
  {
    "version": 2,
    "type":    "atomic",
    "name":    "sha256:4028782c08eae4a8c9a28bf661c0a8d1c2fc8e19dbaae2b018b21011197e1484@cddeb7006d914716e2728000746a0b23",
    "content": "<cryptographic_signature>"
  }
  ]
}

name 字段包含镜像签名的名称,该名称必须是唯一的,格式为 <digest>@<name><digest> 代表镜像名称,<name> 是签名的名称。签名名称长度必须为 32 个字符。<cryptographic_signature> 必须遵循容器/镜像库中记录的规格

12.4.3. 从签名存储中自动导入镜像签名

如果在所有 OpenShift Container Platform master 节点上通过 registry 配置目录配置了签名存储,OpenShift Container Platform 可以自动导入镜像签名。

registry 配置目录包含各种注册表(存储远程容器镜像)和其中存储的内容的配置。单个目录确保不必在每一个命令的命令行选项中提供配置,以便容器/镜像的所有用户可以共享该配置。

默认 registry 配置目录位于/etc/containers/registries.d/default.yaml 文件中。

导致所有红帽镜像自动导入镜像签名的示例配置示例:

docker:
  registry.redhat.io:
    sigstore: https://registry.redhat.io/containers/sigstore 1
1
定义签名存储的 URL。此 URL 用于读取现有签名。
注意

默认情况下,OpenShift Container Platform 自动导入的签名将被取消验证,必须由镜像管理员验证。

有关 registry 配置目录的详情,请参阅 registry 配置目录

第 13 章 有作用域令牌

13.1. 概述

用户可能希望为另一个实体赋予其作为自己的权限,但只能以有限的方式授予权限。例如,项目管理员可能希望委派创建 pod 的权限。执行此操作的一种方法是创建有作用域令牌。

有作用域令牌是一种令牌,它标识为给定用户,但仅限于其范围中的某些操作。目前,只有 cluster-admin 可以创建有范围的令牌。

13.2. 评估

通过将令牌的范围集合转换为 PolicyRule 集合来评估范围。然后,请求会与这些规则进行匹配。请求属性必须至少匹配其中一条范围规则,才能传递给 "normal" 授权程序进行进一步授权检查。

13.3. 用户范围

用户范围主要用于获取给定用户的信息。它们是基于意图的,因此会自动为您创建规则:

  • user:full - 允许使用用户的所有权限对 API 进行完全的读/写访问。
  • user:info - 允许只读访问用户的信息:名称、组等。
  • user:check-access - 允许访问自本地subjectaccessreviewsself-subjectaccessreviews。这些是在请求对象中传递空用户和组的变量。
  • user:list-projects - 允许只读访问权限列出用户有权访问的项目。

13.4. 角色范围

角色范围允许您具有与给定角色相同等级的访问权限,该角色通过命名空间过滤。

  • role:<cluster-role name>:<namespace or * for all> - 将范围限制为 cluster-role 指定的规则,但仅在指定的命名空间中。

    注意

    注意:这可防止升级访问权限。即使角色允许访问 secret、角色绑定和角色等资源,但此范围会拒绝访问这些资源。这有助于防止意外升级。许多人认为 edit 等角色并不被视为升级角色,但是如果访问 secret,这是升级角色。

  • role:<cluster-role name>:<namespace or * for all>:! - 这与上例类似,但包含感叹号会导致此范围允许升级访问。

第 14 章 监控镜像

14.1. 概述

您可以使用 CLI 监控实例中的镜像和 节点

14.2. 查看镜像统计信息

您可以显示 OpenShift Container Platform 管理的所有镜像的用量统计。换句话说,所有直接或通过 构建推送到内部注册表的镜像

查看用量统计:

$ oc adm top images
NAME                 IMAGESTREAMTAG            PARENTS                   USAGE                         METADATA    STORAGE
sha256:80c985739a78b openshift/python (3.5)                                                            yes         303.12MiB
sha256:64461b5111fc7 openshift/ruby (2.2)                                                              yes         234.33MiB
sha256:0e19a0290ddc1 test/ruby-ex (latest)     sha256:64461b5111fc71ec   Deployment: ruby-ex-1/test    yes         150.65MiB
sha256:a968c61adad58 test/django-ex (latest)   sha256:80c985739a78b760   Deployment: django-ex-1/test  yes         186.07MiB

该命令显示以下信息:

  • 镜像 ID
  • 附带的项目、名称和标签 ImageStreamTag
  • 镜像的潜在父级,按 ID 列出
  • 有关镜像使用位置的信息
  • 标志通知镜像是否包含正确的 Docker 元数据信息
  • 镜像大小

14.3. 查看 ImageStreams 统计信息

您可以显示有关 ImageStreams 的用量统计。

查看用量统计:

$ oc adm top imagestreams
NAME                STORAGE     IMAGES  LAYERS
openshift/python    1.21GiB     4       36
openshift/ruby      717.76MiB   3       27
test/ruby-ex        150.65MiB   1       10
test/django-ex      186.07MiB   1       10

该命令显示以下信息:

  • 项目和名称 ImageStream
  • 存储在 Red Hat Container Registry内部 ImageStream 的整个 的大小
  • 这个特定 ImageStream 指向的镜像数
  • ImageStream 层数由

14.4. 修剪镜像

从以上命令返回的信息有助于执行镜像修剪。

第 15 章 管理安全性上下文约束

15.1. 概述

通过安全性上下文约束,管理员可以控制容器集的权限。要了解更多有关此 API 类型的信息,请参阅安全性上下文约束 (SCC)架构文档。您可以使用 CLI,以普通 API 对象的形式管理实例中的 SCC。

注意

您必须具有cluster-admin 来管理 SCC。

重要

不要修改默认 SCC。自定义默认 SCC 会导致升级时出现问题。而是创建新的 SCC。

15.2. 列出安全性上下文约束

获取当前的 SCC 列表:

$ oc get scc

NAME               PRIV      CAPS      SELINUX     RUNASUSER          FSGROUP     SUPGROUP    PRIORITY   READONLYROOTFS   VOLUMES
anyuid             false     []        MustRunAs   RunAsAny           RunAsAny    RunAsAny    10         false            [configMap downwardAPI emptyDir persistentVolumeClaim secret]
hostaccess         false     []        MustRunAs   MustRunAsRange     MustRunAs   RunAsAny    <none>     false            [configMap downwardAPI emptyDir hostPath persistentVolumeClaim secret]
hostmount-anyuid   false     []        MustRunAs   RunAsAny           RunAsAny    RunAsAny    <none>     false            [configMap downwardAPI emptyDir hostPath nfs persistentVolumeClaim secret]
hostnetwork        false     []        MustRunAs   MustRunAsRange     MustRunAs   MustRunAs   <none>     false            [configMap downwardAPI emptyDir persistentVolumeClaim secret]
nonroot            false     []        MustRunAs   MustRunAsNonRoot   RunAsAny    RunAsAny    <none>     false            [configMap downwardAPI emptyDir persistentVolumeClaim secret]
privileged         true      [*]       RunAsAny    RunAsAny           RunAsAny    RunAsAny    <none>     false            [*]
restricted         false     []        MustRunAs   MustRunAsRange     MustRunAs   RunAsAny    <none>     false            [configMap downwardAPI emptyDir persistentVolumeClaim secret]

15.3. 检查安全性上下文约束对象

您可以查看特定 SCC 的信息,包括这个 SCC 应用到哪些用户、服务帐户和组。

例如,检查 restricted SCC:

$ oc describe scc restricted
Name:					restricted
Priority:				<none>
Access:
  Users:				<none> 1
  Groups:				system:authenticated 2
Settings:
  Allow Privileged:			false
  Default Add Capabilities:		<none>
  Required Drop Capabilities:		KILL,MKNOD,SYS_CHROOT,SETUID,SETGID
  Allowed Capabilities:			<none>
  Allowed Seccomp Profiles:		<none>
  Allowed Volume Types:			configMap,downwardAPI,emptyDir,persistentVolumeClaim,projected,secret
  Allow Host Network:			false
  Allow Host Ports:			false
  Allow Host PID:			false
  Allow Host IPC:			false
  Read Only Root Filesystem:		false
  Run As User Strategy: MustRunAsRange
    UID:				<none>
    UID Range Min:			<none>
    UID Range Max:			<none>
  SELinux Context Strategy: MustRunAs
    User:				<none>
    Role:				<none>
    Type:				<none>
    Level:				<none>
  FSGroup Strategy: MustRunAs
    Ranges:				<none>
  Supplemental Groups Strategy: RunAsAny
    Ranges:				<none>
1
列出 SCC 应用到的用户和服务帐户。
2
列出 SCC 应用到的组。
注意

要在升级过程中保留自定义 SCC,请不要编辑优先级、用户、组、标签和注解以外的默认 SCC 的设置。

15.4. 创建新安全性上下文约束

创建新 SCC:

  1. 在 JSON 或 YAML 文件中定义 SCC:

    安全性上下文约束对象定义

    kind: SecurityContextConstraints
    apiVersion: v1
    metadata:
      name: scc-admin
    allowPrivilegedContainer: true
    runAsUser:
      type: RunAsAny
    seLinuxContext:
      type: RunAsAny
    fsGroup:
      type: RunAsAny
    supplementalGroups:
      type: RunAsAny
    users:
    - my-admin-user
    groups:
    - my-admin-group

    另外,您还可以通过将 requiredDropCapabilities 字段设置为所需的值来为 SCC 添加丢弃功能。所有指定的功能都将从容器中丢弃。例如,要创建具有 KILLMKNODSYS_CHROOT 所需丢弃功能的 SCC,请将以下内容添加到 SCC 对象中:

    requiredDropCapabilities:
    - KILL
    - MKNOD
    - SYS_CHROOT

    您可以在 Docker 文档中查看可能值的列表。

    提示

    因为功能会被传递给 Docker,所以您可以使用一个特殊的 ALL 值来丢弃所有可能的功能。

  2. 然后,运行 oc create 传递文件来创建它:

    $ oc create -f scc_admin.yaml
    securitycontextconstraints "scc-admin" created
  3. 验证 SCC 已创建好:

    $ oc get scc scc-admin
    NAME        PRIV      CAPS      SELINUX    RUNASUSER   FSGROUP    SUPGROUP   PRIORITY   READONLYROOTFS   VOLUMES
    scc-admin   true      []        RunAsAny   RunAsAny    RunAsAny   RunAsAny   <none>     false            [awsElasticBlockStore azureDisk azureFile cephFS cinder configMap downwardAPI emptyDir fc flexVolume flocker gcePersistentDisk glusterfs iscsi nfs persistentVolumeClaim photonPersistentDisk quobyte rbd secret vsphere]

15.5. 删除安全性上下文约束

删除 SCC:

$ oc delete scc <scc_name>
注意

如果删除了默认 SCC,重启后会重新生成它。

15.6. 更新安全性上下文约束

更新现有的 SCC:

$ oc edit scc <scc_name>
注意

要在升级过程中保留自定义 SCC,请不要编辑优先级、用户和组以外的默认 SCC 的设置。

15.6.1. 安全性上下文约束设置示例

但没有清除 runAsUser 设置

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext: 1
  containers:
  - name: sec-ctx-demo
    image: gcr.io/google-samples/node-hello:1.0

1
当容器或 Pod 没有指定应运行它的用户 ID 时,则生效的 UID 由发出此 Pod 的 SCC 决定。由于在默认情况下,受限 SCC 会授权给所有经过身份验证的用户,所以它可供所有用户和服务帐户使用,并在大多数情形中使用。受限 SCC 使用 MustRunAsRange 策略来约束和默认值 securityContext.runAsUser 字段的可能值。准入插件将在当前项目上查找 openshift.io/sa.scc.uid-range 注解来填充范围字段,因为它不提供这个范围。最后,容器的 runAsUser 等于难以预测的范围的第一个值,因为每个项目都有不同的范围。如需更多信息,请参阅了解预分配值和安全上下文约束

使用 Explicit runAsUser 设置

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000 1
  containers:
    - name: sec-ctx-demo
      image: gcr.io/google-samples/node-hello:1.0

1
只有服务帐户或用户被授予对允许某一用户 ID 的 SCC 访问权限时,OpenShift Container Platform 才会接受请求该用户 ID 的容器或 Pod。SCC 允许任意 ID、属于某一范围的 ID,或特定于请求的确切用户 ID。

这可与 SELinux、fsGroup 和补充组配合使用。如需更多信息,请参阅卷安全性

15.7. 更新默认安全性上下文约束

如果缺少默认 SCC,则在主控机启动时创建默认 SCC。要将 SCC 重置为默认值,或者在升级后将现有 SCC 更新为新的默认定义:

  1. 删除您要重置的任何 SCC,并通过重启 master 来重新创建它
  2. 使用 oc adm policy reconcile-sccs 命令

oc adm policy reconcile-sccs 命令将将所有 SCC 策略设置为默认值,但保留任何其他用户、组、标签和注解,以及您可能已设置的优先级。要查看要更改哪些 SCC,您可以不带选项运行该命令,或使用 -o <format> 选项指定您首选的输出。

在检查完现有 SCC 后,建议您备份现有 SCC,然后使用 --confirm 选项来持久保留数据。

注意

如果要重置优先级并授予,请使用 --additive-only=false 选项。

注意

如果您在 SCC 中有除优先级、用户、组、标签或注解以外的自定义设置,则协调时会丢失这些设置。

15.8. 我怎么样?

以下介绍了使用 SCC 的常见场景和程序。

15.8.1. 授予对 Privileged SCC 的访问权限

在某些情况下,管理员可能希望允许管理员组以外的用户或组创建更多特权 pod。为此,您可以:

  1. 确定您要具有 SCC 访问权限的用户或组。

    警告

    只有用户直接创建容器集时,才能授予用户访问权限。对于代表用户创建的容器集,在大多数情况下 ,系统本身应授予对其执行相关控制器的服务帐户的访问权限。代表用户创建 pod 的资源示例有 Deployments、StatefulSet、DaemonSet 等。

  2. 运行:

    $ oc adm policy add-scc-to-user <scc_name> <user_name>
    $ oc adm policy add-scc-to-group <scc_name> <group_name>

    例如,要允许 e2e-user 访问特权 SCC,请运行:

    $ oc adm policy add-scc-to-user privileged e2e-user
  3. 修改容器的 SecurityContext 以请求特权模式。

15.8.2. 为服务帐户授予 Privileged SCC 访问权限

首先,创建一个服务帐户。例如,要在项目 myproject 中创建服务帐户 mysvcacct

$ oc create serviceaccount mysvcacct -n myproject

然后,将服务帐户添加到 privileged SCC。

$ oc adm policy add-scc-to-user privileged system:serviceaccount:myproject:mysvcacct

然后,确保为服务帐户创建资源。为此,请将 spec.serviceAccountName 字段设置为服务帐户名称。将服务帐户名称留空将导致使用 default 服务帐户。

然后,确保至少有一个 pod 的容器在安全上下文中请求特权模式。

15.8.3. 在 Dockerfile 中启用镜像以使用 USER 运行

放松集群中的安全性,以便镜像不会强制以预分配的 UID 运行,而不授予每个人都对特权 SCC 的访问权限:

  1. 授予所有经过身份验证的用户对 anyuid SCC 的访问权限:

    $ oc adm policy add-scc-to-group anyuid system:authenticated
警告

如果Dockerfile 中没有指定 USER,这允许镜像作为 root UID 运行。

15.8.4. 启用需要 Root 的容器镜像

有些容器镜像(例如: postgresredis)需要 root 访问权限,并对如何拥有卷有一定期望。对于这些镜像,将服务帐户添加到 anyuid SCC 中。

$ oc adm policy add-scc-to-user anyuid system:serviceaccount:myproject:mysvcacct

15.8.5. 在 Registry 上使用 --mount-host

建议将使用 PersistentVolumePersistentVolumeClaim 对象的持久性存储用于 registry 部署。如果您要测试并想使用带 --mount-host 选项的 oc adm registry 命令,您必须首先为 registry 创建新服务帐户并将其添加到 特权 SCC 中。有关完整说明,请参阅《管理员指南 》。

15.8.6. 提供额外的功能

在某些情况下,镜像可能需要 Docker 不提供开箱即用的功能。您可以在 Pod 规格中请求其他功能,这些功能将针对 SCC 进行验证。

重要

这样,镜像就可以使用提升的功能运行,并且仅在需要时才使用。您不应该编辑默认受限 SCC 来启用额外的功能。

与非 root 用户结合使用时,还必须确保使用 setcap 命令赋予需要额外权限的文件。例如,在镜像的Dockerfile 中:

setcap cap_net_raw,cap_net_admin+p /usr/bin/ping

此外,如果 Docker 中默认提供了一项功能,则您无需修改 pod 规格即可请求该功能。例如,默认提供了 NET_RAW,且应该在 ping 上设置功能,因此无需执行特殊步骤来运行 ping

提供额外的功能:

  1. 创建新 SCC
  2. 使用 allowedCapabilities 字段添加允许的能力。
  3. 在创建 pod 时,请在 securityContext.capabilities.add 字段中请求该能力。

15.8.7. 修改集群默认行为

当您为每个人授予对 anyuid SCC 的访问权限时,您的集群:

  • 不预先分配 UID
  • 允许容器以任意用户身份运行
  • 防止特权容器
 $ oc adm policy add-scc-to-group anyuid system:authenticated

要修改集群,使其不预先分配 UID,且不允许容器以 root 用户身份运行,请为每个人授予对非 root SCC 的访问权限:

 $ oc adm policy add-scc-to-group nonroot system:authenticated
警告

对于对集群范围有影响的任何修改,请非常小心。当您向所有经过身份验证的用户(如上例中所示)授予 SCC 时,或者修改应用于受限 SCC 等所有用户的 SCC,它也会影响 Kubernetes 和 OpenShift Container Platform 组件,包括 Web 控制台和集成的容器镜像 registry。使用这些 SCC 进行的更改可能会导致这些组件停止正常运行。

相反,创建自定义 SCC 并将其仅指向特定的用户或组。这样,潜在的问题仅限于受影响的用户或组,不会影响到关键集群组件。

15.8.8. 使用 hostPath 卷插件

要缓解集群中的安全性,以便允许 pod 使用 hostPath 卷插件,而不授予任何人访问更多特权 SCC(如特权、hostaccess 或 hostmount-anyuid )的访问权限,请执行以下操作:

  1. 创建名为 的新 SCC hostpath
  2. 将新 SCC 的 allowHostDirVolumePlugin 参数设置为 true

    $ oc patch scc hostpath -p '{"allowHostDirVolumePlugin": true}'
  3. 为所有用户授予对此 SCC 的访问权限:

    $ oc adm policy add-scc-to-group hostpath system:authenticated

现在,所有请求 hostPath 卷的 pod 都被 hostpath SCC 接受。

15.8.9. 确保 Admission Attempts to use Specific SCC First

您可以通过设置 SCC 的 Priority 字段来控制准入中的 SCC 排序。有关排序的更多信息,请参阅 SCC 优先级部分

15.8.10. 将 SCC 添加到用户、组或项目

在将 SCC 添加到用户或组群前,您可以首先使用 scc-review 选项检查用户或组群是否可以创建 pod。如需更多信息,请参阅授权主题

SCC 不直接授予项目。相反,您要将服务帐户添加到 SCC 中,并指定 pod 中的服务帐户名称,或者在未指定的情况下作为 default 服务帐户运行。

将 SCC 添加到用户:

$ oc adm policy add-scc-to-user <scc_name> <user_name>

将 SCC 添加到服务帐户:

$ oc adm policy add-scc-to-user <scc_name> \
    system:serviceaccount:<serviceaccount_namespace>:<serviceaccount_name>

如果您当前位于服务帐户所属的项目中,您可以使用 -z 标志,只需指定 <serviceaccount_name>

$ oc adm policy add-scc-to-user <scc_name> -z <serviceaccount_name>
重要

强烈建议您使用上述 -z 标志,因为它有助于防止拼写错误,并确保只为指定的服务帐户授予访问权限。如果没有在项目中,使用 -n 选项指定它应用到的项目命名空间。

将 SCC 添加到组中:

$ oc adm policy add-scc-to-group <scc_name> <group_name>

将 SCC 添加到命名空间中的所有服务帐户:

$ oc adm policy add-scc-to-group <scc_name> \
    system:serviceaccounts:<serviceaccount_namespace>

第 16 章 调度

16.1. 概述

16.1.1. 概述

Pod 调度是一个内部过程,决定新 pod 如何放置到集群内的节点上。

调度程度代码具有明确隔离,会监测创建的新 pod 并确定最适合托管它们的节点。然后,它会利用主 API 为 pod 创建 pod 至节点的绑定。

16.1.2. 默认调度

OpenShift Container Platform 附带一个满足大多数用户需求的默认调度程序。默认调度程序同时使用内置和可自定义的工具来确定最适合 pod 的工具。

有关默认调度程序如何决定 pod 放置和可用可自定义参数的详情,请参考默认调度

16.1.3. 高级调度

在您可能需要更多地控制新 pod 的放置位置时,OpenShift Container Platform 高级调度功能允许您配置 pod,以便(或首选)在特定节点上运行或与特定 pod 一起运行 pod。通过高级调度,您还可以防止 pod 放置到某个节点上或放置到另一个 pod 中。

有关高级调度的详情,请查看 高级调度

16.1.4. 自定义调度

OpenShift Container Platform 还允许您通过编辑 pod 规格来使用自己的或第三方调度程序。

如需更多信息,请参阅自定义调度程序

16.2. 默认调度

16.2.1. 概述

OpenShift Container Platform 的默认 pod 调度程序负责确定在集群的节点中放置新的 pod。它从 pod 读取数据,并尝试根据配置的策略寻找最适合的节点。它完全独立存在,作为单机或可插拔的解决方案。它不会修改 pod,只是为 pod 创建将 pod 与特定节点衔接起来的绑定。

16.2.2. 通用调度程序

现有的通用调度程序是平台默认提供的调度程序引擎,它可通过三步操作来选择托管 pod 的节点:

16.2.3. 过滤节点

根据指定的约束或要求过滤可用的节点。这可以通过称为predicates 的过滤器函数列表运行每个节点。

16.2.3.1. 排列过滤后节点列表的优先顺序

这可以通过一系列优先级函数来实现,这些函数为其分配分数介于 0 到 10 之间,0 表示不适合,10 则表示最适合托管该 pod。调度程序配置还可以为每个优先级函数使用简单的权重(正数值)。每个优先级函数提供的节点分数乘以权重(大多数优先级的默认权重为 1),然后将每个节点从所有优先级获得的分数相加。管理员可以使用这个权重属性,为一些优先级赋予更高的重要性。

16.2.3.2. 选择最适合的节点

节点按照分数排序,系统选择分数最高的节点来托管该 pod。如果多个节点的分数相同,则随机选择其中一个。

16.2.4. 调度程序策略

选择 predicatepriorities 定义调度程序的策略。

调度程序配置文件是一个 JSON 文件,指定调度程序将考虑的 predicates 和 priorities。

如果没有调度程序策略文件,则会应用默认配置文件/etc/origin/master/scheduler.json

重要

调度程序配置文件中定义的 predicates 和 priorities 会完全覆盖默认的调度程序策略。如果需要任何默认的 predicates 和 priorities,您必须在调度程序配置文件中明确指定函数。

默认调度程序配置文件

{
    "apiVersion": "v1",
    "kind": "Policy",
    "predicates": [
        {
            "name": "NoVolumeZoneConflict"
        },
        {
            "name": "MaxEBSVolumeCount"
        },
        {
            "name": "MaxGCEPDVolumeCount"
        },
        {
            "name": "MaxAzureDiskVolumeCount"
        },
        {
            "name": "MatchInterPodAffinity"
        },
        {
            "name": "NoDiskConflict"
        },
        {
            "name": "GeneralPredicates"
        },
        {
            "name": "PodToleratesNodeTaints"
        },
        {
            "argument": {
                "serviceAffinity": {
                    "labels": [
                        "region"
                    ]
                }
            },
            "name": "Region"

         }
    ],
    "priorities": [
        {
            "name": "SelectorSpreadPriority",
            "weight": 1
        },
        {
            "name": "InterPodAffinityPriority",
            "weight": 1
        },
        {
            "name": "LeastRequestedPriority",
            "weight": 1
        },
        {
            "name": "BalancedResourceAllocation",
            "weight": 1
        },
        {
            "name": "NodePreferAvoidPodsPriority",
            "weight": 10000
        },
        {
            "name": "NodeAffinityPriority",
            "weight": 1
        },
        {
            "name": "TaintTolerationPriority",
            "weight": 1
        },
        {
            "argument": {
                "serviceAntiAffinity": {
                    "label": "zone"
                }
            },
            "name": "Zone",
            "weight": 2
        }
    ]
}

16.2.4.1. 修改调度程序策略

除非被主配置文件中的 kubernetesMasterConfig.schedulerConfigFile 字段覆盖,master 上的文件默认在名为 /etc/origin/master/scheduler.json 的文件中定义调度程序策略

修改的调度程序配置文件示例

kind: "Policy"
version: "v1"
"predicates": [
        {
            "name": "PodFitsResources"
        },
        {
            "name": "NoDiskConflict"
        },
        {
            "name": "MatchNodeSelector"
        },
        {
            "name": "HostName"
        },
        {
            "argument": {
                "serviceAffinity": {
                    "labels": [
                        "region"
                    ]
                }
            },
            "name": "Region"
        }
    ],
    "priorities": [
        {
            "name": "LeastRequestedPriority",
            "weight": 1
        },
        {
            "name": "BalancedResourceAllocation",
            "weight": 1
        },
        {
            "name": "ServiceSpreadingPriority",
            "weight": 1
        },
        {
            "argument": {
                "serviceAntiAffinity": {
                    "label": "zone"
                }
            },
            "name": "Zone",
            "weight": 2
        }
    ]

修改调度程序策略:

16.2.5. 可用 predicates

predicates 是用于过滤掉不合格节点的规则。

OpenShift Container Platform 中默认提供一些 predicates。其中的一些 predicates 可以通过提供特定参数来自定义。可以组合多个 predicates 来提供更多节点过滤。

16.2.5.1. 静态 predicates

此类 predicates 不接受任何来自于用户的配置参数或输入。它们通过其确切的名称在调度程序配置中指定。

16.2.5.1.1. 默认 predicates

默认的调度程序策略包括以下 predicates:

NoVolumeZoneConflict 检查区中是否有 pod 请求的卷。

{"name" : "NoVolumeZoneConflict"}

MaxEBSVolumeCount 检查可附加到 AWS 实例的最大卷数。

{"name" : "MaxEBSVolumeCount"}

MaxGCEPDVolumeCount 检查 Google Compute Engine(GCE)持久磁盘(PD)的最大数量。

{"name" : "MaxGCEPDVolumeCount"}

MatchInterPodAffinity 检查 pod 关联性/反关联性规则是否允许该 pod。

{"name" : "MatchInterPodAffinity"}

NoDiskConflict 检查 pod 请求的卷是否可用。

{"name" : "NoDiskConflict"}

PodToleratesNodeTaints 检查 pod 是否可以容忍节点污点。

{"name" : "PodToleratesNodeTaints"}
16.2.5.1.2. 其他静态 predicates

OpenShift Container Platform 还支持下列 predicates:

CheckVolumeBinding 根据它请求的卷(它请求的卷)评估 pod 是否可以适合绑定和未绑定 PVC。* 对于绑定的 PVC, predicate 会检查给定节点是否满足对应 PV 的节点关联性。* 对于未绑定 PVC,该 predicate 会搜索可满足 PVC 要求且给定节点满足 PV 节点关联性的可用 PV。

如果所有绑定 PVC 都有与节点兼容的 PV,且所有未绑定 PVC 都可与可用并兼容节点的 PV 匹配,该 predicate 会返回 true。

{"name" : "CheckVolumeBinding"}

CheckVolumeBinding predicate 必须在非默认调度程序中启用。

CheckNodeCondition 检查 pod 是否可以调度到报告磁盘不足、网络不可用 或未就绪状况的节点

{"name" : "CheckNodeCondition"}

PodToleratesNodeNoExecuteTaints 检查 pod 容限是否容忍节点的 NoExecute 污点

{"name" : "PodToleratesNodeNoExecuteTaints"}

CheckNodeLabelPresence 检查节点上是否存在所有指定的标签,而不考虑它们的值。

{"name" : "CheckNodeLabelPresence"}

checkServiceAffinity 检查 ServiceAffinity 标签是否对于节点上调度的 pod 来说是相同的。

{"name" : "checkServiceAffinity"}

MaxAzureDiskVolumeCount 检查 Azure 磁盘卷的最大数量。

{"name" : "MaxAzureDiskVolumeCount"}

16.2.5.2. 常规 predicates

下列常规 predicates 检查是否通过非关键 predicates 和必要 predicates 的检查。非关键 predicates 是只有非关键 pod 需要通过的 predicates,而必要 predicates 则是所有 pod 都需要通过的 predicates。

默认调度程序策略包含常规 predicates。

非关键常规 predicates

PodFitsResources 根据资源可用性(CPU、内存、GPU 等)来确定适合性。节点可以声明其资源容量,然后 pod 可以指定它们所需要的资源。适合性基于请求的资源,而非使用的资源。

{"name" : "PodFitsResources"}
必要常规 predicates

PodFitsHostPorts 确定节点是否有空闲端口可用于请求的 pod 端口(不存在端口冲突)。

{"name" : "PodFitsHostPorts"}

hostname 将根据存在 Host 参数以及与主机名称匹配的字符串来确定适合性。

{"name" : "HostName"}

MatchNodeSelector 根据 pod 中定义的节点选择器(nodeSelector) 查询来确定适合性。

{"name" : "MatchNodeSelector"}

16.2.5.3. 可配置 predicates

您可以在调度程序配置中配置这些 predicates,默认为/etc/origin/master/scheduler.json,添加可影响 predicate 函数的标签。

由于它们是可配置的,因此可以组合相同类型的多个谓词(但配置参数不同),只要其用户定义的名称不同。

有关使用这些优先级的详情,请参考修改调度程序策略

serviceAffinity 根据该 pod 上运行的服务将 pod 放置到节点上。将同一服务的 pod 放置到相同或共同定位的节点上可提高效率。

此 predicate 会尝试在其节点选择器中将带有特定标签的 pod 放置到具有相同标签的节点。

如果 pod 没有在其节点选择器中指定标签,则第一个 pod 会根据可用性放置到任何节点上,服务的所有后续 pod 都会调度到具有与该节点相同标签值的节点上。

"predicates":[
      {
         "name":"<name>", 1
         "argument":{
            "serviceAffinity":{
               "labels":[
                  "<label>" 2
               ]
            }
         }
      }
   ],
1
为 predicate 指定名称。
2
指定要匹配的标签。

例如:

        "name":"ZoneAffinity",
        "argument":{
            "serviceAffinity":{
                "labels":[
                    "rack"
                ]
            }
        }

例如:如果服务的第一个 pod 具有节点选择器 rack,则会将标签为 region=rack 的节点调度到具有相同 region=rack 标签的所有其他后续 pod。如需更多信息,请参阅控制 Pod 放置

也支持多级标签。用户也可以指定服务要调度到同一地区内、同一区域(位于区域下)的所有 pod。

labelsPresence 参数检查特定节点是否有特定标签。标签创建 LabelPreference 优先级使用的节点组。例如,通过标签匹配非常有用,其中节点具有按标签定义的物理位置或状态。

"predicates":[
      {
         "name":"<name>", 1
         "argument":{
            "labelsPresence":{
               "labels":[
                  "<label>" 2
                ],
                "presence": true 3
            }
         }
      }
   ],
1
为 predicate 指定名称。
2
指定要匹配的标签。
3
指定是否需要标签,可以是 truefalse
  • 对于 presence:false,如果节点标签中存在任何请求的标签,则无法调度 pod。如果标签不存在,可以调度 pod。
  • 对于 presence:true,如果节点标签中存在所有请求的标签,则可以调度 pod。如果没有所有标签,则不会调度 pod。

例如:

        "name":"RackPreferred",
        "argument":{
            "labelsPresence":{
                "labels":[
                    "rack",
                    "region"
                ],
                "presence": true
            }
        }

16.2.6. 可用优先级

优先级是根据偏好排列剩余节点等级的规则。

可以指定一组自定义优先级来配置调度程序。OpenShift Container Platform 中默认提供一些优先级。也可通过提供某些参数来自定义其他优先级。可将多个优先级合并,并为每个优先级赋予不同的权重来影响优先顺序。

16.2.6.1. 静态优先级

静态优先级不使用用户提供的配置参数,但权重除外。权重必须指定,且不能为 0 或负数。

这些是在调度程序配置中指定的,默认为/etc/origin/master/scheduler.json

16.2.6.1.1. 默认优先级

默认调度程序策略包括以下优先级。每个优先级函数的权重为 1,但 NodePreferAvoidPodsPriority 的权重为 10000

SelectorSpreadPriority 查找与 pod 匹配的服务、复制控制器 (RC)、复制集 (RS) 和有状态集,然后查找与这些选择器匹配的现有 pod。调度程序优先选择具有较少现有匹配 pod 的节点。然后,它会将 pod 调度到具有与所调度 pod 的选择器匹配的 pod 数量最少的节点上。

{"name" : "SelectorSpreadPriority", "weight" : 1}

InterPodAffinityPriority 通过迭代 weightedPodAffinityTerm 元素并在节点满足对应的 PodAffinityTerm 时加上权重来计算总和。总和最高的节点是优先级最高的节点。

{"name" : "InterPodAffinityPriority", "weight" : 1}

LeastRequestedPriority 优先选择请求资源较少的节点。它计算节点上调度的 pod 所请求的内存和 CPU 百分比,并优先选择可用/剩余容量最高的节点。

{"name" : "LeastRequestedPriority", "weight" : 1}

BalancedResourceAllocation 优先选择资源使用率均衡的节点。它以占容量比形式计算 CPU 和内存已使用量的差值,并基于两个指标相互接近的程度来优先选择节点。这应该总是与 LeastRequestedPriority 一同使用。

{"name" : "BalancedResourceAllocation", "weight" : 1}

NodePreferAvoidPodsPriority 忽略除复制控制器以外的控制器拥有的 pod。

{"name" : "NodePreferAvoidPodsPriority", "weight" : 10000}

NodeAffinityPriority 根据节点关联性调度偏好来排列节点的优先顺序

{"name" : "NodeAffinityPriority", "weight" : 1}

TaintTolerationPriority 为 pod 优先考虑那些具有较少不可容忍的污点的节点。一个不可容忍的污点是键为 PreferNoSchedule 的污点。

{"name" : "TaintTolerationPriority", "weight" : 1}
16.2.6.1.2. 其他静态优先级

OpenShift Container Platform 还支持下列优先级:

如果没有提供优先级配置,则EqualPriority 为所有节点赋予相等的权重 1。建议您仅在测试环境中使用此优先级。

{"name" : "EqualPriority", "weight" : 1}

MostRequestedPriority 优先选择具有最多所请求资源的节点。它计算节点上调度的 pod 所请求的内存与 CPU 百分比,并根据请求量对容量的平均占比的最大值来排列优先级。

{"name" : "MostRequestedPriority", "weight" : 1}

ImageLocalityPriority 优先选择已请求了 pod 容器镜像的节点。

{"name" : "ImageLocalityPriority", "weight" : 1}

ServiceSpreadingPriority 通过尽量减少将属于同一服务的 pod 分配到同一台机器的数量来分散 pod。

{"name" : "ServiceSpreadingPriority", "weight" : 1}

16.2.6.2. 可配置优先级

您可以在调度程序配置中配置这些优先级(默认为/etc/origin/master/scheduler.json ),以添加可影响优先级的标签。

优先级函数的类型由它们所使用的参数来标识。由于它们是可配置的,因此可以组合类型相同(但配置参数不同)的多个优先级,但前提是它们的用户定义名称不同。

有关使用这些优先级的详情,请参考修改调度程序策略

ServiceAntiAffinity 接受一个标签,确保将属于同一服务的 pod 正常地分散到基于标签值的一组节点。它为指定标签值相同的所有节点赋予相同的分数。它将较高的分数给予组内 pod 密度最低的节点。

"priorities":[
    {
        "name":"<name>", 1
        "weight" : 1 2
        "argument":{
            "serviceAntiAffinity":{
                "label":[
                    "<label>" 3
                ]
            }
        }
    }
]
1
指定优先级的名称。
2
指定权重。输入非零正数值。
3
指定要匹配的标签。

例如:

        "name":"RackSpread", 1
        "weight" : 1 2
        "argument":{
            "serviceAntiAffinity":{
                "label": "rack" 3
            }
        }
1
指定优先级的名称。
2
指定权重。输入非零正数值。
3
指定要匹配的标签。
注意

在某些情况下,基于自定义标签的 ServiceAntiAffinity 不能按预期分散 pod。请参考此红帽解决方案

* labelPreference 参数根据指定的标签赋予优先级。如果节点上存在该标签,则该节点被赋予优先级。如果未指定标签,则为没有标签的节点赋予优先级。

"priorities":[
    {
        "name":"<name>", 1
        "weight" : 1, 2
        "argument":{
            "labelPreference":{
                "label": "<label>", 3
                "presence": true 4
            }
        }
    }
]
1
指定优先级的名称。
2
指定权重。输入非零正数值。
3
指定要匹配的标签。
4
指定是否需要该标签,可以是 truefalse

16.2.7. 使用案例

在 OpenShift Container Platform 中调度的一个重要用例是支持灵活的关联性和反关联性策略。

16.2.7.1. 基础架构拓扑级别

管理员可以通过在节点上指定标签(如 region=r1zone=z1rack=s1)为其基础架构(节点 )定义多个拓扑级别。

这些标签名称没有特别含义,管理员可以自由地命名其基础架构级别的任何级别(例如,城市/建筑/房间)。另外,管理员可以为其基础架构拓扑定义任意数量的级别,通常三个级别足够(例如: regionszonesracks)。管理员可以在各个级别上以任何组合指定关联性和反关联性规则。

16.2.7.2. 关联性

管理员应能够配置调度程序,在任何一个甚至多个拓扑级别上指定关联性。特定级别上的关联性指示所有属于同一服务的 pod 调度到属于同一级别的节点。这会让管理员确保对等 pod 在地理上不会过于分散,以此处理应用程序对延迟的要求。如果同一关联性组中没有节点可用于托管 pod,则不调度该 pod。

如果需要更好地控制 pod 的调度位置,请参阅使用节点关联性和使用 Pod 关联性和反关联性。管理员可以利用这些高级调度功能,来指定 pod 可以调度到哪些节点,并且相对于其他 pod 来强制或拒绝调度。

16.2.7.3. 反关联性

管理员应能够配置调度程序,在任何一个甚至多个拓扑级别上指定反关联性。特定级别上的反关联性(或分散)指示属于同一服务的所有 pod 分散到属于该级别的不同节点上。这样可确保应用程序合理分布,以实现高可用性目的。调度程序尝试在所有适用的节点之间尽可能均匀地平衡服务 pod。

如果需要更好地控制 pod 的调度位置,请参阅使用节点关联性和使用 Pod 关联性和反关联性。管理员可以利用这些高级调度功能,来指定 pod 可以调度到哪些节点,并且相对于其他 pod 来强制或拒绝调度。

16.2.8. 策略配置示例

以下配置如果通过调度程序策略文件指定,则指定默认的调度程序配置。

kind: "Policy"
version: "v1"
predicates:
...
  - name: "RegionZoneAffinity" 1
    argument:
      serviceAffinity: 2
        labels: 3
          - "region"
          - "zone"
priorities:
...
  - name: "RackSpread" 4
    weight: 1
    argument:
      serviceAntiAffinity: 5
        label: "rack" 6
1
predicate 的名称。
2
3
predicate 的标签。
4
优先级的名称。
5
6
优先级的标签。

在下方的所有示例配置中,predicates 和 priorities 函数的列表都已截断,仅包含与指定用例相关的内容。在实践中,完整/有意义的调度程序策略应当包含前文所述的大部分(若非全部)默认 predicates 和 priorities。

以下示例定义了三个拓扑级别,即 region(关联性)-> zone(关联性)-> rack(反关联性):

kind: "Policy"
version: "v1"
predicates:
...
  - name: "RegionZoneAffinity"
    argument:
      serviceAffinity:
        labels:
          - "region"
          - "zone"
priorities:
...
  - name: "RackSpread"
    weight: 1
    argument:
      serviceAntiAffinity:
        label: "rack"

以下示例定义了三个拓扑级别,即 city(关联性)-> building(反关联性)-> room(反关联性):

kind: "Policy"
version: "v1"
predicates:
...
  - name: "CityAffinity"
    argument:
      serviceAffinity:
        labels:
          - "city"
priorities:
...
  - name: "BuildingSpread"
    weight: 1
    argument:
      serviceAntiAffinity:
        label: "building"
  - name: "RoomSpread"
    weight: 1
    argument:
      serviceAntiAffinity:
        label: "room"

以下示例定义了一个策略,以仅使用定义了“region”标签的节点,并且优先选择定义有“zone”标签的节点:

kind: "Policy"
version: "v1"
predicates:
...
  - name: "RequireRegion"
    argument:
      labelsPresence:
        labels:
          - "region"
        presence: true
priorities:
...
  - name: "ZonePreferred"
    weight: 1
    argument:
      labelPreference:
        label: "zone"
        presence: true

以下示例组合使用静态和可配置的 predicates 和 priorities:

kind: "Policy"
version: "v1"
predicates:
...
  - name: "RegionAffinity"
    argument:
      serviceAffinity:
        labels:
          - "region"
  - name: "RequireRegion"
    argument:
      labelsPresence:
        labels:
          - "region"
        presence: true
  - name: "BuildingNodesAvoid"
    argument:
      labelsPresence:
        labels:
          - "building"
        presence: false
  - name: "PodFitsPorts"
  - name: "MatchNodeSelector"
priorities:
...
  - name: "ZoneSpread"
    weight: 2
    argument:
      serviceAntiAffinity:
        label: "zone"
  - name: "ZonePreferred"
    weight: 1
    argument:
      labelPreference:
        label: "zone"
        presence: true
  - name: "ServiceSpreadingPriority"
    weight: 1

16.3. 取消调度

16.3.1. 概述

分离涉及根据特定的策略驱除 pod,以便可将 pod 重新调度到更合适的节点上。

集群可以从取消调度和重新调度已在运行的 pod 中受益,原因如下:

  • 节点使用不足或过度使用。
  • Pod 和节点关联性要求(如污点或标签)已更改,并且原始的调度不再适合于某些节点。
  • 节点失败需要移动 pod。
  • 集群中添加了新节点。

descheduler 不调度被驱除的 pod。调度程序自动为被驱除的 pod 执行此任务。

务必要注意,有许多核心组件(如 DNS)对于集群全面运行至关重要,但在常规集群节点而非 master 上运行。如果组件被驱除,集群可能会停止正常工作。要防止 descheduler 删除这些 pod,通过将 scheduler.alpha.kubernetes.io/critical-pod 注解添加到 pod 规格中将 pod 配置为关键 pod。

注意

descheduler 作业被视为关键 pod,可防止 descheduler pod 被 descheduler 驱除。

descheduler 作业和 descheduler pod 在 kube-system 项目中创建,默认创建。

重要

descheduler 只是一个技术预览功能。技术预览功能不包括在红帽生产服务级别协议(SLA)中,且其功能可能并不完善。因此,红帽不建议在生产环境中使用它们。这些技术预览功能可以使用户提早试用新的功能,并有机会在开发阶段提供反馈意见。

如需红帽技术预览功能支持范围的更多信息,请参阅 https://access.redhat.com/support/offerings/techpreview/

descheduler 不会驱除以下类型的 pod:

注意

在 Burstable 和 Guaranteed pod 之前,pod 会被驱除。

以下小节描述了配置和运行 descheduler 的流程:

16.3.2. 创建集群角色

配置 descheduler 在 pod 中正常工作所需的权限:

  1. 使用以下规则创建集群角色

    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
      name: descheduler-cluster-role
    rules:
    - apiGroups: [""]
      resources: ["nodes"]
      verbs: ["get", "watch", "list"] 1
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "watch", "list", "delete"] 2
    - apiGroups: [""]
      resources: ["pods/eviction"] 3
      verbs: ["create"]
    1
    配置该角色以允许查看节点。
    2
    配置该角色以允许查看和删除容器集。
    3
    允许节点驱除绑定到其自身的 pod。
  2. 创建用于运行作业的服务帐户

    # oc create sa <file-name>.yaml -n kube-system

    例如:

    # oc create sa descheduler-sa.yaml -n kube-system
  3. 将集群角色绑定到服务帐户:

    # oc create clusterrolebinding descheduler-cluster-role-binding \
        --clusterrole=<cluster-role-name> \
        --serviceaccount=kube-system:<service-account-name>

    例如:

    # oc create clusterrolebinding descheduler-cluster-role-binding \
        --clusterrole=descheduler-cluster-role \
        --serviceaccount=kube-system:descheduler-sa

16.3.3. 创建 Descheduler 策略

您可以将 descheduler 配置为从违反了 YAML 策略文件中策略定义的规则的节点中删除 pod。然后,您可以创建一个包含策略文件路径的配置映射以及使用该配置映射应用特定取消调度策略 的作业规格

descheduler 策略文件示例

apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
  "RemoveDuplicates":
     enabled: false
  "LowNodeUtilization":
     enabled: true
     params:
       nodeResourceUtilizationThresholds:
         thresholds:
           "cpu" : 20
           "memory": 20
           "pods": 20
         targetThresholds:
           "cpu" : 50
           "memory": 50
           "pods": 50
         numberOfNodes: 3
  "RemovePodsViolatingInterPodAntiAffinity":
     enabled: true
  "RemovePodsViolatingNodeAffinity":
    enabled: true
    params:
      nodeAffinityType:
      - "requiredDuringSchedulingIgnoredDuringExecution"

有三个可与 descheduler 一起使用的默认策略:

您可以根据需要配置和禁用与策略关联的参数。

16.3.3.1. 删除重复的 Pod

RemoveDuplicates 策略确保只有一个 pod 与同一节点上运行的 aReplica Set、ReplicationController、部署配置或 作业相关联。如果存在与这些对象关联的其他 pod,则重复的 pod 会被驱除。删除重复的 pod 会导致在集群中更好地分散 pod。

例如,如果某个节点失败并且节点上的 pod 移到另一个节点,导致多个与 Replica Set 或 Replication Controller 关联的 pod,在同一节点上运行,可能会出现重复的 pod。当出现故障的节点再次就绪后,此策略可用于驱除这些重复的 pod。

没有与此策略关联的参数。

apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
  "RemoveDuplicates":
     enabled: false 1
1
将此值设置为 enabled: true 来使用此策略。设置为 false 以禁用此策略。

16.3.3.2. 创建低节点利用率策略

LowNodeUtilization 策略会查找使用率不足的节点,并从其他节点上驱除 pod,以便这些被驱除的 pod 可以在使用率不足的节点上调度。

节点使用率不足是由 CPU、内存或 pod 数量可配置的阈值 thresholds (基于百分比)决定的。如果节点用量低于所有这些阈值,则该节点会被视为使用率不足,descheduler 可以从其他节点驱除 pod。计算节点资源利用率时会考虑 Pod 请求资源要求。

高阈值 targetThresholds 用于确定正确使用的节点。阈值和 targetThresholds 之间的任何节点都将被正确使用,不考虑驱除。阈值 targetThresholds 可以针对 CPU、内存和 pod 数量(基于百分比)进行配置。

可以针对集群的要求调整这些阈值。

numberOfNodes 参数可以配置为仅在使用率不足的节点超过配置的值时激活策略。如果一些节点利用率不足,则设置这个参数。默认情况下,numberOfNodes 设置为零。

apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
  "LowNodeUtilization":
     enabled: true
     params:
       nodeResourceUtilizationThresholds:
         thresholds: 1
           "cpu" : 20
           "memory": 20
           "pods": 20
         targetThresholds: 2
           "cpu" : 50
           "memory": 50
           "pods": 50
         numberOfNodes: 3 3
1
设置低端阈值。如果节点低于所有三个值,则 descheduler 会认为节点使用率不足。
2
设置高端阈值。如果节点低于这些值,且大于 threshold 值,则 descheduler 会考虑节点正确使用。
3
设置在 descheduler 将从使用率低的节点驱除 pod 前可使用率不足的节点数量。

16.3.3.3. 移除 Pod 冲突间的关联性

RemovePodsViolatingInterPodAntiAffinity 策略确保违反了 pod 间的反关联性的 pod 被从节点中驱除。

例如,Node1 具有 podA、podBpodCpodBpodC 具有反关联性规则,阻止它们在与 podA 相同的节点上运行。podA 将从该节点上运行,以便 podBpodC 可以在该节点上运行。当 podBpodC 在节点上运行时,会出现这种情况。

apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
  "RemovePodsViolatingInterPodAntiAffinity": 1
     enabled: true
1
将此值设置为 enabled: true 来使用此策略。设置为 false 以禁用此策略。

16.3.3.4. 删除 Pod 冲突节点关联性

RemovePodsViolatingNodeAffinity 策略确保违反了节点关联性的所有 pod 都从节点中移除。当节点不再满足 pod 的关联性规则,会出现这种情况。如果存在另外一个节点来满足关联性规则,则 pod 会被驱除。

例如,podA 被调度到 nodeA 上,因为节点在调度时满足 requiredDuringSchedulingIgnoredDuringExecution 节点关联性规则。如果 nodeA 停止满足规则,并且有另外一个节点满足节点关联性规则,则策略会从 nodeA 驱除 pod A 并将其移到其他节点。

apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
  "RemovePodsViolatingNodeAffinity": 1
    enabled: true
    params:
      nodeAffinityType:
      - "requiredDuringSchedulingIgnoredDuringExecution" 2
1
将此值设置为 enabled: true 来使用此策略。设置为 false 以禁用此策略。
2
指定 requiredDuringSchedulingIgnoredDuringExecution 节点关联性类型。

16.3.4. 为 Descheduler 策略创建配置映射

kube-system 项目中的 descheduler 策略文件创建配置映射,以便 descheduler 作业可以引用它。

# oc create configmap descheduler-policy-configmap \
     -n kube-system --from-file=<path-to-policy-dir/policy.yaml> 1
1
您创建的策略文件的路径。

16.3.5. 创建作业规格

为 descheduler 创建作业配置

apiVersion: batch/v1
kind: Job
metadata:
  name: descheduler-job
  namespace: kube-system
spec:
  parallelism: 1
  completions: 1
  template:
    metadata:
      name: descheduler-pod 1
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: "true" 2
    spec:
        containers:
        - name: descheduler
          image: registry.access.redhat.com/openshift3/ose-descheduler
          volumeMounts: 3
          - mountPath: /policy-dir
            name: policy-volume
          command:
          - "/bin/sh"
          - "-ec"
          - |
            /bin/descheduler --policy-config-file /policy-dir/policy.yaml 4
        restartPolicy: "Never"
        serviceAccountName: descheduler-sa 5
        volumes:
        - name: policy-volume
          configMap:
            name: descheduler-policy-configmap
1
为作业指定一个名称。
2
配置 pod,使其不会取消调度。
3
应挂载作业的容器中的卷名称和挂载路径。
4
5
指定您创建的服务帐户的名称。

策略文件作为卷从配置映射中挂载。

16.3.6. 运行 Descheduler

以 pod 中作业的形式运行 descheduler:

# oc create -f <file-name>.yaml

例如:

# oc create -f descheduler-job.yaml

16.4. 自定义调度

16.4.1. 概述

您可以运行多个自定义调度程序以及默认调度程序,并配置要用于各个 pod 的调度程序。

要使用特定的调度程序调度给定 pod,请在 pod 规格中指定调度程序的名称

注意

有关如何创建调度程序二进制文件的信息超出了本文档的讨论范围。例如,请参阅 Kubernetes 文档中的配置多个调度程序

16.4.2. 打包调度程序

在集群中包含自定义调度程序的常规流程涉及创建镜像,并将该镜像包含在部署中。

  1. 将调度程序二进制文件打包到容器镜像中。
  2. 创建包含调度程序二进制文件的容器镜像

    例如:

    FROM <source-image>
    ADD <path-to-binary> /usr/local/bin/kube-scheduler
  3. 将文件保存为 Dockerfile,构建镜像并将其推送到注册表。

    例如:

    docker build -t <dest_env_registry_ip>:<port>/<namespace>/<image name>:<tag>
    docker push <dest_env_registry_ip>:<port>/<namespace>/<image name>:<tag>
  4. 在 OpenShift Container Platform 中,为自定义调度程序创建一个部署。

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: custom-scheduler
      namespace: kube-system
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: custom-scheduler
    subjects:
    - kind: ServiceAccount
      name: custom-scheduler
      namespace: kube-system
    roleRef:
      kind: ClusterRole
      name: system:kube-scheduler
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: custom-scheduler
      namespace: kube-system
      labels:
        app: custom-scheduler
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: custom-scheduler
      template:
        metadata:
          labels:
            app: custom-scheduler
        spec:
          serviceAccount: custom-scheduler
          containers:
            - name: custom-scheduler
              image: "<namespace>/<image name>:<tag>" 1
              imagePullPolicy: Always
    1
    指定您为自定义调度程序创建的容器镜像。

16.4.3. 使用自定义调度程序部署 Pod

在集群中部署自定义调度程序后,您可以将 pod 配置为使用该调度程序,而不是默认调度程序。

  1. 创建或编辑 pod 配置,并使用 schedulerName 参数指定调度程序的名称。名称必须是唯一的。

    使用调度程序的 pod 规格示例

    apiVersion: v1
    kind: Pod
    metadata:
      name: custom-scheduler-example
      labels:
        name: custom-scheduler-example
    spec:
      schedulerName: custom-scheduler 1
      containers:
      - name: pod-with-second-annotation-container
        image: docker.io/ocpqe/hello-pod

    1
    要使用的调度程序的名称。如果没有提供调度程序名称,pod 会自动使用默认调度程序来调度。
  2. 运行以下命令来创建 pod:

    $ oc create -f <file-name>.yaml

    例如:

    $ oc create -f custom-scheduler-example.yaml
  3. 运行以下命令来检查 pod 是否已创建:

    $ oc get pod <file-name>

    例如:

    $ oc get pod custom-scheduler-example
    
    NAME                       READY     STATUS    RESTARTS   AGE
    custom-scheduler-example   1/1       Running   0          4m
  4. 运行以下命令来检查自定义调度程序是否调度了 pod:

    $ oc describe pod <pod-name>

    例如:

    $ oc describe pod custom-scheduler-example

    列出了调度程序的名称,如以下截断的输出所示:

    ...
    
    Events:
      FirstSeen  LastSeen  Count  From                SubObjectPath  Type       Reason Message
      ---------  --------  -----  ----                -------------  --------   ------ -------
      1m         1m        1      custom-scheduler    Normal         Scheduled  Successfully assigned custom-scheduler to <$node1>
    
    ...

16.5. 控制 Pod 放置

16.5.1. 概述

作为集群管理员,您可以设置策略,以防止具有特定角色的应用开发人员在调度 pod 时定位特定的节点。

Pod 节点 Constraints 准入控制器确保 pod 只使用标签部署到指定的节点主机上,并防止没有特定角色的用户使用 nodeSelector 字段调度 pod。

16.5.2. 使用节点名称限制 Pod 放置

使用 Pod 节点约束准入控制器,通过为其分配标签并在 pod 配置的 nodeName 设置中指定它,确保 pod 只部署到指定节点主机上。

  1. 确保您有所需的标签(请参阅在节点上 Updating Labelss)和 节点选择器设置

    例如,请确保您的 pod 配置具有指示所需标签的 nodeName 值:

    apiVersion: v1
    kind: Pod
    spec:
      nodeName: <value>
  2. 修改 master 配置文件/etc/origin/master/master-config.yaml,将 PodNodeConstraints 添加到 admissionConfig 部分:

    ...
    admissionConfig:
      pluginConfig:
        PodNodeConstraints:
          configuration:
            apiversion: v1
            kind: PodNodeConstraintsConfig
    ...
  3. 重启 OpenShift Container Platform 以使更改生效。

    # master-restart api
    # master-restart controllers

16.5.3. 使用 Node Selector 约束 Pod 放置

使用节点选择器,您可以确保 pod 只放置到具有特定标签的节点上。作为集群管理员,您可以使用 Pod 节点约束准入控制器来设置一个策略,防止没有 pod/绑定权限的用户使用节点选择器来调度 pod。

master 配置文件的 nodeSelectorLabelBlacklist 字段可让您控制某些角色可在 pod 配置的 nodeSelector 字段中指定的标签。具有 pod/binding 权限角色的用户、服务帐户和组可以指定任何节点选择器。没有 pod/绑定权限的组群会被禁止为出现在 nodeSelectorLabelBlacklist 中的任何标签设置 nodeSelector

例如,OpenShift Container Platform 集群可能包含五个数据中心,分布在两个区域。在美国,us-east、us -centralus-west ;在亚太地区 (APAC)、apac-east 和apac-west。每个地理区域中的每个节点都会进行相应的标记。例如: region: us-east

作为集群管理员,您可以创建一个基础架构,应用程序开发人员应在最接近其地理位置的节点上部署 pod。您可以创建一个节点选择器,将美国数据中心分组到 superregion: us 中,并将 APAC 数据中心分组到 superregion: apac 中。

要维护每个数据中心的资源载入,您可以在 master 配置的 nodeSelectorLabelBlacklist 部分添加所需的 region。然后,每当位于美国的开发人员创建 pod 时,它会部署到具有 superregion: us 标签的一个区域的节点。如果开发人员尝试将其 pod 的特定区域作为目标(例如 region: us-east),则他们会收到错误。如果他们再次尝试,其 pod 上没有节点选择器,它仍然可以部署到他们尝试的目标区域,因为 superregion: us 被设置为项目级别节点选择器,标记为 region: us-east 的节点也会被标记为 superregion: us

  1. 确保您有所需的标签(请参阅在节点上 Updating Labelss)和 节点选择器设置

    例如,请确保您的 pod 配置具有指示所需标签的 nodeSelector 值:

    apiVersion: v1
    kind: Pod
    spec:
      nodeSelector:
        <key>: <value>
    ...
  2. 修改 master 配置文件/etc/origin/master/master-config.yaml,将 nodeSelectorLabelBlacklist 添加到带有您要拒绝 pod 放置的节点主机的标签的 admissionConfig 部分:

    ...
    admissionConfig:
      pluginConfig:
        PodNodeConstraints:
          configuration:
            apiversion: v1
            kind: PodNodeConstraintsConfig
            nodeSelectorLabelBlacklist:
              - kubernetes.io/hostname
              - <label>
    ...
  3. 重启 OpenShift Container Platform 以使更改生效。

    # master-restart api
    # master-restart controllers

16.5.4. 控制 Pod 放置到项目

Pod Node Selector 准入控制器允许您将 pod 强制到与特定项目关联的节点上,并防止 pod 调度到这些节点上。

Pod Node Selector 准入控制器使用 pod 中指定的项目和节点选择器的标签决定 pod 放置的位置。只有 pod 中的节点选择器与项目中的标签匹配时,才会将新 pod 放置到与项目关联的节点上。

在 pod 创建后,节点选择器合并到 pod 中,以便 pod 规格包括最初包含在规格中的标签以及节点选择器中的任何新标签。以下示例说明了合并效果。

Pod Node Selector 准入控制器还允许您创建在特定项目中允许的标签列表。此列表充当白名单,使开发人员能够知道可在项目中使用哪些标签,并让管理员更好地控制集群中的标签。

激活 Pod Node Selector 准入控制器

  1. 使用以下方法之一配置 Pod Node Selector 准入控制器和白名单:

    • 将以下内容添加到 master 配置文件/etc/origin/master/master-config.yaml 中:

      admissionConfig:
        pluginConfig:
          PodNodeSelector:
            configuration:
              podNodeSelectorPluginConfig: 1
                clusterDefaultNodeSelector: "k3=v3" 2
                ns1: region=west,env=test,infra=fedora,os=fedora 3
      1
      添加 Pod Node Selector 准入控制器插件
      2
      为所有节点创建默认标签。
      3
      在指定项目中创建允许的标签白名单。在这里,项目是 ns1,标签是 key=value 对。
    • 创建包含准入控制器信息的文件:

      podNodeSelectorPluginConfig:
          clusterDefaultNodeSelector: "k3=v3"
           ns1: region=west,env=test,infra=fedora,os=fedora

      然后,在 master 配置中引用该文件:

      admissionConfig:
        pluginConfig:
          PodNodeSelector:
            location: <path-to-file>
      注意

      如果项目没有指定节点选择器,则与该项目关联的 pod 将使用默认节点选择器(clusterDefaultNodeSelector)合并。

  2. 重启 OpenShift Container Platform 以使更改生效。

    # master-restart api
    # master-restart controllers
  3. 创建包含 scheduler.alpha.kubernetes.io/node-selector 注解和标签的项目对象。

    apiVersion: v1
    kind: Namespace
    metadata
      name: ns1
      annotations:
        scheduler.alpha.kubernetes.io/node-selector: env=test,infra=fedora 1
    spec: {},
    status: {}
    1
    创建标签以匹配项目标签选择器的注释。此处键/值标签为 env=testinfra=fedora
    注意

    使用 Pod Node Selector 准入控制器时,您无法使用 oc adm new-project <project-name> 来设置项目节点选择器。当您使用 oc adm new-project myproject --node-selector='type=user-node,region=<region> 命令设置项目节点选择器时,OpenShift Container Platform 会设置 openshift.io/node-selector 注解,该注解由 NodeEnv 准入插件处理。

  4. 创建在节点选择器中包含标签的 pod 规格,例如:

    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        name: hello-pod
      name: hello-pod
    spec:
      containers:
        - image: "docker.io/ocpqe/hello-pod:latest"
          imagePullPolicy: IfNotPresent
          name: hello-pod
          ports:
            - containerPort: 8080
              protocol: TCP
          resources: {}
          securityContext:
            capabilities: {}
            privileged: false
          terminationMessagePath: /dev/termination-log
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      nodeSelector: 1
        env: test
        os: fedora
      serviceAccount: ""
    status: {}
    1
    与项目标签匹配的节点选择器。
  5. 在项目中创建 pod:

    # oc create -f pod.yaml --namespace=ns1
  6. 检查节点选择器标签是否已添加到 pod 配置中:

    get pod pod1 --namespace=ns1 -o json
    
    nodeSelector": {
     "env": "test",
     "infra": "fedora",
     "os": "fedora"
    }

    节点选择器合并到 pod 中,容器集应当调度到适当的项目中。

如果您创建了一个没有在项目规格中指定的标签的 pod,则 pod 不会被调度到该节点上。

例如,这里的标签 env: production 不在任何项目规格中:

nodeSelector:
 "env: production"
 "infra": "fedora",
 "os": "fedora"

如果有一个没有节点选择器注解的节点,则 pod 会在该处调度。

16.6. Pod 优先级和抢占功能

16.6.1. 应用 pod 优先级和抢占

您可以在集群中启用 pod 优先级与抢占功能。Pod 优先级指明 pod 相对于其他 pod 的重要程度,并根据这个优先级对 pod 进行排队。Pod 抢占则允许集群驱除低优先级 pod 或与之争抢,从而在合适的节点上没有可用空间时能够调度优先级较高的 pod。pod 优先级也会影响 pod 的调度顺序以及节点上资源不足驱除顺序。

要使用优先级和抢占功能,您需要创建优先级类来定义 pod 的相对权重。然后,在 pod 规格中引用优先级类,以应用这个权重来进行调度。

抢占由调度程序配置文件中的 disablePreemption 参数控制,该参数默认设置为 false

16.6.2. 关于 pod 优先级

当启用 Pod 优先级和抢占功能时,调度程序会按照优先级调度待处理 pod,并将待处理 pod 放在调度队列中优先级较低的其他待处理 pod 的前面。因此,如果达到调度要求,较高优先级的 pod 可能比低优先级的 pod 更早调度。如果 pod 无法调度,调度程序会继续调度其他较低优先级 pod。

16.6.2.1. Pod 优先级类

您可以为 pod 分配一个优先级类,它是一种非命名空间的对象,用于定义从名称到优先级整数值的映射。数值越大,优先级越高。

优先级类对象可以取小于或等于 1000000000(十亿)的 32 位整数值。对于不应被抢占或驱除的关键 pod,可保留大于十亿的数值。默认情况下,OpenShift Container Platform 有两个保留优先级类,用于需要保证调度的关键系统 pod。

  • system-node-critical - 此优先级类的值为 2000001000,用于不应从节点驱除的所有 pod。具有此优先级类的 pod 示例有 sdn-ovs 和 sdn 等。
  • system-cluster-critical - 此优先级类的值为 2000000000(二十亿),用于对集群而言很重要的 pod。在某些情况下,具有此优先级类的 Pod 可以从节点中驱除。例如,配置了 system-node-critical 优先级类的 pod 可以具有优先权。不过,此优先级类确实能够保证调度。具有此优先级类的 pod 示例有 fluentd 以及 descheduler 这样的附加组件等。
注意

如果升级现有的集群,则现有 pod 的优先级相当于为零。但是,带有 scheduler.alpha.kubernetes.io/critical-pod 注解的现有 pod 会自动转换为 system-cluster-critical 类。

16.6.2.2. Pod 优先级名称

拥有一个或多个优先级类后,您可以创建 pod,并在 pod 规格中指定优先级类名称。优先准入控制器使用优先级类名称字段来填充优先级的整数值。如果没有找到给定名称的优先级类,pod 将被拒绝。

以下 YAML 是使用上例中创建的优先级类的 pod 配置示例:优先级准入控制器检查规格,并将 pod 的优先级解析为 1000000。

16.6.3. 关于 pod 抢占

当开发人员创建 pod 时,pod 会排入某一队列。当启用 Pod 优先级和抢占功能时,调度程序会从队列中选择一个 pod,并尝试将 pod 调度到节点上。如果调度程序无法在满足 pod 的所有指定要求的适当节点上找到空间,则会为待处理 pod 触发抢占逻辑。

当调度程序在一个节点上抢占一个或多个 pod 时,优先级较高的 pod 规格的 nominatedNodeName 字段被设置为节点的名称,nodename 字段。调度程序使用 nominatedNodeName 字段跟踪为 pod 保留的资源,并为用户提供有关集群中抢占的信息。

在调度程序抢占了某一较低优先级 pod 后,调度程序会尊重该 pod 的安全终止期限。如果在调度程序等待较低优先级 pod 终止过程中另一节点变为可用,调度程序会将较高优先级 pod 调度到该节点上。因此,pod 规格的 nominatedNodeName 字段和 nodeName 字段可能会有所不同。

另外,如果调度程序在某一节点上抢占 pod 并正在等待终止,这时又有优先级比待处理 pod 高的 pod 需要调度,那么调度程序可以改为调度这个优先级更高的 pod。在这种情况下,调度程序会清除待处理 pod 的 nominatedNodeName,使 pod 有资格调度到另一节点。

抢占不一定从节点中移除所有较低优先级 pod。调度程序可以通过移除一部分较低优先级 pod 调度待处理 pod。

只有待处理 pod 能够调度到节点时,调度程序才会对这个节点考虑 pod 抢占。

16.6.3.1. Pod 抢占和其他调度程序设置

如果启用 pod 优先级与抢占功能,请考虑其他的调度程序设置:

pod 优先级和 pod 中断预算
pod 中断预算指定某一时间必须保持在线的副本的最小数量或百分比。如果您指定了 pod 中断预算,OpenShift Container Platform 会在抢占 pod 时尽力尊重这些预算。调度程序会尝试在不违反 pod 中断预算的前提下抢占 pod。如果找不到这样的 pod,则可能会无视 pod 中断预算要求而抢占较低优先级 pod。
pod 优先级和 pod 关联性
pod 关联性要求将新 pod 调度到与具有同样标签的其他 pod 相同的节点上。

如果待处理 pod 与节点上的一个或多个低优先级 pod 具有 pod 间关联性,调度程序就不能在不违反关联要求的前提下抢占较低优先级 pod。这时,调度程序会寻找其他节点来调度待处理 pod。但是,不能保证调度程序能够找到合适的节点,因此可能无法调度待处理 pod。

要防止这种情况,请仔细配置优先级相同的 pod 的 pod 关联性。

16.6.3.2. 安全终止被抢占的 pod

在抢占 pod 时,调度程序会等待 pod 安全终止期限过期,允许 pod 完成工作并退出。如果 pod 在到期后没有退出,调度程序会终止该 pod。此安全终止期限会在调度程序抢占该 pod 的时间和待处理 pod 调度到节点的时间之间造成一个时间差。

要尽量缩短这个时间差,可以为较低优先级 pod 配置较短的安全终止期限。

16.6.4. Pod 优先级示例场景

Pod 优先级和抢占为 pod 分配优先级,以进行调度。调度程序将抢占(驱除)较低优先级 pod,从而调度优先级较高的 pod。

典型的抢占场景

Pod P 是一个待处理 pod。

  1. 调度程序会找到 Node N,删除一个或多个 pod 会导致 Pod P 调度到该节点上。
  2. 调度程序从 Node N 中删除较低优先级 pod,并将 Pod P 调度到该节点上。
  3. Pod PnominatedNodeName 字段被设置为 Node N 的名称。
注意

Pod P 不一定调度到已提名节点。

抢占和终止期限

抢占的 pod 具有较长的终止期限。

  1. 调度程序在 Node N 上抢占了一个较低优先级 pod。
  2. 调度程序会等待 pod 正常终止。
  3. 出于其他调度原因,节点 M 变为可用
  4. 然后调度程序可以在节点 M 上调度 Pod P

16.6.5. 配置优先级和抢占

您可以通过创建优先级类对象并使用 pod 规格中的 priorityClassName 将 pod 与优先级关联来应用 pod 优先级与抢占功能。

优先级类对象示例

apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
  name: high-priority 1
value: 1000000 2
globalDefault: false 3
description: "This priority class should be used for XYZ service pods only." 4

1
优先级类对象的名称。
2
对象的优先级值。
3
此可选字段指定是否应该将这个优先级类用于 pod,而不指定优先级类名。此字段默认为 false。集群中只能存在一个 globalDefault 设置为 true 的优先级类。如果没有 globalDefault:true 的优先级类,则没有优先级类名称的 pod 的优先级为零。添加带有 globalDefault:true 的优先级类只会影响在添加优先级类后创建的 pod,且不会更改现有 pod 的优先级。
4
此可选任意文本字符串用于描述开发人员应对哪些 pod 使用这个优先级类。

带有优先级类名称的 pod 规格示例

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority 1

1
指定要用于此 pod 的优先级类。

配置集群以使用优先级与抢占功能:

  1. 创建一个或多个优先级类:

    1. 指定优先级的名称和值。
    2. (可选)指定优先级类中的 globalDefault 字段和描述。
  2. 创建 pod 或编辑现有的 pod 以包含优先级类的名称。您可以将优先级名称直接添加到 pod 配置或 pod 模板中:

16.6.6. 禁用优先级与抢占

您可以禁用 pod 优先级与抢占功能。

禁用该功能后,现有 pod 会保留其优先级字段,但会禁用抢占功能,而且也会忽略优先级字段。如果禁用该功能,您便无法在新 pod 中设置优先级类名称。

重要

在集群面临资源压力时,关键 pod 依赖于调度程序抢占功能来进行调度。因此,红帽建议您不要禁用抢占。DaemonSet pod 由 DaemonSet 控制器调度,不受禁用抢占的影响。

为集群禁用抢占:

  1. 修改 master-config.yaml,将 schedulerArgs 部分中的 disablePreemption 参数设置为 false

    disablePreemption=false
  2. 重启 OpenShift Container Platform master 服务和调度程序以应用更改。

    # master-restart api
    # master-restart scheduler

16.7. 高级调度

16.7.1. 概述

高级调度涉及配置 pod,以便 pod 需要在特定节点上运行,或者优先在特定节点上运行。

通常,不需要高级调度,因为 OpenShift Container Platform 会自动以合理的方式放置 pod。例如,默认调度程序会尝试在节点间平均分配 pod,并考虑节点中的可用资源。但是,您可能需要更多地控制 pod 的放置位置。

如果 pod 需要位于具有更快磁盘速度(或阻止放置于该计算机上)或来自两个不同服务的 pod 以便它们进行通信的机器上,您可以使用高级调度来实现这一点。

为确保将适当的新 pod 调度到专用的一组节点上,并防止将其他新 pod 调度到这些节点上,您可以根据需要组合这些方法。

16.7.2. 使用高级调度

在集群中调用高级调度的方法有几种:

Pod 关联性和反关联性

容器集关联性允许 pod 向一组容器集指定关联性 (或反关联性),这表示应用的延迟要求,因为安全性等原因。节点对放置没有控制权。

容器集关联性使用节点上的标签和 pod 上的标签选择器来创建 pod 放置规则。规则可以是强制(必需)或est-effort(首选)。

请参阅使用 Pod 关联性和反关联性

节点关联性

节点关联性允许 pod 向一组节点 (因为其特殊硬件、位置、高可用性要求等)指定关联性(或反关联性)。节点对放置没有控制权。

节点关联性使用节点上的标签和 pod 上的标签选择器来创建 pod 放置规则。规则可以是强制(必需)或est-effort(首选)。

请参阅使用节点关联性

Node Selectors

节点选择器是最简单的高级调度形式。与节点关联性一样,节点选择器也使用节点上的标签和 pod 上的标签选择器,以允许 pod 控制它可以放置的节点。但是,节点选择器没有节点关联性具有的必要和偏好规则。

请参阅使用 Node Selectors

污点和容限

taint/Tolerations 允许节点控制哪些 pod 应该(或不应该)调度到节点上。污点是节点上的标签,容限是 pod 上的标签。pod 上的标签必须匹配(或容许)节点上的标签(taint)才能调度。

与关联性相比,污点/容限有一个优势。例如,如果您添加到集群中,一组具有不同标签的新节点,则需要更新您要访问该节点的每个 pod 上的关联性,以及您不想使用新节点的所有其他 pod。使用污点/容限时,您只需要更新置入这些新节点上的 pod,因为会出现其他 pod。

请参阅使用 Taints 和 Tolerations

16.8. 高级调度和节点关联性

16.8.1. 概述

节点关联性是由调度程序用来确定 pod 的可放置位置的一组规则。规则使用节点上的自定义标签和 pod 中指定的选择器来定义。节点关联性允许 pod 为其可放置的一组节点指定关联性 (或反关联性)。节点对放置没有控制权。

例如,您可以将 pod 配置为仅在具有特定 CPU 或位于特定可用区的节点上运行。

节点关联性规则有两种,即必要规则和偏好规则。

必须满足必要规则,pod 才能调度到节点上。偏好规则指定在满足规则时调度程序会尝试强制执行规则,但不保证一定能强制执行成功。

注意

如果节点标签在运行时改变,使得不再满足 pod 上的节点关联性规则,该 pod 将继续在这个节点上运行。

16.8.2. 配置节点关联性

您可以通过 pod 规格文件配置节点关联性。 您可以指定必要规则或偏好规则 ,或同时指定两者。如果您同时指定,节点必须首先满足必要规则,然后尝试满足偏好规则。

以下示例是一个 pod 规格,它包含一条规则,要求 pod 放置到带有键为 e2e-az-NorthSouth 且值为 e2e-az-Northe2e-az-South 的标签的节点上:

具有节点关联性必要规则的 pod 配置文件示例

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity: 1
      requiredDuringSchedulingIgnoredDuringExecution: 2
        nodeSelectorTerms:
        - matchExpressions:
          - key: e2e-az-NorthSouth 3
            operator: In 4
            values:
            - e2e-az-North 5
            - e2e-az-South 6
  containers:
  - name: with-node-affinity
    image: docker.io/ocpqe/hello-pod

1
用于配置节点关联性的小节。
2
定义必要规则。
3 5 6
必须匹配键/值对(标签)才会应用该规则。
4
运算符表示节点上的标签和 pod 规格中 matchExpression 参数的值集合之间的关系。这个值可以是 InNotInExistsDoesNotExistLtGt

以下示例是带有偏好规则的节点规格,该规则的标签键为 e2e-az-EastWest,pod 的首选值为 e2e-az-Easte2e-az-West

具有节点关联性偏好规则的 pod 配置文件示例

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity: 1
      preferredDuringSchedulingIgnoredDuringExecution: 2
      - weight: 1 3
        preference:
          matchExpressions:
          - key: e2e-az-EastWest 4
            operator: In 5
            values:
            - e2e-az-East 6
            - e2e-az-West 7
  containers:
  - name: with-node-affinity
    image: docker.io/ocpqe/hello-pod

1
用于配置节点关联性的小节。
2
定义偏好规则。
3
为偏好规则指定权重。优先选择权重最高的节点。
4 6 7
必须匹配键/值对(标签)才会应用该规则。
5
运算符表示节点上的标签和 pod 规格中 matchExpression 参数的值集合之间的关系。这个值可以是 InNotInExistsDoesNotExistLtGt

没有明确的节点反关联性概念,但使用 NotInDoesNotExist 运算符复制该行为。

注意

如果您在同一 pod 配置中使用节点关联性和节点选择器,请注意以下几点:

  • 如果同时配置了 nodeSelectornodeAffinity,则必须满足这两个条件才能将 pod 调度到候选节点上。
  • 如果您指定了多个与 nodeAffinity 类型关联的 nodeSelectorTerms,那么其中一个 nodeSelectorTerms 满足时 pod 可以调度到节点上。
  • 如果您指定了多个与 nodeSelectorTerms 关联的 matchExpressions,则只有所有 matchExpressions 都满足时 pod 才能调度到节点上。

16.8.2.1. 配置所需的节点关联性规则

必须满足必要规则,pod 才能调度到节点上。

以下步骤演示了一个简单的配置,此配置会创建一个节点,以及调度程序要放置到该节点上的 pod。

  1. 通过编辑节点配置或使用 oc label node 命令为节点添加标签:

    $ oc label node node1 e2e-az-name=e2e-az1
    注意

    要修改集群中的节点,请根据需要更新节点配置映射。不要手动编辑 node-config.yaml 文件。

  2. 在 pod 规格中,使用 nodeAffinity 小节来配置 requiredDuringSchedulingIgnoredDuringExecution 参数:

    1. 指定必须满足的键和值。如果您希望新 pod 调度到您编辑的节点上,请使用与节点中的标签相同的 keyvalue 参数。
    2. 指定 operator。Operator 可以是 InNotInExistsDoesNotExistLtGt。例如,使用运算符 In 来要求节点上存在该标签:

      spec:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: e2e-az-name
                  operator: In
                  values:
                  - e2e-az1
                  - e2e-az2
  3. 创建 pod:

    $ oc create -f e2e-az2.yaml

16.8.2.2. 配置节点关联性偏好规则

偏好规则指定在满足规则时调度程序会尝试强制执行规则,但不保证一定能强制执行成功。

以下步骤演示了一个简单的配置,此配置会创建一个节点,以及调度程序尝试放置到该节点上的 pod。

  1. 通过编辑节点配置或执行 oc label node 命令为节点添加标签:

    $ oc label node node1 e2e-az-name=e2e-az3
    注意

    要修改集群中的节点,请根据需要更新节点配置映射。不要手动编辑 node-config.yaml 文件。

  2. 在 pod 规格中,使用 nodeAffinity 小节来配置 preferredDuringSchedulingIgnoredDuringExecution 参数:

    1. 为节点指定一个权重,值为 1 到 100 的数字。优先选择权重最高的节点。
    2. 指定必须满足的键和值。如果您希望新 pod 调度到您编辑的节点上,请使用与节点中的标签相同的 keyvalue 参数:

            preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 1
              preference:
                matchExpressions:
                - key: e2e-az-name
                  operator: In
                  values:
                  - e2e-az3
  3. 指定 operator。Operator 可以是 InNotInExistsDoesNotExistLtGt。例如,使用运算符 In 来要求节点上存在该标签。
  4. 创建 pod。

    $ oc create -f e2e-az3.yaml

16.8.3. 示例

以下示例演示了节点关联性。

16.8.3.1. 具有匹配标签的节点关联性

以下示例演示了具有匹配标签的节点与 pod 的节点关联性:

  • Node1 节点具有标签 zone:us

    $ oc label node node1 zone=us
  • pod pod-s1 在节点关联性必要规则下具有 zoneus 键/值对:

    $ cat pod-s1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-s1
    spec:
      containers:
        - image: "docker.io/ocpqe/hello-pod"
          name: hello-pod
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                - key: "zone"
                  operator: In
                  values:
                  - us
  • 使用标准命令创建 pod:

    $ oc create -f pod-s1.yaml
    pod "pod-s1" created
  • pod pod-s1 可以调度到 Node1 上:

    $ oc get pod -o wide
    NAME     READY     STATUS       RESTARTS   AGE      IP      NODE
    pod-s1   1/1       Running      0          4m       IP1     node1

16.8.3.2. 无匹配标签的节点关联性

以下示例演示了无匹配标签的节点与 pod 的节点关联性:

  • Node1 节点具有标签 zone:emea

    $ oc label node node1 zone=emea
  • pod pod-s1 在节点关联性必要规则下具有 zoneus 键/值对:

    $ cat pod-s1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-s1
    spec:
      containers:
        - image: "docker.io/ocpqe/hello-pod"
          name: hello-pod
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                - key: "zone"
                  operator: In
                  values:
                  - us
  • pod pod-s1 无法调度到 Node1 上:

    $ oc describe pod pod-s1
    
    ...
    Events:
     FirstSeen LastSeen Count From              SubObjectPath  Type                Reason
     --------- -------- ----- ----              -------------  --------            ------
     1m        33s      8     default-scheduler Warning        FailedScheduling    No nodes are available that match all of the following predicates:: MatchNodeSelector (1).

16.9. 高级调度和 Pod 关联性和反关联性

16.9.1. 概述

通过 pod 关联性和 pod 反关联性,您可以指定有关如何将 pod 放置到其他 pod 的规则。规则使用节点上的自定义标签和 pod 中指定的选择器来定义。pod 关联性/反关联性允许 pod 为其可以放置的一组 pod 指定关联性(或反关联性)。节点对放置没有控制权。

例如,您可以使用关联性规则,在服务内或相对于其他服务中的 pod 来分散或聚拢 pod。如果特定服务的 pod 的性能已知会受到另一服务的 pod 影响,那么您可以利用反关联性规则,防止前一服务的 pod 调度到与后一服务的 pod 相同的节点上。或者,您可以将服务的 pod 分散到不同的节点或可用区间,以减少关联的故障。

pod 关联性/反关联性允许您根据其他 pod 上的标签限制 pod 有资格调度到哪些节点。标签是键 /值对。

  • 如果新 pod 上的标签选择器与当前 pod 上的标签匹配,pod 关联性可以命令调度程序将新 pod 放置到与其他 pod 相同的节点上。
  • 如果新 pod 上的标签选择器与当前 pod 上的标签匹配,pod 反关联性可以阻止调度程序将新 pod 放置到与具有相同标签的 pod 相同的节点上。

pod 关联性规则有两种,即必要规则和偏好规则。

必须满足必要规则,pod 才能调度到节点上。偏好规则指定在满足规则时调度程序会尝试强制执行规则,但不保证一定能强制执行成功。

注意

根据 pod 优先级和抢占设置,调度程序可能无法在不违反关联性要求的情况下为 pod 查找适当的节点。若是如此,pod 可能不会被调度。

要防止这种情况,请仔细配置优先级相同的 pod 的 pod 关联性。

16.9.2. 配置 Pod 关联性和反关联性

您可以通过 pod 规格文件来配置 pod 的关联性/反关联性。 您可以指定必要规则或偏好规则 ,或同时指定两者。如果您同时指定,节点必须首先满足必要规则,然后尝试满足偏好规则。

以下示例显示配置了 pod 关联性和反关联性的 pod 规格。

在本例中,pod 关联性规则表示,只有当节点至少有一个已在运行的 pod 带有键为 security 且值为 S1 的标签时,pod 才可以调度到节点上。pod 反关联性规则表示,如果该节点已在运行带有键为 security 且值为 S2 的标签的 pod,则 pod 首选不要调度到该节点上。

设有 pod 关联性的 pod 配置文件示例

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity: 1
      requiredDuringSchedulingIgnoredDuringExecution: 2
      - labelSelector:
          matchExpressions:
          - key: security 3
            operator: In 4
            values:
            - S1 5
        topologyKey: failure-domain.beta.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: docker.io/ocpqe/hello-pod

1
用于配置 pod 关联性的小节。
2
定义必要规则。
3 5
必须匹配键和值(标签)才会应用该规则。
4
运算符表示现有 pod 上的标签和新 pod 规格中 matchExpression 参数的值集合之间的关系。可以是 InNotInExistsDoesNotExist

设有 pod 反关联性的 pod 配置文件示例

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-antiaffinity
spec:
  affinity:
    podAntiAffinity: 1
      preferredDuringSchedulingIgnoredDuringExecution: 2
      - weight: 100  3
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security 4
              operator: In 5
              values:
              - S2
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: docker.io/ocpqe/hello-pod

1
用于配置 pod 反关联性的小节。
2
定义偏好规则。
3
为偏好规则指定权重。优先选择权重最高的节点。
4
描述用来决定何时应用反关联性规则的 pod 标签。指定标签的键和值。
5
运算符表示现有 pod 上的标签和新 pod 规格中 matchExpression 参数的值集合之间的关系。可以是 InNotInExistsDoesNotExist
注意

如果节点标签在运行时改变,使得不再满足 pod 上的关联性规则,pod 会继续在该节点上运行。

16.9.2.1. 配置关联性规则

以下步骤演示了一个简单的双 pod 配置,它创建一个带有某标签的 pod,以及一个使用关联性来允许随着该 pod 一起调度的 pod。

  1. 创建 pod 规格中具有特定标签的 pod:

    $ cat team4.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: security-s1
      labels:
        security: S1
    spec:
      containers:
      - name: security-s1
        image: docker.io/ocpqe/hello-pod
  2. 在创建其他 pod 时,请按下方所示编辑 pod 规格:

    1. 使用 podAffinity 小节配置 requiredDuringSchedulingIgnoredDuringExecution 参数或 preferredDuringSchedulingIgnoredDuringExecution 参数:
    2. 指定必须满足的键和值。如果您希望新 pod 与另一个 pod 一起调度,请使用与第一个 pod 上的标签相同的 keyvalue 参数。

          podAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                - key: security
                  operator: In
                  values:
                  - S1
              topologyKey: failure-domain.beta.kubernetes.io/zone
    3. 指定 operator。Operator 可以是 InNotInExistsDoesNotExist。例如,使用运算符 In 来要求节点上存在该标签。
    4. 指定 topologyKey,它是系统用来表示此类拓扑域的预填充 Kubernetes 标签
  3. 创建 pod。

    $ oc create -f <pod-spec>.yaml

16.9.2.2. 配置反关联性规则

以下步骤演示了一个简单的双 pod 配置,它创建一个带有某标签的 pod,以及一个使用反关联性偏好规则来尝试阻止随着该 pod 一起调度的 pod。

  1. 创建 pod 规格中具有特定标签的 pod:

    $ cat team4.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: security-s2
      labels:
        security: S2
    spec:
      containers:
      - name: security-s2
        image: docker.io/ocpqe/hello-pod
  2. 在创建其他 pod 时,请编辑 pod 规格来设置以下参数:
  3. 使用 podAffinity 小节配置 requiredDuringSchedulingIgnoredDuringExecution 参数或 preferredDuringSchedulingIgnoredDuringExecution 参数:

    1. 为节点指定一个 1 到 100 的权重。优先选择权重最高的节点。
    2. 指定必须满足的键和值。如果您希望新 pod 不与另一个 pod 一起调度,请使用与第一个 pod 上的标签相同的 keyvalue 参数。

          podAntiAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                  - key: security
                    operator: In
                    values:
                    - S2
                topologyKey: kubernetes.io/hostname
    3. 为偏好规则指定一个 1 到 100 的权重。
    4. 指定 operator。Operator 可以是 InNotInExistsDoesNotExist。例如,使用运算符 In 来要求节点上存在该标签。
  4. 指定 topologyKey,它是系统用来表示此类拓扑域的预填充 Kubernetes 标签
  5. 创建 pod。

    $ oc create -f <pod-spec>.yaml

16.9.3. 示例

以下示例演示了 pod 关联性和 pod 反关联性。

16.9.3.1. Pod 关联性

以下示例演示了具有匹配标签和标签选择器的 pod 的 pod 关联性。

  • pod team4 具有标签 team:4

    $ cat team4.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: team4
      labels:
         team: "4"
    spec:
      containers:
      - name: ocp
        image: docker.io/ocpqe/hello-pod
  • pod team4apodAffinity 下具有标签选择器 team:4

    $ cat pod-team4a.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: team4a
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: team
                operator: In
                values:
                - "4"
            topologyKey: kubernetes.io/hostname
      containers:
      - name: pod-affinity
        image: docker.io/ocpqe/hello-pod
  • team4a pod 调度到与 team4 pod 相同的节点上。

16.9.3.2. Pod 反关联性

以下示例演示了具有匹配标签和标签选择器的 pod 的 pod 反关联性。

  • pod pod-s1 具有标签 security:s1

    $ cat pod-s1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-s1
      labels:
        security: s1
    spec:
      containers:
      - name: ocp
        image: docker.io/ocpqe/hello-pod
  • pod pod-s2podAntiAffinity 下具有标签选择器 security:s1

    $ cat pod-s2.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-s2
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: security
                operator: In
                values:
                - s1
            topologyKey: kubernetes.io/hostname
      containers:
      - name: pod-antiaffinity
        image: docker.io/ocpqe/hello-pod
  • pod pod-s2 无法调度到与 pod-s1 相同的节点上。

16.9.3.3. 无匹配标签的 Pod 反关联性

以下示例演示了在没有匹配标签和标签选择器时的 pod 的 pod 关联性。

  • pod pod-s1 具有标签 security:s1

    $ cat pod-s1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-s1
      labels:
        security: s1
    spec:
      containers:
      - name: ocp
        image: docker.io/ocpqe/hello-pod
  • pod pod-s2 具有标签选择器 security:s2

    $ cat pod-s2.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-s2
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: security
                operator: In
                values:
                - s2
            topologyKey: kubernetes.io/hostname
      containers:
      - name: pod-affinity
        image: docker.io/ocpqe/hello-pod
  • 除非有一个带有 security:s2 标签的 pod 的节点,否则不会调度 pod pod-s2。如果没有具有该标签的其他 pod,新 pod 会保持在待处理状态:

    NAME      READY     STATUS    RESTARTS   AGE       IP        NODE
    pod-s2    0/1       Pending   0          32s       <none>

16.10. 高级调度和 Node Selectors

16.10.1. 概述

节点选择器指定一个键值对映射。规则使用节点上的自定义标签和 pod 中指定的选择器来定义。

若要使 pod 有资格在某一节点上运行,pod 必须具有指定为该节点上标签的键值对。

如果您在同一 pod 配置中使用节点关联性和节点选择器 ,请参阅以下重要注意事项

16.10.2. 配置 Node Selectors

在 pod 配置中使用 nodeSelector,您可以确保 pod 只放置到带有特定标签的节点上。

  1. 确保您有所需的标签(请参阅在节点上 Updating Labelss)和 节点选择器设置

    例如,请确保您的 pod 配置具有指示所需标签的 nodeSelector 值:

    apiVersion: v1
    kind: Pod
    spec:
      nodeSelector:
        <key>: <value>
    ...
  2. 修改 master 配置文件/etc/origin/master/master-config.yaml,将 nodeSelectorLabelBlacklist 添加到带有您要拒绝 pod 放置的节点主机的标签的 admissionConfig 部分:

    ...
    admissionConfig:
      pluginConfig:
        PodNodeConstraints:
          configuration:
            apiversion: v1
            kind: PodNodeConstraintsConfig
            nodeSelectorLabelBlacklist:
              - kubernetes.io/hostname
              - <label>
    ...
  3. 重启 OpenShift Container Platform 以使更改生效。

    # master-restart api
    # master-restart controllers
注意

如果您在同一 pod 配置中使用节点选择器和节点关联性,请注意:

  • 如果同时配置了 nodeSelectornodeAffinity,则必须满足这两个条件才能将 pod 调度到候选节点上。
  • 如果您指定了多个与 nodeAffinity 类型关联的 nodeSelectorTerms,那么其中一个 nodeSelectorTerms 满足时 pod 可以调度到节点上。
  • 如果您指定了多个与 nodeSelectorTerms 关联的 matchExpressions,则只有所有 matchExpressions 都满足时 pod 才能调度到节点上。

16.11. 高级调度、管理和容忍

16.11.1. 概述

通过污点和容限,节点可以控制哪些 pod 应该(或不应该)调度到节点上。

16.11.2. 污点和容限

通过使用污点(taint),节点可以拒绝调度 pod,除非 pod 具有匹配的容限(toleration)

您可以通过节点规格(NodeSpec)将污点应用到节点,并通过 pod 规格(PodSpec)将容限应用到 pod。节点上的污点指示节点排斥所有不容许该污点的 pod。

污点与容限由 key、value 和 effect 组成。运算符允许您将其中一个参数留空。

表 16.1. 污点和容限组件

参数描述

key

key 是任意字符串,最多 253 个字符。key 必须以字母或数字开头,可以包含字母、数字、连字符、句点和下划线。

value

value 是任意字符串,最多 63 个字符。value 必须以字母或数字开头,可以包含字母、数字、连字符、句点和下划线。

effect

effect 的值包括:

NoSchedule

  • 与污点不匹配的新 pod 不会调度到该节点上。
  • 该节点上现有的 pod 会保留。

PreferNoSchedule

  • 与污点不匹配的新 pod 可以调度到该节点上,但调度程序会尽量不这样调度。
  • 该节点上现有的 pod 会保留。

NoExecute

  • 与污点不匹配的新 pod 无法调度到该节点上。
  • 节点上没有匹配容限的现有 pod 将被移除。

operator

Equal

key/value/effect 参数必须匹配。这是默认值。

Exists

key/effect 参数必须匹配。您必须保留一个空 value 参数,该参数与任何参数匹配。

容限与污点匹配:

  • 如果 operator 参数设置为 Equal

    • key 参数相同;
    • value 参数相同;
    • effect 参数相同。
  • 如果 operator 参数设置为 Exists

    • key 参数相同;
    • effect 参数相同。

16.11.2.1. 使用多个污点

您可以在同一个节点中放入多个污点,并在同一 pod 中放入多个容限。OpenShift Container Platform 按照如下所述处理多个污点和容限:

  1. 处理 pod 具有匹配容限的污点。
  2. 其余的不匹配污点在 pod 上有指示的 effect:

    • 如果至少有一个不匹配污点具有 NoSchedule effect,OpenShift Container Platform 无法将 pod 调度到该节点上。
    • 如果没有不匹配污点具有 NoSchedule effect,但至少有一个不匹配污点具有 PreferNoSchedule effect,OpenShift Container Platform 会尝试不将 pod 调度到该节点上。
    • 如果至少有一个不匹配污点具有 NoExecute effect,OpenShift Container Platform 会将 pod 从该节点驱除(如果它已在该节点上运行),或者 pod 不会被调度到该节点上(如果还没有在该节点上运行)。

      • 不容许污点的 Pod 会立即被驱除。
      • 如果 Pod 容许污点,且没有在容限规格中指定 tolerationSeconds,则会永久保持绑定。
      • 如果 Pod 在指定 tolerationSeconds 中容许污点,则会在指定时间内保持绑定。

例如:

  • 节点具有以下污点:

    $ oc adm taint nodes node1 key1=value1:NoSchedule
    $ oc adm taint nodes node1 key1=value1:NoExecute
    $ oc adm taint nodes node1 key2=value2:NoSchedule
  • pod 具有以下容限:

    tolerations:
    - key: "key1"
      operator: "Equal"
      value: "value1"
      effect: "NoSchedule"
    - key: "key1"
      operator: "Equal"
      value: "value1"
      effect: "NoExecute"

在本例中,pod 无法调度到节点上,因为没有与第三个污点匹配的容限。如果在添加污点时 pod 已在节点上运行,pod 会继续运行,因为第三个污点是三个污点中 pod 唯一不容许的污点。

16.11.3. 将 Taint 添加到现有节点

您可以使用 oc adm taint 命令,使用 Taint 和 toleration 组件表中描述的参数为节点添加污点

$ oc adm taint nodes <node-name> <key>=<value>:<effect>

例如:

$ oc adm taint nodes node1 key1=value1:NoExecute

这个示例在 node1 上放置一个键为 key1 且值为 value1 的污点,污点效果为 NoExecute

16.11.4. 在 Pod 中添加容限

要为 pod 添加容限,请编辑 pod 规格使其包含 tolerations 部分:

使用 Equal 运算符的 pod 配置文件示例

tolerations:
- key: "key1" 1
  operator: "Equal" 2
  value: "value1" 3
  effect: "NoExecute" 4
  tolerationSeconds: 3600 5

1 2 3 4
5
tolerationSeconds 参数指定 pod 在被驱除前可以保持与节点绑定的时长。请参阅以下使用 Toleration seconds to Delay Pod 驱除

使用 Exists 运算符的 pod 配置文件示例

tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 3600

这两个容限都与以上 oc adm taint 命令创建的污点匹配。具有任一容限的 pod 可以调度到 node1

16.11.4.1. 使用容忍 seconds seconds to Delay Pod Evictions

您可以通过在 pod 规格中指定 tolerationSeconds 参数来指定 pod 在被驱除前可以保持与节点绑定的时长。如果将具有 NoExecute effect 的污点添加到节点,则所有不容许该污点的 pod 都被立即驱除(容许该污点的 pod 不会被驱除)。但是,如果要被驱除的 pod 具有 tolerationSeconds 参数,则只有该时间到期后 pod 才会被驱除。

例如:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

在这里,如果此 pod 正在运行但没有匹配的污点,pod 保持与节点绑定 3600 秒,然后被驱除。如果污点在这个时间之前移除,pod 就不会被驱除。

16.11.4.1.1. 为 Toleration second 设置默认值

此插件为 pod 设置默认的强制容限,容许 node.kubernetes.io/not-ready:NoExecutenode.kubernetes.io/unreachable:NoExecute 污点五分钟。

如果用户提供的 pod 配置已具有任一容限,则不会添加默认值。

启用 Default Toleration seconds:

  1. 修改 master配置文件(/etc/origin/master/master-config.yaml),将 DefaultTolerationSeconds 添加到 admissionConfig 部分:

    admissionConfig:
      pluginConfig:
        DefaultTolerationSeconds:
          configuration:
            kind: DefaultAdmissionConfig
            apiVersion: v1
            disable: false
  2. 重启 OpenShift 以使更改生效:

    # master-restart api
    # master-restart controllers
  3. 验证是否已添加默认值:

    1. 创建 pod:

      $ oc create -f </path/to/file>

      例如:

      $ oc create -f hello-pod.yaml
      pod "hello-pod" created
    2. 检查 pod 容限:

      $ oc describe pod <pod-name> |grep -i toleration

      例如:

      $ oc describe pod hello-pod |grep -i toleration
      Tolerations:    node.kubernetes.io/not-ready=:Exists:NoExecute for 300s

16.11.5. 节点问题的 Pod 驱除

OpenShift Container Platform 可以配置为用污点来代表节点不可访问节点未就绪状况。这样,就可以对每个 pod 设置在节点变得不可访问或未就绪时保持与节点绑定的时长,而不是使用默认的五分钟。

当启用 Taint Based Evictions 功能时,节点控制器会自动添加污点,并禁用从 Ready 节点驱除 pod 的一般逻辑。

  • 如果节点进入未就绪状态,则添加 node.kubernetes.io/not-ready:NoExecute 污点,且无法将 pod 调度到该节点上。现有 pod 在容限秒数期限内保留。
  • 如果节点进入不可访问状态,则会添加 node.kubernetes.io/unreachable:NoExecute 污点,且无法将 pod 调度到该节点上。现有 pod 在容限秒数期限内保留。

启用基于污点的驱除:

  1. 修改 master配置文件(/etc/origin/master/master-config.yaml),将以下内容添加到 kubernetesMasterConfig 部分:

    kubernetesMasterConfig:
       controllerArguments:
         feature-gates:
         - TaintBasedEvictions=true
  2. 检查污点是否已添加到节点:

    $ oc describe node $node | grep -i taint
    
    Taints: node.kubernetes.io/not-ready:NoExecute
  3. 重启 OpenShift 以使更改生效:

    # master-restart api
    # master-restart controllers
  4. 为 pod 添加容限:

    tolerations:
    - key: "node.kubernetes.io/unreachable"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 6000

    tolerations:
    - key: "node.kubernetes.io/not-ready"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 6000
注意

为了保持由于节点问题而导致 pod 驱除的现有速率限制行为,系统以限速方式添加污点。这可防止在主控机从节点分区等情形中发生大量 pod 驱除。

16.11.6. DaemonSet 和 Tolerations

DaemonSet pod 是使用 node.kubernetes.io/unreachablenode.kubernetes.io/not-readyNoExecute 容限创建的,没有 tolerationSeconds,以确保 DaemonSet pod 不会因为这些问题而被驱除,即使禁用了 Default Toleration Seconds 功能。

16.11.7. 示例

污点和容限是一种灵活的方式,可以禁止 pod 离开节点或驱除不应在节点上运行的 pod。些典型的 scenrios 是:

16.11.7.1. 为用户指定节点

您可以指定一组节点供一组特定用户独占使用。

指定专用节点:

  1. 给这些节点添加污点:

    例如:

    $ oc adm taint nodes node1 dedicated=groupName:NoSchedule
  2. 通过编写自定义准入控制器,为 pod 添加对应的容限。

    只有具有这些容限的 pod 才可以使用专用节点。

16.11.7.2. 将用户绑定到节点

您可以配置节点,使特定用户只能使用专用节点。

配置节点以使用户只能使用该节点:

  1. 给这些节点添加污点:

    例如:

    $ oc adm taint nodes node1 dedicated=groupName:NoSchedule
  2. 通过编写自定义准入控制器,为 pod 添加对应的容限。

    准入控制器应添加节点关联性,要求 pod 只能调度到带有 key:value 标签(dedicated=groupName)标签的节点。

  3. 向专用节点添加与污点类似的标签(如 key:value 标签)。

16.11.7.3. 具有特殊硬件的节点

如果集群中有少量节点具有特殊的硬件(如 GPU),您可以使用污点和容限让不需要特殊硬件的 pod 与这些节点保持距离,从而将这些节点保留给那些确实需要特殊硬件的 pod。您还可以要求需要特殊硬件的 pod 使用特定的节点。

确保 pod 被禁止使用特殊硬件:

  1. 使用以下命令之一,给拥有特殊硬件的节点添加污点:

    $ oc adm taint nodes <node-name> disktype=ssd:NoSchedule
    $ oc adm taint nodes <node-name> disktype=ssd:PreferNoSchedule
  2. 为使用准入控制器使用特殊硬件的 pod 添加对应的容限。

例如,准入控制器可以使用 pod 的一些特征来决定是否应该允许 pod 通过添加容限来使用特殊节点。

要确保 pod 只能使用特殊硬件,您需要一些额外的机制。例如,您可以给具有特殊硬件的节点打上标签,并在需要该硬件的 pod 上使用节点关联性。

第 17 章 设置配额

17.1. 概述

资源配额由 ResourceQuota 对象定义,提供约束来限制每个项目的聚合资源消耗。它可根据类型限制项目中创建的对象数量,以及该项目中资源可以消耗的计算资源和存储的总和。

17.2. 按配额管理的资源

下文描述了可通过配额管理的一组计算资源和对象类型。

注意

如果 status.phase in (Failed, Succeeded) 为 true,则 pod 处于终端状态。

表 17.1. 按配额管理的计算资源

资源名称描述

cpu

非终端状态的所有 Pod 的 CPU 请求总和不能超过这个值。cpurequests.cpu 的值相同,并可互换使用。

memory

非终端状态的所有 Pod 的 内存请求总和不能超过这个值。memoryrequests.memory 的值相同,并可互换使用。

ephemeral-storage

非终端状态的所有本地临时存储请求总和不能超过这个值。ephemeral-storagerequests.ephemeral-storage 的值相同,并可互换使用。只有在您启用了临时存储技术预览时,此资源才可用。此功能默认为禁用。

requests.cpu

非终端状态的所有 Pod 的 CPU 请求总和不能超过这个值。cpurequests.cpu 的值相同,并可互换使用。

requests.memory

非终端状态的所有 Pod 的 内存请求总和不能超过这个值。memoryrequests.memory 的值相同,并可互换使用。

requests.ephemeral-storage

非终端状态的所有临时存储请求总和不能超过这个值。ephemeral-storagerequests.ephemeral-storage 的值相同,并可互换使用。只有在您启用了临时存储技术预览时,此资源才可用。此功能默认为禁用。

limits.cpu

非终端状态的所有 Pod 的 CPU 限值总和不能超过这个值。

limits.memory

非终端状态的所有 Pod 的内存限值总和不能超过这个值。

limits.ephemeral-storage

非终端状态的所有 Pod 的临时存储限值总和不能超过这个值。只有在您启用了临时存储技术预览时,此资源才可用。此功能默认为禁用。

表 17.2. 按配额管理的存储资源

资源名称描述

requests.storage

处于任何状态的所有持久性卷声明的存储请求总和不能超过这个值。

persistentvolumeclaims

项目中可以存在的持久性卷声明的总数。

<storage-class-name>.storageclass.storage.k8s.io/requests.storage

在处于任何状态且具有匹配存储类的所有持久性卷声明中,存储请求总和不能超过这个值。

<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims

项目中可以存在的具有匹配存储类的持久性卷声明的总数。

表 17.3. 对象数按配额管理

资源名称描述

pods

项目中可以存在的处于非终端状态的 Pod 总数。

replicationcontrollers

项目中可以存在的复制控制器的总数。

resourcequotas

项目中可以存在的资源配额总数。

services

项目中可以存在的服务总数。

secrets

项目中可以存在的 secret 的总数。

configmaps

项目中可以存在的 ConfigMap 对象的总数。

persistentvolumeclaims

项目中可以存在的持久性卷声明的总数。

openshift.io/imagestreams

项目中可以存在的镜像流的总数。

您可以在创建配额时使用 count/<resource>.<group> 语法为这些标准命名空间资源类型配置对象数配额

$ oc create quota <name> --hard=count/<resource>.<group>=<quota> 1
1
<resource> 是资源的名称,<group> 是 API 组(若适用)。使用 kubectl api-resources 命令获取资源及其关联的 API 组列表。

17.2.1. 为扩展资源设置资源配额

扩展资源不允许过量使用资源,因此您必须为配额中的相同扩展资源指定 requestslimits。目前,扩展资源只允许使用带有前缀 requests. 的配额项。以下是如何为 GPU 资源 nvidia.com/gpu 设置资源配额的示例场景。

流程

  1. 确定集群中某个节点中有多少 GPU 可用。例如:

    # oc describe node ip-172-31-27-209.us-west-2.compute.internal | egrep 'Capacity|Allocatable|gpu'
                        openshift.com/gpu-accelerator=true
    Capacity:
     nvidia.com/gpu:  2
    Allocatable:
     nvidia.com/gpu:  2
      nvidia.com/gpu  0           0

    本例中有 2 个 GPU 可用。

  2. 在命名空间 nvidia 中设置配额。在本例中,配额是 1:

    # cat gpu-quota.yaml
    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: gpu-quota
      namespace: nvidia
    spec:
      hard:
        requests.nvidia.com/gpu: 1
  3. 创建配额:

    # oc create -f gpu-quota.yaml
    resourcequota/gpu-quota created
  4. 验证命名空间是否设置了正确的配额:

    # oc describe quota gpu-quota -n nvidia
    Name:                    gpu-quota
    Namespace:               nvidia
    Resource                 Used  Hard
    --------                 ----  ----
    requests.nvidia.com/gpu  0     1
  5. 运行一个请求单个 GPU 的 Pod:

    # oc create pod gpu-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      generateName: gpu-pod-
      namespace: nvidia
    spec:
      restartPolicy: OnFailure
      containers:
      - name: rhel7-gpu-pod
        image: rhel7
        env:
          - name: NVIDIA_VISIBLE_DEVICES
            value: all
          - name: NVIDIA_DRIVER_CAPABILITIES
            value: "compute,utility"
          - name: NVIDIA_REQUIRE_CUDA
            value: "cuda>=5.0"
    
        command: ["sleep"]
        args: ["infinity"]
    
        resources:
          limits:
            nvidia.com/gpu: 1
  6. 验证 Pod 是否在运行:

    # oc get pods
    NAME              READY     STATUS      RESTARTS   AGE
    gpu-pod-s46h7     1/1       Running     0          1m
  7. 验证配额 Used 计数器是否正确:

    # oc describe quota gpu-quota -n nvidia
    Name:                    gpu-quota
    Namespace:               nvidia
    Resource                 Used  Hard
    --------                 ----  ----
    requests.nvidia.com/gpu  1     1
  8. 尝试在 nvidia 命名空间中创建第二个 GPU Pod。从技术上讲,该节点支持 2 个 GPU,因为它有 2 个 GPU:

    # oc create -f gpu-pod.yaml
    Error from server (Forbidden): error when creating "gpu-pod.yaml": pods "gpu-pod-f7z2w" is forbidden: exceeded quota: gpu-quota, requested: requests.nvidia.com/gpu=1, used: requests.nvidia.com/gpu=1, limited: requests.nvidia.com/gpu=1

    应该会显示此 Forbidden 错误消息,因为您有设为 1 个 GPU 的配额,但这一 Pod 试图分配第二个 GPU,而这超过了配额。

17.3. 配额范围

每个配额都有一组关联的范围。配额仅在与枚举范围交集匹配时才测量资源的使用量。

为配额添加范围会限制该配额可应用的资源集合。指定允许的集合之外的资源会导致验证错误。

影响范围描述

Terminating

匹配 spec.activeDeadlineSeconds >= 0 的 pod。

NotTerminating

匹配 spec.activeDeadlineSecondsnil 的 pod。

BestEffort

匹配 cpumemory 具有最佳服务质量的 pod。有关提交计算资源的更多信息,请参阅服务质量类

NotBestEffort

匹配 cpumemory 没有最佳服务质量的 pod。

BestEffort 范围将配额限制为限制以下资源:

  • pods

Terminating、NotTerminatingNotBestEffort 范围将配额限制为跟踪以下资源:

  • pods
  • memory
  • requests.memory
  • limits.memory
  • cpu
  • requests.cpu
  • limits.cpu
  • ephemeral-storage
  • requests.ephemeral-storage
  • limits.ephemeral-storage
注意

只有在启用了临时存储技术预览功能时,才会应用临时存储请求和限值。此功能默认为禁用。

17.4. 配额强制

在项目中首次创建资源配额后,项目会限制您创建可能会违反配额约束的新资源,直到它计算了更新后的使用量统计。

在创建了配额并且更新了使用量统计后,项目会接受创建新的内容。当您创建或修改资源时,配额使用量会在请求创建或修改资源时立即递增。

在您删除资源时,配额使用量在下一次完整重新计算项目的配额统计时才会递减。可配置的时间量决定了将配额使用量统计降低到其当前观察到的系统值所需的时间。

如果项目修改超过配额使用量限值,服务器将拒绝该操作,并将相应的错误消息返回给用户,说明违反了配额约束,以及它们当前观察到的系统中使用量统计。

17.5. 请求(request)与限制(limits)的比较

在分配计算资源时,每个容器可以为每个 CPU、内存和临时存储指定请求和限制值。配额可以限制任何这些值。

如果配额具有为 requests.cpurequests.memory 指定的值,那么它要求每个传入的容器都明确请求这些资源。如果配额具有为 limits.cpulimits.memory 指定的值,那么它要求每个传入的容器都为这些资源指定明确的限值。

17.6. 资源配额定义示例

core-object-counts.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: core-object-counts
spec:
  hard:
    configmaps: "10" 1
    persistentvolumeclaims: "4" 2
    replicationcontrollers: "20" 3
    secrets: "10" 4
    services: "10" 5

1
项目中可以存在的 ConfigMap 对象的总数。
2
项目中可以存在的持久性卷声明 (PVC) 的总数。
3
项目中可以存在的复制控制器的总数。
4
项目中可以存在的 secret 的总数。
5
项目中可以存在的服务总数。

openshift-object-counts.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: openshift-object-counts
spec:
  hard:
    openshift.io/imagestreams: "10" 1

1
项目中可以存在的镜像流的总数。

compute-resources.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    pods: "4" 1
    requests.cpu: "1" 2
    requests.memory: 1Gi 3
    requests.ephemeral-storage: 2Gi 4
    limits.cpu: "2" 5
    limits.memory: 2Gi 6
    limits.ephemeral-storage: 4Gi 7

1
项目中可以存在的处于非终端状态的 Pod 总数。
2
在非终端状态的所有 Pod 中,CPU 请求总和不能超过 1 个内核。
3
在非终端状态的所有 Pod 中,内存请求总和不能超过 1Gi。
4
在非终端状态的所有 Pod 中,临时存储请求总和不能超过 2Gi。
5
在非终端状态的所有 Pod 中,CPU 限值总和不能超过 2 个内核。
6
在非终端状态的所有 Pod 中,内存限值总和不能超过 2Gi。
7
在非终端状态的所有 Pod 中,临时存储限值总和不能超过 4Gi。

besteffort.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: besteffort
spec:
  hard:
    pods: "1" 1
  scopes:
  - BestEffort 2

1
项目中可以存在的具有 BestEffort 服务质量的非终端状态 pod 总数。
2
将配额仅限为具有 BestEffort 服务质量的匹配 pod,适用于内存或 CPU。

compute-resources-long-running.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources-long-running
spec:
  hard:
    pods: "4" 1
    limits.cpu: "4" 2
    limits.memory: "2Gi" 3
    limits.ephemeral-storage: "4Gi" 4
  scopes:
  - NotTerminating 5

1
处于非终端状态的 Pod 总数。
2
在非终端状态的所有 Pod 中,CPU 限值总和不能超过这个值。
3
在非终端状态的所有 Pod 中,内存限值总和不能超过这个值。
4
在非终端状态的所有 Pod 中,临时存储限值总和不能超过这个值。
5
将配额限制为仅将 spec.activeDeadlineSeconds 设置为 nil 的匹配 pod。构建 pod 将归 NotTerminating 下,除非应用了 RestartNever 策略。

compute-resources-time-bound.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources-time-bound
spec:
  hard:
    pods: "2" 1
    limits.cpu: "1" 2
    limits.memory: "1Gi" 3
    limits.ephemeral-storage: "1Gi" 4
  scopes:
  - Terminating 5

1
处于非终端状态的 Pod 总数。
2
在非终端状态的所有 Pod 中,CPU 限值总和不能超过这个值。
3
在非终端状态的所有 Pod 中,内存限值总和不能超过这个值。
4
在非终端状态的所有 Pod 中,临时存储限值总和不能超过这个值。
5
将配额仅限为 spec.activeDeadlineSeconds >=0 匹配的匹配 pod。例如,此配额适用于构建或部署器 Pod,而非 Web 服务器或数据库等长时间运行的 Pod。

storage-consumption.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: storage-consumption
spec:
  hard:
    persistentvolumeclaims: "10" 1
    requests.storage: "50Gi" 2
    gold.storageclass.storage.k8s.io/requests.storage: "10Gi" 3
    silver.storageclass.storage.k8s.io/requests.storage: "20Gi" 4
    silver.storageclass.storage.k8s.io/persistentvolumeclaims: "5" 5
    bronze.storageclass.storage.k8s.io/requests.storage: "0" 6
    bronze.storageclass.storage.k8s.io/persistentvolumeclaims: "0" 7

1
项目中的持久性卷声明总数
2
在一个项目中的所有持久性卷声明中,请求的存储总和不能超过这个值。
3
在一个项目中的所有持久性卷声明中,金级存储类中请求的存储总和不能超过这个值。
4
在一个项目中的所有持久性卷声明中,银级存储类中请求的存储总和不能超过这个值。
5
在一个项目中的所有持久性卷声明中,银级存储类中声明总数不能超过这个值。
6
在一个项目中的所有持久性卷声明中,铜级存储类中请求的存储总和不能超过这个值。当将其设置为 0 时,这表示 bronze 存储类无法请求存储。
7
在一个项目中的所有持久性卷声明中,铜级存储类中请求的存储总和不能超过这个值。当将其设置为 0 时,这表示 bronze 存储类无法创建声明。

17.7. 创建配额

要创建配额,首先在文件中定义配额,如 Sample Resource Quota Definitions 中的示例。然后,使用该文件将其应用到项目:

$ oc create -f <resource_quota_definition> [-n <project_name>]

例如:

$ oc create -f core-object-counts.yaml -n demoproject

17.7.1. 创建对象数配额

您可以为所有 OpenShift Container Platform 标准命名空间资源类型创建对象数配额,如 BuildConfig 和DeploymentConfig。对象配额数将定义的配额施加于所有标准命名空间资源类型。

在使用资源配额时,如果服务器存储中存在某一对象,则从其配额中扣减。这些类型的配额对防止耗尽存储资源很有用处。

要为资源配置对象数配额,请运行以下命令:

$ oc create quota <name> --hard=count/<resource>.<group>=<quota>,count/<resource>.<group>=<quota>

例如:

$ oc create quota test --hard=count/deployments.extensions=2,count/replicasets.extensions=4,count/pods=3,count/secrets=4
resourcequota "test" created

$ oc describe quota test
Name:                         test
Namespace:                    quota
Resource                      Used  Hard
--------                      ----  ----
count/deployments.extensions  0     2
count/pods                    0     3
count/replicasets.extensions  0     4
count/secrets                 0     4

本例将列出的资源限制为集群中各个项目的硬限值。

17.8. 查看配额

您可以在 Web 控制台导航到项目的 Quota 页面,查看与项目配额中定义的硬限值相关的使用量统计。

您还可以使用 CLI 查看配额详情:

  1. 首先,获取项目中定义的配额列表。例如,对于名为 demoproject 的项目:

    $ oc get quota -n demoproject
    NAME                AGE
    besteffort          11m
    compute-resources   2m
    core-object-counts  29m
  2. 然后,描述您感兴趣的配额,如 core-object-counts 配额:

    $ oc describe quota core-object-counts -n demoproject
    Name:			core-object-counts
    Namespace:		demoproject
    Resource		Used	Hard
    --------		----	----
    configmaps		3	10
    persistentvolumeclaims	0	4
    replicationcontrollers	3	20
    secrets			9	10
    services		2	10

17.9. 配置配额同步周期

当删除一组资源时,资源的同步时间由/etc/origin/master/master-config.yaml 文件中的 resource-quota-sync-period 设置决定。

在恢复配额使用量之前,用户在尝试重用资源时可能会遇到问题。您可以将 resource-quota-sync-period 设置更改为在所需时间(以秒为单位)重新生成资源集合,并使资源再次可用:

kubernetesMasterConfig:
  apiLevels:
  - v1beta3
  - v1
  apiServerArguments: null
  controllerArguments:
    resource-quota-sync-period:
      - "10s"

进行任何更改后,重启主服务以应用它们。

# master-restart api
# master-restart controllers

在使用自动化时,调整重新生成时间对创建资源和决定资源使用量很有帮助。

注意

resource-quota-sync-period 设置旨在平衡系统性能。缩短同步周期可能会导致 master 遭遇重度负载。

17.10. 部署配置中的配额核算

如果为项目定义了配额,请参阅 Deployment Resources 了解任何部署配置的注意事项。

17.11. 需要 Explicit Quota 以消耗资源

如果资源不受配额管理,用户可以消耗的资源量就不会有限制。例如,如果没有与金级存储类相关的存储配额,则项目可以创建的金级存储量没有限制。

对于高成本计算或存储资源,管理员可能需要授予显式配额才能消耗资源。譬如,如果某个项目没有显式赋予与金级存储类有关的存储配额,则该项目的用户将无法创建该类型的存储。

要要求显式配额来消耗特定资源,应该将以下小节添加到 master-config.yaml 中。

admissionConfig:
  pluginConfig:
    ResourceQuota:
      configuration:
        apiVersion: resourcequota.admission.k8s.io/v1alpha1
        kind: Configuration
        limitedResources:
        - resource: persistentvolumeclaims 1
        matchContains:
        - gold.storageclass.storage.k8s.io/requests.storage 2
1
默认限制其消耗的组/资源。
2
通过配额跟踪并且与默认限制的组/资源关联的资源名称。

在上例中,配额系统将拦截创建或更新 PersistentVolumeClaim 的每个操作。它会检查将要消耗的配额理解哪些资源,如果项目中没有配额涵盖这些资源,则请求会被拒绝。在本例中,如果用户创建了使用与金级存储类关联的存储的 PersistentVolumeClaim,且项目中没有匹配的配额,则请求会被拒绝。

17.12. 已知问题

  • 无效的对象可能会导致项目的配额资源耗尽。在验证资源之前,配额会在准入中递增。因此,即使 pod 最终没有持久保留,也可以递增配额。这将在以后的发行版本中解决。(BZ1485375)

第 18 章 设置多项目配额

18.1. 概述

多项目配额由 ClusterResourceQuota 对象定义,允许在多个项目之间共享配额。将聚合每个选定项目中使用的资源,并将使用此聚合来限制所有选定项目中的资源。

18.2. 选择项目

您可以根据注解选择和/或标签选择来选择项目。例如,要根据注解选择项目,请运行以下命令:

$ oc create clusterquota for-user \
     --project-annotation-selector openshift.io/requester=<user-name> \
     --hard pods=10 \
     --hard secrets=20

它创建以下 ClusterResourceQuota 对象:

apiVersion: v1
kind: ClusterResourceQuota
metadata:
  name: for-user
spec:
  quota: 1
    hard:
      pods: "10"
      secrets: "20"
  selector:
    annotations: 2
      openshift.io/requester: <user-name>
    labels: null 3
status:
  namespaces: 4
  - namespace: ns-one
    status:
      hard:
        pods: "10"
        secrets: "20"
      used:
        pods: "1"
        secrets: "9"
  total: 5
    hard:
      pods: "10"
      secrets: "20"
    used:
      pods: "1"
      secrets: "9"
1
将对所选项目强制执行的 ResourceQuotaSpec 对象。
2
注解的简单键/值选择器。
3
可用来选择项目的标签选择器。
4
描述每个所选项目中当前配额使用量的各命名空间映射。
5
所有选定项目的使用量合计。

此多项目配额文档使用默认项目请求端点控制 <user-name> 请求的所有项目。您需要有 10 个 Pod 和 20 个 secret 的限制。

同样,若要根据标签选择项目,请运行以下命令:

$ oc create clusterresourcequota for-name \ 1
    --project-label-selector=name=frontend \ 2
    --hard=pods=10 --hard=secrets=20
1
clusterresourcequotaclusterquota 是同一命令的别名。for-nameclusterresourcequota 对象的名称。
2
要按标签选择项目,请使用 --project-label-selector=key=value 格式提供一个键值对。

它创建以下 ClusterResourceQuota 对象定义:

apiVersion: v1
kind: ClusterResourceQuota
metadata:
  creationTimestamp: null
  name: for-name
spec:
  quota:
    hard:
      pods: "10"
      secrets: "20"
  selector:
    annotations: null
    labels:
      matchLabels:
        name: frontend

18.3. 查看适用 ClusterResourceQuotas

项目管理员无法创建或修改多项目配额来限制自己的项目,但管理员可以查看应用到自己项目的多项目配额文档。项目管理员可以通过 AppliedClusterResourceQuota 资源完成此操作。

$ oc describe AppliedClusterResourceQuota

生成:

Name:   for-user
Namespace:  <none>
Created:  19 hours ago
Labels:   <none>
Annotations:  <none>
Label Selector: <null>
AnnotationSelector: map[openshift.io/requester:<user-name>]
Resource  Used  Hard
--------  ----  ----
pods        1     10
secrets     9     20

18.4. 选择图形性

由于在声明配额分配时会考虑锁定,因此通过多项目配额选择的活跃项目数量是一个重要因素。在单个多项目配额下选择 100 多个项目可能会对这些项目中的 API 服务器响应产生影响。

第 19 章 修剪对象

19.1. 概述

随着时间推移,OpenShift Container Platform 中创建的 API 对象可以通过常规用户操作 (如构建和部署应用程序)积累到 etcd 数据存储中

作为管理员,您可以定期从 OpenShift Container Platform 实例中修剪不再需要的旧版本对象。例如,您可以通过修剪镜像来删除不再使用但仍然占用磁盘空间的旧镜像和旧层。

19.2. 基本修剪操作

CLI 将修剪操作分组到一个通用的父命令下。

$ oc adm prune <object_type> <options>

这将指定:

  • 要对其执行操作的 <object_type>,如 groupsbuildsdeploymentsimages
  • <options> 支持修剪该对象类型。

19.3. 修剪组

要修剪来自外部提供程序的组记录,管理员可以运行以下命令:

$ oc adm prune groups --sync-config=path/to/sync/config [<options>]

表 19.1. 修剪组 CLI 配置选项

选项描述

--confirm

指明应该执行修剪,而不是空运行。

--blacklist

指向组黑名单文件的路径。有关黑名单文件结构,请参阅使用 LDAP 同步组

--whitelist

指向组白名单文件的路径。有关白名单文件结构,请参阅使用 LDAP 同步组

--sync-config

指向同步配置文件的路径。有关此文件的结构,请参阅配置 LDAP 同步

查看 prune 命令删除的组:

$ oc adm prune groups --sync-config=ldap-sync-config.yaml

执行修剪操作:

$ oc adm prune groups --sync-config=ldap-sync-config.yaml --confirm

19.4. 修剪部署

要修剪因为时间和状态而不再需要的部署,管理员可运行以下命令:

$ oc adm prune deployments [<options>]

表 19.2. 修剪部署 CLI 配置选项

选项描述

--confirm

指明应该执行修剪,而不是空运行。

--orphans

修剪所有不再存在部署配置、状态为 complete 或 failed 且副本数为零的部署。

--keep-complete=<N>

对于每个部署配置,保留状态为完成且副本数为零的最后 N 个部署。(默认值 5

--keep-failed=<N>

对于每个部署配置,保留状态为失败且副本数为零的最后 N 个部署。(默认值 1

--keep-younger-than=<duration>

不要修剪相对于当前时间年龄不到 <duration> 的对象。(默认 60m)有效的测量单位包括纳秒(ns)、微秒(us)、毫秒(ms)、秒(s)、分钟(m)和小时(h)。

查看修剪操作要删除的对象:

$ oc adm prune deployments --orphans --keep-complete=5 --keep-failed=1 \
    --keep-younger-than=60m

真正执行修剪操作:

$ oc adm prune deployments --orphans --keep-complete=5 --keep-failed=1 \
    --keep-younger-than=60m --confirm

19.5. 修剪构建

要修剪系统因为年龄和状态而不再需要的构建,管理员可运行以下命令:

$ oc adm prune builds [<options>]

表 19.3. 修剪构建 CLI 配置选项

选项描述

--confirm

指明应该执行修剪,而不是空运行。

--orphans

修剪不再存在构建配置、状态为 Complete、Failed、Error 或 Canceled 的构建。

--keep-complete=<N>

对于每个构建配置,保留状态为 Complete 的最后 N 个构建。(默认值 5

--keep-failed=<N>

对于每个构建配置,保留状态为 failed、error 或 Canceled 的最后 N 个构建(默认值 1

--keep-younger-than=<duration>

不修剪相对于当前时间年龄不到 <duration> 的对象。(默认值 60m

查看修剪操作要删除的对象:

$ oc adm prune builds --orphans --keep-complete=5 --keep-failed=1 \
    --keep-younger-than=60m

真正执行修剪操作:

$ oc adm prune builds --orphans --keep-complete=5 --keep-failed=1 \
    --keep-younger-than=60m --confirm

19.6. 修剪镜像

要修剪系统因为年龄、状态或超过限值而不再需要的镜像,管理员可运行以下命令:

$ oc adm prune images [<options>]
注意

除非使用了 --prune-registry=false,否则修剪镜像会从集成 registry 中删除数据。要使此操作正常工作,请确保 registry 已配置为 truestorage:delete:enabled

注意

使用 --namespace 标志修剪镜像不会移除镜像,只有镜像流。镜像是没有命名空间的资源。因此,将修剪限制到特定的命名空间会导致无法计算其当前使用量。

默认情况下,集成 registry 会缓存 blob 元数据来减少对存储的请求数量,并提高处理请求的速度。修剪不会更新集成 registry 缓存。在修剪后推送的镜像如果含有修剪的层,它们会被破坏,因为不会推送在缓存中有元数据的已修剪层。因此,修剪之后需要清除缓存。这可以通过重新部署 registry 来完成:

$ oc rollout latest dc/docker-registry

如果集成 registry 使用 redis 缓存,则需要手动清理数据库。

如果无法在修剪后重新部署 registry,则必须永久禁用缓存

表 19.4. 修剪镜像 CLI 配置选项

选项描述

--all

包括没有推送到 registry 但已通过 pullthrough 镜像的镜像。默认为开启。要将修剪限制为推送到集成 registry 的镜像,传递 --all=false

--certificate-authority

与 OpenShift Container Platform 管理的 registry 通信时使用的证书颁发机构文件的路径。默认为来自当前用户配置文件的证书颁发机构数据。如果提供,将启动安全连接。

--confirm

指明应该执行修剪,而不是空运行。这需要具有指向集成容器镜像 registry 的有效路由。如果这个命令在集群网络外运行,则需要使用 --registry-url 提供路由。

--force-insecure

谨慎使用这个选项。允许与通过 HTTP 托管或具有无效 HTTPS 证书的 Docker 注册表进行不安全连接。如需更多信息,请参阅使用安全连接或不安全连接

--keep-tag-revisions=<N>

对于每个镜像流,每个标签最多保留 N 个镜像修订。(默认值 3

--keep-younger-than=<duration>

不修剪相对于当前时间年龄不到 <duration> 的镜像。不修剪被相对于当前时间年龄不到 <duration> 的其他对象引用的镜像。(默认值 60m

--prune-over-size-limit

修剪超过同一项目中定义的最小限值的每个镜像。这个标记不能与 --keep-tag-revisions--keep-younger-than 组合。

--registry-url

联系 registry 时使用的地址。命令将尝试使用由受管镜像和镜像流决定的集群内部 URL。如果失败(registry 无法解析或访问),则需要使用此标志提供一个替代路由。registry 主机名可以带有前缀 https://http://,它们强制执行特定的连接协议。

--prune-registry

此选项与其他选项规定的条件一起使用,用于控制是否修剪 registry 中与 OpenShift Container Platform 镜像 API 对象对应的数据。默认情况下,镜像修剪同时处理镜像 API 对象和 registry 中的对应数据。当您只关注移除 etcd 内容时(可能要减少镜像对象的数量,但并不关心清理 registry)或要通过硬化 Pruning Registry 来单独进行此操作(可能在 registry 的适当维护窗口期间),此选项很有用处。

19.6.1. 镜像修剪条件

  • 删除至少在 --keep-younger-than 分钟之前创建且目前没有引用的任何镜像"由 OpenShift Container Platform 管理"(带有注解 openshift.io/image.managed的镜像):

    • 任何小于 --keep-younger-than 分钟前创建的 pod。
    • 任何小于 --keep-younger-than 分钟前创建的镜像流。
    • 任何正在运行的容器集。
    • 任何待处理的 pod。
    • 任何复制控制器。
    • 任何部署配置。
    • 任何构建配置。
    • 任何构建。
    • stream.status.tags[].items 中的 --keep-tag-revisions 最新项目。
  • 删除任何"由 OpenShift Container Platform 管理"的镜像(带有注解 openshift.io/image.managed 的镜像),该镜像超过同一项目中定义的最小限值,且目前没有被以下对象引用:

    • 任何正在运行的容器集。
    • 任何待处理的 pod。
    • 任何复制控制器。
    • 任何部署配置。
    • 任何构建配置。
    • 任何构建。
  • 不支持从外部 registry 进行修剪。
  • 镜像被修剪后,会从引用 status.tags 中的镜像的所有镜像流中移除对该镜像的所有引用。
  • 也移除不再被任何镜像引用的镜像层。
注意

--prune-over-size-limit 无法与 --keep-tag-revisions--keep-younger-than 标记结合使用。这样做将返回不允许执行此操作的信息。

注意

与尝试通过一个命令同时修剪这两者相比,将 OpenShift Container Platform 镜像 API 对象和镜像数据从 Registry 分离,使用 --prune-registry=false 然后再对 Registry 进行硬修剪可以缩减一些计时窗口。但是,计时窗口不会完全剔除。

例如,您仍然可以创建一个引用镜像的 Pod,因为修剪会标识该镜像进行修剪。您仍然应该跟踪修剪操作期间创建的 API 对象,该对象可能会引用镜像,从而避免引用已删除的内容。

另外,请注意,在没有使用 --prune-registry 选项或使用 --prune-registry=true 选项的情况下重新执行修剪操作不会导致修剪之前由 --prune-registry=false 修剪的镜像的镜像 registry 中关联的存储。任何使用 --prune-registry=false 修剪的镜像都可以通过 Hard Pruning the Registry 从 registry 存储中删除。

查看修剪操作要删除的对象:

  1. 最多保留三个标签修订,并且保留年龄不足 60 分钟的资源(镜像、镜像流和 pod):

    $ oc adm prune images --keep-tag-revisions=3 --keep-younger-than=60m
  2. 修剪超过定义的限值的所有镜像:

    $ oc adm prune images --prune-over-size-limit

实际对上述选项执行修剪操作:

$ oc adm prune images --keep-tag-revisions=3 --keep-younger-than=60m --confirm

$ oc adm prune images --prune-over-size-limit --confirm

19.6.2. 使用安全或不安全连接

安全连接是首选和推荐的方法。它通过 HTTPS 协议来进行,并且会强制验证证书。prune 命令始终会尝试使用它(如果可能)。如果不可能,在某些情况下可能会回退到不安全连接,而这很危险。这时,会跳过证书验证或使用普通 HTTP 协议。

除非指定了 --certificate-authority,否则在以下情况下允许回退到不安全连接:

  1. prune 命令使用 --force-insecure 选项运行。
  2. 提供的 registry-url 带有 http:// 方案前缀。
  3. 提供的 registry-url 是本地链接地址或 localhost。
  4. 当前用户的配置允许不安全连接。这可能是由用户使用 --insecure-skip-tls-verify 登录或在提示时选择不安全连接造成的。
重要

如果 registry 是由不同于 OpenShift Container Platform 使用的证书颁发机构保护的,则需要使用 --certificate-authority 标志来指定。否则,prune 命令将失败,并显示与使用 Wrong 证书颁发机构或使用 安全连接 Against Secured Registry 中列出的类似错误

19.6.3. 镜像修剪问题

镜像没有被修剪

如果您的镜像不断积累,且 prune 命令只删除预期的一小部分,请确保了解镜像被视为修剪候选者的条件

请尤其确保您要删除的镜像在每个标签历史记录中的位置高于您所选的标签修订阈值。例如,考虑一个名为 sha:abz 的旧旧镜像。在命名空间 N 中运行以下命令,镜像被标记,您会在一个名为 myapp 的镜像流中对该镜像标记三次:

$ image_name="sha:abz"
$ oc get is -n openshift -o go-template='{{range $isi, $is := .items}}{{range $ti, $tag := $is.status.tags}}{{range $ii, $item := $tag.items}}{{if eq $item.image "'$image_name'"}}{{$is.metadata.name}}:{{$tag.tag}} at position {{$ii}} out of {{len $tag.items}}
{{end}}{{end}}{{end}}{{end}}' # Before this place {{end}}{{end}}{{end}}{{end}}, use new line
myapp:v2 at position 4 out of 5
myapp:v2.1 at position 2 out of 2
myapp:v2.1-may-2016 at position 0 out of 1

使用默认选项时,永远不会修剪镜像,因为它出现在 myapp:v2.1-may-2016 标签历史记录中的 0 的位置。要将镜像视为需要修剪,管理员必须:

  1. 使用 oc adm prune images 命令指定 --keep-tag-revisions=0

    小心

    此操作将从所有含有基础镜像的命名空间中有效移除所有标签,除非它们比指定阈值年轻,或者有比指定阈值年轻的对象引用它们。

  2. 删除所有位置低于修订阈值的istag,即 myapp:v2.1myapp:v2.1-may-2016
  3. 在历史记录中进一步移动镜像,可以通过运行新构建并推送到同一 istag,或者标记其他镜像。遗憾的是,这对旧版标签可能并不始终适合。

应该避免在标签的名称中包含特定镜像的构建日期或时间,除非镜像需要保留不定的时长。这样的标签通常在历史记录中只有一个镜像,实际上会永久阻止它们被修剪。了解有关 istag 命名的更多信息.

对不安全 registry 使用安全连接

如果您在 oc adm prune images 输出中看到类似如下的消息,则您的 registry 不会被保护,oc adm prune images 客户端会尝试使用安全连接:

error: error communicating with registry: Get https://172.30.30.30:5000/healthz: http: server gave HTTP response to HTTPS client
  1. 重新连接的解决方案是保护注册表的安全。如果不需要,您可以通过在命令中附加 --force-insecure 来强制客户端使用不安全连接(不推荐)。

19.6.3.1. 对受保护 registry 使用不安全连接

如果您在 oc adm prune images 命令输出中看到以下错误之一,这意味着您的 registry 已设有保护,而不是使用 oc adm prune images 客户端用于连接验证的证书。

error: error communicating with registry: Get http://172.30.30.30:5000/healthz: malformed HTTP response "\x15\x03\x01\x00\x02\x02"
error: error communicating with registry: [Get https://172.30.30.30:5000/healthz: x509: certificate signed by unknown authority, Get http://172.30.30.30:5000/healthz: malformed HTTP response "\x15\x03\x01\x00\x02\x02"]

默认情况下,存储在用户配置文件中的证书颁发机构数据用于与主 API 通信。

使用 --certificate-authority 选项为容器镜像 registry 服务器提供正确的证书颁发机构。

使用错误的证书颁发机构

以下错误表示用于为受保护容器镜像 registry 的证书签名的证书颁发机构与客户端使用的不同。

error: error communicating with registry: Get https://172.30.30.30:5000/: x509: certificate signed by unknown authority

确保提供带有 --certificate-authority 标志的正确选项。

作为临时解决方案,可以添加 --force-insecure 标记(不推荐)。

19.7. 硬修剪 registry

OpenShift Container Registry 可能会积累未被 OpenShift Container Platform 集群的 etcd 引用的 Blob。因此,基本镜像修剪过程对它们无法操作。它们称为孤立的 Blob

以下情形中可能会出现孤立的 Blob:

  • 使用 oc delete image <sha256:image-id> 命令手动删除镜像,该命令只从 etcd 中删除镜像,而不从 registry 存储中删除。
  • docker 守护进程失败引发推送到 registry,这会导致上传一些 Blob,但镜像清单(这作为最后一个组件上传)不会上传。所有唯一镜像 Blob 变成孤立的 Blob。
  • OpenShift Container Platform 因为配额限制而拒绝某一镜像。
  • 标准镜像修剪器删除镜像清单,但在删除相关 Blob 前中断。
  • registry 修剪器中有一个程序错误,无法移除预定的 Blob,从而导致引用它们的镜像对象被移除,并且 Blob 变成孤立的 Blob。

硬修剪 registry,这是独立于基本镜像修剪的过程,允许您删除孤立的 Blob。如果 OpenShift Container registry 的存储空间不足,并且您认为有孤立的 Blob,则应该执行硬修剪。

这应该是罕见的操作,只有在有证据表明创建了大量新的孤立项时才需要。否则,您可以定期执行标准镜像修剪,例如一天一次(取决于要创建的镜像数量)。

从 registry 中硬修剪孤立的 Blob:

  1. 登录 :使用 CLI 以具有访问令牌的用户身份登录
  2. 运行基本镜像修剪 :基本镜像修剪会移除不再需要的额外镜像。硬修剪不移除自己的镜像,只移除保存在 registry 存储中的 Blob。因此,您应该在硬修剪之前运行此操作。

    如需了解步骤,请参阅修剪镜像。

  3. registry 切换为只读模式 : 如果 registry 没有以只读模式运行,则任何在修剪的同时发生的推送都将:

    • 失败,并导致出现新的孤立项;或者
    • 成功,尽管镜像不可拉取(因为删除了一些引用的 Blob)。

    只有 registry 切回到读写模式后,推送才会成功。因此,必须仔细地调度硬修剪。

    将 registry 切换成只读模式:

    1. 设置以下环境变量:

      $ oc set env -n default \
          dc/docker-registry \
          'REGISTRY_STORAGE_MAINTENANCE_READONLY={"enabled":true}'
    2. 默认情况下,上一步骤完成后,注册表应自动重新部署;等待重新部署完成后再继续。然而,如果您禁用了这些触发器,则必须手动重新部署 registry,以便获取新的环境变量:

      $ oc rollout -n default \
          latest dc/docker-registry
  4. 添加 system:image-pruner 角色 :用于运行 registry 实例的服务帐户需要额外的权限才能列出某些资源。

    1. 获取服务帐户名称:

      $ service_account=$(oc get -n default \
          -o jsonpath=$'system:serviceaccount:{.metadata.namespace}:{.spec.template.spec.serviceAccountName}\n' \
          dc/docker-registry)
    2. system:image-pruner 集群角色添加到服务帐户:

      $ oc adm policy add-cluster-role-to-user \
          system:image-pruner \
          ${service_account}
  5. (可选)以空运行模式运行修剪器 : 要查看要移除多少 Blob,请以空运行模式运行硬修剪器。不会实际进行任何更改:

    $ oc -n default \
        exec -i -t "$(oc -n default get pods -l deploymentconfig=docker-registry \
        -o jsonpath=$'{.items[0].metadata.name}\n')" \
        -- /usr/bin/dockerregistry -prune=check

    另外,若要获得修剪候选者的准确路径,可提高日志级别:

    $ oc -n default \
        exec "$(oc -n default get pods -l deploymentconfig=docker-registry \
          -o jsonpath=$'{.items[0].metadata.name}\n')" \
        -- /bin/sh \
        -c 'REGISTRY_LOG_LEVEL=info /usr/bin/dockerregistry -prune=check'

    已截断的输出示例

    $ oc exec docker-registry-3-vhndw \
        -- /bin/sh -c 'REGISTRY_LOG_LEVEL=info /usr/bin/dockerregistry -prune=check'
    
    time="2017-06-22T11:50:25.066156047Z" level=info msg="start prune (dry-run mode)" distribution_version="v2.4.1+unknown" kubernetes_version=v1.6.1+$Format:%h$ openshift_version=unknown
    time="2017-06-22T11:50:25.092257421Z" level=info msg="Would delete blob: sha256:00043a2a5e384f6b59ab17e2c3d3a3d0a7de01b2cabeb606243e468acc663fa5" go.version=go1.7.5 instance.id=b097121c-a864-4e0c-ad6c-cc25f8fdf5a6
    time="2017-06-22T11:50:25.092395621Z" level=info msg="Would delete blob: sha256:0022d49612807cb348cabc562c072ef34d756adfe0100a61952cbcb87ee6578a" go.version=go1.7.5 instance.id=b097121c-a864-4e0c-ad6c-cc25f8fdf5a6
    time="2017-06-22T11:50:25.092492183Z" level=info msg="Would delete blob: sha256:0029dd4228961086707e53b881e25eba0564fa80033fbbb2e27847a28d16a37c" go.version=go1.7.5 instance.id=b097121c-a864-4e0c-ad6c-cc25f8fdf5a6
    time="2017-06-22T11:50:26.673946639Z" level=info msg="Would delete blob: sha256:ff7664dfc213d6cc60fd5c5f5bb00a7bf4a687e18e1df12d349a1d07b2cf7663" go.version=go1.7.5 instance.id=b097121c-a864-4e0c-ad6c-cc25f8fdf5a6
    time="2017-06-22T11:50:26.674024531Z" level=info msg="Would delete blob: sha256:ff7a933178ccd931f4b5f40f9f19a65be5eeeec207e4fad2a5bafd28afbef57e" go.version=go1.7.5 instance.id=b097121c-a864-4e0c-ad6c-cc25f8fdf5a6
    time="2017-06-22T11:50:26.674675469Z" level=info msg="Would delete blob: sha256:ff9b8956794b426cc80bb49a604a0b24a1553aae96b930c6919a6675db3d5e06" go.version=go1.7.5 instance.id=b097121c-a864-4e0c-ad6c-cc25f8fdf5a6
    ...
    Would delete 13374 blobs
    Would free up 2.835 GiB of disk space
    Use -prune=delete to actually delete the data

  6. 运行硬修剪 :在 docker-registry pod 的一个正在运行的实例中执行以下命令来运行硬修剪:

    $ oc -n default \
        exec -i -t "$(oc -n default get pods -l deploymentconfig=docker-registry -o jsonpath=$'{.items[0].metadata.name}\n')" \
        -- /usr/bin/dockerregistry -prune=delete

    输出示例

    $ oc exec docker-registry-3-vhndw \
        -- /usr/bin/dockerregistry -prune=delete
    
    Deleted 13374 blobs
    Freed up 2.835 GiB of disk space

  7. registry 切回到读写模式 :在修剪完成后,可以通过执行以下命令将 registry 切回到读写模式:

    $ oc set env -n default dc/docker-registry REGISTRY_STORAGE_MAINTENANCE_READONLY-

19.8. 修剪 Cron 任务

重要

Cron Jobs 只是一个技术预览功能。技术预览功能不包括在红帽生产服务级别协议(SLA)中,且其功能可能并不完善。因此,红帽不建议在生产环境中使用它们。这些技术预览功能可以使用户提早试用新的功能,并有机会在开发阶段提供反馈意见。

如需红帽技术预览功能支持范围的更多信息,请参阅 https://access.redhat.com/support/offerings/techpreview/

Cron 任务可以修剪成功的任务,但可能无法正确处理失败的作业。因此,集群管理员应定期手动清理作业。我们还建议将对 cron 作业的访问限制为一小组值得信任的用户 ,并设置适当的配额以防止 cron 作业创建太多的作业和 pod。

第 20 章 使用自定义资源扩展 Kubernetes API

20.1. Kubernetes 自定义资源定义

在 Kubernetes API 中,资源是存储某一类 API 对象集合的端点。例如,内置 pod 资源包含一组 Pod 对象。

自定义资源是扩展 Kubernetes API 的对象,或者允许您将自己的 API 引入到项目或集群中。

自定义资源定义 (CRD)文件定义了您自己的对象类型,并允许 API 服务器处理整个生命周期。将 CRD 部署到集群中会导致 Kubernetes API 服务器开始提供指定的自定义资源。

当您创建新的自定义资源定义(CRD)时,Kubernetes API 服务器会通过创建新的 RESTful 资源路径来做出反应,该路径可由整个集群或单个项目(命名空间)访问。与现有的内置对象一样,删除项目会删除该项目中的所有自定义对象。

如果要向用户授予 CRD 访问权限,请使用集群角色聚合为用户授予 admin、edit 或 view 默认集群角色的访问权限。集群角色聚合支持将自定义策略规则插入到这些集群角色中。这一行为会将新资源整合至集群的 RBAC 策略中,就像内置资源一样。

注意

虽然只有集群管理员可以创建 CRD,但如果 CRD 有读写权限,您可以从 CRD 创建对象。

20.2. 创建自定义资源定义

要创建自定义对象,您必须首先创建一个自定义资源定义(CRD)。

注意

只有集群管理员才能创建 CRD。

流程

要创建 CRD:

  1. 创建一个包含以下示例字段的 YAML 文件:

    自定义资源定义的 YAML 文件示例

    apiVersion: apiextensions.k8s.io/v1beta1 1
    kind: CustomResourceDefinition
    metadata:
      name: crontabs.stable.example.com 2
    spec:
      group: stable.example.com 3
      version: v1 4
      scope: Namespaced 5
      names:
        plural: crontabs 6
        singular: crontab 7
        kind: CronTab 8
        shortNames:
        - ct 9

    1
    使用 apiextensions.k8s.io/v1beta1 API。
    2
    为定义指定名称。这必须采用 <plural-name><group> 格式,使用 groupplural 字段中的值。
    3
    为 API 指定组名。API 组是一个逻辑上相关的对象集。例如,所有批处理对象(如 JobScheduledJob )都可能位于批处理 API 组(如 batch.api.example.com)中。最好使用组织的完全限定域名。
    4
    指定 URL 中要用的版本名称。每个 API 组均可能存在于多个版本中。例如: v1alphav1betav1
    5
    指定自定义对象是否可用于项目(Namespaced)或集群中的所有项目(Cluster)。
    6
    指定 URL 中要用的复数名称。plural 字段与 API URL 中的资源相同。
    7
    指定将在 CLI 上用作别名并用于显示的单数名称。
    8
    指定可创建的对象类型。类型可以采用 CamelCase。
    9
    指定与 CLI 中的资源相匹配的较短字符串。
    注意

    默认情况下,自定义资源定义是全集群范围的,适用于所有项目。

  2. 创建对象:

    oc create -f <file-name>.yaml

    在以下位置新建一个 RESTful API 端点:

    /apis/<spec:group>/<spec:version>/<scope>/*/<names-plural>/...

    例如,以下端点便是通过示例文件创建的:

    /apis/stable.example.com/v1/namespaces/*/crontabs/...

    您可以使用此端点 URL 来创建和管理自定义对象。对象类型基于您创建的自定义资源定义对象的 spec.kind 字段。

20.3. 为自定义资源定义创建集群角色

创建集群范围的自定义资源定义(CRD)后,您可以为其授予权限。如果使用 admin、edit 和 view 默认集群角色,请利用集群角色聚合来制定规则。

重要

您必须为每个角色明确分配权限。权限更多的角色不会继承权限较少角色的规则。如果要为某个角色分配规则,还必须将该操作动词分配给具有更多权限的角色。例如,如果您将"get crontabs"权限授予 view 角色,则还必须将其授予 edit 和 admin 角色。admin 或 edit 角色通常分配给通过项目模板创建项目的用户

先决条件

  • 创建 CRD。

流程

  1. 为 CRD 创建集群角色定义文件。集群角色定义是一个 YAML 文件,其中包含适用于各个集群角色的规则。OpenShift Container Platform 控制器会将您指定的规则添加至默认集群角色中。

    集群角色定义的 YAML 文件示例

    apiVersion: rbac.authorization.k8s.io/v1 1
    kind: ClusterRole
    items:
      - metadata:
          name: aggregate-cron-tabs-admin-edit 2
          labels:
            rbac.authorization.k8s.io/aggregate-to-admin: "true" 3
            rbac.authorization.k8s.io/aggregate-to-edit: "true" 4
        rules:
          - apiGroups: ["stable.example.com"] 5
            resources: ["crontabs"] 6
            verbs: ["get", "list", "watch", "create",
                    "update", "patch", "delete", "deletecollection"] 7
      - metadata:
          name: aggregate-cron-tabs-view 8
          labels:
            # Add these permissions to the "view" default role.
            rbac.authorization.k8s.io/aggregate-to-view: "true" 9
            rbac.authorization.k8s.io/aggregate-to-cluster-reader: "true" 10
        rules:
          - apiGroups: ["stable.example.com"] 11
            resources: ["crontabs"] 12
            verbs: ["get", "list", "watch"] 13

    1
    使用 apiextensions.k8s.io/v1beta1 API。
    2 8
    为定义指定名称。
    3
    指定该标签向 admin 默认角色授予权限。
    4
    指定该标签向 edit 默认角色授予权限。
    5 11
    指定 CRD 的组名。
    6 12
    指定适用于这些规则的 CRD 的复数名称。
    7 13
    指定代表角色获得的权限的操作动词。例如,将读取和写入权限应用到 admin 和 edit 角色,并且仅对 view 角色应用读取权限。
    9
    指定该标签向 view 默认角色授予权限。
    10
    指定该标签向 cluster-reader 默认角色授予权限。
  2. 创建集群角色:

    oc create -f <file-name>.yaml

20.4. 从 CRD 创建自定义对象

创建自定义资源定义(CRD)对象后,您可以创建自定义对象来使用其规格。

自定义对象可以包含包含任意 JSON 代码的自定义字段。

先决条件

  • 创建 CRD。

流程

  1. 为自定义对象创建 YAML 定义。在以下示例中,cronSpecimage 自定义字段是在 CronTab 的自定义对象中设置的。kind 来自自定义资源定义对象的 spec.kind 字段。

    自定义对象的 YAML 文件示例

    apiVersion: "stable.example.com/v1" 1
    kind: CronTab 2
    metadata:
      name: my-new-cron-object 3
      finalizers: 4
      - finalizer.stable.example.com
    spec: 5
      cronSpec: "* * * * /5"
      image: my-awesome-cron-image

    1
    指定自定义资源定义中的组名称和 API 版本(名称/版本)。
    2
    指定自定义资源定义中的类型。
    3
    指定对象的名称。
    4
    指定对象的结束程序(如有)。结束程序可让控制器实现在删除对象之前必须完成的条件。
    5
    指定特定于对象类型的条件。
  2. 创建对象文件后,创建对象:

    oc create -f <file-name>.yaml

20.5. 管理自定义对象

创建对象后,您可以管理自定义资源。

先决条件

  • 创建自定义资源定义(CRD)。
  • 从 CRD 创建对象。

流程

  1. 要获取有关特定自定义资源的信息,请输入:

    oc get <kind>

    例如:

    oc get crontab
    
    NAME                 KIND
    my-new-cron-object   CronTab.v1.stable.example.com

    请注意,资源名称不区分大小写,您可以使用 CRD 中定义的单数或复数形式,以及任何短名称。例如:

    oc get crontabs
    oc get crontab
    oc get ct
  2. 您还可以查看自定义资源的原始 YAML 数据:

    oc get <kind> -o yaml
    oc get ct -o yaml
    
    apiVersion: v1
    items:
    - apiVersion: stable.example.com/v1
      kind: CronTab
      metadata:
        clusterName: ""
        creationTimestamp: 2017-05-31T12:56:35Z
        deletionGracePeriodSeconds: null
        deletionTimestamp: null
        name: my-new-cron-object
        namespace: default
        resourceVersion: "285"
        selfLink: /apis/stable.example.com/v1/namespaces/default/crontabs/my-new-cron-object
        uid: 9423255b-4600-11e7-af6a-28d2447dc82b
      spec:
        cronSpec: '* * * * /5' 1
        image: my-awesome-cron-image 2
    1 2
    显示用于创建对象的 YAML 的自定义数据。

第 21 章 垃圾收集器

21.1. 概述

OpenShift Container Platform 节点执行两种类型的垃圾回收:

21.2. Container Garbage Collection

容器垃圾回收默认是启用的,并会在响应达到驱除阈值时自动发生。节点会尝试为任何可从 API 访问的 pod 保留容器。如果 pod 已被删除,则容器也会被删除。只要 pod 没有被删除且没有达到驱除阈值,容器就会保留。如果节点面临磁盘压力,它将移除容器,它们的日志将不再能通过 oc logs 访问。

容器垃圾收集策略基于三个节点设置:

设置描述

minimum-container-ttl-duration

容器符合垃圾回收条件的最低期限。默认值为 0。使用 0 代表没有限制。此设置的值可以使用单位后缀指定,例如 h 表示小时,m 表示分钟 ,s 代表秒。

maximum-dead-containers-per-container

每个容器要保留的旧实例数量。默认值为 1

maximum-dead-containers

节点中死容器的最大数量。默认值为 -1,即无限制。

注意

在存在冲突时,maximum-dead-containers 设置优先于 maximum-dead-containers-per-container 设置。例如:如果保留 maximum-dead-containers-per-container 的数量,则会导致一个大于 maximum-dead-containers 的容器总数,则会删除最旧的容器来满足 maximum-dead-containers 限制。

当节点移除死容器时,这些容器内的所有文件也会被删除。只有节点创建的容器才会收集垃圾回收。

如果您不想使用默认设置,您可以在适当的节点配置映射的 kubeletArguments 部分为这些设置指定值。如果尚未存在,请添加 部分。

注意

如果节点配置映射中没有这些参数,则容器垃圾回收使用默认值执行。

Container Garbage Collection 设置

kubeletArguments:
  minimum-container-ttl-duration:
    - "10s"
  maximum-dead-containers-per-container:
    - "2"
  maximum-dead-containers:
    - "240"

21.2.1. 检测容器进行删除

垃圾收集器循环的每个旋转都执行以下步骤:

  1. 检索可用容器的列表。
  2. 过滤掉所有运行或未超过 minimum-container-ttl-duration 参数的容器。未处于活动状态的容器可能处于 exited、死机或已终止状态。
  3. 根据 pod 和镜像名称成员资格,将所有剩余容器分类为等类。
  4. 删除所有未经识别的容器(由 kubelet 管理但名称不正确的容器)。
  5. 对于每个包含的容器超过 maximum-dead-containers-per-container 参数的类,根据创建时间对类中的容器进行排序。
  6. 首先从最旧的容器删除容器,直到满足 maximum-dead-containers-per-container 参数。
  7. 如果列表中的容器数量仍然超过 maximum-dead-containers 参数,收集器开始从每个类中删除容器,因此每个类中的容器数量不会大于每个类或 <all_remaining_containers>/<number_of_classes> 的平均容器数量。
  8. 如果这仍然不够,收集器会对列表中的所有容器进行排序,并首先从最旧的容器中删除容器,直到满足 maximum-dead-containers 标准为止。
重要

更新默认设置以满足您的需要。

垃圾回收仅移除没有与其关联的 pod 的容器。

21.3. Image Garbage Collection

镜像垃圾回收依靠节点上 cAdvisor 报告的磁盘用量来决定从节点中移除哪些镜像。它考虑以下设置:

设置描述

image-gc-high-threshold

触发镜像垃圾回收的磁盘用量百分比(以整数表示)。

image-gc-low-threshold

镜像垃圾回收试尝试释放的磁盘用量百分比(以整数表示)。

要启用镜像垃圾回收,在适当的节点配置映射的 kubeletArguments 部分指定这些设置的值。如果尚未存在,请添加 部分。

注意

如果节点配置映射中没有这些参数,则镜像垃圾回收将使用默认值来执行。

Image Garbage Collection 设置

kubeletArguments:
  image-gc-high-threshold:
    - "85"
  image-gc-low-threshold:
    - "80"

21.3.1. 检测镜像以进行删除

每次运行垃圾收集器都会检索两个镜像列表:

  1. 当前在至少一个 pod 中运行的镜像列表
  2. 主机上可用镜像的列表

随着新容器运行,新镜像即会出现。所有镜像都标有时间戳。如果镜像正在运行(上方第一个列表)或者刚被检测到(上方第二个列表),它将标上当前的时间。其余镜像的标记来自于以前的运行。然后,所有镜像都根据时间戳进行排序。

一旦开始回收,首先删除最旧的镜像,直到满足停止条件。

第 22 章 分配节点资源

22.1. 分配节点资源的目的

为提供更可靠的调度并最大程度减少节点资源过量使用,请保留一部分 CPU 和内存资源供底层节点组件 (如 kubelet、kube-proxy 和容器引擎)使用。您保留的资源也供 sshd、NetworkManager 等其余系统组件使用。指定要保留的资源可为调度程序提供有关节点可用于 pod 使用的剩余内存和 CPU 资源的更多信息。

22.2. 为分配的资源配置节点

通过配置 system-reserved 节点设置,为 OpenShift Container Platform 中的节点组件和系统组件保留资源。

OpenShift Container Platform 不使用 kube-reserved 设置。Kubernetes 和提供 Kubernetes 环境的一些云供应商的文档可能建议配置 kube-reserved。该信息不适用于 OpenShift Container Platform 集群。

当您根据资源限制调整集群并使用驱除强制实施限制时,请小心。当内存资源运行不足时,强制 system-reserved 限制可防止关键系统服务接收 CPU 时间或终止关键系统服务。

在大多数情况下,调优资源分配是通过调整调整,然后使用类生产工作负载监控集群性能来进行的。这个过程会重复,直到集群稳定并符合服务级别协议。

有关这些设置的影响的更多信息,请参阅计算分配资源

设置描述

kube-reserved

此设置不会用于 OpenShift Container Platform。将您要保留的 CPU 和内存资源添加到 system-reserved 设置中。

system-reserved

为节点组件和系统组件保留的资源。默认为 none。

运行以下命令,使用一个工具(如 lscgroup )查看由 system-reserved 控制的服务:

# yum install libcgroup-tools
$ lscgroup memory:/system.slice

通过添加一组 <resource_type>=<resource_quantity> 对在节点配置映射的 kubeletArguments 部分中保留资源。例如,cpu=500m,memory=1Gi 保留 500 毫秒的 CPU 和 1GB 内存。

例 22.1. Node-Allocatable 资源设置

kubeletArguments:
  system-reserved:
    - "cpu=500m,memory=1Gi"

如果 system-reserved 字段不存在,请添加该字段。

注意

不要直接编辑 node-config.yaml 文件。

要确定这些设置的适当值,请使用节点概述 API 查看节点的资源使用情况。如需更多信息,请参阅由节点报告的系统资源

设置 system-reserved

  • 监控节点的高水位标记的内存使用情况:

    $ ps aux | grep <service-name>

    例如:

    $ ps aux | grep atomic-openshift-node
    
    USER       PID   %CPU  %MEM  VSZ     RSS  TTY    STAT  START  TIME  COMMAND
    root       11089 11.5  0.3   112712  996  pts/1  R+    16:23  0:00  grep --color=auto atomic-openshift-node

    如果这个值接近您的 system-reserved 标记,您可以提高 system-reserved 值。

  • 运行以下命令,使用工具(如 cgget )监控系统服务的内存使用情况:

    # yum install libcgroup-tools
    $ cgget -g memory  /system.slice | grep memory.usage_in_bytes

    如果这个值接近您的 system-reserved 标记,您可以提高 system-reserved 值。

  • 使用 OpenShift Container Platform 集群装载程序来测量处于各种集群状态的部署的性能指标

22.3. 计算分配的资源

分配的资源数量根据以下公式来计算:

[Allocatable] = [Node Capacity] - [system-reserved] - [Hard-Eviction-Thresholds]
注意

从可分配量中保留 Hard-Eviction-Thresholds 可以提高系统可靠性,因为对节点级别的 pod 强制使用可分配量的值。experimental-allocatable-ignore-eviction 设置可用于保留旧行为,但将在以后的版本中弃用。

如果 [Allocatable] 为负数,它将被设置为 0

22.4. 查看 Node-Allocatable 资源和容量

要查看节点的当前容量和可分配的资源,请运行以下命令:

$ oc get node/<node_name> -o yaml

在以下部分输出中,可分配的值小于容量。差别是正常的,与 system-reservedcpu=500m,memory=1Gi 资源分配匹配。

status:
...
  allocatable:
    cpu: "3500m"
    memory: 6857952Ki
    pods: "110"
  capacity:
    cpu: "4"
    memory: 8010948Ki
    pods: "110"
...

调度程序使用 allocatable 的值来决定节点是否为 pod 调度的候选者。

22.5. 节点报告的系统资源

每个节点报告容器运行时和 kubelet 使用的系统资源。要简化配置 system-reserved,请使用节点概述 API 查看节点的资源使用情况。节点摘要位于 <master>/api/v1/nodes/<node>/proxy/stats/summary

例如,要从 cluster.node22 节点访问资源,请运行以下命令:

$ curl <certificate details> https://<master>/api/v1/nodes/cluster.node22/proxy/stats/summary

响应中包括类似如下的信息:

{
    "node": {
        "nodeName": "cluster.node22",
        "systemContainers": [
            {
                "cpu": {
                    "usageCoreNanoSeconds": 929684480915,
                    "usageNanoCores": 190998084
                },
                "memory": {
                    "rssBytes": 176726016,
                    "usageBytes": 1397895168,
                    "workingSetBytes": 1050509312
                },
                "name": "kubelet"
            },
            {
                "cpu": {
                    "usageCoreNanoSeconds": 128521955903,
                    "usageNanoCores": 5928600
                },
                "memory": {
                    "rssBytes": 35958784,
                    "usageBytes": 129671168,
                    "workingSetBytes": 102416384
                },
                "name": "runtime"
            }
        ]
    }
}

有关证书详情的详情,请参阅 REST API 概述

22.6. 节点强制

节点可以根据配置的可分配值限制 pod 可消耗的资源总量。此功能可以防止 pod 使用系统服务(如容器运行时和节点代理)所需的 CPU 和内存资源,从而显著提高节点可靠性。为提高节点可靠性,管理员应该根据目标保留资源使用。

节点使用一个新的 cgroup 分级结构来强制实施资源限制,它强制实现服务质量。所有 pod 都在专用的 cgroup 层次结构中启动,与系统守护进程隔离。

要配置节点强制,请在适当的节点配置映射中使用以下参数

例 22.2. 节点 Cgroup 设置

kubeletArguments:
  cgroups-per-qos:
    - "true" 1
  cgroup-driver:
    - "systemd" 2
  enforce-node-allocatable:
    - "pods" 3
1
为每个服务质量启用或禁用 cgroup 层次结构。cgroups 由节点管理。任何更改此设置都需要完全排空节点。这个标记必须是 true 才能使节点强制实施 node-allocatable 资源限制。默认值为 true,红帽不建议客户更改这个值。
2
节点用于管理 cgroup 层次结构的 cgroup 驱动程序。此值必须与与容器运行时关联的驱动程序匹配。有效值是 systemdcgroupfs,但红帽支持 systemd
3
以逗号分隔的范围列表,用于节点应强制执行节点资源限制的范围。默认值为 pods,红帽支持 pods

管理员应该像对待具有保证服务质量的 pod 一样对待系统守护进程。系统守护进程可能会在其限定控制组中爆发,此行为需要作为集群部署的一个部分进行管理。通过在 system-reserved 中指定资源为系统守护进程保留 CPU 和内存资源,如为分配资源配置节点部分所示。

要查看设置的 cgroup 驱动程序,请运行以下命令:

$ systemctl status atomic-openshift-node -l | grep cgroup-driver=

输出包括类似如下的响应:

--cgroup-driver=systemd

有关管理和故障排除 cgroup 驱动程序的更多信息,请参阅控制组(Cgroups)简介

22.7. 驱除阈值

如果某个节点面临内存压力,这可能会影响整个节点以及该节点上运行的所有 pod。如果系统守护进程使用的内存超过保留的内存量,则可能会出现内存不足事件,从而影响整个节点和节点上运行的所有 pod。为避免系统内存不足事件或降低内存不足事件的可能性,节点提供资源不足处理

第 23 章 过量使用

23.1. 概述

容器可以指定计算资源请求和限值。请求用于调度容器,以提供最低服务保证。限制约束节点上可以消耗的计算资源数量。

调度程序会尝试优化集群中所有节点的计算资源使用。它将 pod 放置到特定的节点上,同时考虑 pod 的计算资源请求和节点的可用容量。

请求和限制使管理员能够允许和管理节点上资源的过量使用,这在可以接受以保证性能换取容量的开发环境中是理想的。

23.2. 请求和限制

对于每个计算资源,容器可以指定一个资源请求和限制。根据确保节点有足够可用容量以满足请求值的请求来做出调度决策。如果容器指定了限制,但忽略了请求,则请求会默认采用这些限制。容器无法超过节点上指定的限制。

限制的强制实施取决于计算资源类型。如果容器没有请求或限制,容器会调度到没有资源保障的节点。在实践中,容器可以在最低本地优先级适用的范围内消耗指定的资源。在资源较少的情况下,不指定资源请求的容器将获得最低的服务质量。

23.2.1. 调整缓冲区块限制

如果 Fluentd 日志记录器无法跟上大量日志,则需要切换到文件缓冲来降低内存用量并防止数据丢失。

Fluentd buffer_chunk_limit 由环境变量 BUFFER_SIZE_LIMIT 决定,其默认值为 8m。每个输出的文件缓冲大小由环境变量 FILE_BUFFER_LIMIT 决定,其默认值为 256Mi。持久性卷大小必须大于 FILE_BUFFER_LIMIT 与输出乘以。

例如,在 Fluentd 和 Mux pod 上,持久性卷 /var/lib/fluentd 应该由 PVC 或 hostmount 准备。然后,将该区域用作文件缓冲区。

buffer_typebuffer_path 在 Fluentd 配置文件中配置,如下所示:

$ egrep "buffer_type|buffer_path" *.conf
output-es-config.conf:
  buffer_type file
  buffer_path `/var/lib/fluentd/buffer-output-es-config`
output-es-ops-config.conf:
  buffer_type file
  buffer_path `/var/lib/fluentd/buffer-output-es-ops-config`
filter-pre-mux-client.conf:
  buffer_type file
  buffer_path `/var/lib/fluentd/buffer-mux-client`

Fluentd buffer_queue_limit 是变量 BUFFER_QUEUE_LIMIT 的值。这个值默认为 32

环境变量 BUFFER_QUEUE_LIMIT 计算为 (FILE_BUFFER_LIMIT / (number_of_outputs * BUFFER_SIZE_LIMIT))

如果 BUFFER_QUEUE_LIMIT 变量具有默认值:

  • FILE_BUFFER_LIMIT = 256Mi
  • number_of_outputs = 1
  • BUFFER_SIZE_LIMIT = 8Mi

buffer_queue_limit 的值为 32。要更改 buffer_queue_limit,您需要更改 FILE_BUFFER_LIMIT 的值。

在这个公式中,如果所有日志都发送到单个资源,number_of_outputs1,它会为每个额外资源递增 1。例如,number_of_outputs 的值为:

  • 1 - 如果所有日志都发送到单个 ElasticSearch pod
  • 2 - 如果应用程序日志发送到 ElasticSearch pod,并且 ops 日志发送到另一个 ElasticSearch pod
  • 4 - 如果应用程序日志发送到 ElasticSearch pod,ops 日志发送到另一个 ElasticSearch pod,并且这两者都转发到其他 Fluentd 实例

23.3. 计算资源

计算资源的节点强制行为特定于资源类型。

23.3.1. CPU

容器可以保证获得其请求的 CPU 量,还可额外消耗节点上提供的超额 CPU,但不会超过容器指定的限制。如果多个容器试图使用超额 CPU,则会根据每个容器请求的 CPU 数量来分配 CPU 时间。

例如,如果一个容器请求了 500m CPU 时间,另一个容器请求了 250m CPU 时间,那么该节点上提供的额外 CPU 时间以 2:1 比例在这两个容器之间分配。如果容器指定了一个限制,它将被限速,无法使用超过指定限制的 CPU。

使用 Linux 内核中的 CFS 共享支持强制实施 CPU 请求。默认情况下,使用 Linux 内核中的 CFS 配额支持以 100ms 测量间隔强制实施 CPU 限制,但这可以禁用

23.3.2. 内存

容器可以保证获得其请求的内存量。容器可以使用高于请求量的内存,但一旦超过请求量,就有可能在节点上遇到内存不足情形时被终止。

如果容器使用的内存少于请求量,它不会被终止,除非系统任务或守护进程需要的内存量超过了节点资源保留考虑在内的内存量。如果容器指定了内存限制,则超过限制数量时会立即被终止。

23.3.3. 临时存储

注意

只有在启用了临时存储技术预览功能时才适用。此功能默认为禁用。如果启用,OpenShift Container Platform 集群将使用临时存储来存储在集群销毁后不需要保留的信息。要启用此功能,请参阅配置临时存储

容器可以保证请求的临时存储量。容器可以使用比请求量更多的临时存储,但一旦超过请求量,则可在可用的临时磁盘空间过低时将它终止。

如果容器使用的临时存储少于请求,它不会被终止,除非系统任务或守护进程需要比节点资源保留中考虑更多的本地临时存储。如果容器指定了临时存储的限制,则超过限制数量时会立即被终止。

23.4. 服务质量类

当节点上调度了没有发出请求的 pod,或者节点上所有 pod 的限制总和超过了机器可用容量时,该节点处于过量使用状态。

在过量使用环境中,节点上的 pod 可能会在任意给定时间点尝试使用超过可用量的计算资源。发生这种情况时,节点必须为 pod 赋予不同的优先级。有助于做出此决策的工具称为服务质量 (QoS) 类。

对于每个计算资源,容器划分到三个 QoS 类中的一个,它们按照优先级降序排列:

表 23.1. 服务质量类

优先级类名称描述

1(最高)

Guaranteed

如果为所有资源设置了限制和可选请求(不等于 0)并且它们相等,则容器被归类为 Guaranteed

2

Burstable

如果为所有资源设置了请求和可选限制(不等于 0)并且它们不相等,则容器被归类为 Burstable

3(最低)

BestEffort

如果没有为任何资源设置请求和限制,则容器被归类为 BestEffort

内存是一种不可压缩的资源,因此在内存量较低的情况下,优先级最低的容器首先被终止:

  • Guaranteed 容器优先级最高,并且保证只有在它们超过限制或者系统遇到内存压力且没有优先级更低的容器可被驱除时,才会被终止。
  • 在遇到系统内存压力时,Burstable 容器如果超过其请求量并且不存在其他 BestEffort 容器,则有较大的可能会被终止。
  • BestEffort 容器被视为优先级最低。系统内存不足时,这些容器中的进程最先被终止。

23.5. 为使用过量配置 Master

调度基于请求的资源,而配额和硬限制指的是资源限制,它们可以设置为高于请求的资源。请求和限制的差值决定了过量使用程度;例如,如果为容器赋予 1Gi 内存请求和 2Gi 内存限制,则根据 1Gi 请求将容器调度到节点上,但最多可使用 2Gi;因此过量使用为 200%。

如果 OpenShift Container Platform 管理员希望控制过量使用的程度,并管理节点上的容器密度,可以将主控机配置为覆盖开发人员容器上设置的请求和限制之间的比率。与项目各项目的 LimitRange 指定限制和默认值结合使用时,这会调整容器限制和请求,以达到所需的过量使用程度。

这需要在master-config.yaml 中配置 ClusterResourceOverride 准入控制器(如下例所示)(如果存在,则使用现有配置树,或者根据需要引进缺少的元素):

  admissionConfig:
    pluginConfig:
      ClusterResourceOverride: 1
        configuration:
          apiVersion: v1
          kind: ClusterResourceOverrideConfig
          memoryRequestToLimitPercent: 25 2
          cpuRequestToLimitPercent: 25 3
          limitCPUToMemoryPercent: 200 4
1
这是插件名称,忽略与插件名称完全匹配的情况和任何内容。
2
(可选,1-100)如果指定或默认指定了容器内存限值,则内存请求将被覆盖到限制的这个百分比。
3
(可选,1-100)如果指定或默认指定了容器 CPU 限值,则 CPU 请求将被覆盖到限制的这个百分比。
4
(可选,正整数)如果指定或默认指定了容器内存限值,则 CPU 限值被覆盖为内存限值的百分比,扩展 1Gi 的 RAM 等于 1 个 CPU 内核。这是在覆盖 CPU 请求前处理的(如果已配置)。

更改 master 配置后,需要重启 master。

请注意,如果没有在容器上设置限制,则这些覆盖无效。 创建一个带有默认限制(每个独立的项目或在项目模板中)的 LimitRange 对象,以确保应用覆盖。

另请注意,覆盖后,容器限值和请求必须仍需要由项目中的 LimitRange 对象验证。这可能会导致 pod 被禁止的情况。例如,开发人员指定了一个接近最小限制的限制,然后其请求被覆盖为低于最小限制。这个问题在以后会加以解决,但目前而言,请小心地配置此功能和 LimitRange。

配置后,可通过编辑项目并添加以下注解来禁用每个项目覆盖(例如,允许独立于覆盖配置基础架构组件):

quota.openshift.io/cluster-resource-override-enabled: "false"

23.6. 为过量使用配置节点

在过量使用的环境中,务必要正确配置节点,以提供最佳的系统行为。

23.6.1. 保留内存等级的服务 Tiers

您可以使用 experimental-qos-reserved 参数指定在特定 QoS 级别上 pod 保留的内存百分比。此功能尝试保留请求的资源,阻止较低 QoS 类中的 pod 使用较高 QoS 类中 pod 所请求的资源。

通过将资源保留为更高的 QOS 级别,没有资源限制的 pod 可以防止在较高 QoS 级别上对 pod 请求的资源进行隔离。

要配置 experimental-qos-reserved 参数,请编辑适当的节点配置映射

kubeletArguments:
  cgroups-per-qos:
  - true
  cgroup-driver:
  - 'systemd'
  cgroup-root:
  - '/'
  experimental-qos-reserved: 1
  - 'memory=50%'
1
指定如何在 QoS 级别上保留 pod 资源请求。

OpenShift Container Platform 使用 experimental-qos-reserved 参数,如下所示:

  • experimental-qos-reserved=memory=100% 将阻止 BurstableBestEffort QOS 类消耗较高 QoS 类请求的内存。这会增加 BestEffortBurstable 工作负载中 OOM 的风险,而是增加 GuaranteedBurstable 工作负载的内存资源保障。
  • experimental-qos-reserved=memory=50% 将允许 BurstableBestEffort QOS 类消耗较高 QoS 类请求的内存的一半。
  • experimental-qos-reserved=memory=0% 值将允许 BurstableBestEffort QoS 类最多消耗完整节点可分配量(如果可用),但会增加 Guaranteed 工作负载无法访问请求的内存的风险。此条件等同于禁用这项功能。

23.6.2. 强制 CPU 限制

默认情况下,节点使用 Linux 内核中的 CPU CFS 配额支持来强制实施指定的 CPU 限值。如果您不想在该节点上强制实施 CPU 限制,可以通过修改适当的节点配置映射来禁用其强制功能,使其包含以下参数:

kubeletArguments:
  cpu-cfs-quota:
    - "false"

如果需要禁用 CPU 限制强制实施,则务必要了解其对您的节点可能造成的影响:

  • 如果容器发出 CPU 请求,它将继续由 Linux 内核中的 CFS 份额来实施。
  • 如果容器没有明确发出 CPU 请求,但它确实指定了限制,则请求将默认为指定的限制,并且由 Linux 内核中的 CFS 份额来实施。
  • 如果容器同时指定 CPU 请求和限制,则由 Linux 内核中的 CFS 份额来实施请求,并且限制不会对节点产生影响。

23.6.3. 为系统进程保留资源

调度程序确保根据 pod 请求,节点上所有 pod 有充足的资源。它验证节点上的容器请求总和是否大于节点容量。它包含由节点启动的所有容器,但不包含在集群知识之外启动的容器或进程。

建议您保留节点容量的某些部分,以便让集群正常工作所需的系统守护进程(sshd、docker )。特别是,建议您为内存等不可压缩的资源保留资源。

如果要为非 pod 进程显式保留资源,可以通过两种方式这样做:

  • 首选方法是通过指定可用于调度的资源来分配节点资源。如需了解更多详细信息,请参阅分配节点资源
  • 或者,您可以创建一个资源reserver pod,它只是保留集群在节点上调度的容量。例如:

    例 23.1. resource-reserver Pod Definition

    apiVersion: v1
    kind: Pod
    metadata:
      name: resource-reserver
    spec:
      containers:
      - name: sleep-forever
        image: gcr.io/google_containers/pause:0.8.0
        resources:
          limits:
            cpu: 100m 1
            memory: 150Mi 2
    1
    集群未知的主机级守护进程在节点上保留的 CPU 数量。
    2
    集群未知的主机级守护进程在节点上保留的内存量。

    您可以将定义保存到文件中,如resource-reserver.yaml ,然后将文件放在节点配置目录中,如/etc/origin/node/--config=<dir> 位置(如果另有指定)。

    另外,通过在适当的节点配置映射的 kubeletArguments.config 参数中指定目录,将节点服务器配置为从节点配置目录中读取定义

    kubeletArguments:
      config:
        - "/etc/origin/node" 1
    1
    如果指定了 --config=<dir>,请在此处使用 <dir>

    resource-reserver.yaml 文件就位后,启动节点服务器也会启动 sleep-forever 容器。调度程序考虑节点的剩余容量,并调整相应地放置集群 pod 的位置。

    要删除 resource-reserver pod,您可以从节点配置目录中删除或移动resource-reserver.yaml 文件。

23.6.4. 内核可调项

当节点启动时,它会确保为内存管理正确设置内核可微调标识。除非物理内存不足,否则内核应该永不会在内存分配时失败。

为确保此行为,节点指示内核始终过量使用内存:

$ sysctl -w vm.overcommit_memory=1

节点还指示内核在内存不足时不会崩溃。相反,内核 OOM 终止程序应该根据优先级终止进程:

$ sysctl -w vm.panic_on_oom=0
注意

节点上应该已设置了上述标记,不需要进一步操作。

23.6.5. 禁用交换内存

自 OpenShift 容器平台 3.9 起,交换作为 Ansible 节点安装的一部分被禁用。不再支持启用交换,但对于将来的版本正在评估对 swap 的适当支持。

重要

在启用交换的情况下运行 会产生意外的后果。如果启用了交换,则任何可用资源处理驱除阈值之外的可用内存将无法正常工作。利用资源处理,允许在面临内存压力时从节点驱除 pod,并在没有此类压力的替代节点上重新调度 pod。

第 24 章 处理资源错误

24.1. 概述

本主题讨论了如何防止 OpenShift Container Platform 遇到内存不足(OOM)和磁盘空间不足状况的最佳尝试。

当可用计算资源较低时,节点必须保持稳定性。这在处理内存或磁盘等不可压缩的资源时尤为重要。如果两个资源都耗尽,则节点将变得不稳定。

管理员可以使用可配置驱除策略主动监控节点,并防止节点耗尽计算和内存资源的情况。

本节还提供有关 OpenShift Container Platform 如何处理资源不足状况的信息,并提供示例场景和 推荐做法

警告

如果为节点启用了交换内存,则该节点无法检测到它处于内存压力下。

要利用基于内存的驱除,操作员必须禁用交换

24.2. 配置驱除策略

当节点在可用资源上运行不足时,驱除策略允许节点失败一个或多个 pod。如果 pod 失败,节点可以回收所需的资源。

驱除策略是驱除触发器信号的组合 ,该信号具有节点配置文件中设置的特定驱除阈值或通过 命令行设置驱除可能比较困难,其中节点对超过阈值的 pod 立即执行操作或软,即节点允许在采取行动前宽限期执行操作。

注意

要修改集群中的节点,请根据需要更新节点配置映射。不要手动编辑 node-config.yaml 文件。

通过使用配置良好的驱除策略,节点可以主动监控和防止计算资源的总消耗。

注意

当节点出现故障时,节点会终止 pod 中的所有容器,PodPhase 转换为 Failed

检测磁盘压力时,节点支持 nodefsimagefs 文件系统分区。

nodefs 或者 rootfs 是节点用于本地磁盘卷、守护进程日志、emptyDir 和其他本地存储的文件系统。例如: rootfs 是提供/ 的文件系统。rootfs 包含 openshift.local.volumes,默认为/var/lib/origin/openshift.local.volumes

imagefs 是容器运行时用来存储镜像和单个容器可写层的文件系统。在 imagefs 中,驱除阈值已满 85%。imagefs 文件系统取决于运行时,对于 Docker,容器使用哪个存储驱动程序。

  • 对于 Docker:

    • 如果您使用 devicemapper 存储驱动程序,imagefs 是精简池。

      您可以通过在 Docker 守护进程中设置 --storage-opt dm.basesize 标志来限制容器的读取和写入层。

      $ sudo dockerd --storage-opt dm.basesize=50G
    • 如果您使用 overlay2 存储驱动程序,imagefs 是包含/var/lib/docker/overlay2 的文件系统。
  • 对于使用覆盖驱动程序的 CRI-O,imagefs 默认是/var/lib/containers/storage
注意

如果不使用本地存储隔离(临时存储),且不使用 XFS 配额(volumeConfig),则无法限制 pod 的本地磁盘用量。

24.2.1. 使用节点配置创建策略

要配置驱除策略,请编辑适当的节点配置映射在 eviction-hardeviction-soft 参数下指定驱除阈值。

以下示例显示驱除阈值:

硬驱除的节点配置文件示例

kubeletArguments:
  eviction-hard: 1
  - memory.available<100Mi 2
  - nodefs.available<10%
  - nodefs.inodesFree<5%
  - imagefs.available<15%
  - imagefs.inodesFree<10%

1
2
基于特定驱除触发信号的驱除阈值。
注意

您必须为 inodesFree 参数提供百分比值。您可以为其他参数提供百分比或数值。

Soft Eviction 的节点配置文件示例

kubeletArguments:
  eviction-soft: 1
  - memory.available<100Mi 2
  - nodefs.available<10%
  - nodefs.inodesFree<5%
  - imagefs.available<15%
  - imagefs.inodesFree<10%
  eviction-soft-grace-period:3
  - memory.available=1m30s
  - nodefs.available=1m30s
  - nodefs.inodesFree=1m30s
  - imagefs.available=1m30s
  - imagefs.inodesFree=1m30s

1
2
基于特定驱除触发信号的驱除阈值。
3
软驱除的宽限期。将默认值保留为最佳性能。

重启 OpenShift Container Platform 服务以使更改生效:

# systemctl restart atomic-openshift-node

24.2.2. 了解驱除信号

您可以配置节点,以对下表中描述的任何信号触发驱除决定。您可以向驱除阈值和阈值添加驱除信号

查看信号:

curl <certificate details> \
  https://<master>/api/v1/nodes/<node>/proxy/stats/summary

表 24.1. 支持的驱除信号

节点条件驱除信号描述

MemoryPressure

memory.available

memory.available = node.status.capacity[memory] - node.stats.memory.workingSet

节点上的可用内存已超过驱除阈值。

DiskPressure

nodefs.available

nodefs.available = node.stats.fs.available

节点根文件系统或镜像文件系统上的可用磁盘空间已超过驱除阈值。

nodefs.inodesFree

nodefs.inodesFree = node.stats.fs.inodesFree

imagefs.available

imagefs.available = node.stats.runtime.imagefs.available

imagefs.inodesFree

imagefs.inodesFree = node.stats.runtime.imagefs.inodesFree

上表中的每一信号支持字面值或基于百分比的值,但 inodesFree 除外。inodesFree 信号必须指定为百分比。基于百分比的值是相对于与每个信号关联的总容量计算的。

脚本使用 kubelet 执行相同的步骤集从您的 cgroup 驱动程序派生 memory.available 的值。脚本在计算中排除非活动文件内存(即非活动 LRU 列表中文件支持的内存数),因为它假定不活跃文件内存在压力下可以回收。

注意

不要使用 free -m 等工具,因为 free -m 不在容器中工作。

OpenShift Container Platform 每 10 秒监控一次这些文件系统。

如果您将卷和日志存储在专用文件系统中,则节点不会监控该文件系统。

注意

节点支持根据磁盘压力触发驱除决策的能力。在因为磁盘压力而驱除 pod 前,节点还会执行容器和镜像垃圾回收

24.2.3. 了解驱除阈值

您可以配置节点来指定驱除阈值。达到阈值会触发节点回收资源。您可以在节点配置文件中配置阈值

如果满足驱除阈值,且与其关联的宽限期无关,节点会报告一个条件来指示节点处于内存或磁盘压力下。报告压力会阻止调度程序在尝试回收资源时调度节点上的任何其他 pod。

节点继续按照 node-status-update-frequency 参数指定的频率报告节点状态更新。默认频率为 10s (十秒)。

驱除阈值可能比较困难 ,因为当您允许回收资源前宽限期时,节点会在达到阈值时立即采取行动,或者是软的

注意

当您以某一级别使用量为目标时,软驱除使用更为常见,但可以容忍临时激增。我们建议设置软驱除阈值低于硬驱除阈值,但时间周期可以是特定于操作员的。系统保留还应涵盖软驱除阈值。

软驱除阈值是一个高级功能。在尝试使用软驱除阈值前,您应该配置硬驱除阈值。

阈值配置有以下形式:

<eviction_signal><operator><quantity>

例如,如果操作器有一个具有 10Gi 内存的节点,如果可用内存低于 1Gi,则该操作员希望驱除驱除阈值,内存的驱除阈值可以指定为以下之一:

memory.available<1Gi
memory.available<10%
注意

节点每隔 10 秒评估并监控驱除阈值,且无法修改值。这是内务间隔。

24.2.3.1. 了解硬驱除阈值

硬驱除阈值没有宽限期。达到硬驱除阈值时,节点会立即采取措施回收关联的资源。例如,节点可以立即终止一个或多个 pod,且没有安全终止。

要配置硬驱除阈值,请在 eviction-hard 下的节点配置文件中添加驱除阈值 ,如使用 Node Configuration 创建策略所示

使用硬驱除阈值的示例节点配置文件

kubeletArguments:
  eviction-hard:
  - memory.available<500Mi
  - nodefs.available<500Mi
  - nodefs.inodesFree<5%
  - imagefs.available<100Mi
  - imagefs.inodesFree<10%

这个示例是一个常规准则,不推荐设置。

24.2.3.1.1. 默认 hardd Eviction Thresholds

OpenShift Container Platform 为 eviction-hard 使用以下默认配置。

...
kubeletArguments:
  eviction-hard:
  - memory.available<100Mi
  - nodefs.available<10%
  - nodefs.inodesFree<5%
  - imagefs.available<15%
...

24.2.3.2. 了解 Soft Eviction Thresholds

软驱除阈值将驱除阈值与管理员指定的必要宽限期配对。在超过这个宽限期前,节点不会回收与驱除信号关联的资源。如果在节点配置中未提供宽限期,则该节点会在系统启动时生成错误。

另外,如果满足软驱除阈值,Operator 可以指定一个允许的最大 pod 终止宽限期,以便在从节点驱除 pod 时使用。如果指定了 eviction-max-pod-grace-period,节点会在 pod.Spec.TerminationGracePeriodSeconds 和最大允许宽限期中使用 lesser 值。如果没有指定,节点会立即终止 pod,且没有安全终止。

对于软驱除阈值,支持以下标记:

  • eviction-soft: 一组驱除阈值,如 memory.available<1.5Gi。如果达到对应的宽限期,阈值会触发 pod 驱除。
  • eviction-soft-grace-period: 一组驱除宽限期,如 memory.available=1m30s。宽限期与在触发 pod 驱除前必须保留软驱除阈值的时长对应。
  • eviction-max-pod-grace-period:终止 pod 时所用最大允许宽限期(以秒为单位),以响应满足软驱除阈值。

要配置软驱除阈值,请在 eviction-soft 下的节点配置文件中添加驱除阈值 ,如使用 Node Configuration 创建策略所示

使用 Soft Eviction Thresholds 的节点配置文件示例

kubeletArguments:
  eviction-soft:
  - memory.available<500Mi
  - nodefs.available<500Mi
  - nodefs.inodesFree<5%
  - imagefs.available<100Mi
  - imagefs.inodesFree<10%
  eviction-soft-grace-period:
  - memory.available=1m30s
  - nodefs.available=1m30s
  - nodefs.inodesFree=1m30s
  - imagefs.available=1m30s
  - imagefs.inodesFree=1m30s

这个示例是一个常规准则,不推荐设置。

24.3. 为调度配置资源量

您可以控制提供多少节点资源可用于调度,以便调度程序能够完全分配节点并防止驱除。

system-reserved 设置为您要用于部署 pod 以及 system-daemons 的资源量。system-reserved 资源为操作系统守护进程保留,如 sshdNetworkManager。只有在 pod 使用超过其可分配的资源量时,才应执行驱除。

节点报告两个值:

  • Capacity:计算机上的资源量。
  • Allocatable:提供用于调度的资源量。

要配置可分配的资源量,请编辑适当的节点配置映射来为 eviction-hardeviction-soft 添加或修改 system-reserved 参数。

kubeletArguments:
  eviction-hard: 1
    - "memory.available<500Mi"
  system-reserved:
    - "memory=1.5Gi"
1
这个阈值可以是 eviction-hardeviction-soft

要确定 system-reserved 设置的适当值,请使用节点概述 API 确定节点的资源使用情况。如需更多信息,请参阅为分配的资源配置节点

重启 OpenShift Container Platform 服务以使更改生效:

# systemctl restart atomic-openshift-node

24.4. 控制节点条件 Oscillation

如果节点在软驱除阈值之上或低于软驱除阈值,但没有超过关联的宽限期,oscillation 可能会导致调度程序出现问题。

要防止 oscillation,请设置 eviction-pressure-transition-period 参数来控制节点在摆脱压力状况前必须等待的时间。

  1. 使用一组 <resource_type>=<resource_quantity> 对编辑或将该参数添加到相应节点配置映射的 kubeletArguments 部分。

    kubeletArguments:
      eviction-pressure-transition-period="5m"

    当节点在指定期间没有达到指定压力状况的驱除阈值时,节点会将状况切换回 false。

    注意

    在进行任何调整前,请使用默认值 5 分钟。默认值旨在使系统稳定,并防止调度程序在节点发生前将新 pod 调度到该节点。

  2. 重启 OpenShift Container Platform 服务以使更改生效:

    # systemctl restart atomic-openshift-node

24.5. 重新声明节点级别资源

如果满足驱除标准,节点将启动回收压力资源的进程,直至信号低于定义的阈值。在这段时间中,节点不支持调度任何新 pod。

节点在节点驱除最终用户 pod 前尝试回收节点级别资源,具体取决于主机系统是否为容器运行时配置了专用 imagefs

使用 Imagefs

如果主机系统有 imagefs:

  • 如果 nodefs 文件系统满足驱除阈值,节点会按照以下顺序释放磁盘空间:

    • 删除死机容器集和容器。
  • 如果 imagefs 文件系统满足驱除阈值,节点会按照以下顺序释放磁盘空间:

    • 删除所有未使用的镜像。
没有 Imagefs

如果主机系统没有 imagefs:

  • 如果 nodefs 文件系统满足驱除阈值,节点会按照以下顺序释放磁盘空间:

    • 删除死机容器集和容器。
    • 删除所有未使用的镜像。

24.6. 了解 Pod 驱除

如果满足驱除阈值并传递宽限期,节点将启动驱除 pod 的进程,直到信号低于定义的阈值。

节点根据服务质量排列要驱除的 pod。在具有相同服务质量的 pod 中,节点根据相对于容器集调度请求的计算资源的消耗对 pod 进行排名。

每个服务质量级别都有内存不足分数。Linux 内存不足工具(OOM 终止程序)使用分数来确定要结束哪些 pod。如需更多信息,请参阅了解服务质量和内存不足 Killer

下表列出了每个服务质量级别和相关内存不足分数。

表 24.2. 服务质量等级

服务质量描述

Guaranteed

相对于其请求的资源最多消耗的 Pod 会首先失败。如果没有 pod 超过其请求,策略会以资源的最大使用者为目标。

Burstable

相对于资源请求最多消耗资源的 Pod 会首先失败。如果没有 pod 超过其请求,策略会以资源的最大使用者为目标。

BestEffort

首先消耗最多资源的 Pod 会失败。

确保服务质量 pod 不会因为另一个 pod 的资源消耗而被驱除,除非系统守护进程(如节点或容器引擎)消耗的资源多于使用 system-reserved 分配的资源,或者节点只保证服务质量 pod 保留。

如果节点只保证保留的服务 pod 质量,则节点会驱除对节点稳定性影响最小的 pod,并将意外消耗的影响限制为服务 pod 的其他保证质量。

本地磁盘是最佳服务质量资源。如果需要,节点会一次驱除 pod,以在遇到磁盘压力时重新声明磁盘空间。节点根据服务质量对 pod 进行排名。如果节点响应缺少自由内节点,则节点会首先驱除服务质量最低的 pod 来回收内节点。如果节点响应缺少可用磁盘,节点会将 pod 排在消耗最大本地磁盘量的服务质量内,然后首先驱除这些 pod。

24.6.1. 了解服务质量和内存不足 Killer

如果节点在可以回收内存前遇到系统内存不足(OOM)事件,则该节点会依赖于 OOM 终止程序响应。

节点根据 pod 的服务质量为每个容器设置 oom_score_adj 值。

表 24.3. 服务质量等级

服务质量oom_score_adj value

Guaranteed

-998

Burstable

min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

BestEffort

1000

如果节点在节点遇到系统 OOM 事件前无法回收内存,OOM 终止程序进程会计算 OOM 分数:

% of node memory a container is using + oom_score_adj = oom_score

然后,节点以最高分数结束容器。

与调度请求相比,服务质量最低且消耗最大内存的容器首先被终止。

与 pod 驱除不同,如果 pod 容器因 OOM 终止,节点可以根据节点重启策略重启容器。

24.7. 了解 Pod 调度程序和 OOR 条件

当调度程序在节点上放置其他 pod 时,调度程序查看节点状况。例如,如果节点具有如下驱除阈值:

eviction-hard is "memory.available<500Mi"

如果可用内存低于 500Mi,节点会在 Node.Status.Conditions 中报告一个值为 true。MemoryPressure

表 24.4. 节点条件和调度程序行为

节点条件调度程序行为

MemoryPressure

如果节点报告了这个状况,调度程序不会将 BestEffort pod 放置到那个节点上。

DiskPressure

如果节点报告了此状况,调度程序不会将任何其他 pod 放置到该节点上。

24.8. 场景示例

一个 Operator:

  • 内存容量为 10Gi 的节点。
  • 希望为系统守护进程(如内核、节点和其他守护进程)保留 10% 的内存容量。
  • 希望以 95% 的内存利用率驱除 pod,以减少系统 OOM 的压力和发生。

这个配置中隐式的是,system-reserved 应该包含驱除阈值所覆盖的内存量。

要达到该容量,某些 pod 使用的不仅仅是其请求,或者系统使用了超过 1Gi。

如果节点具有 10 Gi 的容量,而您想要使用 system-reserved 设置为系统守护进程保留 10% 的容量,请执行以下计算:

capacity = 10 Gi
system-reserved = 10 Gi * .1 = 1 Gi

可分配的资源数量变为:

allocatable = capacity - system-reserved = 9 Gi

这意味着,调度程序将请求 9Gi 内存的 pod 调度到该节点。

如果要启用驱除,以便在节点发现可用内存不足 30 秒时或立即达到 5% 容量时触发驱除,您需要调度程序将可分配内存评估为 8Gi。因此,确保您的系统保留涵盖您的驱除阈值更大。

capacity = 10 Gi
eviction-threshold = 10 Gi * .1 = 1 Gi
system-reserved = (10Gi * .1) + eviction-threshold = 2 Gi
allocatable = capacity - system-reserved = 8 Gi

在适当的节点配置映射中添加以下内容

kubeletArguments:
  system-reserved:
  - "memory=2Gi"
  eviction-hard:
  - "memory.available<.5Gi"
  eviction-soft:
  - "memory.available<1Gi"
  eviction-soft-grace-period:
  - "memory.available=30s"

此配置可确保调度程序不会将 pod 放置到节点上,并立即降低内存压力并触发驱除。此配置假定这些容器集使用的使用量少于其配置的请求。

第 25 章 设置限值范围

25.1. Limit Ranges 目的

限值范围由 LimitRange 对象定义,枚举 pod 、容器、镜像、镜像流和持久性卷声明级别的项目中的计算资源约束,并指定 pod、容器、镜像、镜像流或持久性卷声明可以使用的资源量。

要创建和修改资源的所有请求都会针对项目中的每个 LimitRange 对象进行评估。如果资源违反了任何限制,则会拒绝该资源。如果资源没有设置显式值,如果约束支持默认值,则默认值将应用到该资源。

对于 CPU 和内存限值,如果指定了最大值但没有指定最小限制,则资源可能会消耗比最大值更多的 CPU 和内存资源。

您可以使用临时存储技术预览功能为临时存储指定限值和请求。此功能默认为禁用。要启用此功能,请参阅配置临时存储

核心限制对象定义

apiVersion: "v1"
kind: "LimitRange"
metadata:
  name: "core-resource-limits" 1
spec:
  limits:
    - type: "Pod"
      max:
        cpu: "2" 2
        memory: "1Gi" 3
      min:
        cpu: "200m" 4
        memory: "6Mi" 5
    - type: "Container"
      max:
        cpu: "2" 6
        memory: "1Gi" 7
      min:
        cpu: "100m" 8
        memory: "4Mi" 9
      default:
        cpu: "300m" 10
        memory: "200Mi" 11
      defaultRequest:
        cpu: "200m" 12
        memory: "100Mi" 13
      maxLimitRequestRatio:
        cpu: "10" 14

1
限制范围对象的名称。
2
pod 可在所有容器中的节点上请求的最大 CPU 量。
3
pod 可在所有容器中的节点上请求的最大内存量。
4
pod 可在所有容器中的节点上请求的最小 CPU 量。如果您没有设置 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的消耗可能会超过 max CPU 值。
5
pod 可在所有容器中的节点上请求的最小内存量。如果您没有设置 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的消耗可能超过 max 内存值。
6
pod 中单个容器可以请求的最大 CPU 量。
7
pod 中单个容器可以请求的最大内存量。
8
pod 中单个容器可以请求的最小 CPU 量。如果您没有设置 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的消耗可能会超过 max CPU 值。
9
pod 中单个容器可以请求的最小内存量。如果您没有设置 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的消耗可能超过 max 内存值。
10
如果没有在 pod 规格中指定限制,则容器的默认 CPU 限值。
11
如果没有在 pod 规格中指定限制,容器的默认内存限值。
12
如果没有在 pod 规格中指定请求,容器的默认 CPU 请求。
13
如果没有在 pod 规格中指定请求,容器的默认内存请求。
14
容器最大的限制与请求的比率。

有关如何测量 CPU 和内存的更多信息,请参阅计算资源

OpenShift Container Platform Limit Range 对象定义

apiVersion: "v1"
kind: "LimitRange"
metadata:
  name: "openshift-resource-limits"
spec:
  limits:
    - type: openshift.io/Image
      max:
        storage: 1Gi 1
    - type: openshift.io/ImageStream
      max:
        openshift.io/image-tags: 20 2
        openshift.io/images: 30 3
    - type: "Pod"
      max:
        cpu: "2" 4
        memory: "1Gi" 5
        ephemeral-storage: "1Gi" 6
     max:
        cpu: "1" 7
        memory: "1Gi" 8

1
可以推送到内部 registry 的最大镜像大小。
2
镜像流规格中定义的唯一镜像标签的最大数量。
3
镜像流状态规格中定义的最大镜像引用数量。
4
pod 可在所有容器中的节点上请求的最大 CPU 量。
5
pod 可在所有容器中的节点上请求的最大内存量。
6
如果启用了临时存储技术预览功能,pod 可在所有容器间请求的最大临时存储量。
7
pod 可在所有容器中的节点上请求的最小 CPU 量。如果您设置了 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的消耗可能会超过 max CPU 值。
8
pod 可在所有容器中的节点上请求的最小内存量。如果您没有设置 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的消耗可能会超过 max 内存值。

您可以在一个限制范围对象中指定核心和 OpenShift Container Platform 资源。为清晰起见,将单独在两个示例中显示这些参数。

25.1.1. 容器限制

支持的资源:

  • CPU
  • 内存

支持的限制:

对于每个容器,如果指定,则必须满足以下条件:

表 25.1. Container

约束行为

Min

Min[resource] 小于或等于 container.resources.requests[resource] (必需)小于或等于 container/resources.limits[resource] (可选)

如果配置定义了 min CPU,请求值必须大于 CPU 值。如果没有设置 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的资源量可能会超过 max 值。

Max

container.resources.limits[resource] (必需)小于或等于 Max[resource]

如果配置定义了 max CPU,则不需要定义 CPU 请求值。但是,您必须设置一个满足限制范围中指定的最大 CPU 约束的限制。

MaxLimitRequestRatio

MaxLimitRequestRatio[resource] 小于或等于(container.resources.limits[resource] / container.resources.requests[resource])

如果限制范围定义了 maxLimitRequestRatio 约束,则任何新容器都必须具有 requestlimit 值。另外,OpenShift Container Platform 通过将 limitrequest 分隔来计算限制与请求的比率。结果应该是一个大于 1 的整数。

例如,如果容器的 limit 值中具有 cpu: 500request 值中有 cpu: 100cpu 的限值与请求比例为 5。这个比例必须小于或等于 maxLimitRequestRatio

支持的默认值:

Default[resource]
如果没有,默认为 container.resources.limit[resource] 指定值。
Default Requests[resource]
如果没有,默认为 container.resources.requests[resource] 指定值。

25.1.2. Pod 限制

支持的资源:

  • CPU
  • 内存

支持的限制:

在 pod 中的所有容器中,需要满足以下条件:

表 25.2. Pod

约束强制行为

Min

Min[resource] 小于或等于 container.resources.requests[resource] (必需)小于或等于 container.resources.limits[resource]。如果没有设置 min 值,或者将 min 设置为 0,则结果没有限制,pod 消耗的资源量可能会超过 max 值。

Max

container.resources.limits[resource] (必需)小于或等于 Max[resource]

MaxLimitRequestRatio

MaxLimitRequestRatio[resource] 小于或等于(container.resources.limits[resource] / container.resources.requests[resource])。

25.1.3. 镜像限制

支持的资源:

  • 存储

资源类型名称:

  • openshift.io/Image

对于镜像,如果指定,则必须满足以下条件:

表 25.3. Image

约束行为

Max

image.dockerimagemetadata.size 小于或等于 Max[resource]

注意

要防止超过限制的 Blob 上传到 registry,必须将 registry 配置为强制实施配额。REGISTRY_MIDDLEWARE_REPOSITORY_OPENSHIFT_ENFORCEQUOTA 环境变量必须设置为 true。对于新部署,默认将环境变量设置为 true

警告

在上传的镜像清单中,镜像大小并非始终可用。这对使用 Docker 1.10 或更高版本构建并推送到 v2 registry 的镜像来说尤为如此。如果此类镜像使用较旧的 Docker 守护进程拉取,则镜像清单由 registry 转换为 schema v1,且不包括所有大小信息。镜像没有设置存储限制会阻止镜像上传。

这个问题正在被决。

25.1.4. 镜像流限制

支持的资源:

  • openshift.io/image-tags
  • openshift.io/images

资源类型名称:

  • openshift.io/ImageStream

每个镜像流,如果指定,则必须满足以下条件:

表 25.4. ImageStream

约束行为

Max[openshift.io/image-tags]

length( uniqueimagetags( imagestream.spec.tags ) ) 小于或等于 Max[openshift.io/image-tags]

uniqueimagetags 返回对给定 spec 标签的镜像的唯一引用。

Max[openshift.io/images]

length( uniqueimages( imagestream.status.tags ) ) 小于或等于 Max[openshift.io/images]

uniqueimages 返回 status 标签中找到的唯一镜像名称。名称等于镜像的摘要。

25.1.4.1. 镜像参考计数

openshift.io/image-tags 资源代表唯一镜像引用。可能的引用包括 ImageStreamTagImageStreamImageDockerImage。可以使用 oc tagoc import-image 命令或使用标签跟踪来创建标签。内部和外部引用之间没有区别。但是,镜像流规格中标记的每个唯一引用仅计算一次。它不以任何方式限制推送到内部容器镜像 registry,但对标签限制很有用。

openshift.io/images 资源代表镜像流状态中记录的唯一镜像名称。它允许对可以推送到内部 registry 的大量镜像进行限制。内部和外部引用无法区分。

25.1.5. PersistentVolumeClaim Limits

支持的资源:

  • 存储

支持的限制:

在一个项目中的所有持久性卷声明中,必须满足以下条件:

表 25.5. Pod

约束强制行为

Min

Min[resource] <= claim.spec.resources.requests[resource](必需)

Max

claim.spec.resources.requests[resource] (必需)<= Max[resource]

限制范围对象定义

{
  "apiVersion": "v1",
  "kind": "LimitRange",
  "metadata": {
    "name": "pvcs" 1
  },
  "spec": {
    "limits": [{
        "type": "PersistentVolumeClaim",
        "min": {
          "storage": "2Gi" 2
        },
        "max": {
          "storage": "50Gi" 3
        }
      }
    ]
  }
}

1
限制范围对象的名称。
2
持久性卷声明中可请求的最小存储量。
3
在持久性卷声明中请求的最大存储量。

25.2. 创建限制范围

将限制范围应用到一个项目:

  1. 使用您的所需规格创建限值范围对象定义。
  2. 创建对象:

    $ oc create -f <limit_range_file> -n <project>

25.3. 查看限制

您可以通过在 Web 控制台中导航到项目的 Quota 页面来查看项目中定义的任何限值范围。

您还可以执行以下步骤,使用 CLI 查看限制范围详情:

  1. 获取项目中定义的限值范围对象列表。例如,对于名为 demoproject 的项目:

    $ oc get limits -n demoproject

    输出示例

    NAME              AGE
    resource-limits   6d

  2. 描述限值范围。例如,对于名为 resource-limits 的限制范围:

    $ oc describe limits resource-limits -n demoproject

    输出示例

    Name:                           resource-limits
    Namespace:                      demoproject
    Type                            Resource                Min     Max     Default Request Default Limit   Max Limit/Request Ratio
    ----                            --------                ---     ---     --------------- -------------   -----------------------
    Pod                             cpu                     200m    2       -               -               -
    Pod                             memory                  6Mi     1Gi     -               -               -
    Container                       cpu                     100m    2       200m            300m            10
    Container                       memory                  4Mi     1Gi     100Mi           200Mi           -
    openshift.io/Image              storage                 -       1Gi     -               -               -
    openshift.io/ImageStream        openshift.io/image      -       12      -               -               -
    openshift.io/ImageStream        openshift.io/image-tags -       10      -               -               -

25.4. 删除限制范围

要删除限制范围,不再强制实施项目的限值:

  • 运行以下命令:

    $ oc delete limits <limit_name>

第 26 章 节点问题检测程序

26.1. 概述

节点问题检测程序通过发现某些问题并将这些问题报告 API 服务器来监控节点的健康状况。检测器以 daemonset 用户身份在每个节点上运行。

重要

节点问题检测程序只是一个技术预览功能。技术预览功能不包括在红帽生产服务级别协议(SLA)中,且其功能可能并不完善。因此,红帽不建议在生产环境中使用它们。这些技术预览功能可以使用户提早试用新的功能,并有机会在开发阶段提供反馈意见。

如需红帽技术预览功能支持范围的更多信息,请参阅 https://access.redhat.com/support/offerings/techpreview/

节点问题检测程序读取系统日志并监视特定条目,并使其在 control plane 中可见,您可以使用 OpenShift Container Platform 命令(如 oc get nodeoc get event )来查看这些问题。然后,您可以根据情况采取措施更正这些问题,或使用您选择的工具(如 OpenShift Container Platform 日志监控 )捕获信息。检测到的问题可分为以下类别之一:

  • NodeCondition: 使节点对 pod 不可用的永久问题。主机重启后,节点状况才会被清除。
  • Event: 对节点的影响有限但具有信息性的临时问题。

节点问题检测程序可以检测到:

  • 容器运行时问题:

    • 不响应的运行时守护进程
  • 硬件问题:

    • 错误 CPU
    • 错误内存
    • 错误磁盘
  • 内核问题:

    • 内核死锁条件
    • 损坏的文件系统
    • 不响应的运行时守护进程
  • 基础架构守护进程问题:

    • NTP 服务中断

26.2. 节点问题检测器输出示例

以下示例显示来自 Node Problem Detector 监视特定节点上的内核死锁节点状况的输出。该命令使用 oc get node 监控日志中 KernelDeadlock 条目的特定节点过滤。

# oc get node <node> -o yaml | grep -B5 KernelDeadlock

无问题节点问题检测器输出示例

message: kernel has no deadlock
reason: KernelHasNoDeadlock
status: false
type: KernelDeadLock

KernelDeadLock 条件的输出示例

message: task docker:1234 blocked for more than 120 seconds
reason: DockerHung
status: true
type: KernelDeadLock

本例演示了节点问题检测程序的输出,它监视节点上的事件。以下命令将 oc get event 用于默认项目,监视 Node Problem Detector 配置映射的 kernel-monitor.json 部分中列出的事件。

# oc get event -n default --field-selector=source=kernel-monitor --watch

显示节点上事件的输出示例

LAST SEEN                       FIRST SEEN                    COUNT NAME     KIND  SUBOBJECT TYPE    REASON      SOURCE                   MESSAGE
2018-06-27 09:08:27 -0400 EDT   2018-06-27 09:08:27 -0400 EDT 1     my-node1 node            Warning TaskHunk    kernel-monitor.my-node1  docker:1234 blocked for more than 300 seconds
2018-06-27 09:08:27 -0400 EDT   2018-06-27 09:08:27 -0400 EDT 3     my-node2 node            Warning KernelOops  kernel-monitor.my-node2  BUG: unable to handle kernel NULL pointer deference at nowhere
2018-06-27 09:08:27 -0400 EDT   2018-06-27 09:08:27 -0400 EDT 1     my-node1 node            Warning KernelOops  kernel-monitor.my-node2  divide error 0000 [#0] SMP

注意

节点问题检测程序会消耗资源。如果使用节点问题检测程序,请确保有足够的节点来平衡集群性能。

26.3. 安装节点问题检测程序

如果在/etc/ansible/hosts 清单文件中将 openshift_node_problem_detector_install 设置为 true,安装会默认创建一个 Node Problem Detector daemonset,并为检测器创建一个项目,名为 openshift-node-problem-detector

注意

因为 Node Problem Detector 只是一个技术预览,所以 openshift_node_problem_detector_install 默认被设置为 false。安装节点问题检测程序时,您必须手动将该参数改为 true

如果没有安装节点问题检测程序,切换到 playbook 目录并运行 openshift-node-problem-detector/config.yml playbook 来安装节点问题检测器:

$ cd /usr/share/ansible/openshift-ansible
$ ansible-playbook playbooks/openshift-node-problem-detector/config.yml

26.4. 自定义检测的条件

您可以通过编辑 Node Problem Detector 配置映射,将节点问题检测程序配置为监视任何日志字符串。

节点问题检测器配置映射示例

apiVersion: v1
kind: ConfigMap
metadata:
  name: node-problem-detector
data:
  docker-monitor.json: |  1
    {
        "plugin": "journald", 2
        "pluginConfig": {
                "source": "docker"
        },
        "logPath": "/host/log/journal", 3
        "lookback": "5m",
        "bufferSize": 10,
        "source": "docker-monitor",
        "conditions": [],
        "rules": [              4
                {
                        "type": "temporary", 5
                        "reason": "CorruptDockerImage", 6
                        "pattern": "Error trying v2 registry: failed to register layer: rename /var/lib/docker/image/(.+) /var/lib/docker/image/(.+): directory not empty.*" 7
                }
        ]
    }
  kernel-monitor.json: |  8
    {
        "plugin": "journald", 9
        "pluginConfig": {
                "source": "kernel"
        },
        "logPath": "/host/log/journal", 10
        "lookback": "5m",
        "bufferSize": 10,
        "source": "kernel-monitor",
        "conditions": [                 11
                {
                        "type": "KernelDeadlock", 12
                        "reason": "KernelHasNoDeadlock", 13
                        "message": "kernel has no deadlock"  14
                }
        ],
        "rules": [
                {
                        "type": "temporary",
                        "reason": "OOMKilling",
                        "pattern": "Kill process \\d+ (.+) score \\d+ or sacrifice child\\nKilled process \\d+ (.+) total-vm:\\d+kB, anon-rss:\\d+kB, file-rss:\\d+kB"
                },
                {
                        "type": "temporary",
                        "reason": "TaskHung",
                        "pattern": "task \\S+:\\w+ blocked for more than \\w+ seconds\\."
                },
                {
                        "type": "temporary",
                        "reason": "UnregisterNetDevice",
                        "pattern": "unregister_netdevice: waiting for \\w+ to become free. Usage count = \\d+"
                },
                {
                        "type": "temporary",
                        "reason": "KernelOops",
                        "pattern": "BUG: unable to handle kernel NULL pointer dereference at .*"
                },
                {
                        "type": "temporary",
                        "reason": "KernelOops",
                        "pattern": "divide error: 0000 \\[#\\d+\\] SMP"
                },
                {
                        "type": "permanent",
                        "condition": "KernelDeadlock",
                        "reason": "AUFSUmountHung",
                        "pattern": "task umount\\.aufs:\\w+ blocked for more than \\w+ seconds\\."
                },
                {
                        "type": "permanent",
                        "condition": "KernelDeadlock",
                        "reason": "DockerHung",
                        "pattern": "task docker:\\w+ blocked for more than \\w+ seconds\\."
                }
        ]
    }

1
适用于容器镜像的规则和条件。
2 9
监控服务,以逗号分隔列表中。
3 10
监控服务日志的路径。
4 11
要监控的事件列表。
5 12
用于指示错误的标签(temporary)或 NodeCondition(permanent)。
6 13
描述错误的文本消息。
7 14
节点问题检测程序监视的错误消息。
8
适用于内核的规则和条件。

要配置节点问题检测程序,请添加或移除问题条件和事件。

  1. 使用文本编辑器编辑节点问题检测程序配置映射。

    $ oc edit configmap -n openshift-node-problem-detector node-problem-detector
  2. 根据需要删除、添加或编辑任何节点条件或事件。

    {
           "type": <`temporary` or `permanent`>,
           "reason": <free-form text describing the error>,
           "pattern": <log message to watch for>
    },

    例如:

    {
           "type": "temporary",
           "reason": "UnregisterNetDevice",
           "pattern": "unregister_netdevice: waiting for \\w+ to become free. Usage count = \\d+"
    },
  3. 重启正在运行的容器集以应用更改。要重启 pod,您可以删除所有现有的 pod:

    # oc delete pods -n openshift-node-problem-detector -l name=node-problem-detector
  4. 要将节点问题检测器输出显示到标准输出(stdout)和标准错误(stderr),在 DaemonSet 中为节点问题检测器添加以下内容:

    spec:
      template:
        spec:
          containers:
          - name: node-problem-detector
            command:
            - node-problem-detector
            - --alsologtostderr=true 1
            - --log_dir="/tmp" 2
            - --system-log-monitors=/etc/npd/kernel-monitor.json,/etc/npd/docker-monitor.json 3
    1
    将输出发送到标准输出(stdout)。
    2
    错误日志的路径。
    3
    插件配置文件的逗号分隔路径。

26.5. 验证节点问题检测程序正在运行

验证节点问题检测程序是否活跃:

  • 运行以下命令以获取 Problem Node Detector pod 的名称:

    # oc get pods -n openshift-node-problem-detector
    
    NAME                          READY     STATUS    RESTARTS   AGE
    node-problem-detector-8z8r8   1/1       Running   0          1h
    node-problem-detector-nggjv   1/1       Running   0          1h
  • 运行以下命令,以查看 Problem Node Detector pod 的日志信息:

    # oc logs -n openshift-node-problem-detector <pod_name>

    输出结果应类似如下:

    # oc logs -n openshift-node-problem-detector node-problem-detector-c6kng
    I0416 23:22:00.641354       1 log_monitor.go:63] Finish parsing log monitor config file: {WatcherConfig:{Plugin:journald PluginConfig:map[source:kernel] LogPath:/host/log/journal Lookback:5m} BufferSize:10 Source:kernel-monitor DefaultConditions:[{Type:KernelDeadlock Status:false Transition:0001-01-01 00:00:00 +0000 UTC Reason:KernelHasNoDeadlock Message:kernel has no deadlock}]
  • 通过模拟节点上的事件来测试节点问题检测程序:

    # echo "kernel: divide error: 0000 [#0] SMP." >> /dev/kmsg
  • 通过模拟节点上的条件来测试节点问题检测程序:

    # echo "kernel: task docker:7 blocked for more than 300 seconds." >> /dev/kmsg

26.6. 卸载节点问题检测程序

卸载节点问题检测程序:

  1. 在 Ansible 清单文件中添加以下选项:

    [OSEv3:vars]
    openshift_node_problem_detector_state=absent
  2. 进入 playbook 目录并运行config.yml Ansible playbook:

    $ cd /usr/share/ansible/openshift-ansible
    $ ansible-playbook playbooks/openshift-node-problem-detector/config.yml

第 27 章 为 Ingress 流量分配唯一外部 IP

27.1. 概述

将外部流量传输到集群的一种方法是使用 ExternalIP 或 IngressIP 地址。

重要

这个功能只在非云部署中被支持。对于云(GCE、AWS 和 OpenStack)部署,请使用 Load Balancer 服务自动部署云负载均衡器以服务端点为目标。

OpenShift Container Platform 支持两个 IP 地址池:

  • Loadbalancer 在为服务选择外部 IP 地址时使用 IngressIP。
  • 当用户从配置的池中选择特定的 IP 时,可使用 ExternalIP。
注意

两者都必须配置为 OpenShift Container Platform 主机上要使用的设备,无论是网络接口控制器(NIC)或虚拟以太网,以及外部路由。建议为此选择 ipfailover,因为它选择主机并配置 NIC。

IngressIP 和 ExternalIP 都允许外部流量访问集群,如果正确路由,外部流量可以通过服务公开的任何 TCP/UDP 端口访问该服务的端点。当手动将外部 IP 分配给服务时,这比管理有限共享 IP 地址的端口空间要简单。另外,这些地址在配置高可用性时可用作虚拟 IP(VIP)。

OpenShift Container Platform 支持自动和手动分配 IP 地址,并且保证每个地址都被分配到最多一个服务。这样可保证,无论由其他服务公开的端口是什么,每个服务都可以公开选择的端口。

27.2. 限制

要使用 ExternalIP,您可以:

  • externalIPNetworkCIDRs 范围内选择一个 IP 地址。
  • 从 master 配置文件的 ingressIPNetworkCIDR 池分配了一个 IP 地址。在本例中,OpenShift Container Platform 实现了负载均衡器服务类型的非云版本,并为服务分配 IP 地址。

    小心

    您必须确保分配的 IP 地址池在集群中的一个或多个节点上终止。您可以使用现有的 oc adm ipfailover 确保外部 IP 高度可用。

对于手动配置的外部 IP,潜在的端口冲突是在一对一保留的基础上处理的。如果您请求端口,则仅当尚未为该 IP 地址分配端口时,该端口才可用。例如:

手动配置的外部 IP 的端口冲突示例

使用相同的外部 IP 地址 172.7.7.7 手动配置了两个服务。

MongoDB service A 请求端口 27017,然后 MongoDB service B 请求同一端口;第一个请求获取端口。

但是,端口冲突不是入口控制器分配的外部 IP 的问题,因为控制器为每个服务分配了一个唯一的地址。

27.3. 将集群配置为使用唯一外部 IP

在非云集群中,ingressIPNetworkCIDR 默认被设置为 172.29.0.0/16。如果您的集群环境还没有使用此私有范围,您可以使用默认值。但是,如果要使用不同的范围,则必须在/etc/origin/master/master-config.yaml 文件中设置 ingressIPNetworkCIDR,然后才能分配入口 IP。然后,重新启动 master 服务。

小心

分配给类型为 LoadBalancer 的服务的外部 IP 始终在 ingressIPNetworkCIDR 范围内。如果 ingressIPNetworkCIDR 被修改,使得分配的外部 IP 不再在范围内,受影响的服务将会被分配与新范围兼容的新外部 IP。

/etc/origin/master/master-config.yaml 示例

networkConfig:
  ingressIPNetworkCIDR: 172.29.0.0/16

27.3.1. 为服务配置 Ingress IP

要分配入口 IP:

  1. 为 LoadBalancer 服务创建 YAML 文件,该服务通过 loadBalancerIP 设置请求特定的 IP:

    LoadBalancer 配置示例

    apiVersion: v1
    kind: Service
    metadata:
      name: egress-1
    spec:
      ports:
      - name: db
        port: 3306
      loadBalancerIP: 172.29.0.1
      type: LoadBalancer
      selector:
        name: my-db-selector

  2. 在 pod 上创建一个 LoadBalancer 服务:

    $ oc create -f loadbalancer.yaml
  3. 检查 服务是否有外部 IP。例如,对于名为 myservice 的服务,

    $ oc get svc myservice

    当您的 LoadBalancer-type 服务分配有外部 IP 时,输出中会显示 IP:

    NAME         CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    myservice    172.30.74.106   172.29.0.1    3306/TCP    30s

27.4. 为开发或测试路由 Ingress CIDR

添加静态路由,将入口 CIDR 的流量定向到集群中的节点。例如:

# route add -net 172.29.0.0/16 gw 10.66.140.17 eth0

在上例中,172.29.0.0/16ingressIPNetworkCIDR10.66.140.17 是节点 IP。

27.4.1. 服务 externalIP

除了集群的内部 IP 地址外,应用开发人员还可以配置集群外部的 IP 地址。作为 OpenShift Container Platform 管理员,您需要确保流量使用此 IP 到达节点。

管理员必须在master-config.yaml 文件中配置的 externalIPNetworkCIDRs 范围内选择 externalIP。master-config.yaml 改变时,必须重启 master 服务。

# master-restart api
# master-restart controllers

externalIPNetworkCIDR 示例 /etc/origin/master/master-config.yaml

networkConfig:
  externalIPNetworkCIDR: 172.47.0.0/24

服务 externalIP 定义(JSON)

{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "my-service"
    },
    "spec": {
        "selector": {
            "app": "MyApp"
        },
        "ports": [
            {
                "name": "http",
                "protocol": "TCP",
                "port": 80,
                "targetPort": 9376
            }
        ],
        "externalIPs" : [
            "80.11.12.10"         1
        ]
    }
}

1
在其上公开该端口的外部 IP 地址列表。除了内部 IP 地址外)

第 28 章 监控和调试路由器

28.1. 概述

根据底层实施,您可以通过多种方式监控正在运行的路由器。本节讨论 HAProxy 模板路由器和要检查的组件,以确保其健康状况。

28.2. 查看统计信息

HAProxy 路由器为 HAProxy 统计数据公开 web 侦听器。输入路由器的公共 IP 地址和正确配置的端口(默认为1936 )来查看统计信息页面,并在提示时输入管理员密码。此密码和端口在路由器安装期间配置,但可通过查看容器上的haproxy.config 文件找到。

要提取 Prometheus 格式的原始统计信息,请运行以下命令:

$ curl -u <user>:<password> -kv <router_IP>:<STATS_PORT>/metrics

有关获取此命令所需信息的详情,请参阅公开路由器指标

28.3. 禁用 statistics 视图

默认情况下,HAProxy 统计数据在端口 1936 (使用受密码保护的帐户)上公开。要禁用公开 HAProxy 统计信息,请指定 0 作为 stats 端口号。

$ oc adm router hap --service-account=router --stats-port=0

注意:HAProxy 仍然会收集和存储统计信息,它只是不会通过 web 侦听器公开它们。您仍然可以通过向 HAProxy 路由器容器内的 HAProxy AF_UNIX 套接字发送请求来获取对统计数据的访问。

$ cmd="echo 'show stat' | socat - UNIX-CONNECT:/var/lib/haproxy/run/haproxy.sock"
$ routerPod=$(oc get pods --selector="router=router"  \
    --template="{{with index .items 0}}{{.metadata.name}}{{end}}")
$ oc exec $routerPod -- bash -c "$cmd"
重要

为了安全起见oc exec 命令在访问特权容器时无法正常工作。反之,您可以 SSH 到节点主机,然后在所需容器上使用 docker exec 命令。

28.4. 查看日志

要查看路由器日志,请在 pod 上运行 oc logs 命令。由于该路由器作为插件进程运行,负责管理底层实施,因此日志用于插件,而非实际的 HAProxy 日志。

要查看 HAProxy 生成的日志,请启动 syslog 服务器并使用以下环境变量将位置传递到路由器 pod:

表 28.1. 路由器 Syslog 变量

环境变量描述

ROUTER_SYSLOG_ADDRESS

syslog 服务器的 IP 地址。如果没有指定端口,端口 514 为默认值。

ROUTER_LOG_LEVEL

可选。设置 以更改 HAProxy 日志级别。如果没有设置,则默认日志级别为警告。这可以改为 HAProxy 支持的任何日志级别。

ROUTER_SYSLOG_FORMAT

可选。设置 以定义自定义 HAProxy 日志格式。这可以改为 HAProxy 接受的任何日志格式字符串。

将正在运行的路由器 pod 设置为向 syslog 服务器发送信息:

$ oc set env dc/router ROUTER_SYSLOG_ADDRESS=<dest_ip:dest_port>  ROUTER_LOG_LEVEL=<level>

例如,以下命令将 HAProxy 设置为使用默认端口 514 将日志发送到 127.0.0.1,并将日志级别更改为 debug

$ oc set env dc/router ROUTER_SYSLOG_ADDRESS=127.0.0.1 ROUTER_LOG_LEVEL=debug

28.5. 查看路由器内部

routes.json

路由由 HAProxy 路由器处理,它们存储在内存中、磁盘和 HAProxy 配置文件中。内部路由表示法传递到模板以生成 HAProxy 配置文件,可在/var/lib/haproxy/router/routes.json 文件中找到。在对路由问题进行故障排除时,请查看此文件以查看用于驱动配置的数据。

HAProxy 配置

您可以在/var/lib/haproxy/conf/haproxy.config 文件中找到为特定路由创建的 HAProxy 配置和后端。映射文件位于同一目录中。将传入的请求映射到后端时,帮助前端和后端使用映射文件。

证书

证书存储在两个位置:

  • 边缘终止和重新加密终止的路由的证书存储在/var/lib/haproxy/router/certs 目录中。
  • 用于连接后端以重新加密终止路由的证书存储在/var/lib/haproxy/router/cacerts 目录中。

文件由路由的命名空间和名称键。密钥、证书和 CA 证书串联到一个文件中。您可以使用 OpenSSL 查看这些文件的内容。

第 29 章 高可用性

29.1. 概述

本节介绍在 OpenShift Container Platform 集群中为 pod 和服务设置高可用性。

IP 故障转移(IP failover)在一组节点上管理一个虚拟 IP(VIP)地址池。集合中的每个 VIP 将由从该集合中选择的节点提供服务。只要有单一节点可用,就可以提供 VIP。无法将 VIP 显式分发到节点上。因此,可能有没有 VIP 的节点,以及具有多个 VIP 的其他节点。如果只有一个节点,则所有 VIP 都会位于其中。

注意

VIP 必须可以从集群外部路由。

IP 故障转移会监控每个 VIP 上的端口,以确定该端口能否在节点上访问。如果端口无法访问,则不会将 VIP 分配给该节点。如果端口被设置为 0,则会抑制这个检查。 检查脚本 执行所需的测试

IP 故障切换使用Keepalived 在一组主机上托管一组外部可访问的 VIP 地址。在一个时间点上,每个 VIP 仅由一个主机提供服务。 keepalived 使用 VRRP 协议来确定将服务于 VIP 的主机(来自主机组)。如果主机不可用,或者 Keepalived 监控的服务没有响应,VIP 会切换到集合中的另一台主机。因此,只要主机可用,VIP 就会始终提供服务。

当运行 Keepalived 的主机通过检查脚本时,主机会根据其优先级和当前 MASTER 的优先级变为 MASTER 状态,具体由抢占策略决定

管理员可以通过 --notify-script= 选项提供脚本,该选项会在状态发生变化时调用。当为 VIP 服务时,keepalived 处于 MASTER 状态,当另一个节点服务 VIP 时,处于 BACKUP 状态;当检查脚本失败时,keepalived 的状态为 FAULT当状态发生变化时,会调用notify 脚本的新状态

OpenShift Container Platform 支持通过运行 oc adm ipfailover 命令创建 IP 故障切换部署配置。IP 故障转移部署配置指定 VIP 地址的集合,以及服务它们的一组节点。一个集群可以具有多个 IP 故障转移部署配置,各自管理自己的一组唯一的 VIP 地址。IP 故障切换配置中的每个节点运行 IP 故障切换 pod,此 pod 运行 Keepalived

当使用 VIP 访问具有主机网络(如路由器)的 pod 时,应用程序 pod 应在运行 ipfailover pod 的所有节点上运行。这可让任何 ipfailover 节点成为 master 节点,并在需要时服务 VIP。如果应用程序 pod 没有在所有带有 ipfailover 的节点上运行,则一些 ipfailover 节点永远不会为 VIP 服务,或者某些应用程序 pod 永远不会接收任何流量。对 ipfailover 和应用容器集使用相同的选择器和复制数,以避免出现这种不匹配的情况。

在使用 VIP 访问服务时,任何节点都可以位于 ipfailover 节点集中,因为该服务可在所有节点上访问(无论应用程序 pod 在哪里运行)。任何 ipfailover 节点都可以随时成为 master。服务可以使用外部 IP 和服务端口,也可以使用 nodePort。

在服务定义中使用外部 IP 时,VIP 设置为外部 IP,ipfailover 监控端口被设置为服务端口。在群集的每个节点上都打开了 nodePort,该服务将对当前支持 VIP 的任何节点的流量进行负载平衡。在本例中,ipfailover 监控端口在服务定义中被设置为 nodePort。

重要

设置 nodePort 是一个特权操作。

重要

即使一个服务 VIP 具有高可用性,但性能仍会受到影响。keepalived 确保每个 VIP 都由配置中某些节点提供服务,即使其他节点没有服务,多个 VIP 也会在同一节点上结束。当 ipfailover 将多个 VIP 放置在同一节点上时,可能会在一组 VIP 间实现外部负载平衡。

使用 ingressIP 时,您可以将 ipfailover 设置为具有与 ingressIP 范围相同的 VIP 范围。您还可以禁用监控端口。在这种情况下,所有 VIP 都会出现在集群中的同一节点上。任何用户都可以使用 ingressIP 设置服务,并使其高度可用。

重要

集群中最多有 255 个 VIP。

29.2. 配置 IP 故障

使用 oc adm ipfailover 命令及适当的选项来创建 ipfailover 部署配置。

重要

目前,ipfailover 与云基础架构不兼容。对于 AWS,可以利用 Elastic Load Balancer(ELB)来使用 AWS 控制台使 OpenShift Container Platform 高度可用。

作为管理员,您可以在整个集群或节点的子集上配置 ipfailover,具体由标签选择器定义。您还可以在集群中配置多个 IP 故障转移部署配置,每个配置都独立于其他配置。oc adm ipfailover 命令创建 ipfailover 部署配置,以确保每个符合限制或使用的标签的节点都运行故障切换 pod。此 pod 运行Keepalived,它在所有 Keepalived 守护进程中使用 VRRP(虚拟路由器冗余协议),以确保受监视端口上的服务可用,如果不可用,Keepalived 将自动浮动虚拟 IP(VIP)。

对于生产环境,请确保使用至少两个节点的 --selector=<label> 来选择节点。另外,设置与给定标记选择器的节点数量匹配的 --replicas=<n> 值。

oc adm ipfailover 命令包含命令行选项,用于设置控制 Keepalived 的环境变量。环境变量以 OPENSHIFT_HA_* 开头,可以根据需要更改它们。

例如,以下命令将在选择标记为 router=us-west-ha 的节点中创建 IP 故障切换配置(在具有 7 个虚拟 IP 监控侦听端口 80 的服务(如路由器进程)的 4 个节点上,创建 IP 故障切换配置。

$ oc adm ipfailover --selector="router=us-west-ha" \
    --virtual-ips="1.2.3.4,10.1.1.100-104,5.6.7.8" \
    --watch-port=80 --replicas=4 --create

29.2.1. 虚拟 IP 地址

keepalived 管理一组虚拟 IP 地址(VIP)。管理员必须确保所有这些地址:

  • 可在集群外部配置的主机上访问。
  • 不可用于集群中的任何其他目的。

keepalived 在每个节点上确定所需的服务是否在运行。如果是,支持 VIP,Keepalived 会参与协商以确定哪个节点将服务 VIP。对于要参与的节点,服务必须侦听 VIP 上的观察端口,或者必须禁用检查。

注意

集合中的每个 VIP 最终都可能由不同的节点提供。

29.2.2. 检查并通知脚本

keepalived 通过定期运行可选用户提供的检查脚本来监控应用的健康状况。例如,该脚本可以通过发出请求并验证响应来测试 Web 服务器。

该脚本通过 oc adm ipfailover 命令的 --check-script=<script> 选项提供。脚本必须以 0 forPASSFAIL1 退出。

默认情况下,检查每两秒钟进行一次,但可使用 --check-interval=<seconds> 选项进行更改。

如果未提供检查脚本,则运行一个简单的默认脚本来测试 TCP 连接。当监控器端口为 0 时,默认测试会被阻止。

对于每个虚拟 IP(VIP),keepalived 会保留节点的状态。节点上的 VIP 可能处于 MASTER、BACK UPFAULT 状态。节点上处于 FAULT 状态的所有 VIP 都参与协商,以确定 VIP 的 MASTER。所有丢失者都进入 BACKUP 状态。当 MASTER 上的检查脚本失败时,VIP 进入 FAULT 状态并触发重新协商。当 BACKUP 失败时,VIP 进入 FAULT 状态。当检查脚本再次在 FAULT 状态的 VIP 上传递时,它会退出 FAULT 并协商 MASTER。生成的状态为 MASTERBACKUP

管理员可以提供可选的通知脚本,每当状态发生变化时调用该脚本。keepalived 将以下三个参数传递给脚本:

  • $1 - "GROUP"|"INSTANCE"
  • $2 - 组或实例的名称
  • $3 - 新状态("MASTER"|"BACKUP"|"FAULT")

这些脚本在 IP 故障切换 pod 中运行,并使用 pod 的文件系统,而不是主机文件系统。这些选项需要脚本的完整路径。管理员必须在容器集中提供 脚本,以便从运行通知脚本中提取结果。提供脚本的建议方法是使用 ConfigMap

检查 和通知脚本的完整路径名称添加到 keepalived 配置文件/etc/keepalived/keepalived.conf 中,每次 keepalived 启动时都会载入该文件。脚本可以通过 ConfigMap 添加到 pod,如下所示:

  1. 创建所需的脚本并创建 ConfigMap 来存放它。该脚本没有输入参数,对于 OK 返回 0,对于 FAIL 返回 1

    检查脚本mycheckscript.sh:

    #!/bin/bash
        # Whatever tests are needed
        # E.g., send request and verify response
    exit 0
  2. 创建 ConfigMap:

    $ oc create configmap mycustomcheck --from-file=mycheckscript.sh
  3. 在 pod 中添加脚本有两种方法:使用 oc 命令或编辑部署配置。在这两种情况下,挂载的 configMap 文件的 defaultMode 都必须允许执行。典型值为 0755493 十进制)。

    1. 使用 oc 命令:

      $ oc env dc/ipf-ha-router \
          OPENSHIFT_HA_CHECK_SCRIPT=/etc/keepalive/mycheckscript.sh
      $ oc set volume dc/ipf-ha-router --add --overwrite \
          --name=config-volume \
          --mount-path=/etc/keepalive \
          --source='{"configMap": { "name": "mycustomcheck", "defaultMode": 493}}'
    2. 编辑 ipf-ha-router 部署配置:

      1. 使用 oc edit dc ipf-ha-router 使用文本编辑器编辑路由器部署配置。

        ...
            spec:
              containers:
              - env:
                - name: OPENSHIFT_HA_CHECK_SCRIPT  1
                  value: /etc/keepalive/mycheckscript.sh
        ...
                volumeMounts: 2
                - mountPath: /etc/keepalive
                  name: config-volume
              dnsPolicy: ClusterFirst
        ...
              volumes: 3
              - configMap:
                  defaultMode: 0755 4
                  name: customrouter
                name: config-volume
        ...
        1
        spec.container.env 字段中,添加 OPENSHIFT_HA_CHECK_SCRIPT 环境变量以指向挂载的脚本文件。
        2
        添加 spec.container.volumeMounts 字段以创建挂载点。
        3
        添加一个新的 spec.volumes 字段来引用 ConfigMap。
        4
        这会设置文件的执行权限。返回后,它将以十进制(493)显示。
      2. 保存更改并退出编辑器。这会重启 ipf-ha-router

29.2.3. VRRP 抢占

当主机通过传递检查脚本而退出 FAULT 状态时,如果新主机的优先级低于 MASTER 状态,则主机将变为 BACKUP。但是,如果它具有更高的优先级,抢占策略会决定它在集群中的角色。

nopreempt 策略不会将 MASTER 从较低优先级主机移到较高优先级的主机。使用 Preempt 300 (默认值),keepalived 会等待指定的 300 秒,并将 MASTER 移到更高优先级的主机。

指定抢占:

  1. 当使用 preemption-strategy 创建 ipfailover 时:

    $ oc adm ipfailover --preempt-strategy=nopreempt \
      ...
  2. 使用 oc set env 命令设置变量:

    $ oc set env dc/ipf-ha-router \
        --overwrite=true \
        OPENSHIFT_HA_PREEMPTION=nopreempt
  3. 使用 oc edit dc ipf-ha-router 编辑路由器部署配置:

    ...
        spec:
          containers:
          - env:
            - name: OPENSHIFT_HA_PREEMPTION  1
              value: nopreempt
    ...

29.2.4. keepalived 多广播

OpenShift Container Platform 的 IP 故障切换内部使用 keepalived

重要

确保上方标记的节点上启用了多播,并且可以接受 224.0.0.18(VRRP 多播 IP 地址)的网络流量。

在启动 keepalived 守护进程前,启动脚本会验证允许多播流量流的 iptables 规则。如果没有这样的规则,启动脚本会创建一个新规则,并将其添加到 IP 表配置中。这个新规则添加到 IP 表配置的位置取决于 --iptables-chain= 选项。如果指定了 --iptables-chain= 选项,规则会添加到选项中指定的链中。否则,该规则会添加到 INPUT 链中。

重要

每当节点上运行一个或多个 keepalived 守护进程时,iptables 规则都必须存在。

iptables 规则可以在最后一个 keepalived 守护进程终止后删除。规则不会自动删除。

您可以在每个节点上手动管理 iptables 规则。它只在没有显示时才创建(只要 ipfailover 没有使用 --iptable-chain="" 选项创建)。

重要

您必须确保在系统重启后手动添加的规则保留。

请注意,因为每个 keepalived 守护进程都通过多播 224.0.0.18 使用 VRRP 协议与其同级协商。每个 VIP 必须有不同的 VRRP-id(范围为 0..255)。

$ for node in openshift-node-{5,6,7,8,9}; do   ssh $node <<EOF

export interface=${interface:-"eth0"}
echo "Check multicast enabled ... ";
ip addr show $interface | grep -i MULTICAST

echo "Check multicast groups ... "
ip maddr show $interface | grep 224.0.0

EOF
done;

29.2.5. 命令行选项和环境变量

表 29.1. 命令行选项和环境变量

选项变量名称默认备注

--watch-port

OPENSHIFT_HA_MONITOR_PORT

80

ipfailover pod 会尝试在每个 VIP 上打开到这个端口的 TCP 连接。如果建立连接,则服务将被视为正在运行。如果此端口设为 0,则测试始终通过。

--interface

OPENSHIFT_HA_NETWORK_INTERFACE

 

用于发送 VRRP 流量的 ipfailover 接口名称。默认情况下使用 eth0

--replicas

OPENSHIFT_HA_REPLICA_COUNT

2

要创建的副本数。这必须与 ipfailover 部署配置中的 spec.replicas 值匹配。

--virtual-ips

OPENSHIFT_HA_VIRTUAL_IPS

 

要复制的 IP 地址范围列表。必须提供.(例如,1.2.3.4-6,1.2.3.9.) 详情请查看本讨论

--vrrp-id-offset

OPENSHIFT_HA_VRRP_ID_OFFSET

0

如需了解更多详细信息,请参阅 VRRP ID Offset 讨论。

--virtual-ip-groups

OPENSHIFT_HA_VIP_GROUPS

 

为 VRRP 创建的组数量。如果没有设置,则会为使用 --virtual-ips 选项指定的每个虚拟 IP 范围创建一个组。如需更多信息,请参阅为 254 多个地址配置 IP 故障切换

--iptables-chain

OPENSHIFT_HA_IPTABLES_CHAIN

输入

iptables 链的名称,以自动添加 iptables 规则来允许 VRRP 流量。如果没有设置该值,则不会添加 iptables 规则。如果链不存在,则不会创建它。

--check-script

OPENSHIFT_HA_CHECK_SCRIPT

 

定期运行的脚本的 pod 文件系统中的完整路径名称,以验证应用是否正常运行。详情请查看本讨论

--check-interval

OPENSHIFT_HA_CHECK_INTERVAL

2

检查脚本运行的期间(以秒为单位)。

--notify-script

OPENSHIFT_HA_NOTIFY_SCRIPT

 

当状态发生变化时运行的脚本的 pod 文件系统中的完整路径名称。详情请查看本讨论

--preemption-strategy

OPENSHIFT_HA_PREEMPTION

Preempt 300

处理新高优先级主机的策略。如需了解更多详细信息,请参阅 VRRP Preemption 部分

29.2.6. VRRP ID Offset

由 ipfailover 部署配置管理的每个 ipfailover pod(每个节点/副本 1 个 pod)都运行 keepalived 守护进程。随着配置更多 ipfailover 部署配置,会创建更多 pod,并将更多守护进程加入到常见 VRRP 协商中。此协商由所有 keepalived 守护进程完成,并决定哪些节点将服务于哪些虚拟 IP(VIP)。

在内部,keepalived 会为每个 VIP 分配唯一的 vrrp-id。协商使用这组 vrrp-ids,在做出决策时,与获胜 vrrp-id 对应的 VIP 在获胜节点上提供服务。

因此,对于 ipfailover 部署配置中定义的每个 VIP,ipfailover pod 必须分配对应的 vrrp-id。这可以通过从 --vrrp-id-offset 开始,并按顺序将 vrrp-ids 分配给 VIP 列表。vrrp-ids 可能具有范围为 1..255 的值。

当有多个 ipfailover 部署配置时,必须小心指定 --vrrp-id-offset,以便有空间增加部署配置中的 VIPS 数量,且没有 vrrp-id 范围重叠。

29.2.7. 为超过 254 地址配置 IP 故障转移

IP 故障转移管理仅限于 254 组 VIP 地址。默认情况下,OpenShift Container Platform 会为每个组分配一个 IP 地址。您可以使用 virtual-ip-groups 选项更改此项,以便每个组中有多个 IP 地址,并在配置 IP 故障切换时为每个 VRRP 实例定义 VIP 组数量。

在 VRRP 故障转移事件中,对 VIP 进行分组会为每个 VRRP 创建更广泛的 VIP 分配范围,并在集群中的所有主机都能够从本地访问服务时很有用。例如,当使用 ExternalIP 公开服务时。

注意

使用故障转移的一个规则是,请勿将路由等服务限制到一个特定的主机。相反,服务应复制到每一主机上,以便在 IP 故障转移时,不必在新主机上重新创建服务。