DevOps/Docker

[Docker] Docker Network - 2

TTOII 2022. 5. 10. 20:07
728x90

✔️ 네트워크 플러그인 종류

  • bridge
  • host
  • null
  • ....

자주 사용하는 건 bridge와 host이다.

 

✔️ 브릿지 네트워크

✔️ 호스트에서 확인하는 방법

인터페이스 확인

ip addr show
  • docker0 : 브릿지
  • vethX : 가상 인터페이스

브릿지 확인 명령 설치

sudo apt install bridge-utils
brctl show

NAT 테이블 확인

sudo iptables -t nat -L -n
  • MASQUERADE: Source NAT

 

✔️ 컨테이너에서 확인하는 방법

sudo apt update
sudo apt install iproute2
ip addr show
ip route

 

✔️ 사용 예제

 vagrant@docker  ~  docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
b4481141a27b   bridge    bridge    local
4159dc8b15f3   host      host      local
42eb99a7c805   none      null      local

도커에서 제공하는 네트워크의 목록을 볼 수 있다.
우리가 기본적으로 사용하는 network는 브릿지 네트워크이다.

 vagrant@docker  ~  docker inspect c2
"Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "b4481141a27b0b11d720939fd66f241944615ba65a4d6c0303c692f47fd67ef6",
                    "EndpointID": "595bc1de6a883a6a7f890c30f53267658db35dc896e1e436ff283051bccb6bd5",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }

 

network ls 에서 봤던 브릿지 네트워크의 NETWORK ID와 컨테이너 실행 후 inspect로 확인한 NetworkID가 같다.

컨테이너를 만들 때 별도로 지정하지 않으면 기본적으로 사용하는 네트워크는 브릿지 네트워크이다.
도커 엔진을 설치하면 자동으로 브릿지 타입의 네트워크가 생성되며 일반적으로 컨테이너는 이것을 사용한다.

 vagrant@docker  ~  docker inspect b448
