호스트 하나, 두 개의 워크로드, 그리고 이들을 나누는 요구 사항이 있습니다. 상용 VPN을 통해 나가야 하는 다운로드 클라이언트와, 호스트의 연결을 계속 사용해야 하는 관리 인터페이스입니다. VPN 클라이언트를 호스트에 설치하면 요구 사항이 즉시 무너집니다. 두 워크로드가 하나의 출구를 공유하게 되기 때문입니다. 실제로 작동하는 패턴은 터널을 자체 컨테이너로 옮기고, 그것이 필요한 워크로드만 그 컨테이너의 네트워크 네임스페이스에 참여하게 하는 것입니다. 나머지는 평소의 Docker 경로를 유지하며, 이어지는 세부 사항은 네임스페이스를 누가 소유하는지의 결과입니다.

공유 네임스페이스가 규칙을 바꾸는 이유

다른 컨테이너의 네임스페이스에 참여한 컨테이너는 자체 네트워크 스택을 받지 못합니다. 참여한 컨테이너와 동일한 인터페이스, 동일한 라우팅 테이블, 동일한 loopback을 봅니다. 그래서 이 패턴이 유용합니다. 워크로드는 평소대로 이름을 해석하고 연결하며, 아래의 터널은 애플리케이션도 모르게 트래픽을 가로챕니다. 대가는 네트워크 관련 설정마다 소유자가 하나가 되며, 참여하는 컨테이너가 그 소유자가 아니라는 점입니다.

이 소유권 규칙은 대부분의 구성 질문에 질문이 나오기 전에 답합니다. 설정이 네트워크 스택에 속하면 사이드카 쪽입니다. 프로세스를 설명하는 설정이라면 워크로드에 남습니다.

loopback은 사람들을 놀라게 하는 부분입니다. 참여하는 컨테이너 내부에서 localhost는 사이드카의 loopback으로 해석되며 자기만의 사설 주소로 해석되지 않습니다. 따라서 격리를 기대하며 127.0.0.1에 바인딩하는 서비스는 이제 그 주소를 터널 컨테이너와 공유합니다.

네임스페이스를 소유한 컨테이너에서 포트를 게시하세요

포트 매핑은 수신 프로세스가 다른 컨테이너에서 실행되더라도 네임스페이스 소유자의 것입니다. 매핑은 사이드카에 선언하고, 워크로드는 자체 프로세스 구성을 그대로 유지하게 두세요. 참여하는 서비스의 매핑은 효력을 낼 수 없습니다. 엔진이 거부하든, 컨테이너가 시작되는 방식에 따라 조용히 무시하든 마찬가지이며, 그래서 방금 전까지 접근 가능했던 인터페이스가 패턴을 적용하는 순간 사라집니다.

services:
  gluetun:
    image: qmcgaw/gluetun
    cap_add: [NET_ADMIN]
    ports: ["8080:8080"]
  client-vpn:
    image: example/client
    network_mode: "service:gluetun"
  client-direct:
    image: example/client
    ports: ["8081:8080"]

이 블록을 세 개의 서비스가 아니라 세 개의 결정으로 읽으세요. 사이드카가 터널과 게시된 포트를 소유하고, 참여하는 클라이언트는 네트워크를 전혀 선언하지 않으며, 직접 클라이언트는 기본 네트워크에 자체 매핑을 유지합니다. 사이드카가 인터페이스를 만들고 경로를 바꿀 수 있는 것은 NET_ADMIN 기능 덕분이며, 이 컨테이너가 자신이 서비스하는 워크로드보다 더 많은 권한을 필요로 하는 이유입니다.

직접 연결로 남는 서비스는 자체 네트워크를 유지합니다

