클러스터에 접속했을 때는 재시작이 대부분 잦아든 뒤였다. API 서버는 readiness 검사에 응답했고, 서비스 경로에서도 문제가 확인되지 않았다. 앞서 반복해서 종료됐던 제어 영역의 프로세스들도 안정됐거나 간헐적으로만 재시작되고 있었다.

앞서 kube-controller-manager와 kube-scheduler가 연달아 종료됐지만, 에어갭 환경이라 바로 접속해 조사하기는 어려웠다. 남아 있는 로그와 VM 지표로 당시의 상황을 재구성하고, 추가로 확인할 내용을 인프라 담당자에게 전달했다.

01 / 남아 있는 기록

현재 상태로는 이전 재시작을 설명할 수 없었다

접속했을 때 확인한 readiness와 현재 프로세스의 로그는 그 시점의 상태를 보여줬다. 하지만 재시작되기 전 프로세스가 왜 종료됐는지는 설명하지 못했다. 종료 직전의 로그와 컨테이너 종료 상태, 재시작 이력을 따로 확인해야 했다.

이전 컨테이너의 로그에는 kube-controller-manager와 kube-scheduler가 리더 자격을 갱신하지 못하고 종료한 기록이 남아 있었다. 재시작이 반복된 직접적인 이유는 확인했지만, 갱신이 왜 제시간에 끝나지 않았는지는 더 살펴봐야 했다.

전체 로그에서 etcd라는 단어를 찾으면 시작 과정의 메시지와 재시도, 장애의 결과로 남은 오류까지 한꺼번에 섞였다. 재시작이 집중된 시간대로 범위를 좁히고, 종료된 프로세스의 마지막 기록부터 API 서버와 etcd의 로그를 맞춰 봤다.

조사에 사용한 기록과 각 기록으로 알 수 있는 것

표를 좌우로 밀어 비교할 수 있습니다.

기록확인할 수 있는 내용
현재 readiness와 로그접속한 시점의 준비 상태와 현재 프로세스에서 나타나는 오류
이전 컨테이너 로그와 종료 상태종료 직전에 어떤 처리가 실패했는지
재시작 이력어느 시간대에 재시작이 집중됐는지
같은 시간대의 VM·디스크 지표프로세스 밖에서도 지연이 나타났는지

현재 경고 이벤트가 많지 않다는 것도 재시작 이력과 모순되는 상황은 아니었다. 이벤트와 로그, 재시작 횟수는 보존하는 기간과 의미가 다르다. 현재 남은 경고의 수보다 당시 프로세스가 종료되기까지의 순서를 확인하는 것이 필요했다.

02 / 리더 갱신과 종료

리더 갱신 실패가 프로세스 종료로 이어지는 과정

kube-controller-manager와 kube-scheduler를 여러 개 실행하는 구성에서는 각 구성 요소의 인스턴스 중 하나가 리더 역할을 맡는다. Kubernetes의 Lease는 누가 그 역할을 맡고 있는지와 갱신 시각을 기록하는 객체다. 리더는 API 서버를 통해 이 기록을 주기적으로 갱신한다.

Kubernetes 문서 · Lease와 리더 선출

Kubernetes also uses Leases to ensure only one instance of a component is running at any given time.

Kubernetes는 어느 시점이든 구성 요소의 인스턴스 하나만 동작하도록 보장하는 데에도 Lease를 사용한다.

각 구성 요소에서 여러 인스턴스가 실행 중이어도 리더 역할은 하나가 맡고, 나머지는 역할을 넘겨받을 수 있도록 대기한다. Lease 자체가 리더를 뽑는 것은 아니다. 각 인스턴스의 리더 선출 로직이 API 서버를 통해 Lease의 소유권 획득과 갱신을 시도한다. 기존 리더가 갱신하지 못해 Lease의 유효 기간이 지나면 다른 인스턴스가 소유권 획득을 시도할 수 있다.

갱신 요청이 한 번 실패했다고 바로 리더 자격을 포기하는 것은 아니다. client-go의 리더 선출 구현은 RenewDeadline으로 정한 시간 안에서 갱신을 재시도한다. 그 안에 성공하지 못하면 리더 역할을 내려놓고, OnStoppedLeading 처리를 호출한다.

client-go v0.37.0 · RenewDeadline 주석

RenewDeadline is the duration that the acting master will retry refreshing leadership before giving up.

RenewDeadline은 현재 리더가 리더 역할을 포기하기 전까지 갱신을 재시도하는 시간이다.

