잡 스펙의 `network` 블록

잡 스펙의 network 블록

network 블록은 태스크 그룹의 네트워킹 요구 사항(네트워크 모드와 포트 할당 포함)을 지정해요. Nomad에서 잡을 스케줄링하면 그것들은 다른 잡과 서비스와 함께 머신 fleet에 걸쳐 프로비저닝돼요. 잡이 어떤 호스트에 프로비저닝될지 미리 알 수 없기 때문에, Nomad는 태스크가 시작될 때 네트워크 구성을 제공해요.

이 문서는 포트에서 리슨 하려는 서비스에만 적용된다는 점에 유의하세요. 배치 잡이나 아웃바운드 연결만 하는 서비스는 포트를 할당할 필요가 없어요. 사용 가능한 어떤 인터페이스든 사용해 아웃바운드 연결을 만들기 때문이에요.

출처: 문서

본문

배치 job -> group -> **network**
job "docs" {
  group "example" {
    network {
      port "http" {}
      port "https" {}
      port "lb" {
        static = 8889
      }
    }
  }
}

네트워크 모드 (Network modes)

network 블록이 네트워킹 모드 bridge로 정의되면 태스크 그룹의 모든 태스크가 같은 네트워크 네임스페이스를 공유해요. 이것은 Consul 서비스 메시의 전제 조건이에요. 네트워크 네임스페이스 안에서 실행되는 태스크는 같은 호스트에서 네임스페이스 밖의 애플리케이션에는 보이지 않아요. 이렇게 하면 Connect가 활성화된 애플리케이션이 공유 네트워크 스택 안에서 localhost에만 바인딩하고, 인그레스·이그레스 트래픽에 프록시를 사용할 수 있어요.

bridge 모드를 사용하려면 클라이언트의 cni_path 구성에 지정된 위치에 참조 CNI 플러그인이 설치되어 있어야 해요. 이 플러그인들은 bridge 네트워크를 만들고 적절한 iptables 규칙을 구성하는 데 사용돼요.

네트워크 모드는 Linux 클라이언트에서 실행되는 할당에서만 지원돼요. 다른 모든 운영체제는 host 네트워킹 모드를 사용해요.

경고: bridge 네트워크 모드를 사용할 때 외부 접근을 막으려면 워크로드를 루프백 인터페이스에만 바인딩하세요. 자세한 내용은 Bridge networking 문서를 참고하세요.

매개변수 (Parameters)

  • mbits ([_deprecated_] int: 10) - MBits로 요구되는 대역폭을 지정.

  • port ([Port]: nil) - TCP/UDP 포트 할당을 지정하고 동적 포트와 예약 포트를 모두 지정하는 데 사용할 수 있어요.

  • mode (string: "host") - 네트워크 모드. 이 옵션은 Linux 클라이언트에서만 지원돼요. 다음 모드를 사용할 수 있어요:

    • none - 태스크 그룹은 네트워크 인터페이스가 없는 격리된 네트워크를 가짐.
    • bridge - 태스크 그룹은 호스트와 브리지된 인터페이스가 있는 격리된 네트워크 네임스페이스를 가짐. bridge 네트워킹은 현재 docker, exec, raw_exec, java 태스크 드라이버에서만 지원된다는 점에 유의하세요.
    • host - 각 태스크가 호스트 네트워크 네임스페이스에 참여하고 공유 네트워크 네임스페이스는 생성되지 않음.
    • cni/<cni network name> - 태스크 그룹은 CNI가 구성한 네트워크가 있는 격리된 네트워크 네임스페이스를 가짐.
  • hostname (string: "") - 네트워크 네임스페이스에 할당된 호스트네임. 이것은 현재 Docker 드라이버에서만, 그리고 mode가 bridge로 설정되었을 때만 지원돼요. 이 매개변수는 보간을 지원해요.

  • dns ([DNSConfig]: nil) - 할당에 대한 DNS 구성을 설정. 기본적으로 모든 태스크 드라이버는 클라이언트 호스트에서 DNS 구성을 상속받아요. DNS 구성은 현재 Linux 클라이언트에서만 지원돼요. mode="cni/*를 사용 중이면 이 값들이 CNI 플러그인이 반환하는 DNS 구성을 덮어쓴다는 점에 유의하세요.

  • cni ([CNIConfig]: nil) - mode="cni/*와 함께 사용할 할당별 네트워크 구성의 커스텀 CNI 인자를 설정.

