这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

集群管理

关于创建和管理 Kubernetes 集群的底层细节。

集群管理概述面向任何创建和管理 Kubernetes 集群的读者人群。 我们假设你大概了解一些核心的 Kubernetes 概念

规划集群

查阅安装中的指导,获取如何规划、建立以及配置 Kubernetes 集群的示例。本文所列的文章称为发行版

在选择一个指南前,有一些因素需要考虑:

  • 你是打算在你的计算机上尝试 Kubernetes,还是要构建一个高可用的多节点集群? 请选择最适合你需求的发行版。
  • 你正在使用类似 Google Kubernetes Engine 这样的被托管的 Kubernetes 集群, 还是管理你自己的集群
  • 你的集群是在本地还是云(IaaS) 上?Kubernetes 不能直接支持混合集群。 作为代替,你可以建立多个集群。
  • 如果你在本地配置 Kubernetes,需要考虑哪种 网络模型最适合。
  • 你的 Kubernetes 在裸金属硬件上还是虚拟机(VMs) 上运行?
  • 你是想运行一个集群,还是打算参与开发 Kubernetes 项目代码? 如果是后者,请选择一个处于开发状态的发行版。 某些发行版只提供二进制发布版,但提供更多的选择。
  • 让你自己熟悉运行一个集群所需的组件

管理集群

保护集群

保护 kubelet

可选集群服务

1 - 证书

要了解如何为集群生成证书,参阅证书

2 - 管理资源

你已经部署了应用并通过服务暴露它。然后呢? Kubernetes 提供了一些工具来帮助管理你的应用部署,包括扩缩容和更新。 我们将更深入讨论的特性包括 配置文件标签

组织资源配置

许多应用需要创建多个资源,例如 Deployment 和 Service。 可以通过将多个资源组合在同一个文件中(在 YAML 中以 --- 分隔) 来简化对它们的管理。例如:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

可以用创建单个资源相同的方式来创建多个资源:

kubectl apply -f https://k8s.io/examples/application/nginx-app.yaml
service/my-nginx-svc created
deployment.apps/my-nginx created

资源将按照它们在文件中的顺序创建。 因此,最好先指定服务,这样在控制器(例如 Deployment)创建 Pod 时能够 确保调度器可以将与服务关联的多个 Pod 分散到不同节点。

kubectl create 也接受多个 -f 参数:

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-svc.yaml -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml

还可以指定目录路径,而不用添加多个单独的文件:

kubectl apply -f https://k8s.io/examples/application/nginx/

kubectl 将读取任何后缀为 .yaml.yml 或者 .json 的文件。

建议的做法是,将同一个微服务或同一应用层相关的资源放到同一个文件中, 将同一个应用相关的所有文件按组存放到同一个目录中。 如果应用的各层使用 DNS 相互绑定,那么你可以将堆栈的所有组件一起部署。

还可以使用 URL 作为配置源,便于直接使用已经提交到 Github 上的配置文件进行部署:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/zh-cn/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

kubectl 中的批量操作

资源创建并不是 kubectl 可以批量执行的唯一操作。 kubectl 还可以从配置文件中提取资源名,以便执行其他操作, 特别是删除你之前创建的资源:

kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

在仅有两种资源的情况下,可以使用"资源类型/资源名"的语法在命令行中 同时指定这两个资源:

kubectl delete deployments/my-nginx services/my-nginx-svc

对于资源数目较大的情况,你会发现使用 -l--selector 指定筛选器(标签查询)能很容易根据标签筛选资源:

kubectl delete deployment,services -l app=nginx
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

由于 kubectl 用来输出资源名称的语法与其所接受的资源名称语法相同, 你可以使用 $()xargs 进行链式操作:

kubectl get $(kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service)
kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service | xargs -i kubectl get {}
NAME           TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)      AGE
my-nginx-svc   LoadBalancer   10.0.0.208   <pending>     80/TCP       0s

上面的命令中,我们首先使用 examples/application/nginx/ 下的配置文件创建资源, 并使用 -o name 的输出格式(以"资源/名称"的形式打印每个资源)打印所创建的资源。 然后,我们通过 grep 来过滤 "service",最后再打印 kubectl get 的内容。

如果你碰巧在某个路径下的多个子路径中组织资源,那么也可以递归地在所有子路径上 执行操作,方法是在 --filename,-f 后面指定 --recursive 或者 -R

例如,假设有一个目录路径为 project/k8s/development,它保存开发环境所需的 所有清单,并按资源类型组织:

project/k8s/development
├── configmap
│   └── my-configmap.yaml
├── deployment
│   └── my-deployment.yaml
└── pvc
    └── my-pvc.yaml

默认情况下,对 project/k8s/development 执行的批量操作将停止在目录的第一级, 而不是处理所有子目录。 如果我们试图使用以下命令在此目录中创建资源,则会遇到一个错误:

kubectl apply -f project/k8s/development
error: you must provide one or more resources by argument or filename (.json|.yaml|.yml|stdin)

正确的做法是,在 --filename,-f 后面标明 --recursive 或者 -R 之后:

kubectl apply -f project/k8s/development --recursive
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

--recursive 可以用于接受 --filename,-f 参数的任何操作,例如: kubectl {create,get,delete,describe,rollout} 等。

有多个 -f 参数出现的时候,--recursive 参数也能正常工作:

kubectl apply -f project/k8s/namespaces -f project/k8s/development --recursive
namespace/development created
namespace/staging created
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

如果你有兴趣进一步学习关于 kubectl 的内容,请阅读 命令行工具(kubectl)

有效地使用标签

到目前为止我们使用的示例中的资源最多使用了一个标签。 在许多情况下,应使用多个标签来区分集合。

例如,不同的应用可能会为 app 标签设置不同的值。 但是,类似 guestbook 示例 这样的多层应用,还需要区分每一层。前端可以带以下标签:

     labels:
        app: guestbook
        tier: frontend

Redis 的主节点和从节点会有不同的 tier 标签,甚至还有一个额外的 role 标签:

     labels:
        app: guestbook
        tier: backend
        role: master

以及

     labels:
        app: guestbook
        tier: backend
        role: slave

标签允许我们按照标签指定的任何维度对我们的资源进行切片和切块:

kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml
kubectl get pods -Lapp -Ltier -Lrole
NAME                           READY     STATUS    RESTARTS   AGE       APP         TIER       ROLE
guestbook-fe-4nlpb             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-ght6d             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-jpy62             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-redis-master-5pg3b   1/1       Running   0          1m        guestbook   backend    master
guestbook-redis-slave-2q2yf    1/1       Running   0          1m        guestbook   backend    slave
guestbook-redis-slave-qgazl    1/1       Running   0          1m        guestbook   backend    slave
my-nginx-divi2                 1/1       Running   0          29m       nginx       <none>     <none>
my-nginx-o0ef1                 1/1       Running   0          29m       nginx       <none>     <none>
kubectl get pods -lapp=guestbook,role=slave
NAME                          READY     STATUS    RESTARTS   AGE
guestbook-redis-slave-2q2yf   1/1       Running   0          3m
guestbook-redis-slave-qgazl   1/1       Running   0          3m

金丝雀部署(Canary Deployments)

另一个需要多标签的场景是用来区分同一组件的不同版本或者不同配置的多个部署。 常见的做法是部署一个使用金丝雀发布来部署新应用版本 (在 Pod 模板中通过镜像标签指定),保持新旧版本应用同时运行。 这样,新版本在完全发布之前也可以接收实时的生产流量。

例如,你可以使用 track 标签来区分不同的版本。

主要稳定的发行版将有一个 track 标签,其值为 stable

     name: frontend
     replicas: 3
     ...
     labels:
        app: guestbook
        tier: frontend
        track: stable
     ...
     image: gb-frontend:v3

然后,你可以创建 guestbook 前端的新版本,让这些版本的 track 标签带有不同的值 (即 canary),以便两组 Pod 不会重叠:

     name: frontend-canary
     replicas: 1
     ...
     labels:
        app: guestbook
        tier: frontend
        track: canary
     ...
     image: gb-frontend:v4

前端服务通过选择标签的公共子集(即忽略 track 标签)来覆盖两组副本, 以便流量可以转发到两个应用:

  selector:
     app: guestbook
     tier: frontend

你可以调整 stablecanary 版本的副本数量,以确定每个版本将接收 实时生产流量的比例(在本例中为 3:1)。 一旦有信心,你就可以将新版本应用的 track 标签的值从 canary 替换为 stable,并且将老版本应用删除。

想要了解更具体的示例,请查看 Ghost 部署教程

更新标签

有时,现有的 pod 和其它资源需要在创建新资源之前重新标记。 这可以用 kubectl label 完成。 例如,如果想要将所有 nginx pod 标记为前端层,运行:

kubectl label pods -l app=nginx tier=fe
pod/my-nginx-2035384211-j5fhi labeled
pod/my-nginx-2035384211-u2c7e labeled
pod/my-nginx-2035384211-u3t6x labeled

首先用标签 "app=nginx" 过滤所有的 Pod,然后用 "tier=fe" 标记它们。 想要查看你刚才标记的 Pod,请运行:

kubectl get pods -l app=nginx -L tier
NAME                        READY     STATUS    RESTARTS   AGE       TIER
my-nginx-2035384211-j5fhi   1/1       Running   0          23m       fe
my-nginx-2035384211-u2c7e   1/1       Running   0          23m       fe
my-nginx-2035384211-u3t6x   1/1       Running   0          23m       fe

这将输出所有 "app=nginx" 的 Pod,并有一个额外的描述 Pod 的 tier 的标签列 (用参数 -L 或者 --label-columns 标明)。

想要了解更多信息,请参考 标签kubectl label 命令文档。

更新注解

有时,你可能希望将注解附加到资源中。注解是 API 客户端(如工具、库等) 用于检索的任意非标识元数据。这可以通过 kubectl annotate 来完成。例如:

kubectl annotate pods my-nginx-v4-9gw19 description='my frontend running nginx'
kubectl get pods my-nginx-v4-9gw19 -o yaml
apiVersion: v1
kind: pod
metadata:
  annotations:
    description: my frontend running nginx
...

想要了解更多信息,请参考 注解kubectl annotate 命令文档。

扩缩你的应用

当应用上的负载增长或收缩时,使用 kubectl 能够实现应用规模的扩缩。 例如,要将 nginx 副本的数量从 3 减少到 1,请执行以下操作:

