# 基本概念

# Pod

在Kubernetes中,最小的管理元素不是一个个独立的容器,而是Pod,Pod是最小的,管理,创建,计划的最小单元.

Pod 中封装着应用的容器(有的情况下是好几个容器),存储、独立的网络 IP,管理容器如何运行的策略选项。Pod 代表着部署的一个单位:kubernetes 中应用的一个实例,可能由一个或者多个容器组合在一起共享资源。

在 Kubernetes 集群中 Pod 有如下两种使用方式:

  • 一个 Pod 中运行一个容器。“每个 Pod 中一个容器” 的模式是最常见的用法;在这种使用方式中,你可以把 Pod 想象成是单个容器的封装,kuberentes 管理的是 Pod 而不是直接管理容器。
  • 在一个 Pod 中同时运行多个容器。一个 Pod 中也可以同时封装几个需要紧密耦合互相协作的容器,它们之间共享资源。这些在同一个 Pod 中的容器可以互相协作成为一个 service 单位 —— 一个容器共享文件,另一个 “sidecar (opens new window)” 容器来更新这些文件。Pod 将这些容器的存储资源作为一个实体来管理。

Pod 中可以同时运行多个进程(作为容器运行)协同工作。同一个 Pod 中的容器会自动的分配到同一个 node 上。同一个 Pod 中的容器共享资源、网络环境和依赖,它们总是被同时调度。

注意在一个 Pod 中同时运行多个容器是一种比较高级的用法。只有当你的容器需要紧密配合协作的时候才考虑用这种模式。例如,你有一个容器作为 web 服务器运行,需要用到共享的 volume,有另一个 “sidecar (opens new window)” 容器来从远端获取资源更新这些文件

网络

每个 Pod 都会被分配一个唯一的 IP 地址。Pod 中的所有容器共享网络空间,包括 IP 地址和端口。Pod 内部的容器可以使用 localhost 互相通信。Pod 中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)

存储

可以为一个 Pod 指定多个共享的 Volume。Pod 中的所有容器都可以访问共享的 volume。Volume 也可以用来持久化 Pod 中的存储资源,以防容器重启后文件丢失

使用Pod

你很少会直接在 kubernetes 中创建单个 Pod。因为 Pod 的生命周期是短暂的,用后即焚的实体。当 Pod 被创建后(不论是由你直接创建还是被其他 Controller),都会被 Kubernetes 调度到集群的 Node 上。直到 Pod 的进程终止、被删掉、因为缺少资源而被驱逐、或者 Node 故障之前这个 Pod 都会一直保持在那个 Node 上。

Pod 不会自愈。如果 Pod 运行的 Node 故障,或者是调度器本身故障,这个 Pod 就会被删除。同样的,如果 Pod 所在 Node 缺少资源或者 Pod 处于维护状态,Pod 也会被驱逐。Kubernetes 使用更高级的称为 Controller 的抽象层,来管理 Pod 实例。虽然可以直接使用 Pod,但是在 Kubernetes 中通常是使用 Controller 来管理 Pod 的

Pod Controller

Controller 可以创建和管理多个 Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个 Node 故障,Controller 就能自动将该节点上的 Pod 调度到其他健康的 Node 上。

# 安全策略

Pod 安全策略 是集群级别的资源,它能够控制 Pod 运行的行为,以及它具有访问什么的能力。 PodSecurityPolicy 对象定义了一组条件,指示 Pod 必须按系统所能接受的顺序运行。 它们允许管理员控制如下方面

控制面 字段名称
已授权容器的运行 privileged
为容器添加默认的一组能力 defaultAddCapabilities
为容器去掉某些能力 requiredDropCapabilities
容器能够请求添加某些能力 allowedCapabilities
控制卷类型的使用 volumes (opens new window)
主机网络的使用 hostNetwork (opens new window)
主机端口的使用 hostPorts
主机 PID namespace 的使用 hostPID
主机 IPC namespace 的使用 hostIPC
主机路径的使用 allowedHostPaths (opens new window)
容器的 SELinux 上下文 seLinux (opens new window)
用户 ID runAsUser (opens new window)
配置允许的补充组 supplementalGroups (opens new window)
分配拥有 Pod 数据卷的 FSGroup fsGroup (opens new window)
必须使用一个只读的 root 文件系统 readOnlyRootFilesystem