port 매개변수 (port parameters)

  • static (int: nil) - 할당할 정적 TCP/UDP 포트. 생략하면 동적 포트가 선택돼요. system 또는 로드 밸런서 같은 특수 잡을 제외하고는 정적 포트 사용을 권장하지 않아요.

  • to (string:nil) - "bridge" 모드를 사용할 때 태스크의 네트워크 네임스페이스 안에서 매핑할 포트를 구성하는 데 적용. 이 필드를 생략하거나 -1로 설정하면 매핑된 포트가 스케줄러가 할당한 동적 포트와 같아져요. NOMAD_PORT_<label> 환경 변수는 to 값을 포함해요.

  • host_network (string:nil) - 포트를 할당할 때 사용할 호스트 네트워크 이름을 지정. 포트 매핑 시 호스트 포트는 일치하는 호스트 네트워크 주소로만 트래픽을 전달해요.

  • ignore_collision (bool: false) - 포트가 이미 예약되어 있을 수 있는 노드에 그룹을 배치할 수 있게 허용. SO_REUSEPORT 유닉스 소켓 옵션을 지원하는 프로그램을 위한 것으로, 프로그램의 여러 인스턴스가 같은 포트에 바인딩할 수 있어요. host 네트워크 모드와 static 포트에서만 호환돼요. 일부 태스크 드라이버(예: docker)는 배치 후 런타임 오류를 피하기 위해 network_mode = "host"(또는 유사한 것)를 설정해야 할 수도 있어요.

포트에 할당된 레이블은 서비스 디스커버리에서 포트를 식별하는 데 사용되고, 애플리케이션이 바인딩해야 할 포트를 나타내는 환경 변수 이름에도 사용돼요. 예:

port "foo" {}

태스크가 시작되면 다음 환경 변수가 전달돼요:

  • NOMAD_IP_foo - 주어진 포트 레이블에 할당된 호스트 IP.
  • NOMAD_PORT_foo - 주어진 포트 레이블의 포트 값.
  • NOMAD_ADDR_foo - 편의를 위해 사용할 수 있는 ip:port 결합.

포트의 레이블은 그저 텍스트일 뿐이며 Nomad에는 특별한 의미가 없어요.

0.0.0.0에 바인딩 (Binding to 0.0.0.0)

port 블록은 호스트 IP와 포트를 할당해요. 프로세스가 네트워크 네임스페이스 안에서 리슨하는 주소는 설정하지 않아요. 그건 애플리케이션에서 구성하세요:

  • 서비스가 어떤 인터페이스로든 전달되는 트래픽을 받아야 하면 NOMAD_PORT_<label>로 0.0.0.0(네임스페이스의 모든 주소)에 바인딩하세요.
  • 서비스가 할당 주소(bridge/CNI 모드에서 호스트 포트 매핑이 전달하는 IP)에서만 리슨해야 하면 NOMAD_ALLOC_IP_<label>에 바인딩하세요.
  • 프로세스가 호스트 네트워크 네임스페이스를 공유하고 스케줄링된 호스트 주소에서 리슨해야 하면 host 네트워크 모드에서만 NOMAD_IP_<label>에 바인딩하세요.
  • 서비스가 할당에만 비공개로 유지되어야 하면 127.0.0.1에 바인딩하세요(bridge 모드와 서비스 메시 프록시에서 흔함).

