도커는 전에도 써봤다. 이미지 빌드하고 컨테이너 띄우고 포트 하나 열어서 접속되는 것까지.
그런데 이번에 컨테이너가 두 개가 되면서 질문이 하나 생겼다. Web 컨테이너에서 API 컨테이너를 부르려면 어떻게 하는가?
같은 서버 안에 있어도 그냥 되는 게 아니었다. 셋째 날에 배운 것들을 내가 이해한 대로 적어둔다.
격리
도커를 쓰는 이유로 보통 경량화와 이식성을 든다. 맞는 말인데 그 이식성의 대가로 따라오는 게 격리라고 이해했다.
컨테이너는 자기만의 네트워크 네임스페이스를 갖는다. 자기가 붙은 도커 네트워크에서 IP를 하나 받고 그 네트워크 밖은 모른다. 같은 호스트에 있어도 붙은 네트워크가 다르면 기본적으로는 서로 안 통한다.
그러니까 "같은 서버에 있으니 당연히 통하겠지"가 틀린 전제였다.
호스트 안을 들여다보면 인터페이스가 두 종류로 보인다.
eth0 → 호스트가 VPC에서 받은 진짜 랜카드
docker0 → 도커가 호스트 안에 만든 가상 스위치docker0이 컨테이너들이 붙는 가상 스위치다. 컨테이너마다 veth라는 가상 케이블 한 쌍이 생겨서 한쪽은 컨테이너 안에, 한쪽은 이 스위치에 꽂힌다.
컨테이너가 밖으로 나갈 때는 호스트를 거쳐 나간다. 도커 브리지가 NAT를 해준다.
이름이 겹치는 게 하나 있다. 네이버 클라우드에도 NAT Gateway가 있고 도커에도 NAT가 있는데 붙는 자리가 완전히 다르다. 도커 NAT는 호스트 안에서 컨테이너를 호스트 주소로 바꿔주는 일이고 Ncloud NAT Gateway는 그 호스트가 VPC 밖으로 나갈 때 걸리는 자리다. 요청 하나가 둘 다 통과한다.
사용자 정의 브리지
무엇인가. 내가 직접 만드는 도커 네트워크다. 도커를 깔면 기본 bridge가 하나 생기는데, 그것 말고 애플리케이션 단위로 따로 만드는 것이다.
언제 쓰나. 컨테이너 두 개 이상이 서로를 이름으로 불러야 할 때다. 기본 bridge는 컨테이너를 다 받아주기는 하는데 이름으로는 서로를 못 찾는다. IP를 직접 알아내서 써야 한다.
그 IP가 언제든 바뀐다는 게 문제다. 컨테이너를 지웠다 다시 띄우면 다른 주소를 받는다. Web의 설정 파일에 API의 IP를 적어두는 순간 그 설정은 다음 재시작까지만 유효해진다.
흐름. 네트워크를 하나 만들고 → 컨테이너를 띄울 때 --network로 거기에 붙이고 → 서로를 컨테이너 이름으로 부른다.
docker network create campus-net
docker run -d --name api --network campus-net api:1.0
docker run -d --name web --network campus-net web:1.0사용자 정의 브리지에는 도커 내장 DNS가 붙는다. 같은 네트워크에 있는 컨테이너 이름이 DNS에 등록되고 api:3000처럼 이름으로 부르면 해석된다. IP가 바뀌어도 이름은 안 바뀐다.
| 구분 | 기본 bridge | 사용자 정의 bridge |
|---|---|---|
| 생성 | 도커 설치할 때 자동 | 애플리케이션 단위로 직접 |
| 이름 해석 | 안 됨 | 내장 DNS가 컨테이너 이름을 해석 |
| 격리 | 여러 컨테이너가 한데 붙음 | 필요한 것만 골라서 연결 |
Compose를 쓰면 이걸 알아서 해준다. 서비스 이름이 그대로 DNS 이름이 된다. 그냥 되는 것처럼 보이지만 아래에서 네트워크를 하나 만들어준다.
포트 세 층
컨테이너끼리 통하는 것과 밖에서 들어오는 것은 다른 얘기다. 밖에서 들어오려면 포트를 열어줘야 한다.
이 과정에서 포트가 세 층으로 나왔다.
| 구분 | 예시 | 있는 곳 |
|---|---|---|
| Container Port | 80 (web), 3000 (api) | 컨테이너 안 프로세스가 실제로 듣는 포트 |
| Published Port | 30080 | 호스트가 받아서 컨테이너로 넘겨주는 자리 |
| SSH 터널 로컬 포트 | 127.0.0.1:30080 | 내 PC에서 요청을 넣는 자리 |
내 PC :30080 → (SSH 터널) → Docker VM :30080 → Web Container :80Published Port가 열리는 "호스트"는 도커 데몬이 있는 서버다. 관리 서버에서 명령을 쳐도 포트는 컨테이너가 도는 서버에 열린다. 명령을 치는 곳과 컨테이너가 도는 곳은 다르다.
그리고 Published Port를 열면 그건 호스트의 네트워크 인터페이스에 그대로 열린다. 앞 글에서 만든 ACG와 NACL이 여기서 다시 등장한다. 컨테이너 얘기인 줄 알았는데 다시 Subnet 얘기가 된다.
Overlay 네트워크
셋째 날에는 도커가 깔린 서버가 두 대가 됐다. Swarm으로 묶어서 Manager 한 대, Worker 한 대.
여기서 질문이 하나 더 생겼다. 브리지는 호스트 안에 만들어진 가상 스위치다. 그럼 KR-1 서버의 컨테이너와 KR-2 서버의 컨테이너는 어떻게 통하는가?
브리지로는 안 된다. docker0은 그 호스트 밖으로 나가지 않는다.
무엇인가. Overlay는 노드 사이에 VXLAN 터널을 뚫어서 물리적으로 다른 서버에 있는 컨테이너들을 하나의 네트워크에 있는 것처럼 만드는 것이다.
언제 쓰나. 호스트가 두 대 이상이고 그 위의 컨테이너들이 서로를 불러야 할 때다. 같은 Overlay에 붙어 있으면 어느 노드에 있든 서비스 이름으로 서로를 부른다.
그 터널이 지나가는 길은 UDP 4789다.
KR-1 노드 ←── VXLAN / UDP 4789 ──→ KR-2 노드Overlay를 깔았다고 VPC Route나 ACG, NACL이 없어지지는 않는다. 노드 사이의 UDP 4789가 막혀 있으면 컨테이너는 여전히 서로를 못 찾는다.
가상 네트워크는 진짜 네트워크 위에 얹히는 것이지 진짜 네트워크를 대신하는 게 아니라고 이해했다.
대역이 겹치면
하나 더 들은 게 있다. 도커가 만드는 브리지 대역이 VPC 대역이나 VPN Pool과 겹치면 호스트가 패킷을 엉뚱한 인터페이스로 보낸다.
같은 주소를 두 곳이 자기 것이라고 주장하는 상황이다. 이렇게 되면 컨테이너 문제가 아니라 Peering이나 VPN 연결이 통째로 이상해진다.
Workload VPC가 10.60.0.0/16이었으니 도커 쪽이 이 대역을 침범하지 않는지 확인하고 넘어갔다. 실습에서는 안 겹쳤지만 겹쳤다면 원인을 찾는 데 한참 걸렸을 것 같다. 증상이 컨테이너 쪽에서 나타나지 않기 때문이다.
컨테이너를 하나만 띄울 때는 네트워크를 생각할 일이 없다. 두 개가 되면 이름이 필요하고, 호스트가 두 대가 되면 터널이 필요하고, 그 터널은 결국 아래에 깔린 진짜 네트워크 위를 지나간다.
격리를 얻는 대신 연결을 직접 설계하게 된 셈이다. 컨테이너가 편한 건 격리 덕분인데 어려워지는 것도 정확히 같은 이유라는 게, 이날 가장 오래 남은 얘기였다.
네이버 클라우드 심화 과정 회고 3일차 · 전체 목차로
← 이전 · 2일차 (NACL, ACG, Prometheus)
다음 · 4일차 (K3s, Server와 Agent) →