kubectl scale deployment/my-nginx --replicas=1
deployment.extensions/my-nginx scaled

现在,你的 Deployment 管理的 Pod 只有一个了。

kubectl get pods -l app=nginx
NAME                        READY     STATUS    RESTARTS   AGE
my-nginx-2035384211-j5fhi   1/1       Running   0          30m

想要让系统自动选择需要 nginx 副本的数量,范围从 1 到 3,请执行以下操作:

kubectl autoscale deployment/my-nginx --min=1 --max=3
horizontalpodautoscaler.autoscaling/my-nginx autoscaled

现在,你的 nginx 副本将根据需要自动地增加或者减少。

想要了解更多信息,请参考 kubectl scale命令文档、 kubectl autoscale 命令文档和 水平 Pod 自动伸缩 文档。

就地更新资源

有时,有必要对你所创建的资源进行小范围、无干扰地更新。

kubectl apply

建议在源代码管理中维护一组配置文件 (参见配置即代码), 这样,它们就可以和应用代码一样进行维护和版本管理。 然后,你可以用 kubectl apply 将配置变更应用到集群中。

这个命令将会把推送的版本与以前的版本进行比较,并应用你所做的更改, 但是不会自动覆盖任何你没有指定更改的属性。

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx configured

注意,kubectl apply 将为资源增加一个额外的注解,以确定自上次调用以来对配置的更改。 执行时,kubectl apply 会在以前的配置、提供的输入和资源的当前配置之间 找出三方差异,以确定如何修改资源。

目前,新创建的资源是没有这个注解的,所以,第一次调用 kubectl apply 时 将使用提供的输入和资源的当前配置双方之间差异进行比较。 在第一次调用期间,它无法检测资源创建时属性集的删除情况。 因此,kubectl 不会删除它们。

所有后续的 kubectl apply 操作以及其他修改配置的命令,如 kubectl replacekubectl edit,都将更新注解,并允许随后调用的 kubectl apply 使用三方差异进行检查和执行删除。

kubectl edit

或者,你也可以使用 kubectl edit 更新资源:

kubectl edit deployment/my-nginx

这相当于首先 get 资源,在文本编辑器中编辑它,然后用更新的版本 apply 资源:

kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml
vi /tmp/nginx.yaml
# do some edit, and then save the file

kubectl apply -f /tmp/nginx.yaml
deployment.apps/my-nginx configured

rm /tmp/nginx.yaml

这使你可以更加容易地进行更重大的更改。 请注意,可以使用 EDITORKUBE_EDITOR 环境变量来指定编辑器。

想要了解更多信息,请参考 kubectl edit 文档。

kubectl patch

你可以使用 kubectl patch 来更新 API 对象。此命令支持 JSON patch、 JSON merge patch、以及 strategic merge patch。 请参考 使用 kubectl patch 更新 API 对象kubectl patch.

破坏性的更新

在某些情况下,你可能需要更新某些初始化后无法更新的资源字段,或者你可能只想立即进行递归更改, 例如修复 Deployment 创建的不正常的 Pod。若要更改这些字段,请使用 replace --force, 它将删除并重新创建资源。在这种情况下,你可以修改原始配置文件:

kubectl replace -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml --force
deployment.apps/my-nginx deleted
deployment.apps/my-nginx replaced

在不中断服务的情况下更新应用

在某些时候,你最终需要更新已部署的应用,通常都是通过指定新的镜像或镜像标签, 如上面的金丝雀发布的场景中所示。kubectl 支持几种更新操作, 每种更新操作都适用于不同的场景。

我们将指导你通过 Deployment 如何创建和更新应用。

假设你正运行的是 1.14.2 版本的 nginx:

kubectl create deployment my-nginx --image=nginx:1.14.2
deployment.apps/my-nginx created

要更新到 1.16.1 版本,只需使用我们前面学到的 kubectl 命令将 .spec.template.spec.containers[0].imagenginx:1.14.2 修改为 nginx:1.16.1

kubectl edit deployment/my-nginx

没错,就是这样!Deployment 将在后台逐步更新已经部署的 nginx 应用。 它确保在更新过程中,只有一定数量的旧副本被开闭,并且只有一定基于所需 Pod 数量的新副本被创建。 想要了解更多细节,请参考 Deployment

接下来

3 - 集群网络系统

集群网络系统是 Kubernetes 的核心部分,但是想要准确了解它的工作原理可是个不小的挑战。 下面列出的是网络系统的的四个主要问题:

  1. 高度耦合的容器间通信:这个已经被 Podslocalhost 通信解决了。
  2. Pod 间通信:本文档讲述重点。
  3. Pod 和服务间通信:由服务负责。
  4. 外部和服务间通信:也由服务负责。

Kubernetes 的宗旨就是在应用之间共享机器。 通常来说,共享机器需要两个应用之间不能使用相同的端口,但是在多个应用开发者之间 去大规模地协调端口是件很困难的事情,尤其是还要让用户暴露在他们控制范围之外的集群级别的问题上。

动态分配端口也会给系统带来很多复杂度 - 每个应用都需要设置一个端口的参数, 而 API 服务器还需要知道如何将动态端口数值插入到配置模块中,服务也需要知道如何找到对方等等。 与其去解决这些问题,Kubernetes 选择了其他不同的方法。

要了解 Kubernetes 网络模型,请参阅此处

如何实现 Kubernetes 的网络模型

有很多种方式可以实现这种网络模型,本文档并不是对各种实现技术的详细研究, 但是希望可以作为对各种技术的详细介绍,并且成为你研究的起点。

接下来的网络技术是按照首字母排序,顺序本身并无其他意义。

ACI

Cisco Application Centric Infrastructure 提供了一个集成覆盖网络和底层 SDN 的解决方案来支持容器、虚拟机和其他裸机服务器。 ACI 为 ACI 提供了容器网络集成。 点击这里查看概述。

Antrea

Antrea 项目是一个开源的联网解决方案,旨在成为 Kubernetes 原生的网络解决方案。它利用 Open vSwitch 作为网络数据平面。 Open vSwitch 是一个高性能可编程的虚拟交换机,支持 Linux 和 Windows 平台。 Open vSwitch 使 Antrea 能够以高性能和高效的方式实现 Kubernetes 的网络策略。 借助 Open vSwitch 可编程的特性,Antrea 能够在 Open vSwitch 之上实现广泛的联网、安全功能和服务。

Kubernetes 的 AWS VPC CNI

AWS VPC CNI 为 Kubernetes 集群提供了集成的 AWS 虚拟私有云(VPC)网络。该 CNI 插件提供了高吞吐量和可用性,低延迟以及最小的网络抖动。 此外,用户可以使用现有的 AWS VPC 网络和安全最佳实践来构建 Kubernetes 集群。 这包括使用 VPC 流日志、VPC 路由策略和安全组进行网络流量隔离的功能。

使用该 CNI 插件,可使 Kubernetes Pod 拥有与在 VPC 网络上相同的 IP 地址。 CNI 将 AWS 弹性网络接口(ENI)分配给每个 Kubernetes 节点,并将每个 ENI 的辅助 IP 范围用于该节点上的 Pod。 CNI 包含用于 ENI 和 IP 地址的预分配的控件,以便加快 Pod 的启动时间,并且能够支持多达 2000 个节点的大型集群。

此外,CNI 可以与 用于执行网络策略的 Calico 一起运行。 AWS VPC CNI 项目是开源的,请查看 GitHub 上的文档

Kubernetes 的 Azure CNI

Azure CNI 是一个开源插件, 将 Kubernetes Pods 和 Azure 虚拟网络(也称为 VNet)集成在一起,可提供与 VM 相当的网络性能。 Pod 可以通过 Express Route 或者 站点到站点的 VPN 来连接到对等的 VNet, 也可以从这些网络来直接访问 Pod。Pod 可以访问受服务端点或者受保护链接的 Azure 服务,比如存储和 SQL。 你可以使用 VNet 安全策略和路由来筛选 Pod 流量。 该插件通过利用在 Kubernetes 节点的网络接口上预分配的辅助 IP 池将 VNet 分配给 Pod。

Azure CNI 可以在 Azure Kubernetes Service (AKS) 中获得。

Calico

Calico 是一个开源的联网及网络安全方案, 用于基于容器、虚拟机和本地主机的工作负载。 Calico 支持多个数据面,包括:纯 Linux eBPF 的数据面、标准的 Linux 联网数据面 以及 Windows HNS 数据面。Calico 在提供完整的联网堆栈的同时,还可与 云驱动 CNIs 联合使用,以保证网络策略实施。

Cilium

Cilium 是一个开源软件,用于提供并透明保护应用容器间的网络连接。 Cilium 支持 L7/HTTP,可以在 L3-L7 上通过使用与网络分离的基于身份的安全模型寻址来实施网络策略, 并且可以与其他 CNI 插件结合使用。

华为的 CNI-Genie

CNI-Genie 是一个 CNI 插件, 可以让 Kubernetes 在运行时使用不同的网络模型实现同时被访问。 这包括以 CNI 插件运行的任何实现,比如 FlannelCalicoWeave-net

CNI-Genie 还支持将多个 IP 地址分配给 Pod, 每个都来自不同的 CNI 插件。

cni-ipvlan-vpc-k8s

cni-ipvlan-vpc-k8s 包含了一组 CNI 和 IPAM 插件来提供一个简单的、本地主机、低延迟、高吞吐量 以及通过使用 Amazon 弹性网络接口(ENI)并使用 Linux 内核的 IPv2 驱动程序 以 L2 模式将 AWS 管理的 IP 绑定到 Pod 中, 在 Amazon Virtual Private Cloud(VPC)环境中为 Kubernetes 兼容的网络堆栈。

这些插件旨在直接在 VPC 中进行配置和部署,Kubelets 先启动, 然后根据需要进行自我配置和扩展它们的 IP 使用率,而无需经常建议复杂的管理 覆盖网络、BGP、禁用源/目标检查或调整 VPC 路由表以向每个主机提供每个实例子网的 复杂性(每个 VPC 限制为50-100个条目)。 简而言之,cni-ipvlan-vpc-k8s 大大降低了在 AWS 中大规模部署 Kubernetes 所需的网络复杂性。

Coil

Coil 是一个为易于集成、提供灵活的出站流量网络而设计的 CNI 插件。 与裸机相比,Coil 的额外操作开销低,并允许针对外部网络的出站流量任意定义 NAT 网关。

Contiv-VPP

