Assign Public IP to Docker Container (MACVLAN)

HPE DL380에는 LAN포트가 4개나 있다.
그냥 두고쓰기에는 조금 찜찜한 감이 있어서 Docker를 올려 컨테이너별로 IP를 부여하고싶었다.
Xen같은 경우는 XenServer에서 쉽게 설정할 수 있는데
Docker는 뭔가 좀 까다로워서 이렇게 글을 남긴다.

1. As-is

기본적으로 Docker의 컨테이너는 별도의 Private IP를 부여받는다.
이렇게 되면 당연히 host와 컨테이너, 컨테이너와 컨테이너간 통신이 안된다.
그렇기 때문에 Docker에는 서로가 통신할 수 있도록 Bridge 인터페이스가 존재한다.

다시말해 Bridge 인터페이스는

  • Bridge 인터페이스에 물린 모든 컨테이너들의 통신을 매개하고
  • 외부와 통신할 수 있도록 NAT(IP Masquerade)를 수행한다.

물론 이런 설정이라면 외부에서 내부의 특정 컨테이너에 접속하기는 불가능하다.
이를 위해 docker에서는 –port 옵션으로 포트포워딩을 지원한다.
또는 –expose 옵션으로 host의 포트와 연결할 수도 있다.

이 경우 여러 장단점이 있다.
간단하기도 하고, 포트포워딩만 해놓으면 알아서 다 해주니 편하기도 하다.
하지만 host가 병목이 된다는 중요한 문제가 있다.
게다가 내가 하고싶은 것은 포트포워딩이 아니라 IP의 할당이다.

2. To Be

macvlan은 물리 NIC에 가상의 sub-interface를 여러개 부여하는 기법이다.
이 경우 sub-interface들이 host와 통신을 못한다는 단점이 있으나
macvlan에 연결된 컨테이너들(sub-interface)끼리는 통신이 가능하다.

이 점이 bridge 네트워크와는 다른 점이다.
bridge 네트워크의 경우에는 컨테이너와 host가 서로 통신이 됐으나, macvlan은 그렇지 못하다.
macvlan은 애초에 sub-interface를 만드는 기술이지 NAT 등을 수행하는 기술은 아니기 때문이다.
이는 VM이 host와 통신하지 못하는 이유와 동일하다. (그래서 VM에도 bridge interface를 둔다.)

그렇다고 macvlan이 안좋은 것인가라고 하기에는 아직 이르다.
macvlan이 bridge에 비해 좋은 점은

  • 물리 PIC에 연결되어있어, 컨테이너에 물리주소를 부여할 수 있다는 점과
  • 같은 macvlan 네트워크에 있는 컨테이너끼리는 MAC으로 통신을 하기 때문에, 루프가 생기지 않고 심지어는 macvlan 인터페이스를 거치지않고 End-to-End로 통신할 수 있다는 점이다.

macvlan도 내부의 컨테이너들이 어떻게 통신하느냐에 따라 네 가지(Private, VEPA, Bridge, PassThru)의 Mode로 구분하는데, docker는 그 중 bridge방식밖에 지원하지 않는다.
더 자세한건 나도 잘 모르므로 이런 블로그를 참고하면 된다.

서론이 길었고, 실제로 컨테이너에 공인IP를 부여해보자

순서는 다음과 같다

  • 원하는 ip 대역에 맞게 macvlan 인터페이스를 생성한다.
  • macvlan을 이용하여 컨테이너를 생성한다.
  • (선택) host와 통신할 수 있게 추가로 interface를 만든다.

host와 컨테이너 간 통신할 수 있게 하는건 보안에 큰 위협이 된다.
필자는 웹서비스를 올릴 생각이라 host와 컨테이너 간 통신은 설정하지 않았다.

2-1. macvlan 인터페이스 생성

우선 필요한 것은 부여할 공인 ip의 subnet과 gateway 정보를 파악하는 것이다.
그리고 부모 인터페이스로 삼을 네트워크 인터페이스(eth0, eno0 등)를 선정한다.

docker network create -d 
  macvlan -o macvlan_mode=bridge \
  --subnet=192.168.0.0/24 \
  --gateway=192.168.0.1 \
  -o parent=eno1 \
  macvlan_bridge

이를 통해 macvlan_bridge라는 이름의 macvlan 인터페이스를 할당했다.

2-2. 컨테이너 생성

위에서 만든 macvlan_bridge에 컨테이너를 붙여본다.
아래와 같이 단순히 –net 옵션을 지정하는 것만으로 sub-interface가 생성된다.

docker run -dit \
  --net macvlan_bridge \
  --ip 192.168.0.10 \
  --publish 192.168.0.10:3306:3306 \
  --name db \
  --env MYSQL_ROOT_PASSWORD=123456789 \
  mysql

간단하게 MySQL 컨테이너를 올려보았다.
이 컨테이너는

  • macvlan_bridge 인터페이스에 연결되어있고
  • ip는 192.168.0.10을 할당받았으며
  • 192.168.0.10:3306으로 오는 패킷을 컨테이너의 3306에 포트포워딩하되, 다른 포트는 모두 close하였다.

지금 이 상태만으로도 외부, 즉 192.168.0.x 네트워크에서 이 컨테이너에 192.168.0.10:3306으로 접근할 수 있다.
물론 할당한 ip가 공인 ip라면 인터넷을 통해 접근할 수도 있다.

2-3. host – 컨테이너 통신

만약 host와 컨테이너 간 통신이 필요한 상황이라면 이 둘을 통신할 수 있도록 인터페이스를 하나 더 두어야 한다.
인터페이스를 두어 host와 컨테이너가 통신할 수 있도록 iptable을 변경하면 된다.

앞서 말했듯이 보안에 취약하다.
그래도 설정하고 싶다면 이 블로그를 참고하면 된다.
설정이 어렵진 않다.

Hits: 667

댓글 남기기