Pod 安全策略 由设置和策略组成,它们能够控制 Pod 访问的安全特征。这些设置分为如下三类:

  • 基于布尔值控制:这种类型的字段默认为最严格限制的值。
  • 基于被允许的值集合控制:这种类型的字段会与这组值进行对比,以确认值被允许。
  • 基于策略控制:设置项通过一种策略提供的机制来生成该值,这种机制能够确保指定的值落在被允许的这组值中。

# 生命周期

Kubernetes 中 Pod 的生命周期,包括生命周期的不同阶段、存活和就绪探针、重启策略等。

Pod 的 status 字段是一个 PodStatus 对象,PodStatus 中有一个 phase 字段。

Pod 的相位(phase)是 Pod 在其生命周期中的简单宏观概述。该字段并不是对容器或 Pod 的综合汇总,也不是为了做为综合状态机。

Pod 相位的数量和含义是严格指定的。除了本文档中列举的状态外,不应该再假定 Pod 有其他的 phase 值。

下面是 phase 可能的值:

  • 挂起(Pending):Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间。
  • 运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。
  • 成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。
  • 失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。
  • 未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。

Pod 有一个 PodStatus 对象,其中包含一个 PodCondition 数组。 PodCondition 数组的每个元素都有一个 type 字段和一个 status 字段。type 字段是字符串,可能的值有 PodScheduled、Ready、Initialized、Unschedulable 和 ContainersReady。status 字段是一个字符串,可能的值有 True、False 和 Unknown

容器探针

探针是由 kubelet (opens new window) 对容器执行的定期诊断。要执行诊断,kubelet 调用由容器实现的 Handler (opens new window)。有三种类型的处理程序:

  • ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
  • TCPSocketAction:对指定端口上的容器的 IP 地址进行 TCP 检查。如果端口打开,则诊断被认为是成功的。
  • HTTPGetAction:对指定的端口和路径上的容器的 IP 地址执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

  • 成功:容器通过了诊断。
  • 失败:容器未通过诊断。
  • 未知:诊断失败,因此不会采取任何行动。

Kubelet 可以选择是否执行在容器上运行的两种探针执行和做出反应:

  • livenessProbe:指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 (opens new window) 的影响。如果容器不提供存活探针,则默认状态为 Success
  • readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success

# Hook

Pod hook(钩子)是由 Kubernetes 管理的 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook。

Hook 的类型包括两种:

  • exec:执行一段命令
  • HTTP:发送 HTTP 请求。

postStart 在容器创建之后(但并不能保证钩子会在容器 ENTRYPOINT 之前)执行,这时候 Pod 已经被调度到某台 node 上,被某个 kubelet 管理了,这时候 kubelet 会调用 postStart 操作,该操作跟容器的启动命令是在同步执行的,也就是说在 postStart 操作执行完成之前,kubelet 会锁住容器,不让应用程序的进程启动,只有在 postStart 操作完成之后容器的状态才会被设置成为 RUNNING。

PreStop 在容器终止之前被同步阻塞调用,常用于在容器结束前优雅的释放资源。

如果 postStart 或者 preStop hook 失败,将会终止容器

调试hook

Hook 调用的日志没有暴露给 Pod 的 event,所以只能通过 describe 命令来获取,如果有错误将可以看到 FailedPostStartHookFailedPreStopHook 这样的 event

# Preset

Preset 就是预设,有时候想要让一批容器在启动的时候就注入一些信息,比如 secret、volume、volume mount 和环境变量,而又不想一个一个的改这些 Pod 的 template,这时候就可以用到 PodPreset 这个资源对象了。

