在跳上Kubernetes之前先看看Nomad

2021-03-01 00:12:04

最近,我偶然发现,然后偶然发现了David Anderson关于“新Kubernetes”的有趣帖子,基于他与Vallery Lancey的讨论,即如果他们从头开始重写Kubernetes,他们会做些什么。有趣的是,关于“新型Kubernetes”的提案中相当不错的一部分是Hashicorp为Nomad设计的选择,Nomad是一个被低估的协调器,而且非常简单(上述“新型Kubernetes”的主要目标之一)。

有人意识到Docker Swarm确实存在,但是已经放弃了软件/提供生命支持,因此不再推荐使用,但是由于使用起来非常简单,因此仍在讨论中出现。对于大多数人来说,这使Kubernetes成为唯一的“严肃”选项,但这是一个非常复杂的软件,具有许多活动部件,在大多数情况下并不需要或不需要。

这激发了我写关于Nomad的系列文章,内容是什么,为什么很棒,为什么缺少,以及如何使用。

Hashicorp的Nomad是易于运行和维护的,但非常灵活的任务计划程序/编排器。它依靠插件来执行,自动缩放和其他功能,并且可以通过其任务驱动程序运行几乎所有东西-Docker,contairnerd,LXC,rkt,podman,Java,fork / exec,QEMU,鞭炮,FreeBSD监狱。

它以单个二进制文件的形式出现,以两种模式运行(服务器,以3或5为一组,它们可以制定调度决策并托管API和配置,还有数量不限的工作程序,它们可以实际运行您想要的任何程序)运行),并且可以通过领事自动将其聚类。配置(用于作业和Nomad本身)都在HCL(我稍后会详细介绍它的功能)或JSON(主要用于何时由机器/脚本/工具而非人提交作业)中进行配置)。可以通过多区域联合来连接多个集群,以共享ACL和API转发(您可以向任何区域的任何服务器提交作业或请求日志,并将其转发到适当的服务器)。开箱即用的部署可能很复杂(滚动,金丝雀,蓝色/绿色),并且所有内容都是受版本控制和可回滚的。

像大多数HashiCorp工具一样,它是“开放核心”,这意味着大多数功能都可以在开放源代码版本中使用,以及一些更高级/企业级的功能(在Nomad的情况下,是跨区域/群集部署-可以同时部署一些功能)多个单独的群集,使用Sentinel和类似代码作为策略)需要升级到Nomad Enterprise。

job是一个声明性文件,其中包含任务组,每个任务都是由exec驱动程序运行的容器/二进制文件/任何文件

系统作业(在所有客户端节点上运行,等效于Kubernetes DaemonSet,用于监视/记录代理/负载均衡器)

服务,注册为领事服务,因此可以发现(通过API或DNS)

部署,作业的每个版本,都可以对其进行跟踪并可以回滚到

运行Docker容器(jaegertracing / all-in-one:1.21)的非常基本的工作示例,其CPU限制为1000Mhz,RAM为1024MB,并向Consul注册服务:

工作" jaeger" {type =" service"组" api" {task" jaeger" {driver =" docker" config {image =" jaegertracing / all-in-one:1.21" }资源{cpu = 1000内存= 1024}服务{名称=" jaeger-query" }}}}

请注意,这是一项非常基本的工作,没有运行状况检查,没有持久性存储,没有额外的配置,没有更新策略,没有自动缩放,没有暴露的端口。

Nomad跟踪每个作业的完整定义和部署历史记录,并允许您通过UI,CLI或API轻松回滚和比较它们,例如:

#列出名为" opentelemetry-collector" $ nomad的工作历史记录的版本opentelemetry-collectorVersion = 1Stable = falseSubmit Date = 2021-01-08T21:30:30 + 01:00Version = 0Stable = trueSubmit Date = 2021-01-08T21:29:48 + 01:00#检查版本之间的差异$游牧工作历史-p opentelemetry-collectorVersion = 1稳定= false提交日期= 2021-01-08T21:30:30 + 01:00差异= + / -工作:" +/-采集器" +/-任务组:" opentelemetry-收集器" +/-任务:" opentelemetry-collector" +/- Config {参数[0]:"-config = local / otel / config.yaml" +/-图片:&otel / opentelemetry-collector-contrib:0.15.0" => " otel / opentelemetry-collector-contrib:0.16.0"端口[0]:"健康状况"端口[1]:" jaeger_thrift_compact" }版本= 0稳定=正确提交日期= 2021-01-08T21:29:48 + 01:00#还原作业" opentelemetry-collector"到版本0 $ nomad job还原opentelemetry-collector 0

