Skip to content

Files

Latest commit

cbff0a9 · Dec 29, 2021

History

History
1478 lines (1131 loc) · 65.9 KB

File metadata and controls

1478 lines (1131 loc) · 65.9 KB

五、在实践中利用 Kubernetes 资源

在本章中,我们将设计一个虚构的大规模平台,挑战 Kubernetes 的能力和可扩展性。Hue 平台就是要打造一个无所不知、无所不能的数字助理。色调是你的数字延伸。色相会帮你做任何事情,找到任何东西,而且,在很多情况下会代表你做很多事情。它显然需要存储大量信息,与许多外部服务集成,响应通知和事件,并明智地与您交互。

我们将利用本章中的机会更好地了解 kubectl 和相关工具,并详细探索我们以前见过的熟悉资源,如 pods,以及新资源,如 jobs。我们将探索高级调度和资源管理。在这一章的最后,你将清楚地了解 Kubernetes 是多么令人印象深刻,以及它如何被用作极其复杂的系统的基础。

设计色相平台

在这一节中,我们将设置舞台,并定义令人惊叹的 Hue 平台的范围。色相不是大哥,色相是小弟!顺化会做你允许它做的任何事情。色相可以做很多事情,这可能会让一些人担心,但是你可以选择色相能帮你多少或者帮你多少。准备好一次狂野之旅吧!

定义色调的范围

色相将管理你的数字角色。它会比你自己更了解你。以下是 Hue 可以管理和帮助您的一些服务列表:

  • 搜索和内容聚合
  • 医疗–电子健康记录,DNA 测序
  • 智能家居
  • 金融——银行、储蓄、退休、投资
  • 办公室
  • 社会的
  • 旅行
  • 幸福
  • 家庭的

智能提醒和通知

让我们想想可能性。色相会认识你,但也会认识你的朋友,所有领域其他用户的集合体。Hue 将实时更新其模型。它不会被陈旧的数据所迷惑。它会代表你行事,呈现相关信息,不断学习你的喜好。它可以推荐你可能喜欢的新节目或书籍,根据你的时间表和你的家人或朋友预订餐厅,并控制你的房子自动化。

安全性、身份和隐私

色相是你在网上的代理。有人窃取你的色相身份,甚至只是偷听你的色相互动,其后果是毁灭性的。潜在用户甚至可能不愿意相信顺化组织的身份。让我们设计一个非信任系统,用户可以随时拔掉 Hue 的插头。以下是一些想法:

  • 通过具有多因素授权的专用设备实现强身份,包括多种生物特征因素
  • 频繁轮换的凭据
  • 所有外部服务的快速服务暂停和身份验证(需要每个提供商的原始身份证明)
  • Hue 后端将通过短期令牌与所有外部服务进行交互
  • 将 Hue 构建为松散耦合的微服务的集合,具有很强的划分性
  • gdpr 法规遵从性
  • 端到端加密
  • 避免拥有关键数据(让外部提供商管理)

Hue 的架构将需要支持巨大的变化和灵活性。在现有能力和外部服务不断升级,新的能力和外部服务集成到平台中的地方,它还需要具有很强的可扩展性。这种规模需要微服务,其中每个功能或服务都完全独立于其他服务,除了通过标准和/或可发现的 API 定义良好的接口。

色调成分

在开始我们的微服务之旅之前,让我们回顾一下我们需要为 Hue 构建的组件类型。

用户概要

用户配置文件是一个主要组件,有许多子组件。这是用户的本质,他们的偏好,他们在每个领域的历史,以及 Hue 知道的关于他们的一切。你可以从色相中获得的好处受到轮廓丰富度的强烈影响。但是,配置文件管理的信息越多,如果数据(或部分数据)泄露,您可能遭受的损害就越大。

管理用户档案的一大部分是 Hue 将向用户提供的报告和见解。Hue 将采用复杂的机器学习来更好地理解用户及其与其他用户和外部服务提供商的交互。

用户图

用户图组件对跨多个域的用户之间的交互网络进行建模。每个用户参与多个网络:脸书、Instagram、Twitter 等社交网络;专业网络;爱好网络;和志愿者社区。其中一些网络是临时的,Hue 将能够构建它们来造福用户。Hue 可以利用其丰富的用户连接配置文件来改善交互,甚至无需公开隐私信息。

身份

如前所述,身份管理至关重要,因此需要单独的组件。用户可能更喜欢管理具有不同身份的多个互斥简档。例如,用户可能不愿意将他们的健康档案与他们的社交档案混合在一起,因为这样会有无意中向他们的朋友透露个人健康信息的风险。虽然 Hue 可以为您找到有用的连接,但您可能更喜欢牺牲功能来获得更多隐私。

经授权的

授权者是用户明确授权 Hue 代表其执行某些动作或收集各种数据的关键组件。这包括对物理设备的访问、外部服务的帐户和主动性级别。

外部服务

Hue 是一个外部服务的聚合器。它不是为了取代你的银行、你的健康提供者或你的社交网络。它会保存很多关于你的活动的元数据,但是内容会保留在你的外部服务中。每个外部服务都需要一个专用组件来与外部服务应用编程接口和策略进行交互。当没有应用编程接口可用时,Hue 通过自动化浏览器或本地应用来模拟用户。

通用传感器

顺化价值主张的一大部分是代表用户行事。为了有效地做到这一点,Hue 需要意识到各种事件。例如,如果 Hue 为您预订了假期但它感觉到有更便宜的航班,它可以自动更改您的航班或要求您确认。有无限多的东西可以感知。为了控制传感,需要一个通用的传感器。通用传感器将是可扩展的,但是公开了一个通用接口,即使添加了越来越多的传感器,Hue 的其他部分也可以统一使用该接口。

通用执行器

这是通用传感器的对应物。Hue 需要代表你执行动作;例如,预订航班或预约医生。为此,Hue 需要一个通用的致动器,它可以扩展以支持特定的功能,但可以以统一的方式与其他组件交互,如身份管理器和授权器。

用户学习者

这是色相的大脑。它会持续监控你的所有互动(你授权的),并更新你和你网络中其他用户的模型。这将让 Hue 随着时间的推移变得越来越有用,预测你需要什么,什么会让你感兴趣,提供更好的选择,在正确的时间浮出更多相关信息,避免令人讨厌和专横。

色相微服务

每个组件的复杂性都是巨大的。一些组件,如外部服务、通用传感器和通用致动器,将需要在数百、数千或更多的外部服务中运行,这些服务在 Hue 的控制之外不断变化。即使是用户学习者也需要跨多个领域和领域学习用户的偏好。微服务通过允许 Hue 逐渐发展并增长更多独立的功能来满足这一需求,而不会在其自身的复杂性下崩溃。每个微服务都通过标准接口与通用的 Hue 基础设施服务进行交互,或者通过定义良好的版本化接口与一些其他服务进行交互。每个微服务的表面区域都是可管理的,微服务之间的协调基于标准的最佳实践。

插件

插件是扩展 Hue 而不增加接口的关键。关于插件的事情是,你经常需要跨越多个抽象层的插件链。例如,如果你想让为 Hue 和 YouTube 增加一个新的整合,那么你可以收集很多 YouTube 特有的信息——你的频道、最喜欢的视频、推荐和你看过的视频。为了向用户显示这些信息并允许他们对其进行操作,您需要跨多个组件的插件,最终也需要在用户界面中。智能设计将有助于将建议、选择和延迟通知等操作类别聚合到许多不同的服务中。

插件的伟大之处在于它们可以被任何人开发。最初,Hue 开发团队将不得不开发插件,但是随着 Hue 变得更加流行,外部服务将希望与 Hue 集成并构建 Hue 插件来启用他们的服务。

当然,这将导致插件注册、批准和监管的整个生态系统。

数据存储

Hue 将需要几种类型的数据存储以及每种类型的多个实例来管理其数据和元数据:

  • 关系数据库
  • 图形数据库
  • 时间序列数据库
  • 内存缓存
  • Blob 存储

