external-dns icon indicating copy to clipboard operation
external-dns copied to clipboard

feat(source): add Nomad service

Open kiurchv opened this issue 8 months ago • 18 comments

Description

This PR introduces a new source for ExternalDNS: nomad-service.

The purpose of this source is to allow ExternalDNS to discover DNS endpoints from services registered in HashiCorp Nomad, a flexible workload orchestrator. This enables ExternalDNS users to manage DNS records for workloads orchestrated by Nomad using the same toolset they already use for Kubernetes and other platforms.

Nomad provides native service discovery and a metadata-rich API. This source leverages the service metadata—specifically, service tags—to configure DNS endpoints.

Example

Here's an example of a Nomad job that registers a service with DNS configuration:

job "whoami" {
  group "demo" {
    network {
      mode = "host"

      port "http" {
        static = 80
      }
    }

    service {
      name = "whoami-demo"
      port = "http"
      provider = "nomad"
      tags = [
        "external-dns.hostname=whoami.example.org.",
      ]
    }

    task "server" {
      driver = "docker"

      config {
        image = "traefik/whoami"
        ports = ["http"]
      }

      env {
        WHOAMI_PORT_NUMBER = "${NOMAD_PORT_http}"
      }
    }
  }
}

This job will result in ExternalDNS creating a DNS record for whoami.example.org. pointing to the service's IP address.

CLI Flags

The following optional command-line flags were introduced to configure the Nomad client used by the source:

--nomad-address=""             Nomad endpoint address. If empty, defaults to $NOMAD_ADDR or http://127.0.0.1:4646
--nomad-region=""              Nomad region to use. If not provided, the default agent region is used
--nomad-token=NOMAD-TOKEN      Nomad per-request ACL token
--nomad-wait-time=0s           WaitTime limits how long a Watch will block. If not provided, the agent default values will be used

Example Usage

To run ExternalDNS with the nomad-service source and an in-memory provider:

external-dns \
  --source=nomad-service \
  --provider=inmemory \
  --nomad-address=http://127.0.0.1:4646

Note on Nomad Tags vs Kubernetes Annotations

Nomad does not have annotations like Kubernetes, so ExternalDNS-specific configuration is done through service tags.

Unlike Kubernetes annotations, which are a key-value map, Nomad tags are represented as a flat array of strings. To work around this difference, the implementation expects tags to follow a key=value format under the external-dns. prefix.

For example:

tags = [
  "external-dns.hostname=example.nomad.internal.",
  "external-dns.ttl=300",
  "external-dns.controller=dns-controller",
  "external-dns.set-identifier=my-id"
]

Most of ExternalDNS annotations are supported via tags (e.g., hostname, TTL, target, etc.), provider-specific configuration is also supported.


This integration:

  • Provides a consistent developer experience across platforms like Kubernetes and Nomad
  • Enables automated DNS record management for Nomad-deployed services
  • Uses the pluggable ExternalDNS architecture to extend support to non-Kubernetes orchestrators

Checklist

  • [x] Unit tests updated
  • [x] End user documentation updated

kiurchv avatar Apr 14 '25 00:04 kiurchv

CLA Signed

The committers listed above are authorized under a signed CLA.

  • :white_check_mark: login: kiurchv / name: Myroslav Kiurchev (0959ee2ef75e371e8c2ce6bf5dcc635b39df0b5b, f997a1c349c4de8bb77ae8b7bd9f5341bc4e1ffc, 9e5b7eb847f80b7c480c7f2b99c6bbd251e3d897, 2c13ebef2972002b4664a9fff42d4703b6c30009)

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Once this PR has been reviewed and has the lgtm label, please assign raffo for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment Approvers can cancel approval by writing /approve cancel in a comment

k8s-ci-robot avatar Apr 14 '25 00:04 k8s-ci-robot

Welcome @kiurchv!

It looks like this is your first PR to kubernetes-sigs/external-dns 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-sigs/external-dns has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. :smiley:

k8s-ci-robot avatar Apr 14 '25 00:04 k8s-ci-robot

Hi @kiurchv. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

k8s-ci-robot avatar Apr 14 '25 00:04 k8s-ci-robot

Nomad is not a kubernetes source, it's completely different orchestrator/scheduler. Could you explain a little bit more how the integration suppose to work? And what is wrong with native Nomad + Consul DNS handling that is the reccomeded way of doing things on that platform?

ivankatliarchuk avatar Apr 14 '25 07:04 ivankatliarchuk

