K8s 笔记 (I)

组件与对象

Jacob Xie published on
26 min, 5033 words

Categories: Doc

Tags: k8s

Kubernetes 组件

k8s cluster

控制面组件

控制面组件 Control Plane Components 处理整个集群的决策(例如资源调度),同样也检测和响应集群的事件(例如当一个部署的 replicas 字段不满足条件时,启动一个新的 pod)。

  • Kube api server: 用于暴露 k8s 的 API,即 k8s 的前端服务。

  • etcd: 一致性和高可用的键值数据库,存储 k8s 所有的集群数据。

  • kube scheduler: 用于观察哪些新建的 Pods 并没有被赋予节点,选择一个节点用于运行它们;以下几种因素作为调度的抉择:单独与集合的资源需求,硬件/软件/策略的约束,亲和性与反亲和性规则,数据位置,内部负载间的干扰,以及最后时限。

  • kube controller manager: 用于运行控制器进程。理论上来说每个控制器都是独立的进程,但是为了减少复杂性,它们都被编译在单个二进制文件中并单进程运行。它们的其中一些类型为:

    • 节点控制器 node controller:当节点关闭时通知与响应。

    • 任务控制器 job controller:观察代表 one-off 任务的任务对象,然后创建 Pods 运行并完成这些任务。

    • 端点控制器 endpoints controller:填充端点对象(即加入 Service 与 Pod)。

    • 服务账户与令牌控制器 service account & token controller:为新的命名空间创建默认账户和 API 访问令牌。

  • cloud controller manager: 嵌入特定云的控制逻辑。

节点组件

节点组件 Node Components 维护运行中的 pods 并提控 k8s 运行时的环境。

  • kubelet: 运行在每个集群节点上,保证容器都运行在 Pod 中。

  • kube proxy: 运行在每个集群节点上的网络代理,实现 k8s 服务 (Service)概念的一部分。

  • 容器运行时: 负责运行容器的软件,如 Docker,containerd,CRI-O 以及 k8s 的其他任何实现。

插件

插件 Addons 使用 k8s 资源(DaemonSet,Deployment 等)实现集群功能。插件中命名空间域的资源属于 kube-system 命名空间。

  • DNS: 为 k8s 服务提供 DNS 记录。

  • Web UI: 提供用户界面。

  • 容器资源监控: 将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中,并提供浏览这些数据的界面。

  • 集群层面日志: 将容器的日志数据保存到一个集中的日志存储中,并提供搜索与浏览接口。

理解 k8s 对象

k8s 的对象是 k8s 系统中持久化的实体。k8s 使用这些实体来状态化集群:

  • 描述哪些应用正在运行以及它们运行在哪些节点

  • 描述这些应用可用的资源

  • 描述这些应用应该如何作用的规则,例如重启,更新以及错误容忍等

创建一个对象是在告诉 k8s 集群应该如何运作,它们也是集群的理想状态。需要使用 k8s 的 API 才能创建,修改,或者删除它们。可以使用 kubectl 命令行或者其他的客户端库。

对象 Spec 与 Status

几乎所有 k8s 对象都包含两个嵌套对象的字段,用于管理对象的配置:specstatus。前者为创建该对象时所得,提供对象所需资源的描述;后者描述对象的当前状态,包括 k8s 系统与它的组件所提供的支持与更新。

描述一个 k8s 对象

在 k8s 中创建一个对象,必须提供对象用于描述其理想状态与基础信息的 spec。当通过 k8s API 来创建对象时(或是直接通过 kubectl),API 也同样需要这些信息的 JSON 请求体。通常的做法是提供一个包含信息的 .yaml 文件给 kubectl kubectl 在发起 API 请求时则会将 .yaml 文件中的信息转换为 JSON。

以下是一个 .yaml 文件的例子(application/deployment.yaml):

apiVersion: apps/v1

kind: Deployment

metadata:
  name: nginx-deployment

spec:
  selector:
    matchLabels:
      app: nginx

  replicas: 2 # 需要 2 个 pods 来匹配该模版

  template:
    metadata:
      labels:
        app: nginx

    spec:
      containers:
        - name: nginx

          image: nginx:1.14.2

          ports:
            - containerPort: 80

接着使用 kubectl apply 命令执行:

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

其将会返回:

deployment.apps/nginx-deployment created

所需的字段

.yaml 文件中需要如下几个字段:

  • apiVersion: k8s 的 API 版本

  • kind: 对象类别

  • metadata: 用于区别对象的唯一标识,包括 name 字符串,UID,以及可选的 namespace

  • spec: 该对象所期望的状态

k8s 对象的管理

