클러스터에 노드 연결하기

클러스터에 노드 연결하기

개별 노드로 노마드 클러스터를 만들려면 노드들이 서로를 알게 해야 해요. 이를 수행하는 방법은 여러 가지가 있어요:

  • 수동 부트스트랩(Manual bootstrap)
  • 클라우드 자동 조인(Cloud Auto-Join)
  • Consul 활용

이 가이드는 각 방법을 설명하고 설정 스니펫을 제공해요. 이 스니펫을 여러분의 설정을 시작하는 출발점으로 사용할 수 있어요.

클라이언트 도입 토큰(client node introduction tokens)을 사용해 어떤 클라이언트가 클러스터에 조인할 수 있는지 제한할 수도 있어요.

출처: 문서

본문

수동 클러스터링

노마드 클러스터를 수동으로 부트스트랩하는 방법은 추가 도구에 의존하지 않지만, 클러스터를 구성하는 과정에 운영자의 참여가 필요해요. 부트스트랩할 때 노마드 서버와 클라이언트는 적어도 하나의 노마드 서버 주소를 알고 있어야 해요.

이런 방식은 달걀-닭 문제를 만들 수 있어요. 즉, 나머지 서버와 클라이언트가 클러스터에 조인하려면 먼저 하나의 서버가 완전히 부트스트랩되고 구성되어야 해요. 이 요구사항은 프로비저닝 시간을 늘리고 프로비저닝 과정에 순서 의존성을 추가할 수 있어요.

먼저 단일 노마드 서버를 부트스트랩하고 그 IP 주소를 확보해야 해요. 그 노드의 IP 주소를 얻으면 이 주소를 설정에 넣어 주세요.

노마드 서버의 경우 설정은 대략 이렇게 생겼어요:

server {
  enabled          = true
  bootstrap_expect = 3

  # This is the IP address of the first server provisioned
  server_join {
    retry_join = ["<known-address>:4648"]
  }
}

대안으로, 모든 서버가 시작된 뒤 개별 서버에서 server join 명령을 실행해 서버 주소를 제공해 서버들을 클러스터링할 수도 있어요. server join 명령은 항상 리더(leader)에 대해 실행해야 해요. 모든 서버가 다른 서버 하나에 조인할 수 있고, 이후 gossip 프로토콜로 나머지 서버를 발견해요.

$ nomad server join <known-address>

노마드 클라이언트의 경우 설정은 대략 이렇게 생겨요:

client {
  enabled = true
  server_join {
    retry_join = ["<known-address>:4647"]
  }
}

클라이언트 노드의 서버 목록은 node config 명령으로 실행 중에 업데이트할 수 있어요.

$ nomad node config -update-servers <IP>:4647

포트는 RPC 포트에 해당해요. IP 주소에 포트를 지정하지 않으면 기본 RPC 포트인 4647이 사용돼요.

서버가 클러스터에 추가되거나 제거되면 이 정보는 클라이언트에 푸시돼요. 따라서 서버 하나만 지정하면 돼요. 최초 접촉 이후에는 클라이언트 리전에 있는 전체 서버 목록이 클라이언트와 공유되거든요.

클라우드 자동 조인 사용

retry_join 파라미터는 go-discover 라이브러리의 통합 인터페이스를 사용해 클라우드 메타데이터로 자동 클러스터 조인을 지원해요. 지원되는 클라우드 제공자에서 retry-join을 사용하려면 명령줄이나 설정 파일에 key=value key=value ... 문자열로 구성을 지정해요. 값은 문자 그대로 받아들여지며 URL 인코딩하면 안 돼요. 값에 공백, 백슬래시, 큰따옴표가 포함되면 큰따옴표로 감싸고 일반적인 이스케이프 규칙을 적용해야 해요.

{
  "retry_join": ["provider=my-cloud config=val config2=\"some other val\" ..."]
}

cloud-autojoin 문서에서 클라우드 제공자별 구성을 확인해 주세요. 이는 정적 IP나 DNS 주소와 결합하거나 제공자별로 여러 구성을 사용할 수도 있어요. 프록시 뒤에서 디스커버리를 사용하려면 Golang net/http 라이브러리 규칙에 따라 HTTP_PROXY, HTTPS_PROXY, NO_PROXY 환경 변수를 설정해야 해요.

Consul로 노드 자동 클러스터링