@ivankatliarchuk Like a Kubernetes Nomad have similar services concept and similar to Kubernetes services each Nomad service points to a workload instance IP address. And since ExternalDNS architecture allows to implement any Source to be independent of Kubernetes cluster presence and run controller application in any orchestrator/scheduler setup I don't see any issues with running ExternalDNS inside Nomad cluster. There are already present CloudFoundry source which is using only CloudFoundry API and not depends on Kubernetes.

Historically Nomad has an integration with Consul service discovery (which allows to achieve service address resolution via DNS similar to Kubernetes internal DNS naming). Also, since version 1.3 Nomad has native service discovery mechanism, which enables service discovery via Nomad API (but not via DNS). Neither of these service discovery mechanisms address the problem which solves ExternalDNS.

There are similar solutions present such as nomad-external-dns and D53 but they are pretty limited in terms of feature set and provider support.

So my proposal to add a new source implementation to ExternalDNS seems like a shortest and unobtrusive way to bring such functionality to Nomad

kiurchv avatar Apr 14 '25 14:04 kiurchv

My concern isn't with Nomad itself, but whether it effectively serves our project's target audience. I have no opinion, opeartional support could be tricky as we are mainly kubernetes experts, but could be an interesting experiment.

@mloiseleur @Raffo @szuecs wdyt?

ivankatliarchuk avatar Apr 14 '25 17:04 ivankatliarchuk

@ivankatliarchuk Why not ? It's interesting to have alternatives.

@kiurchv First, thanks for this proposal.

Is this possible to strictly align current k8s annotations with nomad service tags ?

mloiseleur avatar Apr 27 '25 19:04 mloiseleur

@mloiseleur Thanks for your attention.

Sure this is possible. Actually the current implementation with shortened annotation names was inspired by similar Traefik's Nomad service discovery integration: https://doc.traefik.io/traefik/routing/providers/nomad

So if we're using Traefik as load balancer in Nomad cluster such service configuration could look like this (example from the real Nomad job):

service {
  name = "whoami"
  port = "http"
  provider = "nomad"
  tags = [
    "external-dns.hostname=whoami.internal",
    "external-dns.target=${attr.unique.network.ip-address}",
    "traefik.enable=true",
    "traefik.http.routers.http.rule=Host(`whoami.internal`)",
  ]
}

kiurchv avatar Apr 27 '25 20:04 kiurchv

/ok-to-test

ivankatliarchuk avatar Apr 27 '25 23:04 ivankatliarchuk

/label tide/merge-method-squash

ivankatliarchuk avatar Apr 27 '25 23:04 ivankatliarchuk

Actually the current implementation with shortened annotation names was inspired by similar Traefik's Nomad service discovery integration

Thanks, it's more clear. Maybe we will do the same for k8s annotations one day :).

mloiseleur avatar Apr 29 '25 06:04 mloiseleur

This PR LGTM.

@kiurchv Do you think you can rebase this PR ? We need one or two review and we should be good to go.

If this cannot make it for the release, I'm thinking about adding a line about this PR in order to find some interested nomad users to come and review this PR.

/retitle feat(source): add Nomad service

mloiseleur avatar May 10 '25 15:05 mloiseleur

@mloiseleur Done. Also, I've moved duplicated ServiceSource code into own function

kiurchv avatar May 10 '25 19:05 kiurchv

PR needs rebase.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

k8s-ci-robot avatar May 14 '25 13:05 k8s-ci-robot

The Kubernetes project currently lacks enough contributors to adequately respond to all PRs.

This bot triages PRs according to the following rules:

  • After 90d of inactivity, lifecycle/stale is applied
  • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
  • After 30d of inactivity since lifecycle/rotten was applied, the PR is closed

You can:

  • Mark this PR as fresh with /remove-lifecycle stale
  • Close this PR with /close
  • Offer to help out with Issue Triage

Please send feedback to sig-contributor-experience at kubernetes/community.

/lifecycle stale

k8s-triage-robot avatar Aug 12 '25 14:08 k8s-triage-robot

/remove-lifecycle stale

kiurchv avatar Aug 29 '25 14:08 kiurchv

The Kubernetes project currently lacks enough contributors to adequately respond to all PRs.

This bot triages PRs according to the following rules:

  • After 90d of inactivity, lifecycle/stale is applied
  • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
  • After 30d of inactivity since lifecycle/rotten was applied, the PR is closed

You can:

  • Mark this PR as fresh with /remove-lifecycle stale
  • Close this PR with /close
  • Offer to help out with Issue Triage

Please send feedback to sig-contributor-experience at kubernetes/community.

/lifecycle stale

k8s-triage-robot avatar Nov 27 '25 14:11 k8s-triage-robot