Docker 네트워크 이해

Docker 를 단순한 빠르고 가볍게 애플리케이션을 구성하게 해주는 컨테이너(Container) 로만 이해하는 건 좁은 시각이다. Docker 는 이러한 컨테이너들을 위한 네트워크 환경도 제공하고 있다. 이것은 Docker 가 종합적인 IT 인프라를 제공할 수 있다는 것을 의미한다.

Docker 네크워크 상태

Docker 를 호스트에 설치하면 우선적으로 다음과 같은 네트워크 장치가 하나 보인다.

컨테이너를 하나도 실행하지 않은 상태에서 단지 Docker 데몬이 실행만 되어도 이렇게 docker0 라는 네트워크 장치가 나타난다. 아이피는 172.17.0.1 이며 브로드캐스트(Broadcast) 가 172.17.255.255 인 것으로 봐서 docker0 장치가 속하는 네트워크 대역은 172.17.0.0/16 임을 알 수 있다.

docker0 는 Docker 가 실행되면서 자동으로 생성되는 가상의 네트워크 장치다. 네트워크 장치에도 여러가지 역할 혹은 모드를 부여할 수 있는데, docker0 가상네트워크는 브릿지모드로 동작하도록되어 있다.

docker0 가상네트워크가 브릿지 모드로 동작한다는 것은 이 브릿지에 연결한 네트워크 장비들을 외부와 통신하도록 해준다. 말 그대로 다리 역할을 하는 것이 docker0 이다.

이제 컨테이너 하나를 생성해 보자.

우분투(Ubuntu) 컨테이너 하나를 실행했다. 이 상태에서 호스트 네트워크를 살펴보면 다음과 같다.

없었던 veth83317a4 가 생겼다. 그리고 docker0 가상네트워크의 브릿지 상태는 다음과 같다.

docker0 에 veth83317a4 가상네트워크 장치가 연결되었다는 것을 볼 수 있다. 이것은 ‘ip link’ 나 ‘ip a’ 명령어로도 확인할 수 있다.

‘master docker0’ 가 이 가상네트워크 장치가 docker0 브릿지에 연결되었다는 것을 말해준다.

우분투 컨테이너의 네트워크 상태는 어떻게 될까? 다음과 같이 확인이 가능하다.

eth0 장치가 있고, 아이피는 172.17.0.2 로 할당되어 있다. 그리고 Default Gateway 는 다음과 같이 확인할 수 있다.

우분투 컨테이너의 Default Gateway 는 컨테이너를 실행한 호스트의 Docker 가상네트워크인 docker0 를 가르키고 있다. 결국 Docker 컨테이너의 모든 네트워크는 호스트의 docker0 를 가르키고 이는 모든 외부와의 트래픽은 docker0 을 통해서만 가능하다.

이를 도식으로 나타내면 다음과 같다.

Docker 컨테이너 네트워크

Docker0 브릿지 네트워크

이 docker0 브릿지 네트워크는 Docker 네트워크중에 하나다.

Docker 가 기본으로 제공하는 네트워크 기능들이 나온다. 여기서 bridge 에 대한 내용을 자세하 살펴보면 다음과 같다.

위 내용을 보면 IPAM 에 Config 를보면 네트워크 설정 내용이 보인다. 그리고 Options 에 “com.docker.network.bridge.name”: “docker0” 라는게 보인다. 이렇게 docker0 가상네트워크가 Docker 에서 생성한 Bridge 라는 것을 알 수 있다.

또, 현재 Bridge 에 연결되 실행되는 컨테이너 네트워크 정보도 함께 알 수 있다.

Docker 네트워크 특징

도커 호스트에 있는 컨테이너를 위한 가상네트워크 장비는 컨테이너와 peer 된 상태가 된다. 컨테이너를 하나씩 실행할때마다 하나씩 늘어난다. 컨테이너 갯수에 맞춰서 존재한다는 것이다.

도커 호스트에 컨테이너를 위한 가상네트워크 장비인 veth4327b92 는 컨테이너가 재시작될때마다 veth 를 제외한 숫자가 매번 바뀐다. 우분투 컨테이너를 재시작하고 난후 vethffc000e 로 변경됐다.