kube-controller-manager와 kube-scheduler에는 리더 자격을 잃었을 때 프로세스를 종료하는 처리가 있다. 세부 종료 방식은 버전과 설정에 따라 다르며, 종료 이후의 재시작은 이를 실행하는 환경의 정책에 따른다. 따라서 반복 재시작을 조사할 때는 프로세스의 종료 기록과 함께, 리더 갱신이 왜 제시간에 끝나지 않았는지도 확인해야 한다. 아래 코드는 이 동작을 설명하기 위해 Kubernetes v1.37.0에서 발췌했다.

kube-controller-manager v1.37.0 · 리더 상실 시 종료 처리
OnStoppedLeading: func() {
    logger.Error(nil, "leaderelection lost/stopped")
    if !utilfeature.DefaultFeatureGate.Enabled(cmfeatures.ControllerManagerReleaseLeaderElectionLockOnExit) {
        klog.FlushAndExit(klog.ExitFlushTimeout, 1)
    }
},
v1.37.0에서는 ControllerManagerReleaseLeaderElectionLockOnExit 기능이 비활성화된 경우, 이 콜백에서 로그를 내보내고 종료 코드 1로 프로세스를 끝낸다.
kube-scheduler v1.37.0 · 리더 상실 시 종료 처리
// We lost the lock.
logger.Error(nil, "Leaderelection lost")
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
종료 요청을 받은 경우와 구분해, 리더 lock을 잃은 경우에 실행되는 부분이다. 오류를 기록하고 종료 코드 1로 프로세스를 끝낸다.

이 갱신도 API 요청이므로 서버가 응답할 때까지 기다려야 한다. API 서버는 Lease를 비롯한 Kubernetes 객체의 상태를 etcd에 저장한다.

Kubernetes 문서 · 제어 영역 구성 요소

Consistent and highly-available key value store for all API server data.

API 서버의 모든 데이터를 저장하는, 일관성과 고가용성을 갖춘 키-값 저장소.

각 구성 요소를 세 개씩 실행하는 경우의 역할 구분

표를 좌우로 밀어 비교할 수 있습니다.

구성 요소담당하는 작업리더와 나머지 인스턴스
kube-controller-manager클러스터의 현재 상태가 원하는 상태에 맞도록 조정하는 컨트롤러 실행자기 인스턴스 중 하나가 리더로 작업을 수행하고 나머지는 대기
kube-scheduler아직 노드에 배정되지 않은 Pod를 실행할 노드 선정별도로 하나가 리더로 스케줄링을 수행하고 나머지는 대기
kube-apiserverKubernetes API 요청 처리와 객체 상태의 조회·변경API 요청을 처리할 리더 하나를 선출하지 않으며, 세 인스턴스 모두 요청 처리 가능
etcdKubernetes 객체의 상태 저장과 변경 기록 복제정상 동작 중에는 Raft 리더 하나와 팔로워 둘로 구성

기본 리더 선출을 사용하는 고가용성 구성을 설명하기 위한 예시다. 인스턴스 수나 배치를 실제 장애 환경에서 가져온 것은 아니다.

예를 들어 A·B·C 서버에 각 구성 요소를 하나씩 실행한다면, controller-manager의 리더는 A에, scheduler의 리더는 B에, etcd의 리더는 C에 있을 수 있다. API 서버는 세 인스턴스 모두 요청을 받을 수 있으므로, 로드 밸런서로 요청을 나눠 보낼 수 있다.

Kubernetes 문서 · 고가용성 구성의 API 서버

The kube-apiserver is exposed to worker nodes using a load balancer.

kube-apiserver는 로드 밸런서를 통해 워커 노드에 노출된다.

리더를 선출하는 절차는 각각 독립적이지만, Lease를 저장하는 경로는 etcd로 이어진다. controller-manager나 scheduler가 Lease 갱신을 요청하면 API 서버가 그 변경을 etcd에 저장한다. 따라서 etcd에서 처리가 늦어지면 Lease 갱신 응답도 늦어질 수 있다. Lease 갱신 실패만으로 etcd의 리더가 바뀌었다고 판단할 수는 없지만, 갱신 실패를 조사할 때 etcd의 상태까지 확인해야 하는 이유다.

03 / etcd의 처리 지연

etcd의 응답은 어디에서 늦어질 수 있을까

etcd는 여러 구성원이 같은 변경 기록에 합의하도록 Raft를 사용한다. 리더가 현재 임기(term)에 새로 받은 변경을 로그에 추가하고 다른 구성원에게 복제하면, 자신을 포함한 과반수에 기록된 뒤 그 변경을 확정할 수 있다. 모든 구성원이 응답할 때까지 기다리는 방식은 아니다.

Raft 논문 · 현재 임기의 로그 확정 (§5.4.2, 본문 8쪽)