由于 Hue 的范围,这些数据库中的每一个都必须是集群的、可扩展的和分布式的。

此外,Hue 将在边缘设备上使用本地存储。

无状态微服务

微服务应该大部分是无状态的。这将允许启动特定的实例并快速终止,并在必要时跨基础架构迁移。该状态将由存储管理,并由具有短期访问令牌的微服务访问。Hue 会在适当的时候将频繁访问的数据存储在容易水合的快速缓存中。

无服务器功能

Hue 每个用户的大部分功能将涉及与外部服务或其他 Hue 服务相对较短的交互。对于这些活动,可能没有必要运行需要扩展和管理的完整的持久微服务。更合适的解决方案可能是使用更轻量级的无服务器函数。

基于队列的交互

所有这些微服务都需要相互对话。用户将要求 Hue 代表他们执行任务。外部服务将通知 Hue 各种事件。队列加上无状态微服务提供了完美的解决方案。当相关事件或请求从队列中弹出时,每个微服务的多个实例将监听各种队列并做出响应。无服务器功能也可能因特定事件而触发。这种安排非常稳健,易于扩展。每个组件都可以是冗余的和高度可用的。虽然每个组件都容易出错,但系统非常容错。

队列也可以用于异步 RPC 或请求-响应风格的交互,其中调用实例提供一个私有队列名称,响应被发送到私有队列。

也就是说,有时直接的服务到服务的交互(或者无服务器的功能到服务的交互)虽然一个定义良好的接口更有意义,并且简化了架构。

规划工作流

色相往往需要支持工作流。一个典型的工作流程需要一个高层次的任务,比如预约牙医。它将提取用户的牙医详细信息和时间表,将其与用户的时间表进行匹配,在多个选项之间进行选择,潜在地与用户确认,进行预约,并设置提醒。我们可以将工作流分为全自动和人工工作流,其中人工参与。还有一些工作流程涉及花钱,可能需要额外的批准级别。

自动工作流

自动工作流不需要人工干预。Hue 完全有权从头到尾执行所有步骤。用户分配给 Hue 的自主权越多,它就越有效。用户将能够查看和审核过去和现在的所有工作流。

人工工作流

人工工作流需要与人进行交互。大多数情况下,用户需要从多个选项中做出选择或批准某项操作。但它可能涉及另一项服务的人员。例如,为了预约牙医,Hue 可能必须从秘书那里获得一份可用时间的列表。在未来,Hue 将能够处理与人类的对话,并有可能自动化其中一些工作流程。

了解预算的工作流程

一些工作流程,如支付账单或购买礼物,需要花钱。虽然理论上,Hue 可以被授予对用户银行账户的无限访问权限,但大多数用户可能更愿意为不同的工作流程设置预算,或者只是将支出作为一项人类认可的活动。潜在地,用户可以授予 Hue 对专用账户或账户集的访问权,并且基于提醒和报告,根据需要向 Hue 分配更多或更少的资金。

用 Kubernetes 构建色相平台

在本节中,我们将了解各种 Kubernetes 资源,以及它们如何帮助我们构建 Hue。首先,我们将更好地了解通用的 kubectl,然后我们将看看如何在 Kubernetes 中运行长期运行的进程,在内部和外部公开服务,使用名称空间来限制访问,启动临时作业,以及混合使用非集群组件。显然,Hue 是一个巨大的项目,所以我们将在一个局部集群上演示这些想法,而不是实际构建一个真正的 Hue Kubernetes 集群。把它主要看作是一个思维实验。如果你想探索在 Kubernetes 上构建一个真正的基于微服务的分布式系统,请查看Kubernetes 的实践微服务

有效使用 kubectl

库贝斯克是你的瑞士军刀。它几乎可以围绕一个集群做任何事情。在引擎盖下,kubectl 通过 API 连接到你的集群。它读取您的~/.kube/config文件,该文件包含连接到您的一个或多个集群所需的信息。这些命令分为多个类别:

  • 通用命令:以通用方式为处理资源:创建、获取、删除、运行、应用、修补、替换等等
  • 集群管理命令:处理节点和整个集群:集群信息、证书、流失等等
  • 故障排除命令:描述、记录、连接、执行等
  • 部署命令:对进行部署和缩放:卷展栏、缩放、自动缩放等等
  • 设置命令:为处理标签和注释:标签、注释等等
  • 杂项命令:帮助、配置和版本
  • 定制命令:将的 kustomize.io 功能集成到 kubectl 中

您可以使用 Kubernetes 的config view命令查看配置。

以下是我的本地 k3s 集群的配置:

$ k config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://localhost:6443
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
  user:
    password: 6ce7b64ff48ac13f06af428d92b3d4bf
    username: admin 

理解 kubectl 资源配置文件

许多 kubectl 操作,如create,需要复杂的层次结构(因为 API 需要这种结构)。kubectl 使用 YAML 或 JSON 配置文件。YAML 更简洁,更易读。以下是用于创建 pod 的 YAML 配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: ""
  labels:
    name: ""
  namespace: ""
  annotations: []
  generateName: ""
spec:
     ... 

ApiVersion(堆叠版本)

非常重要的 Kubernetes API 不断发展,可以通过不同版本的 API 支持同一资源的不同版本。

种类

Kind 告诉 Kubernetes 它正在处理什么类型的资源;在这种情况下,pod。这始终是必需的。

[计]元数据

描述 POD 及其运行位置的大量信息:

  • 名称:在其名称空间中唯一标识 pod
  • 标签:可以应用多个标签
  • 命名空间:pod 所属的命名空间
  • 注释:可供查询的注释列表

投机

Spec 是一个 POD 模板,包含了启动 POD 所需的所有信息。它可能非常复杂,因此我们将从多个部分进行探讨:

spec:
  containers: [
      ...
  ],
  "restartPolicy": "",
  "volumes": [] 

容器规格

POD 规格的容器部分是容器规格的列表。每个容器规范都具有以下结构:

name: "",
image: "",
command: [""],
args: [""],
env:
    - name: "",
      value: ""
imagePullPolicy: "",
ports: 
    - containerPort": 0,
      name: "",
      protocol: ""
resources:
    cpu: ""
    memory: "" 

每个容器都有一个映像,一个命令,如果指定的话,会替换 Docker 映像命令。它也有参数和环境变量。然后,当然还有映像拉取策略、端口和资源限制。我们在前面的章节中已经讨论过了。

在 POD 中部署长期运行的微服务

长时间运行的微服务应该在 pods 中运行,并且是无状态的。让我们看看如何为 Hue 的一个微服务创建豆荚。稍后,我们将提高抽象级别并使用部署。

创建豆荚

让我们从创建 Hue 学习者内部服务的常规 pod 配置文件开始。这个服务不需要作为公共服务公开,它会监听一个通知队列,并将其见解存储在一些持久存储中。

我们需要一个简单的容器,它将在 POD 中运行。这可能是有史以来最简单的 Docker 文件,它将模拟 Hue 学习者:

FROM busybox
CMD ash -c "echo 'Started...'; while true ; do sleep 10 ; done" 

它使用busybox基础映像,打印到标准输出Started...,然后进入无限循环,据说这是长期运行的。

我已经构建了两个标记为g1g1/hue-learn:0.3g1g1/hue-learn:v0.4的 Docker 映像,并将它们推送到 Docker Hub 注册中心(g1g1是我的用户名):

$ docker build . -t g1g1/hue-learn:0.3
$ docker build . -t g1g1/hue-learn:0.4
$ docker push g1g1/hue-learn:0.3
$ docker push g1g1/hue-learn:0.4 

现在,这些映像可以被拉进顺化豆荚里的容器里。

我们将在这里使用 YAML,因为它更简洁,更易读。以下是样板和元数据标签:

apiVersion: v1
kind: Pod
metadata:
  name: hue-learner
  labels:
    app: hue
    service: learner
    runtime-environment: production
    tier: internal-service 

