Tam's Blog

<- Quay về trang chủ

Networking (5) Docker network - bridge driver

Tiếp tục chuỗi bài viết tìm hiểu về virtual netowrk interface, trái ngược với những bài viết trước hơi mang tính lí thuyết, bài viết hôm nay sẽ có tính ứng dụng hơn, đó là cách docker sử dụng virtual ethernetvirtual bridge để hiện thực một phần hệ thống network.

Dựa theo tài liệu của Docker, bridge là diver mặc định của docker network, bridge driver cho phép các container chạy trên cùng 1 máy vật lý giao tiếp với nhau, đối với các container chạy trên các máy vật lý khác nhau, chúng ta cần sử dụng các driver network khác như host, NAT, overlay (e.g VxLAN),...

Overview

Các container chạy trong các namespace khác nhau với sự độc lập về tài nguyên, network, hình dưới mô tả cách docker sử dụng bridge driver.

docker-network-bridge

bridge driver cho phép các container trong cùng 1 bridge giao tiếp với nhau, có 2 thành phần trong mô hình này:

Các gói tin sẽ đi từ trong namespace ra máy chủ, qua bridge rồi đi vào namespace còn lại, bridge đóng vai trò như 1 gateway và điều chuyển các gói tin ethernet.

Ví dụ

Mình sẽ chạy thử một ví dụ và capture gói tin bằng tcpdump để chứng minh flow ở trên.

Đầu tiên, tạo mới 1 network với driver bridge có tên bridge_example và kiểm tra bằng lệnh docker network ls

docker network create \
    --driver bridge \
    --subnet 192.180.1.0/24 \
    --gateway 192.180.1.1 \
    bridge_example

# verify
docker network ls

NETWORK ID     NAME             DRIVER    SCOPE
8e2893b1f2cc   bridge_example   bridge    local

Tiếp tục chạy 2 container và sử dụng driver vừa mới tạo.

docker run -t -d --privileged \
    --network bridge_example \
    --name c-test-1 ubuntu

docker run -t -d --privileged \
    --network bridge_example \
    --name c-test-2 ubuntu

# verify
docker container ps
docker network inspect bridge_example

Kết quả khi kiểm tra bridge bridge_example (mình đã lược bỏ bớt những phần không cần thiết)

[
    {
        "Name": "bridge_example",
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.180.1.0/24",
                    "Gateway": "192.180.1.1"
                }
            ]
        },
        "Containers": {
            "784ce52efd837b214ade8f5b124c42e94a87f71230028fa7365251853d19fc67": {
                "Name": "c-test-2",
                "EndpointID": "92aab0450cdba799d9a6a11f464f7d4a7e625d41b9b55c8be2d63b215db6f271",
                "MacAddress": "02:42:c0:b4:01:03",
                "IPv4Address": "192.180.1.3/24",
                "IPv6Address": ""
            },
            "fb8ec7decde7d57937d16d4da20ec00c36ca938af8896b599c6d5e942008e1dc": {
                "Name": "c-test-1",
                "EndpointID": "3de362f05f3e6c29898a726a8116191fb38a9609dd53a6f473a59bda1b6a940b",
                "MacAddress": "02:42:c0:b4:01:02",
                "IPv4Address": "192.180.1.2/24",
                "IPv6Address": ""
            }
        }
    }
]

Kết quả cho thấy 2 containers đang sử dụng bridge bridge_example.

Ở máy chủ, kiểm tra các virtual network interface bằng lệnh ifconfig

br-8e2893b1f2cc Link encap:Ethernet  HWaddr 02:42:11:82:AD:98
          inet addr:192.180.1.1  Bcast:192.180.1.255  Mask:255.255.255.0
          inet6 addr: fe80::42:11ff:fe82:ad98/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7997 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9651 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:420682 (410.8 KiB)  TX bytes:26921283 (25.6 MiB)

docker0   Link encap:Ethernet  HWaddr 02:42:9E:E4:B1:D5
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:9eff:fee4:b1d5/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:119202 errors:0 dropped:0 overruns:0 frame:0
          TX packets:159094 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:6285502 (5.9 MiB)  TX bytes:322699596 (307.7 MiB)

