HashiCorp Nomad 集群部署与运维实战

一、概述

HashiCorp Nomad 是一个简单、灵活的编排器,用于部署和管理任何类型的应用程序(容器、传统应用、微服务)。与 Kubernetes 相比,Nomad 更轻量、更易用,适合中小规模集群和混合工作负载场景。

1.1 核心特性

  • 简单:单二进制文件,无需外部依赖
  • 灵活:支持 Docker、Java、Python、Go 等多种驱动
  • 可扩展:支持从单节点到全球多区域部署
  • 高可用:内置 Raft 共识算法,支持集群高可用

1.2 架构组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────────────────────────┐
│ Nomad Cluster │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Server │ │ Server │ │ Server │ │
│ │ (Raft) │ │ (Raft) │ │ (Raft) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ▲ ▲ ▲ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ┌─────────────┐ ┌──────┴──────┐ ┌─────────────┐ │
│ │ Client │ │ Client │ │ Client │ │
│ │ (Worker) │ │ (Worker) │ │ (Worker) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘

二、部署规划

2.1 硬件要求

角色 最低配置 推荐配置 数量
Server 2C4G 4C8G 3 或 5(奇数)
Client 2C4G 根据负载 N

2.2 网络规划

端口 协议 用途 开放范围
4646 TCP/UDP HTTP API 全部节点
4647 TCP RPC(Server) Server 节点间
4648 TCP Raft(Server) Server 节点间
4649 TCP Serf(Server) 全部节点
4649 UDP Serf(Client) 全部节点

2.3 节点规划示例

1
2
3
4
5
6
7
8
9
Server 节点:
- nomad-server-01: 10.0.1.10
- nomad-server-02: 10.0.1.11
- nomad-server-03: 10.0.1.12

Client 节点:
- nomad-client-01: 10.0.1.20
- nomad-client-02: 10.0.1.21
- nomad-client-03: 10.0.1.22

三、部署步骤

3.1 安装 Nomad(所有节点)

1
2
3
4
5
6
7
8
9
10
# 下载 Nomad
wget https://releases.hashicorp.com/nomad/1.7.0/nomad_1.7.0_linux_amd64.zip
unzip nomad_1.7.0_linux_amd64.zip
sudo mv nomad /usr/local/bin/
nomad version

# 创建必要目录
sudo mkdir -p /opt/nomad/data
sudo mkdir -p /etc/nomad.d
sudo mkdir -p /var/log/nomad

3.2 配置 Server 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# /etc/nomad.d/nomad-server.hcl
datacenter = "dc1"
data_dir = "/opt/nomad/data"
log_level = "INFO"

server {
enabled = true
bootstrap_expect = 3
server_join {
retry_join = ["10.0.1.10", "10.0.1.11", "10.0.1.12"]
}
}

client {
enabled = false
}

addresses {
http = "0.0.0.0"
rpc = "0.0.0.0"
serf = "0.0.0.0"
}

bind_addr = "0.0.0.0"

ports {
http = 4646
rpc = 4647
serf = 4648
}

3.3 配置 Client 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# /etc/nomad.d/nomad-client.hcl
datacenter = "dc1"
data_dir = "/opt/nomad/data"
log_level = "INFO"

server {
enabled = false
server_join {
retry_join = ["10.0.1.10", "10.0.1.11", "10.0.1.12"]
}
}

client {
enabled = true
network_interface = "eth0"

options {
"driver.raw_exec.enable" = "1"
"driver.docker.volumes" = "1"
}

host_volume "shared-data" {
path = "/opt/nomad/data/shared"
read_only = false
}
}

addresses {
http = "0.0.0.0"
rpc = "0.0.0.0"
serf = "0.0.0.0"
}

bind_addr = "0.0.0.0"

ports {
http = 4646
rpc = 4647
serf = 4648
}

# 插件配置
plugin "docker" {
config {
volumes {
enabled = true
}
}
}

3.4 创建 Systemd 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# /etc/systemd/system/nomad.service
[Unit]
Description=Nomad
Documentation=https://nomadproject.io/docs/
Wants=network-online.target
After=network-online.target

[Service]
User=root
Group=root
PIDFile=/var/run/nomad.pid
ExecStart=/usr/local/bin/nomad agent -config /etc/nomad.d
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
KillSignal=SIGINT
LimitNOFILE=65536
LimitNPROC=infinity

[Install]
WantedBy=multi-user.target

3.5 启动服务

1
2
3
4
5
6
7
8
9
10
# 重新加载 systemd
sudo systemctl daemon-reload

# 启动 Server 节点(先启动 3 个 Server)
sudo systemctl enable nomad
sudo systemctl start nomad

# 检查状态
sudo systemctl status nomad
journalctl -u nomad -f

3.6 验证集群状态

1
2
3
4
5
6
7
8
9
10
11
# 在 Server 节点执行
export NOMAD_ADDR="http://10.0.1.10:4646"

# 查看集群成员
nomad server members

# 查看节点状态
nomad node status

# 查看 Raft 状态
nomad operator raft list-peers

四、部署应用示例

4.1 简单的 Job 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# webapp.nomad.hcl
job "webapp" {
datacenters = ["dc1"]
type = "service"

group "web" {
count = 3

network {
port "http" {
static = 8080
to = 80
}
}

service {
name = "webapp"
port = "http"

check {
name = "http_health"
type = "http"
path = "/health"
interval = "10s"
timeout = "2s"
}
}

task "nginx" {
driver = "docker"

config {
image = "nginx:alpine"
ports = ["http"]
}

resources {
cpu = 500
memory = 256
}
}
}
}

4.2 提交 Job