Contiv-VPP 是用于 Kubernetes 的用户空间、面向性能的网络插件,使用 fd.io 数据平面。

Contrail / Tungsten Fabric

Contrail 是基于 Tungsten Fabric 的,真正开放的多云网络虚拟化和策略管理平台。 Contrail 和 Tungsten Fabric 与各种编排系统集成在一起,例如 Kubernetes、OpenShift、OpenStack 和 Mesos, 并为虚拟机、容器或 Pods 以及裸机工作负载提供了不同的隔离模式。

DANM

DANM 是一个针对在 Kubernetes 集群中运行的电信工作负载的网络解决方案。 它由以下几个组件构成:

  • 能够配置具有高级功能的 IPVLAN 接口的 CNI 插件
  • 一个内置的 IPAM 模块,能够管理多个、集群内的、不连续的 L3 网络,并按请求提供动态、静态或无 IP 分配方案
  • CNI 元插件能够通过自己的 CNI 或通过将任务授权给其他任何流行的 CNI 解决方案(例如 SRI-OV 或 Flannel)来实现将多个网络接口连接到容器
  • Kubernetes 控制器能够集中管理所有 Kubernetes 主机的 VxLAN 和 VLAN 接口
  • 另一个 Kubernetes 控制器扩展了 Kubernetes 的基于服务的服务发现概念,以在 Pod 的所有网络接口上工作

通过这个工具集,DANM 可以提供多个分离的网络接口,可以为 Pod 使用不同的网络后端和高级 IPAM 功能。

Flannel

Flannel 是一个非常简单的能够满足 Kubernetes 所需要的覆盖网络。已经有许多人报告了使用 Flannel 和 Kubernetes 的成功案例。

Hybridnet

Hybridnet 是一个为混合云设计的开源 CNI 插件, 它为一个或多个集群中的容器提供覆盖和底层网络。Overlay 和 underlay 容器可以在同一个节点上运行, 并具有集群范围的双向网络连接。

Jaguar

Jaguar 是一个基于 OpenDaylight 的 Kubernetes 网络开源解决方案。 Jaguar 使用 vxlan 提供覆盖网络,而 Jaguar CNIPlugin 为每个 Pod 提供一个 IP 地址。

k-vswitch

k-vswitch 是一个基于 Open vSwitch 的简易 Kubernetes 网络插件。 它利用 Open vSwitch 中现有的功能来提供强大的网络插件,该插件易于操作,高效且安全。

Knitter

Knitter 是一个支持 Kubernetes 中实现多个网络系统的解决方案。 它提供了租户管理和网络管理的功能。除了多个网络平面外,Knitter 还包括一组端到端的 NFV 容器网络解决方案, 例如为应用程序保留 IP 地址、IP 地址迁移等。

Kube-OVN

Kube-OVN 是一个基于 OVN 的用于企业的 Kubernetes 网络架构。 借助于 OVN/OVS,它提供了一些高级覆盖网络功能,例如子网、QoS、静态 IP 分配、流量镜像、网关、 基于 openflow 的网络策略和服务代理。

Kube-router

Kube-router 是 Kubernetes 的专用网络解决方案, 旨在提供高性能和易操作性。 Kube-router 提供了一个基于 Linux LVS/IPVS 的服务代理、一个基于 Linux 内核转发的无覆盖 Pod-to-Pod 网络解决方案和基于 iptables/ipset 的网络策略执行器。

L2 networks and linux bridging

如果你具有一个“哑”的 L2 网络,例如“裸机”环境中的简单交换机,则应该能够执行与上述 GCE 设置类似的操作。 请注意,这些说明仅是非常简单的尝试过-似乎可行,但尚未经过全面测试。 如果你使用此技术并完善了流程,请告诉我们。

根据 Lars Kellogg-Stedman 的这份非常不错的 “Linux 网桥设备” 使用说明来进行操作。

Multus (a Multi Network plugin)

Multus 是一个多 CNI 插件, 使用 Kubernetes 中基于 CRD 的网络对象来支持实现 Kubernetes 多网络系统。

Multus 支持所有参考插件(比如: FlannelDHCPMacvlan ) 来实现 CNI 规范和第三方插件(比如: CalicoWeaveCiliumContiv)。 除此之外,Multus 还支持 SRIOVDPDKOVS-DPDK & VPP 的工作负载, 以及 Kubernetes 中基于云的本机应用程序和基于 NFV 的应用程序。

OVN4NFV-K8s-Plugin(基于 OVN 的 CNI 控制器和插件)

OVN4NFV-K8S-Plugin 是基于 OVN 的 CNI 控制器插件,提供基于云原生的服务功能链 (SFC)、多个 OVN 覆盖网络、动态子网创建、虚拟网络的动态创建、VLAN Provider 网络、Direct Provider 网络且可与其他多网络插件组合,非常适合多集群网络中基于边缘的云原生工作负载。

NSX-T

VMware NSX-T 是一个网络虚拟化和安全平台。 NSX-T 可以为多云及多系统管理程序环境提供网络虚拟化,并专注于具有异构端点和技术堆栈的新兴应用程序框架和体系结构。 除了 vSphere 管理程序之外,这些环境还包括其他虚拟机管理程序,例如 KVM、容器和裸机。

NSX-T Container Plug-in (NCP) 提供了 NSX-T 与容器协调器(例如 Kubernetes)之间的结合, 以及 NSX-T 与基于容器的 CaaS/PaaS 平台(例如 Pivotal Container Service(PKS)和 OpenShift)之间的集成。

OVN(开放式虚拟网络)

OVN 是一个由 Open vSwitch 社区开发的开源的网络虚拟化解决方案。 它允许创建逻辑交换器、逻辑路由、状态 ACL、负载均衡等等来建立不同的虚拟网络拓扑。 该项目在 ovn-kubernetes 提供特定的 Kubernetes 插件和文档。

Weaveworks 的 Weave Net

Weave Net 为 Kubernetes 及其托管应用提供的、弹性且易用的网络系统。 Weave Net 可以作为 CNI 插件 运行或者独立运行。 在这两种运行方式里,都不需要任何配置或额外的代码即可运行,并且在两种情况下, 网络都为每个 Pod 提供一个 IP 地址 -- 这是 Kubernetes 的标准配置。

接下来

网络模型的早期设计、运行原理以及未来的一些计划, 都在联网设计文档里有更详细的描述。

4 - Kubernetes 系统组件指标

通过系统组件指标可以更好地了解系统组个内部发生的情况。系统组件指标对于构建仪表板和告警特别有用。

Kubernetes 组件以 Prometheus 格式 生成度量值。 这种格式是结构化的纯文本,旨在使人和机器都可以阅读。

Kubernetes 中组件的指标

在大多数情况下,可以通过 HTTP 访问组件的 /metrics 端点来获取组件的度量值。 对于那些默认情况下不暴露端点的组件,可以使用 --bind-address 标志启用。

这些组件的示例:

在生产环境中,你可能需要配置 Prometheus 服务器 或 某些其他指标搜集器以定期收集这些指标,并使它们在某种时间序列数据库中可用。

请注意,kubelet 还会在 /metrics/cadvisor/metrics/resource/metrics/probes 端点中公开度量值。这些度量值的生命周期各不相同。

如果你的集群使用了 RBAC, 则读取指标需要通过基于用户、组或 ServiceAccount 的鉴权,要求具有允许访问 /metrics 的 ClusterRole。 例如:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
  - nonResourceURLs:
      - "/metrics"
    verbs:
      - get

指标生命周期

Alpha 指标 → 稳定的指标 → 弃用的指标 → 隐藏的指标 → 删除的指标

Alpha 指标没有稳定性保证。这些指标可以随时被修改或者删除。

稳定的指标可以保证不会改变。这意味着:

  • 稳定的、不包含已弃用(deprecated)签名的指标不会被删除(或重命名)
  • 稳定的指标的类型不会被更改

已弃用的指标最终将被删除,不过仍然可用。 这类指标包含注解,标明其被废弃的版本。

例如:

  • 被弃用之前:

    # HELP some_counter this counts things
    # TYPE some_counter counter
    some_counter 0
    
  • 被弃用之后:

    # HELP some_counter (Deprecated since 1.15.0) this counts things
    # TYPE some_counter counter
    some_counter 0
    

隐藏的指标不会再被发布以供抓取,但仍然可用。 要使用隐藏指标,请参阅显式隐藏指标节。

删除的指标不再被发布,亦无法使用。

显示隐藏指标

如上所述,管理员可以通过设置可执行文件的命令行参数来启用隐藏指标, 如果管理员错过了上一版本中已经弃用的指标的迁移,则可以把这个用作管理员的逃生门。

show-hidden-metrics-for-version 标志接受版本号作为取值,版本号给出 你希望显示该发行版本中已弃用的指标。 版本表示为 x.y,其中 x 是主要版本,y 是次要版本。补丁程序版本不是必须的, 即使指标可能会在补丁程序发行版中弃用,原因是指标弃用策略规定仅针对次要版本。

该参数只能使用前一个次要版本。如果管理员将先前版本设置为 show-hidden-metrics-for-version, 则先前版本中隐藏的度量值会再度生成。不允许使用过旧的版本,因为那样会违反指标弃用策略。

以指标 A 为例,此处假设 A 在 1.n 中已弃用。根据指标弃用策略,我们可以得出以下结论:

  • 在版本 1.n 中,这个指标已经弃用,且默认情况下可以生成。
  • 在版本 1.n+1 中,这个指标默认隐藏,可以通过命令行参数 show-hidden-metrics-for-version=1.n 来再度生成。
  • 在版本 1.n+2 中,这个指标就将被从代码中移除,不会再有任何逃生窗口。

如果你要从版本 1.12 升级到 1.13,但仍依赖于 1.12 中弃用的指标 A,则应通过命令行设置隐藏指标: --show-hidden-metrics=1.12,并记住在升级到 1.14 版本之前删除此指标依赖项。

禁用加速器指标

kubelet 通过 cAdvisor 收集加速器指标。为了收集这些指标,对于 NVIDIA GPU 之类的加速器, kubelet 在驱动程序上保持打开状态。这意味着为了执行基础结构更改(例如更新驱动程序), 集群管理员需要停止 kubelet 代理。

现在,收集加速器指标的责任属于供应商,而不是 kubelet。供应商必须提供一个收集指标的容器, 并将其公开给指标服务(例如 Prometheus)。

