扩展和补充 Kubernetes(评论和视频报告)

扩展和补充 Kubernetes(评论和视频报告)

8月XNUMX日 会议 圣徒 HighLoad++ 2019作为“DevOps 和运营”部分的一部分,给出了一份“扩展和补充 Kubernetes”的报告,Flant 公司的三名员工参与了该报告的创建。 在其中,我们讨论了许多我们想要扩展和补充 Kubernetes 功能的情况,但我们没有找到现成的简单解决方案。 我们以开源项目的形式提供了必要的解决方案,本次演讲也是专门针对它们的。

按照传统,我们很高兴向您展示 报告视频 (50 分钟,比文章内容丰富得多)和文本形式的主要摘要。 去!

K8s 中的核心和附加功能

Kubernetes 正在改变行业和早已建立的管理方法:

  • 感谢他 抽象,我们不再使用设置配置或运行命令(Chef、Ansible...)等概念进行操作,而是使用容器、服务等分组。
  • 我们可以在不考虑细微差别的情况下准备申请 具体站点,它将在其上启动:裸机、提供商之一的云等。
  • 有了 K8s,您变得前所未有的方便 最佳实践 关于组织基础设施:扩展技术、自我修复、容错等。

然而,当然,一切并不是那么顺利:Kubernetes也带来了自己的新挑战。

Kubernetes 没有 是一个解决所有用户所有问题的组合。 核心 Kubernetes 只负责一组最低限度的必要功能,这些功能存在于 簇:

扩展和补充 Kubernetes(评论和视频报告)

Kubernetes 核心定义了一组基本原语,用于对容器进行分组、管理流量等。 我们在中更详细地讨论了它们 2年前的报告.

扩展和补充 Kubernetes(评论和视频报告)

另一方面,K8s 提供了扩展可用功能的绝佳机会,这有助于关闭其他功能 - 具体的 ——用户需求。 Kubernetes 的添加是集群管理员的责任,他们必须安装和配置一切必要的东西,以使集群“处于正确的状态”[以解决他们的特定问题]。 这些都是什么样的补充呢? 让我们看一些例子。

附加组件示例

安装 Kubernetes 后,我们可能会感到惊讶,节点内和节点之间的 Pod 交互所必需的网络无法自行运行。 Kubernetes 内核不保证必要的连接;相反,它决定网络 接口 (CNI)对于第三方附加组件。 我们必须安装这些附加组件之一,它将负责网络配置。

扩展和补充 Kubernetes(评论和视频报告)

一个很接近的例子是数据存储解决方案(本地磁盘、网络块设备、Ceph...)。 最初它们位于核心,但随着出现 CSI 情况会发生与已经描述的类似的情况:接口位于 Kubernetes 中,其实现位于第三方模块中。

其他示例包括:

  • 入口-控制器 (请参阅他们的评论 我们最近的文章).
  • 证书经理:

    扩展和补充 Kubernetes(评论和视频报告)

  • 运营商 是一整类附加组件(其中包括提到的证书管理器),它们定义原语和控制器。 他们的工作逻辑仅受我们想象力的限制,并允许我们将现成的基础设施组件(例如 DBMS)转换为原语,这比使用一组容器及其设置更容易使用。 已经编写了大量的运算符 - 即使其中许多尚未准备好用于生产,但这只是时间问题:

    扩展和补充 Kubernetes(评论和视频报告)

  • 指标 - Kubernetes 如何将接口(Metrics API)与实现(第三方附加组件,如 Prometheus 适配器、Datadog 集群代理...)分离的另一个例子。
  • 监测与统计,在实践中不仅需要 普罗米修斯和格拉法纳,还有 kube-state-metrics、node-exporter 等。

这不是完整的添加列表...例如,在 Flant 公司,我们目前安装 29 个补充 (所有这些总共创建了 249 个 Kubernetes 对象)。 简而言之,如果没有添加,我们就无法看到集群的生命周期。

自动化

Operator 旨在自动化我们每天遇到的日常操作。 以下是现实生活中的示例,编写运算符将是一个很好的解决方案:

  1. 有一个私有(即需要登录)注册表,其中包含应用程序的图像。 假设每个 pod 都分配有一个特殊的秘密,允许在注册表中进行身份验证。 我们的任务是确保在命名空间中找到这个秘密,以便 Pod 可以下载图像。 可能有很多应用程序(每个应用程序都需要一个秘密),并且定期更新秘密本身很有用,因此消除了手动布置秘密的选项。 这就是操作员发挥作用的地方:我们创建一个控制器,它将等待命名空间出现,并根据此事件向命名空间添加一个秘密。
  2. 默认情况下,禁止从 pod 访问互联网。 但有时可能需要:访问权限机制简单地工作是合乎逻辑的,不需要特定的技能,例如,通过命名空间中存在某个标签。 运营商可以如何帮助我们? 创建一个控制器,等待标签出现在命名空间中,并添加适当的 Internet 访问策略。
  3. 类似的情况:假设我们需要添加某个 污点,如果它有类似的标签(带有某种前缀)。 操作员的动作是显而易见的......