1
2
3
4
5
6
7
8
9
10
11
# 验证 Job 配置
nomad job validate webapp.nomad.hcl

# 提交 Job
nomad job run webapp.nomad.hcl

# 查看 Job 状态
nomad job status webapp

# 查看分配情况
nomad job status -verbose webapp

4.3 扩缩容

1
2
3
4
5
6
7
8
# 扩容到 5 个实例
nomad job scale webapp web 5

# 缩容到 2 个实例
nomad job scale webapp web 2

# 查看缩放状态
nomad job status webapp

4.4 停止和删除 Job

1
2
3
4
5
# 停止 Job
nomad job stop webapp

# 删除 Job(包括历史)
nomad job purge webapp

五、运维管理

5.1 节点维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 标记节点为不可调度(维护模式)
nomad node drain -enable -self

# 查看节点状态
nomad node status -detailed

# 取消维护模式
nomad node drain -disable -self

# 从集群移除节点
nomad node status
nomad node drain -enable <node-id>
# 等待任务迁移完成后
nomad node toggle-drain -disable <node-id>
nomad node apply -purge <node-id>

5.2 日志管理

1
2
3
4
5
6
7
8
# 查看 Nomad 日志
journalctl -u nomad -f

# 查看 Job 日志
nomad logs -job webapp

# 查看特定分配日志
nomad logs <allocation-id>

5.3 备份与恢复

1
2
3
4
5
# 备份 Raft 数据
nomad operator raft snapshot save -token=<token> nomad-backup.snap

# 恢复 Raft 数据(灾难恢复)
nomad operator raft snapshot restore -token=<token> nomad-backup.snap

5.4 升级流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 备份当前状态
nomad operator raft snapshot save backup-v1.7.0.snap

# 2. 逐台升级 Server 节点(滚动升级)
# 在每台 Server 上:
wget https://releases.hashicorp.com/nomad/1.8.0/nomad_1.8.0_linux_amd64.zip
unzip -o nomad_1.8.0_linux_amd64.zip
sudo mv nomad /usr/local/bin/
sudo systemctl restart nomad

# 3. 验证集群健康
nomad server members
nomad operator raft list-peers

# 4. 升级 Client 节点(同上)

六、监控与告警

6.1 Prometheus 配置

1
2
3
4
5
6
7
8
9
10
11
# prometheus.yml
scrape_configs:
- job_name: 'nomad-server'
static_configs:
- targets: ['10.0.1.10:4646', '10.0.1.11:4646', '10.0.1.12:4646']
metrics_path: '/v1/metrics'

- job_name: 'nomad-client'
static_configs:
- targets: ['10.0.1.20:4646', '10.0.1.21:4646', '10.0.1.22:4646']
metrics_path: '/v1/metrics'

6.2 关键监控指标

指标 说明 告警阈值
nomad_server_raft_fsm_pending Raft 待处理任务 > 100
nomad_server_raft_commit_time Raft 提交延迟 > 1s
nomad_client_allocs_running 运行中的分配数 -
nomad_client_allocs_failed 失败的分配数 > 0
nomad_job_allocs_desired 期望的分配数 与实际对比

6.3 告警规则示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# alerting_rules.yml
groups:
- name: nomad
rules:
- alert: NomadServerDown
expr: up{job="nomad-server"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Nomad Server {{ $labels.instance }} 宕机"

- alert: NomadJobFailed
expr: nomad_client_allocs_failed > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Nomad Job 分配失败"

七、常见问题排查

7.1 Server 节点无法加入集群

1
2
3
4
5
6
7
8
9
# 检查网络连通性
telnet 10.0.1.10 4647
telnet 10.0.1.10 4648

# 查看日志
journalctl -u nomad | grep -i "join\|raft"

# 检查防火墙
sudo iptables -L -n | grep 464

7.2 Client 节点无法注册

1
2
3
4
5
6
7
8
# 检查 Server 地址配置
cat /etc/nomad.d/nomad-client.hcl | grep retry_join

# 查看注册日志
journalctl -u nomad | grep -i "register\|heartbeat"

# 手动测试连接
nomad server members -address=http://10.0.1.10:4646

7.3 Job 一直处于 Pending 状态

1
2
3
4
5
6
7
8
9
10
11
# 查看 Job 状态详情
nomad job status -verbose webapp

# 查看分配失败原因
nomad alloc status <allocation-id>

# 检查节点资源
nomad node status -detailed

# 查看节点可用资源
nomad node status -json <node-id> | jq '.Resources'

八、最佳实践

8.1 生产环境建议

  1. Server 节点数量:始终使用奇数(3 或 5),避免脑裂
  2. 分离角色:Server 和 Client 节点分离部署
  3. 持久化存储:使用独立磁盘存储 Raft 数据
  4. 网络隔离:Server 节点间使用专用网络
  5. 备份策略:定期备份 Raft 快照

8.2 安全加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 启用 TLS
tls {
http = true
rpc = true

ca_file = "/etc/nomad.d/ca.pem"
cert_file = "/etc/nomad.d/server.pem"
key_file = "/etc/nomad.d/server-key.pem"
}

# 启用 ACL
acl {
enabled = true
token_policy = "write"
}

8.3 资源预留

1
2
3
4
5
6
7
# 为系统预留资源
client {
reserved {
cpu = 1000
memory = 2048
}
}

九、总结

Nomad 以其简洁的架构和灵活的设计,成为 Kubernetes 之外的优秀编排选择。适合以下场景:

  • 中小规模集群(< 1000 节点)
  • 混合工作负载(容器 + 传统应用)
  • 快速部署和迭代需求
  • 多区域、多数据中心部署

掌握 Nomad 的部署和运维,可以为团队提供更灵活的编排方案选择。