Nomad保持期望的状态及其历史记录,并且与terraform计划类似,nomad的工作计划使我们可以预览应用新的工作文件时将发生的变化。还有一个功能,可以通过-check-index标志来验证计划和运行之间是否没有任何变化(等同于计划文件中的terraform应用):

$ nomad工作计划otel.nomad +/-工作:" otel" +/-任务组:" opentelemetry" (1个创建/销毁更新)+/-任务:" opentelemetry-collector" (强制创建/销毁更新)+/- Config {参数[0]:"-config = local / otel / config.yaml" +/-图片:&otel / opentelemetry-collector-contrib:0.15.0" => &otel / opentelemetry-collector-contrib:0.20.0"端口[0]:"健康状况"端口[1]:" jaeger_thrift_compact" }计划程序空运行:-已成功分配所有任务。作业修改索引:413使用版本验证运行提交作业:nomad job run -check-index 413 otel.nomad当运行带有check-index标志的作业时,该作业仅如果给定的作业修改索引与服务器端版本匹配,则运行该命令。如果索引已更改,则其他用户已修改作业,并且该计划的结果可能无效。

总体而言,这是一个非常有用的功能,尤其是在本地或通过CI / CD进行协作时。

要检查作业的状态,在nomad job和nomad alloc下有一些命令

$ nomad作业状态otelID = otelName = otelSubmit日期= 2021-02-27T20:41:29 + 01:00Type = servicePriority = 50Datacenters = dc1Namespace = defaultStatus = runningPeriodic = falseParameterized = falseSummaryTask组已排队等待开始运行运行失败完成Lostotel 0 0 1 0 0 0最新部署ID = ea533b6f状态=成功描述=部署已成功完成部署的任务组所需放置的健康状况不健康进展Deadlineotel 1 1 1 0 2021-02-27T20:51:45 + 01:00AllocationsID节点ID任务组版本所需的状态已创建已修改89031cfd d3cbeb7e otel 0运行20年前运行4秒钟前#日志处于分配级别(类似于Kubernetes,它们位于容器级别),因此我们使用alloc id $ nomad alloc日志89031cfd [...]来获取它们。

Nomad允许使用生命周期节来定义任务组中任务的生命周期及其状态。我们可以使用prestart(用于初始化),poststart(用于伴侣(用于代理)(在Kubernetes中为代理和适配器模式))或poststop(用于清理),并通过sidecar bool定义它是否应与主要任务一样运行(s),例如:

任务" init" {生命周期{hook =" prestart" sidecar = false}驱动程序=" docker" config {image =" alpine / httpie"命令=" http" args = [" POST&#34 ;," https://some-internal-service-for-provisioning-stuff.local/v1/new" ;," job_id =&#39 ; $ {NOMAD_JOB_ID}!'" ]}}任务"流利的" {生命周期{hook =" poststart" #应该在主要任务sidecar = true之后开始#应该在主要任务运行的同时运行,并且在失败后重新启动} driver =" docker" config {image =" fluentd / fluentd" } ...}任务" main-app" {...}任务"清理" {生命周期{挂钩=" poststop" } driver =" docker" config {image =" alpine"命令=" rm -rf" args = [" / var / lib / volume-with-super-secret-data" }}

Kubernetes中众所周知的ACL(访问控制列表)或RBAC(基于角色的访问控制)允许定义谁可以做什么,从而使并非每个具有网络访问权限的人都可以拥有全部管理员权限并可以运行/停止任何操作。 Nomad的ACL系统与Consul和Vault的ACL系统非常相似,并使用JSON(主要用于非人类用户)或HCL来定义带有规则的策略,这些规则描述了可对哪个对象采取何种操作。

#一个基本策略,该策略允许预定义的读取策略具有对列表和读取的只读访问权限:#作业,卷和扩展详细信息,以及在默认名称空间名称空间" default&#34中的作业创建和日志访问的额外功能。 {policy =" read"功能= [" submit-job"," dispatch-job"," read-logs"]}

策略分配仅通过CLI完成,与Kubernetes在YAML中发生的方式不同,Kubernetes在策略管理中也是如此:

#在Nomadnomad acl策略apply中创建/更新策略-description" Application Developer policy" my-policy my-policy.hclnomad acl令牌create -name =" Test token" -policy =我的策略-类型= clientAccessor ID = 4e3c1ac7-52d0-6c68-94a2-5e75f17e657eSecret ID = 0be3c623-cc90-3645-c29d-5f0629084f68Name = Test tokenType = clientGlobal = falsePolicies = [my-policy] Create Time = 2021- 02-10 18:41:53.851133 +0000 UTC创建索引= 15修改索引= 15

只是为了进行比较,Kubernetes中的等效语法(由于YAML,我认为写起来很奇怪冗长):

apiVersion:rbac.authorization.k8s.io/v1种类:角色元数据:命名空间:默认名称:测试规则:-apiGroups:[""]资源:[" pods&#34 ;,& #34; services"]#一个Nomad作业同时包含pod等效项(任务组)和services动词:[" get&#34 ;、" watch&#34 ;、" list&# 34;,"日志","创建","更新","补丁"]

使用此令牌,作为CLI的环境变量(NOMAD_TOKEN)或标志(-token)或API的HTTP标头(X-Nomad-Token),我们就可以做。

ACL策略和令牌可以选择与联合群集共享,以简化管理。我们还可以通过Vault的Nomad Secret Backend拥有临时令牌,该令牌会生成具有特定策略的一次性令牌/短期令牌,但没有与Kubernetes相同的隐式或显式工作角色。服务帐户,为此必须通过Vault(将Vault策略分配给作业)。

Nomad即使具有“高级”功能,例如跨数据中心/区域链接多个群集,也易于安装,维护,更新和扩展。

在本地运行Nomad进行开发/测试仅需下载二进制文件并运行Nomad代理-dev(比microk8s或minikube或同类更容易),而Consul和Vault也是如此(您可能需要复制生产Nomad本地环境)。

升级Nomad服务器只是替换二进制文件并重新启动服务。有详细的升级指南,其中列出了主要的更改以及可能需要解决的重大变化/事情,但是它相对较少(由于已经在1.0+上,所以会更少)。升级之前,首先需要抽干客户(还有详细的指南),并且可以通过迁移节来调整操作期间作业的行为。

Nomad会收集有关其自身以及其中运行的所有内容的大量指标,这些指标可以发送到兼容的代理(statsD,Datadog),也可以由Prometheus / OpenMetrics兼容的抓取工具进行抓取,甚至还提供了有关设置Prometheus进行监视和警报的指南。

可以通过游牧服务器连接,server_join配置块(可以使用静态IP / DNS或动态云自动连接(基于云提供商标签或类似标签))手动或通过Consul.Federation来完成集群的形成/连接。通过通过其WAN IP / DNS和端口将服务器加入另一个群集中来完成:

Nomad具有广泛的API(包括最新的酷炫事件流等添加,允许创建集成和基于Nomad集群中发生的事情而起作用的工具),并且与其他工具(包括Hashicorp的一些工具)很好地集成在一起。 ,很好地补充游牧到对手更丰富的功能Kubernetes及其生态系统的功能。

诸如服务发现和K / V存储(分别为服务和ConfigMaps)和秘密存储(秘密)之类的东西,甚至包括更大的Kubernetes生态系统的一部分(如服务网格),都被委托给HashiStack的其他,现有的,经过良好使用和经过测试的部分(Consul和Vault),这很有意义-它遵循Unix的哲学“做一件事,并且做得很好”,并使Hashicorp及其用户更轻松地工作-Consul和Vault已经在各自的领域中得到了广泛使用,稳定和非常流行利基,关注点的分离和分离使我们可以在Nomad之外使用它们(例如,您可以使用Nomad托管或不托管在Vault上的保管箱,以秘密存储/动态机密/对在Nomad或其他地方运行的应用程序进行身份验证;也许您甚至已经有一个正在运行的保管库群集,您只需将Nomad群集连接到该群集,您就可以为所有机密使用相同的存储(无论从何处使用它们)。

最大的缺点是,如果您只想运行Nomad群集,则还需要另外两个工具来安装,维护,保持最新状态,等等。但是看到这三个都是相似的(单个二进制文件,Raft共识算法,文档(包括非常详细的升级指南),实现起来并不复杂。

还可以通过Consul与第三方工具(例如Traefik和Fabio)进行集成,以实现自动反向代理/负载平衡。

您可以在Nomad作业文件中使用Vault和Consul的一种方法是通过Nomad作业中的模板节(配置块),该模板节可基于模板创建文件或环境变量,并且基于Hashicorp的尊贵领事模板。

#根据领事K / V和服务,Nomad元数据和保险柜机密创建YAML配置文件:template {data =<< EOH --- bind_port:{{env" NOMAD_PORT_db" }}#端口" db" scratch_dir:{{env" NOMAD_TASK_DIR" }}#任务文件夹node_id:{{env" node.unique.id" }}#Nomad节点service_key的唯一ID:{{key" service / my-key" }}#由领事K / V商店loki_addr的service / my-key填充:{{range service" loki" }} {{.Address}}:{{.Port}} {{end}}#由名为" loki"的服务的IP和端口填充。在领事馆s服务目录some_secret:{{带有秘密\" secret / data / my-secret \" }} {{。Data.data.value}} {{end}}" #由Vault EOH目标中的secret / my-secret秘密填充=" local / file.yml"}#进行相同操作,而不是写入YAML文件,而是将值导出为env变量:template { ...目的地=" secrets / file.env" #secrets是每个任务的特殊文件夹,可用于存储秘密,并且无法通过API / CLI / UI浏览。有关以下内容的更多信息,env = true}

HVLv2大大增强了HCLv1的动态性和更好的类型管理。可以使用变量,进行循环,包含文件等。一些常见用例的示例:

使用“ env”变量来确定我们是本地开发人员还是生产人员,并相应地设置变量;并在所有情况下都使用带有默认参数的列表变量,用于本地/生产的默认参数,其他参数并将其合并。

注意:局部变量是文件的局部变量,而变量变量更类似于函数参数(它们可以通过文件,env变量以及默认值等进行传递)

变量" env" {默认=" local"}变量" args" {type = list(string)default = []} locals {datacenter = var.env ==" local" ? " dc1" :" eu-west-1" count = var.env ==" local" ? 1:5 default_args = ["-something"] local_args = ["-verbose"] prod_args = ["-production-mode 1&#34 ;,&# 34;-secure"] args = var.env ==" local" ? concat(local。default_args,local。local_args,var.args):concat(local。default_args,local。prod_args,var。args)}作业" test" {...数据中心= [本地。数据中心]#Nomad在这里需要一个列表,因此[]组" test" {count = local。计数任务" test" {driver =" docker" config {image =" registry.test.com:0.1" args = [对于in in var。 args:a]#Nomad需要一个列表,因此[[}}}}

为了进行比较,如果没有第三方工具,那么在Kubernetes-land中这两个都是不可能的-用于基本模板/逻辑的Helm,jsonnet,tanka,ytt等,这在YAML之上有很多开销,并且像Vault这样的东西Sidecar秘密注入的代理程序或Secrets Store CSI驱动程序,它允许通过CSI驱动程序将秘密作为文件系统对象读取。当然,对于后一个问题存在Kubernetes ConfigMap和Secrets,但是与Nomad的处理方式相比存在一些限制:

Kubernetes Secrets并不是真正的秘密,它们是存储在etcd中的base64编码的字符串(因此,您依赖于对其进行加密以实现“安全性”)

Kubernetes ConfigMap和Secrets是完全静态的,您无法进行任何动态处理(if,for循环,服务的IP /端口等)。

两者都与部署断开连接-如果更新Pod使用的ConfigMap,则需要重新启动后者以确认更改(与Nomad的change_mode比较,后者可以控制如何以及是否在配置更改时重新启动/重新加载任务)

您不能混搭,例如只有一个文件包含机密,服务地址,K / V配置等。(除非您使用initContainer和bash手动进行操作)

如果没有外部工具来进行续订和重启,您将无法拥有动态机密(例如AWS或具有有限生存时间的数据库凭证)

Nomad原生支持几种部署方法-滚动更新(每个实例一次更新X),金丝雀(一小部分正在运行的实例已更新,是否受到异常/错误/错误/等等的监视,以及是否所有情况)可以在一段时间内正常运行,其余的也进行更新)和蓝色/绿色(两个大小相同的环境正在运行,蓝色和绿色一个在运行,但是只有一个在服务流量;另一个在完全获取更新后的版本,运行测试,一切正常之后,将流量切换到该测试),并可以执行自动回滚。

#通用配置计数= 3#3分配更新{max_parallel = 1#并行升级的实例数health_check =" checks" #决定分配状态的因素-对人员/监视/等而言,状态可能是*健康状况*检查*,*状态*(如果所有任务都在运行)或*手动*。通过API}#canary count = 3#3个分配更新{max_parallel = 1 canary = 1#1 canary分配}#蓝色/绿色count = 3#3个分配更新{max_parallel = 3 canary = 3#canary = max_parallel = count = >绿色环保}

可以通过CLI,Web UI或API促进金丝雀或蓝色/绿色部署(例如,根据指标/日志/跟踪数据自动生成)。 Hashicorp在其HashiCorp Learn平台上提供了详细的示例指南。

从本质上讲,至少在容器方面,Nomad上的网络非常简单和基础。默认情况下,每个任务都以桥接模式在其组中的其他任务共享的命名空间中获得IP(由Docker / etc。提供)(以允许sidecar代理)。通过使用主机网络dyna可以暴露端口

......