docker engine에서 네트워크를 어떻게 구성하고, 컨테이너에 연결하는지 정리

운영체제 확인

Docker는 기본적으로 Linux의 커널 기능을 통해 컨테이너 기술들을 구현. 때문에 Mac, Window에서는 VM위에 docker를 돌리기에, host상에서는 network 인터페이스, 컴퓨팅 리소스가 안보일 수 있음

때문에 해당 실험은 Ubuntu Baremetal 서버에서 진행

Docker Network

도커 네트워크 드라이버는 다음과 같이 설정할 수 있음

DriverDescription
bridgeThe default network driver.
hostRemove network isolation between the container and the Docker host.
noneCompletely isolate a container from the host and other containers.
overlayOverlay networks connect multiple Docker daemons together.
ipvlanIPvlan networks provide full control over both IPv4 and IPv6 addressing.
macvlanAssign a MAC address to a container.
  • bridge : 기본 드라이버. 컨테이너 veth ↔ docker0 (브리지) ↔ host NIC. 호스트는 docker0 IP로 게이트웨이 역할
  • host : 컨테이너가 host 네트워크 stack을 그대로 공유. 별도 네트워크 네임스페이스 없음, 컨테이너가 host NIC를 직접 사용
  • none : 네트워크 연결 없음. 컨테이너 네임스페이스에 NIC만 있고 라우팅 없음
  • overlay : VXLAN 기반으로 여러 host의 Docker 데몬을 하나의 L2 네트워크처럼 묶음. Docker Swarm/K8s 등에서 사용
  • ipvlan : 컨테이너가 host NIC와 동일한 L2 도메인에서 IP를 직접 받음. bridge 불필요, VLAN 기반으로 L3 라우팅
  • macvlan : 컨테이너가 고유한 MAC 주소를 받아 host NIC를 스위치에 “물리 장비 여러 대”처럼 보이게 함

Linux Network Interface

리눅스에서 네트워크 인터페이스(Network Interface) 는 네트워크 계층에서 데이터를 송수신하기 위해 커널이 제공하는 논리적 장치

하드웨어/소프트웨어 구현에 따라 물리 인터페이스와 가상 인터페이스로 나뉨

물리 인터페이스 (Physical NIC)

  • 서버나 PC에 장착된 실제 네트워크 카드 (ex. eth0, enp2s0)
  • 하드웨어 MAC 주소를 가지며 스위치/라우터 같은 물리 네트워크 장비와 직접 연결
  • 외부 네트워크와 통신하기 위한 기본 경로

가상 인터페이스 (Virtual NIC)

소프트웨어적으로 여러 종류의 가상 인터페이스를 제공

  • loopback (lo): 자기 자신과 통신하기 위한 인터페이스. 항상 127.0.0.1 로 연결됨.
  • bridge (docker0, br-xxxxxx): 소프트웨어 스위치 역할. 여러 NIC을 묶어 같은 네트워크 세그먼트에 두는 기능
  • veth: 가상 이더넷 페어(링크). 물리적인 이더넷 연결 가상화
  • tun/tap: 사용자 공간과 커널 네트워크 스택을 연결하는 가상 장치. VPN에서 사용
  • macvlan/ipvlan: 가상으로 별도의 MAC/IP를 가진 인터페이스를 호스트 NIC 위에 올려서, 컨테이너가 외부 네트워크에 직접 붙은 것처럼 동작

IP

각 인터페이스는 IP 주소, netmask, gateway 등을 설정 가능

  • 물리 NIC: 외부 네트워크와 직접 연결되므로 ISP나 내부 DHCP 서버에서 IP를 받음
  • 가상 NIC: Docker나 K8s, VPN 같은 소프트웨어가 IPAM(IP Address Management)을 통해 주소를 할당

실험

services:
  # default bridge(docker0)에 붙는 컨테이너
  web-default:
    image: nginx:alpine
    container_name: web-default
    restart: unless-stopped
    # 로컬에서 바로 보기 쉽게 포트 하나만 매핑
    ports:
      - "18080:80"
 
  # 사용자 정의 bridge(br-xx)에 붙는 컨테이너 1
  web-bridge-1:
    image: nginx:alpine
    container_name: web-bridge-1
    restart: unless-stopped
    networks:
      br_xx:
        ipv4_address: 172.30.0.10
 
  # 사용자 정의 bridge(br-xx)에 붙는 컨테이너 2
  web-bridge-2:
    image: nginx:alpine
    container_name: web-bridge-2
    restart: unless-stopped
    networks:
      br_xx:
        ipv4_address: 172.30.0.11
