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
| 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
| [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
| sudo systemctl daemon-reload
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
| export NOMAD_ADDR="http://10.0.1.10:4646"
nomad server members
nomad node status
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
| nomad job validate webapp.nomad.hcl
nomad job run webapp.nomad.hcl
nomad job status webapp
nomad job status -verbose webapp
|
4.3 扩缩容
1 2 3 4 5 6 7 8
| nomad job scale webapp web 5
nomad job scale webapp web 2
nomad job status webapp
|
4.4 停止和删除 Job
1 2 3 4 5
| nomad job stop webapp
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
| journalctl -u nomad -f
nomad logs -job webapp
nomad logs <allocation-id>
|
5.3 备份与恢复
1 2 3 4 5
| nomad operator raft snapshot save -token=<token> nomad-backup.snap
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
| nomad operator raft snapshot save backup-v1.7.0.snap
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
nomad server members nomad operator raft list-peers
|
六、监控与告警
6.1 Prometheus 配置
1 2 3 4 5 6 7 8 9 10 11
| 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
| 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
| 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
| nomad job status -verbose webapp
nomad alloc status <allocation-id>
nomad node status -detailed
nomad node status -json <node-id> | jq '.Resources'
|
八、最佳实践
8.1 生产环境建议
- Server 节点数量:始终使用奇数(3 或 5),避免脑裂
- 分离角色:Server 和 Client 节点分离部署
- 持久化存储:使用独立磁盘存储 Raft 数据
- 网络隔离:Server 节点间使用专用网络
- 备份策略:定期备份 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 的部署和运维,可以为团队提供更灵活的编排方案选择。