kubectl 命令行工具支持几个不同的方法用于创建与管理 k8s 对象。

警告:

一个 k8s 的对象应该仅有一种技术来进行管理。混合不同的技术管理同一个对象将会造成未定义行为。

管理技术作用于推荐的环境支持的写入者学习曲线
指令式命令活跃对象测试项目1+
指令式对象配置单个文件生产项目1
声明式对象配置文件目录生产项目1+

指令式命令

使用指令式命令时,用户是对集群里的活跃对象进行直接操作。例如:

kubectl create deployment nginx --image nginx

与对象配置相比,这种方式的优点在于:

  • 命令简单,易学并且易于记忆
  • 命令仅需一步即可对集群进行更改

缺点在于:

  • 命令不能与变更审查流程集成
  • 命令不提供与更改关联的审核跟踪
  • 除了实时内容之外,命令不提供记录的源
  • 命令不提供用于创建新对象的模板

指令式对象配置

kubectl 命令指定操作(创建,替换等),可选标志和至少一个文件名。指定的文件必须包含 YAML 或者 JSON 格式的对象的完整定义。

警告:

replace 命令将现有规范替换为新的规范,并放弃对配置文件中缺少的对象的所有更改。该方法不应对独立于配置文件更新的对象使用。例如,服务类型 LoadBalancer 拥有 externalIPs 字段是独立于集群的配置更新的。

例如,创建配置文件中定义的对象:

kubectl create -f nginx.yaml

删除两个配置文件中定义的对象:

kubectl delete -f nginx.yaml -f redis.yaml

覆盖配置来更新配置文件中定义的对象:

kubectl replace -f nginx.yaml

与指令式命令相比的优点在于:

  • 对象配置可以储存在例如 Git 中
  • 对象配置可以与流程集成,例如在推送或者审计之前检查更新
  • 对象配置提供了用于创建新对象的模板

与指令式命令相比的缺点在于:

  • 对象配置需要对对象架构有基本的了解
  • 对象配置需要额外的步骤来编写 YAML 文件

与声明式对象配置相比的优点在于:

  • 指令式对象配置行为更加简单
  • 从 k8s 1.5 版本开始,指令对象配置更加成熟

与声明式对象配置相比的缺点在于:

  • 指令式对象配置更适合文件,而不是目录
  • 对活动对象的更新必须反映在配置文件中,否则会在下一次替换时丢失

声明式对象配置

对本地存储的对象配置文件进行操作,但不需要对文件执行操作。kubectl 会自动检测每个文件的创建,更新和删除操作。这使得配置可以在目录上工作,根据目录中配置文件对不同的对象执行不同的操作。

说明:

声明式对象配置保留其他编写者的修改,即使这些更改未被合并到对象配置文件中。可以通过 patch API 操作仅写入观察到的差异,而不是使用 replace API 操作来替换整个对象配置来实现。

例如,处理 configs 目录中的所有对象配置文件,创建并更新活跃对象。可以先使用 diff 子命令查看将要进行的修改,然后再进行应用:

kubectl diff -f configs/
kubectl apply -f configs/

递归处理目录:

kubectl diff -R -f configs/
kubectl apply -R -f configs/

与指令式对象配置相比的优点:

  • 对活动对象所做的更改即使未合并到配置文件中,也会被保留下来。
  • 声明性对象配置更好地支持对目录进行操作并自动检测每个文件的操作类型(创建,修补,删除)。

与指令式对象配置相比的缺点:

  • 声明式对象配置难于调试并且出现异常时结果难以理解。
  • 使用 diff 产生的部分更新会创建复杂的合并和补丁操作。

对象名称与 IDs

在集群里的同种资源类型下,每个对象都拥有唯一名称。在整个 k8s 系统中,每个对象也拥有一个唯一的 UID 。

  • 名称:一个由客户端所提供的字符串,意味着一个拥有资源 URL 的对象,例如 /api/v1/pods/some-name

  • UID:由 k8s 系统自动生成的唯一标识符用于区分对象。

命名空间

在 k8s 中,命名空间提供了一个机制来隔离单个集群内的资源组。在一个命名空间内的资源名称都是唯一的,不同的命名空间不受此约束。基于命名空间的作用域仅适用于命名空间对象(例如 Deployments,Services 等),而不是整个集群范围的对象(例如 StorageClass,Nodes,PersistentVolumes 等)。

命名空间的意义在于存在多个跨组或者跨项目用户的环境。对于少于十个用户的情况,完全不应该创建或者考虑命名空间。

标签与选择算符

