- 전편에 이어서 -


이번에는 아래 그림에서와 같이 L3 network로 연결된 Docker 호스트간 L2 통신 방법(2)에 대해 다룬다. 네트워크가 다른 2개의 호스트 사이에 인터넷이 있든, 여러 개의 라우터가 존재하든, 호스트간의 연결이 가능하다면 이 방법을 쓸 수 있다. 예를 들어 아마존 SWS의 가상머신과 사무실의 인터넷이 연결된 Docker 호스트 내부의 Docker 사이의 연결도 물론 가능할 것이다.




사전 준비

  • 라우터로 연결된 서로 다른 네트워크를 가지는 Docker Host(VM) 2대
  • Docker Host #1: CentOS 7.3 Minimal Server(1611 ver), 10.255.20.10/24
  • Docker Host #2: CentOS 7.3 Minimal Server(1611 ver), 10.255.30.10/24



Docker & Open vSwitch 설치와 firewall 설정


* Docker 호스트에 EPEL 리포지터리와 RDO 프로젝트를 통해서 Docker와 Open vSwitch를 설치

(Open vSwich의 별도 설치 방법에 대해서는 http://bryan.wiki/276 참고)

yum clean all
yum install -y epel-release https://www.rdoproject.org/repos/rdo-release.rpm
yum install -y firewalld docker openvswitch bridge-utils
yum update -y
systemctl start openvswitch firewalld
systemctl enable openvswitch firewalld


* VxLAN 터널링을 위한 패킷은 UDP 4789, 8472 포트를 통해 전송된다. firewall 설정에서 이 2개 포트를 모두 개방

firewall-cmd --add-port=4789/udp --add-port=8472/udp
firewall-cmd --permanent --add-port=4789/udp --add-port=8472/udp


* trusted 영역에 대해 10.*.*.* 대역의 통신이 모두 가능하도록 개방(docker0 네트워크 10.x.0.0/16 을 서브넷으로 포함하는 상위 네트워크의 예: 10.0.0.0/8)

* 다른 예: docker0 네트워크를 172.17.0.0/16 이나 172.18.0.0/16 ... 등으로 할 경우에는 상위 네트워크를 172.16.0.0/12 로 하면 된다

firewall-cmd --permanent --zone=trusted --add-source=172.16.0.0/12


각각의 도커 호스트에서 Default Docker 네트워크 설정


2개의 Docker 호스트의 Docker 네트워크를 기본으로 설정해 둔다. 본 과정에서는 큰 의미가 없으며, 별도 옵션을 주지 않고 컨테이너를 만들면 여기서 지정된 네트워크를 Docker 엔진이 자동으로 설정해 주게 될 것이다(본 포스팅과는 직접 연관 없는 부분으로, 이 과정은 생략 가능).

[root@dockerhost-1 ~#] /etc/sysconfig/docker-network

DOCKER_NETWORK_OPTIONS="--bip=172.17.0.1/16 --iptables=false --ip-masq=false --ip-forward=true"

[root@docker01 ~#] systemctl restart docker



[root@dockerhost-2 ~#] /etc/sysconfig/docker-network

DOCKER_NETWORK_OPTIONS="--bip=172.17.0.2/16 --iptables=false --ip-masq=false --ip-forward=true"

[root@docker02 ~#] systemctl restart docker



접속 경로의 연결을 위한 Open vSwitch Bridge와 VxLAN 설정


* dockerhost-1, dockerhost-2 에서 다음의 bash 스크립트를 작성해 두자(앞서 #1회의 내용을 스크립트 방식으로 바꾸어 보았다). 스크립트 내부의 커멘트를 참고하고, 붉은 표시된 부분을 잘 구분해서 사용해야 한다.

* 양쪽 호스트의 연결 방식은 네트워크 정보가 바뀐 것 외에는 전 편 #1에서의 방식과 거의 유사하다.


[root@dockerhost-1 ~]# vi set-vxlan-all.sh 

#!/bin/bash

#

# Making VxLAN L2 tunnel over L3 network

#

# 1. Lower part - OVS bridge & VxLAN tunnel to remote

#

if [ $# != 2 ]; then

  echo "Check params..."

  echo "Usage: $0 ovs_sw_ip/cidr target_host_ip"

  exit 1

fi

ovs-vsctl add-br ovs_sw0

ip addr add $1 dev ovs_sw0 && ip link set dev ovs_sw0 up

ovs-vsctl add-port ovs_sw0 vtep0 -- set interface vtep0 type=vxlan options:remote_ip=$2

#

# 2. VETH pair between OVS bridge and Linux bridge(docker0)

#

ip link add veth_sw0 type veth peer name veth_d0

ovs-vsctl add-port ovs_sw0 veth_sw0

brctl addif docker0 veth_d0

ip link set dev veth_sw0 up

ip link set dev veth_d0 up

#

echo "Done."

[root@dockerhost-1 ~]# chmod a+x set-vxlan-all.sh 

[root@dockerhost-1 ~]# ./set-vxlan-all.sh 172.31.0.1/12 10.255.30.10


[root@dockerhost-2 ~]# vi set-vxlan-all.sh 

내용은 dockerhost-1 와 같음

[root@dockerhost-2 ~]# chmod a+x set-vxlan-all.sh 

[root@dockerhost-2 ~]# ./set-vxlan-all.sh 172.31.0.2/12 10.255.20.10



여기까지 설정하였다면 그림에서의 하단(Open vSwitch 브리지간)의 터널 개통(VxLAN L2 터널)이 정상적으로 되었는지 확인해 보기로 하자

[root@dockerhost-1 ~]# ping 172.31.0.2

PING 172.31.0.2 (172.31.0.2) 56(84) bytes of data.

64 bytes from 172.31.0.2: icmp_seq=1 ttl=64 time=2.15 ms

64 bytes from 172.31.0.2: icmp_seq=2 ttl=64 time=3.47 ms

64 bytes from 172.31.0.2: icmp_seq=3 ttl=64 time=2.17 ms

^C

--- 172.31.0.2 ping statistics ---

3 packets transmitted, 3 received, 0% packet loss, time 2019ms

rtt min/avg/max/mdev = 2.157/2.599/3.471/0.618 ms

[root@dockerhost-1 ~]#


[root@dockerhost-2 ~]# ping 172.31.0.1

PING 172.31.0.1 (172.31.0.1) 56(84) bytes of data.

64 bytes from 172.31.0.1: icmp_seq=1 ttl=64 time=1.95 ms

64 bytes from 172.31.0.1: icmp_seq=2 ttl=64 time=6.44 ms

64 bytes from 172.31.0.1: icmp_seq=3 ttl=64 time=1.80 ms

^C

--- 172.31.0.1 ping statistics ---

3 packets transmitted, 3 received, 0% packet loss, time 2027ms

rtt min/avg/max/mdev = 1.809/3.400/6.440/2.150 ms

[root@dockerhost-2 ~]# 



컨테이너와 Linux Bridge 간의 Veth Pair를 통한 연결 


하단의 터널이 뚫렸으므로 상단의 연결선을 만들어 보자. 전 편 #1 에서와 다른 점은, 2개의 Docker 컨테이너가 동일한 네트워크(여기서의 예를 들면 172.17.0.0/16)를 가져야 하므로, 컨테이너의 IP를 직접 지정해 주어야 한다는 것이다.


하지만 아쉽게도 Docker 환경에서 기본적으로는 컨테이너의 IP를 직접 지정할 수 있는 방법은 User Defined Network(docker network create ... & docker run --net=myown --ip=... 형식) 외에는 따로 없으므로, 다음에 제공되는 스크립트를 이용해서 컨테이너의 내부 인터페이스(eth0)를 docker0 브리지와 직접 연결해 주어야 한다(Docker network namespace 영역을 직접 다룸). 처리 내용은 아래의 스크립트 내용에 담겨 있으니 자세히 보고 이해해 두자. 각 Docker 호스트에서 동일하게 set-docker-network.sh 라는 이름으로 스크립트 파일을 만들고 chmod a+x 로 실행 가능하게 설정해 둔다.


#!/bin/bash
# $1: Docker ID
# $2: Docker0 IP/CIDR
# $3: Docker0 Gateway
#
# Checking params
#
if [ $# != 3 ]; then
  echo "Check params..."
  echo "Usage: $0 docker-id-or-name docker-ip/cidr gateway"
  exit 1
fi
echo "Seting docker $1's network to... " $2", GW:" $3

#
# get pid of the docker
#
pid=$(sudo docker inspect -f '{{.State.Pid}}' $1)
# echo $pid

#
# Prepare docker network namespace
#
mkdir -p /var/run/netns
ln -s /proc/$pid/ns/net /var/run/netns/$pid
echo "Network namespace created"

#
# VETH pair create, link to docker0 linux bridge side
#
ip link add veth_doc$pid type veth peer name veth_con$pid
brctl addif docker0 veth_doc$pid
ip link set veth_doc$pid up
echo "Created veth pair and linked veth_doc$pid to docker0"

#
# Link VETH pair end-point to container's eth0
#
ip link set veth_con$pid netns $pid
ip netns exec $pid ip link set dev veth_con$pid name eth0
ip netns exec $pid ip link set eth0 up
echo "Linked veth_con$pid to container's eth0"

#
# Set network information of container's eth0
#
ip netns exec $pid ip addr add $2 dev eth0
ip netns exec $pid ip route add default via $3
echo "Set container's eth0 network ... Done"

 

dockerhost-1 호스트에서 새로운 Docker 컨테이너를 --net=none 으로 즉, 네트워크 없이 생성한다. 

[root@dockerhost-1 ~]# docker run -i -t --name=docker1 --net=none busybox /bin/sh

/ # ctrl+p, ctrl+q 로 빠져나온다


[root@dockerhost-1 ~]# ./set-docker-network.sh docker1 172.17.0.10/16 172.17.0.1

[root@dockerhost-1 ~]# docker attach docker1

/ # ifconfig

eth0      Link encap:Ethernet  HWaddr EA:88:7E:B1:E5:08  

          inet addr:172.17.0.10  Bcast:0.0.0.0  Mask:255.255.0.0

          inet6 addr: fe80::e888:7eff:feb1:e508/64 Scope:Link

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:71 errors:0 dropped:0 overruns:0 frame:0

          TX packets:47 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000 

          RX bytes:5743 (5.6 KiB)  TX bytes:4016 (3.9 KiB)


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:1 

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


/ # 

* docker1 부분은 이름 없이 생성한 경우는  docker ps 로 보이는 컨테이너 ID를 사용해도 된다



dockerhost-2 호스트에서 새로운 Docker 컨테이너를 같은 방식으로 생성하되 IP 부분만, 같은 네트워크의 다른 IP 주소로 바꾼다. 

[root@dockerhost-2 ~]# docker run -i -t --name=docker2 --net=none busybox /bin/sh

/ # ctrl+p, ctrl+q 로 빠져나온다


[root@dockerhost-2 ~]# ./set-docker-network.sh docker2 172.17.0.11/16 172.17.0.2

[root@dockerhost-2 ~]# docker attach docker2

/ # ifconfig

eth0      Link encap:Ethernet  HWaddr 2E:69:95:56:52:FA  

          inet addr:172.17.0.11  Bcast:0.0.0.0  Mask:255.255.0.0

          inet6 addr: fe80::2c69:95ff:fe56:52fa/64 Scope:Link

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:74 errors:0 dropped:0 overruns:0 frame:0

          TX packets:70 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000 

          RX bytes:6206 (6.0 KiB)  TX bytes:5262 (5.1 KiB)


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:1 

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


/ # ping 172.17.0.10

PING 172.17.0.10 (172.17.0.10): 56 data bytes

64 bytes from 172.17.0.10: seq=0 ttl=64 time=2.427 ms

64 bytes from 172.17.0.10: seq=1 ttl=64 time=2.061 ms

64 bytes from 172.17.0.10: seq=2 ttl=64 time=1.767 ms

64 bytes from 172.17.0.10: seq=3 ttl=64 time=2.203 ms

^C

--- 172.17.0.10 ping statistics ---

4 packets transmitted, 4 packets received, 0% packet loss

round-trip min/avg/max = 1.767/2.114/2.427 ms

/ #

* docker2 컨테이너에서 docker1 컨테이너로 ping 연결이 가능한지 확인



컨테이너간 연결 최종 확인


마지막으로, 실제로 2개의 컨테이너간의 연결이 확실한지를 nc(NetCat)을 통해서 테스트해 보자.


dockerhost-1 호스트의 컨테이너에서 라우팅 상태 확인 후, 80 포트로 nc 리스닝

/ # route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0

172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

/ # nc -l -p 8080 0.0.0.0

Test from docker2 container...

/ # 


dockerhost-2 호스트의 컨테이너에서 라우팅 상태 확인 후, docker1 컨테이너로 nc 접속(메시지 전송)

/ # route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

0.0.0.0         172.17.0.2      0.0.0.0         UG    0      0        0 eth0

172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

/ # echo "Test from docker2 container..." | nc 172.17.0.10 80

/ #




Linux 브리지, OVS 브리지 & VxLAN 설정 초기화를 위한 스크립트


Bonus: 생성된 브리지와 인터페이스 등의 설정 정보를 초기화하는 스크립트가 필요해 보인다. 생성된 순서를 되짚어 가며 삭제 등의 처리를 일일이 해야 하므로, 테스트 과정을 수정, 반복하는 경우의 귀찮은 일을 해결해 보려는 것으로 필수 과정은 아니니 참고로 보아 두면 좋겠다(스크립트 파일명은, 위의 set-vxlan-all.sh 의 설정 정보를 삭제해 주는 의미이므로 del-vxlan-all.sh 정도로 하면 되겠다).


#!/bin/bash
if [ $# != 1 ]; then
  echo "Check params..."
  echo "Usage: $0 ovs_sw_ip/cidr"
  exit 1
fi
# Turn off each port of veth pair
ip link set dev veth_d0 down
ip link set dev veth_sw0 down
# Detach port veth_d0 on docker0 linux-bridge
brctl delif docker0 veth_d0
# Detach port veth_sw0 on ovs_sw0 OVS-bridge, then remove it
ovs-vsctl del-port ovs_sw0 veth_sw0
ip link del veth_sw0
# vtep0, ovs_sw0
ovs-vsctl del-port ovs_sw0 vtep0
ip addr del $1 dev ovs_sw0
ip link set dev ovs_sw0 down
ovs-vsctl del-br ovs_sw0

* 17번째 라인의 $1 파라미터 부분은 해당 Docker 호스트에서 설정한 ovs_sw0 브리지에 할당된 IP/CIDR(여기서는 172.31.0.1/12 또는 172.31.0.2/12) 부분을 지정한다



- Barracuda -



[관련 글]


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


제목이 다소 길지만 앞으로 2회에 걸쳐서 정리할 내용은 Open vSwitch의 VxLAN 터널링을 이용하여 L2 network로 연결된 Docker 호스트의 Docker 컨테이너간 L3 통신 방법(1)과, L3 network로 연결된 Docker 호스트간 L2 통신 방법(2)에 대해 다루고자 한다. 본 1편에서 다루고자 하는 내용을 그림으로 나타내면 다음 그림과 같다. 도커/컨테이너에 대한 기본적인 개념 설명은 여기서는 다루지 않으며, 이후에 도커의 네트워크에 대해 간략히 따로 다룰 예정이다.




그림을 보면, 2개의 호스트가 Flat한 L2 Network로 연결되어 있고(SDN: Software-Defined-Network 개념으로 보면 Underlay) 각각의 호스트 내에 Docker Container 들은 서로 다른 네트워크를 가지며, VxLAN 터널을 통한 L3 연결(SDN 개념으로 보면 Overlay)가 가능한 구조임을 알 수 있다. 그림과 같은 네트워크를 설계할 때에 특히 주의해야 할 점은, 하단의 OVS Bridge의 네트워크(10.0.0.0/8)가 상단의 Linux Bridge(docker0)의 네트워크(10.x.0.0/16)를 포함하는 구조를 가져야 한다는 것이다(참고: http://www.ipaddressguide.com/cidr).



사전 준비

  • Flat Network로 연결된 Docker Host(VM) 2대
  • Docker Host #1: CentOS 7.3 Minimal Server(1611 ver), 192.168.10.163/16
  • Docker Host #2: CentOS 7.3 Minimal Server(1611 ver), 192.168.10.164/16



Docker & Open vSwitch 설치와 firewall 설정


* Docker 호스트에 EPEL 리포지터리와 RDO 프로젝트를 통해서 Docker와 Open vSwitch를 설치

(Open vSwich의 별도 설치 방법에 대해서는 http://bryan.wiki/276 참고)

yum clean all
yum install -y epel-release https://www.rdoproject.org/repos/rdo-release.rpm
yum install -y firewalld docker openvswitch bridge-utils
yum update -y
systemctl start openvswitch firewalld
systemctl enable openvswitch firewalld


* VxLAN 터널링을 위한 패킷은 UDP 4789, 8472 포트를 통해 전송된다. firewall 설정에서 이 2개 포트를 모두 개방

firewall-cmd --add-port=4789/udp --add-port=8472/udp
firewall-cmd --permanent --add-port=4789/udp --add-port=8472/udp


* trusted 영역에 대해 10.*.*.* 대역의 통신이 모두 가능하도록 개방(docker0 네트워크 10.x.0.0/16 을 서브넷으로 포함하는 상위 네트워크의 예: 10.0.0.0/8)

* 다른 예: docker0 네트워크를 172.17.0.0/16 이나 172.18.0.0/16 ... 등으로 할 경우에는 상위 네트워크를 172.16.0.0/12 로 하면 된다

firewall-cmd --permanent --zone=trusted --add-source=10.0.0.0/8


각각의 도커 호스트에서 Docker 네트워크 설정


* Docker 에서 네트워크가 가능하려면 --net=bridge(기본 모드) 또는 --net=host 옵션으로 컨테이너를 생성하게 된다. 컨테이너 생성시에 별도의 옵션을 지정하지 않으면 bridge 모드가 사용되는데, 여기서는 기본 모드인 bridge 모드를 사용할 것이며, /etc/sysconfig 디렉토리의 docker-network 설정 파일을 다음과 같이 설정한다.

[root@docker01 ~#] /etc/sysconfig/docker-network

DOCKER_NETWORK_OPTIONS="--bip=10.1.0.1/16 --iptables=false --ip-masq=false --ip-forward=true"

[root@docker01 ~#] systemctl restart docker


[root@docker02 ~#] /etc/sysconfig/docker-network

DOCKER_NETWORK_OPTIONS="--bip=10.2.0.1/16 --iptables=false --ip-masq=false --ip-forward=true"

[root@docker02 ~#] systemctl restart docker

--bip 는 Bridge IP 를 뜻하며, 컨테이너의 네트워크에서 자동적으로 10.1.0.1, 10.2.0.1 을 default gateway 로 바라보게 된다.

  


위와 같이 설정하고 Docker 컨테이너를 생성하면, docker01 호스트에서 첫 번째로 만들어지는 컨테이너의 IP는 10.1.0.2 가 될 것이다(docker02 에서는 10.2.0.2).


여기까지 끝이라면 좋겠지만 아쉽게도 그렇지 않다. 2개의 호스트에서 만든 각각의 컨테이너에서 서로 다른 컨테이너로 접속하려 하면? 당연히, 안된다. 서로 다른 네트워크간의 연결은 통로가 열려 있지 않으면 당연히 통신이 되지 않는다(아직 컨테이너 생성까지 진도는 나가지 않았지만, busybox 컨테이너를 docker01, docker02 에서 각각 만들고 docker01 호스트에서 ping 10.2.0.2 해보자. 당연히 안된다).



접속 경로의 연결을 위한 Open vSwitch Bridge와 VxLAN 설정


* docker01 에서는 다음과 같이 Open vSwitch 를 설정

[root@docker01 ~#] ovs-vsctl add-br ovs_sw0

[root@docker01 ~#] ip addr add 10.100.0.1/8 dev ovs_sw0 && ip link set dev ovs_sw0 up

[root@docker01 ~#] ovs-vsctl add-port ovs_sw0 vxlan0 -- set Interface vxlan0 type=vxlan options:remote_ip=192.168.10.164


* docker02 에서는 다음과 같이 Open vSwitch 를 설정

[root@docker02 ~#] ovs-vsctl add-br ovs_sw0

[root@docker02~#] ip addr add 10.100.0.2/8 dev ovs_sw0 && ip link set dev ovs_sw0 up

[root@docker02 ~#] ovs-vsctl add-port ovs_sw0 vxlan0 -- set Interface vxlan0 type=vxlan options:remote_ip=192.168.10.163


위의 그림에서 보듯이 각 Docker 호스트에 sw0 라는 Open vSwitch 브리지간의 터널을 개통하는 과정이다. 주의 깊게 보아야 하는 부분은 vxlan0 라는 VTEP(VxLAN Terminal End Point)가 상대편 Docker 호스트의 네트워크 IP를 바라보게 설정하는 것이다.


여기까지 설정하였다면 그림에서의 하단(Open vSwitch 브리지간)의 터널 개통이 정상적으로 되었는지 확인해 보기로 하자

[root@docker01 ~]# ping 10.100.0.2

PING 10.100.0.2 (10.100.0.2) 56(84) bytes of data.

64 bytes from 10.100.0.2: icmp_seq=1 ttl=64 time=2.43 ms

64 bytes from 10.100.0.2: icmp_seq=2 ttl=64 time=0.418 ms

64 bytes from 10.100.0.2: icmp_seq=3 ttl=64 time=0.333 ms

64 bytes from 10.100.0.2: icmp_seq=4 ttl=64 time=0.354 ms

64 bytes from 10.100.0.2: icmp_seq=5 ttl=64 time=0.410 ms

^C

--- 10.100.0.2 ping statistics ---

5 packets transmitted, 5 received, 0% packet loss, time 4001ms

rtt min/avg/max/mdev = 0.333/0.790/2.438/0.824 ms

10.100.0.1/8 과 10.100.0.2/8 은 동일한 네트워크 대역이고, VxLAN 터널을 통해 서로 연결이 잘 되고 있음을 확인할 수 있다.



Linux Bridge와 Open vSwitch Bridge 간의 연결선, Veth Pair


하단의 터널이 뚫렸으므로 상단의 연결선을 만들어 보자. Docker container(브리지 모드일 경우)는 기본적으로 Linux Bridge와 자동으로 연결이 맺어 진다. 즉, Docker 엔진이 docker0 라는 디폴트 브리지(Linux Bridge)에 Docker 컨테이너의 네트워크 디바이스를 내부적으로 연결시켜 주게 된다.


그렇다면 우리는, 그 아래의 docker0와 sw0 사이의 연결을 맺어 주기만 하면 되는데, 이를 위해 VETH pair 라고 하는, 양쪽 끝이 연결되어 있는 연결 쌍(pair)이라는 도구를 사용해서 위 아래의 접점에 붙여 주면 된다.


그림에서 처럼, docker0(Linux Bridge)와 sw0(Open vSwitch Bridge)간의 연결을, Veth Pair 라고 하는 연결선을 통해서 맺어 줄 수 있다. 다음과 같이 해 보자. 이 연결선의 End Point(끝점) 들 중에서 veth_d0는 docker0 쪽으로, veth_sw0는 sw0 쪽으로 연결한다.

[root@docker01 ~]# ip link add veth_sw0 type veth peer name veth_d0

[root@docker01 ~]# ovs-vsctl add-port ovs_sw0 veth_sw0

[root@docker01 ~]# brctl addif docker0 veth_d0

[root@docker01 ~]# ip link set dev veth_sw0 up

[root@docker01 ~]# ip link set dev veth_d0 up


docker02 에서도 위와 같이 설정해 주자. End Point 들의 이름은 docker01 에서와 다르게 주어도 무관하지만 script 작성 등의 자동화를 위해서는 최대한 일관성 유지를 하는 것이 좋다.

[root@docker02 ~]# ip link add veth_sw0 type veth peer name veth_d0

[root@docker02 ~]# ovs-vsctl add-port ovs_sw0 veth_sw0

[root@docker02 ~]# brctl addif docker0 veth_d0

[root@docker02 ~]# ip link set dev veth_sw0 up

[root@docker02 ~]# ip link set dev veth_d0 up


각각의 Docker 호스트에서 다음과 같이 실행해서 브리지 설정을 확인한다.

[root@docker01 ~]# ovs-vsctl show

f3c8825d-73ba-4ee5-a136-3db14a32e990

    Bridge "ovs_sw0"

        Port "veth_sw0"

            Interface "veth_sw0"

        Port "ovs_sw0"

            Interface "ovs_sw0"

                type: internal

        Port "vxlan0"

            Interface "vxlan0"

                type: vxlan

                options: {remote_ip="192.168.10.164"}

    ovs_version: "2.5.2"


[root@docker01 ~]# brctl show

bridge name bridge id   STP enabled interfaces

docker0   8000.0242f84de852 no    veth_d0



Docker 컨테이너를 만들고 L3 연결 확인


docker01, docker02 호스트에서 각각 컨테이너 생성, 네트워크 확인

[root@docker01 ~]# docker run -i -t busybox /bin/sh

/ # ifconfig

eth0      Link encap:Ethernet  HWaddr 02:42:0A:01:00:02  

          inet addr:10.1.0.2  Bcast:0.0.0.0  Mask:255.255.0.0

          inet6 addr: fe80::42:aff:fe01:2/64 Scope:Link

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:88 errors:0 dropped:0 overruns:0 frame:0

          TX packets:51 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:0 

          RX bytes:7240 (7.0 KiB)  TX bytes:4470 (4.3 KiB)


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:1 

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


[root@docker02 ~]# docker run -i -t busybox /bin/sh

/ # ifconfig

eth0      Link encap:Ethernet  HWaddr 02:42:0A:02:00:03  

          inet addr:10.2.0.3  Bcast:0.0.0.0  Mask:255.255.0.0

          inet6 addr: fe80::42:aff:fe02:3/64 Scope:Link

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:24 errors:0 dropped:0 overruns:0 frame:0

          TX packets:20 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:0 

          RX bytes:1880 (1.8 KiB)  TX bytes:1656 (1.6 KiB)


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:1 

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


docker01 호스트의 컨테이너에서 docker02 호스트의 컨테이너와 ping 테스트

/ # ping 10.2.0.2

PING 10.2.0.2 (10.2.0.2): 56 data bytes

64 bytes from 10.2.0.2: seq=0 ttl=63 time=1.730 ms

64 bytes from 10.2.0.2: seq=1 ttl=63 time=0.528 ms

64 bytes from 10.2.0.2: seq=2 ttl=63 time=0.321 ms



마지막으로, 실제로 2개의 컨테이너간의 연결이 확실한지를 nc(NetCat)을 통해서 테스트해 보자. busybox 에 기본적으로 있는 기능이다.


docker01 호스트의 컨테이너에서 8080 포트로 nc 리스닝

/ # nc -l -p 8080 0.0.0.0

Test from docker02 container...

/ # 


docker02 호스트의 컨테이너에서 docker01 호스트의 컨테이너로 nc 접속(메시지 전송)

/ # echo "Test from docker02 container..." | nc 10.1.0.2 8080

/ #




컨테이너에서 외부 네트워크로 접속하려면?


서로 다른 호스트에 각각 분리되어 있는 컨테이너간의 네트워크 연결은 가능해 졌지만, 원래의 Docker 네트워크 구조를 바꿔 버렸기 떄문에 현재의 설정으로는 컨테이너 사이의 네트워크만 가능할 뿐이다. 즉 외부 네트워크(192.168.10.* 를 벗어난)으로의 접속은 불가능한 상태다.


이 때에는 다음과 같이 firewall-cmd 를 통해서 목적지가 다를 경우 masquerading(NATting)이 되도록 설정해 주면 된다(각 docker 호스트에서 실행).


* (참고) 컨테이너 prompt 에서 exit 또는 ctrl-d 로 빠져나오게 되면 컨테이너가 생성되면서 실행된 터미널이 종료되면서 컨테이너는 STOP 상태로 빠지게 된다. 이 컨테이너에 재접속 하려면 "docker start" 로 다시 동작시켜야 하는데, STOP 되지 않도록 빠져나오려면 ctrl-p 와 ctrl-q 를 차례로 누르면 되며, 이후에 "docker attach" 로 재접속할 수 있게 된다.

[root@docker01 ~]# firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.0.0.0/8 ! -d 10.0.0.0/8 -j MASQUERADE

success

[root@docker01 ~]# firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.0.0.0/8 ! -d 10.0.0.0/8 -j MASQUERADE

success

[root@docker01 ~]# 

[root@docker01 ~]# docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

871a0f2765b6        busybox             "/bin/sh"           3 days ago          Up 3 days                               ecstatic_hopper

[root@docker01 ~]# docker attach 871a0f2765b6

/ # ping yahoo.com

PING yahoo.com (206.190.36.45): 56 data bytes

64 bytes from 206.190.36.45: seq=0 ttl=48 time=158.904 ms

64 bytes from 206.190.36.45: seq=1 ttl=48 time=159.080 ms

64 bytes from 206.190.36.45: seq=2 ttl=48 time=159.194 ms

64 bytes from 206.190.36.45: seq=3 ttl=48 time=164.183 ms

^C

--- yahoo.com ping statistics ---

4 packets transmitted, 4 packets received, 0% packet loss

round-trip min/avg/max = 158.904/160.340/164.183 ms



- Barracuda -



[관련 글]

2017/06/08 - [Technical/Cloud, 가상화] - Open vSwitch, VxLAN을 이용한 분산 Docker 컨테이너 간의 네트워크 연결(2/2)


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


Open vSwitch는 SDN을 구현하는 대표적 오픈소스 기술 중의 하나이다. Open vSwitch는 분산된 노드들간의 가상 터널을 구현하는 3가지 방법(GRE, VxLAN, IPSec)을 모두 지원하는데, 본 편은 다음에 이어질 포스팅에서 다룰 VxLAN에 대한 사전 준비 과정에 해당한다고 할 수 있다. 대상 OS는 CentOS 7.x 로 하고, Open vSwitch 를 설치하는 방법은 크게 3가지 정도를 생각해 볼 수 있겠다. 소스를 빌드하거나 RPM 빌드 또는 레드햇의 Epel 레포지터리를 통해 yum 으로 설치하는 방법이 있다. 여기서는 뒤의 2가지를 다뤄두도록 한다(자세한 설명은 생략하고 필수적으로 수행되는 script 위주로 note 함).


이미지 출처: openvswitch.org


RPM 빌드를 통한 설치


* 다운로드 주소는 openvswitch.org의 LTS(Long Term Service) series 에 해당하는 2.5.x 을 찾아서 주소를 복사해 오면 된다

# yum clean all

# yum update -y

# yum -y install wget bridge-utils openssl-devel gcc make python-devel openssl-devel kernel-devel graphviz kernel-debug-devel autoconf automake rpm-build redhat-rpm-config libtool python-twisted-core python-zope-interface PyQt4 desktop-file-utils libcap-ng-devel groff checkpolicy selinux-policy-devel

# adduser ovs

# su - ovs

ovs ~$ mkdir -p rpmbuild/SOURCES

ovs ~$ cd rpmbuild/SOURCES/

ovs ~$ wget http://openvswitch.org/releases/openvswitch-2.5.2.tar.gz

ovs ~$ tar xvzf openvswitch-2.5.2.tar.gz 

ovs ~$ rpmbuild -bb --nocheck openvswitch-2.5.2/rhel/openvswitch-fedora.spec

ovs ~$ exit

# yum localinstall /home/ovs/rpmbuild/RPMS/x86_64/openvswitch-2.5.2-1.el7.centos.x86_64.rpm -y

# systemctl start openvswitch

# systemctl enable openvswitch

# ovs-vsctl -V

ovs-vsctl (Open vSwitch) 2.5.2

Compiled May 31 2017 22:33:26

DB Schema 7.12.1



EPEL 을 통한 yum 설치


* EPEL(Extra Packages of Enterprise Linux): 레드햇에서 제공하는 별도의 패키지 저장소(주로 레드햇 계열인 RHEL, CentOS, Fedora 용도)

# yum install -y epel-release https://www.rdoproject.org/repos/rdo-release.rpm

# yum install -y openvswitch bridge-utils

# yum update -y

# systemctl start openvswitch

# systemctl enable openvswitch



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들