애플리케이션 리더 선출

애플리케이션 리더 선출 (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 값이 표시되지 않습니다. 다른 클라이언트는 이를 잠금 요청을 조정하는 방법으로 사용할 수 있습니다.

더 알아보기 (Learn more)