본문 바로가기
도커(Docker)

Docker Network

by Bentist 2022. 1. 27.

네트워크

네트워크는 두 대 이상의 컴퓨터가 서로 연결되어 있는 것을 의미한다. 도커 또한 컨테이너 관점에서는 마치 독립적인 환경을 가진 가상 머신이기 때문에 컨테이너가 다른 컨테이너 또는 컨테이너를 외부와 연결시키기 위해 각각의 컨테이너를 식별할 수 있는 IP주소가 필요하고, 도커에서는 이를 미리 가상 네트워크로 설정해놓았다.

도커는 호스트 컴퓨터(서버)의 물리적 네트워크와 도커의 가상 네트워크 두 가지로 네트워크가 이루어져 있다.

호스트 NIC와 컨테이너의 연결 통로, docker0(가상 브릿지)

Linux는 Docker를 설치하면 docker0라는 가상의 네트워크 인터페이스(연결 통로)가 자동으로 생성되는데, 가상 브리지(bridge)라고 부른다. 그림 맨 아래의 eth0호스트 서버의 물리 NIC로, NIC는 네트워크 인터페이스 컨트롤러(network interface controller, NIC)로 컴퓨터를 네트워크에 연결하여 통신하기 위해 사용하는 하드웨어 장치이다. 랜 카드, 네트워크 어댑터라고도 한다.
eth0는 호스트 서버의 네트워크 장치이기 때문에 이미 인터넷에 연결되어 있다고 가정하며, 이 물리 NIC가 NAPT 역할을 하는 Linux의 Iptables을 통해 docker0와 통신을 하게 된다. 그래서 컨테이너를 실행하게 되면 호스트는 NIC에서 직접 컨테이너와 연결하는 것이 아니라, docker0 브릿지를 통해 호스트 서버가 컨테이너와 연결될 수 있다. 가상 브리지인 docker0는 각각의 컨테이너와 통신을 하게 되고, 가상 브리지와 각 컨테이너는 MAC주소로 통신을 한다.

NAPT(Network Address Port Translation)

docker0와 물리 NIC사이의 NAPT(Network Address Port Translation)는 하나의 IP 주소를 여러 컴퓨터가 공유하는 기술로, IP주소와 포트 번호를 변환하는 기능이다. NAPT는 NAT를 확장한 기술인데, NAT는 공인 IP주소 부족 문제를 해결하기 위해 내부망에서는 사설 IP주소를 사용하여 통신을 하고, 외부망과의 통신에는 공인 IP 주소로 자동 변환하는 기술이다. NAPT는 IP주소에 포트 번호까지 동적으로 변환하기 때문에 하나의 공인 IP주소로 여러 대의 머신이 동시에 연결할 수 있다. Docker에서는 NAPT로 Linux의 Iptables를 사용한다.

Docker에서는 컨테이너 시작 시에 컨테이너 안에서 사용하고 있는 포트를 가상 브리지인 docker0에게 개방한다. 예를 들어 컨테이너 시작 시에 컨테이너 안의 웹 서버가 사용하는 80 포트는 기본적으로 docker0에 연결되어 있어서 호스트 서버에서 접근할 수 있고, 외부 네트워크에서 호스트 IP의 8080 포트에 접근하면 컨테이너 안의 80 포트로 연결되게끔 매핑시키게 된다.

그림처럼 호스트 IP의 각 포트에 서로 다른 컨테이너의 Private IP의 포트를 매핑시켜주는 것이 NAPT의 역할이라고 할 수 있다. 도커에서는 -p 옵션으로 호스트 PC에 8080 포트로 접속하면, 자동으로 해당 컨테이너의 80 포트로 연결한다. 당연히 호스트 PC IP의 8080 포트는 먼저 개방해놔야 하며, 아파치 웹 서버는 기본적으로 80번 포트를 디폴트로 사용하고 있다.

docker -d -p 8080:80 --name apacheweb2 httpd

만약 Docker for windows로 내 PC에서 도커를 실행하고 있다면, 웹 브라우저에서 localhost:8080로 접속하면 아파치 웹서버를 볼 수 있다. AWS EC2로 도커를 실행하고 있다면, 인바운드 규칙으로 가서 8080 포트를 추가해야 한다.

가상 NIC(vethxxxx) 인터페이스= etc0