networks:
  br_xx:
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.0.0/16
          gateway: 172.30.0.1
 
 

네트워크 구성도

  • 별도의 네트워크를 지정하지 않는 컨테이너는 default bridge 네트워크 docker0에 붙여서 연결
  • 컨테이너간 네트워크 연결은 bridge interface를 통해 연결

host의 인터페이스 확인

cocopam@soyo:~/docker-network-test$ ifconfig
br-cbbf334f7335: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.30.0.1  netmask 255.255.0.0  broadcast 172.30.255.255
        inet6 fe80::8f8:f4ff:fec7:cb37  prefixlen 64  scopeid 0x20<link>
        ether 0a:f8:f4:c7:cb:37  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::780f:6aff:fe8b:f762  prefixlen 64  scopeid 0x20<link>
        ether 7a:0f:6a:8b:f7:62  txqueuelen 0  (Ethernet)
        RX packets 13349  bytes 10863266 (10.8 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15979  bytes 2222681 (2.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp2s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.21  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::e251:d8ff:fe19:d971  prefixlen 64  scopeid 0x20<link>
        ether e0:51:d8:19:d9:71  txqueuelen 1000  (Ethernet)
        RX packets 4305101  bytes 3610932183 (3.6 GB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2058422  bytes 377432017 (377.4 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 542525534  bytes 113566300139 (113.5 GB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 542525534  bytes 113566300139 (113.5 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth5f81027: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::e805:acff:fe5b:a1a0  prefixlen 64  scopeid 0x20<link>
        ether ea:05:ac:5b:a1:a0  txqueuelen 0  (Ethernet)
        RX packets 3  bytes 126 (126.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15  bytes 1626 (1.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth3931235: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::74b6:a4ff:fe8e:ffe7  prefixlen 64  scopeid 0x20<link>
        ether 76:b6:a4:8e:ff:e7  txqueuelen 0  (Ethernet)
        RX packets 3  bytes 126 (126.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 16  bytes 1580 (1.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

vethf3e5e1e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::3cf6:9bff:fe36:245a  prefixlen 64  scopeid 0x20<link>
        ether 3e:f6:9b:36:24:5a  txqueuelen 0  (Ethernet)
        RX packets 3  bytes 126 (126.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 18  bytes 1752 (1.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

  • 새로운 br-cbbf334f7335 브릿지 인터페이스 생성
  • veth 가상 이더넷 링크 3개 생성

bridge에 링크된 인터페이스

cocopam@soyo:~/docker-network-test$ brctl show br-cbbf334f7335
bridge name	        bridge id		    STP enabled	   interfaces
br-cbbf334f7335		8000.0af8f4c7cb37	no		       veth3931235
							                           vethf3e5e1e

  • 생성된 브릿지에 링크된 두 개의 veth 확인

Host routing table 확인

cocopam@soyo:~/docker-network-test$ netstat -nr
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG        0 0          0 enp2s0
172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 docker0
172.30.0.0      0.0.0.0         255.255.0.0     U         0 0          0 br-cbbf334f7335
192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp2s0
192.168.0.1     0.0.0.0         255.255.255.255 UH        0 0          0 enp2s0

  • 할당한 네트워크이 사설 대역 172.30.0.0/16 라우팅은 생성한 br-cbbf334f733로 감
  • 때문에 HOST에서도 bridge를 타고 컨테이너에 접근 가능

라우팅 테스트

cocopam@soyo:~/docker-network-test$ docker exec -it web-bridge-1 ifconfig
eth0      Link encap:Ethernet  HWaddr 7E:93:22:8D:92:C5
          inet addr:172.30.0.10  Bcast:172.30.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:18 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1752 (1.7 KiB)  TX bytes:126 (126.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

  • 컨테이너 내부의 eth0에 172.30.0.10가 할당되어있고, 해당 링크의 MAC이 7E:93:22:8D:92:C5 인것을 확인

host bridge tcp dump

cocopam@soyo:~$ docker exec -it web-bridge-1 ping 172.30.0.11
PING 172.30.0.11 (172.30.0.11): 56 data bytes
64 bytes from 172.30.0.11: seq=0 ttl=64 time=0.539 ms
64 bytes from 172.30.0.11: seq=1 ttl=64 time=0.189 ms
64 bytes from 172.30.0.11: seq=2 ttl=64 time=0.183 ms



cocopam@soyo:~/docker-network-test$ sudo tcpdump -i br-cbbf334f7335
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on br-cbbf334f7335, link-type EN10MB (Ethernet), snapshot length 262144 bytes
21:54:33.977616 ARP, Request who-has 172.30.0.11 tell 172.30.0.10, length 28
21:54:33.977694 ARP, Reply 172.30.0.11 is-at 7a:7c:8e:e5:dd:93 (oui Unknown), length 28
21:54:33.977710 IP 172.30.0.10 > 172.30.0.11: ICMP echo request, id 47, seq 0, length 64
21:54:33.977936 IP 172.30.0.11 > 172.30.0.10: ICMP echo reply, id 47, seq 0, length 64
21:54:34.978097 IP 172.30.0.10 > 172.30.0.11: ICMP echo request, id 47, seq 1, length 64
21:54:34.978184 IP 172.30.0.11 > 172.30.0.10: ICMP echo reply, id 47, seq 1, length 64
21:54:35.978355 IP 172.30.0.10 > 172.30.0.11: ICMP echo request, id 47, seq 2, length 64
21:54:35.978439 IP 172.30.0.11 > 172.30.0.10: ICMP echo reply, id 47, seq 2, length 64
21:54:36.978563 IP 172.30.0.10 > 172.30.0.11: ICMP echo request, id 47, seq 3, length 64
21:54:36.978657 IP 172.30.0.11 > 172.30.0.10: ICMP echo reply, id 47, seq 3, length 64
21:54:37.978790 IP 172.30.0.10 > 172.30.0.11: ICMP echo request, id 47, seq 4, length 64
21:54:37.978873 IP 172.30.0.11 > 172.30.0.10: ICMP echo reply, id 47, seq 4, length 64
21:54:38.979002 IP 172.30.0.10 > 172.30.0.11: ICMP echo request, id 47, seq 5, length 64
21:54:38.979089 IP 172.30.0.11 > 172.30.0.10: ICMP echo reply, id 47, seq 5, length 64
21:54:39.337760 ARP, Request who-has 172.30.0.10 tell 172.30.0.11, length 28
21:54:39.337843 ARP, Reply 172.30.0.10 is-at 7e:93:22:8d:92:c5 (oui Unknown)
  • web-bridge-1(172.30.0.10) 에서 web-bridge-2(172.30.0.11) ping을 확인
    • ICMP를 위해서 IP를 MAC으로 변환하는 ARP 패킷 전송
    • 이후 web-bridge-2에서 eth0 MAC 전달 (arp reply)

bridge mac table 확인

cocopam@soyo:~/docker-network-test$ sudo bridge fdb show | grep br-cbbf334f7335
33:33:00:00:00:01 dev br-cbbf334f7335 self permanent
33:33:00:00:00:02 dev br-cbbf334f7335 self permanent
01:00:5e:00:00:6a dev br-cbbf334f7335 self permanent
33:33:00:00:00:6a dev br-cbbf334f7335 self permanent
01:00:5e:00:00:01 dev br-cbbf334f7335 self permanent
33:33:ff:c7:cb:37 dev br-cbbf334f7335 self permanent
33:33:ff:00:00:00 dev br-cbbf334f7335 self permanent
0a:f8:f4:c7:cb:37 dev br-cbbf334f7335 vlan 1 master br-cbbf334f7335 permanent
0a:f8:f4:c7:cb:37 dev br-cbbf334f7335 master br-cbbf334f7335 permanent
7e:93:22:8d:92:c5 dev vethf3e5e1e master br-cbbf334f7335
3e:f6:9b:36:24:5a dev vethf3e5e1e vlan 1 master br-cbbf334f7335 permanent
3e:f6:9b:36:24:5a dev vethf3e5e1e master br-cbbf334f7335 permanent
7a:7c:8e:e5:dd:93 dev veth3931235 master br-cbbf334f7335
76:b6:a4:8e:ff:e7 dev veth3931235 vlan 1 master br-cbbf334f7335 permanent
76:b6:a4:8e:ff:e7 dev veth3931235 master br-cbbf334f7335 permanent
  • web-bridge-1
    • 컨테이너 내부의 7E:93:22:8D:92:C5 eth0 MAC vethf3e5e1e로 포워딩
    • veth MAC 76:b6:a4:8e:ff:e7
  • 마찬가지로 web-bridge-2도 연결된 것 확인 가능

Ref

https://docs.docker.com/engine/network/