Uptime Kuma가 WordPress의 이미지 URL을 검사하다가 반복해서 404를 보고했습니다. 평소에는 존재하는 파일이었고, 감시 요청에는 Cloudflare 캐시를 우회하는 헤더도 넣어 두었습니다. 처음에는 특정 노드에서 Ceph 마운트가 빠진 것이 아닌가 생각했습니다.
조사를 이어가니 요청이 실패하는 지점은 파일 저장소보다 앞에 있었습니다. Gateway 프록시의 헬스체크가 차단되고, 재시작한 프록시에 라우팅 설정이 전달되지 않는 흐름이 확인됐습니다. 여기에 별도로 살펴본 NetBird와 Cilium의 iptables 규칙 순서 문제도 있었습니다. 10월 6일에 시작한 복구와 재부팅 검증은 한국 시간으로 10월 7일 00:22에 마쳤습니다.
캐시를 우회한 404와 프록시의 상태 확인하기
감시 대상은 WordPress의 업로드 디렉터리에 있는 실제 이미지였습니다. 이번 환경에서는 특정 요청 헤더가 있을 때 캐시를 우회하도록 Cloudflare 규칙을 설정해 두었습니다. 같은 조건의 요청과 프록시 상태를 대조하면서, 정상 캐시가 장애를 가리지 않도록 확인했습니다.
핵심 단서는 프록시가 반환한 no matching route와 no config loaded였습니다. WordPress가 파일을 찾지 못한 경우와, 프록시가 WordPress로 보낼 경로를 갖고 있지 않은 경우는 같은 404로 보일 수 있습니다. 먼저 어느 계층이 이 응답을 만들었는지 구분해야 했습니다.
브라우저·감시 요청
→ Cloudflare
→ Cloudflare Gateway 프록시
→ WordPress Service
→ WordPress Pod의 uploads
Cilium: 클러스터 내부 네트워크·정책 처리
NetBird: 노드 간 네트워크 구성에 참여프록시의 연결 상태도 나눠 봤습니다. Cloudflare edge에 연결돼 있다는 사실만으로 애플리케이션 라우팅까지 준비됐다고 볼 수는 없었습니다. 실제로 터널 연결은 성립했지만 /readyz가 503과 no config loaded를 반환하는 Pod가 있었습니다.
| 관찰한 응답 | 이번 장애에서 확인한 의미 |
|---|---|
| 404, no matching route | 프록시가 요청에 맞는 라우팅을 찾지 못함 |
| /healthz 200 | 인터페이스 수정 후 health 검사 통과 |
| /readyz 503, no config loaded | 프록시의 라우팅 설정은 여전히 준비되지 않음 |
인터넷은 유선인데 헬스체크는 Wi-Fi IP에서 출발했습니다
문제가 발생한 노드는 유선과 Wi-Fi 인터페이스를 함께 가지고 있었습니다. 인터넷 기본 경로는 유선을 사용했습니다. 하지만 같은 노드의 Pod로 향하는 경로에서는 다른 출발 IP가 선택됐습니다. 실제 Gateway 헬스체크 패킷은 Wi-Fi IP에서 출발하고 있었습니다.
당시 Cilium의 인터페이스 목록은 eth+ enp+ wt+ bond+였습니다. VM의 유선 인터페이스는 포함됐지만, 물리 노드의 wlp로 시작하는 Wi-Fi 인터페이스는 빠져 있었습니다. 그 결과 VM의 출발 IP는 host identity로 인식됐고, 문제 노드의 Wi-Fi IP는 외부 트래픽으로 분류돼 헬스체크가 차단됐습니다.
기본 경로의 우선순위만 살펴서는 이 차이를 알기 어려웠습니다. 목적지가 로컬 Pod일 때는 인터넷 기본 경로를 따라가지 않기 때문입니다. 이 문제에서는 해당 Pod IP를 목적지로 경로를 조회하고, 실제 패킷의 출발 IP와 Cilium의 인식을 함께 보는 것이 필요했습니다.
ip route get <해당 노드의 Pod IP>
# 출력의 dev와 src를 확인합니다.
# 인터넷 기본 경로의 우선순위만으로 probe의 출발 IP를 단정하지 않습니다.노드의 kubelet
→ 로컬 Pod 경로에서 Wi-Fi 출발 IP 선택
→ Cilium이 해당 IP를 host로 인식하지 못함
→ Gateway 헬스체크 차단
→ 프록시 재시작
→ 라우팅 설정 누락 시 404설정 원본과 실행 중인 Cilium을 함께 맞추기
해결 설정은 RKE2 서버의 기존 Cilium manifest에서 관리했습니다. 별도 노드 설정을 추가하는 대신 인터페이스 목록에 eno+와 wlp+를 포함해 다음과 같이 정리했습니다.
eth+ enp+ eno+ wt+ bond+ wlp+설정 파일을 바꾼 뒤에는 실행 중인 Cilium이 새 설정을 읽었는지 다시 확인했습니다. RKE2 agent를 재시작했어도 기존 Cilium Pod가 그대로 살아 있어 이전 설정을 쓰는 상태가 남아 있었기 때문입니다.
문제 노드의 Cilium Pod를 재생성한 뒤 Wi-Fi IP가 host identity로 인식되는 것을 확인했습니다. Gateway의 /healthz는 200으로 회복됐습니다. 다만 이때도 /readyz는 설정을 받지 못해 503이었습니다. 헬스체크를 복구하는 것과 라우팅 설정을 복구하는 것은 각각 처리해야 했습니다.
프록시에 라우팅 설정을 다시 전달하기
프록시는 실행만 되면 라우팅할 수 있는 구조가 아니었습니다. 컨트롤러가 전달한 설정이 있어야 요청을 올바른 Service로 보낼 수 있었습니다. 초기 복구에서는 컨트롤러가 설정을 다시 보내도록 했고, 이후 재부팅 검증 중 나타난 다른 프록시의 설정 누락도 확인했습니다.
그 사례에서는 컨트롤러 로그에 이전 Pod IP로 설정을 보내려던 흔적이 있었고, 새 프록시 두 개 중 하나만 대상으로 잡힌 상태였습니다. 당시 현재 DNS 조회 결과에는 두 프록시가 모두 나왔습니다. EndpointSlice의 임시 메타데이터 변경으로 재동기화를 유도하자 두 프록시에 설정이 전달됐고, 해당 Pod는 재시작 없이 Ready와 /readyz 200을 회복했습니다. 임시 메타데이터는 제거했습니다.
확인된 것은 새 프록시의 설정 누락과 재전달 후 복구입니다. Pod 교체 시 DNS 가시성이나 캐시 타이밍이 영향을 줬을 가능성은 있지만, 정확한 과거 DNS 응답까지 확보한 것은 아닙니다. 컨트롤러의 이 동기화 문제를 영구적으로 수정한 작업도 아니므로, 향후 프록시 교체 때는 설정 수신 상태를 계속 확인해야 합니다. 당시 확인한 구현은 Gateway Controller의 proxy syncer입니다.
NetBird의 conntrack 예외와 Cilium 규칙 순서
같이 나타난 Redis DNS·health 실패는 Wi-Fi 출발 IP 문제로 한꺼번에 설명할 수 없었습니다. DNS 요청이 정상 Pod IP에서 출발한 사례도 있었고, 다른 노드에서도 지연이 관찰됐습니다. 네트워크 규칙을 별도로 살펴볼 필요가 있었습니다.
우리 구성에서는 NetBird 관련 conntrack 예외가 Cilium의 NOTRACK 처리보다 먼저 실행돼야 SNAT 경로가 유지됐습니다. 그런데 Cilium이 자신의 체인을 앞에 삽입하면서 예외 규칙보다 먼저 실행되는 순서가 만들어졌습니다.
이 환경에서는 prepend-iptables-chains=false를 적용해 Cilium 체인이 뒤에 배치되도록 했습니다. 이 옵션은 Cilium 1.19.6 소스에 있는 공식 옵션입니다. 다른 테이블의 규칙 순서에도 영향을 주는 설정이므로, 적용 후 실제 통신과 규칙 순서를 함께 확인했습니다.
세 master의 설정 원본을 일치시키고 Cilium 10개를 순차 재시작했습니다. OCI 네 노드에서 예외 규칙이 Cilium보다 앞에 유지되는 것을 확인했고, 기존 예외 규칙 관리 타이머는 10분 간격으로 유지했습니다. 이 값은 이번 NetBird·Cilium 조합에서 검증한 설정입니다.
실제 재부팅까지 확인한 결과
마지막에는 worker 한 대와 control-plane 한 대를 실제로 재부팅했습니다. 부팅 ID 변경을 확인했으며, 재부팅 뒤 DNS·네트워크 검사는 각 45개씩 총 90개가 모두 통과했습니다.
| 확인 항목 | 결과 |
|---|---|
| 재부팅 전 30분간 Redis DNS·health 복합 검사 | 1,431회, 실패 0회 |
| 재부팅 후 두 노드의 DNS·네트워크 검사 | 90개 통과 |
| control-plane 재부팅 중 캐시 우회 HTTP 검사 | 30회, 실패 0회 |
| 최종 실행 중인 Pod | 216개 모두 Ready |
| Cilium·iptables·etcd 상태 | 정상 확인 |
control-plane이 종료되는 동안 Redis 검사에서는 일시적인 실패 세 건이 발생했고, 이후 회복됐습니다. 재부팅 전 성공한 복합 검사 중에도 약 5초가 걸린 표본이 하나 있었으며, 그 표본에서 DNS와 health 중 어느 작업이 느렸는지는 구분되지 않았습니다. 이후 분리해서 시간을 잰 검사에서는 1초를 넘는 표본이 없었습니다. 전체 재부팅 과정에서 단 한 번도 끊기지 않았거나, 장기적인 간헐 장애까지 모두 없어진 것을 증명할 결과는 아닙니다.
존재하는 이미지가 간헐적으로 404를 반환한다면 파일 존재 여부와 함께, 요청을 받은 프록시가 실제 라우팅 설정을 가지고 있는지도 볼 필요가 있습니다. 이번에는 출발 IP, Cilium identity, 프록시 readiness, 규칙 순서를 차례로 확인한 것이 복구로 이어졌습니다.

답글 남기기