我使用版本注释而不是标签的原因是标签用于标识部署中的一组 pods。不允许修改标签。

接下来是重要的containers规范,它为每个容器定义了强制名称和映像:

spec:
  containers:
  - name: hue-learner
    image: g1g1/hue-learn:0.3 

资源部分告诉 Kubernetes 容器的资源需求,这允许更有效和紧凑的调度和分配。这里,容器请求 200 毫 cpu 单位(0.2 核心)和 256 MiB (2 的 28 字节次方):

 resources:
      requests:
        cpu: 200m
        memory: 256Mi 

环境部分允许集群管理员提供容器可用的环境变量。这里它告诉它通过dns发现队列和商店。在测试环境中,它可能使用不同的发现方法:

 env:
    - name: DISCOVER_QUEUE
      value: dns
    - name: DISCOVER_STORE
      value: dns 

用标签装饰豆荚

明智地标记 POD 是灵活操作的关键。它让您可以动态地发展集群,将您的微服务组织成可以统一操作的组,并动态地深入观察不同的子集。

例如,我们的 Hue 学习窗格有以下标签:

  • 运行时环境:生产
  • 等级:内部服务

runtime-environment标签允许对属于某个环境的所有 POD 执行全局操作。tier标签可用于查询属于特定层的所有 POD。这些只是例子;你的想象力是这里的极限。

下面是如何用get pods命令列出标签:

$ kubectl get po -n kube-system --show-labels
NAME                       READY   STATUS    RESTARTS   AGE   LABELS
coredns-b7464766c-s4z28    1/1     Running   3          15d   k8s-app=kube-dns,pod-template-hash=b7464766c
svclb-traefik-688zv        2/2     Running   6          15d   app=svclb-traefik,controller-revision-hash=66fd644d6,pod-template-generation=1,svccontroller.k3s.cattle.io/svcname=traefik
svclb-traefik-hfk8t        2/2     Running   6          15d   app=svclb-traefik,controller-revision-hash=66fd644d6,pod-template-generation=1,svccontroller.k3s.cattle.io/svcname=traefik
svclb-traefik-kp9wh        2/2     Running   6          15d   app=svclb-traefik,controller-revision-hash=66fd644d6,pod-template-generation=1,svccontroller.k3s.cattle.io/svcname=traefik
svclb-traefik-sgmbg        2/2     Running   6          15d   app=svclb-traefik,controller-revision-hash=66fd644d6,pod-template-generation=1,svccontroller.k3s.cattle.io/svcname=traefik
traefik-56688c4464-c4sfq   1/1     Running   3          15d   app=traefik,chart=traefik-1.64.0,heritage=Tiller,pod-template-hash=56688c4464,release=traefik 

现在,如果您想过滤并仅列出traefik app类型的豆荚:

$ kubectl get po -n kube-system -l app=traefik
NAME                       READY   STATUS    RESTARTS   AGE
traefik-56688c4464-c4sfq   1/1     Running   3          15d 

通过部署部署长期运行的流程

在一个大规模的系统中,豆荚永远不应该只是被创造和释放。如果一个 POD 由于任何原因意外死亡,你需要另一个 POD 来替换它,以保持整体容量。您可以自己创建复制控制器或副本集,但这为错误以及部分失败的可能性敞开了大门。当你以声明性的方式启动你的 POD 时,指定你想要多少副本更有意义。这就是 Kubernetes 部署的目的。

让我们使用 Kubernetes 部署资源来部署我们的 Hue 学习者微服务的三个实例。请注意,部署对象在 Kubernetes 1.9 中变得稳定:

apiVersion: apps/v1
 kind: Deployment
 metadata:
  name: hue-learn
  labels:
    app: hue
    service: learn
 spec:
  replicas: 3
  selector:
    matchLabels:
      app: hue
      service: learn
  template:
    metadata:
      labels:
        app: hue
    spec:
             <same spec as in the pod template> 

POD 规格与之前 POD 配置文件中的spec部分相同。

让我们创建部署并检查其状态:

$ kubectl create -f hue-learn-deployment.yaml
deployment.apps/hue-learn created
$ kubectl get deployment hue-learn
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
hue-learn   3/3     3            3           32s
$ kubectl get pods -l app=hue
NAME                         READY   STATUS    RESTARTS   AGE
hue-learn-558f5c45cd-fbvpj   1/1     Running   0          81s
hue-learn-558f5c45cd-s6vkk   1/1     Running   0          81s
hue-learn-558f5c45cd-tdlpq   1/1     Running   0          81s 

使用kubectl describe命令可以获得更多关于部署的信息。

更新部署

Hue 平台是一个庞大且不断发展的系统。你需要不断升级。部署可以更新,以无痛的方式推出更新。您可以更改 pod 模板来触发完全由 Kubernetes 管理的滚动更新。

目前,所有 POD 都运行 0.3 版:

$ kubectl get pods -o jsonpath='{.items[*].spec.containers[0].image}'
g1g1/hue-learn:0.3
g1g1/hue-learn:0.3
g1g1/hue-learn:0.3 

让我们更新部署以升级到 0.4 版本。修改部署文件中的映像版本。不要修改标签;它会导致错误。保存到hue-learn-deployment-0.4.yaml。然后我们可以使用apply命令升级版本,并验证 POD 现在运行 0.4:

$ kubectl apply -f hue-learn-deployment-0.4.yaml
deployment "hue-learn" updated 

请注意,由于滚动更新操作的性质,可能需要几分钟才能看到以下输出:

$ kubectl get pods -o jsonpath='{.items[*].spec.containers[0].image}'
g1g1/hue-learn:0.4
g1g1/hue-learn:0.4
g1g1/hue-learn:0.4 

请注意,会创建新的 pods,并且原始的 0.3 pods 会以滚动更新的方式终止:

$ kubectl get pods
NAME                         READY   STATUS        RESTARTS   AGE    IP           NODE                    
hue-learn-558f5c45cd-fbvpj   1/1     Terminating   0          8m7s   10.42.3.15   k3d-k3s-default-server  
hue-learn-558f5c45cd-s6vkk   0/1     Terminating   0          8m7s   10.42.0.7    k3d-k3s-default-worker-0
hue-learn-558f5c45cd-tdlpq   0/1     Terminating   0          8m7s   10.42.2.15   k3d-k3s-default-worker-2
hue-learn-5c9bb545d9-lggk7   1/1     Running       0          38s    10.42.2.16   k3d-k3s-default-worker-2
hue-learn-5c9bb545d9-pwflv   1/1     Running       0          31s    10.42.1.10   k3d-k3s-default-worker-1
hue-learn-5c9bb545d9-q25hl   1/1     Running       0          35s    10.42.0.8    k3d-k3s-default-worker-0 

分离内部和外部服务

内部服务是仅由集群中的其他服务或作业(或登录并运行临时工具的管理员)直接访问的服务。在某些情况下,根本不访问内部服务,只是执行它们的功能,并将它们的结果存储在一个持久存储中,其他服务以解耦的方式访问该存储。

但是有些服务需要向用户或外部程序公开。让我们看看一个假的 Hue 服务,它为用户管理一个提醒列表。它并没有做太多事情——只是返回一个固定的提醒列表——但是我们将使用它来说明如何公开服务。我已经向 Docker Hub 推送了一张hue-reminders图片:

docker push g1g1/hue-reminders:3.0 

部署内部服务

这里是部署,与 Hue-learning 部署非常相似,只是我去掉了annotationsenvresources部分,只保留了一两个标签来节省空间,在容器中增加了一个ports部分。这一点至关重要,因为服务必须公开一个端口,其他服务可以通过该端口访问它:

apiVersion: apps/v1
kind: Deploymen
tmetadata:
  name: hue-reminders
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hue
      service: reminders
  template:
    metadata:
      name: hue-reminders
      labels:
        app: hue
        service: reminders
    spec:
      containers:
      - name: hue-reminders
        image: g1g1/hue-reminders:3.0
        ports:
        - containerPort: 8080 