本页是关于 PodPreset 的概述,该对象用来在 Pod 创建的时候向 Pod 中注入某些特定信息。该信息可以包括 secret、volume、volume mount 和环境变量

Kubernetes 提供了一个准入控制器(PodPreset),当其启用时,Pod Preset 会将应用创建请求传入到该控制器上。当有 Pod 创建请求发生时,系统将执行以下操作:

  1. 检索所有可用的 PodPresets
  2. 检查 PodPreset 标签选择器上的标签,看看其是否能够匹配正在创建的 Pod 上的标签。
  3. 尝试将由 PodPreset 定义的各种资源合并到正在创建的 Pod 中。
  4. 出现错误时,在该 Pod 上引发记录合并错误的事件,PodPreset 不会注入任何资源到创建的 Pod 中。
  5. 注释刚生成的修改过的 Pod spec,以表明它已被 PodPreset 修改过。注释的格式为 podpreset.admission.kubernetes.io/podpreset-<pod-preset name>": "<resource version>"

每个 Pod 可以匹配零个或多个 Pod Prestet;并且每个 PodPreset 可以应用于零个或多个 Pod。 PodPreset 应用于一个或多个 Pod 时,Kubernetes 会修改 Pod Spec。对于 EnvEnvFromVolumeMounts 的更改,Kubernetes 修改 Pod 中所有容器的容器 spec;对于 Volume 的更改,Kubernetes 修改 Pod Spec

禁用特定Pod的Pod Preset

在某些情况下,您可能不希望 Pod 被任何 Pod Preset 所改变。在这些情况下,您可以在 Pod 的 Pod Spec 中添加注释:podpreset.admission.kubernetes.io/exclude:"true"

启用Pod Preset

为了在群集中使用 Pod Preset,您必须确保以下内容:

  1. 您已启用 settings.k8s.io/v1alpha1/podpreset API 类型。例如,可以通过在 API server 的 --runtime-config 选项中包含 settings.k8s.io/v1alpha1=true 来完成此操作。
  2. 您已启用 PodPreset 准入控制器。 一种方法是将 PodPreset 包含在为 API server 指定的 --admission-control 选项值中。
  3. 您已经在要使用的命名空间中通过创建 PodPreset 对象来定义 PodPreset

# 容器

每个运行的容器都是可重复的; 包含依赖环境在内的标准,意味着无论你在哪里运行它都会得到相同的行为。

容器将应用程序从底层的主机设施中解耦。 这使得在不同的云或 OS 环境中部署更加容易。

Kubernetes 集群中的每个节点 (opens new window)都会运行容器, 这些容器构成分配给该节点的 Pod (opens new window)。 单个 Pod 中的容器会在共同调度下,于同一位置运行在相同的节点上。

# lint容器

Pod (opens new window) 能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的 Init 容器。

Init 容器与普通的容器非常像,除了如下两点:

  • Init 容器总是运行到成功完成为止。
  • 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成。

如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的 restartPolicy 为 Never,它不会重新启动。

指定容器为 Init 容器,在 PodSpec 中添加 initContainers 字段,以 v1.Container 类型对象的 JSON 数组的形式,还有 app 的 containers 数组。 Init 容器的状态在 status.initContainerStatuses 字段中以容器状态数组的格式返回(类似 status.containerStatuses 字段)

与普通容器的不同之处

Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。 然而,Init 容器对资源请求和限制的处理稍有不同,在下面 资源 (opens new window) 处有说明。 而且 Init 容器不支持 Readiness Probe,因为它们必须在 Pod 就绪之前运行完成。

如果为一个 Pod 指定了多个 Init 容器,那些容器会按顺序一次运行一个。只有当前面的 Init 容器必须运行成功后,才可以运行下一个 Init 容器。当所有的 Init 容器运行完成后,Kubernetes 才初始化 Pod 和运行应用容器

Lint容器能做什么