在任何集群中,必须解决日常任务,并且 正确地 这可以使用运算符来完成。

总结所有描述的故事,我们得出的结论是 为了在 Kubernetes 中舒适地工作,您需要: 但) 安装附加组件, b) 发展经营者 (用于解决日常管理任务)。

如何为 Kubernetes 编写声明?

一般来说,该方案很简单:

扩展和补充 Kubernetes(评论和视频报告)

...但事实证明:

  • Kubernetes API 是一个相当重要的东西,需要花费大量时间才能掌握;
  • 编程也不适合所有人(选择 Go 语言作为首选语言是因为它有一个特殊的框架 - 运营商SDK);
  • 框架本身的情况类似。

底线: 编写一个控制器 (操作员)必须 花费大量资源 研究材料。 这对于“大型”运营商来说是合理的——例如,对于 MySQL DBMS。 但是,如果我们还记得上面描述的示例(揭露秘密、将 Pod 访问互联网......),我们也希望正确执行这些示例,那么我们就会明白,所付出的努力将超过我们现在需要的结果:

扩展和补充 Kubernetes(评论和视频报告)

一般来说,会出现一个两难的困境:花费大量资源并找到合适的工具来编写语句,或者以老式的方式(但速度很快)进行编写。 为了解决这个问题 - 在这些极端之间找到折衷方案 - 我们创建了自己的项目: 外壳操作符 (另见他的 最近的公告 在轮毂上).

Shell 操作符

他如何工作? 该集群有一个 pod,其中包含带有 shell 操作符的 Go 二进制文件。 他旁边有一套 钩子 (有关它们的更多详细信息 - 见下文)。 shell 操作符本身订阅了某些 发展 在 Kubernetes API 中,一旦发生就会启动相应的钩子。

shell 操作符如何知道针对哪些事件调用哪些钩子? 这些信息由钩子本身传输到 shell 操作符,而且它们做得非常简单。

钩子是 Bash 脚本或任何其他接受单个参数的可执行文件 --config 并以 JSON 进行响应。 后者确定它感兴趣的对象以及应该响应哪些事件(对于这些对象):

扩展和补充 Kubernetes(评论和视频报告)

我将说明我们示例之一的 shell 运算符的实现 - 分解用于使用应用程序映像访问私有注册表的秘密。 它由两个阶段组成。

练习:1.写一个hook

首先,在hook中我们将处理 --config,表明我们对命名空间感兴趣,特别是它们的创建时刻:

[[ $1 == "--config" ]] ; then
  cat << EOF
{
  "onKubernetesEvent": [
    {
      "kind": "namespace",
      "event": ["add"]
    }
  ]
}
EOF
…

逻辑会是什么样的? 也很简单:

…
else
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  kubectl create -n ${createdNamespace} -f - << EOF
Kind: Secret
...
EOF
fi

第一步是找出创建了哪个命名空间,第二步是使用以下命令创建它 kubectl 此名称空间的秘密。

练习:2. 组装图像

剩下的就是将创建的钩子传递给 shell 操作符 - 如何做到这一点? shell-operator 本身是一个 Docker 镜像,所以我们的任务是将钩子添加到该镜像中的一个特殊目录中:

FROM flant/shell-operator:v1.0.0-beta.1
ADD my-handler.sh /hooks

剩下的就是组装它并推动它:

$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

最后一步是将映像部署到集群。 为此,我们要写 部署:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1 # 1
      serviceAccountName: my-operator              # 2

有两点需要注意:

  1. 新创建的图像的指示;
  2. 这是一个系统组件(至少)需要订阅 Kubernetes 中的事件并向命名空间分配机密的权限,因此我们为该钩子创建一个 ServiceAccount(和一组规则)。

结果——我们解决了问题 给亲戚 对于 Kubernetes,以创建分解秘密的操作符的方式。

其他 shell 操作符功能

要限制钩子将使用的所选类型的对象, 它们可以被过滤,根据某些标签进行选择(或使用 matchExpressions):

"onKubernetesEvent": [
  {
    "selector": {
      "matchLabels": {
        "foo": "bar",
       },
       "matchExpressions": [
         {
           "key": "allow",
           "operation": "In",
           "values": ["wan", "warehouse"],
         },
       ],
     }
     …
  }
]

假如 去重机制,它 - 使用 jq 过滤器 - 允许您将大型 JSON 对象转换为小型对象,其中仅保留那些我们想要监视更改的参数。

当调用钩子时,shell 操作符会传递它 对象数据,可用于任何需要。

触发钩子的事件不限于 Kubernetes 事件:shell-operator 提供支持 按时间调用钩子 (类似于传统调度程序中的 crontab),以及特殊事件 启动时。 所有这些事件都可以组合并分配给同一个钩子。