호스트 쪽 포트 게시는 할당된 호스트 네트워크 주소를 사용해요(host_network 참고). 클라이언트에 커스텀 host_network 블록이 없을 때 bridge 또는 cni/* 모드에서는 포트 매핑 규칙이 기본적으로 어떤 대상 주소와도 일치해요. bind_wildcard_default_host_network를 참고하세요.

dns 매개변수 (dns parameters)

  • servers (array<string>: nil) - 이름 해석에 할당이 사용하는 DNS 네임서버를 설정.
  • searches (array<string>: nil) - 호스트네임 조회의 검색 목록을 설정.
  • options (array<string>: nil) - 내부 리졸버 변수를 설정.

이 매개변수들은 보간을 지원해요.

cni 매개변수 (cni parameters)

  • args (map<string><string>: nil) - 네트워크 구성을 위한 CNI 인자를 설정. 이것들은 CNI 스펙에 따라 CNI_ARGS로 바뀌어요.

이 매개변수들은 보간을 지원해요.

예시 (Examples)

다음 예시는 network 블록만 보여줘요. network 블록은 위에 나열된 배치에서만 유효하다는 점을 기억하세요.

동적 포트 (Dynamic ports)

이 예시는 "http"로 레이블된 포트에 대한 동적 포트 할당을 지정해요. 동적 포트는 20000에서 32000 범위에서 할당돼요.

클러스터에서 실행되는 대부분의 서비스는 동적 포트를 사용해야 해요. 이는 포트가 스케줄러에 의해 동적으로 할당되고, 서비스가 시작 시 어느 포트에 바인딩할지 알기 위해 환경 변수를 읽어야 한다는 뜻이에요.

group "example" {
  network {
    port "http" {}
    port "https" {}
  }
}
network {
  port "http" {}
}

정적 포트 (Static ports)

정적 포트는 같은 포트를 가진 다른 잡이 이미 예약하지 않은 호스트에 잡을 배치해요.

이 예시는 "lb"로 레이블된 포트에 대한 정적 포트 할당을 지정해요.

network {
  port "lb" {
    static = 6539
  }
}

SO_REUSEPORT 유닉스 소켓 옵션을 지원하는 프로그램은 ignore_collision = true를 설정해 단일 노드에 여러 복사본을 배치할 수 있어요.

매핑된 포트 (Mapped ports)

일부 드라이버(Docker와 QEMU 등)는 포트 매핑을 허용해요. 매핑된 포트란 애플리케이션이 고정된 포트에서 리슨하고(환경 변수를 읽을 필요 없음) 동적 포트가 컨테이너 또는 가상 머신의 포트로 매핑되는 것을 의미해요.

group "app" {
  network {
    port "http" {
      to = 8080
    }
  }

  task "example" {
    driver = "docker"

    config {
      ports = ["http"]
    }
  }
}

위 예시는 Docker 드라이버용이에요. 서비스는 컨테이너 안에서 포트 8080에서 리슨해요. 드라이버가 동적 포트를 이 서비스에 자동으로 매핑해요.

태스크가 시작되면 HTTP 서비스가 바인딩된 호스트 포트를 나타내는 NOMAD_HOST_PORT_http라는 추가 환경 변수가 전달돼요.

Bridge 모드 (Bridge mode)

Bridge 모드는 호환되는 태스크가 네트워킹 스택과 인터페이스를 공유할 수 있게 해요. 그러면 Nomad가 개별 태스크 드라이버에 의존하지 않고도 포트 매핑을 수행할 수 있어요.

다음 예시는 bridge 모드와 포트 매핑을 사용하는 그룹 레벨 네트워크 블록이에요.

network {
  mode = "bridge"
  port "http" {
    static = 9002
    to     = 9002
  }
}

bridge 모드를 사용하면 firewalld가 활성화된 호스트에서 아웃바운드 네트워크 요청이 실패할 수 있어요. 여기에는 CentOS, Rocky Linux, Oracle Linux 같은 대부분의 RHEL 기반 Linux 배포판이 포함돼요. firewalld가 Nomad 잡에서 오는 네트워크 요청을 허용하게 하는 한 가지 해결책은 nomad 브리지 인터페이스를 신뢰 대상으로 표시하는 거예요.

$ sudo firewall-cmd --zone=trusted --add-interface=nomad
$ sudo firewall-cmd --zone=trusted --add-interface=nomad --permanent

이후 네트워크에 접근할 수 있게 영향받은 잡을 재시작해야 해요. 자세한 내용은 Docker 문서의 Docker and iptables에서 찾을 수 있어요.

DNS

다음 예시는 Google의 DNS 리졸버 8.8.8.8과 8.8.4.4를 사용하도록 할당을 구성해요.

network {
  dns {
    servers = ["8.8.8.8", "8.8.4.4"]
  }
}

Container Network Interface (CNI)

Nomad는 각 노드에 CNI 네트워크 구성에 대한 지문을 등록해 CNI를 지원해요. 이것들은 CNI 구성의 name 필드로 노드에 연결돼요. 그 name은 네트워크 mode 필드를 cni/<name> 형태로 설정할 때 사용할 수 있어요.

예를 들어 다음 CNI 구성이 노드에 있었다면, 다음의 네트워크 블록을 사용할 수 있어요.

{
  "cniVersion": "0.3.1",
  "name": "mynet",
  "plugins": [
    {
      "type": "ptp",
      "ipMasq": true,
      "ipam": {
        "type": "host-local",
        "subnet": "172.16.30.0/24",
        "routes": [
          {
            "dst": "0.0.0.0/0"
          }
        ]
      }
    },
    {
      "type": "portmap",
      "capabilities": { "portMappings": true }
    }
  ]
}
network {
  mode = "cni/mynet"
  port "http" {
    to = 8080
  }
}

Nomad 클라이언트는 정의된 포트 블록을 기반으로 portmap 플러그인에 대한 올바른 capabilities 인자를 구성해요.

IPv6 주소의 Stateless Address Autoconfiguration (SLAAC)

CNI 네트워킹은 CNI 플러그인이 얻은 주소가 할당 수명 동안 바뀌지 않을 것이라고 가정해요. Nomad의 서비스 디스커버리 지원도 주소가 바뀌지 않을 것이라고 가정해요. 커널이 IPv6 주소에 stateless address autoconfiguration (SLAAC)을 사용하도록 구성되어 있다면, 프리픽스가 할당될 때까지 기다리는 CNI 플러그인을 사용해야 해요. arodd/cni-slaacwait 저장소는 macvlan과 dhcp 플러그인을 함께 연결하고 주소를 기다리는 방법을 보여줘요. 하지만 태스크가 시작된 후 프리픽스가 바뀌면 네트워킹이 실패해요. CNI의 이 설계 제한에 대한 자세한 내용은 containernetworking/1016을 참고하세요. 대신 connect 블록과 Consul DNS를 사용해 워크로드 간에 트래픽을 동적으로 라우팅할 것을 권장해요.

CNI 인자 (CNI args)

다음 예시는 위에 지정된 커스텀 CNI 플러그인에 대한 CNI 인자를 지정해요.

network {
  mode = "cni/mynet"
  port "http" {
    to = 8080
  }
  cni {
    args = {
     "nomad.region" : "${node.region}"
    }
  }
}

호스트 네트워크 (Host networks)

경우에 따라 포트가 호스트의 특정 인터페이스나 주소에만 할당되어야 할 수 있어요. 포트의 host_network 필드는 포트 할당을 단일 명명된 호스트 네트워크로 제한해요. 포트에 host_network가 설정되면 Nomad는 그 이름의 host_network를 정의한 노드에 할당을 스케줄링해요. 설정하지 않으면 "default" 호스트 네트워크가 사용되는데, 일반적으로 기본 경로가 연결된 주소예요.

Nomad가 정의된 host_network가 있는 포트에 대해 포트 매핑을 할 때, 포트 매핑 규칙은 호스트 주소를 대상 주소로 사용해요. Docker 같은 태스크 드라이버는 같은 주소에 호스트 포트를 게시해요. 호스트 쪽을 0.0.0.0으로 게시하는 port 속성은 없어요. 필요한 인터페이스와 일치하는 host_network를 사용하거나, 0.0.0.0에 바인딩에서 설명된 기본 와일드카드 매핑 동작에 의존하세요.

network {
  mode = "bridge"

  # define a port to use for public https traffic
  port "https" {
    static       = 443
    to           = 8080
    host_network = "public"
  }
  # define a port that is only exposed to private traffic
  port "admin" {
    to           = 9000
    host_network = "private"
  }
}

모든 주소에 바인딩 (Bind on all addresses)

이 예시는 동적 호스트 포트를 컨테이너 포트 8080에 매핑해요. 프로세스는 컨테이너 안에서 0.0.0.0:8080에서 리슨하므로 전달된 트래픽을 받아요:

job "http" {
  group "api" {
    network {
      port "http" {
        to = 8080
      }
    }

    task "server" {
      driver = "docker"

      config {
        image = "hashicorp/http-echo:latest"
        args  = ["-listen", ":8080", "-text", "hello"]
        ports = ["http"]
      }
    }
  }
}

태스크가 호스트 네트워크 네임스페이스를 사용하고 동적 포트를 쓸 때는 리슨 주소로 0.0.0.0을, 포트로 NOMAD_PORT_<label>을 전달하세요:

task "server" {
  driver = "raw_exec"

  config {
    command = "/usr/local/bin/my-service"
    args    = ["--bind", "0.0.0.0", "--port", "${NOMAD_PORT_http}"]
  }
}

제한 사항 (Limitations)

  • 태스크 그룹 레벨에서 정의될 때 network 블록은 하나만 지정할 수 있어요.
  • 그룹 네트워크 포트에 대해 NOMAD_PORT_<label>과 NOMAD_HOST_PORT_<label> 환경 변수만 설정돼요.

더 알아보기 (Learn more)