… an entry from its current term is committed once that entry is stored on a majority of the servers.

… 현재 임기의 항목은 서버 과반수에 저장되면 확정된다.

예를 들어 투표 구성원이 세 개인 안정된 클러스터라면 과반수는 둘이다. 리더와 다른 한 구성원이 제때 기록하면 나머지 하나가 늦어도 합의를 진행할 수 있다. 반면 리더와 나머지 구성원 사이의 통신이나 여러 구성원이 함께 사용하는 자원이 지연되면, 과반수의 응답을 모으는 과정도 늦어질 수 있다. 여기서 세 개라는 수는 동작을 설명하기 위한 예시다.

변경 기록은 메모리에 복사하는 것으로 끝나지 않는다. etcd는 복구에 필요한 기록을 WAL(Write-Ahead Log)에 남기며, 저장 장치에 기록을 반영하는 동기화 작업도 필요하다. 합의가 끝나는 시간에는 구성원 사이의 통신 지연과 디스크 기록 지연이 모두 영향을 준다. 전송할 데이터가 적더라도 이 기다림이 길어지면 작은 상태 갱신도 늦어질 수 있다.

etcd v3.7 문서 · 합의 지연과 디스크 I/O

Consensus performance, especially commit latency, is limited by two physical constraints: network IO latency and disk IO latency.

합의 성능, 특히 커밋 지연은 네트워크 I/O 지연과 디스크 I/O 지연이라는 두 가지 물리적 제약을 받는다.

Raft의 리더는 다른 구성원에게 주기적으로 heartbeat를 보낸다. 일정 시간 동안 리더의 메시지를 받지 못한 구성원은 새 선거를 시작할 수 있다. 그런데 메시지가 늦었다는 사실만으로 네트워크가 끊겼다고 판단할 수는 없다. etcd의 디스크 기록이 늦거나 프로세스가 CPU를 충분히 할당받지 못한 경우에도 heartbeat 처리가 지연될 수 있다.

etcd v3.7 FAQ · heartbeat가 늦어지는 이유

Before the leader sends heartbeats attached with metadata, it may need to persist the metadata to disk.

리더는 메타데이터가 포함된 heartbeat를 보내기 전에 해당 메타데이터를 디스크에 기록해야 할 수 있다.

따라서 heartbeat 지연을 조사할 때는 네트워크뿐 아니라 디스크 기록과 CPU 할당 지연도 함께 살펴봐야 한다. 어느 과정에서 지연됐는지 좁히려면 같은 시간대의 로그와 VM 지표를 맞춰 봐야 한다.

04 / 가설이 달라진 이유

I/O는 줄었는데 읽기와 쓰기는 더 오래 걸렸다

처음에는 네트워크 단절을 우선 의심했다. etcd의 heartbeat가 늦어지고 리더 유지에 문제가 생겼으며, API 서버 요청도 제한 시간 안에 끝나지 않았다. 구성원들이 잠시 서로 통신하지 못한 상황으로 볼 수 있었다.

하지만 연결이 끊겼다가 복구되는 구간이 뚜렷하지 않았다. 읽기 요청을 조율하는 과정과 요청 처리, heartbeat가 비슷한 시간대에 함께 지연됐다. 하나의 연결이 끊긴 것 외에 프로세스나 VM이 한동안 처리를 진행하지 못한 상황도 확인할 필요가 있었다.

Grafana의 디스크 지표에서는 읽기·쓰기 지연 시간이 늘어나는 동안 I/O 처리량이 급격히 줄었다. 완료된 I/O가 줄었다는 사실만으로 요청량도 줄었다고 볼 수는 없었다. 처리가 지연돼 완료되는 작업이 줄었을 가능성도 있었으므로, 처리량과 지연 시간을 함께 봤다.

이 패턴만으로 저장 장치의 문제를 확정하거나 부하와 자원 경합을 배제할 수는 없었다. 그래서 etcd가 있는 VM에만 나타나는 현상인지 비교했다.

Kubernetes에서 다른 역할을 맡은 VM에서도 같은 시간대에 비슷한 읽기·쓰기 지연이 보였다. 이 점은 etcd만의 부하로 설명하기 어려웠다. 여러 VM이 공유하는 호스트나 가상 디스크, 스토리지와 네트워크 경로를 확인할 이유가 생겼다.

그래서 우선 확인할 가설을 네트워크 단절에서 여러 VM에 걸친 일시적인 처리 정체로 바꿨다. 호스트가 VM에 CPU를 제때 할당하지 못한 경우, VM 자체가 잠시 멈춘 경우, 가상 디스크나 스토리지 경로가 지연된 경우가 모두 가능했다. 네트워크도 후보에서 제외하지 않았다.

