본문 바로가기
아키텍쳐

서비스의 고가용성(HA)를 지키기위해 적용 할 수 있는 방법 들

by hayz 2024. 6. 30.

서비스를 안정적이게 운영하기 위해서는 서비스가 중간에 다운 되거나, 응답이 없거나, 장애가 발생하는 상황이 생기지 않아야 한다. 서비스가 이러한 응답을 제대로 받는지에 대한 측정 지표를 가용성(availability)이라고 하는데, 안정적인 서비스는 고가용성(HA; high availability) 을 지킨다 라고 표현한다.  이번 글에서는 서비스의 고가용성 즉 HA 를 만족시키기 위한 여러가지 방법들에 대해서 알아본다. 

 

 

1. 쿠버네티스(kubernetes) 사용

 

Production-Grade Container Orchestration

Production-Grade Container Orchestration

kubernetes.io

 

가장 쉽고 대표적으로 고가용성을 만족하기 위한 방법으로는 쿠버네티스를 사용하는 방법이 있다. 쿠버네티스는 컨테이너 오케스트레이션 툴로, 서비스를 이미지 형식으로 만든 후 쿠버네티스 환경 위에서 pod 라는 형태로 동작시키면 쿠버네티스가 알아서 해당 pod 가 죽으면 다시 살려주는 과정을 반복한다. 더 나아가서 deployment 라고 하는 형태로 동작시킨 다면 우리가 원하는 replicas 의 갯수대로 pod 가 운영되면서 서비스의 갯수도 동적으로 조절하고 다운되지 않도록 자동으로 설정이 가능한다. 

 

 

2.  primary - standby 구조 적용

primary - standby 구조는 DB 를 운영할 때 많이 들어볼 수 있는 용어이다. primay - standby 구조는 하나의 primary 와 standby 구조로 이루어져 있는 형태로, 사용자 요청을 받고 수행하는 주체를 primary 라고 하고 혹시 primary 가 문제가 발생했을 때를 대비해 언제든 구동될 준비가 되어 있는 상태의 서버를 의미한다. 그래서 DB 서비스를 예로 들자면, primary DB 는 read/write query 를 모두 수행할 수 있고, secondary 는 read-only 형태로 동작하는 구조를 생각 할 수 있다. 

 

이런 형태도 쿠버네티스 위에 올릴수 있겠지만, 단 한대의 서버만 운영되어야 하는 상황이라면 deployment 형식으로 확장시키기도 어려울 것이다. 심지어 쿠버네티스 위에 올리지 못하는 환경의 경우, 예를 들어 vm 을 사용해야 하는 경우나 클라이언트 서버에 설치되어야 한다면 직접 고가용성을 제어해야 한다. 

 

이때, 직접 고가용성을 제어하는 여러 방법들이 있는데 그중에 leader election 을 이용한 방법들에 대해서 몇가지를 살펴 보겠다. 

leader election 은 말 그대로 리더를 선출 하는 방식인데, 선출된 리더를 primary 로 사용하고, 만약 리더에 문제가 생기면 대기 하고 있던 후보 군들 (standby) 에서 한 놈이 리더로 승격되는 구조를 의미 한다. 

 

2-1.  분산 메타 저장소를 이용한 방법 (e.g. zookeeper)

leader election 을 적용하기 위한 방법들 중 가장 대표적인 방법이 분산 메타 저장소를 이용하는 방식이다. 대표적인 플랫폼으로는 zookeeper 가 있다. zookeeper 는 분산 코디네이터로 key value 형식의 메타데이터를 이용해 서버 간의 상태를 파악하고 관리하는 형식이다. 

 

예를 들어서, 3대의 서버가 있고 이 서버간의 고가용성을 유지하기 위해 leader election 방법을 적용하고 싶다고 하자. 그리고 leader election 을 제어하기 위한 목적으로 zookeeper 를 이용한다고 가정해 보자. 이때 적용할 수 있는 아주 간단한 방법으로는 zookeeper 에 key-value 형식으로 {"leader" : "name: server-1, timestamp:2023:11:12:9392"} 과 같은 식으로 저장해 두는 방식이다.  여기서 의미하는건 server 1 이 leader 인 겻이고 server-1 의 health check time 을 주기적으로 업데이트 한다. 이렇게 하면 leader 가 해당 데이터에 주기적으로 생존신고를 하면서 살아 있음을 알리게 되는데  만약 timestamp 가 일정 시간 이후 까지 업데이트가 안된다면  후보군들이었던 다른 서버들이  "leader" key 를 가진 객체에 자신의 데이터를 업데이트 한다. 하지만 동일한 key 에는 여러 value 가 나올수 없으므로 결국 하나의 server 만 leader 객체에 업데이트 성공하게 될 것이다. 

 

혹은, zookeeper 의 znode 데이터 특성을 이용해서 leader 를 선택할 수도 있다. (참고) znode 와 연결된 서버의 connection 이 끊기면 데이터가 사라진다는 특성과 znode 안에서 순차적으로 데이터를 넣을수 있는 특성을 이용해서 가장 첫번째 입력되었던 즉 queue 형식이라면 가장 오래된 데이터와 연결되 서버가 leader 가 되는 것이고, 이때 서버와의 연결이 끊기면 queue 에서 빠져나간 이후 그 다음에 들어온 서버가 leader 로 선정되는 방식이다. 

 

 

2-2.  서버 들 간의 자체 통신을 통한 방법 (e.g.  raft)

2-1 에서 살펴본 방식은 외부 저장소를 이용해 leader 를 선출하는 방식인데, 이런 경우 외부 저장소에 의존성이 생길수 있다는 특징이 있다. 이런 경우 각 서버간의 서로 통신을 주고 받으며 leader 를 선정하는 방식이 있는데 대표적으로 raft 알고리즘이 있다. raft 알고리즘은 간단히 말해서 리더가 주기적으로 후보자 서버들에게 ping 을 보내고, 후보자는 해당 ping 을 보고 리더가 살아있음을 인지하게 된다. 이때 리더가 죽으면 ping 을 보내지 못하게 되고 후보자들 중 한명이 먼저 리더가 죽었다는 것을 알아 차리면 본인이 리더로 스스로 승격 하며 다른 후보자들에게 ping 을 보내게 된다. 

 

하지만 raft 알고리즘을 적용할때 주의해야 하는 점은 정족수(quorum) 을 지켜야 한다는 점이다. 정족수는 서버들 중에서 리더를 선출하거나 문제가 생김을 방지 하기 위해 존재해야 하는 최소 갯수를 의미하는데 보통 (n/2)+1 개를 의미 한다. 3개의 서버가 있을때 1개가 죽으면 남아 있는 서버가 2개 이므로 (3/2)+1 = 2 로 정족수를 만족하지만, 2개가 죽으면 남은 서버는 1대 이기 때문에 정족수를 만족시키지 못하고 가용성 구조를 지키지 못하게 된다. 

 

이렇게 해서 고가용성을 지키기 위한 다양한 방법을 보았다. 각 방법들마다 특징들이 있기 때문에 서비스에 맞는 방법을 적용하는 것을 추천한다. 

 

 

 

댓글