因为 Init 容器具有与应用程序容器分离的单独镜像,所以它们的启动相关代码具有如下优势:

  • 它们可以包含并运行实用工具,但是出于安全考虑,是不建议在应用程序容器镜像中包含这些实用工具的。
  • 它们可以包含使用工具和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要 FROM 另一个镜像,只需要在安装过程中使用类似 sedawkpythondig 这样的工具。
  • 应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。
  • Init 容器使用 Linux Namespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,它们能够具有访问 Secret 的权限,而应用程序容器则不能。
  • 它们必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以 Init 容器能够提供了一种简单的阻塞或延迟应用容器的启动的方法,直到满足了一组先决条件

# pause容器

# 限制范围

默认情况下, Kubernetes 集群上的容器运行使用的计算资源 (opens new window)没有限制。 使用 Kubernetes 资源配额 (opens new window), 管理员(也称为 集群操作者)可以在一个指定的命名空间 (opens new window)内限制集群资源的使用与创建。 在命名空间中,一个 Pod (opens new window) 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。 作为集群操作者或命名空间级的管理员,你可能也会担心如何确保一个 Pod 不会垄断命名空间内所有可用的资源。

LimitRange 是限制命名空间内可为每个适用的对象类别 (例如 Pod 或 PersistentVolumeClaim (opens new window)) 指定的资源分配量(限制和请求)的策略对象。

一个 LimitRange(限制范围) 对象提供的限制能够做到:

  • 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
  • 在一个命名空间中实施对每个 PersistentVolumeClaim (opens new window) 能申请的最小和最大的存储空间大小的限制。
  • 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
  • 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。

当某命名空间中有一个 LimitRange 对象时,将在该命名空间中实施 LimitRange 限制。

LimitRange 的名称必须是合法的 DNS 子域名 (opens new window)

资源限制和请求的约束

  • 管理员在一个命名空间内创建一个 LimitRange 对象。
  • 用户在此命名空间内创建(或尝试创建) Pod 和 PersistentVolumeClaim 等对象。
  • 首先,LimitRanger 准入控制器对所有没有设置计算资源需求的所有 Pod(及其容器)设置默认请求值与限制值。
  • 其次,LimitRange 跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 所定义的最小、最大资源使用量以及使用量比值。
  • 若尝试创建或更新的对象(Pod 和 PersistentVolumeClaim)违反了 LimitRange 的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码 403 Forbidden 以及描述哪一项约束被违反的消息。
  • 若你在命名空间中添加 LimitRange 启用了对 cpumemory 等计算相关资源的限制, 你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod。
  • LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。 如果你添加或修改 LimitRange,命名空间中已存在的 Pod 将继续不变。
  • 如果命名空间中存在两个或更多 LimitRange 对象,应用哪个默认值是不确定的。

# 资源配额

当多个用户或团队共享具有固定节点数目的集群时,人们会担心有人使用超过其基于公平原则所分配到的资源量。

资源配额是帮助管理员解决这一问题的工具。

资源配额,通过 ResourceQuota 对象来定义,对每个命名空间的资源消耗总量提供限制。 它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命名空间中的 Pod 可以使用的计算资源的总上限。