当我们运行部署时,两个hue-remindersPOD 被添加到集群中:

$ kubectl create -f hue-reminders-deployment.yaml
deployment.apps/hue-reminders created
$ kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
hue-learn-5c9bb545d9-lggk7      1/1     Running   0          22m
hue-learn-5c9bb545d9-pwflv       1/1     Running   0          22m
hue-learn-5c9bb545d9-q25hl      1/1     Running   0          22m
hue-reminders-9b7d65d86-4kf5t   1/1     Running   0          7s
hue-reminders-9b7d65d86-tch4w   1/1     Running   0          7s 

好的。POD 正在运行。理论上,其他服务可以查找或配置其内部 IP 地址,直接访问即可,因为它们都在同一个网络地址空间。但这不符合比例。每次提醒的豆荚死亡并被新豆荚取代,或者当我们只是扩大豆荚的数量时,所有访问这些豆荚的服务都必须知道它。Kubernetes 服务通过为所有 POD 提供一个稳定的接入点来解决这个问题。以下是服务定义:

apiVersion: v1
kind: Service
metadata:
  name: hue-reminders
  labels:
    app: hue
    service: reminders
spec:
  ports:
  - port: 8080
    protocol: TCP
  selector:
    app: hue
    service: reminders 

该服务有一个选择器,通过匹配的标签来确定支撑 POD。它还公开了一个端口,其他服务将使用它来访问它(它不一定是与容器的端口相同的端口)。

协议字段可以是 TCP、UDP 之一,因为 Kubernetes 1.12 也是 SCTP 协议(如果启用了功能门)。

创建色调提醒服务

让我们创建服务并稍微探索一下:

$ kubectl create -f hue-reminders-service.yaml
service/hue-reminders created
$ kubectl describe svc hue-reminders
Name:              hue-reminders
Namespace:         default
Labels:            app=hue
                   service=reminders
Annotations:       <none>
Selector:          app=hue,service=reminders
Type:              ClusterIP
IP:                10.43.166.58
Port:              <unset>  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.42.1.12:8080,10.42.2.17:8080
Session Affinity:  None
Events:            <none> 

服务已启动并运行。其他 POD 可以通过环境变量或 DNS 找到它。所有服务的环境变量都在 pod 创建时设置。这意味着,如果在您创建服务时一个 pod 已经在运行,您将不得不终止它,并让 Kubernetes 用环境变量重新创建它(您通过部署创建您的 pod,对吗?):

$ kubectl exec hue-learn-5c9bb545d9-w8hrr -- printenv | grep HUE_REMINDERS_SERVICE
HUE_REMINDERS_SERVICE_HOST=10.43.166.58
HUE_REMINDERS_SERVICE_PORT=8080 

但是使用 DNS 要简单得多。您的服务域名是:

<service name>.<namespace>.svc.cluster.local 
$ kubectl exec hue-learn-5c9bb545d9-w8hrr -- nslookup hue-reminders.default.svc.cluster.local
Server:    10.43.0.10
Address 1: 10.43.0.10 kube-dns.kube-system.svc.cluster.local
Name:      hue-reminders.default.svc.cluster.local
Address 1: 10.43.247.147 hue-reminders.default.svc.cluster.local 

现在,默认名称空间中的所有服务都可以通过其服务端点和端口8080访问hue-reminders服务:

$ kubectl exec hue-learn-5c9bb545d9-w8hrr -- wget -q -O - hue-reminders.default.svc.cluster.local:8080
Dentist appointment at 3pm
Dinner at 7pm 

是的,此刻hue-reminders总是返回同样的两个提醒:

Dentist appointment at 3pm
Dinner at 7pm 

向外部公开服务

该服务可在集群内部访问。如果你想让世界了解它,Kubernetes 提供了三种方法:

  • 为直接访问配置节点端口
  • 如果在云环境中运行,请配置云负载平衡器
  • 如果在裸机上运行,请配置您自己的负载平衡器

在为外部访问配置服务之前,您应该确保它是安全的。Kubernetes 文档有一个很好的例子,它涵盖了这里所有血淋淋的细节:

https://github . com/kubrines/examples/blob/master/staging/https-engine/readme . MD

我们已经在第 4 章**中讲述了保护 Kubernetes的原则。

这里是hue-reminders服务的spec部分,通过NodePort与世界接触:

spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
    protocol: TCP
    name: http
  - port: 443
    protocol: TCP
    name: https
  selector:
    app: hue-reminders 

通过NodePort公开服务的主要缺点是端口号在所有服务之间共享,您必须在整个集群中全局协调它们以避免冲突。

但是还有其他原因,您可能希望避免直接公开 Kubernetes 服务,并且您可能更喜欢在服务前面使用 Ingress 资源。

进入

入口是一个 Kubernetes 配置对象,让你向外界公开一个服务,并处理很多细节。它可以执行以下操作:

  • 为您的服务提供一个外部可见的网址
  • 负载平衡流量
  • 终止 SSL
  • 提供基于名称的虚拟主机

要使用入口,您必须在集群中运行入口控制器。请注意,入口是在 Kubernetes 1.1 中引入的,但它仍处于测试阶段,有许多限制。如果你在 GKE 运行你的集群,你可能没问题。否则,请小心操作。入口控制器目前的局限性之一是它不是为扩展而构建的。因此,它还不是色相平台的好选择。我们将在第 10 章探索高级网络中更详细地介绍入口控制器。

以下是入口资源的外观:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80 

请注意注释,该注释提示它是与 Nginx 入口控制器一起工作的入口对象。还有许多其他入口控制器,它们通常使用注释来编码它们需要的信息,这些信息不会被入口对象本身及其规则捕获。

其他入口控制器包括:

  • 特拉菲克
  • 格洛
  • 轮廓
  • 自动气象站入口控制器
  • HAPRoxy 入口
  • “旅行者”号

高级调度

Kubernetes 最强的套装之一是它强大而灵活的调度程序。简单地说,调度器的工作就是选择节点来运行新创建的 pods。理论上,调度程序甚至可以在节点之间移动现有的豆荚,但实际上它目前并没有这样做,而是将这一功能留给了其他组件。

默认情况下,调度程序遵循几个指导原则,包括:

  • 跨节点从同一副本集或有状态集拆分 pods
  • 将 pod 调度到有足够资源满足 pod 请求的节点
  • 平衡节点的整体资源利用率

这是非常好的默认行为,但是有时您可能想要更好地控制特定的 pod 放置。Kubernetes 1.6 引入了几个高级调度选项,让您可以精细控制在哪些节点上调度或不调度哪些 POD,以及哪些 POD 要一起调度或单独调度。

让我们在 Hue 的上下文中回顾一下这些机制。

节点选择器

节点选择器非常简单。一个 pod 可以在它的规范中指定它想要调度的节点。例如,trouble-shooter窗格有一个nodeSelector ,它指定了worker-2节点的kubernetes.io/hostname标签:

apiVersion: v1
kind: Pod
metadata:
  name: trouble-shooter
  labels:
    role: trouble-shooter
spec:
  nodeSelector:
    kubernetes.io/hostname: k3d-k3s-default-worker-2
  containers:
  - name: trouble-shooter
    image: g1g1/py-kube:0.2
    command: ["bash"]
    args: ["-c", "echo started...; while true ; do sleep 1 ; done"] 

当创建这个 pod 时,它确实被安排到worker-2节点:

$ k apply -f trouble-shooter.yaml
pod/trouble-shooter created
$ k get po trouble-shooter -o jsonpath='{.spec.nodeName}'
k3d-k3s-default-worker-2 

污点和宽容

