Logo

Docker Compose 네트워크

Docker Compose는 여러 개의 컨테이너(container)로 구성된 애플리케이션을 관리하기 위한 간단한 오케스트레이션(Orchestration) 도구입니다. 여러 개의 컨테이너로 구성된 Docker Compose 애플리케이션 내에서 컨테이너 간의 통신은 어떻게 이루어질까요?

Docker 네트워크에 대해서 생소하신 분들은 관련 포스팅를 통해 먼저 기본 개념을 파악하시기를 권장드립니다.

Docker Compose 설정법이나 커맨드가 생소하신 분들은 아래 포스팅를 먼저 읽고 돌아오시기를 추천드립니다.

디폴트 네트워크

기본적으로 Docker Compose는 하나의 디폴트 네트워크에 모든 컨테이너를 연결합니다. 디폴트 네트워크의 이름은 docker-compose.yml가 위치한 디렉토리 이름 뒤에 _default가 붙습니다. 예를 들어, 디렉토리 이름이 our_app라면 디폴트 네트워크 이름은 our_app_default가 됩니다.

디폴트 네트워크의 이름은 Docker Compose로 애플리케이션을 올릴 때 어렵지 않게 확인할 수 있습니다. 왜냐하면 Compose는 먼저 네트워크를 생성해놓고 각 컨테이너를 구동한 후 네트워크에 연결시키기 때문입니다.

$ cd our_app
$ docker-compose up -d
Creating network "our_app_default" with the default driver
Creating our_app_db_1 ... done
Creating our_app_web_1 ... done

Docker Compose로 애플리케이션을 내릴 때는 반대 순서로 먼저 컨테이너를 종료/제거해놓고 제일 마지막에 네트워크를 제거합니다.

$ docker-compose down
Stopping our_app_web_1 ... done
Stopping our_app_db_1  ... done
Removing our_app_web_1 ... done
Removing our_app_db_1  ... done
Removing network our_app_default

애플리케이션이 돌아가고 있는 중에도 Docker 네트워크 목록을 조회하면 디폴트 네트워크가 확인됩니다.

docker network ls
NETWORK ID          NAME                   DRIVER              SCOPE
f1859120a0c3        bridge                 bridge              local
95b00551745b        host                   host                local
1f7202baa40a        none                   null                local
2539640ca106        our_app_default        bridge              local

컨테이너 간 통신

디폴트 네트워크 안에서 컨테이너 간의 통신에서는 서비스의 이름이 호스트명으로 사용됩니다.

예를 들어, web 서비스의 컨테이너에서 db 서비스의 컨테이너를 대상으로 ping 명령어를 날릴 수 있습니다. 디폴트 네트워크 상에서 db 서비스 컨테이너의 IP가 192.168.48.2인 것으로 확인이되네요.

$ docker-compose exec web ping db
PING db (192.168.48.2) 56(84) bytes of data.
64 bytes from our_app_db_1.our_app_default (192.168.48.2): icmp_seq=1 ttl=64 time=0.094 ms
64 bytes from our_app_db_1.our_app_default (192.168.48.2): icmp_seq=2 ttl=64 time=0.162 ms
64 bytes from our_app_db_1.our_app_default (192.168.48.2): icmp_seq=3 ttl=64 time=0.080 ms
64 bytes from our_app_db_1.our_app_default (192.168.48.2): icmp_seq=4 ttl=64 time=0.348 ms
64 bytes from our_app_db_1.our_app_default (192.168.48.2): icmp_seq=5 ttl=64 time=0.195 ms
64 bytes from our_app_db_1.our_app_default (192.168.48.2): icmp_seq=6 ttl=64 time=0.187 ms

컨테이넌 간 통신에서 주의할 점은 접속하는 위치가 디폴트 네트워크 내부냐 외부냐에 따라서 포트(port)가 달라질 수 있다는 것입니다.

예를 들어, docker-compose.ymlweb 서비스의 ports 설정이 아래와 같다면,

services:
  web:
    build: .
    ports:
      - "8001:8000"

호스트 컴퓨터에서 접속할 때는 8001 포트를 사용해야 하고, 같은 디폴트 네트워크 내의 다른 컨테이너에서 접속할 때는 포트 8000을 사용해야 합니다.

  • 호스트 컴퓨터에서 web 서비스 컨테이너 접속
$ curl -I localhost:8001
HTTP/1.1 200 OK
Date: Fri, 05 Jun 2020 02:05:10 GMT
Server: WSGIServer/0.2 CPython/3.8.2
Content-Type: text/html
X-Frame-Options: DENY
Content-Length: 16351
X-Content-Type-Options: nosniff
  • 같은 네트워크 내의 다른 컨테이너에서 web 서비스 컨테이너 접속
$ docker-compose exec alpine curl -I web:8000
HTTP/1.1 200 OK
Date: Fri, 05 Jun 2020 02:13:46 GMT
Server: WSGIServer/0.2 CPython/3.8.2
Content-Type: text/html
X-Frame-Options: DENY
Content-Length: 16351
X-Content-Type-Options: nosniff

커스텀 네트워크 추가

Docker Compose는 디플트 네트워크 뿐만 아니라 다른 네트워크도 필요에 따라 추가해줄 수 있습니다.

