새 주소로 바꿔 배포했는데 상대 쪽 로그에는 여전히 옛 주소로 요청이 오고 있었다. 컨테이너 안에서 env | grep API_URL 을 찍어 보니 설정 자체는 새 주소로 들어가 있었다.
Table of contents
Open Table of contents
조사 — 실제로 나가는 주소
설정과 실제가 다르다는 뜻이므로 tcpdump -nn port 443 으로 나가는 패킷을 직접 잡아 봤다. 목적지 IP 를 dig 결과와 대조하니 옛 도메인이 가리키는 주소였다.
설정은 새 주소인데 실제로는 옛 주소로 가고 있었다. 설정값을 확인하는 것과 실제로 어디로 나가는지를 확인하는 것은 다른 일이었다.
원인 — 조용히 넘어간 대체 경로
옛 주소를 쓰는 자리를 컨테이너 안에서 grep -rn 으로 검색하니 config/channels.php 에 fallback_url 이 남아 있었다. 새 주소 호출이 실패하면 조용히 옛 주소로 넘어가는 구조였다.
그 전환을 알리는 로그가 Log::debug() 로 돼 있어서 아무도 보고 있지 않았다. 새 주소가 왜 실패하는지 확인하니 상대가 새 도메인에 중간 인증서를 안 붙인 문제였고 알려서 고쳐졌다.
판단 기준 — 대체 자체의 재검토
고치고 나서 대체 동작 자체가 맞는지를 다시 따져 봤다. 옛 주소는 곧 없어질 예정이라 대체로 남겨 두면 그날 요청이 전부 실패한다.
두 가지 방법이 있었는데 fallback_url 을 당장 지우는 것과 옛 주소가 없어지는 날짜까지 두되 로그를 warning 으로 올리고 전환 횟수를 CloudWatch 지표로 세는 것이다. 뒤쪽을 골랐다. 이유는 상대 인증서 문제가 재발하면 그 며칠은 대체가 실제로 요청을 살려서다. 대신 그 날짜에 지우는 것을 잊으면 그날 요청이 전부 실패한다는 것을 감수하고 달력에 걸었다.
주의 — 밖과 안이 다른 이름 해석
호스트의 dig 와 컨테이너 안 getent hosts 의 결과가 서로 달랐다. 컨테이너 쪽에 옛 항목이 캐시돼 있었고 재시작하고 나서야 맞았다.
밖에서 보면 정상인데 안에서는 다른 경우가 있으므로 실제로 요청을 보내는 자리에서 확인해야 한다. 잡아 보는 방법은 tcpdump 와 getent hosts 와 요청 로그와 스택 트레이스 넷을 상황에 따라 골라 썼다.
조치 — 도구 없이 코드로 남기기
운영 컨테이너에는 조사에 쓸 도구를 깔 수 없는 제약이 있었다. 이미지에 들어 있지도 않고 설치 자체가 막혀 있었다.
그래서 Guzzle 공식 문서대로 Middleware::mapRequest() 를 핸들러 스택에 얹어 나가는 요청의 getMethod() 와 getUri()->getHost() 와 getPath() 를 로그에 남기게 했다. 전체 주소를 안 남기고 호스트와 경로만 남긴 것은 질의 문자열에 값이 실릴 수 있기 때문이다. 도구를 못 쓰면 코드에서 남기면 되고 그것이 오히려 계속 남아서 나중에 더 쓸모 있었다.
정리
- 설정이 맞아도 실제로 나가는 곳이 다를 수 있다
tcpdump로 나가는 것을 잡아서 어느 주소인지 확인한다- 대체 주소가 있으면 조용히 그리로 간다
- 대체로 넘어간 것을 알리고 센다
- 곧 없어질 주소를 대체로 두지 않는다
- 어느 코드에서 부르는지 남기는 것이 가장 빠르다
- 컨테이너 안에서 확인하면 밖과 다를 수 있다
- 도구를 못 쓰면
mapRequest()미들웨어처럼 코드에서 남긴다