쿼카러버의 기술 블로그

[k8s] 쿠버네티스에서의 리더 선출 (feat. 쿠버네티스, stateless) 본문

[Infra & Server]/데브옵스

[k8s] 쿠버네티스에서의 리더 선출 (feat. 쿠버네티스, stateless)

quokkalover 2023. 12. 16. 11:25

리더 선출이란 여러 대의 동일한 코드로 동작하는 어플리케이션 인스턴스들 중에서 특정 인스턴스를 '대표'로 선정하는 과정을 말한다. 리더 선출의 주된 목적은 시스템 내에서 중요한 결정이나 작업을 담당할 ‘리더’를 선정하는 것이다. 이 과정은 특히 분산 시스템이나 클러스터 환경에서 중요하다.

 

본 글에서는 그 중에서도 쿠버네티스에서의 리더 선출에 대해 다룬다. 다시 말해 쿠버네티스에 의해 조정되고 관리되는 리더 선출 메커니즘에 대해 다루려고 한다. 쿠버네티스는 Distributed Lock을 활용해 다수의 인스턴스 중 한 인스턴스를 리더로 선출할 수 있게 하여, 오직 그 인스턴스만이 특정 작업을 수행할 수 있도록 보장한다.

 

참고로 블록체인에서도 리더를 선출한다. 하지만 이 글에서 말할 리더 선출과는 다른 개념이다. 블록체인에서의 리더 선출은 네트워크의 탈중앙화를 유지하고, 특정 참여자가 지속적으로 권한을 독점하는 것을 방지하기 위해 무작위성을 도입하기 때문이다. 예를 들어보면 이더리움 2.0과 같은 PoS 시스템에서는 일정량의 암호화폐를 '스테이킹'하는 참여자 중에서 무작위로 또는 특정 기준에 따라 블록을 생성할 '검증자'를 선출하고, 이 과정이 리더 선출에 가깝다. 또한 DPos라고, 특정 참여자를 대표로 선출하고, 이들 대표가 블록 생성에 참여하는 시스템도 있는데, 이는 명확한 리더 선출 과정을 포함하고 있다. 전통적인 분산 시스템에서의 리더 역할과는 다르다. 네트워크 운영의 핵심적인 요소 중 하나로 리더가 있을 뿐 전통적인 분산시스템에서와는 다른 역할과 목적을 가진다.

참고로 이런 글을 쓰는 목적은 정보 공유도 크지만, 내가 스스로 나중에 다시 돌아보려고 할때 좋은 참고가 될만한 자료를 만들어 두기 위함도 있다. 리더 선출만 볼게 아니라, 다른 개념들도 짧게나마 다루어 어쩌다 이 글을 보게 된 사람들이 관련해서 공부할만한 키워드들도 던져두고. 그래서 좀 길어질 수도 있지만 총 두편의 글로 나누어 써보려고한다. 첫 번째 글은 쿠버네티스, 리더 선출과 관련된 개념들을 요약해보고, 두 번째 글은 본격적으로 리더 선출을 진행하는 예시를 다룰 것이다.
 
자 그럼 시작해보자!

 

 

쿠버네티스(kubernetes) 그리고 MSA(Microservice Architecture)

쿠버네티스는 여러 컴퓨터 프로그램(특히 ‘컨테이너’라고 불리는 작은 프로그램(or 어플리케이션))을 체계적으로 관리하고 조정하는 도구다. 프로그램들이 서로 잘 소통하고, 필요할 때 적절히 사용될 수 있도록 도와주는 일을 담당한다. 예를 들어 어떤 프로그램이 갑자기 많은 사용자의 요청을 받게 되면, 쿠버네티스가 자동으로 더 많은 ‘컨테이너’를 생성하여 부하를 분산시켜준다. 이를 짧게 요약해서 표현해보면 컨테이너화된 어플리케이션들의 오케스트레이션, 즉 마이크로서비스의 복잡한 관리를 단순화한다고 표현할 수 있다. 쿠버네티스에서 제공하는 인프라 관련 서비스를 나열해보면 대표적으로 서비스 디스커버리, 스케일링, 로드밸런싱, self-healing(자가 치유), 리더 선출 등이 있다.