docker-compose.ymlnetworks 항목 아래에 our_net이라는 네트워크를 추가하고, web 서비스의 networks 항목 아래에 our_net 네트워크를 추가하겠습니다. 이렇게 설정을 하게되면 db 서비스는 디폴트 네트워크에만 연결되지만, web 서비스는 디폴트 네트워크 뿐만 아니라 our_net 네트워크에도 연결되게 됩니다.

services:
  web:
    build: .
    ports:
      - "8000:8000"
    networks:
      - default
      - our_net

  db:
    image: postgres
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres

networks:
  our_net:
    driver: bridge

Docker Compose로 애플리케이션을 올려보면 두 개의 네트워크가 생성되는 것을 알 수 있습니다.

$ docker-compose up -d
Creating network "our_app_default" with the default driver
Creating network "our_app_our_net" with driver "bridge"
Creating our_app_db_1 ... done
Creating our_app_web_1 ... done
$ our_app docker network ls
NETWORK ID          NAME                   DRIVER              SCOPE
f1859120a0c3        bridge                 bridge              local
95b00551745b        host                   host                local
1f7202baa40a        none                   null                local
2682634e6535        our_app_default        bridge              local
525403b38bbe        our_app_our_net        bridge              local

our_net은 Docker Compose 내부에서 정의된 네트워크 이므로 애플리케이션을 내릴 때 디폴트 네트워크와 함께 삭제됩니다.

$ docker-compose down
Stopping our_app_web_1 ... done
Stopping our_app_db_1  ... done
Removing our_app_web_1 ... done
Removing our_app_db_1  ... done
Removing network our_app_default
Removing network our_app_our_net

외부 네트워크 사용

Docker Compose가 제공하는 디폴트 네트워크 대신에 외부에서 미리 생성해놓은 다른 네트워크를 사용할 수도 있습니다.

먼저 our_net이라는 네트워크를 별도로 생성하겠습니다.

$ docker network create our_net
6d791b927c8c151c45a10ac13c62f3571ecf38a90756fd2ca1c62b7d3de804e8
$ docker network ls
NETWORK ID          NAME                   DRIVER              SCOPE
f1859120a0c3        bridge                 bridge              local
95b00551745b        host                   host                local
1f7202baa40a        none                   null                local
6d791b927c8c        our_net                bridge              local

그 다음, docker-compose.yml에서 default 네트워크의 external 옵션에 our_net을 설정합니다.

networks:
  default:
    external:
      name: our_net

이제 Docker Compose로 애프리케이션을 올리고, our_net 네트워크의 상세 정보를 확인해보면 Containers 항목에 두 개의 컨테이너가 연결된 것을 볼 수 있습니다.

$ docker-compose up -d
Creating our_app_db_1 ... done
Creating our_app_web_1 ... done
$ docker network inspect our_net
[
    {
        "Name": "our_net",
        "Id": "6d791b927c8c151c45a10ac13c62f3571ecf38a90756fd2ca1c62b7d3de804e8",
        "Created": "2020-06-06T15:55:05.3384403Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.208.0/20",
                    "Gateway": "192.168.208.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "7bcc8194e4018fa48328c6a1aa95a47829c77b850091bebaf1dd6661f5c79df9": {
                "Name": "our_app_db_1",
                "EndpointID": "4f3ffce8c6afc8d3de543859c1671bfe38b897c6e492a1be891de56ec2dfbf90",
                "MacAddress": "02:42:c0:a8:d0:02",
                "IPv4Address": "192.168.208.2/20",
                "IPv6Address": ""
            },
            "dda9d355041adc8f90063e8876fe20a2c804b84b55ece3b6860e3dfb139099c7": {
                "Name": "our_app_web_1",
                "EndpointID": "b0f93bbb39c9eb83d4f286250b90239632667c6b89954d57c9593f32a44c3033",
                "MacAddress": "02:42:c0:a8:d0:03",
                "IPv4Address": "192.168.208.3/20",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

외부에서 생성된 네트워크이므로 Docker Compose 애플리케이션을 내릴 때 해당 네트워크가 함께 삭제되지 않습니다.

$ docker-compose down
Stopping our_app_web_1 ... done
Stopping our_app_db_1  ... done
Removing our_app_web_1 ... done
Removing our_app_db_1  ... done
Network our_net is external, skipping

외부 네트워크를 잘 활용하면 서로 다른 Docker Compose에서 돌아가고 있는 컨테이너 간에도 연결도 가능하게 됩니다. 예를 들어, 첫 번째 Docker Compose의 디폴트 네트워크를 두 번째 Docker Compose의 커스텀 네트워크로 추가해주면, 두 번째 Docker Compose 내의 컨테이너도 첫 번째 Docker Compose의 디폴트 네트워크에 연결될 수 있습니다.

마치면서

Docker Compose는 디폴트 네트워크를 제공하기 때문에 별도 네트워크 관련 설정 없이도 모든 컨테이너가 연결되어 편리하게 사용할 수 있습니다. 약간의 설정을 통해서 다른 네트워크를 추가하거나 외부 네트워크를 사용하는 것도 가능합니다.