资源配额的工作方式如下:

  • 不同的团队可以在不同的命名空间下工作。这可以通过 RBAC (opens new window) 强制执行。
  • 集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。
  • 当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会跟踪集群的资源使用情况, 以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。
  • 如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。
  • 如果命名空间下的计算资源 (如 cpumemory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。 提示: 可使用 LimitRanger 准入控制器来为没有设置计算资源需求的 Pod 设置默认值。

# network policy

# kube-proxy

# 网络

当你在一个有严格网络边界的环境里运行 Kubernetes,例如拥有物理网络防火墙或者拥有公有云中虚拟网络的自有数据中心, 了解 Kubernetes 组件使用了哪些端口和协议是非常有用的。

控制面

协议 方向 端口范围 目的 使用者
TCP 入站 6443 Kubernetes API server 所有
TCP 入站 2379-2380 etcd server client API kube-apiserver, etcd
TCP 入站 10250 Kubelet API 自身, 控制面
TCP 入站 10259 kube-scheduler 自身
TCP 入站 10257 kube-controller-manager 自身

尽管 etcd 的端口也列举在控制面的部分,但你也可以在外部自己托管 etcd 集群或者自定义端口

工作节点

协议 方向 端口范围 目的 使用者
TCP 入站 10250 Kubelet API 自身, 控制面
TCP 入站 30000-32767 NodePort Services† 所有

Kubernetes 管理的是集群,Kubernetes 中的网络要解决的核心问题就是每台主机的 IP 地址网段划分,以及单个容器的 IP 地址分配。概括为:

  • 保证每个 Pod 拥有一个集群内唯一的 IP 地址
  • 保证不同节点的 IP 地址划分不会重复
  • 保证跨节点的 Pod 可以互相通信
  • 保证不同节点的 Pod 可以与跨节点的主机互相通信

为了解决该问题,出现了一系列开源的 Kubernetes 中的网络插件与方案,如:

  • flannel
  • calico
  • contiv
  • weave
  • kube-router
  • cilium
  • canal

# CNI

CNI给pod分配ip,给namespace添加网卡

CNI对行为有要求,对具体实现不关心

原生K8s CNI 与第三方

https://github.com/containernetworking/cni

# Flannel

Flannel 是作为一个二进制文件的方式部署在每个 node 上,主要实现两个功能:

  • 为每个 node 分配 subnet,容器将自动从该子网中获取 IP 地址
  • 当有 node 加入到网络中时,为每个 node 增加路由配置

扁平网络

https://github.com/flannel-io/flannel

# Calico

Calico 创建和管理一个扁平的三层网络(不需要 overlay),每个容器会分配一个可路由的 IP。由于通信时不需要解包和封包,网络性能损耗小,易于排查,且易于水平扩展。

小规模部署时可以通过 BGP client 直接互联,大规模下可通过指定的 BGP Route Reflector 来完成,这样保证所有的数据流量都是通过 IP 路由的方式完成互联的。

Calico 基于 iptables 还提供了丰富而灵活的网络 Policy,保证通过各个节点上的 ACL 来提供 Workload 的多租户隔离、安全组以及其他可达性限制等功能。

可以使用 kubectl 直接管理 Calico

Felix

Felix 以 agent 代理的形式在每台机器端点上运行。对路由和 ACL 以及主机编程,为该主机上的端点提供所需的连接。

根据具体的编排器环境,Felix 负责:

接口管理

将有关接口的信息编入内核,以便内核能够正确处理来自该端点的流量。特别是,确保主机响应来自每个工作负载的 ARP 请求,提供主机的 MAC,并为它所管理的接口启用 IP 转发。它还监控接口,以确保编程在适当的时候应用。

路由编程

将其主机上的端点的路由编程到 Linux 内核的 FIB(转发信息库)。这可以确保到达主机上的以这些端点为目的地的数据包被相应地转发。

ACL 编程

在 Linux 内核中编程 ACL,以确保只有有效的流量可以在端点之间发送,并且端点不能规避 Calico 的安全措施。

状态报告

提供网络健康数据。特别是在配置其主机时报告错误和问题。这些数据被写入数据存储,以便对网络的其他组件和运营商可见。

BIRD

BGP Internet Routing Daemon,简称 BIRD。从 Felix 获取路由,并分发到网络上的 BGP peer,用于主机间的路由。在每个 Felix 代理的节点上运行。

BGP 客户端负责:

路由分配

当 Felix 将路由插入 Linux 内核的 FIB 时,BGP 客户端将它们分配给部署中的其他节点。这确保了部署中的有效流量路由。

BGP 路由反射器的配置

BGP 路由反射器通常是为大型部署而配置的,而不是一个标准的 BGP 客户端。BGP 路由反射器作为连接 BGP 客户端的一个中心点。(标准 BGP 要求每个 BGP 客户端在网状拓扑结构中与其他每个 BGP 客户端连接,这很难维护)。

为了实现冗余,你可以无缝部署多个 BGP 路由反射器。BGP 路由反射器只参与网络的控制:没有终端数据通过它们。当 Calico BGP 客户端将其 FIB 中的路由通告给路由反射器时,路由反射器将这些路由通告给部署中的其他节点。

IPIP onlink

# Cilium

Cilium 是一款开源软件,也是 CNCF 的孵化项目,目前已有公司 (opens new window)提供商业化支持,还有基于 Cilium 实现的服务网格解决方案。最初它仅是作为一个 Kubernetes 网络组件。Cilium 在 1.7 版本后推出并开源了 Hubble (opens new window),它是专门为网络可视化设计,能够利用 Cilium 提供的 eBPF (opens new window) 数据路径,获得对 Kubernetes 应用和服务的网络流量的深度可见性。这些网络流量信息可以对接 Hubble CLI、UI 工具,可以通过交互式的方式快速进行问题诊断。除了 Hubble 自身的监控工具,还可以对接主流的云原生监控体系 ——Prometheus 和 Grafana,实现可扩展的监控策略

Cilium 为基于 Kubernetes 的 Linux 容器管理平台上部署的服务,透明地提供服务间的网络和 API 连接及安全。

Cilium 底层是基于 Linux 内核的新技术 eBPF (opens new window),可以在 Linux 系统中动态注入强大的安全性、可见性和网络控制逻辑。 Cilium 基于 eBPF (opens new window) 提供了多集群路由、替代 kube-proxy 实现负载均衡、透明加密以及网络和服务安全等诸多功能。除了提供传统的网络安全之外,eBPF (opens new window) 的灵活性还支持应用协议和 DNS 请求 / 响应安全。同时,Cilium 与 Envoy 紧密集成,提供了基于 Go 的扩展框架。因为 eBPF (opens new window) 运行在 Linux 内核中,所以应用所有 Cilium 功能,无需对应用程序代码或容器配置进行任何更改。

基于微服务的应用程序分为小型独立服务,这些服务使用 HTTPgRPCKafka 等轻量级协议通过 API 相互通信。但是,现有的 Linux 网络安全机制(例如 iptables)仅在网络和传输层(即 IP 地址和端口)上运行,并且缺乏对微服务层的可见性。

Hubble 是一个完全分布式的网络和安全可观察性平台。它建立在 Cilium 和 eBPF (opens new window) 之上,以完全透明的方式实现对服务的通信和行为以及网络基础设施的深度可见性(visibility)。

通过建立在 Cilium 之上,Hubble 可以利用 eBPF (opens new window) 实现可见性。依靠 eBPF (opens new window),所有的可见性都是可编程的,并允许采用一种动态方法,最大限度地减少开销,同时按照用户的要求提供深入和详细的可见性。Hubble 的创建和专门设计是为了最好地利用 eBPF (opens new window) 的能力

特性

基于身份的安全性

Cilium 可见性和安全策略基于容器编排系统的标识(例如,Kubernetes 中的 Label)。在编写安全策略、审计和故障排查时,再也不用担心网络子网或容器 IP 地址了。

卓越的性能

eBPF (opens new window) 利用 Linux 底层的强大能力,通过提供 Linux 内核的沙盒可编程性来实现数据路径,从而提供卓越的性能。

API 协议可见性 + 安全性

传统防火墙仅根据 IP 地址和端口等网络标头查看和过滤数据包。Cilium 也可以这样做,但也可以理解并过滤单个 HTTP、gRPC 和 Kafka 请求,这些请求将微服务拼接在一起。

专为扩展而设计

Cilium 是为扩展而设计的,在部署新 pod 时不需要节点间交互,并且通过高度可扩展的键值存储进行所有协调。

# 存储

#

Container 中的文件在磁盘上是临时存放的,这给 Container 中运行的较重要的应用程序带来一些问题。 问题之一是当容器崩溃时文件丢失。 kubelet 会重新启动容器,但容器会以干净的状态重启。 第二个问题会在同一 Pod 中运行多个容器并共享文件时出现。 Kubernetes 卷(Volume) (opens new window) 这一抽象概念能够解决这两个问题

Docker 也有卷(Volume) (opens new window) 的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录。 Docker 提供卷驱动程序,但是其功能非常有限。

Kubernetes 支持很多类型的卷。 Pod (opens new window) 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。

使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 容器中的进程看到的文件系统视图是由它们的容器镜像 (opens new window) 的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。 其中根文件系统同容器镜像的内容相吻合。 任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。

卷挂载在镜像中的指定路径 (opens new window)下。 Pod 配置中的每个容器必须独立指定各个卷的挂载位置。

卷不能挂载到其他卷之上(不过存在一种使用 subPath (opens new window) 的相关机制),也不能与其他卷有硬链接。

卷类型

cephfs

cephfs 卷允许你将现存的 CephFS 卷挂载到 Pod 中。 不像 emptyDir 那样会在 Pod 被删除的同时也会被删除,cephfs 卷的内容在 Pod 被删除时会被保留,只是卷被卸载了。 这意味着 cephfs 卷可以被预先填充数据,且这些数据可以在 Pod 之间共享。同一 cephfs 卷可同时被多个写者挂载。

configMap

configMap (opens new window) 卷提供了向 Pod 注入配置数据的方法。 ConfigMap 对象中存储的数据可以被 configMap 类型的卷引用,然后被 Pod 中运行的容器化应用使用。

引用 configMap 对象时,你可以在卷中通过它的名称来引用。 你可以自定义 ConfigMap 中特定条目所要使用的路径。 下面的配置显示了如何将名为 log-config 的 ConfigMap 挂载到名为 configmap-pod 的 Pod 中

log-config ConfigMap 以卷的形式挂载,并且存储在 log_level 条目中的所有内容都被挂载到 Pod 的 /etc/config/log_level 路径下。 请注意,这个路径来源于卷的 mountPathlog_level 键对应的 path

downwardAPI

downwardAPI 卷用于为应用提供 downward API (opens new window) 数据。 在这类卷中,所公开的数据以纯文本格式的只读文件形式存在。

emptyDir

当 Pod 分派到某个节点上时,emptyDir 卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。 就像其名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。

emptyDir 的一些用途:

  • 缓存空间,例如基于磁盘的归并排序。
  • 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
  • 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。

emptyDir.medium 字段用来控制 emptyDir 卷的存储位置。 默认情况下,emptyDir 卷存储在该节点所使用的介质上; 此处的介质可以是磁盘、SSD 或网络存储,这取决于你的环境。 你可以将 emptyDir.medium 字段设置为 "Memory", 以告诉 Kubernetes 为你挂载 tmpfs(基于 RAM 的文件系统)。 虽然 tmpfs 速度非常快,但是要注意它与磁盘不同:tmpfs 在节点重启时会被清除, 并且你所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。

你可以通过为默认介质指定大小限制,来限制 emptyDir 卷的存储容量。 此存储是从节点临时存储 (opens new window)中分配的。 如果来自其他来源(如日志文件或镜像分层数据)的数据占满了存储,emptyDir 可能会在达到此限制之前发生存储容量不足的问题。

fc(光纤通道)

fc 卷类型允许将现有的光纤通道块存储卷挂载到 Pod 中。 可以使用卷配置中的参数 targetWWNs 来指定单个或多个目标 WWN(World Wide Names)。 如果指定了多个 WWN,targetWWNs 期望这些 WWN 来自多路径连接。

# 持久卷

存储的管理是一个与计算实例的管理完全不同的问题。 PersistentVolume 子系统为用户和管理员提供了一组 API, 将存储如何制备的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。

持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class) (opens new window)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。

