HashiCorp Nomad

DevOpsコンテナNomadHashiCorpワークロードオーケストレーションスケジューラーエッジコンピューティング

DevOpsツール

HashiCorp Nomad

概要

HashiCorp Nomadは、シンプルで柔軟なワークロードオーケストレーターです。コンテナ、VM、バイナリアプリケーションを統一してスケジューリングし、Kubernetesよりも軽量でシンプルな選択肢を提供します。

詳細

HashiCorp Nomad(ノマド)は、HashiCorpが開発したワークロードオーケストレーションプラットフォームです。2015年にリリースされ、「シンプルで柔軟なワークロードオーケストレーター」として設計されました。コンテナ(Docker、Podman)、仮想マシン(QEMU)、バイナリアプリケーション、Java アプリケーション等、多様なワークロードを統一してスケジューリング可能。単一バイナリでの簡単デプロイ、Kubernetesより少ない概念とシンプルな設定、HashiCorpスタック(Vault、Consul、Terraform)との密な統合が特徴です。高性能なスケジューラー、マルチリージョン・マルチクラウド対応、ゼロダウンタイムデプロイ、ローリングアップデート、カナリアデプロイメント等の高度な機能を提供。特にエッジコンピューティング、IoT、規模の小さなクラスター、混在ワークロード環境での利用に適しており、Kubernetesの複雑さを避けたい組織に人気。現在では、金融、製造、エネルギー等の業界で、インフラストラクチャの簡素化とHashiCorpエコシステム統合を求める企業での採用が拡大しています。

メリット・デメリット

メリット

  • シンプル: Kubernetesより少ない概念で理解しやすい
  • マルチワークロード: コンテナ・VM・バイナリの統一管理
  • 軽量: 少ないリソースでの動作と単一バイナリデプロイ
  • HashiCorpスタック統合: Vault、Consul、Terraformとの密な連携
  • エッジ対応: 小規模環境・分散環境に最適
  • 運用性: 直感的なWeb UI とシンプルな設定
  • 柔軟性: 既存インフラとの統合が容易
  • マルチリージョン: 複数データセンター間での統一管理

デメリット

  • エコシステム: Kubernetesほどの豊富なツール群なし
  • 機能制限: 高度なオーケストレーション機能の不足
  • 学習リソース: ドキュメントとコミュニティがKubernetesより少ない
  • サードパーティ対応: Kubernetes向けツールの対応状況
  • ネットワーク: 複雑なネットワーク設定への制約
  • ストレージ: 永続ボリューム管理の機能が限定的
  • 企業サポート: エンタープライズサポートが限定的
  • 人材: Kubernetes経験者の方が多く採用しやすい

主要リンク

書き方の例

Nomadクラスター構築

# Nomad バイナリダウンロード・インストール
wget https://releases.hashicorp.com/nomad/1.6.0/nomad_1.6.0_linux_amd64.zip
unzip nomad_1.6.0_linux_amd64.zip
sudo mv nomad /usr/local/bin/

# 設定ファイル作成(サーバーモード)
sudo mkdir -p /etc/nomad.d
cat > /etc/nomad.d/nomad.hcl << EOF
datacenter = "dc1"
data_dir   = "/opt/nomad/data"
bind_addr  = "0.0.0.0"

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

client {
  enabled = true
  servers = ["10.0.1.10:4647", "10.0.1.11:4647", "10.0.1.12:4647"]
}

ui_config {
  enabled = true
}

consul {
  address = "127.0.0.1:8500"
}

vault {
  enabled = true
  address = "http://vault.example.com:8200"
}
EOF

# systemd サービス設定
cat > /etc/systemd/system/nomad.service << EOF
[Unit]
Description=Nomad
Documentation=https://www.nomadproject.io/
Requires=network-online.target
After=network-online.target

[Service]
Type=notify
ExecStart=/usr/local/bin/nomad agent -config=/etc/nomad.d/
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable nomad
sudo systemctl start nomad

Job定義(Docker コンテナ)

# web-app.nomad
job "web-app" {
  datacenters = ["dc1"]
  type        = "service"

  group "web" {
    count = 3

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

    service {
      name = "web-app"
      port = "http"
      
      tags = [
        "web",
        "nginx",
        "frontend"
      ]

      check {
        type     = "http"
        path     = "/health"
        interval = "30s"
        timeout  = "2s"
      }
    }

    task "nginx" {
      driver = "docker"

      config {
        image = "nginx:alpine"
        ports = ["http"]
        
        mount {
          type   = "bind"
          source = "local/nginx.conf"
          target = "/etc/nginx/nginx.conf"
        }
      }

      template {
        data = <<EOF
events {
    worker_connections 1024;
}
http {
    upstream app {
        server {{ range service "app-backend" }}{{ .Address }}:{{ .Port }};{{ end }}
    }
    server {
        listen 80;
        location / {
            proxy_pass http://app;
        }
        location /health {
            return 200 "OK";
        }
    }
}
EOF
        destination = "local/nginx.conf"
      }

      resources {
        cpu    = 100
        memory = 128
      }

      env {
        ENV = "production"
      }
    }
  }

  update {
    max_parallel      = 1
    min_healthy_time  = "10s"
    healthy_deadline  = "3m"
    progress_deadline = "10m"
    auto_revert       = true
    canary            = 1
  }
}