남아 있는 로그와 지표를 시간대별로 맞추면 VM에서 보이는 I/O 지연, etcd의 처리 지연, API 서버의 시간 초과, 리더 갱신 실패가 이어지는 흐름을 재구성할 수 있었다. 다만 지표의 수집 간격과 남은 로그만으로 각 사건의 선후관계를 정밀하게 확정하기는 어려웠다. 이 흐름은 인프라 쪽 지연이 상위 구성 요소에 영향을 줬다는 가설을 뒷받침하는 근거로 정리했다.

VM의 디스크 지연과 etcd 내부의 처리 지연은 측정하는 위치가 다르다. 이 둘을 더 세밀하게 연결하려면 WAL 동기화, 내부 데이터베이스 반영, 구성원 사이의 통신 시간을 나눠 볼 수 있다. 아래는 같은 유형의 지연을 조사할 때 참고할 지표이며, 이 사례에서 확인한 측정 결과는 아니다.

etcd v3.7 문서 · 디스크 지표

High disk operation latencies (wal_fsync_duration_seconds or backend_commit_duration_seconds) often indicate disk issues. It may cause high request latency or make the cluster unstable.

디스크 작업 지연 시간(wal_fsync_duration_seconds 또는 backend_commit_duration_seconds)이 길면 디스크 문제를 나타내는 경우가 많다. 이로 인해 요청 지연이 커지거나 클러스터가 불안정해질 수 있다.

etcd v3.7 문서 · 네트워크 지표

Round-Trip-Time histogram between peers.

구성원 사이의 왕복 통신 시간을 기록한 히스토그램.

지연 위치를 좁힐 때 참고할 etcd 지표

표를 좌우로 밀어 비교할 수 있습니다.

지표측정하는 구간
etcd_disk_wal_fsync_duration_secondsWAL 기록을 저장 장치에 동기화하는 시간
etcd_disk_backend_commit_duration_seconds내부 데이터베이스의 변경을 디스크에 반영하는 시간
etcd_network_peer_round_trip_time_secondsetcd 구성원 사이의 왕복 통신 시간

각 지표는 시간의 분포를 제공한다. VM의 디스크 지연과 같은 값으로 취급하거나, 한 지표의 상승만으로 원인을 확정할 수는 없다.

05 / 조사 내용 전달

확인한 사실과 추가로 확인할 내용을 나눴다

Kubernetes와 VM 안에서 보이는 기록만으로는 호스트의 스케줄링이나 가상 디스크 대기열, 실제 스토리지의 상태까지 확인할 수 없었다. 접속할 수 있는 범위와 변경할 수 있는 범위도 달랐다. 지연이 시작된 위치를 더 좁히려면 호스트와 스토리지의 기록이 필요했다.

보고서에 구분해서 담은 내용

표를 좌우로 밀어 비교할 수 있습니다.

구분정리한 내용
확인한 사실이전 프로세스는 리더 갱신 실패 뒤 종료됐다. 같은 장애 시간대에 API 서버와 etcd의 처리 지연이 나타났다.
함께 나타난 현상서로 다른 역할의 VM에서 읽기·쓰기 지연이 늘고 I/O 처리량이 줄었다.
우선 확인할 가설여러 VM에 영향을 준 인프라의 일시적 정체가 etcd와 API 서버, 리더 갱신을 지연시켰을 가능성
추가 확인 대상해당 VM들이 공유한 자원과 그 시간대의 호스트·스토리지·네트워크 기록

접속 당시에는 서비스 경로에서 문제가 확인되지 않았다. 추가로 확인해야 할 대상은 VM에 자원을 할당하는 호스트와 스토리지·네트워크 경로였다. 근거 없이 구성 요소를 재시작하거나 설정을 바꾸는 것으로는 이 가설을 확인할 수 없었다.

추가로 확인할 항목에는 해당 VM들이 호스트나 스토리지 경로를 공유했는지, 장애 시간대에 VM 일시 정지나 CPU 할당 지연이 있었는지를 담았다. 가상 디스크와 스토리지의 대기·전환·점검 기록도 확인 대상이었다. 공통 네트워크 경로에서 지연이 발생했을 가능성도 포함했다.

남은 기록으로 확인한 것은 리더 갱신 실패와 프로세스 종료, 같은 시간대에 나타난 API 서버와 etcd의 지연이었다. 지연이 시작된 위치는 추가 확인이 필요했다. 확인한 사실과 추가 확인이 필요한 내용을 구분하고, 장애 시간대와 영향을 받은 VM을 함께 보고서로 정리해 프로젝트 매니저를 통해 인프라 담당자에게 전달했다.