持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式 (opens new window))。

尽管 PersistentVolumeClaim 允许用户消耗抽象的存储资源, 常见的情况是针对不同的问题用户需要的是具有不同属性(如,性能)的 PersistentVolume 卷。 集群管理员需要能够提供不同性质的 PersistentVolume, 并且这些 PV 卷之间的差别不仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了存储类(StorageClass) 资源。

# 临时卷

有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。 例如,缓存服务经常受限于内存大小,而且可以将不常用的数据转移到比内存慢的存储中,对总体性能的影响并不大。

另有些应用程序需要以文件形式注入的只读数据,比如配置数据或密钥。

临时卷 就是为此类用例设计的。因为卷会遵从 Pod 的生命周期,与 Pod 一起创建和删除, 所以停止和重新启动 Pod 时,不会受持久卷在何处可用的限制。

临时卷在 Pod 规约中以 内联 方式定义,这简化了应用程序的部署和管理。

Kubernetes 为了不同的用途,支持几种不同类型的临时卷:

emptyDirconfigMapdownwardAPIsecret 是作为 本地临时存储 (opens new window) 提供的。它们由各个节点上的 kubelet 管理。

CSI 临时卷 必须 由第三方 CSI 存储驱动程序提供。

通用临时卷 可以 由第三方 CSI 存储驱动程序提供,也可以由支持动态制备的任何其他存储驱动程序提供。 一些专门为 CSI 临时卷编写的 CSI 驱动程序,不支持动态制备:因此这些驱动程序不能用于通用临时卷。

