분산 잠금(Distributed Locking)과 Consul, etcd 활용법
1. 분산 잠금(Distributed Locking) 개요
분산 시스템에서는
여러 프로세스나 노드가 동시에 동일한 리소스(데이터, 파일 등)에 접근할 수 있습니다.
이로 인해 **경쟁 조건(Race Condition)**이나 데이터 불일치가 발생할 수 있으므로
**분산 잠금(Distributed Locking)**이 필수적입니다.
분산 잠금이란
- 여러 인스턴스 간에 리소스에 대한 접근을 **직렬화(Serialize)**하는 메커니즘을 의미합니다.
- 이를 통해 **동시성 제어(Concurrency Control)**와 **데이터 정합성(Data Consistency)**을 확보할 수 있습니다.
2. 분산 잠금의 핵심 요건
- Mutual Exclusion(상호 배제): 하나의 노드만 리소스에 접근할 수 있어야 함
- Deadlock Avoidance(교착 상태 방지): 잠금 획득 실패 시 시스템이 멈추지 않아야 함
- Fault Tolerance(장애 복원력): 노드 장애 발생 시 잠금 자동 해제
- Fairness(공정성): 모든 노드가 공평하게 잠금 획득 기회를 가져야 함
3. 대표적인 분산 잠금 방식
3.1 데이터베이스 기반 잠금
- 관계형 데이터베이스에 Lock 테이블을 생성하여 관리
- 단점: DB 부하 증가, 네트워크 지연에 취약
3.2 캐시 기반 잠금(Redis)
- Redis SETNX 명령어를 이용한 분산 잠금
- Expire(만료) 기능을 활용하여 장애 복구
- RedLock 알고리즘을 통해 안정성 강화
3.3 분산 키-밸류 저장소 기반 잠금(Consul, etcd, Zookeeper)
- 고가용성(HA) 클러스터 구성
- 리더 선출, 세션 기반 락 관리 제공
- 시스템 중단 없이 잠금 유지 가능
이 중 오늘은 특히 Consul과 etcd를 중점적으로 다루겠습니다.
4. Consul을 이용한 분산 잠금
4.1 Consul 소개
- HashiCorp에서 개발한 서비스 디스커버리 및 구성 관리 툴
- Key-Value 저장소 기능 제공
- 세션(Session) 및 체크(Check) 기반 분산 락 지원
4.2 Consul의 락 원리
- 노드가 Session을 생성한다.
- 특정 Key에 대해 Acquire(획득) 요청을 한다.
- Session이 유효할 때만 해당 Key에 대한 소유권이 유지된다.
- 세션 만료(노드 장애 등) 시 잠금은 자동 해제된다.
4.3 Consul 락 구현 방법
# 1. 세션 생성
curl --request PUT http://localhost:8500/v1/session/create -d '{"Name":"my-lock-session"}'
# 2. Key 획득 시도
curl --request PUT http://localhost:8500/v1/kv/lock/my-key?acquire=<session_id> -d 'lock_value'
# 3. Key 해제
curl --request PUT http://localhost:8500/v1/kv/lock/my-key?release=<session_id>
또는 Consul API나 Consul Template, HashiCorp SDK를 통해 락을 보다 고수준으로 관리할 수 있습니다.
4.4 Consul 락 사용 시 주의사항
- **TTL(Session Timeout)**을 적절히 설정하여 장애 시 잠금 해제를 보장해야 함
- Retry 로직을 추가하여 Lock 획득 실패 대비 필요
- 네트워크 파티션(Network Partition) 발생 시 복구 전략 수립 필요
5. etcd를 이용한 분산 잠금
5.1 etcd 소개
- CoreOS가 개발하고, 현재는 CNCF에서 관리
- 고가용성(HA), 강력한 일관성(Strong Consistency)을 갖춘 Key-Value 저장소
- Raft Consensus 알고리즘을 기반으로 리더 선출 및 데이터 복제 수행
- Kubernetes의 주요 컴포넌트로 활용됨
5.2 etcd의 락 원리
- Lease를 발급받는다.
- Lease를 바탕으로 Key를 생성하고 소유권을 주장한다.
- Lease 만료 시 자동으로 Key 삭제 → Lock 해제
- 이를 통해 Deadlock 및 Zombie Lock 방지
5.3 etcd 락 구현 방법
Python 예제(etcd3 라이브러리 사용):
import etcd3
client = etcd3.client(host='localhost', port=2379)
# Lease 생성 (10초 동안 유효)
lease = client.lease(10)
# Key를 Lease와 함께 생성 (Lock 획득)
success, _ = client.transaction(
compare=[
client.transactions.version('my-lock-key') == 0
],
success=[
client.transactions.put('my-lock-key', 'locked', lease)
],
failure=[]
)
if success:
print("Lock acquired")
else:
print("Lock not acquired")
# Lock 해제 (Key 삭제)
client.delete('my-lock-key')
5.4 etcd 락 사용 시 주의사항
- Lease 갱신(KeepAlive) 기능을 사용하여 장시간 락 유지 시 세션 유지 필요
- Lock 획득 실패 시 지수적 백오프(Exponential Backoff) 전략 적용 권장
- etcd 클러스터 상태(리더 선출, 네트워크 상태)에 따라 일시적 오류 발생 가능
6. Consul vs etcd 분산 잠금 비교
항목 | Consul | etcd |
기반 프로토콜 | Gossip, Raft | Raft |
잠금 관리 방식 | Session 기반 | Lease 기반 |
복구 시나리오 | 세션 만료로 자동 해제 | Lease 만료로 자동 해제 |
장애 복원력 | 강함 | 매우 강함 |
사용 난이도 | 쉬움 | 중간 |
Kubernetes 통합 | 별도 통합 필요 | 기본 통합(K8s Core 컴포넌트) |
운영 도구 | Consul UI, CLI, API | etcdctl, API |
확장성 | 높은 편 | 매우 높음 |
7. 실전 적용 팁
- 고가용성 구성: Consul/etcd 모두 3개 이상의 노드로 클러스터 구성
- Lock Timeout 설정: 네트워크 장애를 고려해 적절한 TTL/Lease 시간 설정
- Retry 및 백오프 구현: Lock 획득 실패 시 무한 루프 방지
- 모니터링 연동: Prometheus, Grafana 등을 통해 Lock 상태를 모니터링
- 네트워크 장애 대비: 클러스터 간 Heartbeat, Health Check 주기 조정
8. 주의해야 할 함정
- Split-Brain: 네트워크 분리로 두 개 이상의 노드가 동시에 Lock을 가진 것처럼 동작할 수 있음
- Zombie Lock: 프로세스 죽음에도 락이 해제되지 않는 상황
- 락 경합(High Contention): 대량의 Lock 요청이 집중될 때 지연 및 실패 발생
이를 방지하려면
정확한 TTL/Lease 설정,
Heartbeat 기반 Health Check,
Auto Failover 구현이 필수입니다.
9. 결론
분산 환경에서 안정적인 Lock 메커니즘은
데이터 정합성, 서비스 안정성, 장애 대응력을 좌우합니다.
- Consul은 간편한 API와 서비스 디스커버리 통합 장점
- etcd는 강력한 일관성과 Kubernetes 친화적 특성
환경과 요구사항에 맞추어 적절한 솔루션을 선택하고,
TTL/Lease를 통해 장애 복구까지 고려한
견고한 분산 잠금 체계를 구축해야 합니다.
끊임없이 복잡해지는 분산 시스템에서
**"Locking 전략"**은 더 이상 부가 기능이 아니라,
필수 생존 도구입니다.