为家庭服务器运行Nomad

2021-02-17 18:08:19

自从我在Hydra(我的家庭服务器)上写了一篇文章以来已经很长时间了。我将Hydra用作测试平台来学习新工具,工作流程,这给我自托管应用程序带来欢乐的同时又获得了一些回报。

在2个RPi4节点上部署了相当少的K3s设置。我无法继续此设置,因为:有些应用程序没有基于ARM的映像(这是在2019年,M1炒作时代之前)。

编写YAML太无聊了(我也在工作)。没给我欢乐。

RPi 2x节点+ K3s + DO Droplet。用于网络的尾标。与以前的设置相比,这是一个很大的进步。我部署了一个DO节点,并添加了Node Labels以仅在DO Node上部署持久性工作负载。

我使用了自己的工具Kubekutr + Kustomize,它有助于配置的版本控制。

花了很多时间来启用新服务。懒了,除了最初的3-4个应用程序外没有太多其他内容。

DO上的单个节点。用于部署Docker容器的Terraform。我相信第三次迭代对我很有帮助。我保持设置超级简单,使用Terraform将工作负载部署为Docker容器。

启用新服务的时间从几个小时减少到几分钟。对我来说,这是一个巨大的胜利。我部署了大约10-15项新服务,以直接在服务器上进行尝试。

大约一个月前,凯拉什(Kallash)询问了有关游牧民族的反馈。我们在Zerodha(印度最大的股票经纪商)进行了评估,以将我们的服务从Kubernetes迁移到Nomad(稍后会详细介绍)。自从我上次看到Nomad以来已经过去了将近2年,因此绝对值得重新评估(尤其是自从最近达到1.0以来)。我想尝试游牧回答个人的好奇心:这是什么做的不同于Kubernetes?没有比实际弄脏手更好的方法了,对吗?

在遵循了官方网站的简短教程之后,我有信心针对实际的工作量进行尝试。在我之前的设置中,我托管了许多应用程序(Pihole,Gita,Grafana等),并认为这将是通过在Nomad群集中部署相同服务来学习Nomad工作方式的好方法。我的期望值是零,我已经有了一个不错的设置,该设置可靠并且可以运行。我在本地Nomad群集中的体验很愉快,我能够在不到30分钟的时间内从0到1迅速达到。与K8相比,此BTW强烈表明Nomad入门容易。您甚至必须在K8s集群中部署单个容器之前就已经想过要登记的大量不同概念,这很奇怪。 Nomad在这里采取了简单的方法,将开发人员的概念简化为三件事:

职位:职位是不同群体的集合。作业是放置调度程序类型,更新策略和ACL的约束的地方。

组:组是不同任务的集合。组始终在同一Nomad客户端节点上执行。您将希望将Groups用于用例,例如日志记录Sidecar,反向代理等。

任务:原子工作单元。 Nomad中的任务可以运行容器/二进制/ Java VM等,定义安装点,env变量,要公开的端口等。

如果您来自K8,则可以将Task视为Pod,将Group视为Replicaset。在K8中,没有什么比Job更好的了。但!最酷的部分?您不必熟悉所有不同类型的复制副本(部署,后台驻留程序,状态集)以及配置它们的不同方法。

是否想在Nomad中做正常工作作为定期工作?只需将以下块添加到您现有的Job中:

您要使服务作为批处理作业运行(在所有Nomad节点上-相当于K8s中的Daemonset)?只需对您现有的工作进行以下更改:

您会看到这就是我对UX的关注。如果您来自K8的背景,那么有很多这样的示例会在您的脸上留下漂亮的笑容。

Tailscale VPN:在笔记本电脑/移动设备和DO服务器之间充当网状层。对于公开内部服务很有用。

Caddy用于所有服务的反向代理和自动SSL设置。我运行了2个Caddy实例:

Terraform:具有IaC(红外线代码)的主要组件。管理模块:Nomad Jobs。在将环境变量,Nomad作业文件中的配置文件模板化后,用于运行工作负载。