DisableAcceleratorUsageMetrics 特性门控 禁止由 kubelet 收集的指标。 关于何时会在默认情况下启用此功能也有一定规划

组件指标

kube-controller-manager 指标

控制器管理器指标可提供有关控制器管理器性能和运行状况的重要洞察。 这些指标包括通用的 Go 语言运行时指标(例如 go_routine 数量)和控制器特定的度量指标, 例如可用于评估集群运行状况的 etcd 请求延迟或云提供商(AWS、GCE、OpenStack)的 API 延迟等。

从 Kubernetes 1.7 版本开始,详细的云提供商指标可用于 GCE、AWS、Vsphere 和 OpenStack 的存储操作。 这些指标可用于监控持久卷操作的运行状况。

比如,对于 GCE,这些指标称为:

cloudprovider_gce_api_request_duration_seconds { request = "instance_list"}
cloudprovider_gce_api_request_duration_seconds { request = "disk_insert"}
cloudprovider_gce_api_request_duration_seconds { request = "disk_delete"}
cloudprovider_gce_api_request_duration_seconds { request = "attach_disk"}
cloudprovider_gce_api_request_duration_seconds { request = "detach_disk"}
cloudprovider_gce_api_request_duration_seconds { request = "list_disk"}

kube-scheduler 指标

特性状态: Kubernetes v1.21 [beta]

调度器会暴露一些可选的指标,报告所有运行中 Pods 所请求的资源和期望的约束值。 这些指标可用来构造容量规划监控面板、访问调度约束的当前或历史数据、 快速发现因为缺少资源而无法被调度的负载,或者将 Pod 的实际资源用量 与其请求值进行比较。

kube-scheduler 组件能够辩识各个 Pod 所配置的资源 请求和约束。 在 Pod 的资源请求值或者约束值非零时,kube-scheduler 会以度量值时间序列的形式 生成报告。该时间序列值包含以下标签:

  • 名字空间
  • Pod 名称
  • Pod 调度所处节点,或者当 Pod 未被调度时用空字符串表示
  • 优先级
  • 为 Pod 所指派的调度器
  • 资源的名称(例如,cpu
  • 资源的单位,如果知道的话(例如,cores

一旦 Pod 进入完成状态(其 restartPolicyNeverOnFailure,且 其处于 SucceededFailed Pod 阶段,或者已经被删除且所有容器都具有 终止状态),该时间序列停止报告,因为调度器现在可以调度其它 Pod 来执行。 这两个指标称作 kube_pod_resource_requestkube_pod_resource_limit

指标暴露在 HTTP 端点 /metrics/resources,与调度器上的 /metrics 端点 一样要求相同的访问授权。你必须使用 --show-hidden-metrics-for-version=1.20 标志才能暴露那些稳定性为 Alpha 的指标。

禁用指标

你可以通过命令行标志 --disabled-metrics 来关闭某指标。 在例如某指标会带来性能问题的情况下,这一操作可能是有用的。 标志的参数值是一组被禁止的指标(例如:--disabled-metrics=metric1,metric2)。

指标顺序性保证

在 Alpha 阶段,标志只能接受一组映射值作为可以使用的指标标签。 每个映射值的格式为<指标名称>,<标签名称>=<可用标签列表>,其中 <可用标签列表> 是一个用逗号分隔的、可接受的标签名的列表。

最终的格式看起来会是这样: --allow-label-value <指标名称>,<标签名称>='<可用值1>,<可用值2>...', <指标名称2>,<标签名称>='<可用值1>, <可用值2>...', ....

下面是一个例子:

--allow-label-value number_count_metric,odd_number='1,3,5', number_count_metric,even_number='2,4,6', date_gauge_metric,weekend='Saturday,Sunday'

接下来

5 - 日志架构

应用日志可以让你了解应用内部的运行状况。日志对调试问题和监控集群活动非常有用。 大部分现代化应用都有某种日志记录机制。同样地,容器引擎也被设计成支持日志记录。 针对容器化应用,最简单且最广泛采用的日志记录方式就是写入标准输出和标准错误流。

但是,由容器引擎或运行时提供的原生功能通常不足以构成完整的日志记录方案。 例如,如果发生容器崩溃、Pod 被逐出或节点宕机等情况,你可能想访问应用日志。 在集群中,日志应该具有独立的存储和生命周期,与节点、Pod 或容器的生命周期相独立。 这个概念叫 集群级的日志

集群级日志架构需要一个独立的后端用来存储、分析和查询日志。 Kubernetes 并不为日志数据提供原生的存储解决方案。 相反,有很多现成的日志方案可以集成到 Kubernetes 中。 下面各节描述如何在节点上处理和存储日志。

Kubernetes 中的基本日志记录

这里的示例使用包含一个容器的 Pod 规约,每秒钟向标准输出写入数据。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

用下面的命令运行 Pod:

kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml

输出结果为:

pod/counter created

像下面这样,使用 kubectl logs 命令获取日志:

kubectl logs counter

输出结果为:

0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...

你可以使用命令 kubectl logs --previous 检索之前容器实例的日志。 如果 Pod 中有多个容器,你应该为该命令附加容器名以访问对应容器的日志。 详见 kubectl logs 文档。 如果 Pod 有多个容器,你应该为该命令附加容器名以访问对应容器的日志, 使用 -c 标志来指定要访问的容器的日志,如下所示:

kubectl logs counter -c count

详见 kubectl logs 文档

节点级日志记录

节点级别的日志记录

容器化应用写入 stdoutstderr 的任何数据,都会被容器引擎捕获并被重定向到某个位置。 例如,Docker 容器引擎将这两个输出流重定向到某个 日志驱动(Logging Driver) , 该日志驱动在 Kubernetes 中配置为以 JSON 格式写入文件。

默认情况下,如果容器重启,kubelet 会保留被终止的容器日志。 如果 Pod 在工作节点被驱逐,该 Pod 中所有的容器也会被驱逐,包括容器日志。

节点级日志记录中,需要重点考虑实现日志的轮转,以此来保证日志不会消耗节点上全部可用空间。 Kubernetes 并不负责轮转日志,而是通过部署工具建立一个解决问题的方案。 例如,在用 kube-up.sh 部署的 Kubernetes 集群中,存在一个 logrotate,每小时运行一次。 你也可以设置容器运行时来自动地轮转应用日志。

例如,你可以找到关于 kube-up.sh 为 GCP 环境的 COS 镜像设置日志的详细信息, 脚本为 configure-helper 脚本

当使用某 CRI 容器运行时 时,kubelet 要负责对日志进行轮换,并 管理日志目录的结构。kubelet 将此信息发送给 CRI 容器运行时,后者 将容器日志写入到指定的位置。在 kubelet 配置文件 中的两个 kubelet 参数 containerLogMaxSizecontainerLogMaxFiles 可以用来配置每个日志文件的最大长度和每个容器可以生成的日志文件个数上限。

当运行 kubectl logs 时, 节点上的 kubelet 处理该请求并直接读取日志文件,同时在响应中返回日志文件内容。

系统组件日志

系统组件有两种类型:在容器中运行的和不在容器中运行的。例如:

  • 在容器中运行的 kube-scheduler 和 kube-proxy。
  • 不在容器中运行的 kubelet 和容器运行时。

在使用 systemd 机制的服务器上,kubelet 和容器运行时将日志写入到 journald 中。 如果没有 systemd,它们将日志写入到 /var/log 目录下的 .log 文件中。 容器中的系统组件通常将日志写到 /var/log 目录,绕过了默认的日志机制。 他们使用 klog 日志库。 你可以在日志开发文档 找到这些组件的日志告警级别约定。

和容器日志类似,/var/log 目录中的系统组件日志也应该被轮转。 通过脚本 kube-up.sh 启动的 Kubernetes 集群中,日志被工具 logrotate 执行每日轮转,或者日志大小超过 100MB 时触发轮转。

集群级日志架构

虽然Kubernetes没有为集群级日志记录提供原生的解决方案,但你可以考虑几种常见的方法。 以下是一些选项:

  • 使用在每个节点上运行的节点级日志记录代理。
  • 在应用程序的 Pod 中,包含专门记录日志的边车(Sidecar)容器。
  • 将日志直接从应用程序中推送到日志记录后端。

使用节点级日志代理

使用节点日志记录代理

你可以通过在每个节点上使用 节点级的日志记录代理 来实现集群级日志记录。 日志记录代理是一种用于暴露日志或将日志推送到后端的专用工具。 通常,日志记录代理程序是一个容器,它可以访问包含该节点上所有应用程序容器的日志文件的目录。

由于日志记录代理必须在每个节点上运行,通常可以用 DaemonSet 的形式运行该代理。 节点级日志在每个节点上仅创建一个代理,不需要对节点上的应用做修改。

容器向标准输出和标准错误输出写出数据,但在格式上并不统一。 节点级代理 收集这些日志并将其进行转发以完成汇总。

使用 sidecar 容器运行日志代理

你可以通过以下方式之一使用边车(Sidecar)容器:

  • 边车容器将应用程序日志传送到自己的标准输出。
  • 边车容器运行一个日志代理,配置该日志代理以便从应用容器收集日志。

传输数据流的 sidecar 容器

带数据流容器的边车容器

利用边车容器向自己的 stdoutstderr 传输流的方式, 你就可以利用每个节点上的 kubelet 和日志代理来处理日志。 边车容器从文件、套接字或 journald 读取日志。 每个边车容器向自己的 stdoutstderr 流中输出日志。

这种方法允许你将日志流从应用程序的不同部分分离开,其中一些可能缺乏对写入 stdoutstderr 的支持。重定向日志背后的逻辑是最小的,因此它的开销几乎可以忽略不计。 另外,因为 stdoutstderr 由 kubelet 处理,你可以使用内置的工具 kubectl logs

例如,某 Pod 中运行一个容器,该容器向两个文件写不同格式的日志。 下面是这个 pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

不建议在同一个日志流中写入不同格式的日志条目,即使你成功地将其重定向到容器的 stdout 流。相反,你可以创建两个边车容器。每个边车容器可以从共享卷 跟踪特定的日志文件,并将文件内容重定向到各自的 stdout 流。

下面是运行两个边车容器的 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-1
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-2
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/2.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

现在当你运行这个 Pod 时,你可以运行如下命令分别访问每个日志流:

kubectl logs counter count-log-1

输出为:

0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...
kubectl logs counter count-log-2

输出为:

Mon Jan  1 00:00:00 UTC 2001 INFO 0
Mon Jan  1 00:00:01 UTC 2001 INFO 1
Mon Jan  1 00:00:02 UTC 2001 INFO 2
...

集群中安装的节点级代理会自动获取这些日志流,而无需进一步配置。 如果你愿意,你也可以配置代理程序来解析源容器的日志行。

注意,尽管 CPU 和内存使用率都很低(以多个 CPU 毫核指标排序或者按内存的兆字节排序), 向文件写日志然后输出到 stdout 流仍然会成倍地增加磁盘使用率。 如果你的应用向单一文件写日志,通常最好设置 /dev/stdout 作为目标路径, 而不是使用流式的边车容器方式。

应用本身如果不具备轮转日志文件的功能,可以通过边车容器实现。 该方式的一个例子是运行一个小的、定期轮转日志的容器。 然而,还是推荐直接使用 stdoutstderr,将日志的轮转和保留策略 交给 kubelet。

具有日志代理功能的边车容器

含日志代理的边车容器

如果节点级日志记录代理程序对于你的场景来说不够灵活,你可以创建一个 带有单独日志记录代理的边车容器,将代理程序专门配置为与你的应用程序一起运行。

下面是两个配置文件,可以用来实现一个带日志代理的边车容器。 第一个文件包含用来配置 fluentd 的 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluentd.conf: |
    <source>
      type tail
      format none
      path /var/log/1.log
      pos_file /var/log/1.log.pos
      tag count.format1
    </source>

    <source>
      type tail
      format none
      path /var/log/2.log
      pos_file /var/log/2.log.pos
      tag count.format2
    </source>

    <match **>
      type google_cloud
    </match>    

第二个文件描述了运行 fluentd 边车容器的 Pod 。 flutend 通过 Pod 的挂载卷获取它的配置数据。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-agent
    image: k8s.gcr.io/fluentd-gcp:1.30
    env:
    - name: FLUENTD_ARGS
      value: -c /etc/fluentd-config/fluentd.conf
    volumeMounts:
    - name: varlog
      mountPath: /var/log
    - name: config-volume
      mountPath: /etc/fluentd-config
  volumes:
  - name: varlog
    emptyDir: {}
  - name: config-volume
    configMap:
      name: fluentd-config

在示例配置中,你可以将 fluentd 替换为任何日志代理,从应用容器内 的任何来源读取数据。

从应用中直接暴露日志目录

直接从应用程序暴露日志

从各个应用中直接暴露和推送日志数据的集群日志机制 已超出 Kubernetes 的范围。

6 - 系统日志

系统组件的日志记录集群中发生的事件,这对于调试非常有用。 你可以配置日志的精细度,以展示更多或更少的细节。 日志可以是粗粒度的,如只显示组件内的错误, 也可以是细粒度的,如显示事件的每一个跟踪步骤(比如 HTTP 访问日志、pod 状态更新、控制器动作或调度器决策)。

Klog

klog 是 Kubernetes 的日志库。 klog 为 Kubernetes 系统组件生成日志消息。

有关 klog 配置的更多信息,请参见命令行工具参考

Kubernetes 正在进行简化其组件日志的努力。下面的 klog 命令行参数从 Kubernetes 1.23 开始已被废弃, 会在未来版本中移除:

  • --add-dir-header
  • --alsologtostderr
  • --log-backtrace-at
  • --log-dir
  • --log-file
  • --log-file-max-size
  • --logtostderr
  • --one-output
  • --skip-headers
  • --skip-log-headers
  • --stderrthreshold

输出总会被写到标准错误输出(stderr)之上,无论输出格式如何。 对输出的重定向将由调用 Kubernetes 组件的软件来处理。 这一软件可以是 POSIX Shell 或者类似 systemd 这样的工具。

在某些场合下,例如对于无发行主体的(distroless)容器或者 Windows 系统服务, 这些替代方案都是不存在的。那么你可以使用 kube-log-runner 可执行文件来作为 Kubernetes 的封装层,完成对输出的重定向。 在很多 Kubernetes 基础镜像中,都包含一个预先构建的可执行程序。 这个程序原来称作 /go-runner,而在服务器和节点的发行版本库中,称作 kube-log-runner

下表展示的是 kube-log-runner 调用与 Shell 重定向之间的对应关系:

用法POSIX Shell(例如 Bash)kube-log-runner <options> <cmd>
合并 stderr 与 stdout,写出到 stdout2>&1kube-log-runner(默认行为 )
将 stderr 与 stdout 重定向到日志文件1>>/tmp/log 2>&1kube-log-runner -log-file=/tmp/log
输出到 stdout 并复制到日志文件中2>&1 | tee -a /tmp/logkube-log-runner -log-file=/tmp/log -also-stdout
仅将 stdout 重定向到日志>/tmp/logkube-log-runner -log-file=/tmp/log -redirect-stderr=false

klog 输出

传统的 klog 原生格式示例:

I1025 00:15:15.525108       1 httplog.go:79] GET /api/v1/namespaces/kube-system/pods/metrics-server-v0.3.1-57c75779f-9p8wg: (1.512ms) 200 [pod_nanny/v0.0.0 (linux/amd64) kubernetes/$Format 10.56.1.19:51756]

消息字符串可能包含换行符:

I1025 00:15:15.525108       1 example.go:79] This is a message
which has a line break.