Job定義(バイナリアプリケーション)

# go-app.nomad
job "go-app" {
  datacenters = ["dc1"]
  type        = "service"

  group "app" {
    count = 2

    network {
      port "api" {
        to = 8080
      }
    }

    service {
      name = "go-api"
      port = "api"
      
      check {
        type     = "tcp"
        interval = "10s"
        timeout  = "2s"
      }
    }

    task "api-server" {
      driver = "exec"

      artifact {
        source      = "https://releases.example.com/myapp/v1.2.3/myapp-linux-amd64"
        destination = "local/"
        mode        = "file"
      }

      config {
        command = "local/myapp-linux-amd64"
        args    = ["--port", "${NOMAD_PORT_api}"]
      }

      env {
        DATABASE_URL = "postgresql://user:${vault_secret}@db.example.com:5432/myapp"
      }

      vault {
        policies = ["database-read"]
      }

      resources {
        cpu    = 200
        memory = 256
      }

      logs {
        max_files     = 3
        max_file_size = 10
      }
    }
  }
}

Batch Job(データ処理)

# batch-job.nomad
job "data-processing" {
  datacenters = ["dc1"]
  type        = "batch"

  periodic {
    cron             = "0 2 * * *"  # 毎日午前2時
    prohibit_overlap = true
  }

  group "process" {
    count = 1

    task "etl" {
      driver = "docker"

      config {
        image = "python:3.9"
        command = "python"
        args = ["process_data.py"]
        
        mount {
          type   = "bind"
          source = "local/script"
          target = "/app"
        }
      }

      artifact {
        source      = "git::https://github.com/company/data-scripts.git"
        destination = "local/script"
      }

      env {
        AWS_ACCESS_KEY_ID     = "${aws_access_key}"
        AWS_SECRET_ACCESS_KEY = "${aws_secret_key}"
        DATA_BUCKET          = "my-data-bucket"
      }

      vault {
        policies = ["aws-s3-access"]
      }

      resources {
        cpu    = 500
        memory = 1024
      }
    }
  }
}

システムJob(ログ収集)

# log-collector.nomad
job "log-collector" {
  datacenters = ["dc1"]
  type        = "system"

  group "collector" {
    task "fluentd" {
      driver = "docker"

      config {
        image = "fluentd:latest"
        
        mount {
          type   = "bind"
          source = "/var/log"
          target = "/var/log"
          readonly = true
        }
        
        mount {
          type   = "bind"
          source = "local/fluent.conf"
          target = "/fluentd/etc/fluent.conf"
        }
      }

      template {
        data = <<EOF
<source>
  @type tail
  path /var/log/nomad/nomad.log
  pos_file /var/log/fluentd/nomad.log.pos
  tag nomad.log
  format json
</source>

<match nomad.**>
  @type elasticsearch
  host {{ range service "elasticsearch" }}{{ .Address }}{{ end }}
  port {{ range service "elasticsearch" }}{{ .Port }}{{ end }}
  index_name nomad-logs
</match>
EOF
        destination = "local/fluent.conf"
      }

      resources {
        cpu    = 100
        memory = 128
      }
    }
  }
}

CSI Storage Plugin

# csi-plugin.nomad
job "csi-plugin" {
  datacenters = ["dc1"]
  type        = "system"

  group "csi-node" {
    task "csi-plugin" {
      driver = "docker"

      config {
        image = "my-registry/csi-driver:latest"
        
        args = [
          "--endpoint=${CSI_ENDPOINT}",
          "--nodeid=${node.unique.id}"
        ]

        privileged = true
      }

      csi_plugin {
        id        = "ebs"
        type      = "node"
        mount_dir = "/csi"
      }

      resources {
        cpu    = 100
        memory = 128
      }
    }
  }
}

Nomad CLI操作

# Job管理
nomad job run web-app.nomad
nomad job status web-app
nomad job stop web-app
nomad job restart web-app

# スケーリング
nomad job scale web-app 5

# デプロイメント確認
nomad deployment list
nomad deployment status <deployment-id>
nomad deployment promote <deployment-id>

# Allocation管理
nomad alloc status <alloc-id>
nomad alloc logs <alloc-id>
nomad alloc exec <alloc-id> /bin/sh

# ノード管理
nomad node status
nomad node drain <node-id>
nomad node eligibility <node-id> ineligible

# システム確認
nomad server members
nomad operator autopilot get-config
nomad system gc

# メトリクス・モニタリング
nomad metrics
nomad monitor -log-level DEBUG

高可用性設定

# nomad.hcl (HA設定)
server {
  enabled          = true
  bootstrap_expect = 3
  
  server_join {
    retry_join = ["provider=aws tag_key=Environment tag_value=production"]
  }
  
  encrypt = "base64-encrypted-gossip-key"
  
  autopilot {
    cleanup_dead_servers      = true
    last_contact_threshold    = "200ms"
    max_trailing_logs         = 250
    server_stabilization_time = "10s"
  }
}

acl {
  enabled = true
}

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"
}