Nomad之所以大放异彩,是因为它遵循UNIX的哲学,即“使每个程序做好一件事情”。简而言之,Nomad只是工作负载的协调者。它只关心诸如装箱,计划决策之类的事情。

如果您正在运行异构工作负载,则运行一台服务器(或一组服务器)会很快变得昂贵。因此,在这种情况下,协调者倾向于有意义。他们倾向于通过高效运行各种工作负载来节省成本。这是协调员真正要做的所有事情。

Nomad不会干扰您的DNS设置,服务发现,机密管理机制以及几乎所有其他内容。如果您阅读Kubernetes失败案例中的一些文章,则最常见的中断原因是网络(DNS,ndots等)。围绕K8的许多营销从未谈论过这些事情。

我始终认为“第0天很容易,第N天是对你的技能的真正考验”。任何人都可以将工作负载部署到K8s集群,这始终是Day N的操作,涉及调试网络中断,神秘的容器重启,正确的资源分配以及其他需要实际技能和精力的复杂问题。它不像kubectl应用-f那样容易,而我最主要的抱怨是那些在营销中错过了这一点的人间距(显而易见!)。

Nomad达到了易于操作且功能强大的最佳效果。如果您想:Nomad是一个不错的选择:

(不是在开玩笑)您已经厌倦了运行Helm图表或编写大型YAML清单。 Nomad作业的配置语法对人类友好并且易于掌握。

Nomad可作为单个二进制文件获得。如果要在本地尝试,则只需要sudo nomad agent -dev,您将拥有一个以开发人员模式运行的Nomad Server客户端以及一个UI。这使开发人员可以轻松地在本地测试部署,因为此部署与生产部署之间的配置差异很小。不要忘记,它非常容易自托管Nomad集群。我尚未见到任何在生产过程中自行托管K8集群的人,而始终没有专职团队照顾它的人。

如果您依赖自定义控制器和运算符。操作员模式是一种管理大型复杂分布式系统(如数据库,作业队列等)的新方法。有很多社区构建的运营商,可帮助减少运行这些服务的工作量。但是,所有这些都与" Kubernetes"生态系统。如果您发现自己正在运行任何这样的运算符,那么在Nomad生态系统中翻译相同的运算符将很困难(并非不可能)。

由于我将一些工作负载从DO docker容器设置迁移到Nomad,因此我演示了一些用例,如果您想开始将服务迁移到Nomad,这可能会有所帮助

上下文:我正在将Caddy作为所有服务的反向代理运行。如前所述,Nomad只关心调度,因此您如何精确地进行服务发现?您需要Consul(或Consul,Nomad之类的东西没有硬限制)才能使用其IP地址注册服务名称。这是您的操作方法:

在Nomad工作规格的.task部分中​​,您需要使用您正在注册的端口注册服务名称,并将其他标签注册为元数据(可选):

Nomad的模板在幕后使用领事模板。这是一个小型实用程序,可以持续监视Consul / Vault密钥,并且如果其中任何一个密钥发生更改,则可以重新加载/重新启动工作负载。它也可以用来发现在Consul中注册的服务的地址。因此,这是一个使用领事模板功能提取上游gitea-web服务的IP地址的Caddyfile的示例:

git.mrkaran.dev {{{范围服务" gitea-web" }} reverse_proxy {{.Address}}:{{.Port}} {{end}}}

当作业提交给Nomad时,已渲染的模板将安装在容器内。您可以定义值更改时的操作。例如,在重新部署Gitea容器时,地址很可能会更改。在这种情况下,我们希望Caddy使用在Caddyfile中配置的新地址自动重启:

我运行了Gitea的公共实例,但我想仅将SSH访问限制为我的Tailscale网络。 Nomad具有一个有趣的功能host_network,使您可以在不同的网络接口上绑定任务的不同端口。