shell 运算符的另外两个功能:

  1. 它的作品 异步地。 由于接收到 Kubernetes 事件(例如正在创建的对象),集群中可能会发生其他事件(例如正在删除同一对象),并且钩子需要考虑到这一点。 如果钩子执行时出错,那么默认情况下它将是 记起 直到成功完成(可以更改此行为)。
  2. 它出口 指标 对于 Prometheus,您可以通过它了解 shell 操作符是否正常工作,找出每个钩子的错误数量和当前队列大小。

总结一下报告的这一部分:

扩展和补充 Kubernetes(评论和视频报告)

附加组件安装

为了舒适地使用 Kubernetes,还提到需要安装附加组件。 我将用我们公司现在的做法为例来向您介绍这一点。

我们开始使用具有多个集群的 Kubernetes,唯一添加的是 Ingress。 它需要在每个集群中进行不同的安装,我们为不同的环境做了几种YAML配置:裸机、AWS……

由于集群越多,配置也就越多。 此外,我们还改进了这些配置本身,因此它们变得非常异构:

扩展和补充 Kubernetes(评论和视频报告)

为了让一切井井有条,我们从一个脚本开始(install-ingress.sh),它将我们将部署到的集群类型作为参数,生成必要的 YAML 配置并将其推广到 Kubernetes。

简而言之,我们的进一步路径以及与之相关的推理如下:

  • 要使用 YAML 配置,需要一个模板引擎(在第一阶段,这是简单的 sed);
  • 随着集群数量的增加,自动更新的需求来了(最早的解决方案是将脚本放在Git中,使用cron更新并运行);
  • Prometheus 需要类似的脚本(install-prometheus.sh),然而,值得注意的是,它需要更多的输入数据及其存储(以一种好的方式 - 集中式和集群中),并且可以自动生成一些数据(密码):

    扩展和补充 Kubernetes(评论和视频报告)

  • 向越来越多的集群推出错误的风险不断增加,因此我们意识到安装程序 (即两个脚本:Ingress 和 Prometheus) 需要暂存(Git 中的多个分支,几个​​ cron 来在相应的稳定或测试集群中更新它们);
  • с kubectl apply 它变得很难使用,因为它不是声明性的,只能创建对象,但不能决定它们的状态/删除它们;
  • 我们缺少一些当时根本没有实现的功能:
    • 完全控制集群更新的结果,
    • 根据可以从集群获取的数据(发现)自动确定一些参数(安装脚本的输入),
    • 它以不断发现的形式逻辑发展。

我们在其他项目的框架内实施了所有这些积累的经验 - 插件操作符.

附加操作符

它基于已经提到的 shell 运算符。 整个系统如下所示:

以下内容被添加到 shell-operator 挂钩中:

  • 值存储,
  • 舵图,
  • 组件 监控值存储 并且 - 如果发生任何变化 - 要求 Helm 重新滚动图表。

扩展和补充 Kubernetes(评论和视频报告)

因此,我们可以对 Kubernetes 中的事件做出反应,启动一个钩子,通过这个钩子我们可以对存储进行更改,之后图表将被重新下载。 在结果图中,我们将一组钩子和图表分离成一个组件,我们称之为 模组:

扩展和补充 Kubernetes(评论和视频报告)

可以有很多模块,我们向它们添加全局钩子、全局值存储以及监视该全局存储的组件。

现在,当 Kubernetes 中发生某些情况时,我们可以使用全局钩子对其做出反应并更改全局存储中的某些内容。 此更改将被注意到,并将导致集群中的所有模块被推出:

扩展和补充 Kubernetes(评论和视频报告)

该方案满足安装上述附加组件的所有要求:

  • Helm 负责模板化和声明性。
  • 自动更新的问题是使用全局钩子解决的,该钩子按计划进入注册表,如果在那里看到新的系统映像,则将其推出(即“自身”)。
  • 在集群中存储设置是通过使用 配置映射,其中包含存储的主要数据(在启动时它们被加载到存储中)。
  • 使用钩子解决了密码生成、发现和持续发现的问题。
  • 分段是通过标签实现的,Docker 开箱即用地支持标签。
  • 使用指标来监控结果,通过这些指标我们可以了解状态。

整个系统在 Go 中以单个二进制文件的形式实现,称为 addon-operator。 这使得图表看起来更简单:

扩展和补充 Kubernetes(评论和视频报告)

该图中的主要组件是一组模块 (下面以灰色突出显示)。 现在,我们可以花一点力气为所需的附加组件编写一个模块,并确保它将安装在每个集群中,将被更新并响应集群中所需的事件。

“植物”用途 插件操作符 在 70 多个 Kubernetes 集群上。 当前状态 - 阿尔法版本。 现在我们正在准备发布测试版的文档,但目前在存储库中 可用的例子,在此基础上您可以创建自己的插件。

我在哪里可以获得 addon-operator 的模块? 发布我们的库是我们的下一阶段;我们计划在夏天进行。

视频和幻灯片

表演视频(约 50 分钟):

报告介绍:

PS

我们博客上的其他报道:

您可能还对以下出版物感兴趣:

来源: habr.com

添加评论