Contents

OpenVSwitch

리눅스 브릿지는 딱 브릿지만의 역할을 한다. 도커에서 기본으로 지원하는 네트워크 로 간단하게 사용 할 수 있다는 장점이 있는 반면, 할 수 있는게 별로 없다는 단점도 있다. SaaS나 PaaS 인프라를 구축 할경우 QoS(네트워크 자원 제한), ACL, SPAN 룰, 네트워크 격리 등이 필요한데, 할 수가 없다. 결국 OVS를 이용해야 한다.

도커 1.11 버전은 bridge, host, none 세 개의 네트워크를 지원한다. docker network 명령으로 지원하는 네트워크 목록을 확인 할 수 있다.
# docker network ls
NETWORK ID          NAME                DRIVER
e720cd261b55        bridge              bridge              
997416d39d98        host                host                
7ea7e6fc876b        none                null                
나는 지금까지 리눅스 브릿지를 ovs 브릿지로 바꾸고, 네임스페이드와 veth(가상 네트워크 디바이스), ovs-vsctl을 이용해서 직접 ovs를 제어하는 방법으로 ovs 네트워크를 만들었다. 자세한 내용은 Docker 레퍼런스 네트워크문서를 참고하자.

쓸만한 방법이고 잘 작동하기는 하지만 네임스페이스와 veth 특히 컨테이너 네트워크를 직접 관리해야 한다는 단점이 있다. 컨테이너에 IP를 직접 할당해야 한다는 얘기인데, 이 경우 IP의 중복 사용을 막기 위해서 데이터베이스에 IP와 MAC 같은 자원을 관리해야 한다. 크게 어렵지 않은 작업이라고 생각 할 수 있겠는데, 소프트웨어는 최대한 단순하게 개발해야 한다는게 내 지론이다. 코드가 한줄 추가 되면, 2개의 버그가 생겨나기 마련이다. 가능하면 도커에 맡기는게 좋다. 그래서 도커로 할 수 있는 솔류션을 찾기 시작했다.

docker-ovs-plugin

도커는 볼륨과 네트워크에 대한 플러그인을 제공한다. 나는 ovs 플러그인을 개발해서 도커의 네트워크를 확장하기로 했다. 물론 새로 개발하진 않을 거다. docker-ovs-plugin이라는 오픈소스 기반의 플러그인이 있었다. docker-ovs-plugin을 사용하기로(필요하면 수정한다)했다. docker-ovs-plugin(이하 ovsp)에 대한 자세한 내용은 github.com - cocker-ovs-plugin에서 확인하자.

도커 1.9 이상이 필요하다. (2016년 6월 6일)현재 내가 사용하는 도커 버전은 1.11이니 조건을 만족한다. 호스트에 openvswitch를 설치하자.
# apt-get install openvswitch-common openvswitch-switch
openvswitch 커널모듈을 올린다.
# modeprobe openvswitch
# lsmode openvswitch
openvswitch            90112  0
libcrc32c              16384  1 openvswitch

ovsp 플러그인은 도커 이미지로 제공한다. docker-compose.yml 파일을 만들자. 그전에 docker-compose 패키지를 설치하는 것도 잊지 말자. docker-compose는 하나 이상의 컨테이너로 이루어진 애플리케이션을 실행할 때 사용한다.
# apt-get install docker-compose
아래는 docker-compose.yml 파일이다.
plugin:
  image: gopher-net/ovs-plugin
  volumes:
    - /run/docker/plugins:/run/docker/plugins
    - /var/run/docker.sock:/var/run/docker.sock
  net: host
  stdin_open: true
  tty: true
  privileged: true
  command: -d

ovs:
  image: socketplane/openvswitch:2.3.2
  cap_add:
    - NET_ADMIN
  net: host
