Redis ENOTFOUND — 원래 항상 나던 에러

데이터·관측성3분조회 –

로그인 상태나 잠깐 들고 있을 값은 서버 메모리에 두는 게 제일 간단하지만 그건 파드가 하나일 때고, 여러 개로 늘리면 요청이 어느 파드로 가느냐에 따라 값이 있기도 하고 없기도 합니다. 그래서 여러 파드가 같이 보는 저장소를 하나 두고 거기에 넣습니다. 이 서비스도 세션과 캐시를 그렇게 Redis 로 뺐고 Redis 자체는 직접 안 띄우고 AWS 가 대신 운영해 주는 ElastiCache 로 두고 씁니다.

그러니 Redis 가 안 붙으면 바로 티가 나야 하는 구조인데, 세션이 거기 있으니 로그인이 풀리고 캐시가 없으니 매번 DB 를 치면서 응답이 느려지니, 연결 실패 로그는 그냥 넘기고 지나갈 수 있는 종류가 아닙니다.

배포한 뒤로 파드가 재기동될 때마다 로그에 Redis 연결 실패가 찍히기 시작했습니다.

Error: getaddrinfo ENOTFOUND <old-redis>.cache.amazonaws.com
  errno: -3008, code: 'ENOTFOUND', syscall: 'getaddrinfo'

호스트 이름을 못 찾겠다는 DNS 에러지만, 정작 서비스는 멀쩡히 돌고 Redis 를 쓰는 기능도 다 정상인 채로 에러만 꾸준히 찍혔습니다.

삭제된 지 몇 달 된 엔드포인트 조회

에러에 찍힌 호스트 이름부터 확인해 보니 이미 삭제된 ElastiCache 클러스터였습니다. 몇 달 전에 다른 걸로 교체하면서 없앤 것이라 DNS 자체가 사라진 상태였고 그 이름을 찾으면 ENOTFOUND 가 나는 게 당연합니다.

남는 질문은 앱이 왜 그 죽은 엔드포인트를 아직도 찾느냐고, 답은 이 앱이 Redis 연결 방식을 설정으로 분기한다는 데 있었습니다.

설정 연결 대상
USE_REDIS_CLUSTER: true 클러스터 모드 Redis (실제 운영)
USE_REDIS_CLUSTER: false REDIS_HOST 의 단일 엔드포인트

운영은 USE_REDIS_CLUSTER: true 였으니 REDIS_HOST 에 적힌 옛날 ElastiCache 엔드포인트는 실제로는 안 쓰는 값입니다. 클러스터를 교체할 때 REDIS_HOST 만 정리를 빠뜨린 채 남아 있었던 겁니다.

초기화 때 안 쓰는 값 우선 조회

안 쓰는 값이면 조회할 일도 없어야 하지만, 앱 코드의 초기화 순서가 그렇지 않았습니다. USE_REDIS_CLUSTER: true 인데도 일부 코드가 초기화 시점에 REDIS_HOST, 그러니까 죽은 엔드포인트를 먼저 참조했습니다.

앱 시작
  → REDIS_HOST(죽은 ElastiCache) 먼저 조회 → ENOTFOUND (로그에 찍힘)
  → 클러스터 모드로 연결 → 성공 (서비스 정상)

에러는 매번 찍히지만 곧바로 진짜 Redis 로 붙으니 서비스는 멀쩡합니다. 이 ENOTFOUND 는 장애가 아니라 배포 이후로 계속 나던 노이즈였습니다.

뒤에 성공이 붙는 로그와 진짜 실패

이런 종류의 에러는 뒤에 성공이 이어지는지로 갈리는데, ENOTFOUND 직후에 Redis 연결 성공 로그가 붙어 있으면 실제 연결은 됐다는 뜻이라 서비스 영향이 없고, 성공 로그 없이 에러만 남아 있으면 그때는 진짜 연결 실패라 Redis 상태를 봐야 합니다.

이걸 구분하지 않으면 원래 늘 있던 로그를 보고 장애가 났다며 엉뚱한 데를 파게 됩니다.

고칠 곳은 인프라가 아니라 앱

인프라 쪽에서는 손댈 게 없습니다. 안 쓰는 ElastiCache 엔드포인트라 주소를 갱신해 봐야 의미가 없고, 고칠 자리는 앱입니다. USE_REDIS_CLUSTER: true 일 때 REDIS_HOST 를 보는 로직을 초기화에서 빼거나, 아예 REDIS_HOST env 를 배포 설정에서 지워 참조할 수 없게 하는 쪽이 근본 대응입니다.

인프라를 바꾸면 그걸 참조하던 설정도 같이 정리해야 하고, 안 쓰니까 남겨 둬도 된다고 두면 이렇게 몇 달 뒤에 없는 걸 찾는 로그로 튀어나옵니다.

  1. 불러오는 중