Argo CD Degraded — progressDeadlineSeconds 만료

Kubernetes5분조회 –

Argo CD 콘솔에서 앱 하나가 HEALTH: Degraded 로 떠 있었습니다. 그런데 서비스는 멀쩡히 돌고 사용자 쪽에서 이상하다는 얘기도 없었습니다.

Argo CD 는 Git 에 선언해 둔 상태를 클러스터에 계속 맞춰 주는 GitOps 배포 도구고, 배포만 하는 게 아니라 배포한 앱이 지금 정상인지도 콘솔에 같이 표시해 줍니다. 앱마다 초록 Healthy 나 빨간 Degraded 같은 배지로 붙습니다.

Argo CD 콘솔 목록 화면. 앱마다 초록색 Healthy 또는 빨간색 Degraded 배지가 붙어 있고 왼쪽에 헬스 상태별 개수가 나온다
Argo CD 콘솔의 헬스 표시 — 초록 Healthy, 빨강 Degraded
(ApplicationSets 목록 화면. 출처: Argo CD 공식 문서, Apache-2.0)

그래서 배포가 잘 끝났는지는 대개 이 표시로 봅니다. 파드를 직접 세어 보지 않고 콘솔이 초록이면 됐다고 보게 되는데, 이번엔 그 표시가 빨간색이었습니다.

Degraded 옆에 붙은 메시지는 이랬습니다.

ProgressDeadlineExceeded: ReplicaSet "my-app-xxxx" has timed out progressing.

새 ReplicaSet 이 정해진 시간 안에 진행을 못 끝냈다는 뜻입니다. 이 앱은 Deployment 가 아니라 Argo Rollouts 의 Rollout 으로 배포합니다. Argo Rollouts 는 새 버전을 한 번에 다 바꾸지 않고 카나리나 blue-green 으로 조금씩 넘기면서 지표를 보고 계속할지 되돌릴지 정해 주는 컨트롤러고, 쿠버네티스에 원래 없는 리소스라 CRD(사용자가 추가하는 리소스 종류)로 등록해서 씁니다. 그래서 이 메시지도 기본 컨트롤러가 아니라 Rollouts 쪽에서 나온 것입니다.

지금 상태부터 확인 — 파드는 정상

메시지가 “has timed out” 이라 배포가 지금도 안 끝나고 매달려 있는 줄 알고, 그것부터 확인했습니다.

kubectl -n <ns> get rs <rs> \
  -o custom-columns=NAME:.metadata.name,DESIRED:.spec.replicas,READY:.status.readyReplicas,AVAILABLE:.status.availableReplicas

DESIRED == READY == AVAILABLE. 원하는 개수만큼 떠 있고 전부 준비돼서 서비스에 들어가 있습니다. 파드 쪽에는 손댈 게 없으니 남는 건 저 메시지가 어디서 나오고 왜 안 지워지느냐입니다.

원인 — 안 지워진 과거 타임아웃 기록

메시지를 남기는 쪽은 Argo Rollouts 컨트롤러라, 새 ReplicaSet 이 progressDeadlineSeconds(기본 600초) 안에 진전을 못 하면 컨트롤러가 Rollout 의 status.conditions 에 ProgressDeadlineExceeded 를 기록합니다.

여기까지는 정상 동작이고 문제는 그다음입니다. 원인이 사라지고 파드가 전부 정상으로 돌아와도 그때 남긴 조건이 자동으로 지워지지 않습니다. Argo CD 는 Rollout 의 conditions 를 읽어서 헬스를 판정하니, 오래된 조건 하나 때문에 계속 Degraded 로 표시된 겁니다. 지금 도는 건 멀쩡한데 과거 기록만 남아서 보이던 현상이었습니다.

조건을 직접 열어 보면 그 기록이 그대로 남아 있는 게 보입니다.

kubectl -n <ns> get rollout <ro> \
  -o jsonpath='{range .status.conditions[*]}{.type} {.status} {.reason} -> {.message}{"\n"}{end}'

lastTransitionTime 을 같이 보면 그 조건이 언제 붙었는지 나오고, 며칠 전 날짜면 지금 문제가 아니라 남아 있는 기록입니다.

조치 — 파드는 그대로 두고 상태만 재평가

고쳐야 할 게 기록뿐이라 파드나 ReplicaSet 을 다시 만들 이유가 없습니다. Rollout 의 메타데이터에만 의미 없는 어노테이션을 하나 답니다. spec.template 을 안 건드리니 파드는 교체되지 않습니다.

kubectl -n <ns> patch rollout <ro> --type=merge \
  -p "{\"metadata\":{\"annotations\":{\"touched-at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}}}"

컨트롤러가 이 변경을 감지해 조건을 다시 평가하고 오래된 ProgressDeadlineExceeded 가 정리되면서 Healthy 로 돌아오고, 돌고 있는 파드는 하나도 안 건드립니다.

검색하면 이미지 태그를 바꿨다 되돌리는 식으로 spec.template 을 건드려 다시 평가하게 하는 방법도 나옵니다. 그것도 되기는 하지만 파드가 전부 교체됩니다. 상태 표시만 고치면 되는 상황에서 운영 중인 파드를 갈아 끼울 이유는 없습니다.

기동이 느린 앱이면 — progressDeadlineSeconds 상향

위 조치는 한 번 타임아웃이 났다가 회복된 흔적을 지우는 것이라, 앱이 원래 느려서 정상 배포인데도 600초를 넘긴다면 기록이 계속 다시 생기니 그때는 데드라인 자체를 올려 둡니다.

spec:
  progressDeadlineSeconds: 1200

기본 600초를 늘려 두면 정상 배포가 데드라인에 안 걸리니, 늦게 뜨는 게 정상인 앱에서는 이쪽이 맞습니다.

콘솔의 헬스는 파드 상태가 아니라 conditions

Argo CD 가 Degraded 를 띄우면 지금 고장 났다는 뜻으로 읽힙니다. 하지만 그 표시는 Rollout 의 conditions 를 읽어서 나오는 값이라, 실제 파드 상태와 따로 놀 수 있습니다.

콘솔은 빨간데 서비스는 멀쩡하면, 먼저 DESIRED == READY == AVAILABLE 로 지금 상태를 확인하고 그다음 conditions 를 열어 그 메시지가 언제 것인지 봅니다. 과거 기록이면 메타데이터만 건드려 다시 평가하게 하면 되고, 지금도 계속 생기는 기록이면 그때 데드라인을 올립니다.

참고

  1. 불러오는 중