结构化日志

特性状态: Kubernetes v1.23 [beta]

结构化日志记录旨在日志消息中引入统一结构,以便以编程方式提取信息。 你可以方便地用更小的开销来处理结构化日志。 生成日志消息的代码决定其使用传统的非结构化的 klog 还是结构化的日志。

默认的结构化日志消息是以文本形式呈现的,其格式与传统的 klog 保持向后兼容:

<klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...

示例:

I1025 00:15:15.525108       1 controller_utils.go:116] "Pod status updated" pod="kube-system/kubedns" status="ready"

字符串在输出时会被添加引号。其他数值类型都使用 %+v 来格式化,因此可能导致日志消息会延续到下一行, 具体取决于数据本身

I1025 00:15:15.525108       1 example.go:116] "Example" data="This is text with a line break\nand \"quotation marks\"." someInt=1 someFloat=0.1 someStruct={StringField: First line,
second line.}

上下文日志

特性状态: Kubernetes v1.24 [alpha]

上下文日志建立在结构化日志之上。 它主要是关于开发人员如何使用日志记录调用:基于该概念的代码将更加灵活, 并且支持在结构化日志 KEP 中描述的额外用例。

如果开发人员在他们的组件中使用额外的函数,比如 WithValuesWithName, 那么日志条目将会包含额外的信息,这些信息会被调用者传递给函数。

目前这一特性是由 StructuredLogging 特性门控所控制的,默认关闭。 这个基础设施是在 1.24 中被添加的,并不需要修改组件。 该 component-base/logs/example 命令演示了如何使用新的日志记录调用以及组件如何支持上下文日志记录。

$ cd $GOPATH/src/k8s.io/kubernetes/staging/src/k8s.io/component-base/logs/example/cmd/
$ go run . --help
...
      --feature-gates mapStringBool  A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
                                     AllAlpha=true|false (ALPHA - default=false)
                                     AllBeta=true|false (BETA - default=false)
                                     ContextualLogging=true|false (ALPHA - default=false)
$ go run . --feature-gates ContextualLogging=true
...
I0404 18:00:02.916429  451895 logger.go:94] "example/myname: runtime" foo="bar" duration="1m0s"
I0404 18:00:02.916447  451895 logger.go:95] "example: another runtime" foo="bar" duration="1m0s"

example 前缀和 foo="bar" 会被函数的调用者添加上, 不需修改该函数,它就会记录 runtime 消息和 duration="1m0s" 值。

禁用上下文日志后,WithValuesWithName 什么都不会做, 并且会通过调用全局的 klog 日志记录器记录日志。 因此,这些附加信息不再出现在日志输出中:

$ go run . --feature-gates ContextualLogging=false
...
I0404 18:03:31.171945  452150 logger.go:94] "runtime" duration="1m0s"
I0404 18:03:31.171962  452150 logger.go:95] "another runtime" duration="1m0s"

JSON 日志格式

特性状态: Kubernetes v1.19 [alpha]

--logging-format=json 参数将日志格式从 klog 原生格式改为 JSON 格式。 JSON 日志格式示例(美化输出):

{
   "ts": 1580306777.04728,
   "v": 4,
   "msg": "Pod status updated",
   "pod":{
      "name": "nginx-1",
      "namespace": "default"
   },
   "status": "ready"
}

具有特殊意义的 key:

  • ts - Unix 时间风格的时间戳(必选项,浮点值)
  • v - 精细度(仅用于 info 级别,不能用于错误信息,整数)
  • err - 错误字符串(可选项,字符串)
  • msg - 消息(必选项,字符串)

当前支持JSON格式的组件列表:

日志精细度级别

参数 -v 控制日志的精细度。增大该值会增大日志事件的数量。 减小该值可以减小日志事件的数量。增大精细度会记录更多的不太严重的事件。 精细度设置为 0 时只记录关键(critical)事件。

日志位置

有两种类型的系统组件:运行在容器中的组件和不运行在容器中的组件。例如:

  • Kubernetes 调度器和 kube-proxy 在容器中运行。
  • kubelet 和容器运行时不在容器中运行。

在使用 systemd 的系统中,kubelet 和容器运行时写入 journald。 在别的系统中,日志写入 /var/log 目录下的 .log 文件中。 容器中的系统组件总是绕过默认的日志记录机制,写入 /var/log 目录下的 .log 文件。 与容器日志类似,你应该轮转 /var/log 目录下系统组件日志。 在 kube-up.sh 脚本创建的 Kubernetes 集群中,日志轮转由 logrotate 工具配置。 logrotate 工具,每天或者当日志大于 100MB 时,轮转日志。

接下来

7 - 追踪 Kubernetes 系统组件

特性状态: Kubernetes v1.22 [alpha]

系统组件追踪功能记录各个集群操作的时延信息和这些操作之间的关系。

Kubernetes 组件基于 gRPC 导出器的 OpenTelemetry 协议 发送追踪信息,并用 OpenTelemetry Collector 收集追踪信息,再将其转交给追踪系统的后台。

追踪信息的收集

关于收集追踪信息、以及使用收集器的完整指南,可参见 Getting Started with the OpenTelemetry Collector。 不过,还有一些特定于 Kubernetes 组件的事项值得注意。