eth0      Link encap:Ethernet  HWaddr D6:A6:FD:A7:05:37
          inet addr:192.168.65.4  Bcast:0.0.0.0  Mask:255.255.255.255
          inet6 addr: fe80::d4a6:fdff:fea7:537/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:171798 errors:0 dropped:0 overruns:0 frame:0
          TX packets:129430 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:356622698 (340.1 MiB)  TX bytes:8690969 (8.2 MiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:60 errors:0 dropped:0 overruns:0 frame:0
          TX packets:60 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:5156 (5.0 KiB)  TX bytes:5156 (5.0 KiB)

veth24d52fa Link encap:Ethernet  HWaddr C2:9A:A8:9E:3B:2D
          inet6 addr: fe80::c09a:a8ff:fe9e:3b2d/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:42 errors:0 dropped:0 overruns:0 frame:0
          TX packets:58 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:3108 (3.0 KiB)  TX bytes:4296 (4.1 KiB)

veth98a7f28 Link encap:Ethernet  HWaddr 82:B2:D9:7D:4C:55
          inet6 addr: fe80::80b2:d9ff:fe7d:4c55/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8018 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9712 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:534194 (521.6 KiB)  TX bytes:26925817 (25.6 MiB)

Có 3 interface liên quan đến bridge driver và 2 containers, đó là br-8e2893b1f2cc, veth24d52faveth98a7f28, các gói tin sẽ đi qua các interface này.

Mọi thứ đã được chuẩn bị xong, mình sẽ sử dụng lệnh ping để kiểm tra sự giao tiếp giữa 2 container này. Nếu container không có lệnh ping, bạn có thể cài đặt bằng lệnh:

apt-get update -y & apt-get install -y iputils-ping

Ở container c-test-1, chạy lệnh:

ping -c 2 192.180.1.3

# result
PING 192.180.1.3 (192.180.1.3) 56(84) bytes of data.
64 bytes from 192.180.1.3: icmp_seq=1 ttl=64 time=0.569 ms
64 bytes from 192.180.1.3: icmp_seq=2 ttl=64 time=0.144 ms

--- 192.180.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1024ms
rtt min/avg/max/mdev = 0.144/0.356/0.569/0.212 ms

Kết quả cho thấy 2 container có thể giao tiếp được với nhau, sử dụng tcpdump ở máy chủ để kiểm tra dòng của gói tin:

tcpdump -n -i any
# result
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
10:32:50.205517 veth98a7f28 P   IP 192.180.1.2 > 192.180.1.3: ICMP echo request, id 132, seq 1, length 64
10:32:50.205822 veth24d52fa Out IP 192.180.1.2 > 192.180.1.3: ICMP echo request, id 132, seq 1, length 64
10:32:50.206052 veth24d52fa P   IP 192.180.1.3 > 192.180.1.2: ICMP echo reply, id 132, seq 1, length 64
10:32:50.206067 veth98a7f28 Out IP 192.180.1.3 > 192.180.1.2: ICMP echo reply, id 132, seq 1, length 64
10:32:51.208395 veth98a7f28 P   IP 192.180.1.2 > 192.180.1.3: ICMP echo request, id 132, seq 2, length 64
10:32:51.208470 veth24d52fa Out IP 192.180.1.2 > 192.180.1.3: ICMP echo request, id 132, seq 2, length 64
10:32:51.208587 veth24d52fa P   IP 192.180.1.3 > 192.180.1.2: ICMP echo reply, id 132, seq 2, length 64
10:32:51.208607 veth98a7f28 Out IP 192.180.1.3 > 192.180.1.2: ICMP echo reply, id 132, seq 2, length 64


tcpdunp -n -i br-8e2893b1f2cc
# result
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on br-8e2893b1f2cc, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:35:06.201625 IP 192.180.1.2 > 192.180.1.3: ICMP echo request, id 133, seq 1, length 64
10:35:06.201887 IP 192.180.1.3 > 192.180.1.2: ICMP echo reply, id 133, seq 1, length 64
10:35:07.205823 IP 192.180.1.2 > 192.180.1.3: ICMP echo request, id 133, seq 2, length 64
10:35:07.205959 IP 192.180.1.3 > 192.180.1.2: ICMP echo reply, id 133, seq 2, length 64

Ở đây mình phải sử dụng câu lệnh thứ 2 để bắt gói tin trên bridge bởi vì lệnh tcpdump -n -i any sẽ không bắt được những gói tin trên các interface mà địa chỉ đích trên gói tin không phải là nó (tham khảo: tcpdump-any).

Các gói tin ICMP request đi từ veth veth98a7f28 đến bridge br-8e2893b1f2cc rồi vào veth veth24d52fa, các gói tin ICMP reply sẽ có hướng ngược lại.

Tiếp theo, kiểm tra mối liên hệ giữa bridge và 2 veth bằng lệnh brctl showbrctl showmacs br-8e2893b1f2cc

brctl show
# result
bridge name         bridge id           STP enabled     interfaces
br-8e2893b1f2cc     8000.02421182ad98   no              veth98a7f28
                                                        veth24d52fa

brctl showmacs br-8e2893b1f2cc
#result
port no	mac addr		is local?	ageing timer
  1	82:b2:d9:7d:4c:55	yes		   0.00
  1	82:b2:d9:7d:4c:55	yes		   0.00
  2	c2:9a:a8:9e:3b:2d	yes		   0.00
  2	c2:9a:a8:9e:3b:2d	yes		   0.00

Nếu sử dụng địa chỉ MAC từ kết quả ở trên để kiểm tra lại với lệnh ifconfig, bạn sẽ thấy 2 địa chỉ MAC sẽ ứng với 2 veth, như vậy lúc này bridge sẽ có vai trò như 1 switch và điều chuyển gói tin ethernet dựa trên địa chỉ MAC.

Tổng kết

Thông qua bài viết này, mình đã phân tích cách docker sử dụng driver bridge và kiểm chứng bằng các công cụ network như tcpdump, ifconfig, ping,... Trong xu hướng công nghệ hiện nay, docker có thể được xem là 1 phần không thể thiếu trong công việc hàng ngày của mỗi lập trình viên, việc hiểu sâu hơn cách docker hoạt động cũng giúp mình nâng cao chất lượng công việc, giảm thời gian debug khi có lỗi xảy ra.