标签是赋予对象的键/值对,例如 pods。标签的作用是识别对于用户有意义的对象属性,而不是直接作用于核心系统的。标签可以用来管理以及筛选对象。可以在对象创建之初赋予其标签,也可以之后在任何时间添加或者修改。

动机

标签允许用户用一种松散的方式来映射自定义的组织结构于系统的对象,而不需要客户端来存储这些映射。

服务部署 service deployment 和批处理管道 batch processing pipelines 通常是多维的实体(例如,多个分区或部署,多个版本,多层,每层多个微服务)。管理通常需要交差操作,则会打破严格层次展示的封装,特别是严格的层次结构是由基建决定的而不是由用户决定。

例如:

  • "release" : "stable", "release" : "canary"
  • "environment" : "dev", "environment" : "qa", "environment" : "production"
  • "tier" : "frontend", "tier" : "backend", "tier" : "cache"
  • "partition" : "customerA", "partition" : "customerB"
  • "track" : "daily", "track" : "weekly"

语法和字符集

标签是键/值对。合法的标签键拥有两段:可选的前缀以及名称,通过 / 分隔。名称部分需要小于等于 63 个 char,以及使用字母开始与结尾,中间可以包含 -_.。如果定义了前缀,其必须是 DNS 的子域:由点 . 分隔的一系列 DNS 标签,总共不超过 253 个字符,后面接着 /

如果省略了前缀,那么标签的键则对于用户来说是私有的。向最终用户对象添加标签的自动系统组件(例如 kube-schedulerkube-controller-managerkube-apiserverkubectl 或其他第三方自动化工具)必须指定前缀。

kubernetes.io/k8s.io/ 前缀是为 k8s 核心组件保留的。

例如以下是一个 Pod 的配置文件,拥有两个标签 environment: production 以及 app: nginx

apiVersion: v1
kind: Pod
metadata:
  name: label-demo
  labels:
    environment: production
    app: nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.14.2
      ports:
        - containerPort: 80

标签选择算符

不同于名称和 UIDs,标签不提供唯一性。通常来说,我们希望很多对象都携带同种标签。

通过标签选择算符,客户端/用户可以识别对象的集合,因此它是 k8s 中的核心分组原语。

API 现在支持两种类型的选择算符:基于等值的基于集合的。标签选择可以有都好分隔的多个需求组成。在多个需求的情况下,必须满足所有需求,因此逗号分隔符等同于 && 逻辑运算符。

基于等值或者基于不等值的需求允许按标签键和值进行过滤。匹配对象必须满足所有指定的标签约束,即使他们也可能具有其他标签。可接受的运算符有 ===!= 三种。前两个表示相等(仅为同义词),后者表示不相等,例如:

environment = production
tier != frontend

基于集合的标签需求允许通过一组值来过滤键。支持三种操作符:innotinexists(只可以用在键标识符上)。例如:

environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
  • 第一个示例选择了所有键等于 environment 并且值等于 production 或者 qa 的资源。
  • 第二个示例选择了所有键等于 tier 并且值不等于 frontend 或者 backend 的资源,以及所有没有 tier 键标签的资源。
  • 第三个示例选择了所有包含了有 partition 标签的资源;没有校验它的值。
  • 第四个示例选择了所有没有 partition 标签的资源;没有校验它的值。

标签 API

LIST 和 WATCH 过滤

LIST 和 WATCH 操作可以使用查询参数指定标签选择算符过滤一组对象。两种需求都是允许的(这里显示的是它们出现在 URL 查询字符串中)。

  • 基于等值 的需求:?labelSelector=environment%3Dproduction,tier%3Dfrontend
  • 基于集合 的需求:?labelSelector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29

两种标签选择算符都可以通过 REST 客户端用于 list 或 watch 资源。例如使用 kubectl 定位 apiserver 可以使用基于等值的标签选择算符:

kubectl get pods -l environment=production,tier=frontend

或者使用基于集合的需求:

kubectl get pods -l 'environment in (production),tier in (frontend)'

基于集合的需求更具有表达力,它们可以实现值的操作:

kubectl get pods -l 'environment in (production, qa)'

或者通过 exists 运算符限制不匹配:

kubectl get pods -l 'environment,environment notin (frontend)'

注解

使用注解可以为对象附加任意的非标识类的元数据。客户端的工具和库可以获取这些元数据。

用户可以使用标记或者注解来为 k8s 对象附加元数据。标注可以用于归纳对象,而注解不行。一个注解的元数据可以很小或者很大,结构化或者非结构化的,可以包含标签不允许包含的字符。

注解类似于标签,也是键/值的 maps:

"metadata": {
  "annotations": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

注意:键和值必须皆为字符串。换言之,不允许使用述职,布尔值,列表或者其它类型的值作为键或者值。

以下是一些可以记录在注解里的信息:

  • 由声明式配置层管理的字段。在注解中添加这些字段可以用于区分由客户端或者服务端默认的字段,以及由自动自动调整大小或者自适应系统自动生成的字段。

  • 构建,发布,或者镜像信息例如时间戳,发布 IDs,git 分支,PR 数,镜像哈希值,以及注册地址。

  • 指向日志,监控,分析,或者审计仓库的指针。

  • 客户端库或者用于 debug 的工具信息:例如名称,版本以及构建信息。

  • 用户或者工具/系统的初始信息,例如 URLs 或者其它生态系统的关联对象。

  • 轻量级的上线工具的元数据:例如配置或者检查点。

  • 负责人的电话或者页面,或者其它直接的联系信息,例如小组的网页。

  • 从终端用户实现的修改行为或者非标准特性的指令等。

例如以下就是从 Pod 而来的一个配置文件,其拥有注解 imageregistry: https://hub.docker.com/

apiVersion: v1
kind: Pod
metadata:
  name: annotations-demo
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
    - name: nginx
      image: nginx:1.14.2
      ports:
        - containerPort: 80

字段选择器

字段选择器 field selectors 允许用户根据一个或多个资源的字段来选择 k8s 资源。这里是一些关于字段选择器请求的案例:

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

这个 kubectl 命令选择所有 Pods 的 status.phase 字段为 Running

kubectl get pods --field-selector status.phase=Running

说明:

字段选择器是基础资源的过滤器。默认是没有选择器/过滤器被应用的,意味着特定类型的所有资源都被选择。这就使得 kubectl 查询等同于 kubectl get pods 以及 kubectl get pods -- field-select ""

支持的字段

所有的资源类型支持 metadata.name 以及 metadata.namespace 字段。使用非支持的字段选择器则会抛出异常,例如:

kubectl get ingress --field-selector foo.bar=baz
Error from server (BadRequest): Unable to find "ingresses" that match label selector "", field selector "foo.bar=baz": "foo.bar" is not a known field selector: only "metadata.name", "metadata.namespace"

支持的操作

可以使用 === 以及 != 运算符(= 等同于 ==)。例如 kubectl 命令选择 k8s 所有非默认命名空间的服务:

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

链式选择

与其它选择器一样,字段选择器可以通过都好分隔的数组串联在一起。例如 kubectl 命令选择所有 status.phase 不等于 Running 以及 spec.restartPolicy 等于 Always 的 Pods:

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

多资源类型

可以跨资源类型使用字段选择器。例如 kubectl 命令选择所有不为默认命名空间的状态集与服务:

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

Finalizers

Finalizer 是带有命名空间的键,告诉 Kubernetes 等到特定的条件被满足后, 再完全删除被标记为删除的资源。 Finalizer 提醒控制器清理被删除的对象拥有的资源。

所有者与依赖者

在 k8s 中,一些对象是其他对象的“所有者(Owner)”。 例如,ReplicaSet 是一组 Pod 的所有者。拥有所有者的对象是“依赖者(Dependent)”。

推荐的标签

除了 kubectl 和 dashboard 之外,你可以使用其他工具来可视化和管理 Kubernetes 对象。 一组通用的标签可以让多个工具之间相互操作,用所有工具都能理解的通用方式描述对象。

除了支持工具外,推荐的标签还以一种可以查询的方式描述了应用程序。

元数据围绕 应用(application) 的概念进行组织。Kubernetes 不是 平台即服务(PaaS),没有或强制执行正式的应用程序概念。 相反,应用程序是非正式的,并使用元数据进行描述。应用程序包含的定义是松散的。

说明:

这些是推荐的标签。它们使管理应用程序变得更容易但不是任何核心工具所必需的。

共享标签和注解都使用同一个前缀:app.kubernetes.io。没有前缀的标签是用户私有的。共享前缀可以确保共享标签不会干扰用户自定义的标签。

描述示例类型
app.kubernetes.io/name应用程序的名称mysql字符串
app.kubernetes.io/instance用于唯一确定应用实例的名称mysql-abcxzy字符串
app.kubernetes.io/version应用程序的当前版本(例如,语义版本,修订版哈希等)5.7.21字符串
app.kubernetes.io/component架构中的组件database字符串
app.kubernetes.io/part-of此级别的更高级别应用程序的名称wordpress字符串
app.kubernetes.io/managed-by用于管理应用程序的工具helm字符串
app.kubernetes.io/created-by创建该资源的控制器或者用户controller-manager字符串