默认情况下,Kubernetes 组件使用 gRPC 的 OTLP 导出器来导出追踪信息,将信息写到 IANA OpenTelemetry 端口。 举例来说,如果收集器以 Kubernetes 组件的边车模式运行,以下接收器配置会收集 spans 信息,并将它们写入到标准输出。

receivers:
  otlp:
    protocols:
      grpc:
exporters:
  # 用适合你后端环境的导出器替换此处的导出器
  logging:
    logLevel: debug
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [logging]

组件追踪

kube-apiserver 追踪

kube-apiserver 为传入的 HTTP 请求、传出到 webhook 和 etcd 的请求以及重入的请求生成 spans。 由于 kube-apiserver 通常是一个公开的端点,所以它通过出站的请求传播 W3C 追踪上下文, 但不使用入站请求的追踪上下文。

在 kube-apiserver 中启用追踪

要启用追踪特性,需要启用 kube-apiserver 上的 APIServerTracing 特性门控。 然后,使用 --tracing-config-file=<<配置文件路径> 为 kube-apiserver 提供追踪配置文件。 下面是一个示例配置,它为万分之一的请求记录 spans,并使用了默认的 OpenTelemetry 端口。

apiVersion: apiserver.config.k8s.io/v1alpha1
kind: TracingConfiguration
# default value
#endpoint: localhost:4317
samplingRatePerMillion: 100

有关 TracingConfiguration 结构体的更多信息,请参阅 API 服务器配置 API (v1alpha1)

稳定性

追踪工具仍在积极开发中,未来它会以多种方式发生变化。 这些变化包括:span 名称、附加属性、检测端点等等。 此类特性在达到稳定版本之前,不能保证追踪工具的向后兼容性。

接下来

8 - Kubernetes 中的代理

本文讲述了 Kubernetes 中所使用的代理。

代理

用户在使用 Kubernetes 的过程中可能遇到几种不同的代理(proxy):

  1. kubectl proxy

    • 运行在用户的桌面或 pod 中
    • 从本机地址到 Kubernetes apiserver 的代理
    • 客户端到代理使用 HTTP 协议
    • 代理到 apiserver 使用 HTTPS 协议
    • 指向 apiserver
    • 添加认证头信息
  1. apiserver proxy

    • 是一个建立在 apiserver 内部的“堡垒”
    • 将集群外部的用户与集群 IP 相连接,这些IP是无法通过其他方式访问的
    • 运行在 apiserver 进程内
    • 客户端到代理使用 HTTPS 协议 (如果配置 apiserver 使用 HTTP 协议,则使用 HTTP 协议)
    • 通过可用信息进行选择,代理到目的地可能使用 HTTP 或 HTTPS 协议
    • 可以用来访问 Node、 Pod 或 Service
    • 当用来访问 Service 时,会进行负载均衡
  1. kube proxy

    • 在每个节点上运行
    • 代理 UDP、TCP 和 SCTP
    • 不支持 HTTP
    • 提供负载均衡能力
    • 只用来访问 Service
  1. apiserver 之前的代理/负载均衡器:

    • 在不同集群中的存在形式和实现不同 (如 nginx)
    • 位于所有客户端和一个或多个 API 服务器之间
    • 存在多个 API 服务器时,扮演负载均衡器的角色
  1. 外部服务的云负载均衡器:

    • 由一些云供应商提供 (如 AWS ELB、Google Cloud Load Balancer)
    • Kubernetes 服务类型为 LoadBalancer 时自动创建
    • 通常仅支持 UDP/TCP 协议
    • SCTP 支持取决于云供应商的负载均衡器实现
    • 不同云供应商的云负载均衡器实现不同

Kubernetes 用户通常只需要关心前两种类型的代理,集群管理员通常需要确保后面几种类型的代理设置正确。

请求重定向

代理已经取代重定向功能,重定向功能已被弃用。

9 - API 优先级和公平性

特性状态: Kubernetes v1.20 [beta]

对于集群管理员来说,控制 Kubernetes API 服务器在过载情况下的行为是一项关键任务。 kube-apiserver 有一些控件(例如:命令行标志 --max-requests-inflight--max-mutating-requests-inflight ), 可以限制将要接受的未处理的请求,从而防止过量请求入站,潜在导致 API 服务器崩溃。 但是这些标志不足以保证在高流量期间,最重要的请求仍能被服务器接受。

API 优先级和公平性(APF)是一种替代方案,可提升上述最大并发限制。 APF 以更细粒度的方式对请求进行分类和隔离。 它还引入了空间有限的排队机制,因此在非常短暂的突发情况下,API 服务器不会拒绝任何请求。 通过使用公平排队技术从队列中分发请求,这样, 一个行为不佳的 控制器 就不会饿死其他控制器(即使优先级相同)。

本功能特性在设计上期望其能与标准控制器一起工作得很好; 这类控制器使用通知组件(Informers)获得信息并对 API 请求的失效作出反应, 在处理失效时能够执行指数型回退。其他客户端也以类似方式工作。

启用/禁用 API 优先级和公平性

API 优先级与公平性(APF)特性由特性门控控制,默认情况下启用。 有关特性门控的一般性描述以及如何启用和禁用特性门控, 请参见特性门控。 APF 的特性门控称为 APIPriorityAndFairness。 此特性也与某个 API 组 相关: (a) v1alpha1 版本,默认被禁用; (b) v1beta1v1beta2 版本,默认被启用。 你可以在启动 kube-apiserver 时,添加以下命令行标志来禁用此功能门控及 API Beta 组:

kube-apiserver \
--feature-gates=APIPriorityAndFairness=false \
--runtime-config=flowcontrol.apiserver.k8s.io/v1beta1=false,flowcontrol.apiserver.k8s.io/v1beta2=false \
  # ...其他配置不变

或者,你也可以通过 --runtime-config=flowcontrol.apiserver.k8s.io/v1alpha1=true 启用 API 组的 v1alpha1 版本。

命令行标志 --enable-priority-fairness=false 将彻底禁用 APF 特性,即使其他标志启用它也是无效。

概念

APF 特性包含几个不同的功能。 传入的请求通过 FlowSchema 按照其属性分类,并分配优先级。 每个优先级维护自定义的并发限制,加强了隔离度,这样不同优先级的请求,就不会相互饿死。 在同一个优先级内,公平排队算法可以防止来自不同 flow 的请求相互饿死。 该算法将请求排队,通过排队机制,防止在平均负载较低时,通信量突增而导致请求失败。

优先级

如果未启用 APF,API 服务器中的整体并发量将受到 kube-apiserver 的参数 --max-requests-inflight--max-mutating-requests-inflight 的限制。 启用 APF 后,将对这些参数定义的并发限制进行求和,然后将总和分配到一组可配置的 优先级 中。 每个传入的请求都会分配一个优先级;每个优先级都有各自的配置,设定允许分发的并发请求数。

例如,默认配置包括针对领导者选举请求、内置控制器请求和 Pod 请求都单独设置优先级。 这表示即使异常的 Pod 向 API 服务器发送大量请求,也无法阻止领导者选举或内置控制器的操作执行成功。

排队

即使在同一优先级内,也可能存在大量不同的流量源。 在过载情况下,防止一个请求流饿死其他流是非常有价值的 (尤其是在一个较为常见的场景中,一个有故障的客户端会疯狂地向 kube-apiserver 发送请求, 理想情况下,这个有故障的客户端不应对其他客户端产生太大的影响)。 公平排队算法在处理具有相同优先级的请求时,实现了上述场景。 每个请求都被分配到某个 中,该 由对应的 FlowSchema 的名字加上一个 流区分项(Flow Distinguisher) 来标识。 这里的流区分项可以是发出请求的用户、目标资源的名称空间或什么都不是。 系统尝试为不同流中具有相同优先级的请求赋予近似相等的权重。 要启用对不同实例的不同处理方式,多实例的控制器要分别用不同的用户名来执行身份认证。

将请求划分到流中之后,APF 功能将请求分配到队列中。 分配时使用一种称为 混洗分片(Shuffle-Sharding) 的技术。 该技术可以相对有效地利用队列隔离低强度流与高强度流。

排队算法的细节可针对每个优先等级进行调整,并允许管理员在内存占用、 公平性(当总流量超标时,各个独立的流将都会取得进展)、 突发流量的容忍度以及排队引发的额外延迟之间进行权衡。

豁免请求

某些特别重要的请求不受制于此特性施加的任何限制。这些豁免可防止不当的流控配置完全禁用 API 服务器。

资源

流控 API 涉及两种资源。 PriorityLevelConfigurations 定义隔离类型和可处理的并发预算量,还可以微调排队行为。 FlowSchemas 用于对每个入站请求进行分类,并与一个 PriorityLevelConfigurations 相匹配。 此外同一 API 组还有一个 v1alpha1 版本,其中包含语法和语义都相同的资源类别。

PriorityLevelConfiguration

一个 PriorityLevelConfiguration 表示单个隔离类型。每个 PriorityLevelConfigurations 对未完成的请求数有各自的限制,对排队中的请求数也有限制。

PriorityLevelConfigurations 的并发限制不是指定请求绝对数量,而是在“并发份额”中指定。 API 服务器的总并发量限制通过这些份额按例分配到现有 PriorityLevelConfigurations 中。 集群管理员可以更改 --max-requests-inflight (或 --max-mutating-requests-inflight )的值, 再重新启动 kube-apiserver 来增加或减小服务器的总流量, 然后所有的 PriorityLevelConfigurations 将看到其最大并发增加(或减少)了相同的比例。

当入站请求的数量大于分配的 PriorityLevelConfigurations 中允许的并发级别时, type 字段将确定对额外请求的处理方式。 Reject 类型,表示多余的流量将立即被 HTTP 429(请求过多)错误所拒绝。 Queue 类型,表示对超过阈值的请求进行排队,将使用阈值分片和公平排队技术来平衡请求流之间的进度。

公平排队算法支持通过排队配置对优先级微调。 可以在增强建议中阅读算法的详细信息,但总之:

  • queues 递增能减少不同流之间的冲突概率,但代价是增加了内存使用量。 值为 1 时,会禁用公平排队逻辑,但仍允许请求排队。
  • queueLengthLimit 递增可以在不丢弃任何请求的情况下支撑更大的突发流量, 但代价是增加了等待时间和内存使用量。
  • 修改 handSize 允许你调整过载情况下不同流之间的冲突概率以及单个流可用的整体并发性。

