새 머신을 세팅하는 중에 Docker 레지스트리 연결 확인 단계에서 계속 막혔고 Connection refused 가 나왔다. setup.sh 에 적혀 있는 10.0.1.20:5000 으로 붙는데도 안 되는 상황이었다.
Table of contents
Open Table of contents
배경 — 네트워크를 의심한 시간
처음에는 네트워크 문제로 보고 방화벽인지 라우팅인지 접속 경로가 필요한지를 차례로 확인했다. 확인해 보니 전부 아니었고 같은 망의 다른 서비스는 멀쩡히 붙고 있었다.
그래서 레지스트리가 도는 서버에 직접 들어가서 확인해 봤다. docker ps 에서 컨테이너는 정상으로 떠 있는데 ss -tlnp 에 5000 을 듣고 있는 프로세스가 아무것도 없었다.
원인 — 게시하지 않는 포트
docker inspect 로 확인하니 -p 5000:5000 에 해당하는 게시 설정 자체가 아예 없었다. Docker 공식 문서대로 브리지 네트워크의 컨테이너 포트는 호스트 밖에서 접근할 수 없고 --publish 를 줘야 열린다. 그러면 밖에서 그 포트로 붙을 수 없는 것이 당연한 상태였다.
그러면 이 레지스트리를 어떻게 쓰는지를 찾아야 했다. nginx 설정을 보니 registry. 도메인으로 받아 proxy_pass 로 컨테이너에 넘기고 있었고 같은 서버의 다른 서비스들도 전부 같은 패턴이었다.
조치 — 인프라의 일관된 패턴
즉 내가 설정에 적은 내부 주소가 틀린 것이었고 도메인을 써야 했다. 어디선가 옛 설정을 복사해 왔거나 처음 만들 때 그렇게 쓴 뒤 나중에 프록시가 붙은 것으로 보인다.
두 가지 방법이 있었다. 컨테이너에 -p 를 추가해 내부 주소를 살리는 것과 설정의 주소를 도메인으로 바꾸는 것이다. 뒤쪽을 골랐다. 이유는 같은 서버의 다른 서비스가 전부 프록시 패턴이라 하나만 예외로 두면 다음 사람이 또 헤매서다. 대신 레지스트리 접근이 nginx 에 의존하게 되는 것을 감수했다. 바꾸니 두 가지가 한꺼번에 해결됐다. 공개 도메인이라 접속 경로가 따로 필요 없어졌고 프록시가 정식 인증서로 종단하므로 daemon.json 의 insecure-registries 도 필요 없어졌다.
검증 — 전부 고치고 다른 머신까지
그다음 grep -r 10.0.1.20 으로 이 주소를 쓰는 곳을 전부 찾아 도메인 기준으로 바꿨다. setup.sh 의 연결 확인과 build.sh 의 이미지 값과 crontab 과 ~/.docker/config.json 넷이 있었다.
하나만 고치면 결국 다른 데서 또 막히게 된다. 같은 인프라를 쓰는 다른 머신들도 확인했더니 일부는 이미 도메인을 쓰고 일부는 내부 주소를 쓰고 있어서 갈라져 있었고 전부 맞췄다.
주의 — 오류 종류가 가르는 방향
이 건에서 정리한 것은 연결 거부라는 메시지가 정확히 무엇을 뜻하는지였다. 응답이 없는 것은 패킷이 안 가거나 응답이 없는 경우이고 거부는 도달했는데 아무도 안 듣고 있는 경우다.
거부면 네트워크가 아니라 대상 쪽이므로 방화벽을 뒤지는 대신 서버에서 듣고 있는지부터 봐야 한다. 오류 종류를 보고 거부면 리슨 여부를 확인하며 안 듣고 있으면 어떻게 쓰는지를 찾아 프록시가 있으면 그 주소를 쓰고 그 주소를 쓰는 곳을 전부 고친 뒤 다른 머신까지 확인하는 순서가 됐다.
정리
- 연결 거부는 아무도 안 듣고 있다는 뜻이다
- 네트워크가 아니라 대상 쪽을 본다
- 컨테이너가 호스트 포트를 게시 안 하고 프록시로만 노출될 수 있다
- 그러면 직접 접속하도록 만든 것이 아니므로 도메인을 쓴다
- 도메인 쪽이 접속 경로도 비보안 설정도 필요 없어 더 간단하다
- 그 주소를 쓰는 곳을 전부 고친다
- 같은 인프라의 다른 머신도 확인한다
- 인프라에 일관된 패턴이 있으면 그것을 따른다