터널을 통과해서는 안 되는 워크로드는 기본 브리지나 자체 사용자 정의 네트워크를 자체 게시 포트와 자체 출구와 함께 유지합니다. 두 컨테이너가 이름으로 대화할 수 있도록 사이드카 네트워크에 연결하고 싶어지지만, 그 한 단계가 트래픽을 조용히 터널로 옮깁니다. 서로 도달해야 한다면 별도의 사용자 정의 네트워크를 만들고 둘 다 거기에 연결하며, 사이드카만 외부로 나가는 유일한 경로로 남기세요.

두 질문을 분리하세요. 어떤 컨테이너가 서로 도달할 수 있는지, 그리고 어느 쪽이 어디로 나가는지입니다. 이 둘을 합치는 것이 이 설계가 다시 호스트 전체 VPN으로 돌아가는 가장 흔한 방법입니다.

첫 한 시간을 위한 장애 매트릭스

대부분의 첫 시도는 네 가지 방식 중 하나로 실패하며 각기 다른 원인이 있습니다. 이 순서로 확인하세요. 처음 두 가지가 거의 모든 보고를 설명하기 때문입니다.

SymptomLikely cause
Web UI unreachable after the changePort mapping declared on the joining container instead of the sidecar
Both containers show the provider's exit addressThe direct service was attached to the sidecar network
Internal names stop resolvingResolver settings live on the host, not in the shared namespace
Health check fails but the app worksThe check probes localhost, which now means the sidecar

네 번째 행은 사람들이 애플리케이션 버그로 잘못 진단하는 항목입니다. 워크로드 자체의 loopback을 대상으로 작성된 프로브는 이제 터널 컨테이너를 검사하므로, 실패하거나 완전히 다른 서비스를 보고합니다.

호스트 이름 하나만 터널이 필요할 때

때로는 전체 터널이 과합니다. 내부 이름 하나는 기업 VPN을 통과해야 하지만 로컬 소켓이나 디스플레이 연결은 계속 작동해야 합니다. 컨테이너 내부의 전체 터널은 그 경로와 호스트로 향하는 모든 것을 깨뜨리므로, 필요한 접두사만 라우팅하세요. 클라이언트를 감싸는 분할 라우팅 래퍼가 그 일을 하며, 지정된 호스트를 터널로 보내고 나머지에는 이전 경로를 그대로 둡니다.

하나의 서비스에만 도달하면 되고 컨테이너의 모든 트래픽을 옮기려 하지 않을 때 이것을 선택하세요. 사이드카와는 다른 도구이며 다른 질문에 답합니다.

검증하고, 그다음 재시작에도 견디게 만드세요

각 컨테이너에 어느 주소에서 오는 것처럼 보이는지 물어보세요. 터널 쪽은 공급자의 주소를, 직접 쪽은 호스트의 출구를 출력해야 합니다. 같은 답이 나오면 직접 서비스가 잘못된 네임스페이스를 물려받았거나 호스트 수준 VPN이 여전히 모든 것을 가로채고 있다는 뜻이며, 이 두 가지 수정은 다릅니다. compose를 편집할 때마다 검사를 다시 실행하세요. 서비스 블록 하나를 복사하는 것만으로도 문제가 재발합니다.

나중에 잘 깨지는 세 가지가 있습니다. DNS는 네임스페이스 소유자를 따르므로 리졸버와 검색 도메인 설정은 호스트가 아니라 사이드카에 속합니다. 포트 포워딩 VPN은 재연결 시 수신 포트를 바꾸므로 공급자의 포워딩 포트와 컨테이너 쪽 수신 포트를 클라이언트 구성이나 포트 매핑에 다시 써야 하며, 보통 공급자의 API를 읽는 작은 업데이터가 이를 수행합니다. 그리고 localhost에 대해 작성된 모든 프로브는 이제 워크로드가 아니라 터널 컨테이너에 묻습니다.

패턴을 적어 두세요. 어느 컨테이너가 네임스페이스를 소유하고 어떤 포트가 그에 속하는지도 포함해서요. 다음 사람은 서비스를 추가하고, 잘못된 블록을 복사하고, 이 글의 첫 번째 장애를 재현할 것입니다.

관련 글