다수의 서비스를 자동으로 배치, 확장 및 관리하는 일은 MSA(Microservice Architecture)에서 매우 중요하다. 대표적으로 시스템의 안정성을 높이기 위해 서비스가 멈추면 자동으로 다시 시작한다든지, 자동으로 트래픽에 따라 서버를 더 띄우던가 줄인다든지 등의 기능이 필요하기 때문이다. 따라서 일반적으로 MSA(마이크로서비스 아키텍처)를 채택한 기업들은 쿠버네티스를 사용함으로써 더 효율적으로 서비스를 개발, 배포 및 관리할 수 있다.

참고로 블록체인 생태계에서 생각해보면 물론 블록체인 노드들은 MSA구조와 거리가 멀지만, 블록체인을 활용한 서비스를 만드는 기업들은 MSA아키텍처를 선택한 경우가 많다.

 

쿠버네티스가 활용되는 가장 대표적인 사례 : stateless

쿠버네티스 환경을 채택하게 되는 가장 흔한 경우가 stateless한 특성을 가진 어플리케이션들을 동시에 실행하고 동작하게 하고 이들을 효율적인 운영하며 관리하는데 있다. stateless한 어플리케이션들은 이전 요청에 대한 정보를 보관하지 않는다는 특징을 가지고 있다. 왜 상태를 유지 하지 않냐고? 각각의 요청을 독립적으로 처리하며 이전 요청의 정보를 보존하지 않는 이 특성 덕분에 트래픽이 늘었을 때 추가적인 인스턴스를 빠르게 배포하여 요청 증가에 대응할 수 있기 때문이다. 이러한 시스템은 더 높은 확장성을 가지고, 부하 분산을 보다 효율적으로 수행할 수 있다.

대표적인 예를 들어보면 웹 어플리케이션 서버를 들 수 있다. 온라인 쇼핑몰의 웹 어플리케이션 서버를 생각해보자. 이 서버는 사용자의 요청에 따라 상품 목록을 보여주고, 주문 처리를 하는 등의 작업을 수행한다. 이 때 어플리케이션은 각 요청을 서로 독립적으로 처리한다. 예를 들어 한 사용자가 상품 페이지를 요청할 때, 그 요청에는 사용자 세션, 상품 ID등 모든 필요한 정보가 포함된다. 하지만 이 정보를 어플리케이션 노드에서 관리하지 않고 외부 데이터베이스나 저장소와 통신하여 데이터 일관성을 유지한다. 주문 정보는 외부 데이터베이스에 주문 정보를 저장하고, 이 데이터베이스는 모든 서버 인스턴스들에게 일관된 데이터 뷰를 제공하도록 하는 것이다.

쿠버네티스는 이러한 stateless한 서비스를 관리하는데 매우 적합하다. 서버에 부하가 많으면 여러 서버 인스턴스를 자동으로 확장하고. 또 부하가 없을 때는 축소한다. 이는 각 인스턴스가 모두 독립적으로 요청을 처리할 수 있기 때문이며, 이를 다르게 표현하면 고가용성과 확장성을 보장한다고 할 수 있다.

잠깐! 멈춰서 여태 얘기했던 이야기 중에 중요한 키워드들을 조금 짚고 넘어가자.

  • stateless : 어플리케이션의 각 인스턴스가 독립적으로 작동하고, 서로의 상태를 알 필요가 없다는 것을 의미. 무상태 시스템을 사용하는 이유는 각 요청을 독립적으로 처리하고, 이전 요청에 대한 정보를 유지하지 않는 특성으로 인해 확장성이 높고 부하 분산이 용이하기 때문.
  • 데이터 일관성의 중요성 : Stateless 어플리케이션은 이전 상태나 요청에 대한 정보를 유지하지 않기 때문에, 모든 요청은 독립적으로 처리됩니다. 이러한 환경에서 데이터 일관성을 유지하는 것은 매우 중요하다.
  • 예를 들어 온라인 쇼핑몰에서 상품을 검색하고 장바구니에 추가하는 등의 행동을 할 때, 이러한 모든 작업들이 동일한 데이터를 바탕으로 이루어져야 한다.
  • 주문 정보와 같은 중요한 데이터가 여러 서버에서 처리된다고 하더라도, 일관되게 관리되고 저장되어야 한다. 다시 말해 데이터의 정확성과 안정성을 보장해야 한다.

 