您可以污染节点,以防止在该节点上调度豆荚。例如,如果您不希望在主节点上调度 pods,这可能会很有用。容忍允许豆荚声明他们可以“容忍”特定的节点污染,然后这些豆荚可以被安排在被污染的节点上。一个节点可以有多个污点,一个 pod 可以有多个容忍度。污点是三重的:关键、价值、效果。密钥和值用于识别污点。效果之一是:

  • NoSchedule(除非节点容忍污染,否则不会为节点安排任何豆荚)
  • PreferNoSchedule(软版NoSchedule;调度器将尝试不调度不能容忍污染的 POD)
  • NoExecute(不会安排新的豆荚,但也将驱逐不容忍污染的现有豆荚)

目前有一个hue-learn pod 运行在主节点(k3d-k3s-default-server)上:

$ kubectl get po -o wide 
NAME                            READY   STATUS    RESTARTS   AGE     IP           NODE                       
hue-learn-5c9bb545d9-dk4c4      1/1     Running   0          7h40m   10.42.3.17   k3d-k3s-default-server
hue-learn-5c9bb545d9-sqx28      1/1     Running   0          7h40m   10.42.2.18   k3d-k3s-default-worker-2
hue-learn-5c9bb545d9-w8hrr      1/1     Running   0          7h40m   10.42.0.11   k3d-k3s-default-worker-0
hue-reminders-6f9f54d8f-hwjwd   1/1     Running   0          3h51m   10.42.0.13   k3d-k3s-default-worker-0
hue-reminders-6f9f54d8f-p4z8z   1/1     Running   0          3h51m   10.42.1.14   k3d-k3s-default-worker-1 

让我们污染我们的主节点:

$ k taint nodes k3d-k3s-default-server master=true:NoExecute
node/k3d-k3s-default-server tainted 

我们现在可以回顾污点:

$ k get nodes k3d-k3s-default-server -o jsonpath='{.spec.taints[0]}'
map[effect:NoExecute key:master value:true] 

是的,成功了!主节点上现在没有预定的 POD 了。主机上的 POD 被终止,新的 POD(hue-learn-5c9bb545d9-nn4xk)现在正在worker-1上运行:

$ kubectl get po -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP           NODE
hue-learn-5c9bb545d9-nn4xk      1/1     Running   0          3m46s   10.42.1.15   k3d-k3s-default-worker-1
hue-learn-5c9bb545d9-sqx28      1/1     Running   0          9h      10.42.2.18   k3d-k3s-default-worker-2
hue-learn-5c9bb545d9-w8hrr      1/1     Running   0          9h      10.42.0.11   k3d-k3s-default-worker-0
hue-reminders-6f9f54d8f-hwjwd   1/1     Running   0          6h      10.42.0.13   k3d-k3s-default-worker-0
hue-reminders-6f9f54d8f-p4z8z   1/1     Running   0          6h      10.42.1.14   k3d-k3s-default-worker-1
trouble-shooter                 1/1     Running   0          16m     10.42.2.20   k3d-k3s-default-worker-2 

要允许豆荚容忍污染,在它们的规格中添加一个容忍,例如:

tolerations:
- key: "master"
  operator: "Equal"
  value: "true"
  effect: "NoSchedule" 

节点亲和力和反亲和力