下表显示了有趣的随机分片配置集合, 每行显示给定的老鼠(低强度流)被不同数量的大象挤压(高强度流)的概率。 表来源请参阅: https://play.golang.org/p/Gi0PLgVHiUg

Example Shuffle Sharding Configurations
随机分片队列数1 个大象4 个大象16 个大象
12324.428838398950118e-090.114313488300991440.9935089607656024
10321.550093439632541e-080.06264798402235450.9753101519027554
10646.601827268370426e-120.000455713209903707760.49999929150089345
9643.6310049976037345e-110.000455012123041122730.4282314876454858
8642.25929199850899e-100.00048866970530404460.35935114681123076
81286.994461389026097e-133.4055790161620863e-060.02746173137155063
71281.0579122850901972e-116.960839379258192e-060.02406157386340147
72567.597695465552631e-146.728547142019406e-080.0006709661542533682
62562.7134626662687968e-122.9516464018476436e-070.0008895654642000348
65124.116062922897309e-144.982983350480894e-092.26025764343413e-05
610246.337324016514285e-168.09060164312957e-114.517408062903668e-07

FlowSchema

FlowSchema 匹配一些入站请求,并将它们分配给优先级。 每个入站请求都会对所有 FlowSchema 测试是否匹配, 首先从 matchingPrecedence 数值最低的匹配开始(我们认为这是逻辑上匹配度最高), 然后依次进行,直到首个匹配出现。

当给定的请求与某个 FlowSchema 的 rules 的其中一条匹配,那么就认为该请求与该 FlowSchema 匹配。 判断规则与该请求是否匹配,不仅要求该条规则的 subjects 字段至少存在一个与该请求相匹配, 而且要求该条规则的 resourceRulesnonResourceRules (取决于传入请求是针对资源URL还是非资源URL)字段至少存在一个与该请求相匹配。

对于 subjects 中的 name 字段和资源和非资源规则的 verbsapiGroupsresourcesnamespacesnonResourceURLs 字段, 可以指定通配符 * 来匹配任意值,从而有效地忽略该字段。

FlowSchema 的 distinguisherMethod.type 字段决定了如何把与该模式匹配的请求分散到各个流中。 可能是 ByUser ,在这种情况下,一个请求用户将无法饿死其他容量的用户; 或者是 ByNamespace ,在这种情况下,一个名称空间中的资源请求将无法饿死其它名称空间的资源请求; 或者它可以为空(或者可以完全省略 distinguisherMethod), 在这种情况下,与此 FlowSchema 匹配的请求将被视为单个流的一部分。 资源和你的特定环境决定了如何选择正确一个 FlowSchema。

默认值

每个 kube-apiserver 会维护两种类型的 APF 配置对象:强制的(Mandatory)和建议的(Suggested)。

强制的配置对象

有四种强制的配置对象对应内置的守护行为。这里的行为是服务器在还未创建对象之前就具备的行为, 而当这些对象存在时,其规约反映了这类行为。四种强制的对象如下:

  • 强制的 exempt 优先级用于完全不受流控限制的请求:它们总是立刻被分发。 强制的 exempt FlowSchema 把 system:masters 组的所有请求都归入该优先级。 如果合适,你可以定义新的 FlowSchema,将其他请求定向到该优先级。
  • 强制的 catch-all 优先级与强制的 catch-all FlowSchema 结合使用, 以确保每个请求都分类。一般而言,你不应该依赖于 catch-all 的配置, 而应适当地创建自己的 catch-all FlowSchema 和 PriorityLevelConfiguration (或使用默认安装的 global-default 配置)。 因为这一优先级不是正常场景下要使用的,catch-all 优先级的并发度份额很小, 并且不会对请求进行排队。

建议的配置对象

建议的 FlowSchema 和 PriorityLevelConfiguration 包含合理的默认配置。 你可以修改这些对象或者根据需要创建新的配置对象。如果你的集群可能承受较重负载, 那么你就要考虑哪种配置最合适。

建议的配置把请求分为六个优先级:

  • node-high 优先级用于来自节点的健康状态更新。
  • system 优先级用于 system:nodes 组(即 kubelet)的与健康状态更新无关的请求; kubelets 必须能连上 API 服务器,以便工作负载能够调度到其上。
  • leader-election 优先级用于内置控制器的领导选举的请求 (特别是来自 kube-system 名称空间中 system:kube-controller-managersystem:kube-scheduler 用户和服务账号,针对 endpointsconfigmapsleases 的请求)。 将这些请求与其他流量相隔离非常重要,因为领导者选举失败会导致控制器发生故障并重新启动, 这反过来会导致新启动的控制器在同步信息时,流量开销更大。
  • workload-high 优先级用于内置控制器的其他请求。
  • workload-low 优先级用于来自所有其他服务帐户的请求,通常包括来自 Pod 中运行的控制器的所有请求。
  • global-default 优先级可处理所有其他流量,例如:非特权用户运行的交互式 kubectl 命令。

建议的 FlowSchema 用来将请求导向上述的优先级内,这里不再一一列举。

强制的与建议的配置对象的维护

每个 kube-apiserver 都独立地维护其强制的与建议的配置对象, 这一维护操作既是服务器的初始行为,也是其周期性操作的一部分。 因此,当存在不同版本的服务器时,如果各个服务器对于配置对象中的合适内容有不同意见, 就可能出现抖动。

每个 kube-apiserver 都会对强制的与建议的配置对象执行初始的维护操作, 之后(每分钟)对这些对象执行周期性的维护。

对于强制的配置对象,维护操作包括确保对象存在并且包含合适的规约(如果存在的话)。 服务器会拒绝创建或更新与其守护行为不一致的规约。

对建议的配置对象的维护操作被设计为允许其规约被重载。删除操作是不允许的, 维护操作期间会重建这类配置对象。如果你不需要某个建议的配置对象, 你需要将它放在一边,并让其规约所产生的影响最小化。 对建议的配置对象而言,其维护方面的设计也支持在上线新的 kube-apiserver 时完成自动的迁移动作,即便可能因为当前的服务器集合存在不同的版本而可能造成抖动仍是如此。

对建议的配置对象的维护操作包括基于服务器建议的规约创建对象 (如果对象不存在的话)。反之,如果对象已经存在,维护操作的行为取决于是否 kube-apiserver 或者用户在控制对象。如果 kube-apiserver 在控制对象, 则服务器确保对象的规约与服务器所给的建议匹配,如果用户在控制对象, 对象的规约保持不变。

关于谁在控制对象这个问题,首先要看对象上的 apf.kubernetes.io/autoupdate-spec 注解。如果对象上存在这个注解,并且其取值为true,则 kube-apiserver 在控制该对象。如果存在这个注解,并且其取值为false,则用户在控制对象。 如果这两个条件都不满足,则需要进一步查看对象的 metadata.generation。 如果该值为 1,则 kube-apiserver 控制对象,否则用户控制对象。 这些规则是在 1.22 发行版中引入的,而对 metadata.generation 的考量是为了便于从之前较简单的行为迁移过来。希望控制建议的配置对象的用户应该将对象的 apf.kubernetes.io/autoupdate-spec 注解设置为 false

对强制的或建议的配置对象的维护操作也包括确保对象上存在 apf.kubernetes.io/autoupdate-spec 这一注解,并且其取值准确地反映了是否 kube-apiserver 在控制着对象。

维护操作还包括删除那些既非强制又非建议的配置,同时注解配置为 apf.kubernetes.io/autoupdate-spec=true 的对象。

健康检查并发豁免

推荐配置没有为本地 kubelet 对 kube-apiserver 执行健康检查的请求进行任何特殊处理 ——它们倾向于使用安全端口,但不提供凭据。 在推荐配置中,这些请求将分配 global-default FlowSchema 和 global-default 优先级, 这样其他流量可以排除健康检查。

如果添加以下 FlowSchema,健康检查请求不受速率限制。

apiVersion: flowcontrol.apiserver.k8s.io/v1beta2
kind: FlowSchema
metadata:
  name: health-for-strangers
spec:
  matchingPrecedence: 1000
  priorityLevelConfiguration:
    name: exempt
  rules:
  - nonResourceRules:
    - nonResourceURLs:
      - "/healthz"
      - "/livez"
      - "/readyz"
      verbs:
      - "*"
    subjects:
    - kind: Group
      group:
        name: system:unauthenticated

问题诊断

启用了 APF 的 API 服务器,它每个 HTTP 响应都有两个额外的 HTTP 头: X-Kubernetes-PF-FlowSchema-UIDX-Kubernetes-PF-PriorityLevel-UID, 注意与请求匹配的 FlowSchema 和已分配的优先级。 如果请求用户没有查看这些对象的权限,则这些 HTTP 头中将不包含 API 对象的名称, 因此在调试时,你可以使用类似如下的命令:

kubectl get flowschemas -o custom-columns="uid:{metadata.uid},name:{metadata.name}"
kubectl get prioritylevelconfigurations -o custom-columns="uid:{metadata.uid},name:{metadata.name}"

来获取 UID 到 FlowSchema 的名称和 UID 到 PriorityLevelConfigurations 的名称的映射。

可观察性

指标