노마드 클러스터를 자동으로 부트스트랩하기 위해 노마드는 또 다른 HashiCorp 오픈소스 도구인 Consul을 활용할 수 있어요. 기존 Consul 클러스터를 대상으로 노마드를 부트스트랩하는 것이 가장 쉬워요. 각 호스트에 Consul 에이전트를 설치하고 구성하면 노마드 서버와 클라이언트가 서로의 존재를 알게 돼요. 추가 이점으로, Consul을 노마드에 통합하면 이후 노마드에서 실행되는 애플리케이션에 대해 서비스 및 헬스 체크 등록을 제공해요.

Consul은 인프라를 데이터센터(datacenter)로 모델링하며, 여러 Consul 데이터센터를 WAN으로 연결해 클라이언트가 다른 데이터센터의 노드를 발견할 수 있게 해요. 노마드 리전은 많은 데이터센터를 포함할 수 있으므로, 각 노마드 리전마다 Consul 클러스터를 실행하고 WAN으로 연결해야 해요. 단일 데이터센터 부트스트랩과 여러 Consul 클러스터를 WAN으로 연결하는 방법은 모두 Consul 튜토리얼을 참고해 주세요.

노마드가 시작되기 전에 Consul 에이전트가 호스트에 설치되어 있으면, 노마드 에이전트는 Consul에 등록하고 다른 노드를 발견해요.

서버의 경우 클러스터에 몇 개의 서버가 있을 것으로 예상하는지 클러스터에 알려야 해요. 노마드는 몇 개의 피어를 기대해야 하는지 알지 못하므로 초기 쿼럼(quorum)을 구성하려면 이 작업이 필요해요. 예를 들어 세 개의 노마드 서버로 리전을 구성하려면 다음 노마드 설정 파일을 사용해요:

# /etc/nomad.d/server.hcl

# data_dir tends to be environment specific.
data_dir = "/opt/nomad/data"

server {
  enabled          = true
  bootstrap_expect = 3
}

이 설정을 디스크에 저장한 뒤 실행해요:

$ nomad agent -config=/etc/nomad.d/server.hcl

노마드 클라이언트에도 비슷한 설정을 사용할 수 있어요:

# /etc/nomad.d/client.hcl

datacenter = "dc1"

# data_dir tends to be environment specific.
data_dir = "/opt/nomad/data"

client {
  enabled = true
}

에이전트는 비슷한 방식으로 시작해요:

$ sudo nomad agent -config=/etc/nomad.d/client.hcl

노마드 클라이언트는 항상 root(또는 sudo)로 실행해야 해요. 위 설정에는 클라이언트와 서버 사이의 IP나 DNS 주소가 전혀 없어요. 이는 노마드가 Consul의 존재를 감지하고 서비스 디스커버리를 활용해 클러스터를 구성했기 때문이에요.

Consul 자동 조인 내부 동작

이 섹션은 Consul과 노마드 통합의 내부를 아주 높은 수준으로 설명해요. 구현이 궁금한 사람만 읽기를 권장해요.

이전 섹션에서 설명했듯이 노마드는 여러 설정 파일을 병합하므로 -config를 두 번 이상 지정할 수 있어요:

$ nomad agent -config=base.hcl -config=server.hcl

명령줄에서 설정을 병합하는 것 외에도 노마드는 합리적인 기본값을 포함하는 자체 내부 설정("기본 설정")을 유지해요. 그 기본 설정 중 하나에는 Consul에 연결하고 통합하기 위한 합리적인 기본값을 지정하는 "consul" 블록이 포함돼 있어요. 본질적으로 이 설정 파일은 다음과 같아요:

# You do not need to add this to your configuration file. This is an example
# that is part of Nomad's internal default configuration for Consul integration.
consul {
  # The address to the Consul agent.
  address = "127.0.0.1:8500"

  # The service name to register the server and client with Consul.
  server_service_name = "nomad"
  client_service_name = "nomad-client"

  # Enables automatically registering the services.
  auto_advertise = true

  # Enabling the server and client to bootstrap using Consul.
  server_auto_join = true
  client_auto_join = true
}

전체 설정 옵션은 consul 스탠자 문서를 참고해 주세요.

클라이언트 노드 도입 토큰 사용

