Nomad是HashiCorp出品的一款轻量级集群调度器,它既支持容器化工作负载也支持普通的命令行程序。一个Nomad集群由服务器节点和客户端节点组成,两者的职责截然不同:服务器节点负责维护集群状态、处理作业提交和调度决策,并通过Raft共识协议在多个服务器之间复制数据;客户端节点则负责接收服务器下发的任务分配,调用本地驱动(如Docker、QEMU、Java等)执行具体任务,并定期向服务器上报节点资源使用情况和任务运行状态。因此,在部署Nomad时,必须明确每一台机器应该扮演的角色,并针对性地编写配置文件。本文将从零开始介绍如何配置一个可用的Nomad服务器和客户端节点,同时涵盖集群启动顺序、常见参数解释以及配置验证方法,帮助读者快速上手。

一、Nomad角色区分与基础配置结构
Nomad使用单一二进制文件,通过不同的命令行参数或配置文件区分服务器和客户端模式。默认情况下,如果配置文件中的server块存在,则节点会启用服务器功能;如果client块存在,则节点会启用客户端功能。一台机器可以同时配置为服务器和客户端,但在生产环境中通常建议物理隔离,以避免客户端任务负载影响服务器性能。
Nomad的配置文件使用HCL(HashiCorp Configuration Language)或JSON格式,其中HCL更简洁易读。配置可以写在一个文件中,也可以通过-config参数指定一个目录,目录中的所有.hcl和.json文件会被合并加载。最基础的配置结构如下:
# 基础配置
data_dir = "/opt/nomad/data"
bind_addr = "0.0.0.0"
# 服务器配置
server {
enabled = true
bootstrap_expect = 3
}
# 客户端配置
client {
enabled = true
servers = ["127.0.0.1:4647"]
}
在上面的示例中,data_dir指定了Nomad存储状态和日志的目录,bind_addr指定了节点监听的地址。当server.enabled为true时节点作为服务器运行,当client.enabled为true时节点作为客户端运行。client.servers列表告诉客户端应该连接哪些服务器节点。需要注意,服务器节点之间通过server_join或retry_join参数互相发现,而客户端只需要知道至少一个可用的服务器地址即可。
配置文件的加载顺序也很重要。如果同时使用-dev模式启动,会忽略大部分配置并创建一个内存态的测试集群。生产环境绝对不要使用-dev模式。建议将配置文件放在版本控制中,并通过配置管理工具分发到各节点,保证环境一致性。
二、Nomad服务器配置详解
服务器节点是Nomad集群的大脑,所有作业提交、状态查询和调度计算都在服务器上完成。服务器之间通过Raft协议选举领导者,只有领导者才能处理写入操作,但所有服务器都可以处理只读请求。配置服务器时,最关键的是指定bootstrap_expect参数,它告诉Nomad期望有多少台服务器参与集群引导。该参数的值必须等于实际服务器节点数量(通常为3、5或7,推荐奇数),在集群首次启动时所有服务器必须同时在线,否则引导过程会卡住。
下面是一个生产级的服务器配置示例:
data_dir = "/var/lib/nomad"
bind_addr = "10.0.0.11"
advertise {
http = "10.0.0.11"
rpc = "10.0.0.11"
serf = "10.0.0.11"
}
server {
enabled = true
bootstrap_expect = 3
encrypt = "your-gossip-encryption-key"
raft_protocol = 3
retry_join = ["10.0.0.11", "10.0.0.12", "10.0.0.13"]
}
acl {
enabled = true
token_ttl = "30s"
policy_ttl = "60s"
}
telemetry {
publish_allocation_metrics = true
publish_node_metrics = true
prometheus_metrics = true
}
这里的advertise块用于指定节点对外通告的地址,在云环境或多网卡机器上尤其重要。server.encrypt用于加密服务器之间的Serf通信,建议使用nomad operator keygen命令生成一个安全的密钥。raft_protocol建议设为3以获得更好的性能。retry_join允许服务器在启动时自动加入现有集群,当服务器重启或后续加入时不需要手动执行nomad server join命令。如果启用了ACL,需要配置acl.enabled = true,并创建相应的策略和令牌,否则所有请求会被拒绝。
服务器集群的引导过程分两种情况:首次启动时,所有指定了bootstrap_expect并且数量足够的服务器会自动进行Raft选举;如果集群已经存在,新加入的服务器只需要通过retry_join或手动执行nomad server join即可。务必注意,不要在已有集群中随意修改bootstrap_expect的值,否则可能导致Raft日志混乱。可以通过nomad operator raft list-peers命令查看当前Raft节点状态。
服务器节点还需要配置limits块来限制并发请求数量,防止过载。例如:
limits {
http_max_conns_per_client = 100
rpc_max_conns_per_client = 100
}
这些参数可以根据服务器硬件和预期负载调整。日志级别可以通过log_level = "INFO"设置,生产环境建议使用INFO或WARN,调试时再改为DEBUG。服务器配置完成后,使用nomad server members可以查看服务器集群成员列表,确认所有节点状态为alive。
三、Nomad客户端配置详解
客户端节点负责实际执行任务,它需要配置可用的驱动、资源限制、节点元数据以及与服务器通信的地址。最基本的客户端配置只需要设置client.enabled = true和client.servers列表即可,但为了生产环境的高效运行,通常还需要配置client.options、client.host_volume、driver等块。
一个典型的客户端配置示例如下:
data_dir = "/var/lib/nomad-client"
bind_addr = "10.0.0.21"
client {
enabled = true
servers = ["10.0.0.11:4647", "10.0.0.12:4647"]
node_class = "web"
meta {
environment = "production"
rack = "rack-01"
}
options {
"driver.raw_exec.enable" = "1"
"docker.volumes.enabled" = "true"
"docker.privileged.enabled" = "false"
}
host_volume "data" {
path = "/mnt/data"
read_only = false
}
}
driver "docker" {
config {
allow_privileged = false
volumes {
enabled = true
}
}
}
reserved {
cpu = 500
memory = 256
disk = 1024
reserved_ports = "22,80,443"
}
上面的配置中,client.servers列出了两个服务器地址,客户端会尝试按顺序连接,如果第一个不可用则自动切换到下一个。node_class用于给节点打上分类标签,作业可以通过constraint或affinity指定只在特定分类的节点上运行。client.meta可以添加任意键值对元数据,这些信息会显示在nomad node status中,并可用于调度约束。client.options用于启用某些驱动的高级选项,例如允许使用raw_exec驱动直接执行命令(默认关闭,因为存在安全隐患)。host_volume定义了一个宿主机目录卷,可以在作业中以volume方式挂载。
驱动配置也很重要。Nomad支持多种任务驱动,如Docker、Podman、QEMU、Java、Exec等。对于Docker驱动,需要在客户端节点上预先安装Docker引擎,并确保Nomad运行用户有权限访问Docker socket。driver "docker"块可以配置Docker相关的默认行为,例如是否允许特权容器、是否启用卷挂载等。如果希望使用Podman,需要将驱动配置为driver "podman"并指定相应路径。客户端配置中的reserved块用于保留一部分资源给操作系统和系统服务,防止调度器将所有CPU和内存分配给任务导致节点不稳定。
客户端节点启动后,会向服务器注册并定期发送心跳。可以通过nomad node status查看所有客户端节点,nomad node status -verbose <node-id>查看详细信息,包括可用资源、驱动状态、元数据等。如果客户端无法连接服务器,常见原因是端口未开放(默认RPC端口4647、Serf端口4648、HTTP端口4646)或防火墙限制,需要检查网络配置和安全组规则。
四、集群启动顺序与验证
在部署多节点Nomad集群时,启动顺序至关重要。首先启动所有服务器节点,并等待它们完成Raft引导和领导者选举。可以通过查看服务器日志中是否出现nomad: cluster leadership acquired来确认。当所有服务器节点的nomad server members都显示alive后,再启动客户端节点。客户端节点会通过client.servers中的地址自动加入集群。如果在服务器尚未就绪时启动客户端,客户端会不断重试连接,这在Nomad中是正常行为,不会造成数据损坏。
验证集群状态可以使用以下命令:
nomad server members nomad node status nomad agent-info
nomad server members显示服务器集群的Raft节点信息,包括每个节点的地址、状态和角色(leader或follower)。nomad node status显示所有客户端节点及其状态。如果一切正常,客户端节点的Status应为ready,Drain为false,Eligibility为eligible。可以通过nomad node drain -enable <node-id>临时将节点标记为不可调度,用于维护操作。
另一个重要的验证手段是提交一个简单的测试作业。下面是一个运行单个Docker容器的最小作业文件:
job "example" {
datacenters = ["dc1"]
type = "service"
group "cache" {
count = 1
network {
port "http" {}
}
task "redis" {
driver = "docker"
config {
image = "redis:7"
ports = ["http"]
}
resources {
cpu = 200
memory = 128
}
}
}
}
将此文件保存为example.nomad,执行nomad job run example.nomad提交。之后使用nomad job status example查看作业运行状态,如果所有分配(Allocations)的状态变为running,说明客户端和服务器配置正确,调度、驱动、网络均正常工作。如果出现问题,可以通过nomad alloc status <alloc-id>查看具体分配日志,或者查看客户端节点上的Nomad日志文件定位错误原因。
最后需要提醒的是,生产环境中的Nomad服务器节点建议至少三台,并分布在不同的故障域(如不同的机架或可用区)。客户端节点数量则取决于工作负载规模,可以动态扩展。所有节点的时钟应当保持同步,推荐使用NTP服务,否则Serf协议可能因时间偏差导致节点被标记为不可达。此外,建议将Nomad的HTTP API限制在受信任的网络中,或者启用ACL和TLS加密,避免未授权访问。
通过以上配置步骤,你可以搭建出一个功能完整的Nomad集群,并在其上部署各种类型的任务。当需要调整配置时,修改配置文件后可以使用nomad agent -config=<dir>重新启动服务,或者通过发送SIGHUP信号让Nomad重新加载部分配置(不是所有参数都支持热加载)。理解服务器和客户端配置的差异,是高效运维Nomad集群的基础,也是排查调度问题的关键所在。