또, 위 도식에 보면 컨테이너 네트워크에 12라는 숫자가 보인다. 그리고 도커 호스트에 컨테이너를 위한 가상네트워크 번호가 13으로 나온다. 이를 보면 도커 컨테이너 네트크워크 숫자는 veth 가상호스트의 숫자보다 하나 작은 값을 가진다는 것을 알 수 있다. 이 숫자도 도커 컨테이너가 재시작될때마다 바뀌지만 1 작은 상태는 그대로 유지 된다. 이로써 도커 컨테이너 네트워크가 외부에 어느 veth 네트워크와 연결되는지를 알수 있게 된다.

또, 컨테이너에서 외부의 veth 가상네트워크를 볼 수가 없다. 이는 커널의 Network Namespace 로 격리를 하기 때문이다.

Docker Network Namespace

docker 의 Network Namespace 는 Docker 컨테이너가 실행될때마다 /var/run/docker/netns 디렉토리에 생성된다. 이에 대한 정보는 컨테이너 상세정보를 보면 알 수 있다.

이제 이것을 ip netns 에서 사용할 수 있도록 심볼릭 링크를 작성해 보자.

이렇게 한 후에 netns 를 살펴보면 다음과 같은 결과가 나온다.

이것을 이용하면 네트워크 정보를 확인할 수 있다.

Network Namespace 를 통해서 컨테이너에 네트워크 관련 명령어를 내릴 수 있다.

docker0 와 eth0 의 연결: iptables -t nat

docker0 에서 외부 네트워크 연결을 위해서 특별한 프로그램을 별도 운영하진 않는다. 단지, NAT 를 위한 Iptables 를 세팅한다. 이것은 오래전에 리눅스 박스를 라우터 장비로 이용하기 위한 방법인(지금에 비교를 하자면 리눅스 박스를 공유기로 이용하는 방법이다.) 마스커레이드(MASQUERADE) 과 완전 동일하다.

docker0 가상네트워크를 공유기로서 동작하도록 설정하는 것이다. iptables 를 한번 봐보자.

패킷이 docker 컨테이너로 들어올때는 PREROUTING 을 거치고 외부로 나갈때는 POSTROUTING 을 거치게 된다.

만일 iptables 명령어로 rule 를 무효화 하게 되면 docker 컨테이너는 외부와 통신하는게 불가능해 진다.

Inter-Container Communication

Docker 의 네트워크는 기본적으로 Inter-Container Communication 이다. 여러개의 컨테이너를 생성하면 각 컨테이너사이에 통신이 가능하다.

만일 이기능을 끄고 싶다면 docker 를 기동할 때에 다음과 같이 옵션을 주면 된다.

혹은 daemon.json 파일을 다음과 같이 작성해서 서비스를 재시작하는 방법도 있다.

이렇게 한 후에 컨테이너 사이에 통신은 불가능해 진다. 이것을 도커 컨테이너 네트워크 격리(Docker Container Network Isolation) 라고해서 구글 검색을 하면 꽤 많은 내용이 나온다.

왜 도커의 네트워크를 격리해야하는가? 이것을 잘 이용하면 네트워크 설계를 보다 보안성을 강화하면서 안전하게 설계할 수 있다. 이것은 마치 AWS 에서 VPC 설계시에 라우터를 조작해 네트워크를 구성하는것과 흡사하다.

이런것을 보면 Docker 는 단순한 컨테이너로서의 기능만을 위한게 아니라는 것을 알 수 있다. 네트워크 레이어 설계도 Docker 를 이해하는데 매우 중요한 요소다.

docker0 브릿지 네트워크 ip 변경

구글을 검색하면 많은 글들이 존재하는데, 최신의 docker 에서는 그렇게 많은 노력을 기울이지 않아도 docker0 브릿지 네트워크의 기본 ip 를 변경할 수 있다.

docker0 브릿지의 기본 네트워크 ip 정보를 변경하는 것은 서브넷, 게이트웨이등을 함께 변경하는 것을 뜻한다. 이는 /etc/docker/daemon.json 파일에서 bind ip 를 지정함으로써 쉽게 바꿀 수 있다.

이렇게 변경하고자하는 아이피를 지정하고 docker 를 다시 시작하면 된다. docker 를 재시작해야하는 일이기 때문에 많은 컨테이너가 운영중일때에 하기에는 문제가 있다.

따라서 이런경우에는 default 브릿지인 docker0 를 애써 변경하기 보다는 또다른 브릿지를 하나 생성하고 이것을 각각 컨테이너에 붙이는 방법으로 변경하는 것이 좋다.

사용자가 정의한 브릿지를 생성하는것을 사용자 정의 네트워크(User-Defined Network) 라고 한다.

Post a comment

You may use the following HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">