클라이언트 도입 토큰(client introduction tokens)을 사용해 어떤 클라이언트가 클러스터에 조인할 수 있는지 제한해요. 클라이언트 노드 도입 기능은 노마드 클러스터의 다중 인증(multi-factor authentication)과 같아요. mTLS를 대체하지는 않지만, 인증되지 않았거나 잘못 구성된 클라이언트가 노마드 클러스터에 조인하는 것을 막는 추가적인 보안 계층을 제공해요.

클라이언트 도입 토큰을 생성할 때 클러스터 접근을 더 보호하기 위해 다음 선택 파라미터를 지정할 수 있어요:

  • Node pool: 이 토큰을 가진 클라이언트가 조인할 수 있는 노드 풀. 이 토큰은 다른 노드 풀에서는 유효하지 않아요.
  • Node name: 토큰은 이 이름의 노드로 제한돼요. 다른 노드는 이 토큰으로 클러스터에 조인할 수 없어요.
  • TTL: 토큰 만료. 만료 이후에는 토큰이 유효하지 않아요.

클라이언트 도입 토큰을 사용하려면 먼저 ACL 시스템을 부트스트랩하고 CLI나 API에서 사용할 management 토큰을 저장해야 해요. 지침은 Bootstrap the ACL system 가이드를 참고해 주세요.

