서비스 디스커버리 구성하기
서비스 디스커버리 구성하기 (Configure service discovery)
서비스 디스커버리에는 두 가지 옵션이 있어요:
- Consul 서비스 디스커버리. Consul 클러스터에 대한 접근이 필요하며, 서비스 디스커버리의 기본 옵션이에요.
- Nomad 서비스 디스커버리. 추가 인프라가 필요 없어요.
Nomad와 Consul 서비스 디스커버리 옵션의 비교는 Nomad의 서비스 디스커버리 문서를 참조하세요.
출처: 문서
본문
워크플로 (Workflow)
다음 단계에 따라 Nomad 작업 명세(jobspec)에서 서비스 디스커버리를 구성해요:
service블록의provider매개변수에 서비스 디스커버리 제공자를 선언해요. Consul이 기본 제공자라는 점에 유의하세요.- 선택적으로
service블록의check블록에서 헬스 체크를 구성해요. - 다른 서비스에 대한 접근을 구성해요.
이 가이드는 Countdash 애플리케이션을 배포하는 예제 jobspec의 섹션을 사용해요. 애플리케이션에는 백엔드 countdash-api 서비스와 통신하는 프론트엔드 countdash-web 서비스가 있어요.
서비스 제공자 선언하기 (Declare the service provider)
Consul은 기본 서비스 제공자이므로 service 블록의 provider 매개변수를 구성할 필요가 없어요. 이 jobspec 예제는 jobspec 구조를 설명하기 위해 provider = "consul"을 명시적으로 선언해요.
작업이 Docker 태스크 드라이버를 사용한다면, jobspec의 network 블록에서 DNS 주소용 호스트의 Docker 브리지 IP를 구성해야 해요. 이 구성은 Nomad 할당이 Consul DNS 서비스 이름을 해석할 수 있게 해줘요. 환경에 따라 달라질 수 있으므로 Consul 시스템 관리자에게 Docker 브리지 IP 주소를 확인하세요.
Docker용 DNS 포워딩 구성은 Consul 설치 과정의 일부예요. 자세한 설명은 Consul 문서의 Docker용 systemd-resolved 구성을 참조하세요.
countdash.nomad.hcl
job "countdash" {
group "countdash-api" {
service {
provider = "consul"
}
network {
...
dns {
servers = ["172.17.0.1"]
}
}
}
}
Consul이 기본 서비스 제공자이므로 Nomad 서비스 디스커버리를 사용하려면 service 블록의 provider 매개변수를 구성해야 해요.
countdash.nomad.hcl
job "countdash" {
group "countdash-api" {
service {
provider = "nomad"
}
}
}
헬스 체크 구성하기 (Configure health checks)
선택적으로 check 블록으로 헬스 체크를 정의해 서비스 카탈로그가 정상 인스턴스만 반환하도록 해요. 헬스 체크 구성은 Consul 서비스 디스커버리나 Nomad 서비스 디스커버리 중 무엇을 사용하든 동일해요.
countdash.nomad.hcl
job "countdash" {
group "countdash-api" {
service {
name = "countdash-api"
...
check {
name = "Countdash API ready"
type = "http"
path = "/actuator/health"
interval = "5s"
timeout = "5s"
check_restart {
limit = 0
}
}
}
}
}
countdash.nomad.hcl
job "countdash" {
group "countdash-web" {
service {
name = "countdash-web"
...
check {
name = "Countdash web ready"
type = "http"
path = "/"
interval = "5s"
timeout = "5s"
}
}
}
}
countdash.nomad.hcl
job "countdash" {
group "countdash-api" {
service {
name = "countdash-api"
...
check {
name = "Countdash API ready"
type = "http"
path = "/actuator/health"
interval = "5s"
timeout = "5s"
check_restart {
limit = 0
}
}
}
}
}
countdash.nomad.hcl
job "countdash" {
group "countdash-web" {
service {
name = "countdash-web"
...
check {
name = "Countdash web ready"
type = "http"
path = "/"
interval = "5s"
timeout = "5s"
}
}
}
}
다른 서비스에 대한 접근 구성하기 (Configure access to other services)
서비스 디스커버리에 Consul을 사용하고 서비스 메시 기능을 활성화하지 않았다면, Consul DNS를 사용해 Consul 카탈로그에서 서비스와 노드를 발견해요. 자세한 내용은 Consul 문서의 표준 조회 가이드를 참조하세요. 업스트림 서비스 해석에 Nomad의 클라이언트 노드 주소를 사용하지 마세요.
Consul 서비스 카탈로그에서 서비스를 찾으려면 다음 형식을 사용해요:
<service_name>.service.<consul_datacenter>.<consul_domain>
Consul의 기본 데이터센터는 dc1이고 기본 도메인은 consul이에요. 이 값들은 구성 가능하므로 Consul 관리자에게 데이터센터와 도메인을 확인하세요.
countdash-web 서비스는 백엔드 countdash-api 서비스와 통신해야 해요. COUNTING_SERVICE_URL 변수는 서비스의 Consul 서비스 카탈로그 값으로 채워져요.
countdash.nomad.hcl
job "countdash" {
group "countdash-web" {
task "countdash-web" {
...
env {
COUNTING_SERVICE_URL = "http://countdash-api.service.dc1.consul:${var.countdash-api-port}"
PORT="${var.countdash-web-port}"
}
}
}
Consul Template service 함수와 함께 template 블록을 사용해 Consul 서비스 디스커버리 카탈로그에서 서비스를 찾을 수도 있어요. 구문과 예제는 Consul Template service 함수 문서를 참조하세요. 템플릿을 구성 파일로 사용하거나 그 내용을 환경 변수로 로드해 애플리케이션의 연결 정보를 구성할 수 있어요. Nomad는 Consul Template을 포함하므로 별도로 설치할 필요가 없어요.
Consul Template nomadService 함수와 함께 template 블록을 사용해 Nomad 서비스 디스커버리 카탈로그에서 서비스를 찾아요. 템플릿을 구성 파일로 사용하거나 그 내용을 환경 변수로 로드해 애플리케이션의 연결 정보를 구성할 수 있어요. Nomad는 Consul Template을 포함하므로 별도로 설치할 필요가 없어요.
countdash-web 서비스는 백엔드 countdash-api 서비스와 통신해야 해요. 이 예제에서 템플릿 블록 코드는 countdash-api를 검색하고 결과를 COUNTING_SERVICE_URL 변수에 할당해요.
countdash.nomad.hcl
job "countdash" {
group "countdash-web" {
task "countdash-web" {
template {
data = <<EOH
BIND_ADDRESS = ":${var.countdash-api-port}"
{{ range nomadService "countdash-api" }}
COUNTING_SERVICE_URL = "http://{{ .Address }}:{{ .Port }}"
{{ end }}
EOH
destination = "local/env.txt"
env = true
}
}
}
}
서비스 카탈로그에서 서비스 찾기 (Find services in the service catalog)
consul catalog services 명령을 사용해 Consul 카탈로그의 서비스를 나열해요.
$ consul catalog services
consul
countdash-api
countdash-web
nomad
nomad-client
Consul의 CLI 사용에 대한 자세한 내용은 Consul 문서를 참조하세요.
v1/catalog/services API 엔드포인트를 사용해 Consul 카탈로그의 서비스를 나열해요.
다음 자리 표시자를 바꿔요:
<consul-http-address>:<port>: Consul의 공개 IP 주소와 포트. 예:http://13.58.60.124<consul-management-token>: Consul 카탈로그를 조회할 적절한 권한이 있는 Consul 토큰 값. 토큰 세부 사항은 Consul의 HTTP API 구조 가이드를 참조하세요.
curl --location 'http://<consul-http-address>:<port>/v1/catalog/services' \
--header 'X-Consul-Token: <consul-management-token>'
응답은 다음과 유사해요:
{
"consul": [],
"countdash-api": [],
"countdash-web": [],
"nomad": [],
"nomad-client": []
}
nomad service list 명령을 사용해 서비스 목록을 가져와요.
$ nomad service list
Service Name Tags
countdash-api []
countdash-web []
v1/services API 엔드포인트를 사용해 서비스를 나열해요.
다음 자리 표시자를 바꿔요:
<nomad-http-address>:<port>: Nomad의 공개 IP 주소와 포트. 예:http://13.58.60.124<nomad-management-token>: Nomad 서비스를 조회할 적절한 권한이 있는 Nomad 토큰 값.
curl --location 'http://<nomad-http-address>:<port>/v1/services' \
--header 'X-Consul-Token: <nomad-management-token>'
응답은 다음과 유사해요:
[
{
"Namespace": "default",
"Services": [
{
"ServiceName": "countdash-api"
},
{
"ServiceName": "countdash-web"
}
]
}
]
배포된 서비스의 공개 IP 주소 찾기 (Find your deployed service's public IP address)
Consul v1/catalog/service/:service_name API 엔드포인트를 사용해 실행 중인 서비스의 공개 IP 주소를 찾아요. 이 예제는 countdash-web 서비스의 공개 IP 주소를 찾아요.
다음 자리 표시자를 바꿔요:
<consul-http-address>:<port>: Consul의 공개 IP 주소와 포트. 예:http://13.58.60.124<consul-management-token>: Consul 카탈로그를 조회할 적절한 권한이 있는 Consul 토큰 값. 토큰 세부 사항은 Consul의 HTTP API 구조 가이드를 참조하세요.
curl --location 'http://<consul-http-address>:<port>/v1/catalog/service/countdash-web?passing' \
--header 'X-Consul-Token: <consul-management-token>' | \
jq -r '.[] | "\(.ServiceAddress):\(.ServicePort)"'
출력은 이 예제에서 AWS EC2 공개 IP와 포트인 공개 IPv4 주소를 반환해요.
ec2-3-145-209-63.us-east-2.compute.amazonaws.com:9002
nomad service info 명령을 사용해 특정 서비스의 세부 정보를 가져와요. 이 예제는 countdash-web 서비스에 대한 정보를 검색하고 출력을 JSON으로 포맷해요. Address 값이 공개 IPv4 주소라는 점에 유의하세요.
$ nomad service info -json countdash-web
[
{
"Address": "ec2-3-21-113-32.us-east-2.compute.amazonaws.com",
"AllocID": "b1d44744-1592-4241-e904-6a4654a40898",
"CreateIndex": 849,
"Datacenter": "dc1",
"ID": "_nomad-task-b1d44744-1592-4241-e904-6a4654a40898-group-countdash-web-countdash-web-countdash-web",
"JobID": "countdash",
"ModifyIndex": 849,
"Namespace": "default",
"NodeID": "b348d8ac-1020-1b9a-fbb8-0d4a9dbeb5ca",
"Port": 9002,
"ServiceName": "countdash-web",
"Tags": []
}
]
Nomad /v1/service/:service_name API 엔드포인트를 사용해 특정 서비스의 세부 정보를 가져와요. 이 예제는 countdash-web 서비스에 대한 정보를 검색해요.
curl --location 'http://<nomad-http-address>:<port>/v1/service/countdash-web' \
--header 'X-Consul-Token: <nomad-management-token>'
응답의 Address 값이 공개 IP 주소예요.
[
{
"Address": "ec2-3-21-113-32.us-east-2.compute.amazonaws.com",
"AllocID": "b1d44744-1592-4241-e904-6a4654a40898",
"CreateIndex": 849,
"Datacenter": "dc1",
"ID": "_nomad-task-b1d44744-1592-4241-e904-6a4654a40898-group-countdash-web-countdash-web-countdash-web",
"JobID": "countdash",
"ModifyIndex": 849,
"Namespace": "default",
"NodeID": "b348d8ac-1020-1b9a-fbb8-0d4a9dbeb5ca",
"Port": 9002,
"ServiceName": "excountdash-web",
"Tags": []
}
]
- Nomad UI에서 Jobs를 클릭해요.
- countdash 작업 이름을 클릭해요. UI가 Overview 탭에 작업 세부 정보를 표시해요.
- Services 탭을 클릭해 작업이 배포한 서비스 목록을 확인해요.
- countdash-web을 클릭해 인스턴스의 공개 IP 주소를 확인해요.
서비스 태그 사용하기 (Use service tags)
service 블록을 같은 이름으로 다른 포트에 여러 번 지정할 수 있어요. 서비스 이름을 조회하면 서비스 디스커버리 카탈로그가 서비스의 모든 인스턴스를 반환해요. 결과를 제한하려면 서비스에 tags를 할당해 그룹화할 수 있어요.
이 예제는 다른 프로토콜을 위해 애플리케이션을 두 포트에 노출해요.
job "..." {
# ...
group "..." {
network {
port "http" {}
port "grpc" {}
}
service {
name = "my-app"
port = "http"
tags = ["http"]
# ...
}
service {
name = "my-app"
port = "grpc"
tags = ["grpc"]
# ...
}
}
}
다른 태그를 할당함으로써 http.my-app과 grpc.my-app 서비스 조회로 각 프로토콜의 포트에 접근할 수 있어요.
특정 태그를 가진 서비스를 조회하려면 Consul DNS 조회 형식에 태그를 앞에 붙여요.
<tag>.<service_name>.service.<consul_datacenter>.<consul_domain>
예를 들어 grpc.my-app.service.dc1.consul이에요.
Consul DNS 주소 형식에 대한 자세한 내용은 Consul 문서의 표준 조회를 참조하세요.
서비스 이름에 태그를 앞에 붙여요. 이 예제는 grpc 서비스 태그를 사용해요.
example.nomad.hcl
job "example" {
group "example-web" {
task "example-web" {
template {
data = <<EOH
{{ range nomadService "grpc.my-app" }}
URL = "http://{{ .Address }}:{{ .Port }}"
{{ end }}
EOH
destination = "local/env.txt"
env = true
}
}
}
}
카나리 배포 태그 (Canary deployment tags)
작업 명세 업그레이드에 카나리 또는 블루/그린을 사용할 때, canary_tags 매개변수 구성으로 카나리 할당에 다른 태그 집합을 지정할 수 있어요.
배포 중 Nomad는 canary_tags에 설정된 태그로 새 할당을 등록하는 반면, 비카나리는 tags의 값을 사용해요. 다른 태그 집합을 가지면 카나리를 미리 보기 위한 별도의 로드 밸런싱 라우팅 규칙을 만들 수 있어요. 자세한 내용은 "로드 밸런서 배포 고려 사항" 가이드를 참조하세요.
Nomad는 tags 또는 canary_tags 중 하나로 서비스를 등록하지만, 값을 공유하려면 두 필드 모두에 설정해야 해요.
Countdash 예제 작업 명세 (Countdash example job specs)
이 예제 Countdash 애플리케이션 jobspec은 AWS EC2 인스턴스(Ubuntu 22.04, AMD64 아키텍처)에서 실행되는 Nomad 클러스터에 countdash라는 작업을 배포해요. 작업은 두 서비스 countdash-api와 countdash-web을 배포하며, 구성된 서비스 디스커버리 제공자에 따라 Consul 서비스 카탈로그나 Nomad 서비스 카탈로그에서 사용할 수 있어요.
프론트엔드와 백엔드 서비스 코드는 에서 확인할 수 있어요.
웹 애플리케이션은 포트 9002에서 실행되므로 AWS 보안 그룹에서 해당 포트를 열어야 해요.
AWS가 아닌 인프라에서 작업을 실행하려면 각 그룹의 service.address 매개변수 값을 attr.unique.network.ip-address 또는 이와 유사한 값으로 업데이트해야 해요. 자세한 내용은 service 블록의 address 매개변수 참조를 참조하세요.
countdash.nomad.hcl
variable "countdash-api-port" {
description = "Countdash API Port"
default = 9001
}
variable "countdash-web-port" {
description = "Countdash web port"
default = 9002
}
job "countdash" {
group "countdash-api" {
count = 1
network {
port "countdash-api" {
static = var.countdash-api-port
}
dns {
servers = ["172.17.0.1"]
}
}
service {
name = "countdash-api"
provider = "consul"
port = "countdash-api"
address = attr.unique.platform.aws.local-ipv4
check {
name = "Countdash API ready"
type = "http"
path = "/actuator/health"
interval = "5s"
timeout = "5s"
check_restart {
limit = 0
}
}
}
task "countdash-api" {
driver = "docker"
meta {
service = "countdash-api"
}
config {
image = "hashicorpdev/counter-api:v3"
ports = ["countdash-api"]
mount {
type = "bind"
source = "local/application.properties"
target = "/application.properties"
}
}
template {
data = "server.port=${var.countdash-api-port}"
destination = "local/application.properties"
}
resources {
memory = 500
}
}
}
group "countdash-web" {
count = 1
network {
port "countdash-web" {
static = var.countdash-web-port
}
dns {
servers = ["172.17.0.1"]
}
}
service {
name = "countdash-web"
provider = "consul"
port = "countdash-web"
address = attr.unique.platform.aws.public-hostname
check {
name = "Countdash web ready"
type = "http"
path = "/"
interval = "5s"
timeout = "5s"
}
}
task "countdash-web" {
driver = "docker"
meta {
service = "countdash-web"
}
env {
COUNTING_SERVICE_URL = "http://countdash-api.service.dc1.consul:${var.countdash-api-port}"
PORT="${var.countdash-web-port}"
}
config {
image = "hashicorpdev/counter-dashboard:v3"
auth_soft_fail = true
ports = ["countdash-web"]
}
}
}
}
countdash.nomad.hcl
variable "countdash-api-port" {
description = "Countdash API Port"
default = 9001
}
variable "countdash-web-port" {
description = "Countdash web port"
default = 9002
}
job "countdash" {
group "countdash-api" {
count = 1
network {
port "countdash-api" {
static = var.countdash-api-port
}
}
service {
name = "countdash-api"
provider = "nomad"
port = "countdash-api"
address = attr.unique.platform.aws.local-ipv4
check {
name = "Countdash API ready"
type = "http"
path = "/actuator/health"
interval = "5s"
timeout = "5s"
check_restart {
limit = 0
}
}
}
task "countdash-api" {
driver = "docker"
meta {
service = "countdash-api"
}
config {
image = "hashicorpdev/counter-api:v3"
ports = ["countdash-api"]
mount {
type = "bind"
source = "local/application.properties"
target = "/application.properties"
}
}
template {
data = "server.port=${var.countdash-api-port}"
destination = "local/application.properties"
}
resources {
memory = 500
}
}
}
group "countdash-web" {
count = 1
network {
port "countdash-web" {
static = var.countdash-web-port
}
}
service {
name = "countdash-web"
provider = "nomad"
port = "countdash-web"
address = attr.unique.platform.aws.public-hostname
check {
name = "Countdash web ready"
type = "http"
path = "/"
interval = "5s"
timeout = "5s"
}
}
task "countdash-web" {
driver = "docker"
meta {
service = "countdash-web"
}
env {
PORT="${var.countdash-web-port}"
}
config {
image = "hashicorpdev/counter-dashboard:v3"
auth_soft_fail = true
ports = ["countdash-web"]
}
template {
data = <<EOH
BIND_ADDRESS = ":${var.countdash-api-port}"
{{ range nomadService "countdash-api" }}
COUNTING_SERVICE_URL = "http://{{ .Address }}:{{ .Port }}"
{{ end }}
EOH
destination = "local/env.txt"
env = true
}
}
}
}