도커를 이용한 데이터 센터 구축이 목표다. 아마도 클라우드 인프라를 구축하는데, 가장 큰 어려움은 네트워크 구성일 것이다. 네트워크 구성을 집중적으로 살펴보려고 한다. 최종목표는 AWS VPC와 같은 네트워크 인프라를 구축하는 거다.
테스트 환경 구성
KVM을 기반으로 테스트 환경을 만들어 보기로 했다.
제대로 테스트하려면 두 개 이상의 물리적인 호스트와 스위치가 필요하다. 지금은 이런 여력이 없어서 KVM을 기반으로 구축하기로 했다. 하이퍼바이저를 기반으로 (개인 PC에)가상환경으로 구성 할 경우, VM위에 VM을 올리는 잉여스러운 삽질(인셉션 프로젝트..)을 해야 했다. 도커로 구축할 경우 한번의 가상화로 실제 환경과 거의 유사한 환경을 만들 수 있다.
VM : KVM으로 만든다. VM 하나가 물리적인 호스트 하나에 대응한다. 4대의 VM 으로 물리적인 호스트를 시뮬레이션한다.
VM 운영체제 : 우분투 리눅스 14.10 서버 버전으로 통일한다. 어느정도 정리가되면 coreos 기반으로 구축해보는 것도 재미있겠다.
Docker 컨테이너 : VM은 여러 개의 컨테이너를 실행한다. 이들 컨테이너를 이용해서 다양한 네트워크 구성을 테스트 한다.
# ping 192.168.5.2
PING 192.168.5.2 (192.168.5.2) 56(84) bytes of data.
64 bytes from 192.168.5.2: icmp_seq=1 ttl=64 time=0.421 ms
64 bytes from 192.168.5.2: icmp_seq=2 ttl=64 time=0.172 ms
호스트 시스템의 브릿지 상태다.
# ovs-vsctl show
41683821-59d5-4443-8cc5-c95dae7deaae
Bridge "br0"
Port "tap0"
Interface "tap0"
Port "tap3"
Interface "tap3"
Port "tap1"
Interface "tap1"
Port "tap2"
Interface "tap2"
Port "br0"
Interface "br0"
type: internal
ovs_version: "2.3.1"
이렇게 해서 VM을 띄우고, VM을 위한 네트워크를 만들었다. 이제 이 위에 도커 네트워크를 구성한다.
Simple 네트워크 구성
도커 기본 네트워크
구성
(아무런 손도 대지 않은)지금의 네트워크 구성은 아래와 같다.
도커 네트워크는 서로 독립적이다. VM-01.Docker에서 VM-02.Docker로는 통신할 수 없다.
같은 VM에 있는 도커들은 서로 통신할 수 있으며, 스위치(OVS)과도 통신 할 수 있다.
2가 가능한 이유는 도커 네트워크를 구성하면서 마스커레이딩(SNAT)을 하기 때문이다. 도커 레벨의 네트워크는 아래와 같이 구성된다.
다른 VM 도커와의 통신
이 구성에서 다른 VM의 도커와 통신하기 위한 가장 간단한 방법은 도커의 포트포워딩 기능을 이용하는 거다.
VM2에서 5000:22로 포트포워딩을 적용한 도커를 띄웠다.
# docker run -p 5000:22 -i -t ubuntu bash
VM1.도커에서 VM2.도커로 연결 테스트
# ssh yundresm@192.168.5.3 -p 5000
The authenticity of host '[192.168.5.3]:5000 ([192.168.5.3]:5000)' can't be established.
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
도커에서 출발한 패킷은 총 2번의 SNAT를 거쳐서, 인터넷으로 향하게 된다.
Flat 네트워크 구성
앞선 기본 네트워크 구성은 "다른 VM의 도커 컨테이너와 네트워크가 단절 된 다는" 큰 문제를 가지고 있다. 포트포워딩을 이용해서 통신하는 방법이 있기는 한데, 좋은 방법은 아니다. 도커 컨테이너들을 같은 브로드캐스트 영역으로 묶어서, L2 통신이 가능하게 하는게 좋은 방법이다.(가장 좋은 방법이라고 하지 않은 이유는 오버레이 네트워크를 구성 L3에서 브로드캐스팅 영역을 만들 수 있기 때문이다. VXLAN, GRE에서 자세히 다룬다.)
가장 간단한 구성은 모든 도커 컨테이너를 L2 네트워크에 묶어버리는 방법이다. 나는 지금 홈 네트워크에서 테스트 중이니, 대략 아래와 같은 구성이 될테다.
내 홈네트워크는 공유기가 구성을 하고 있다. 공유기의 네트워크는 192.168.219.1/24이며, DHCP 서버를 이용해서 홈 네트워크에 연결하는 기기들의 네트워크를 설정한다. 도커 컨테이너가 올라오면, 모두 공유기 네트워크에 직접 붙게 구성을 한다. 그림에서 OVS와 Bridge를 제거하고, 도커 컨테이너가 직접 공유기에 붙는다고 생각하면 이해가 쉬울 거다.
먼저 호스트의 네트워크를 바꿔서 VM들이 공유기 네트워크에 직접 연결되게 만들어야 한다.
OVS 브릿지 스위치 br0를 만든다.
# ovs-vsctl add-br br0
br0에 eth0을 add 한다.
# ovs-vsctl add-port br0 eth0
br0을 uplink로 만들기 위해서 ip를 할당한다.
# cat /etc/network/interfaces
auto br0
iface br0 inet dhcp
eth0를 초기화 한다.
# ifconfig eth0 0
이제 VM을 만들면, br0를 통해서 공유기 네트워크에 연결된다. 현재 각 VM은 static 하게 ip를 설정했는데, dhcp로 설정하도록 변경한다.
# cat /etc/network/interfaces
auto eth0
iface eth0 inet dhcp
여기 까지 했으면, vm이 dhcp로 네트워크가 설정되는 걸 확인 할 수 있을 거다.
다음 도커 컨테이너를 공유기 네트워크에 물려야 한다. 기본 원리는 VM과 동일하다. 즉
eth0의 네트워크 설정을 초기화 하고
docker0 브릿지를 uplink로 만든다.
도커 컨테이너를 실행한다.
1번은 동일하다.
# ifconfig eth0 0
2번을 위해서는 작업이 좀 필요하다. 도커의 기본 브릿지 네트워크를 사용할 경우, 브릿지의 이런 저런 설정들(브릿지 고유의 dhcp 설정 등)을 따라가서 원하는 대로 설정하기가 어렵기 때문이다.
먼저 기본 docker0 브릿지를 삭제한다.
# brctl delbr docker0
커스텀 브릿지에 연결하도록 도커 설정을 변경한다. 이름은 docker0으로 했다. 이름만 같을 뿐, (예전 브릿지와는) 전혀 다른 브릿지다.
# cat /etc/network/interfaces
......
auto docker0=docker0
iface docker0 inet dhcp
도커 서버를 재시작 한다.
# service docker restart
docker0은 dhcp를 통해서 네트워크 설정이 된다. 여기까지 작업한 결과 docker 네트워크를 홈 네트워크에 연결했다.
이제 컨테이너를 실행하면, 공유기 네트워크에 물려서 IP를 할당받는 걸 확인 할 수 있을 거다. 모든 도커 컨테이너가 같은 (공유기)네트워크에 물려 있으니, IP를 이용해서 직접 통신이 가능하다.
평가
내가 구성한 네트워크는 아래와 같이 묘사할 수 있다.
목적에 따라서 평가가 달라지는 모델이다. 인스턴스들이 마치 인터넷을 구성하는 노드들 처럼 L3에서 독립적으로 구성이 된다. 매우 간단한 모델로 구성이 쉽다는 장점이 있다. 설계, 구성, 운용 모든게 쉽다. 일반 유저를 대상으로 퍼블릭 서비스를 하지 않고, 내부 인프라 용으로 사용할 경우 괜찮은 모델이 될 수 있다.
반면 단점은 네트워크 격리(isolation)이 제한 적이다 라는 단점이 있다. Security group 기반의 네트워크 격리만 가능하다. 서비스별 네트워크 할당이라든지 이런거 안된다.
인터넷 서비스는 L4 switch를 이용하거나 Haproxy 클러스터를 구성하는 방법이 있겠다.
Advanced 네트워크 구성을 위한 준비
도커 컨테이너를 OpenvSwitch와 연결하기
도커의 기본 네트워크는 brctl로 관리하는 간단한 브릿지다. 이 브릿지는 간단한 기능만을 제공하기 때문에 VLAN, GRE, VXLAN등의 고급 네트워크 구성이 힘들다. 고급 네트워크를 구성하려면 역시 OVS에 연결해야 한다.
해서 VLAN, GRE, VXLAN을 다루기 전에 도커와 OpenvSwitch를 연결하는 방법을 먼저 살펴보기로 했다. 테스트를 위한 네트워크 구성은 2장 VM간 네트워크 구성의 static 구성이다. 192.168.5.2 VM 에서 작업을 한다.
도커 컨테이너를 실행 할 때 네트워크 타입을 --net=none 로 한다. 이렇게 하면 네트워크가 없는 컨테이너가 실행된다.
# docker.io run --net=none -i -t ubuntu /bin/bash
root@d605bcab2877:/# ifconfig
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:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
OVS 브릿지 br0 만들고, veth 쌍(pair)를 만들어서 하나는 br0에 두고 다른 하나는 컨테이너(d605bcab2877)의 네트워크 디바이스 eth0 으로 추가를 할 것이다.
OVS 브릿지를 만든다.
veth 쌍을 만든다. 하나는 veth-a, 다른 하나는 veth-b로 했다. veth는 네트워크 네임스페이스에서 간단히 설명하고 있다.
# ip link add veth-a type veth peer name veth-b
veth-a를 br0에 추가하고, veth-a를 up link 한다.
# ovs-vsctl add-port br0 veth-a
# ip link set veth-a up
# ovs-vsctl show
3e95bbe3-88b3-4b4d-b39f-b9a24cb53a8e
Bridge "br0"
Port veth-a
Interface veth-a
Port "br0"
Interface "br0"
type: internal
ovs_version: "2.1.3"
veth-b를 컨테이너의 네트워크 네임스페이스에 추가한다.
# ip link set veth-b netns $pid
# ip netns exec $pid ip link set dev veth-b name eth0
# ip netns exec $pid ip link set eth0 address 12:34:56:78:9a:bc
# ip netns exec $pid ip link set eth0 up
# ip netns exec $pid ip addr add 172.17.42.2/16 dev eth0
# ip netns exec $pid ip route add default via 172.17.42.1
# 게이트웨이와의 통신
# ping 172.17.42.1
PING 172.17.42.1 (172.17.42.1) 56(84) bytes of data.
64 bytes from 172.17.42.1: icmp_seq=1 ttl=64 time=0.709 ms
64 bytes from 172.17.42.1: icmp_seq=2 ttl=64 time=0.072 ms
# 호스트 운영체제와의 통신
# ping 192.168.5.1
PING 192.168.5.1 (192.168.5.1) 56(84) bytes of data.
64 bytes from 192.168.5.1: icmp_seq=1 ttl=63 time=2.34 ms
64 bytes from 192.168.5.1: icmp_seq=2 ttl=63 time=0.201 ms
# 인터넷과의 통신
ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=40 time=84.0 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=40 time=78.6 ms
지금의 네트워크 구성이다.
이것으로 컨테이너의 네트워크를 OVS 브릿지로 대체 했다. 이제 OVS의 기능들을 이용해서 고급 네트워크 환경을 구축할 준비가 끝났다.
Multi NIC 구성
하나의 NIC을 이용해서 도커 네트워크를 테스트하려면 애로사항이 꽃필 것이다. 브릿지 네트워크 설정을 조금만 잘못해도 네트워크가 끊겨버리기 때문이다. 그래서 VM의 네트워크 인터페이스를 2개로 분리하기로 했다.
br0 : 도커 네트워크
br1 : 관리 네트워크
도커 네트워크는 도커들간 통신만을 위해서 사용한다. 관리자는 관리 네트워크에 접속해서, 도커 컨테이너 실행, 네트워크 설정, 패키지 관리, 도커 네트워크 문제 해결과 같은 작업을 한다. 관리자는 br1 인터페이스로 접속을 하기 때문에, 도커 네트워크 구성을 하다가 실수를 하더라도, 네트워크가 끊기는 일은 없을 것이다. 구성은 아래와 같다.
br1 브릿지를 새로 만들었다.
도커 네트워크 구성을 위한 스크립트를 만들었다. 에러처리 같은 건 기대하지 말자(귀찮다).
#!/bin/bash
# $1 : 네트워크 작업할 컨테이너의 아이디
cid=$1
ip=$2
# 도커를 위한 MAC ADDRESS
macaddr=$(echo $RANDOM|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
echo "Network Configuration"
echo "MAC : $macaddr"
echo "IP : $ip"
echo "====================="
# 컨테이너의 process id를 이용해서 veth 쌍을 만든다.
# 컨테이너의 process id는 네트워크 도메인이름으로도 사용한다.
pid=`docker inspect -f '{{.State.Pid}}' $cid`
mkdir -p /var/run/netns
ln -s /proc/$pid/ns/net /var/run/netns/$pid
vetha="veth$pid-1"
vethb="veth$pid-2"
ip link add $vetha type veth peer name $vethb
echo "Create veth pair : $vetha <-> $vethb"
# 브릿지에 $vetha를 추가
ovs-vsctl add-port br0 $vetha
ip link set $vetha up
# 도커 컨테이너를 위한 네트워크 설정
ip link set $vethb netns $pid
ip netns exec $pid ip link set dev $vethb name eth0
ip netns exec $pid ip link set eth0 address $macaddr
ip netns exec $pid ip link set eth0 up
ip netns exec $pid ip addr add $ip/24 dev eth0
ip netns exec $pid ip route add default via 192.168.5.1
테스트를 위해서 이름이 2eec9bd8b289인 도커 컨테이너를 실행했다. 스크립트 실행
# ./ovs-work.sh 2eec9bd8b289 192.168.5.202
Network Configuration
MAC : 02:4f:3b:a7:37:3a
IP : 192.168.5.202
=====================
Create veth pair : veth5692-1 <-> veth5692-2
도커 컨테이너에서 네트워크 테스트
# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:4f:3b:a7:37:3a
inet addr:192.168.5.202 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::4f:3bff:fea7:373a/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.5.1 0.0.0.0 UG 0 0 0 eth0
192.168.5.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
# ping 192.168.5.1
PING 192.168.5.1 (192.168.5.1) 56(84) bytes of data.
64 bytes from 192.168.5.1: icmp_seq=1 ttl=64 time=1.27 ms
64 bytes from 192.168.5.1: icmp_seq=2 ttl=64 time=0.211 ms
성공!!
이렇게 해서 VM에 멀티닉을 붙이고, 관리 트래픽과 (도커를 위한) 서비스 트래픽을 분리 함으로써, 좀더 편하게 테스트 할 수 있는 환경을 만들었다. 실제 도커 기반으로 클라우드 인프라를 구성한다면, 호스트에 3개 이상의 NIC(서비스 트래픽, 관리 트래픽, 스토리지 트래픽)를 붙일 테니 일반적인 구성이라고 볼 수 있겠다.
실제 물리적 환경으로 구성해 본다면 대략 아래와 같은 모습이 될 것이다.
Advanced 네트워크
VLAN 네트워크 구성
Flat Network는 아래와 문제점들이 있다.
모든 컨테이너가 같은 브로드캐스트 영역에 놓인다.
멀티터넌트 환경을 만들 수 없다.
Flat Network의 구성을 변경하지 않고, 위 문제를 해결 할 수 있는 가장 간단한 방법은 VLAN 구성이다. 대략 4K 정도의 격리된 네트워크를 구성 할 수 있을 테니, 중/소 규모에서는 적은비용으로 사용 할 만한 방법이다.
100, 200 두 개의 VLAN을 만든 다음, 각 VLAN에 연결된 도커들끼리 통신이 가능하게 하는게 목적이다. 네트워크 구성은 아래와 같을 것이다.
Contents
테스트 환경 구성
VM 간 네트워크 구성
OVS 브릿지 생성
OVS 스크립트 파일
브릿지에 연결된 KVM 실행과 네트워크 설정
Simple 네트워크 구성
도커 기본 네트워크
구성
다른 VM 도커와의 통신
인터넷으로 연결
Flat 네트워크 구성
평가
Advanced 네트워크 구성을 위한 준비
도커 컨테이너를 OpenvSwitch와 연결하기
Multi NIC 구성
Advanced 네트워크
VLAN 네트워크 구성
VXLAN 네트워크 구성
GRE 네트워크 구성
Recent Posts
Archive Posts
Tags