클라이언트 노드 도입 토큰을 사용하려면 다음 단계를 따라요:

  1. 노마드 서버의 server.client_introduction 블록을 구성해요. 이 예시는 strict 강제를 설정해요. 즉 서버가 유효한 토큰이 없는 클라이언트를 거부해요. 추가 강제 옵션은 server.client_introduction 블록 문서를 참고해 주세요.

    nomad.hcl

    data_dir = "/opt/nomad/"
    acl {
      enabled = true
    }
    server {
      enabled          = true
      bootstrap_expect = 1
      client_introduction {
        enforcement          = "strict" # Default = "warn"
        default_identity_ttl = "5m"     # Default = "5m"
        max_identity_ttl     = "30m"    # Default = "30m"
      }
    }
    tls {
      http = true
      rpc  = true
      ca_file   = "/opt/nomad/tls/nomad-agent-ca.pem"
      cert_file = "/opt/nomad/tls/global-server-nomad.pem"
      key_file  = "/opt/nomad/tls/global-server-nomad-key.pem"
    }
    

    노마드 서버가 이미 실행 중이라면 설정 변경을 적용하기 위해 서버 에이전트를 다시 시작해야 해요. 클라이언트 노드에는 추가 구성이 필요하지 않아요.

  2. 클라이언트 도입 토큰 생성을 허용하는 ACL 정책을 만들어요. 정책에는 node 범위에 write 권한이 포함되어야 해요. 이것이 클라이언트 도입 토큰에 필요한 최소 권한이에요.

    CLI — 클러스터용 node 정책을 만들어요. 파일을 client-introduction.hcl로 저장해요.

    node {
      policy = "write"
    }
    

    nomad acl policy apply 명령으로 client-introduction.hcl 파일에서 client-introduction이라는 정책을 만들어요.

    $ nomad acl policy apply client-introduction client-introduction.hcl
    

    API — 클러스터용 node 정책을 만들어요. 파일을 client-introduction.json으로 저장해요.

    {
      "Name": "client-introduction",
      "Rules": {
        "node": {
          "*": {
            "policy": "write"
          }
        }
      }
    }
    

    /v1/acl/policy/:policy_name API 엔드포인트로 클러스터에 정책을 만들어요. <MANAGEMENT_TOKEN>과 <NOMAD_IP> 자리표시자를 management 토큰 값과 노마드 IP 주소로 바꿔 주세요.

    $ curl \
        --request POST \
        --data @client-introduction.json \
        --header "X-Nomad-Token: <MANAGEMENT_TOKEN>" \
        https://<NOMAD_IP>:4646/v1/acl/policy/client-introduction
    
  3. 이전 단계에서 만든 client-introduction 정책을 사용하는 ACL 역할을 만들어요.

    CLI — nomad acl role create 명령으로 client-introduction 정책을 사용하는 client-introduction 역할을 만들어요.

    $ nomad acl role create --tls-skip-verify -name="client-introduction" \
       -policy="client-introduction"
    

    출력은 연결된 정책과 생성 시점의 Raft 인덱스를 포함한 ACL 역할을 설명해요.

    ID           = cf0b4a43-b00f-cc30-b656-b34d66151b04
    Name         = client-introduction
    Description  = <none>
    Policies     = client-introduction
    Create Index = 117
    Modify Index = 117
    

    API — /v1/acl/role API 엔드포인트로 client-introduction 역할을 만들어요. ACL 역할을 정의하고 client-introduction 정책을 역할에 연결하기 위해 client-introduction-role.json 파일을 만들어요.

    {
     "Name": "client-introduction",
     "Policies": [
       {
         "Name": "client-introduction"
       }
     ]
    }
    

    클러스터에 역할을 만들어요. <MANAGEMENT_TOKEN>과 <NOMAD_IP> 자리표시자를 management 토큰 값과 노마드 IP 주소로 바꿔 주세요.

    $ curl \
     --request POST \
     --header "X-Nomad-Token: <MANAGEMENT_TOKEN>" \
     --data @client-introduction-role.json \
     https://<NOMAD_IP>:4646/v1/acl/role
    

    응답은 연결된 정책과 생성 시점의 Raft 인덱스를 포함한 ACL 역할을 설명해요.

    {
      "CreateIndex": 117,
      "Description": "my example ACL Role",
      "ID": "cf0b4a43-b00f-cc30-b656-b34d66151b04",
      "ModifyIndex": 117,
      "Name": "client-introduction",
      "Policies": [
        {
          "Name": "client-introduction"
        }
      ]
    }
    
  4. JSON 웹 토큰(JWT)인 클라이언트 도입 토큰을 생성해요. 이 예시는 토큰을 staging이라는 노드 풀로 제한해요. 노드 풀 제한은 선택 사항이에요.

    CLI — nomad node intro create 명령으로 클라이언트 도입 토큰을 생성해요. 이 예시는 결과를 intro_token.jwt라는 파일에 써요.

    $ nomad node intro create -node-pool=staging  > intro_token.jwt
    

    intro-token.jwt 파일에는 JWT가 들어 있어요.

    "eyJhbG...ZDgy..."
    

    API — /v1/acl/identity/client-introduction-token API 엔드포인트로 클라이언트 도입 토큰을 생성해요. 이 예시는 토큰을 staging이라는 노드 풀로 제한해요. 노드 풀 제한은 선택 사항이에요. 노드 풀을 정의하려면 client-introduction-token.json 파일을 만들어요.

    {
    "NodePool": "staging"
    }
    

    클라이언트 도입 토큰을 만들어요. <MANAGEMENT_TOKEN>과 <NOMAD_IP> 자리표시자를 management 토큰 값과 노마드 IP 주소로 바꿔 주세요.

    $ curl \
       --request POST \
       --header "X-Nomad-Token: <MANAGEMENT_TOKEN>" \
       --data @client-introduction-token.json \
       https://<NOMAD_IP>:4646/v1/acl/identity/client-introduction-token
    

    응답은 JSON 웹 토큰이에요.

    {
     "JWT": "eyJhbG...ZDgy..."
    }
    
  5. 클라이언트 도입 토큰으로 노마드 클라이언트를 시작해요. 다음 옵션 중 하나로 노마드 클라이언트에 클라이언트 도입 토큰을 제공해요:

    • 토큰을 NOMAD_CLIENT_INTRO_TOKEN 환경 변수의 값으로 설정.
    • nomad agent 명령의 -client-intro-token 파라미터 사용.
    • <data_dir>/client_state_dir> 디렉터리(기본 클라이언트 상태 디렉터리)에 intro_token.jwt 파일 배치.

    이 예시는 -client-intro-token 파라미터로 전달된 클라이언트 도입 토큰으로 클라이언트를 시작해요.

    $ nomad agent -config /etc/nomad.d/nomad.hcl \
    -client-intro-token "eyJhbG...ZDgy..."
    
  6. 클라이언트 조인 실패를 모니터링해요. 클라이언트 등록이 실패하는 시점을 판별하는 방법은 다음과 같아요:

    • 서버 로그에서 [ERROR] nomad.client: node registration without introduction token 메시지를 확인.
    • nomad.client.introduction.enforcement 카운터를 모니터링. 이 카운터는 유효한 클라이언트 도입 토큰 없이 조인하려는 클라이언트가 있을 때 증가해요.

더 알아보기 (Learn more)