Incident Library

IL-0580 Recruit · 2019-12-18 (게시일)networkingaws

ConcourseCI 의 배포가 Amazon ECR 이름 해석 타임아웃으로 어떤 날에만 실패했다

Recruit Technologies 의 한 서비스는 컨테이너 환경에서 돌고, CI/CD 에 ConcourseCI 를 쓴다. CI 는 EC2 위에 docker-compose 로 띄운 단순한 구성이다. 2019년 어느 날 배포 중 docker build 가 캐시 이미지를 ECR 에서 pull 하다가 ECR 이름 해석의 i/o timeout 으로 실패했다. 다음 날 재시도하자 성공했지만, 며칠 뒤 같은 오류가 다시 났다.

ECR 이름은 ELB 를 가리키는 CNAME 으로 해석된다. ELB 는 요청량에 따라 유닛 수가 늘고 줄며, 늘면 응답의 A 레코드도 많아진다. 배포가 된 날은 A 레코드가 2개, 실패한 날은 4개 이상이었다. 엔지니어는 Go 의 이름 해석 버그를 다룬 이슈를 찾았고, CI 작업에서 쓰던 Docker 17.03.2-ce 가 그 이슈의 문제 버전인 go1.7.5 로 빌드된 것을 확인했다. Docker 를 업그레이드하려면 Concourse 를 업그레이드하거나 resource-type 을 바꿔야 해서 곧바로 할 수 없었다. 그러다 같은 이름을 조회한 응답이 개발자 컴퓨터에서는 218 바이트, CI 작업 안에서는 580 바이트인 것을 발견했고, CI worker 의 resolv.conf 에서 nameserver 를 바꾸자 배포가 다시 됐다.

이 조치는 임시였으므로 엔지니어는 크기 차이의 원인을 더 팠다. 패킷 캡처로 DNS 메시지 압축이 어딘가에서 풀리고 있다는 것을 알았고, 네트워크마다 캡처해 Concourse worker 안의 DNS Proxy 가 응답을 보낼 때 메시지를 압축하지 않는다는 것을 찾았다. 이 장애는 ECR 의 DNS 응답 크기가 때때로 바뀌는 것과 Concourse 의 내부 DNS Proxy 가 응답을 압축하지 않고 전달하는 것이 겹쳐, 당시 Go 에 있던 이름 해석 버그가 드러난 경우였다. 엔지니어는 Concourse 에 이슈와 PR 을 올렸고, 장애가 난 뒤 PR 을 올리기까지 한 달 넘게 걸렸다.

심각도
–
영향 범위
Deploys through the ConcourseCI pipeline failed on some days
영향 사용자
–
지역
ap-northeast-1
감지까지
–
복구까지
–

증상

배포 버튼을 누르자 ConcourseCI 위에서 도는 Docker 가 ECR 에서 이미지를 pull 하다가 실패했다. 이 서비스는 출시 뒤 이런 오류가 한 번도 없었고, 최근에 구성을 바꾼 일도 없었다.

배포 흐름은 git clone, docker build, docker push, 쿠버네티스 매니페스트 수정과 배포 순서였다. docker build 중 캐시로 쓸 이미지를 pull 하는 단계에서 다음 오류가 났다. Pulling xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/xxxxx:latest (attempt 3 of 3)... Error response from daemon: Get https://xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/v2/: dial tcp: lookup xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com on 172.18.0.3:53: read udp 10.254.0.122:48372->172.18.0.3:53: i/o timeout

엔지니어는 통신이 잠깐 불안정했다고 보고 재시도했지만 다시 실패했다. 밤이 늦었으므로 엔지니어는 Route53 이나 VPC 네트워크가 잠깐 불안정했다고 여기고 다음 날 다시 시도했고, 그대로 성공했다. 며칠 뒤 다시 배포하려 하자 같은 오류가 났다.

개발자 컴퓨터와 CI 작업 안에서 ECR 의 이름 해석을 직접 해 보면 성공했다. CI 는 GitHub Enterprise 에도 접속했는데, 그쪽의 이름 해석은 문제가 없었다.

dig 결과를 비교하자 ECR 이름이 ELB 를 가리키는 CNAME 으로 해석됐고, 배포가 된 날은 A 레코드가 2개, 안 된 날은 4개 이상이었다. 같은 이름을 개발자 컴퓨터에서 조회하면 MSG SIZE rcvd: 218 이었고, CI 작업 안에서 조회하면 MSG SIZE rcvd: 580 이었다.

DNS 메시지 압축을 어디서 풀고 있는지 찾는 데 시간이 오래 걸렸다. 엔지니어는 처음에 Docker 의 내부 DNS 를 의심해 libnetwork 구현을 읽었다. docker 와 dns 라이브러리에 패치를 넣어 Vagrant 로 재현 환경을 만들고, Concourse 와 Docker 가 만든 iptables 도 분석했지만 원인은 거기에 없었다.

증상 DNS 조회 실패이미지 풀 실패

원인

촉발
ECR 엔드포인트인 ELB 의 유닛이 늘어 DNS 응답에 담긴 A 레코드가 4개 이상이 된 날에 DNS 응답이 커졌다.

감지

엔지니어가 서비스를 배포하려고 배포 버튼을 눌렀을 때 CI 의 Docker 가 ECR 이름 해석 타임아웃 오류를 냈다.

조치

후속 조치

교훈

–

출처

관련 사례

내 클러스터에도 있는지 확인하기

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: CI 시스템, 컨테이너 레지스트리와 자격 증명, 클라이언트 쪽 DNS 리졸버와 캐시. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.