리더 선출이 뭐야?

자 이제 본격적으로 리더 선출에 대해 다뤄보자

리더 선출이란 여러 대의 동일한 코드로 동작하는 어플리케이션 인스턴스들 중에서 특정 인스턴스를 '대표'로 선정하는 과정을 말한다. 이 과정은 앞서 말한 스케일링(부하에 따라 서버 증설), 고가용성(자동 장애 복구)과 더불어 분산 시스템 환경에서는 필수적인 기능이다.

앞에서 쿠버네티스는 동일하고 stateless한 특성을 가진 어플리케이션 노드가 동시에 실행되고 동작할 때 편하고 잘 관리하기 위해 사용된다고 했다. 쿠버네티스의 역할이 바로 이런 workload들을 관리하는데 있기 때문이다. 하지만 이런 기능만으로는 우리가 누리고 있는 다양한 서비스들을 구현해낼 수는 없다. 웹 서버같이 같이 다수의 인스턴스가 동시에 동일한 작업을 수행하고 데이터의 일관성은 DB를 통해 관리해야 하는 상황도 있지만, 배치 처리, 캐시 업데이트 등 모든 노드가 동시에 동일한 작업을 수행하지 않아야하는 경우도 존재하기 때문이다. 이를 조금 다르게 표현하면 여러 인스턴스 중 하나를 리더로 지정해서, 리소스의 중복 사용을 방지하고, 데이터 처리의 일관성을 유지해야 하는 일도 필요하다. 그렇기 때문에 이러한 스케일링뿐 아니라 리더 선출 또한 분산시스템에서 매우 중요한 역할을 한다. 리더 선출 프로세스와 스케일링 기능은 분산 시스템 하에서 높은 트래픽과 다양한 요구사항을 효율적으로 처리할 수 있도록 하는데 필수적이다.

 

쿠버네티스에서 리더 선출이 왜 필요해?

리더 선출이 꼭 쿠버네티스에만 속해 있는 개념은 아니다. 그리고 본 글은 쿠버네티스에서의 리더 선출에 대해 다루는 것이지, 사실 분산 시스템 환경에서 리더의 역할은 정말 다양하다.

무튼 쿠버네티스 환경을 선택했다는 것은 대부분 stateless특성을 활용해서 분산시스템의 효율을 높이기 위함일텐데, 이런 환경에서도 리더 선출이 필요한 상황은 있기 마련이다. 그 중에서 가장 기본적인 리더 선출이 필요한 케이스를 다뤄보자.

 
(1) 데이터 일관성 유지 (데이터 충돌 방지)
여러 동일한 어플리케이션들이 동시에 동작한다고 했을 때, 모두 동시에 특정 데이터를 수정하려고 하거나 설정을 적용하려고 하다보면 충돌이 발생할 수 있다.
자꾸 데이터 충돌 충돌 하는데 충돌의 예시를 한번 들어보겠다.

  • 상황: 온라인 상점에서 두 명의 고객이 마지막 남은 하나의 제품을 동시에 구매
  • 충돌: 두 고객 모두 재고가 1개 남아 있다고 보고 제품을 구매
  • 결과: 실제로는 하나만 판매할 수 있는데, 시스템 상에서 제품이 두 번 판매된 것처럼 처리되어 재고 관리에 혼란이 발생

사실 여기까지는 개념 설명을 위한부분이고, 위 케이스는 리더 선출이 아니라, 분산락을 잡아서도 처리할 수 있고 다양한 대안들이 있다.
 