当你开启了 APF 后,kube-apiserver 会暴露额外指标。 监视这些指标有助于判断你的配置是否不当地限制了重要流量, 或者发现可能会损害系统健康的,行为不良的工作负载。

  • apiserver_flowcontrol_rejected_requests_total 是一个计数器向量, 记录被拒绝的请求数量(自服务器启动以来累计值), 由标签 flow_chema(表示与请求匹配的 FlowSchema),priority_evel (表示分配给请该求的优先级)和 reason 来区分。 reason 标签将具有以下值之一:
    • queue-full ,表明已经有太多请求排队,
    • concurrency-limit,表示将 PriorityLevelConfiguration 配置为 Reject 而不是 Queue ,或者
    • time-out, 表示在其排队时间超期的请求仍在队列中。
  • apiserver_flowcontrol_dispatched_requests_total 是一个计数器向量, 记录开始执行的请求数量(自服务器启动以来的累积值), 由标签 flow_schema(表示与请求匹配的 FlowSchema)和 priority_level(表示分配给该请求的优先级)来区分。
  • apiserver_current_inqueue_requests 是一个表向量, 记录最近排队请求数量的高水位线, 由标签 request_kind 分组,标签的值为 mutatingreadOnly。 这些高水位线表示在最近一秒钟内看到的最大数字。 它们补充说明了老的表向量 apiserver_current_inflight_requests (该量保存了最后一个窗口中,正在处理的请求数量的高水位线)。
  • apiserver_flowcontrol_read_vs_write_request_count_samples 是一个直方图向量, 记录当前请求数量的观察值, 由标签 phase(取值为 waitingexecuting)和 request_kind (取值 mutatingreadOnly)拆分。定期以高速率观察该值。
  • apiserver_flowcontrol_read_vs_write_request_count_watermarks 是一个直方图向量, 记录请求数量的高/低水位线, 由标签 phase(取值为 waitingexecuting)和 request_kind (取值为 mutatingreadOnly)拆分;标签 mark 取值为 highlowapiserver_flowcontrol_read_vs_write_request_count_samples 向量观察到有值新增, 则该向量累积。这些水位线显示了样本值的范围。
  • apiserver_flowcontrol_current_inqueue_requests 是一个表向量, 记录包含排队中的(未执行)请求的瞬时数量, 由标签 priorityLevelflowSchema 拆分。
  • apiserver_flowcontrol_current_executing_requests 是一个表向量, 记录包含执行中(不在队列中等待)请求的瞬时数量, 由标签 priority_levelflow_schema 进一步区分。
  • apiserver_flowcontrol_request_concurrency_in_use 是一个规范向量, 包含占用座位的瞬时数量,由标签 priority_levelflow_schema 进一步区分。
  • apiserver_flowcontrol_priority_level_request_count_samples 是一个直方图向量, 记录当前请求的观测值,由标签 phase(取值为waitingexecuting)和 priority_level 进一步区分。 每个直方图都会定期进行观察,直到相关类别的最后活动为止。观察频率高。
  • apiserver_flowcontrol_priority_level_request_count_watermarks 是一个直方图向量, 记录请求数的高/低水位线,由标签 phase(取值为 waitingexecuting)和 priority_level 拆分; 标签 mark 取值为 highlowapiserver_flowcontrol_priority_level_request_count_samples 向量观察到有值新增, 则该向量累积。这些水位线显示了样本值的范围。
  • apiserver_flowcontrol_request_queue_length_after_enqueue 是一个直方图向量, 记录请求队列的长度,由标签 priority_levelflow_schema 进一步区分。 每个排队中的请求都会为其直方图贡献一个样本,并在添加请求后立即上报队列的长度。 请注意,这样产生的统计数据与无偏调查不同。
  • apiserver_flowcontrol_request_concurrency_limit 是一个表向量, 记录并发限制的计算值(基于 API 服务器的总并发限制和 PriorityLevelConfigurations 的并发份额),并按标签 priority_level 进一步区分。
  • apiserver_flowcontrol_request_wait_duration_seconds 是一个直方图向量, 记录请求排队的时间, 由标签 flow_schema(表示与请求匹配的 FlowSchema ), priority_level(表示分配该请求的优先级) 和 execute(表示请求是否开始执行)进一步区分。
  • apiserver_flowcontrol_request_execution_seconds 是一个直方图向量, 记录请求实际执行需要花费的时间, 由标签 flow_schema(表示与请求匹配的 FlowSchema )和 priority_level(表示分配给该请求的优先级)进一步区分。

调试端点

启用 APF 特性后, kube-apiserver 会在其 HTTP/HTTPS 端口提供以下路径:

  • /debug/api_priority_and_fairness/dump_priority_levels —— 所有优先级及其当前状态的列表。你可以这样获取:

    kubectl get --raw /debug/api_priority_and_fairness/dump_priority_levels
    

    输出类似于:

    PriorityLevelName, ActiveQueues, IsIdle, IsQuiescing, WaitingRequests, ExecutingRequests,
    workload-low,      0,            true,   false,       0,               0,
    global-default,    0,            true,   false,       0,               0,
    exempt,            <none>,       <none>, <none>,      <none>,          <none>,
    catch-all,         0,            true,   false,       0,               0,
    system,            0,            true,   false,       0,               0,
    leader-election,   0,            true,   false,       0,               0,
    workload-high,     0,            true,   false,       0,               0,
    
  • /debug/api_priority_and_fairness/dump_queues —— 所有队列及其当前状态的列表。 你可以这样获取:

    kubectl get --raw /debug/api_priority_and_fairness/dump_queues
    

    输出类似于:

    PriorityLevelName, Index,  PendingRequests, ExecutingRequests, VirtualStart,
    workload-high,     0,      0,               0,                 0.0000,
    workload-high,     1,      0,               0,                 0.0000,
    workload-high,     2,      0,               0,                 0.0000,
    ...
    leader-election,   14,     0,               0,                 0.0000,
    leader-election,   15,     0,               0,                 0.0000,
    
  • /debug/api_priority_and_fairness/dump_requests ——当前正在队列中等待的所有请求的列表。 你可以这样获取:

    kubectl get --raw /debug/api_priority_and_fairness/dump_requests
    

    输出类似于:

    PriorityLevelName, FlowSchemaName, QueueIndex, RequestIndexInQueue, FlowDistingsher,       ArriveTime,
    exempt,            <none>,         <none>,     <none>,              <none>,                <none>,
    system,            system-nodes,   12,         0,                   system:node:127.0.0.1, 2020-07-23T15:26:57.179170694Z,
    

    针对每个优先级别,输出中还包含一条虚拟记录,对应豁免限制。

    你可以使用以下命令获得更详细的清单:

    kubectl get --raw '/debug/api_priority_and_fairness/dump_requests?includeRequestDetails=1'
    

    输出类似于:

    PriorityLevelName, FlowSchemaName, QueueIndex, RequestIndexInQueue, FlowDistingsher,       ArriveTime,                     UserName,              Verb,   APIPath,                                                     Namespace, Name,   APIVersion, Resource, SubResource,
    system,            system-nodes,   12,         0,                   system:node:127.0.0.1, 2020-07-23T15:31:03.583823404Z, system:node:127.0.0.1, create, /api/v1/namespaces/scaletest/configmaps,
    system,            system-nodes,   12,         1,                   system:node:127.0.0.1, 2020-07-23T15:31:03.594555947Z, system:node:127.0.0.1, create, /api/v1/namespaces/scaletest/configmaps,
    

接下来

有关 API 优先级和公平性的设计细节的背景信息, 请参阅增强提案。 你可以通过 SIG API Machinery 或特性的 Slack 频道 提出建议和特性请求。

10 - 安装扩展(Addons)

Add-ons 扩展了 Kubernetes 的功能。

本文列举了一些可用的 add-ons 以及到它们各自安装说明的链接。

每个 Add-ons 按字母顺序排序 - 顺序不代表任何优先地位。

网络和网络策略

  • ACI 通过 Cisco ACI 提供集成的容器网络和安全网络。
  • Antrea 在第 3/4 层执行操作,为 Kubernetes 提供网络连接和安全服务。Antrea 利用 Open vSwitch 作为网络的数据面。
  • Calico 是一个安全的 L3 网络和网络策略驱动。
  • Canal 结合 Flannel 和 Calico,提供网络和网络策略。
  • Cilium 是一个 L3 网络和网络策略插件,能够透明的实施 HTTP/API/L7 策略。 同时支持路由(routing)和覆盖/封装(overlay/encapsulation)模式。
  • CNI-Genie 使 Kubernetes 无缝连接到一种 CNI 插件, 例如:Flannel、Calico、Canal 或者 Weave。
  • Contiv 为各种用例和丰富的策略框架提供可配置的网络 (使用 BGP 的本机 L3、使用 vxlan 的覆盖、标准 L2 和 Cisco-SDN/ACI)。 Contiv 项目完全开源安装程序 提供了基于 kubeadm 和非 kubeadm 的安装选项。
  • 基于 Tungsten FabricContrail 是一个开源的多云网络虚拟化和策略管理平台,Contrail 和 Tungsten Fabric 与业务流程系统 (例如 Kubernetes、OpenShift、OpenStack和Mesos)集成在一起, 为虚拟机、容器或 Pod 以及裸机工作负载提供了隔离模式。
  • Flannel 是一个可以用于 Kubernetes 的 overlay 网络提供者。
  • Knitter 是为 kubernetes 提供复合网络解决方案的网络组件。
  • Multus 是一个多插件,可在 Kubernetes 中提供多种网络支持, 以支持所有 CNI 插件(例如 Calico,Cilium,Contiv,Flannel), 而且包含了在 Kubernetes 中基于 SRIOV、DPDK、OVS-DPDK 和 VPP 的工作负载。
  • OVN-Kubernetes 是一个 Kubernetes 网络驱动, 基于 OVN(Open Virtual Network)实现,是从 Open vSwitch (OVS) 项目衍生出来的虚拟网络实现。 OVN-Kubernetes 为 Kubernetes 提供基于覆盖网络的网络实现,包括一个基于 OVS 实现的负载均衡器 和网络策略。
  • OVN4NFV-K8S-Plugin 是一个基于 OVN 的 CNI 控制器插件,提供基于云原生的服务功能链条(Service Function Chaining,SFC)、多种 OVN 覆盖 网络、动态子网创建、动态虚拟网络创建、VLAN 驱动网络、直接驱动网络,并且可以 驳接其他的多网络插件,适用于基于边缘的、多集群联网的云原生工作负载。
  • NSX-T 容器插件(NCP) 提供了 VMware NSX-T 与容器协调器(例如 Kubernetes)之间的集成,以及 NSX-T 与基于容器的 CaaS / PaaS 平台(例如关键容器服务(PKS)和 OpenShift)之间的集成。
  • Nuage 是一个 SDN 平台,可在 Kubernetes Pods 和非 Kubernetes 环境之间提供基于策略的联网,并具有可视化和安全监控。
  • Romana 是一个 Pod 网络的第三层解决方案,并支持 NetworkPolicy API。
  • Weave Net 提供在网络分组两端参与工作的网络和网络策略,并且不需要额外的数据库。

服务发现

  • CoreDNS 是一种灵活的,可扩展的 DNS 服务器,可以 安装为集群内的 Pod 提供 DNS 服务。

可视化管理

  • Dashboard 是一个 Kubernetes 的 Web 控制台界面。
  • Weave Scope 是一个图形化工具, 用于查看你的容器、Pod、服务等。请和一个 Weave Cloud 账号 一起使用, 或者自己运行 UI。

基础设施

遗留 Add-ons

还有一些其它 add-ons 归档在已废弃的 cluster/addons 路径中。

维护完善的 add-ons 应该被链接到这里。欢迎提出 PRs!