使用第三方驱动程序的优势在于,它们可以提供 Kubernetes 本身不支持的功能, 例如,与 kubelet 管理的磁盘具有不同性能特征的存储,或者用来注入不同的数据。

# 卷快照

PersistentVolumePersistentVolumeClaim 这两个 API 资源用于给用户和管理员制备卷类似, VolumeSnapshotContentVolumeSnapshot 这两个 API 资源用于给用户和管理员创建卷快照。

VolumeSnapshotContent 是从一个卷获取的一种快照,该卷由管理员在集群中进行制备。 就像持久卷(PersistentVolume)是集群的资源一样,它也是集群中的资源。

VolumeSnapshot 是用户对于卷的快照的请求。它类似于持久卷声明(PersistentVolumeClaim)。

VolumeSnapshotClass 允许指定属于 VolumeSnapshot 的不同属性。在从存储系统的相同卷上获取的快照之间, 这些属性可能有所不同,因此不能通过使用与 PersistentVolumeClaim 相同的 StorageClass 来表示。

卷快照能力为 Kubernetes 用户提供了一种标准的方式来在指定时间点复制卷的内容,并且不需要创建全新的卷。 例如,这一功能使得数据库管理员能够在执行编辑或删除之类的修改之前对数据库执行备份。

当使用该功能时,用户需要注意以下几点:

  • API 对象 VolumeSnapshotVolumeSnapshotContentVolumeSnapshotClassCRD (opens new window), 不属于核心 API。
  • VolumeSnapshot 支持仅可用于 CSI 驱动。
  • 作为 VolumeSnapshot 部署过程的一部分,Kubernetes 团队提供了一个部署于控制平面的快照控制器, 并且提供了一个叫做 csi-snapshotter 的边车(Sidecar)辅助容器,和 CSI 驱动程序一起部署。 快照控制器监视 VolumeSnapshotVolumeSnapshotContent 对象, 并且负责创建和删除 VolumeSnapshotContent 对象。 边车 csi-snapshotter 监视 VolumeSnapshotContent 对象, 并且触发针对 CSI 端点的 CreateSnapshotDeleteSnapshot 的操作。
  • 还有一个验证性质的 Webhook 服务器,可以对快照对象进行更严格的验证。 Kubernetes 发行版应将其与快照控制器和 CRD(而非 CSI 驱动程序)一起安装。 此服务器应该安装在所有启用了快照功能的 Kubernetes 集群中。
  • CSI 驱动可能实现,也可能没有实现卷快照功能。CSI 驱动可能会使用 csi-snapshotter 来提供对卷快照的支持。详见 CSI 驱动程序文档 (opens new window)
  • Kubernetes 负责 CRD 和快照控制器的安装。

# CSI

k8s定义给存储的标准,container storage interface

Last Updated: 7/11/2023, 2:13:22 AM