wayanjimmy
ENID

Install Nomad, Podman, and Traefik for Home Selfhosting

Background #

Nomad is a scheduler developed by Hashicorp, which can be used to schedule containers, raw execution, and other applications. This unique feature makes Nomad capable of running various types of applications, not just containers through the Task Driver concept.

Prerequisites #

Knowledge Requirements #

Required Environment #

What needs to be prepared:

Important Notes #

Installation #

Make sure the target server can be accessed via SSH key. Then run the following command:

bash
hashi-up nomad install \
  --ssh-target-addr 192.168.1.10 \
  --ssh-target-user ubuntu \
  --ssh-target-key ~/.ssh/id_ed25519 \
  --server

Please make sure to replace the IP, user, and SSH key address according to your environment.

Podman Task Driver #

Podman is one of the task drivers supported by Nomad. Why choose Podman? Because recently I’ve been using Podman as my daily driver. The installation process can follow the guide from Nomad’s official documentation on this page.

Additionally, make sure Podman is already installed.

bash
sudo apt install podman

Next, we need to ensure that Nomad recognizes this plugin.

bash
sudo vim /etc/nomad.d/nomad.hcl

Add the following lines.

hcl
# generated with hashi-up

datacenter = "dc1"
data_dir   = "/opt/nomad"
plugin_dir = "/opt/nomad/data/plugins"
server {
  enabled          = true
  bootstrap_expect = 1
}
plugin "nomad-driver-podman" {      
  enabled = true
}                                   

Restart the Nomad server.

bash
sudo systemctl restart nomad

Make sure the plugin appears in the task driver section.

bash
nomad node status -self -short | grep Drivers
CSI Drivers     = <none>
Drivers         = exec,podman

Nomad Single Node #

To run Nomad as a single-node, we need to enable client mode on this Nomad server. Edit the file /etc/nomad.d/nomad.hcl again and add the following lines:

hcl
# generated with hashi-up

datacenter = "dc1"
data_dir   = "/opt/nomad"
plugin_dir = "/opt/nomad/data/plugins"
server {
  enabled          = true
  bootstrap_expect = 1
}
client {                            
    enabled = true
}                                   

Don’t forget to restart the nomad service

Traefik and Nomad service discovery #

Open nomad webui through your browser, usually at http://192.168.1.10:4646/ui. Then click the “Run job” button and add the following job specification:

hcl
job "traefik" {
  datacenters = ["dc1"]
  type        = "service"

  group "traefik" {
    count = 1

    network {
      port "http" {
        static = 8080
      }

      port "admin" {
        static = 8081
      }
    }

    service {
      name     = "traefik-http"
      provider = "nomad"
      port     = "http"
    }

    task "server" {
      driver = "podman"

      config {
        image = "docker.io/traefik:v2.11.20"
        ports = ["admin", "http"]

        args = [
          "--api.dashboard=true",
          "--api.insecure=true", # Traefik will be installed in insecure mode, preferably not exposed to the internet
          "--entrypoints.web.address=:${NOMAD_PORT_http}",
          "--entrypoints.traefik.address=:${NOMAD_PORT_admin}",
          "--providers.nomad=true",
          "--providers.nomad.endpoint.address=http://${NOMAD_IP_http}:4646",
          "--providers.nomad.exposedByDefault=false"
        ]
      }
    }
  }
}

Deploy demo application #

Next, we’ll deploy an application that demonstrates simple load balancing through Traefik that is already connected to Nomad service discovery.

hcl
job "demo-webapp" {
  datacenters = ["dc1"]

  group "demo" {
    count = 3

    network {
      port  "http"{
        to = -1
      }
    }

    service {
      name = "demo-webapp"
      port = "http"
      provider = "nomad"

      tags = [
        "traefik.enable=true",
        "traefik.http.routers.demo-webapp-http.rule=Host(`demo-webapp-192-168-1-10.nip.io`)",
        "traefik.http.routers.demo-webapp-http.tls=false",
      ]

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

    task "server" {
      env {
        PORT    = "${NOMAD_PORT_http}"
        NODE_IP = "${NOMAD_IP_http}"
      }

      driver = "podman"

      config {
        image = "docker.io/hashicorp/demo-webapp-lb-guide"
        ports = ["http"]
      }
    }
  }
}

After the above job is run, the demo-webapp application will go through the Load balancing process by Traefik first, then be directed to each container.

bash
ubuntu@nomad01:~$ curl http://demo-webapp-192-168-1-10.nip.io:8080
Welcome! You are on node 192.168.1.10:20190
ubuntu@nomad01:~$ curl http://demo-webapp-192-168-1-10.nip.io:8080
Welcome! You are on node 192.168.1.10:25458
ubuntu@nomad01:~$ curl http://demo-webapp-192-168-1-10.nip.io:8080
Welcome! You are on node 192.168.1.10:25482
ubuntu@nomad01:~$ curl http://demo-webapp-192-168-1-10.nip.io:8080
Welcome! You are on node 192.168.1.10:20190

As you can see, responding to our requests are several containers each with different ports, those ports are allocated by Nomad dynamically.

That’s the short tutorial, in the next article I will discuss how to run stateful applications like databases with host volumes in Nomad.

Subscribe