[k8s] etcd & Raft Alogorithm (2) 본문
Runtime Reconfiguration
etcd 클러스터가 동작 중일 때, etcd 서버를 추가/삭제하는 것을 런타임 재구성이라고 한다.
Member Add
3개의 서버로 구성된 etcd 클러스터의 lastIndex가 10101이라고 가정해보자. etcd에는 Snapshot 이라는 개념이 있는데, etcd 서버가 현재까지 받아들인 모든 log를 Entry에서만 관리하는 것이 아니라 파일 시스템에 백업해두는 것을 의미한다.
Snapshot 실행 빈도는 etcd 클러스터를 생성하면서 옵션으로 설정할 수 있고, 디폴트 값은 100,000이다. 이러한 상황에서 4번째 서버를 추가하는 요청이 발생했다고 가정해보자.
etcd는 이러한 구성의 변경을 log append와 동일한 메커니즘으로 처리한다. Leader는 사용자로부터 전달받은 재구성 요청에 따라 새로운 구성에 해당하는 Cnew
를 log Entry에 추가하고 Append RPC call을 이용해 다른 서버에 전파한다.
일반적인 Log Replication과 다른 점이 있다면, 이 구성에 대한 요청은 commit 된 이후에 효력을 발휘하는 것이 아니라 Entry에 write 되자마자 곧장 효력을 발휘한다는 것이다. 만약 구성과 관련된 로그가 Entry 내에 여러 개 존재한다면 가장 최신의 로그를 사용하게 된다.
또한, Log Entry에 write 되자마자 곧장 효력을 발휘하기 때문에 Log Entry에 존재하는 Cnew를 commit 하기 위해 필요한 정족수는 3 (4/2 + 1)으로 증거된다.
서버 추가를 요청한 클라이언트에게 응답을 한 이후, Leader는 추가된 멤버가 가능한 빨리 동일한 log를 보유할 수 있도록 Snapshot을 보내준다. 해당 Snapshot은 파일 시스템에 저장되어 있는 것들 중 가장 최신의 것과 Leader의 현재 Log Entry를 합쳐 생성된다.
새롭게 추가된 서버는 해당 Snapshot을 이용해 DB를 만드는 것으로 Append RPC call을 정상적으로 받아들일 수 있게 된다.
Member Add Risk
만약 새롭게 추가된 4번째 서버가 새롭게 구성되고 있을 때, 새로운 서버 추가 요청이 한 번 더 들어온다면 어떻게 될까?
우선, 새롭게 서버 추가 요청이 Leader의 Log Entry에 저장된다. 정족수는 3 (5 / 2 + 1) 에서 증가되지 않지만, 만약 다른 서버에서 해당 요청 로그를 복제받는 것에 지연이 생긴다면 새롭게 들어온 서버 추가 요청이 지연이 commit 되는 것 또한 지연된다.
이러한 상황에서 추가적인 write 요청이 들어오게 된다면, 해당 요청 또한 block이 되고 클라이언트의 Timeout 시간 동안 해당 요청을 처리하지 못하면 가용성에 문제가 생기게 된다.
Raft 알고리즘은 이러한 문제를 해결하기 위해 Leader, Follower, Candidate 외에 Learner 상태가 존재한다. Learner는 etcd 클러스터의 멤버이지만, 정족수 카운트에서는 제외되는 특별한 상태로 etcd 3.4.0부터 구현됐다.
Learner는 etcd의 promote API를 사용하여 일반적인 Follower로 변경할 수 있다. Learner가 아직 log를 모두 따라잡지 못한 경우에는 거절된다.
Kubeadm, Kubespray와 같이 널리 쓰이는 Kubernetes 설치 자동화 도구는 etcd 클러스터 생성 시 Learner의 개념을 활용하고 있지는 않다. Kubernetes에서 사용하는 etcd 클러스터의 Snapshot은 평균적으로 크기가 수십 Kb에 불과하기 때문에 이러한 이슈가 발생할 가능성은 낮다.
또한 이러한 일을 방지하기 위해 Raft 알고리즘에서는 런타임 재구성은 한 번에 하나씩 처리할 것을 원칙으로 하고 있으며, etcd에서는 restriction을 설정하여 Leader의 log에 commit 되지 않은 구성 요청이 있다면 새로운 구성 요청을 받지 않는다.
Remove Leader
etcd 클러스터에서 특정 서버를 삭제하라는 요청 또한 etcd 서버 추가와 동일한 방식으로 진행된다. 하지만 삭제 대상이 Leader일 경우, Leader는 Leader 삭제에 대한 구성 요청을 commit 시키는데 필요한 로그 복제에 대한 정족수를 셈할 때 자기 자신을 제외한다.
Leader를 제외한만큼의 정족수가 확보되면 Leader 삭제에 대한 내용이 존재하는 로그를 commit 하고 해당 요청을 보낸 클라이언트에게 응답을 보낸다. commit 이후 Leader 상태에서 빠져나오게 되고 더 이상 다른 서버에 Heartbeat를 보내지 않게 된다. 이것을 Raft와 etcd에서는 step down 이라고 표현한다.
Leader가 클라이언트의 삭제 요청을 처리할 때 정족수에서 자기 자신을 제외하였기 때문에, 새로운 Leader의 선출이 가능해진다.
만약 step down 이 발생하기 전에 write 요청이 들어온다면 어떻게 될까?
구성 요청은 Entry에 들어가자마자 효력을 발휘하지만, Leader 삭제 요청의 경우 Entry에 존재하는 가장 최신의 구성 로그에 자신이 없더라도 Leader 역할을 수행한다.
예를 들어, 아직 삭제 요청이 commit 되기 이전에 write 요청이 들어온다면 Leader는 자기 자신을 제외한 정족수만큼 로그 복제를 수행한다.
Member Remove Restriction
Leader는 정족수와 현재 정상 동작 중인 서버의 수를 비교하여 현재 정상 동작 중인 서버 수가 정족수보다 낮아질 것으로 예상되면 클라이언트의 멤버 삭제 요청을 거절한다.
'Infra' 카테고리의 다른 글
[k8s] K8s Storage with OpenEBS (0) | 2024.08.02 |
---|---|
[Network] TCP 프로토콜에 대한 이해 (1) | 2024.07.23 |
[k8s] etcd & Raft Algorithm (1) (0) | 2024.07.10 |
[K8s] k8s Pod 생성 시 벌어지는 일 (0) | 2024.07.09 |
Traefik 이란? (1) | 2024.05.30 |