컨테이너를 실행하면 생성되는 컨테이너의 가상 NIC이다. veth + 난수 형태로 컨테이너 입장에서는 가상 NIC를 eth0이라고 인식한다. 도커에서 NIC를 eth0으로 표현한 이유는 보통 리눅스에서 랜카드가 하나인 경우 장치 이름을 eth0으로 설정하며, 만약 두개를 장착하면 두번째 랜카드는 eth1로 명명하게 된다.

docker0 인터페이스는 실제 네트워크 어댑터(NIC)가 연결되어있지 않은 가상의 리눅스 브릿지로, 내부 로직에 의해 자동으로 172.17.0.1/16로 지정되어 있는데, 컨테이너가 생성되면 이 대역 안에서 IP를 할당받게 된다.

그래서 컨테이너를 생성하면 컨테이너들은 172.17.0.2/16부터 순차적으로 Private IP주소를 할당 받고, 컨테이너의 namespace에 가상 인터페이스인 eth0이 생성되고, 호스트 쪽에서는 veth를 접두어로 갖는 가상 네트워크 인터페이스가 생성되며 이 두 개가 연결된다. 여기서 중요한 점은 각 vethdocker0라는 이름의 브릿지에 연결됨으로써 컨테이너가 결국 외부와 통신할 수 있게 된다.

$ docker network inspect bridge 명령어를 통해 새로 생성한 컨테이너의 사설 IP주소가 172.17.0.2/16로 되어 있음을 확인해볼 수 있다.

ubuntu@ip-172-31-6-118:~$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "3ccead08e5f0e441c764f6efadb0d444cec615571761233dcf7231532365f118",
        "Created": "2022-01-20T08:16:23.187935541Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "7646c0a6fa4e065780a5055eabc33414eb7084dac168cca4f0a348dd6d9395b9": {
                "Name": "apacheweb2",
                "EndpointID": "079ee4f7d1fcd880a5812da3fb7cfd5dca39e0507ca50ca1f3351b537b0cbb50",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""

  • Bridge network (default)
    • 아무 설정 없이 사용했을 때 기본으로 설정되는 network
    • 호스트 PC와 별도의 가상의 네트워크 사용
    • 포트포워딩으로(-p 옵션) 외부 네트워크와 연결
    • 생성 명령어 - $docker network create --driver bridge [네트워크 이름]
  • Host network
    • container의 network 환경을 호스트의  network 환경과 동일하게 사용
    • 포트포워딩 없이 호스트가 container를 직접 제어
  • None network
    • network를 사용하지 않고 local 네트워크만 사용
    • 호스트나 외부와 연결이 단절된다.

EC2 서버의 특정 포트로 웹 서버와 주피터 노트북 컨테이너 연결

1. 웹서버

1) 먼저 localhost인 EC2 호스트 서버에 9999번 포트를 열어준다.

2) 아파치 웹서버는 기본적으로 80번 포트를 할당하므로 -p 옵션으로 호스트 PC의 IP에서 9999 포트로 접속하면, 자동으로 해당 컨테이너의 80 포트로 연결한다.

docker run -d -p 9999:80 --name apacheweb httpd

3) EC2 서버 IP:9999로 접속해보면 주피터 노트북이 잘 작동되고 있음을 확인할 수 있다.

2. 주피터 노트북

1) 먼저 localhost인 EC2 호스트 서버에 1111번 포트를 열어준다.

2) 주피터 노트북은 기본적으로 8888번 포트를 할당하므로 -p 옵션으로 호스트 PC의 IP에서 1111 포트로 접속하면, 자동으로 해당 컨테이너의 8888 포트로 연결한다.

docker run -d -p 1111:8888 jupyter/datascience-notebook

3) EC2 서버 IP:1111로 접속해보면 주피터 노트북이 잘 작동되고 있음을 확인할 수 있다.

우리가 도커가 아닌 윈도우에서 주피터 노트북을 설치하고 실행하면, 터미널이 자동으로 열리면서 다양한 명령문이 출력되는 표준 출력과 함께 주피터 노트북이 실행되었다. 이 터미널을 확인해보면 주피터 노트북에 접속에 필요한 token이 나오면서 자동 실행되는 것을 볼 수 있다.

로컬 컴퓨터에서 실행한 주피터 노트북 터미널 화면

4) 도커에서는 docker logs 컨테이너 ID 일부 명령어를 통해 token을 확인할 수 있다.

5) lab?token= 뒤의 암호만 복사하여 주피터 노트북에 입력

6) 접속 확인

댓글