0. 들어가며
지금까지 우리는 단일 컨테이너를 실행하고, 데이터를 영구적으로 저장하는 방법까지 배웠다. 하지만 실제 애플리케이션은 대부분 여러 컨테이너로 구성된다.
- 웹 서버 컨테이너 (Nginx)
- 애플리케이션 서버 컨테이너 (Spring Boot, Node.js)
- 데이터베이스 컨테이너 (MySQL, PostgreSQL)
이렇게 여러 컨테이너가 함께 동작하려면 컨테이너 간 통신이 필요하다. 이번 포스팅에서는 컨테이너들이 서로 통신할 수 있게 해주는 Docker Network에 대해 알아보겠다.
1. Docker Network의 필요성
1.1. 컨테이너의 격리와 통신
도커 컨테이너는 기본적으로 격리된 환경에서 실행된다. 각 컨테이너는 자신만의 네트워크 네임스페이스를 가지며, 기본적으로는 다른 컨테이너와 격리되어 있다.
[Container A] [Container B] [Container C]
| | |
+----------------+---------------+
|
[Docker Network]
|
[Host Machine]
하지만 애플리케이션을 구성하려면 이 격리된 컨테이너들이 선택적으로 통신할 수 있어야 한다. 예를 들어:
- 웹 서버는 애플리케이션 서버와 통신해야 한다.
- 애플리케이션 서버는 데이터베이스와 통신해야 한다.
- 하지만 데이터베이스는 외부에서 직접 접근할 수 없어야 한다.
이러한 요구사항을 충족시키기 위해 도커는 네트워크 기능을 제공한다.
1.2. 실생활 비유: 아파트 단지
- 각 컨테이너는 아파트 호수다.
- Docker Network는 아파트 단지 내부 도로다.
- 같은 단지(같은 네트워크)에 있는 호수들은 서로 방문할 수 있다.
- 다른 단지(다른 네트워크)에 있는 호수들은 기본적으로 왕래할 수 없다.
- 외부 인터넷은 단지 정문을 통해서만 출입할 수 있다.
2. Docker Network의 구조
2.1. 네트워크 구성 요소
[외부 인터넷]
|
[eth0 (호스트)]
|
[docker0 브리지]
/ | \\
[veth0] [veth1] [veth2]
| | |
[C1] [C2] [C3]
- eth0: 호스트 머신의 실제 네트워크 인터페이스. 외부 인터넷과 연결된다.
- docker0: 도커가 설치될 때 기본으로 생성되는 가상의 브리지 네트워크. 모든 컨테이너를 연결하는 가상의 스위치 역할을 한다.
- veth (Virtual Ethernet): 각 컨테이너가 생성될 때 컨테이너와 docker0 브리지를 연결하는 가상의 케이블 쌍이다. 한쪽 끝은 컨테이너에, 다른 쪽 끝은 docker0 브리지에 연결된다.
2.2. 컨테이너의 IP 할당
기본적으로 각 컨테이너는 172.17.0.0/16 대역의 IP를 할당받는다.
# 컨테이너의 IP 확인
docker inspect <컨테이너명> | grep IPAddress
실행 결과:
"IPAddress": "172.17.0.2",
3. 네트워크 드라이버 종류
도커는 다양한 네트워크 드라이버를 제공한다. 각각 다른 특성과 용도를 가지고 있다.
3.1. bridge (기본값)
가장 기본적인 네트워크 드라이버다. 같은 호스트 내의 컨테이너들이 통신할 수 있도록 해준다.
# 기본 bridge 네트워크 확인
docker network ls
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
특징:
- 같은 bridge 네트워크에 연결된 컨테이너끼리 통신 가능
- 외부에서는 포트 매핑(p)을 통해서만 접근 가능
- 기본 bridge는 DNS 기반 컨테이너 이름 해석을 지원하지 않음 (사용자 정의 bridge는 지원)
3.2. host
컨테이너가 호스트의 네트워크를 직접 사용한다.
docker run -d --network host --name nginx-host nginx
특징:
- 컨테이너가 별도의 IP를 할당받지 않고 호스트의 IP를 공유
- 포트 매핑(p) 불필요 (호스트의 포트를 그대로 사용)
- 네트워크 성능이 중요한 경우 사용
- 격리 수준이 낮아짐 (포트 충돌 위험)
3.3. none
컨테이너를 네트워크에서 완전히 격리한다.
docker run -d --network none --name isolated ubuntu sleep infinity
특징:
- 네트워크 인터페이스가 없음
- 외부와 완전히 차단된 환경이 필요할 때 사용 (보안, 테스트)
3.4. container
다른 컨테이너의 네트워크 네임스페이스를 공유한다.
# 네트워크를 공유할 컨테이너 실행
docker run -d --name shared-network nginx
# 다른 컨테이너가 위 컨테이너의 네트워크를 공유
docker run -it --network container:shared-network ubuntu bash
특징:
- 두 컨테이너가 같은 IP, 같은 포트 공간을 공유
- localhost로 서로 통신 가능
- 주로 사이드카 패턴에서 사용
3.5. overlay (Swarm 모드)
여러 도커 호스트를 하나의 네트워크로 연결한다. (도커 스웜 모드에서 사용)
# Swarm 모드 초기화 후
docker network create -d overlay my-overlay
특징:
- 여러 물리적 서버에 걸친 컨테이너 간 통신 지원
- 클러스터 환경에서 사용
4. 사용자 정의 네트워크 생성 및 활용
기본 bridge 네트워크도 사용할 수 있지만, 사용자 정의 네트워크를 만드는 것이 여러모로 편리하다.
4.1. 사용자 정의 네트워크의 장점
- 자동 DNS 해석: 컨테이너 이름으로 통신 가능
- 격리: 다른 네트워크와 분리되어 보안성 향상
- 유연성: 네트워크별로 다른 설정 적용 가능
4.2. 네트워크 생성 및 관리
# bridge 타입의 네트워크 생성
docker network create --driver bridge my-network
# 특정 서브넷을 지정하여 생성
docker network create \\
--driver bridge \\
--subnet 172.20.0.0/16 \\
--gateway 172.20.0.1 \\
my-custom-network
# 네트워크 목록 확인
docker network ls
# 네트워크 상세 정보 확인
docker network inspect my-network
# 네트워크 삭제
docker network rm my-network
# 사용하지 않는 네트워크 모두 삭제
docker network prune
4.3. 컨테이너를 네트워크에 연결
방법 1: 실행 시 연결
# 생성한 네트워크에 연결하여 컨테이너 실행
docker run -d --name app1 --network my-network nginx
docker run -d --name app2 --network my-network ubuntu sleep infinity
방법 2: 실행 중인 컨테이너를 네트워크에 연결
# 먼저 기본 네트워크로 실행
docker run -d --name app3 nginx
# 실행 중인 컨테이너를 네트워크에 추가 연결
docker network connect my-network app3
# 네트워크에서 컨테이너 분리
docker network disconnect my-network app3
5. 실습: 컨테이너 간 통신
5.1. 기본 네트워크에서의 통신
# 두 개의 Ubuntu 컨테이너 실행 (기본 bridge)
docker run -d --name u1 ubuntu sleep infinity
docker run -d --name u2 ubuntu sleep infinity
# u2에서 u1으로 ping 시도
docker exec u2 ping u1
# -> ping: u1: Name or service not known
기본 bridge 네트워크에서는 컨테이너 이름으로 통신할 수 없다. IP 주소를 직접 알아내야 한다.
# u1의 IP 확인
docker inspect u1 | grep IPAddress
# "IPAddress": "172.17.0.2"
# IP로 ping 시도
docker exec u2 ping 172.17.0.2
# -> 성공!
5.2. 사용자 정의 네트워크에서의 통신
# 사용자 정의 네트워크 생성
docker network create test-network
# 같은 네트워크에 컨테이너 실행
docker run -d --name u3 --network test-network ubuntu sleep infinity
docker run -d --name u4 --network test-network ubuntu sleep infinity
# u4에서 u3으로 ping 시도 (컨테이너 이름으로!)
docker exec u4 ping u3
# -> 성공! (ping 응답 확인)
사용자 정의 네트워크에서는 내장 DNS 서버가 컨테이너 이름을 IP 주소로 자동 변환해준다.
5.3. 여러 네트워크에 동시 연결
# 두 번째 네트워크 생성
docker network create second-network
# u3 컨테이너를 second-network에도 연결
docker network connect second-network u3
# u3의 네트워크 정보 확인
docker inspect u3 | grep -A 10 "Networks"
이제 u3는 두 개의 네트워크에 동시에 연결되어 있다.
6. 실전: 3-Tier 애플리케이션 네트워크 구성
6.1. 시나리오
다음과 같은 3계층 애플리케이션을 구성해보자.
- Web Tier: Nginx (외부에 노출)
- App Tier: Node.js 애플리케이션 (Web에서만 접근 가능)
- DB Tier: MySQL (App에서만 접근 가능, 외부 차단)
6.2. 네트워크 설계
# 두 개의 네트워크 생성
docker network create frontend-network
docker network create backend-network
네트워크 구조:
[외부 인터넷]
|
[Web Container] (frontend-network + backend-network)
| |
[App Container] (backend-network + db-network)
|
[DB Container] (db-network)
6.3. 컨테이너 실행
DB Tier (MySQL)
# DB 볼륨 생성
docker volume create mysql-data
# DB 컨테이너 실행 (backend-network만 연결)
docker run -d \\
--name mysql-db \\
--network backend-network \\
-v mysql-data:/var/lib/mysql \\
-e MYSQL_ROOT_PASSWORD=root \\
-e MYSQL_DATABASE=myapp \\
mysql:8.0
App Tier (Node.js)
먼저 간단한 Node.js 앱을 만들어보자.
// app.js
const express = require('express');
const mysql = require('mysql2');
const app = express();
const connection = mysql.createConnection({
host: 'mysql-db', // 컨테이너 이름으로 접근!
user: 'root',
password: 'root',
database: 'myapp'
});
app.get('/', (req, res) => {
res.send('Hello from App Tier!');
});
app.get('/db', (req, res) => {
connection.query('SELECT NOW()', (err, results) => {
if (err) {
res.send('DB connection failed: ' + err);
} else {
res.send('DB connected! Time: ' + results[0].NOW);
}
});
});
app.listen(3000);
# Dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "app.js"]
# App 이미지 빌드
docker build -t myapp:latest .
# App 컨테이너 실행 (frontend + backend 네트워크 모두 연결)
docker run -d \\
--name app-server \\
--network frontend-network \\
-p 3000:3000 \\
myapp:latest
# backend-network에도 연결
docker network connect backend-network app-server
Web Tier (Nginx)
# Nginx 설정 파일 생성
cat > nginx.conf << 'EOF'
events {
worker_connections 1024;
}
http {
upstream app {
server app-server:3000;
}
server {
listen 80;
location / {
proxy_pass <http://app>;
proxy_set_header Host $host;
}
}
}
EOF
# Nginx 컨테이너 실행 (frontend-network만 연결)
docker run -d \\
--name web-server \\
--network frontend-network \\
-p 80:80 \\
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \\
nginx
6.4. 테스트
- 브라우저에서 http://localhost 접속 → "Hello from App Tier!" 확인
- http://localhost/db 접속 → DB 연결 시간 확인
DB 컨테이너는 frontend-network에 연결되어 있지 않으므로, 외부에서는 직접 접근할 수 없다. App 서버를 통해서만 접근 가능하다.
7. 네트워크 트러블슈팅
7.1. 자주 사용하는 디버깅 명령어
# 컨테이너의 네트워크 설정 확인
docker inspect <컨테이너명> | grep -A 20 "Networks"
# 컨테이너 내부에서 네트워크 도구 설치
docker exec -it <컨테이너명> apt-get update
docker exec -it <컨테이너명> apt-get install -y iputils-ping curl net-tools
# ping 테스트
docker exec app-server ping mysql-db
# 포트 열림 확인
docker exec app-server curl mysql-db:3306
# DNS 확인
docker exec app-server cat /etc/resolv.conf
7.2. 네트워크 격리 확인
# web-server에서 DB로 직접 접근 시도 (실패해야 정상)
docker exec web-server ping mysql-db
# -> ping: mysql-db: Name or service not known
# app-server에서 DB로 접근 (성공)
docker exec app-server ping mysql-db
# -> 성공!
7.3. 포트 충돌 해결
# 어떤 프로세스가 포트를 사용 중인지 확인
sudo netstat -tulpn | grep 8080
# 실행 중인 컨테이너 중지
docker stop <컨테이너명>
8. 네트워크 명령어 요약
| 명령어 | 설명 |
| docker network ls | 네트워크 목록 확인 |
| docker network create [이름] | 새 네트워크 생성 |
| docker network inspect [이름] | 네트워크 상세 정보 |
| docker network connect [네트워크] [컨테이너] | 컨테이너를 네트워크에 연결 |
| docker network disconnect [네트워크] [컨테이너] | 컨테이너를 네트워크에서 분리 |
| docker network rm [이름] | 네트워크 삭제 |
| docker network prune | 사용하지 않는 네트워크 모두 삭제 |
9. 마치며
이번 포스팅에서는 컨테이너 간 통신을 가능하게 하는 Docker Network에 대해 알아보았다.
- 도커는 다양한 네트워크 드라이버(bridge, host, none, container, overlay)를 제공한다.
- 기본 bridge 네트워크는 컨테이너 이름으로 통신할 수 없다.
- 사용자 정의 네트워크에서는 자동 DNS 해석으로 컨테이너 이름으로 통신 가능하다.
- 하나의 컨테이너를 여러 네트워크에 연결할 수 있다.
- 네트워크를 분리하면 보안성을 높일 수 있다.
다음 포스팅에서는 여러 컨테이너로 구성된 애플리케이션을 한 번에 관리하는 방법, 즉 Docker Compose에 대해 알아보겠다.
'Docker' 카테고리의 다른 글
| [ADVANCED #2] 도커 이미지 배포와 공유: Docker Hub 활용법 (0) | 2025.06.01 |
|---|---|
| [ADVANCED #1] 여러 컨테이너 한 번에 관리하기: Docker Compose (0) | 2025.06.01 |
| [BASIC #5] 데이터를 영구적으로 저장하는 법: Docker Volume (0) | 2025.05.31 |
| [BASIC #4] 나만의 이미지 만들기: Dockerfile 완전 정복 (0) | 2025.05.31 |
| [BASIC #3] 밀키트 비유로 완벽 이해하는 도커 이미지와 컨테이너 (0) | 2025.05.31 |