[
    {
        "Name": "bridge",
        "Id": "b4481141a27b0b11d720939fd66f241944615ba65a4d6c0303c692f47fd67ef6",
        ------------------------- 중간 생략 -------------------------
        "ConfigOnly": false,
        "Containers": { # 현재 브릿지 네트워크를 사용하는 컨테이너의 id 
            "c21f9b0352f8cb56e4437cf8260fe5238920a4c144ce23a71977b480e0d391d3": {
                "Name": "stoic_rosalind",
                "EndpointID": "595bc1de6a883a6a7f890c30f53267658db35dc896e1e436ff283051bccb6bd5",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

브릿지 네트워크 ID로 살펴보면 해당 브릿지 네트워크를 사용하는 컨테이너 목록을 확인할 수 있다.

 

✔️ 포트 포워딩이 설정된 컨테이너

sudo iptables -t nat -L -n
  • DNAT : Destination NAT
 vagrant@docker  ~  docker run -d -p 8080:80 httpd
0906c8c2e6287cb92bf9b489d1a84cc371ce4f47df05cd67c80a9c4e740ca953

httpd 컨테이너를 포트 포워딩하여 띄워보자

 vagrant@docker  ~  sudo iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:80

Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  0.0.0.0/0            0.0.0.0/0
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.2:80

Chain DOCKER에 DNAT라는 항목이 보인다. DNAT은 Destination NAT이다.
이 DNAT는 -p 옵션을 사용하여 컨테이너를 실행했을 때만 설정된다.

해석하면 출발지와 목적지는 어디든 상관없으나 tcp 프로토콜을 사용하면서 목적지 포트가 8080이면 그 ip를 172.17.0.2:80으로 바꾸라는 뜻이다.
여기서 172.17.0.2는 앞서 만든 컨테이너의 ip이다.

 vagrant@docker  ~  docker run -d -p 8081:80 httpd
aff4f4f25185679bb184189cc5e1a09faaf1c61779712b6103d5cc917c8cd411

 vagrant@docker  ~  sudo iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:80
MASQUERADE  tcp  --  172.17.0.3           172.17.0.3           tcp dpt:80

Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  0.0.0.0/0            0.0.0.0/0
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.2:80
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8081 to:172.17.0.3:80
 vagrant@docker  ~ 

다른 포트로 서비스하는 httpd 컨테이너를 하나 더 띄우고 iptables를 확인해보자
8081 dst 포트는 172.17.0.3의 80번 포트로 포트 포워딩을 하라고 설정되어 있다.

 

✔️ 네트워크 공유

--network : 연결할 네트워크의 이름 또는 id를 지정할 수 있다. 별도로 지정하지 않으면 기본 브릿지 네트워크에 연결된다.

✔️ 호스트 네트워크

호스트의 네트워크를 공유해서 사용한다.

 vagrant@docker  ~  docker run -d --network host httpd
0dae7d393815f8798e66f458d1dbc6b583cd721448f9ce52daf551a256c4a7e2

host 네트워크를 공유하면 지금까지와 다르게 포트를 지정하지 않아도 192.168.100.100으로 접속이 가능하다는 것이다.

어떻게 가능한 것일까 ?

 vagrant@docker  ~  docker exec -it 0d bash
 root@docker:/usr/local/apache2# apt update
 root@docker:/usr/local/apache2# apt install iproute2

컨테이너 내부로 들어가서 확인해보자

root@docker:/usr/local/apache2# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:3d:10:42:9f:64 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 44739sec preferred_lft 44739sec
    inet6 fe80::3d:10ff:fe42:9f64/64 scope link
       valid_lft forever preferred_lft forever
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:70:af:dd brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.100/24 brd 192.168.100.255 scope global enp0s8
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe70:afdd/64 scope link
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:c5:5d:8f:6e brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c5ff:fe5d:8f6e/64 scope link
       valid_lft forever preferred_lft forever

도커 호스트의 네트워크 인터페이스 항목과 완전히 일치하는 것을 알 수 있다.
호스트 네트워크를 사용한다는 것은 호스트의 네트워크 설정을 그대로 컨테이너에서도 사용한다는 것이다.

"Networks": {
                "host": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "4159dc8b15f36a734349b4e60711251bfac453d01fc30e75be69c1fa3cd3a6c6",
                    "EndpointID": "59ddf33600766d33a8ea0091bf74196e6ba341d0d8787f6ba8868bfdd865f346",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "",
                    "DriverOpts": null
                }
            }

 vagrant@docker  ~  docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
b4481141a27b   bridge    bridge    local
4159dc8b15f3   host      host      local
42eb99a7c805   none      null      local

NetworkID와 docker network ls로 확인한 NETWORK ID가 같음을 알 수 있다.

 vagrant@docker  ~  sudo ss -tnlp
State       Recv-Q      Send-Q             Local Address:Port             Peer Address:Port      Process
LISTEN      0           4096               127.0.0.53%lo:53                    0.0.0.0:*
 users:(("systemd-resolve",pid=582,fd=13))
LISTEN      0           128                      0.0.0.0:22                    0.0.0.0:*
 users:(("sshd",pid=690,fd=3))
LISTEN      0           511                            *:80                          *:*
 users:(("httpd",pid=34177,fd=4),("httpd",pid=34051,fd=4),("httpd",pid=34050,fd=4),("httpd",pid=34049,fd=4),("httpd",pid=34010,fd=4))
LISTEN      0           128                         [::]:22                       [::]:*
 users:(("sshd",pid=690,fd=4))

80번 포트가 httpd 에 의해 열려있다.

 vagrant@docker  ~  docker rm -f 0d
0d

 vagrant@docker  ~  sudo ss -tnlp
State         Recv-Q        Send-Q                Local Address:Port                 Peer Address:Port        Process
LISTEN        0             4096                  127.0.0.53%lo:53                        0.0.0.0:*            users:(("systemd-resolve",pid=582,fd=13))
LISTEN        0             128                         0.0.0.0:22                        0.0.0.0:*            users:(("sshd",pid=690,fd=3))
LISTEN        0             128                            [::]:22                           [::]:*            users:(("sshd",pid=690,fd=4))

이것은 결국 호스트의 port가 열리는 것이다.
네트워크 복제의 의미가 아닌 완전히 같은 네트워크를 쓰는 것이다.

vagrant@docker  ~  docker run -d --network host httpd
fa491094eb3ea0298a2eaddb1a5103396e8946412b89d3924da72314c67bd1bf
 vagrant@docker  ~  docker run -d --network host httpd
4e025f3bff9ca11c49323753c6091217d94b6193d089574e481650ee153146c0
 vagrant@docker  ~  docker ps -a
CONTAINER ID   IMAGE     COMMAND              CREATED          STATUS                     PORTS     NAMES
4e025f3bff9c   httpd     "httpd-foreground"   4 seconds ago    Exited (1) 3 seconds ago             crazy_lehmann
fa491094eb3e   httpd     "httpd-foreground"   12 seconds ago   Up 11 seconds                        angry_golick

만약 host 네트워크에 두번째 연결을 시도하면 Exited 된다.

 vagrant@docker  ~   docker logs 4e
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.2.1. Set the 'ServerName' directive globally to suppress this message
(98)Address already in use: AH00072: make_sock: could not bind to address [::]:80
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
AH00015: Unable to open logs

이미 다른 컨테이너에서 사용하고 있어 이 포트와 연결할 수 없다.
같은 포트를 서로 다른 프로세스에서 열 수 없다. 이것은 운영체제의 기본 원칙이다.

 

✔️ Null 네트워크

네트워크가 없는 컨테이너 생성

docker run -d --network none httpd
 vagrant@docker  ~  docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
 vagrant@docker  ~  docker run -d --network none httpd
9c74bf68c1656042eeac6b1b1cc154f88695b667e84bd287e9883f471058c367
 vagrant@docker  ~  docker exec -it 9c bash
root@9c74bf68c165:/usr/local/apache2# apt update
Err:1 http://deb.debian.org/debian bullseye InRelease
  Temporary failure resolving 'deb.debian.org'
Err:2 http://security.debian.org/debian-security bullseye-security InRelease
  Temporary failure resolving 'security.debian.org'
Err:3 http://deb.debian.org/debian bullseye-updates InRelease
  Temporary failure resolving 'deb.debian.org'
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
W: Failed to fetch http://deb.debian.org/debian/dists/bullseye/InRelease  Temporary failure resolving 'deb.debian.org'
W: Failed to fetch http://security.debian.org/debian-security/dists/bullseye-security/InRelease  Temporary failure resolving 'security.debian.org'
W: Failed to fetch http://deb.debian.org/debian/dists/bullseye-updates/InRelease  Temporary failure resolving 'deb.debian.org'
W: Some index files failed to download. They have been ignored, or old ones used instead.
root@9c74bf68c165:/usr/local/apache2#

이름에서 알 수 있듯이 none은 네트워크가 없다는 것이다.

 vagrant@docker  ~  docker pull ghcr.io/c1t1d0s7/network-multitool
Using default tag: latest
latest: Pulling from c1t1d0s7/network-multitool
ba3557a56b15: Pull complete
0e3bb546cb89: Pull complete
Digest: sha256:8a1a3bca4a684efe1401e4137790beec2d9d6a35f2768be80a6eaa6c313eabc4
Status: Downloaded newer image for ghcr.io/c1t1d0s7/network-multitool:latest
ghcr.io/c1t1d0s7/network-multitool:latest
 vagrant@docker  ~  docker run -it --network none ghcr.io/c1t1d0s7/network-multitool bash
bash-5.1# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
bash-5.1#

확인해봐도 loop back 외에는 아무런 인터페이스가 없다.

none 네트워크를 사용할 일이 많지는 않으나 그럼에도 none 네트워크의 존재 이유는 다음과 같다.
모든 어플리케이션이 반드시 네트워크 통신을 해야 하는 것은 아니다.
로컬에서만 작동하는 어플리케이션은 굳이 컨테이너에 네트워크를 제공할 필요가 없다.

 

✔️ 브릿지 네트워크 생성

브릿지 네트워크를 생성할 수 있다.

docker network create --driver bridge --subnet 192.168.200.0/24 --gateway 192.168.200.1 wordpress-network
 vagrant@docker  ~  docker network create --driver bridge --subnet 192.168.200.0/24 --gateway 192.168.200.1 wordpress-network
dd8cb6c53e9b803cc932b6b4b8cb63bfd014c1a2599547245a4333a9348fc6fb
 vagrant@docker  ~  docker network ls
NETWORK ID     NAME                DRIVER    SCOPE
b4481141a27b   bridge              bridge    local
4159dc8b15f3   host                host      local
42eb99a7c805   none                null      local
dd8cb6c53e9b   wordpress-network   bridge    local

subnet을 192.168.200.0/24으로 gateway를 192.168.200.1 으로 설정한다.

 vagrant@docker  ~  docker network inspect wordpress-network
[
    {
        "Name": "wordpress-network",
        "Id": "dd8cb6c53e9b803cc932b6b4b8cb63bfd014c1a2599547245a4333a9348fc6fb",
        "Created": "2022-05-09T12:13:05.261108274Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.200.0/24",
                    "Gateway": "192.168.200.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

생성한 브릿지 네트워크의 설정을 확인한다.

 vagrant@docker  ~  brctl show
bridge name     bridge id               STP enabled     interfaces
br-dd8cb6c53e9b         8000.0242fc17845a       no # 방금 만든 네트워크의 브릿지
docker0         8000.0242c55d8f6e       no
 vagrant@docker  ~  docker run --name wp-db -d -e MYSQL_ROOT_PASSWORD=P@ssw0rd -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wpadm -e MYSQL_PASSWORD=P@ssw0rd --restart always --cpus 0.5 --memory 1000m -v wp-db-vol:/var/lib/mysql --network wordpress-network mysql:5.7
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
c4b94b7ef4d4a67e17a5d53dba01f02d6dcd69bdf37d24e091276cb3d827484f
 vagrant@docker  ~  docker inspect wp-db
 "Networks": {
                "wordpress-network": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "c4b94b7ef4d4"
                    ],
                    "NetworkID": "dd8cb6c53e9b803cc932b6b4b8cb63bfd014c1a2599547245a4333a9348fc6fb",
                    "EndpointID": "efda2a1dfcadc444d4252f6f396bf34d23385e332c893d89bccdccd95d99cb6d",
                    "Gateway": "192.168.200.1",
                    "IPAddress": "192.168.200.2",
                    "IPPrefixLen": 24,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:c0:a8:c8:02",
                    "DriverOpts": null
                }
            }
 vagrant@docker  ~  docker run --name wp-web -d --link wp-db:mysql -e WORDPRESS_DB_HOST=mysql -e WORDPRESS_DB_USER=wpadm -e WORDPRESS_DB_PASSWORD=P@ssw0rd -e WORDPRESS_DB_NAME=wordpress --restart always --cpus 0.5 --memory 500m -p 80:80 --network wordpress-network wordpress:5-apache
 vagrant@docker  ~  docker inspect wp-web
"Networks": {
                "wordpress-network": {
                    "IPAMConfig": null,
                    "Links": [
                        "wp-db:mysql"
                    ],
                    "Aliases": [
                        "6c19e468fb2a"
                    ],
                    "NetworkID": "dd8cb6c53e9b803cc932b6b4b8cb63bfd014c1a2599547245a4333a9348fc6fb",
                    "EndpointID": "9b3356c11a788c31174ac04c0c9121de963180b02ffba9d77170982ea666cc7a",
                    "Gateway": "192.168.200.1",
                    "IPAddress": "192.168.200.3",
                    "IPPrefixLen": 24,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:c0:a8:c8:03",
                    "DriverOpts": null
                }
            }

728x90