docker-compose.yml 파일을 로드한다.
# docker-compose up -d
Recreating root_ovs_1...
Recreating root_plugin_1...
실행하면 root_ovs_1과 root_plugin_1 두 개의 컨테이너가 올라온다. 이들 컨테이너의 역할은 다음과 같다.
  • root_ovs_1 : openvswitch를 관리하는 컨테이너다. 여기에 ovs 브릿지가 올라간다.
  • root_plugin_1 : docker ovs plugin 데몬 컨테이너다. 가상 인터페이스를 만들어서 ovs 브릿지에 붙이고 다른 하나는 컨테이너에 붙이는 일을 한다. 컨테이너에 붙인 가상 인터페이스에 대한 네트워크 작업도 수행한다.
이제 테스트를 진행해보자. 먼저 ovs 도커 네트워크를 만든다. 네트워크이름은 mynet이다.
# docker network create -d ovs mynet
185f911ef58acbb5dd8d621c262c6c7b2ba2e3a193f95a96cfcd9aa8bd08da38

ovs 네트워크가 올라온 걸 확인 할 수 있을 것이다.
# docker network ls
NETWORK ID          NAME                DRIVER
e720cd261b55        bridge              bridge              
997416d39d98        host                host                
185f911ef58a        mynet               ovs                 
7ea7e6fc876b        none                null                

컨테이너를 mynet 네트워크로 실행했다.
# docker run -it --net=mynet --name=mytest ubuntu /bin/bash

컨테이너가 mynet 네트워크로 올라왔는지 확인해 보자. 앞서 root_ovs_1이 ovs 브릿지를 관리한다고 했다. 이 컨테이너에 접속해서 ovs 브릿지 정보를 읽어보자.
# docker exec -it root_ovs_1 sh
# ovs-vsctl show
27f3b983-999b-4b24-b8ba-980aeb4a4bd7
    Manager "ptcp:6640"
        is_connected: true
    Bridge "ovsbr-185f9"
        Port "ovs-veth0-9a05e"
            Interface "ovs-veth0-9a05e"
                type: system
        Port "ovsbr-185f9"
            Interface "ovsbr-185f9"
                type: internal
    ovs_version: "2.3.2"
ovs 브릿지 ovsbr-195f9가 보인다. 그리고 ovs-veth0-9a05e포트가 보인다. 이 인터페이스가 mytest의 가상 인터페이스에 연결된 인터페이스다. mytest의 Inspect 정보를 보자.
# docker inspect mytest
.... 생략
    "NetworkSettings": {
        "Bridge": "",
        "SandboxID": "951846636ebfd67d3748ada12e622267e9d09c10f627e737e211a163556d7956",
        "HairpinMode": false,
        "LinkLocalIPv6Address": "",
        "LinkLocalIPv6PrefixLen": 0,
        "Ports": {},
        "SandboxKey": "/var/run/docker/netns/951846636ebf",
        "SecondaryIPAddresses": null,
        "SecondaryIPv6Addresses": null,
        "EndpointID": "",
        "Gateway": "",
        "GlobalIPv6Address": "",
        "GlobalIPv6PrefixLen": 0,
        "IPAddress": "",
        "IPPrefixLen": 0,
        "IPv6Gateway": "",
        "MacAddress": "",
        "Networks": {
            "mynet": {
                "IPAMConfig": null,
                "Links": null,
                "Aliases": null,
                "NetworkID": "185f911ef58acbb5dd8d621c262c6c7b2ba2e3a193f95a96cfcd9aa8bd08da38",
                "EndpointID": "9a05e5b4c7734953ba8d08d5b7c3910a5592a2768f95376e61ac22532f010465",
                "Gateway": "172.18.0.1",
                "IPAddress": "172.18.0.2",
                "IPPrefixLen": 16,
                "IPv6Gateway": "",
                "GlobalIPv6Address": "",
                "GlobalIPv6PrefixLen": 0,
                "MacAddress": ""
            }
        }
    }
.... 생략
NetworkID의 첫 5자 185f9가 브릿지의 이름이고, EndpointID의 첫 5자 9a05e가 브릿지에 연결된 포트의 이름이다.

ovs port 이름은 가상 인터페이스에 대한 QoS, ACL의 설정에 사용 할 수 있다. QoS에 대한 자세한 내용은 OpenVSwitch에서의 QoS Policing 문서를 참고하자.