Using a node attribute in a template with levant

I’m using levant on nomad 1.1.6 and utilizing the fileContents function to include an external YAML file.

job "traefik" {
    region      = "global"
    datacenters = ["dc1"]

    type        = "system" # job will be deployed to each node

    group "traefik" {
        count = 1

        task "traefik" {
            driver = "docker"
            config {
                image        = "traefik:v2.5"
                network_mode = "host"
                volumes = [
                    "local/traefik.yml:/etc/traefik/traefik.yml",
                ]
            }

            template {
                # traefik configuration is loaded from external file
                # https://learn.hashicorp.com/tutorials/nomad/dry-jobs-levant?in=nomad/templates
                data = <<EOF
[[ fileContents "traefik.yml" ]]
                EOF
                destination = "local/traefik.yml"
            }

            resources {
                cpu    = 100
                memory = 128
            }
        }

        network {
            port "http" {
                static = 8080
            }

            port "api" {
                static = 8081
            }
        }

        service {
            name = "traefik"

            check {
                name     = "alive"
                type     = "tcp"
                port     = "http"
                interval = "10s"
                timeout  = "2s"
            }
        }
    }
}

The external YML file for configuring traefik contains a attribute reference ${attr.driver.docker.bridge_ip} to connect to consul.
Consul is bound to 172.17.0.1:8500 but when deploying using levant deploy traefik does not receive any services from consul-catalog provider.
Modifying the template to use the above static address, traefik works and services are available.

Node attributes show proper values:

driver.docker             = 1
driver.docker.bridge_ip   = 172.17.0.1
driver.docker.os_type     = linux
driver.docker.runtimes    = io.containerd.runc.v2,io.containerd.runtime.v1.linux,runc
driver.docker.version     = 20.10.7

So my question is, ist this a levant issue, a nomad issue. How it is possible to use attributes in this place.

providers:
    consulCatalog: # https://doc.traefik.io/traefik/providers/consul-catalog/#traefik-consul-catalog
        prefix: traefik # the default
        exposedByDefault: false # services need a traefik.enable=true tag
        endpoint:
            address: "${attr.driver.docker.bridge_ip}:8500" # IP address of docker0 interface
            scheme: http
api:
    dashboard: true
    insecure: true # TODO Will it be available through https ?

entryPoints:
    http:
        address: ":8080"
    traefik:
        address: ":8081"

log:
    level: DEBUG

Hi @MartinAhrer

Thanks for using Nomad and Levant. The Levant templates don’t have access to Nomad attributes. They only have access to variables defined for the template and then passed to Levant. I’m not sure of your entire workflow, but is it possible in your workflow to define a variable and then pass it to Levant as you run it?

  • Derek and the Nomad Team

@DerekStrickland, thanks for the suggestion. I was already thinking of the following but don’t know if that will work.
After investigation and with your input my understanding is the following:

  • Attributes are runtime variables that are substituted when a job is deployed to a node
  • I can define a variable in an env block in the nomad job description
  • Then I should be able to use that env variable in my traefik configuration. traefik supports go templates with the ability to read env variables.

My specific problem is that I need to access a node specific IP: the IP of the docker0 network interface for accessing consul.
It will very likely to be the same but is not guaranteed as it could be configured different on each node. So I want the system to read it during deployment.

I think your understanding is correct. Let me know how it goes!

I added the following env block to my traefik task

env {
  DOCKER_BRIDGE_IP = "${attr.driver.docker.bridge_ip}"
}

As traefik is able to work with go templates I further modified the traefik configuration

providers:
    consulCatalog: # https://doc.traefik.io/traefik/providers/consul-catalog/#traefik-consul-catalog
        prefix: traefik # the default
        exposedByDefault: false # services need a traefik.enable=true tag
        endpoint:
            address: "{{env "DOCKER_BRIDGE_IP"}}:8500" # IP address of docker0 interface
            scheme: http

Thanks for creating levant. Before I had to install the nomad CLI plus envsubst command for substituting variables. This is now far better with one tool with powerful templating.

That’s great to hear! I also wanted to let you know about nomad-pack which is in tech preview as of yesterday! It’s a package manager for Nomad that borrows heavily from Levant, and we have packs for things like Traefik already, with more to come. You can also contribute packs you write. If you check it out, let us know how it works (or doesn’t) for you.