网络{端口" http" {to = 3000}端口" ssh" {to = 22#需要为SSH操作静态分配。 static = 4222#主机上仅暴露于Tailscale IP的SSH端口。 host_network =" tailscale" }}

Nomad没有任何模板功能,因此所有配置都必须来自Consul,机密应来自Vault。但是,由于时间限制,我想更好地了解Nomad和Consul,并在以后使用Vault。我需要一种插值环境变量的方法。这是Terraform出现的地方:

资源" nomad_job" " app" {jobspec = templatefile(" $ {path.module} /conf/shynet.nomad" ;, {shynet_django_secret_key = var.shynet_django_secret_key,shynet_postgresql_password = var.shynet_postgresql_password}} hcl2 {已启用= true}

我们可以将Terraform中的变量(可以通过本地环境中的TF_VAR_获取)传递给Nomad工作规范。在工作说明中,我们可以使用env使它可用于我们的任务:

我使用Restic定期备份服务器并上传到Backblaze B2。由于Nomad使用exec驱动程序支持将任务作为不同的隔离环境(chroot)运行,甚至没有使用raw_exec驱动程序进行隔离,因此我想尝试一下。我不得不在这里使用raw_exec驱动程序,因为主机上的/ data文件路径不适用于chroot环境。

工作" restic" {数据中心= [&hydra"]类型=" batch"定期{cron =" 0 3 * * *" time_zone ="亚洲/加尔各答" disable_overlap = true} ...任务" backup" {驱动程序=" raw_exec" config {#由于`/ data`由`root`拥有,因此restic需要作为`root`产生。 #`raw_exec`生成运行Nomad`客户端的进程(即root)。命令=" $$ {NOMAD_TASK_DIR} /restic_backup.sh" }} ...}

与Nomad合作非常开心。但是,我发现了一些粗糙的情况,我相信人们应该意识到:

修改服务时,有时会忽略host_network属性。我在上游打开了一个问题,但看起来其他人在这里和这里都面临着类似的行为。

目前的host_network无法绑定到浮动IP地址(DigitalOcean / GCP等)。我现在不得不使用我的Droplet的公共IPv4地址。

我尝试使用Consul Connect(带有mTLS的服务网格),但是由于host_network再次出现,我无法使用它。

绝对可以改进Nomad CLI,以获得更一致的体验。使用游牧时,我特别想念使用kubectl。

除此之外,我最终向上游发送了PR,解决了CLI arg排序问题。

在已经自举的Nomad服务器上,如果尝试更改server.bind_addr,它将没有任何效果。我几乎花了很多时间调试它,最终删除了服务器的data_dir为我解决了这个问题。

我将数据库和应用程序作为一个单独的" group"组一起运行在我的设置配置中。不要在生产中这样做。每当您重新启动作业时,组都会重新启动两个容器。这样做的副作用非常有趣:由于我们使用Consul来获取数据库主机,因此该应用程序可能在数据库启动并向Consul注册其新地址之前启动。我将在以后的版本中修复该依赖关系,但是由于我运行的工作负载更少,并且具有自动重试功能,因此我可以保持这种状态就可以了。

与Kubernetes相比,Nomad的社区很小。但是,人们对Gitter,Discourse和Github问题的反应超级好。值得一提的是:

@tgross对Github问题具有超级响应能力,并且在整理问题方面做得很好。

游牧民族的生态系统仍处于起步阶段,我相信对Golang,Ops,分布式系统感兴趣的人们有很多为游牧民族做出贡献的机会。 Nomad的代码库是易于接近的,并且可以在以下几个关键领域做出贡献:

Nomad Job文件:有许多舵机图表可用于遵循最佳实践。游牧民族中类似的事物肯定会很有趣。

Nomad Gotchas:由于K8的广泛使用和广泛采用,因此很自然地要特别强调K8的失败故事。游牧民族是一个相对较小的社区,我们需要更多的调试和出现问题的信息。参考资料。您从失败中学到的知识不止101条安装指南:)

我想我是在Nomad上出售的。我已经在产品中使用Kubernetes两年了,但是如果您要我从头开始编写Deployment规范(没有Googling / kubectl帮助),我将无法使用。编写Nomad配置后,我想不出K8要运行应用程序所需的大量样板。

Nomad还是保留在您的技术堆栈中的一个更简单的部分。有时候,最好不要使事情简单,而实际上却不能从复杂性中获得任何好处。