(2) 리더로서의 역할 수행
실제 서비스를 운영하다보면 만날 수 있는 쿠버네티스에서 리더 선출이 필요할 만한 케이스를 예로 들어보겠다.

  • 상황: 쿠버네티스 클러스터 내에 여러 파드가 실행되고 있으며, 각 파드는 설정 데이터를 로컬에 보관하고 있다고 해보자.
  • 리더의 역할: 리더로 선출된 파드는 정기적으로 다른 모든 파드의 설정 데이터를 확인하고, 중앙 집중식 설정 저장소(예: 분산 데이터베이스)의 최신 상태와 일치하는지 검증한다.
  • 결과: 리더 파드는 데이터 불일치를 발견할 경우, 해당 파드에게 최신 설정을 적용하도록 지시하거나, 필요한 조정 작업을 수행한다. 이를 통해 클러스터 내의 모든 파드가 일관된 데이터와 설정을 유지할 수 있다.

위 예시에 이어서 리더의 역할을 요약해보면 아래와 같다.

  • 일관된 시스템 상태 유지: 리더 파드의 지속적인 모니터링과 조정을 통해, 클러스터 내의 모든 파드는 동일하고 최신의 설정 정보를 유지한다. 이를 통해 어플리케이션의 안정성과 신뢰성을 강화한다.
  • 효율적인 구성 관리: 중앙 집중식 구성 관리와 리더의 역할로 인해, 설정 변경이 필요할 때 전체 클러스터의 파드를 일일이 업데이트하는 번거로움을 줄일 수 있다

리더 선출을 통해 중요한 결정이나 작업이 일관되고 안정적으로 수행되도록 보장함으로써, 전체 시스템의 안정성이 향상되는 것이다. 특히나 데이터 충돌이나 일관성 문제가 발생하면 안되는 경우에는 리더 선출을 통해 한 인스턴스가 이러한 중요한 작업을 관리함으로써, 데이터의 무결성과 일관성을 유지할 수 있다.

 

쿠버네티스에서의 리더 선출 API

쿠버네티스에서의 리더 선출은 분산 락을 활용하는 시스템인데, 쿠버네티스 API 서버와 다른 구성 요소들에서 사용되는 동일한 API를 기반으로 한다. 이는 리더 선출 기능이 쿠버네티스 전반에 걸쳐 깊이 통합되어 있으며, 일관성 있는 방식으로 구현되었음을 의미하기 때문에 믿고 쓸 수 있다는 느낌이 팍팍든다. 쿠버네티스의 리더 선출 메커니즘이 신뢰성 높고 표준화된 코드에 기반을 두고 있기 때문이다. 따라서 개발자들은 이 기능을 자신들의 어플리케이션에 적용할 때, 이미 검증된 메커니즘을 사용하고 있다는 확신을 가질 수 있다. 사실 이 포인트는 개발 할 때 정말 중요하다.. 믿고 쓸 수 있다는 것.. Bully, Paxos, Raft, ZAB 등등 다양한 리더 선출 알고리즘이 있지만 구글링좀 해보면 많은 숙련된 개발자들이 조언하는건 “너가 하지 말고, 있는거 가져다 써~”다. 알고리즘이 매우 복잡하고, 내가 직접 구현하는 순간부터 지옥문이 열리기 때문 ㅎ


따라서 쿠버네티스의 리더 선출은 단순히 기능적인 측면을 넘어서 시스템의 전반적인 신뢰성과 효율성을 높이는 중요한 구성 요소고, 쿠버네티스를 선택하게 만드는 하나의 중요한 이유가 될 수 있다.

 

쿠버네티스의 리더 선출 방식

이제 쿠버네티스의 리더 선출 방식에 대해 자세히 살펴보자. 이 부분 부터는 2020년 Kubecon에서 진행된 발표 내용을 요약했다. 좀 더 자세히 보고 싶으면 https://kccnceu20.sched.com/event/Zerw 자료를 참고하자.
 
리더 선출에 활용되는 공유 객체

쿠버네티스는 리더 선출을 하기 위해 쿠버네티스 버전에 따라 configmap혹은 lease라는 shared object(공유 객체)를 활용한다. 특히 lease는 리더 선출을 위해 설계된 객체기 때문에 적극 활용하라고 한다.

 
공유 객체에 대한 권한 설정