节点亲和力是更复杂的形式nodeSelector。它有三个主要优点:

  • 丰富的选择标准(nodeSelector只是标签上精确匹配的AND
  • 规则可以是软的
  • 可以使用NotInDoesNotExist等运算符实现反亲和

注意如果同时指定nodeSelectornodeA ffi nity,那么 pod 将只被调度到满足这两个要求的节点。

例如,如果我们将以下部分添加到我们的trouble-shooter pod 中,它将无法在任何节点上运行,因为它与nodeSelector冲突:

affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            - k3d-k3s-default-worker-2 

Pod 亲和力和反亲和力

Pod 相似性和反相似性为管理工作负载的运行提供了又一个途径。到目前为止,我们讨论的所有方法——节点选择器、污染/容忍、节点相似性/反相似性——都是关于将荚分配给节点。但是荚果亲缘关系是关于不同荚果之间的关系。Pod affinity 还有其他几个与之相关的概念:命名空间(因为 Pod 是命名空间)、拓扑区域(节点、机架、云提供商区域、云提供商区域)和权重(用于首选调度)。一个简单的例子是,如果你想让hue-reminders总是被安排在一个trouble-shooterPOD 中。让我们看看如何在hue-reminders部署的 pod 模板规范中定义它:

 affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: role
                operator: In
                values:
                - trouble-shooter
            topologyKey: failure-domain.beta.kubernetes.io/zone 

然后重新部署hue-reminders后,所有hue-remindersPOD 计划在trouble-shooterPOD 旁边的worker-2上运行:

$ k get po -o wide
NAME                             READY   STATUS    RESTARTS   AGE    IP           NODE                       
hue-learn-5c9bb545d9-nn4xk       1/1     Running   0          156m   10.42.1.15   k3d-k3s-default-worker-1
hue-learn-5c9bb545d9-sqx28       1/1     Running   0          12h    10.42.2.18   k3d-k3s-default-worker-2
hue-learn-5c9bb545d9-w8hrr       1/1     Running   0          12h    10.42.0.11   k3d-k3s-default-worker-0
hue-reminders-5cb9b845d8-kck5d   1/1     Running   0          14s    10.42.2.24   k3d-k3s-default-worker-2
hue-reminders-5cb9b845d8-kpvx5   1/1     Running   0          14s    10.42.2.23   k3d-k3s-default-worker-2
trouble-shooter                  1/1     Running   0          14m    10.42.2.21   k3d-k3s-default-worker-2 

使用命名空间来限制访问

Hue 项目进展顺利,我们有几百个微服务和大约 100 个开发人员和 DevOps 工程师在进行这项工作。相关的微服务组出现了,您会注意到这些组中的许多是非常自治的。他们完全无视其他群体。此外,还有一些敏感领域,如卫生和金融,您希望更有效地控制对这些领域的访问。输入名称空间。

让我们创建一个新的服务Hue-finance,并将其放入一个名为restricted的新命名空间中。

以下是新restricted命名空间的 YAML 文件:

kind: Namespace
apiVersion: v1
metadata:
  name: restricted
  labels:
    name: restricted 

我们可以像往常一样创建它:

$ kubectl create -f restricted-namespace.yaml
namespace "restricted" created 

一旦创建了名称空间,我们就可以为名称空间配置一个上下文。这将允许限制特定用户对此命名空间的访问:

$ kubectl config set-context restricted --namespace=restricted --cluster=default --user=default
Context "restricted" set.
$ kubectl config use-context restricted
Switched to context "restricted". 

让我们检查一下我们的集群配置:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://localhost:6443
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
- context:
    cluster: default
    namespace: restricted
    user: default
  name: restricted
current-context: restricted
kind: Config
preferences: {}
users:
- name: default
  user:
    password: <REDACTED>
    username: admin 

可以看到,现在有两个上下文,当前上下文受限。如果我们愿意,我们甚至可以创建拥有自己的凭据的专用用户,允许他们在受限的命名空间中操作。在这种情况下,这是不必要的,因为我们是集群管理员。

现在,在这个空的名称空间中,我们可以创建我们的 hue-finance 服务,它将独立于:

$ kubectl create -f hue-finance-deployment.yaml
deployment.apps/hue-learn created
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
hue-finance-7d4b84cc8d-gcjnz   1/1       Running   0          6s
hue-finance-7d4b84cc8d-tqvr9   1/1       Running   0          6s
hue-finance-7d4b84cc8d-zthdr   1/1       Running   0          6s 

你不必切换上下文。您也可以使用--namespace=<namespace>--all-namespaces命令行开关,但是当在同一个非默认名称空间中运行一段时间时,最好将上下文设置为该名称空间。

使用分级集群结构的定制

这是不是错别字。库比特最近合并了 kustomize(https://kustomize.io/)的功能。这是一种不用模板配置 Kubernetes 的方法。kustomize 功能集成到 kubectl 本身的方式有很多戏剧性,因为还有其他选择,如果 kubectl 应该如此固执己见,这是一个悬而未决的问题。但那都是过去的事了。底线是kubectl apply -k解锁了配置选项的宝库。让我们了解它帮助我们解决了什么问题,并利用它来帮助我们管理 Hue。

了解定制的基础知识

Kustomize 的创建是为了响应像 Helm 这样重模板的方法来配置和定制 Kubernetes 集群。它是围绕声明性应用管理的原则设计的。它需要一个有效的 Kubernetes YAML 清单(基础),并通过覆盖额外的 YAML 补丁(覆盖)来专门化或扩展它。覆盖取决于它们的基础。所有文件都是有效的 YAML 文件。没有占位符。

一个kustomization.yaml文件控制过程。任何包含kustomization.yaml文件的目录都称为根目录。例如:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: staging
commonLabels:
    environment: staging
bases:
  - ../base
patchesStrategicMerge:
  - hue-learn-patch.yaml
resources:
  - namespace.yaml 

kustomize 可以在 GitOps 环境中很好地工作,在 Git repo 中存在不同的 kustomizations,对基础、覆盖或kustomization.yaml文件的更改会触发部署。

kustomize 的最佳用例之一是将您的系统组织到多个名称空间中,例如暂存和生产。让我们重新构建 Hue 平台部署清单。

配置目录结构

首先,我们需要一个包含所有清单共性的base目录。然后我们将有一个包含stagingproduction子目录的overlays目录:

$ tree
.
├── base
│   ├── hue-learn.yaml
│   └── kustomization.yaml
├── overlays
│   ├── production
│   │   ├── kustomization.yaml
│   │   └── namespace.yaml
│   └── staging
│       ├── hue-learn-patch.yaml
│       ├── kustomization.yaml
│       └── namespace.yaml 

base目录中的hue-learn.yaml文件只是一个例子。那里可能有很多文件。让我们快速回顾一下:

apiVersion: v1
kind: Pod
metadata:
  name: hue-learner
  labels:
    tier: internal-service
spec:
  containers:
  - name: hue-learner
    image: g1g1/hue-learn:0.3
    resources:
      requests:
        cpu: 200m
        memory: 256Mi
    env:
    - name: DISCOVER_QUEUE
      value: dns
    - name: DISCOVER_STORE
      value: dns 

它与我们之前创建的清单非常相似,但没有app: hue标签。不需要,因为标签是由kustomization.yaml文件提供的,作为所有列出资源的通用标签:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
  app: hue
resources:
  - hue-learn.yaml 

应用自定义

我们可以通过运行base目录上的kubectl kustomize命令来观察结果。您可以看到添加了共同标签app: hue:

$ kubectl kustomize base
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: hue
    tier: internal-service
  name: hue-learner
spec:
  containers:
  - env:
    - name: DISCOVER_QUEUE
      value: dns
    - name: DISCOVER_STORE
      value: dns
    image: g1g1/hue-learn:0.3
    name: hue-learner
    resources:
      requests:
        cpu: 200m
        memory: 256Mi 

为了实际部署 kustomization,我们可以运行kubectl -k apply。但是基地不应该单独部署。让我们深入overlays/staging目录并检查它。

namespace.yaml文件只是创建了暂存命名空间。它还将受益于所有的 kustomizations,正如我们很快会看到的:

apiVersion: v1
kind: Namespace
metadata:
  name: staging 

kustomization.yaml文件增加了常用标签environment: staging。它依赖于base目录并将namespace.yaml文件添加到资源列表中(该列表已经包括了来自base目录的hue-learn.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: staging
commonLabels:
    environment: staging
bases:
  - ../../base
patchesStrategicMerge:
  - hue-learn-patch.yaml
resources:
  - namespace.yaml 

但这还不是全部。kustomization 最有趣的部分是修补。

修补

补丁添加或替换部分清单。他们从不移除现有资源或部分资源。hue-learn-patch.yamlg1g1/hue-learn:0.3 to g1g1/hue-learn:0.4更新映像:

apiVersion: v1
kind: Pod
metadata:
  name: hue-learner
spec:
  containers:
  - name: hue-learner
    image: g1g1/hue-learn:0.4 

这是一次战略合并。Kustomize 支持另一种称为JsonPatches6902的补丁。它基于RFC 6902。它通常比战略合并更简洁。请注意,由于 JSON 是 YAML 的一个子集,我们可以对 JSON 6902 补丁使用 YAML 语法。这是更改映像版本的同一个补丁:

- op: replace
  path: /spec/containers/0/image
  value: g1g1/hue-learn:0.4 

自定义整个登台命名空间

以下是 kustomize 在overlays/staging目录下运行时生成的内容:

$ kubextl kustomize overlays/staging
apiVersion: v1
kind: Namespace
metadata:
  labels:
    environment: staging
  name: staging
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: hue
    environment: staging
    tier: internal-service
  name: hue-learner
  namespace: staging
spec:
  containers:
  - env:
    - name: DISCOVER_QUEUE
      value: dns
    - name: DISCOVER_STORE
      value: dns
    image: g1g1/hue-learn:0.4
    name: hue-learner
    resources:
      requests:
        cpu: 200m
        memory: 256Mi 

请注意,命名空间没有从基础继承app: hue标签,而只是从其本地 kustomization 文件继承了environment: staging标签。另一只手上的hue-learnPOD 也获得了所有标签和namespace名称。

是时候将其部署到集群了:

$ kubectl apply -k overlays/staging/
namespace/staging created
pod/hue-learner created 

现在,我们可以在新创建的staging命名空间中查看 pod:

$ kubectl get po -n staging
NAME          READY   STATUS    RESTARTS   AGE
hue-learner   1/1     Running   0          8s 

启动作业

Hue 已经进化,有很多作为微服务部署的长时间运行的过程,但是它也有很多任务运行,完成一些目标,然后退出。Kubernetes 通过作业资源支持此功能。Kubernetes 作业管理一个或多个 POD,并确保它们运行直到成功。如果作业管理的一个 pod 失败或被删除,则作业将运行新的 pod,直到成功。

还有许多针对 Kubernetes 的无服务器或功能即服务解决方案,但它们都是建立在本地 Kubernetes 之上的。我们将用整整一章的时间来讨论无服务器计算。

这是一个运行 Python 进程来计算 5 的阶乘的作业(提示:它是 120):

apiVersion: batch/v1
kind: Job
metadata:
  name: factorial5
spec:
  template:
    metadata:
      name: factorial5
    spec:
      containers:
      - name: factorial5
        image: g1g1/py-kube:0.2
        command: ["python",
                  "-c",
                  "import math; print(math.factorial(5))"]
      restartPolicy: Never 

注意restartPolicy必须是NeverOnFailure。默认值-Always-无效,因为作业在成功完成后不应重新启动。

让我们开始作业并检查其状态:

$ kubectl create -f factorial-job.yaml
job.batch/factorial5 created
$ kubectl get jobs
NAME         COMPLETIONS   DURATION   AGE
factorial5   1/1           2s         2m53s 

已完成任务的 POD 显示状态为Completed:

$ kubectl get pods
NAME                             READY   STATUS      RESTARTS   AGE
factorial5-tf9qb                 0/1     Completed   0          26m
hue-learn-5c9bb545d9-nn4xk       1/1     Running     3          2d11h
hue-learn-5c9bb545d9-sqx28       1/1     Running     3          2d21h
hue-learn-5c9bb545d9-w8hrr       1/1     Running     3          2d21h
hue-reminders-5cb9b845d8-kck5d   1/1     Running     3          2d8h
hue-reminders-5cb9b845d8-kpvx5   1/1     Running     3          2d8h
trouble-shooter                  1/1     Running     3          2d9h 

factorial5POD 的状态为Completed。让我们在日志中查看它的输出:

$ kubectl logs factorial5-tf9qb
120 

并行运行作业

也可以并行运行作业。规格中有两个字段,称为completionsparallelismcompletions默认设置为1。如果您想要多次成功完成,请增加该值。parallelism决定发射多少 POD。即使parallelism值更大,作业也不会启动超过成功完成所需的 POD。

让我们运行另一个只休眠 20 秒的作业,直到它成功完成三次。我们将使用 6 的parallelism因子,但是将只发射三个 POD:

apiVersion: batch/v1
kind: Job
metadata:
  name: sleep20
spec:
  completions: 3
  parallelism: 6
  template:
    metadata:
      name: sleep20
    spec:
      containers:
      - name: sleep20
        image: g1g1/py-kube:0.2
        command: ["python",
                  "-c",
                  "import time; print('started...');
                   time.sleep(20); print('done.')"]
      restartPolicy: Never 

我们现在可以看到所有的工作都完成了,并且 POD 还没有准备好,因为它们已经完成了工作:

$ kubectl get pods
NAME               READY     STATUS    RESTARTS   AGE
sleep20-2mb7g      0/1       Completed   0          17m
sleep20-74pwh      0/1       Completed   0          15m
sleep20-txgpz      0/1       Completed   0          15m 

清理已完成的作业

当一项工作完成时,它会留下来,它的豆荚也会留下来。这是设计好的,所以你可以看日志或者连接到豆荚进行探索。但是通常,当一项工作成功完成后,就不再需要它了。你有责任清理已完成的工作和它们的容器。最简单的方法是简单地删除作业对象,这也将删除所有 pods:

$ kubectl get jobs
NAME         COMPLETIONS   DURATION   AGE
factorial5   1/1           2s         6h59m
sleep20      3/3           3m7s       5h54m
$ kubectl delete job factorial5
job.batch "factorial5" deleted
$ kubectl delete job sleep20
job.batch "sleep20" deleted 

计划 cron 作业

Kubernetes cron 作业是在指定时间内运行的作业,一次或重复运行。它们表现为/etc/crontab文件中指定的常规 Unix cron 作业。

在 Kubernetes 1.4 中,它们被称为调度作业。但在 Kubernetes 1.5 中,名称改为CronJob。从 Kubernetes 1.8 开始,CronJob资源在 API 服务器中被默认启用,不再需要传递--runtime-config标志,但它仍处于测试阶段。以下是每分钟启动 cron 作业的配置,提醒您进行伸展。在时间表中,您可以将*替换为?:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cron-demo
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            name: cron-demo
        spec:
          containers:
          - name: cron-demo
            image: g1g1/py-kube:0.2
            args:
            - python
            - -c
            - from datetime import datetime; print('[{}] CronJob demo here...'.format(datetime.now()))
          restartPolicy: OnFailure 

在 pod 规范中,在作业模板下,我添加了一个标签名:cron-demo。原因是 Kubernetes 给 cron jobs 和它们的 pods 分配了带有随机前缀的名称。该标签允许您轻松发现特定 cron 作业的所有单元。

请参见以下命令行:

$ kubectl get pods
NAME                         READY   STATUS      RESTARTS   AGE
cron-demo-1568250120-7jrq8   0/1     Completed   0          3m
cron-demo-1568250180-sw5qq   0/1     Completed   0          2m
cron-demo-1568250240-mmfzm   0/1     Completed   0          1m 

请注意,cron 作业的每次调用都会启动一个带有新 pod 的新作业对象:

$ kubectl get jobs
NAME                   COMPLETIONS   DURATION   AGE
cron-demo-1568244780   1/1           2s         1m
cron-demo-1568250060   1/1           3s         88s
cron-demo-1568250120   1/1           3s         38s 

像往常一样,您可以使用logs命令检查已完成 cron 作业的 pod 输出:

$ kubectl logs cron-demo-1568250240-mmfzm
[2020-06-01 01:04:03.245204] CronJob demo here... 

当您删除 cron 作业时,它会停止调度新作业,然后删除所有现有作业对象及其创建的所有 pods。

您可以使用指定的标签(本例中为name=cron-demo)来定位 cron 作业启动的所有作业对象。您也可以暂停 cron 作业,这样它就不会在不删除已完成作业和 pods 的情况下创建更多作业。您也可以通过设置规格历史限制来管理以前的作业:.spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit

混合非集群组件

Kubernetes 集群中的大多数实时系统组件将与集群外组件通信。这些服务可以是完全外部的第三方服务,可通过一些应用编程接口访问,但也可以是运行在同一本地网络中的内部服务,由于各种原因,这些服务不属于 Kubernetes 集群。

这里有两类:集群网络内部和集群网络外部。为什么区别很重要?

集群外网络组件

这些组件不能直接访问集群。他们只能通过 API、外部可见的网址和公开的服务来访问它。这些组件与任何外部用户一样对待。通常,集群组件将只使用外部服务,这不会带来安全问题。例如,在之前的工作中,我们有一个 Kubernetes 集群,它报告了第三方服务的异常(https://sentry.io/welcome/)。这是从 Kubernetes 集群到第三方服务的单向通信。

集群内网络组件

这些是在网络内部运行但不受 Kubernetes 管理的组件。运行这样的组件有很多原因。它们可能是尚未实现 kubernetized 的遗留应用,或者是一些不容易在 Kubernetes 内部运行的分布式数据存储。在网络内部运行这些组件的原因是为了提高性能,并与外部世界隔离,以便这些组件和 pods 之间的流量更加安全。作为同一网络的一部分可以确保低延迟,减少身份验证需求既方便又可以避免身份验证开销。

用 Kubernetes 管理 Hue 平台

在本节中,我们将了解 Kubernetes 如何帮助运营像 Hue 这样的巨大平台。Kubernetes 本身提供了许多功能来协调 pods 并管理配额和限制,检测某些类型的一般故障(硬件故障、进程崩溃、无法访问的服务)并从中恢复。但是在像 Hue 这样的复杂系统中,pods 和服务可能已经启动并运行,但是处于无效状态,或者正在等待其他依赖项来执行它们的职责。这很棘手,因为如果一个服务或 pod 还没有准备好,但是已经在接收请求,那么你需要以某种方式管理它:失败(将责任推给调用者),重试(多少次?多久?多久一次?),并为以后排队(谁来管理这个队列?).

如果整个系统能够意识到不同组件的就绪状态,或者组件只有在真正就绪时才可见,通常会更好。Kubernetes 不知道 Hue,但它提供了几种机制,如活跃度探测器、就绪探测器和 init 容器,以支持集群的特定于应用的管理。

使用活性探测器来确保您的容器是活动的

Kubelets 看着你的容器。如果容器进程崩溃,kubelet 将根据重启策略处理它。但这并不总是足够的。您的进程可能不会崩溃,而是会陷入无限循环或死锁。重启政策可能不够细致。有了活跃度探测器,你就可以决定什么时候一个容器被认为是活的。这是一个 Hue 音乐服务的 pod 模板。它有一个livenessProbe部分,使用httpGet探头。http 探测需要一个方案(HTTP 或 https,默认为 HTTP;一台主机,默认为 PodIp 一条小路;和端口)。

如果 HTTP 状态在200399之间,则认为探测成功。您的容器可能需要一些时间来初始化,因此您可以指定一个initialDelayInSeconds。在此期间,kubelet 不会进行活跃度检查:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: music
    service: music
  name: hue-music
spec:
  containers:
    - name: hue-music
      image: busybox
      livenessProbe:
        httpGet:
          path: /pulse
          port: 8888
          httpHeaders:
          - name: X-Custom-Header
            value: ItsAlive
        initialDelaySeconds: 30
        timeoutSeconds: 1 

如果任何容器的活性探测失败,那么 pod 的重启策略就会生效。确保你的重启策略不是Never,因为那会使探测器无用。

还有两种其他类型的探头:

  • TcpSocket–只需检查端口是否打开
  • Exec–运行成功返回 0 的命令

使用就绪探测器来管理依赖关系

就绪探测器用于不同的目的。您的容器可能已启动并正在运行,但它可能依赖于目前不可用的其他服务。例如,hue-music可能取决于对包含您的收听历史的数据服务的访问。没有准入,它就无法履行职责。

在这种情况下,其他服务或外部客户端不应该向 Hue 音乐服务发送请求,但也不需要重启。就绪探测解决了这个用例。当容器的就绪探测失败时,该容器的 pod 将从它注册的任何服务端点中删除。这确保了请求不会淹没无法处理它们的服务。请注意,您还可以使用就绪探测器来临时移除超额预订的 POD,直到它们耗尽一些内部队列。

这是一个准备就绪探测器样本。我在这里使用exec探针执行一个自定义命令。如果命令退出非零退出代码,容器将被拆除:

readinessProbe:
  exec:
    command:
        - /usr/local/bin/checker
        - --full-check
        - --data-service=hue-multimedia-service
  initialDelaySeconds: 60
  timeoutSeconds: 5 

在同一个容器上同时安装就绪探测器和活动探测器是很好的,因为它们服务于不同的目的。

使用初始化容器进行有序的料盒上料

活跃度和准备状态探测器非常棒。他们认识到,在启动时,可能会有一段时间容器尚未准备好,但不应被视为失败。为了适应这一点,有initialDelayInSeconds设置,它描述了容器不会被认为失效的时间。但是如果这个初始延迟可能非常长呢?也许,在大多数情况下,一个容器在几秒钟后就准备好了,并准备好处理请求,但是因为最初的延迟被设置为五分钟以防万一,我们在容器空闲的地方浪费了很多时间。如果容器是高流量服务的一部分,那么在每次升级后,许多实例都可以闲置五分钟,这几乎使服务不可用。

Init 容器解决了这个问题。一个容器可能有一组初始化容器,在其他容器启动之前运行到完成。init 容器可以处理所有非确定性的初始化,并让应用容器及其就绪探测器具有最小的延迟。

初始化容器是在 Kubernetes 1.6 的测试版中推出的。您可以在 pod 规范中将它们指定为initContainers字段,这与containers字段非常相似。这里有一个例子:

apiVersion: v1
kind: Pod
metadata:
  name: hue-fitness
spec:
  containers:
  - name: hue-fitness
    image: busybox
  initContainers:
  - name: install
    image: busybox 

POD 准备和准备门

豆荚准备在 Kubernetes 1.11 中引入,并在 Kubernetes 1.14 中变得稳定。虽然就绪探测器允许您在 pod 级别确定 pod 是否准备好服务请求,但是支持向 pod 传送流量的整体基础架构可能还没有准备好。例如,服务、网络策略和负载平衡器可能需要一些额外的时间。这可能是一个问题,尤其是在滚动部署期间,在新 POD 真正准备好之前,Kubernetes 可能会终止旧 POD,这将导致服务能力下降,甚至在极端情况下导致服务中断(如果所有旧 POD 都被终止,并且没有新 POD 完全准备好)。

这是Pod ready++提案要解决的问题。这个想法是扩展 POD 准备状态的概念,除了确保所有的容器都准备好之外,还要检查额外的条件。这是通过在PodSpec中添加一个名为readinessGates的新字段来实现的。您可以指定一组必须满足的条件,以使 pod 被视为就绪。在下面的例子中,POD 没有准备好,因为"www.example.com/feature-1"状态为False:

Kind: Pod
...
spec:
  readinessGates:
    - conditionType: www.example.com/feature-1
status:
  conditions:
    - type: Ready  # this is a builtin PodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"   # an extra PodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
... 

与 DaemonSet 豆荚共享

DaemonSet pods 是自动部署的 pods,每个节点(或指定的节点子集)一个。它们通常用于监视节点并确保它们可以运行。这是一个非常重要的功能,我们将在第 13 章监控 Kubernetes 集群中介绍。但是它们可以用于更多的用途。默认 Kubernetes 调度程序的本质是它基于资源可用性和请求来调度 pods。如果您有许多不需要大量资源的单元,同样,许多单元将被安排在同一个节点上。让我们考虑这样一个 pod,它执行一个小任务,然后每秒钟向远程服务发送其所有活动的摘要。现在,假设平均有 50 个这样的 POD 被安排在同一个节点上。这意味着,每秒钟,50 个 POD 用很少的数据发出 50 个网络请求。不如我们把它减少 50 倍,只需要一个网络请求?有了 DaemonSet POD,所有其他 50 个 POD 都可以与其通信,而不是直接与远程服务对话。DaemonSet POD 将从 50 个 POD 中收集所有数据,并每秒钟向远程服务报告一次。当然,这需要远程服务应用编程接口来支持聚合报告。好的一点是豆荚本身不需要修改;它们将被配置为与本地主机上的 DaemonSet pod 对话,而不是与远程服务对话。DaemonSet pod 充当聚合代理。

这个配置文件有意思的地方是hostNetworkhostPIDhostIPC选项设置为true。这使 pods 能够有效地与代理通信,利用它们运行在同一台物理主机上的事实:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: hue-collect-proxy
  labels:
    tier: stats
    app: hue-collect-proxy
spec:
  selector:
    matchLabels:
      tier: stats
      app: hue-collect-proxy
  template:
    metadata:
      labels:
        tier: stats
        app: hue-collect-proxy
    spec:
      hostPID: true
      hostIPC: true
      hostNetwork: true
      containers:
      - name: hue-collect-proxy
        image: busybox 

用 Kubernetes 开发色相平台

在本节中,我们将讨论扩展 Hue 平台和服务其他市场和社区的其他方法。问题是总是这样:我们可以使用什么 Kubernetes 特性和能力来解决新的挑战或需求?

在企业中利用色调

企业通常无法在云中运行,这可能是出于安全和法规遵从性的原因,也可能是出于性能的原因,因为系统必须与不经济的数据和遗留系统一起工作才能迁移到云中。无论哪种方式,Hue for enterprise 都必须支持内部集群和/或裸机集群。

虽然 Kubernetes 通常部署在云上,甚至有一个特殊的云提供商界面,但它不依赖于云,可以部署在任何地方。这确实需要更多的专业知识,但是已经在自己的数据中心运行系统的企业组织拥有这种专业知识。

用色相推进科学

Hue 非常擅长整合来自多个来源的信息,这对科学界来说是个福音。考虑一下 Hue 如何帮助不同学科的科学家进行多学科合作。

科学界网络可能需要跨多个地理分布的集群进行部署。进入集群联盟。Kubernetes 考虑到了这个用例,并开发了对它的支持。我们将在下一章详细讨论它。

用色相教育未来的孩子

Hue 可以用于教育,并为在线教育系统提供许多服务。但是隐私问题可能会阻碍将 Hue 作为一个单一的集中式系统部署给孩子们。一种可能性是有一个单一的集群,为不同的学校提供名称空间。另一个部署选项是每个学校或县都有自己的 Hue Kubernetes 集群。在第二种情况下,Hue for education 必须非常容易操作,以迎合没有大量技术专长的学校。Kubernetes 可以通过为 Hue 提供自愈和自动缩放特性和功能来提供很大帮助,尽可能接近零管理。

摘要

在这一章中,我们设计并规划了 Hue 平台的开发、部署和管理——一个假想的全知全能的服务——建立在微服务架构之上。当然,我们使用 Kubernetes 作为底层的编排平台,并深入研究了它的许多概念和资源。特别是,我们专注于为长期运行的服务部署 pods,而不是为启动短期或 cron 作业的作业部署 pods,探索了内部服务与外部服务的对比,还使用名称空间来分割 Kubernetes 集群。然后,我们看了一个大型系统的管理,比如带有活动和就绪探测器的 Hue、init 容器和 DaemonSets。

现在,您应该对构建由微服务组成的网络级系统感到满意,并了解如何在 Kubernetes 集群中部署和管理它们。

在下一章中,我们将探讨存储这一极其重要的领域。数据是王道,但往往是系统中最不灵活的元素。Kubernetes 提供了一个存储模型和许多选项,用于与各种存储解决方案集成。

参考