애플리케이션 리더 선출
애플리케이션 리더 선출 (Application leader election)
이 문서는 Consul의 세션(Session) 메커니즘과 키/값 저장소(KV store)를 이용해 서비스 인스턴스에 대한 클라이언트 측 리더 선출을 구축하는 과정을 설명해 드릴게요. 분산 잠금(distributed lock)으로 리더를 정하고 싶을 때 이 방법을 그대로 따라 하시면 돼요.
출처: 문서
본문
이 항목은 Consul의 세션 메커니즘 [/consul/docs/automate/session]과 분산 잠금 구축에 사용되는 Consul 키/값 저장소 [/consul/docs/automate/kv]를 사용하여 서비스 인스턴스에 대한 클라이언트 측 리더 선출을 구축하는 과정을 설명합니다.
이 항목은 Consul 자체의 리더 선출과는 관련이 없습니다. Consul 내부에서 사용되는 Raft 리더 선출에 대한 자세한 내용은 합의 프로토콜(consensus protocol) [/consul/docs/concept/consensus] 문서를 참조하세요.
배경 (Background)
일부 분산 애플리케이션(예: HDFS 또는 ActiveMQ)은 애플리케이션 데이터가 최신 상태로 안정적으로 유지되도록 하나의 인스턴스를 리더로 설정해야 합니다.
Consul의 세션 [/consul/docs/automate/session]과 감시(watch) [/consul/docs/automate/watch] 지원을 사용하면 클라이언트가 KV 데이터 저장소의 키에 잠금을 걸어 상호 배제(mutual exclusion)를 보장하고 장애를 우아하게 처리하는 클라이언트 측 리더 선출 프로세스를 구축할 수 있습니다.
참여하는 모든 서비스 인스턴스는 키 형식을 서로 맞춰야 합니다. 다음 패턴을 권장합니다:
service/<service name>/leader
요구 사항 (Requirements)
- 실행 중인 Consul 서버
- 잠금을 획득하고 리더에 대한 정보를 저장할 Consul KV 데이터 저장소의 경로. 이 페이지의 지침에서는 다음 키를 사용합니다:
service/leader. - ACL이 활성화된 경우, 다음 권한을 가진 토큰:
- 서비스 세션 이름에 대한 [/consul/docs/automate/application-leader-election#session-write]
session:write권한 - 키에 대한 [/consul/docs/automate/application-leader-election#key-write]
key:write권한 curl명령
CONSUL_HTTP_TOKEN 환경 변수를 사용하여 토큰을 노출하세요.
클라이언트 측 리더 선출 절차 (Client-side leader election procedure)
클라이언트 측 리더 선출 프로세스를 구축하는 워크플로는 다음 단계로 진행됩니다:
잠금을 획득하려는 각 클라이언트에 대해:
- 클라이언트 노드에 연결된 세션을 생성합니다 [/consul/docs/automate/application-leader-election#create-a-new-session].
acquire파라미터를 사용하여 KV 저장소의 지정된 키 [/consul/docs/automate/application-leader-election#acquire-the-lock]에 잠금을 획득합니다.- KV 키 [/consul/docs/automate/application-leader-election#watch-the-kv-key-for-locks]를 감시하여 잠금이 해제되었는지 확인합니다. 잠금이 없으면 잠금을 획득하려 시도합니다.
잠금을 획득한 클라이언트에 대해:
- 주기적으로 세션을 갱신 [/consul/docs/automate/application-leader-election#renew-a-session]하여 만료를 방지합니다.
- 선택적으로 잠금을 해제 [/consul/docs/automate/application-leader-election#release-a-lock]합니다.
다른 서비스에 대해:
- KV 키 [/consul/docs/automate/application-leader-election#watch-the-kv-key-for-locks]를 감시하여 잠금을 보유한 프로세스가 최소한 하나 있는지 확인합니다.
- KV 경로 아래에 기록된 값을 사용하여 리더를 식별하고 구성(configuration)을 그에 맞게 업데이트합니다.
새 세션 만들기 (Create a new session)
세션에 대한 구성을 만듭니다. 최소한의 실행 가능한 구성에서는 세션 이름을 지정해야 합니다. 다음 예시는 이 구성을 보여줍니다.
{
"Name": "session_name"
}
/session Consul HTTP API [/consul/api-docs/session] 엔드포인트를 사용하여 세션을 생성합니다. 다음 예시에서 노드의 hostname이 세션 이름입니다.
$ curl --silent \
--header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
--data '{"Name": "'`hostname`'"}' \
--request PUT \
http://127.0.0.1:8500/v1/session/create | jq
이 명령은 새로 생성된 세션의 ID를 포함하는 JSON 객체를 반환합니다.
{
"ID": "d21d60ad-c2d2-b32a-7432-ca4048a4a7d6"
}
세션 확인 (Verify session)
/v1/session/list 엔드포인트를 사용하여 기존 세션을 검색합니다.
$ curl --silent \
--header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
--request GET \
http://127.0.0.1:8500/v1/session/list | jq
이 명령은 시스템에서 사용 가능한 모든 세션을 포함하는 JSON 배열을 반환합니다.
[
{
"ID": "d21d60ad-c2d2-b32a-7432-ca4048a4a7d6",
"Name": "hashicups-db-0",
"Node": "hashicups-db-0",
"LockDelay": 15000000000,
"Behavior": "release",
"TTL": "",
"NodeChecks": [
"serfHealth"
],
"ServiceChecks": null,
"CreateIndex": 11956,
"ModifyIndex": 11956
}
]
출력에서 세션이 hashicups-db-0 노드(API 요청을 보낸 클라이언트 에이전트)와 연결되어 있음을 확인할 수 있습니다.
Name을 제외한 모든 파라미터는 기본값으로 설정됩니다. 세션은 TTL 값 없이 생성되며, 이는 세션이 만료되지 않아 명시적으로 삭제해야 한다는 뜻입니다.
필요에 따라 TTL이나 ServiceChecks 같은 더 많은 파라미터를 지정하여 세션을 만들 수도 있습니다:
- [/consul/docs/automate/application-leader-election#ttl]
TTL- 제공된 경우, TTL이 만료되기 전에 갱신하지 않으면 세션이 무효화되고 삭제됩니다. - [/consul/docs/automate/application-leader-election#servicechecks]
ServiceChecks- 모니터링할 서비스 체크 목록을 지정합니다. 체크가 critical 상태를 반환하면 세션이 무효화됩니다.
이러한 추가 파라미터를 설정하면 마지막 갱신 이후 지정된 시간이 지나면 잠금을 자동으로 해제하거나, 잠금을 보유한 서비스가 실패할 때 잠금을 자동으로 해제하는 클라이언트 측 리더 선출 워크플로를 만들 수 있습니다.
사용 가능한 파라미터의 전체 목록은 /session/create 엔드포인트 문서 [/consul/api-docs/session#create-session]를 참조하세요.
잠금 획득 (Acquire the lock)
잠금 요청에 연결할 데이터 객체를 만듭니다.
요청의 데이터는 로컬 인스턴스를 나타내는 JSON 객체여야 합니다. 이 값은 Consul에게는 불투명(opaque)하지만, 클라이언트가 애플리케이션과 통신하는 데 필요한 정보를 포함해야 합니다. 예를 들어 노드 이름과 애플리케이션 포트를 포함하는 JSON 객체일 수 있습니다.
{
"Node": "node-name",
"Port": "8080"
}
API
?acquire=<session> 쿼리 파라미터와 함께 KV 항목 [/consul/api-docs/kv]에 PUT 메서드를 사용하여 주어진 키에 대한 잠금을 획득합니다.
$ curl --silent \
--header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
--data '{"Node": "'`hostname`'"}' \
--request PUT \
http://localhost:8500/v1/kv/service/leader?acquire=d21d60ad-c2d2-b32a-7432-ca4048a4a7d6 | jq
이 요청은 true 또는 false를 반환합니다. true이면 잠금이 획득되었고 로컬 서비스 인스턴스가 이제 리더입니다. false이면 다른 노드가 잠금을 획득했습니다.
CLI
$ consul kv put -acquire -session=d21d60ad-c2d2-b32a-7432-ca4048a4a7d6 /service/leader '{"Node": "'`hostname`'"}'
성공하면 명령은 종료 코드 0으로 종료되고 다음 메시지를 출력합니다.
Success! Lock acquired on: service/leader
잠금이 이미 다른 노드에 의해 획득된 경우 명령은 종료 코드 1로 종료되고 다음 메시지를 출력합니다.
Error! Did not acquire lock
이 예시에서는 노드의 hostname을 키 데이터로 사용했습니다. 이 데이터는 다른 서비스가 구성 파일을 만드는 데 사용할 수 있습니다.
이 잠금 시스템에는 클라이언트가 작업을 수행하기 전에 잠금을 획득하도록 요구하는 강제 메커니즘이 없다는 점에 유의하세요. 클라이언트는 해당 잠금을 소유하지 않고도 키를 읽고, 쓰고, 삭제할 수 있습니다.
KV 키 잠금 감시 (Watch the KV key for locks)
기존 잠금은 클라이언트 측 리더 선출에 참여하는 모든 노드뿐만 아니라 리더의 정체를 알아야 하는 다른 노드들도 모니터링해야 합니다.
- 잠금 보유자는 운영자가 세션을 무효화할 수 있으므로 잠금을 모니터링해야 합니다.
- 잠금을 획득하려는 다른 서비스는 잠금이 해제되었는지 확인하기 위해 모니터링한 후 잠금을 획득하려 시도해야 합니다.
- 다른 노드는 키의 값이 변경되었는지 확인하고 구성(configuration)을 그에 맞게 업데이트하기 위해 잠금을 모니터링해야 합니다.
API
블로킹 쿼리(blocking query)가 활성화된 KV 항목 [/consul/api-docs/kv]에 GET 메서드를 사용하여 잠금을 모니터링합니다.
먼저 현재 값의 최신 인덱스를 확인합니다.
$ curl --silent \
--header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
--request GET \
http://127.0.0.1:8500/v1/kv/service/leader?index=1 | jq
이 명령은 객체의 ModifyIndex를 포함한 키 데이터를 출력합니다.
[
{
"LockIndex": 0,
"Key": "service/leader",
"Flags": 0,
"Value": "eyJOb2...wIn0=",
"Session": "d21d60ad-c2d2-b32a-7432-ca4048a4a7d6",
"CreateIndex": 12399,
"ModifyIndex": 13061
}
]
ModifyIndex 값을 사용하여 잠금 [/consul/api-docs/features/blocking]에 대한 블로킹 쿼리를 실행합니다.
$ curl --silent \
--header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
--request GET \
http://127.0.0.1:8500/v1/kv/service/leader?index=13061 | jq
이 명령은 KV 경로에 변경이 발생할 때까지 멈춰 있다가(hang) 변경 후 경로 데이터를 콘솔에 출력합니다.
[
{
"LockIndex": 0,
"Key": "service/leader",
"Flags": 0,
"Value": "eyJOb2...xIn0=",
"Session": "d21d60ad-c2d2-b32a-7432-ca4048a4a7d6",
"CreateIndex": 12399,
"ModifyIndex": 13329
}
]
자동화 목적으로, 변경이 반환될 때마다 명령을 트리거하도록 블로킹 쿼리 메커니즘에 로직을 추가할 수 있습니다. 더 나은 접근 방식은 CLI 명령 consul watch를 사용하는 것입니다.
CLI
consul watch [/consul/commands/watch] 명령을 사용하여 잠금을 모니터링합니다.
$ consul watch -type=key -key=service/leader cat | jq
이 예시에서는 명령 출력이 셸에 인쇄됩니다. 그러나 명령에 더 복잡한 옵션을 전달하거나, 잠금 데이터 변경에 반응하는 더 복잡한 로직을 포함하는 스크립트를 전달할 수도 있습니다.
이 명령의 예시 출력은 다음과 같습니다:
{
"Key": "service/leader",
"CreateIndex": 12399,
"ModifyIndex": 13061,
"LockIndex": 0,
"Flags": 0,
"Value": "eyJOb2...wIn0=",
"Session": "d21d60ad-c2d2-b32a-7432-ca4048a4a7d6"
}
consul watch 명령은 KV 경로의 변경을 폴링하고, 변경이 있을 때 출력에 지정된 명령을 실행합니다.
출력에서 잠금이 획득되면 Session 파라미터에 잠금을 보유한 세션의 ID가 포함된다는 점을 확인하세요.
세션 갱신 (Renew a session)
TTL 값이 설정된 세션을 생성한 경우 TTL이 만료되기 전에 세션을 갱신해야 합니다.
/v1/session/renew [/consul/api-docs/session#renew-session] 엔드포인트를 사용하여 기존 세션을 갱신합니다.
$ curl --silent \
--header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
--request PUT \
http://127.0.0.1:8500/v1/session/renew/f027470f-2759-6b53-542d-066ae4185e67 | jq
명령이 성공하면 JSON 형식의 세션 정보가 출력됩니다.
[
{
"ID": "f027470f-2759-6b53-542d-066ae4185e67",
"Name": "test",
"Node": "consul-server-0",
"LockDelay": 15000000000,
"Behavior": "release",
"TTL": "30s",
"NodeChecks": [
"serfHealth"
],
"ServiceChecks": null,
"CreateIndex": 11842,
"ModifyIndex": 11842
}
]
잠금 해제 (Release a lock)
TTL 값이 설정되지 않은 세션과 연결된 잠금은 해당 서비스가 실패하더라도 해제되지 않을 수 있습니다.
이런 경우 잠금을 수동으로 해제해야 합니다.
API
$ curl --silent \
--header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
--data '{"Node": "'`hostname`'"}' \
--request PUT \
http://localhost:8500/v1/kv/service/leader?release=d21d60ad-c2d2-b32a-7432-ca4048a4a7d6 | jq
이 명령은 성공 시 true를 출력합니다.
CLI
$ consul kv put -release -session=d21d60ad-c2d2-b32a-7432-ca4048a4a7d6 service/leader '{"Node": "'`hostname`'"}'
성공하면 명령은 성공 메시지를 출력합니다.
Success! Lock released on: service/leader
잠금이 해제된 후에는 키 데이터의 결과에 Session 값이 표시되지 않습니다. 다른 클라이언트는 이를 잠금 요청을 조정하는 방법으로 사용할 수 있습니다.