앞서 말했듯 쿠버네티스의 API를 활용해 configmap이나 lease라는 공유 객체의 정보를 업데이트해서 리더 선출을 할 수 있는데, 이를 위해서는 어플리케이션에 필요한 권한이 있어야 한다. 쿠버네티스는 접근 제어와 권한의 중요성을 매우 강조하는 툴이기 때문에, 오직 인증된 인스턴스만이 공유 객체를 수정할 수 있다. 따라서 운영 환경에서 쿠버네티스의 리더 선출 메커니즘을 적용하기 위해서는 적절한 권한 설정이 필수적이다.

 
리더가 선출되는 방식

자 이제 공유 객체가 어떻게 활용되는지 보자. 쿠버네티스에서의 리더 선출은 여러 인스턴스가 공유된 객체를 업데이트하는 경쟁 과정으로 이해할 수 있다. 각 인스턴스는 앞에 말한 공유 객채인 configmap, 혹은 lease객체를 업데이트 하기 위해 경쟁한다. 이 객체는 쿠버네티스의 표준 코드를 사용하여 잠금 역할을 수행하고, 이 객체를 성공적으로 업데이트하는 첫 번째 인스턴스가 리더로 선출되며, 이 인스턴스가 리더로서 시스템 내에서 특정 작업을 수행할 권한을 가지게 된다.

 
리더가 재선출되는 방식
그럼 리더가 선출되고나서, 만약 해당 인스턴스가 갑자기 꺼지거나 동작을 안할 때는 어떻게 할까? 이에 대비해서 당연히 리더를 재선출하는 로직도 준비가 돼있다. 리더 재선출 프로세스는 다음과 같다.

  • 리더는 공유 객체(예: 컨피그 맵 또는 리스 객체)를 주기적으로(기본적으로 10초마다) 업데이트하여 하트비트 신호를 보낸다.
  • 리더가 연속으로 두 번 업데이트를 실패하면(대략 20초가 소요됨), 다른 파드들은 현재 리더가 다운되었거나 반응이 없다고 인식한다.
  • 이러한 실패가 감지되면, 다른 파드들은 새로운 리더 선출 프로세스를 시작한다.
  • 이후에 객체를 성공적으로 업데이트하는 첫 번째 파드가 새 리더가 된다
  • 현재 리더의 실패 감지와 새 리더 선출 완료까지의 총 시간은 기본 하트비트 및 타임아웃 설정을 고려할 때 일반적으로 25–30초 범위에 있다.

쿠버네티스 리더 선출 사용시 주의 사항

더욱 신뢰성있고 효율적인 어플리케이션을 설계하기 위해서는 리더 선출의 한계와 적절한 사용 사례를 이해해야 한다. 만약 집중해서 이 글을 읽고 있다면 리더가 재선출 되는 방식을 읽다보면 자연스럽게 명확한 한계점 및 주의사항을 떠올릴 수 있을 것이다. 쿠버네티스의 리더 선출 메커니즘에서, 리더에 문제가 발생했을 때 새로운 리더를 선출하기까지는 대략 25–30초의 시간이 소요된다. 이는 쿠버네티스가 리더 실패에 대한 신속한 대응보다는 시스템의 일관성과 안정성을 우선시한게 아닐까 싶다. 리더의 잠재적인 공백기간인 약 30초를 견딜 수 있는 작업에 리더 선출 메커니즘을 적용하는 것이 적합하다. 따라서 쿠버네티스의 리더 선출을 활용하게 된다면 중요한 작업을 처리하는 동안 리더의 공백이 발생할 가능성을 염두에 두고, 이에 대한 적절한 대비책을 마련하는 것이 필수적이다.
자 이렇게 개념 설명을 마무리 하도록 하겠다. 이제 2부에서는 인터넷에 돌아다니는 여러 예제들을 읽어보고 참고해서 정리할 예정이다.

이 글에서 공부해볼만한 키워드들을 좀 던져보면 MSA, Kubernetes, stateless, raft, paxos, configmap, lease, 분산 락, distributed lock, 데이터 일관성이 있다.

 
또 열심히 공부하러 가보자~

 
참고자료

Comments