Linux Network Namespace
1. Network namespace là gì?
Network namespace giúp chúng ta có các mạng riêng biệt trên một host.
Mỗi một namespace sẽ có những giao diện (interface) và bảng định tuyến (routing table) của riêng nó và tách biệt với các namespace khác. Ngoài ra, tiến trình (process) trên hệ thống có thể được liên kết với một network-namespace cụ thể.
Network namespace được sử dụng nhiều trong các dự án OpenStack và Docker. Để có thể hiểu được một cách sâu sắc những dự án đó, bạn phải thông thạo network-namespace.
2. Làm việc với network namespace
Khi khởi động Linux, hệ thống sẽ có một namespace và mỗi tiến trình (process) khi được tạo mới sẽ kế thừa (inherit) namespace này từ tiến trình cha (parent process). Vì vậy, tất cả các tiến trình đều kế thừa network-namespace của tiến trình init (PID=1).
Danh sách các namespace
Để làm việc với network-namespace, sử dụng lệnh ip netns
Liệt kê các network-namespace trên hệ thống:
$ ip netns list
qrouter-68d092ba-2509-462c-87fb-2db5e9678eb5
qdhcp-5dfc8455-bb8d-46af-a815-29bcc74a4640
Tạo mới một namespace
Để tạo mới một namespace:
$ ip netns add truongnh1
$ ip netns add truongnh2
2 namespace được tạo mới: truongnh1 & truongnh2
Như hình vẽ trên, 2 namespace mới đã được tạo độc lập với namespace mặc định của hệ thống.
Một lần nữa liệt kê các network-namespace:
$ ip netns list
truongnh2
truongnh1
qrouter-68d092ba-2509-462c-87fb-2db5e9678eb5
qdhcp-5dfc8455-bb8d-46af-a815-29bcc74a4640
Mỗi khi một namespace được tạo mới có một file tương ứng có cùng tên với tên của namespace tạo ra trong thư mục /var/run/netns
.
Lúc này:
$ ls /var/run/netns
qdhcp-5dfc8455-bb8d-46af-a815-29bcc74a4640
qrouter-68d092ba-2509-462c-87fb-2db5e9678eb5
truongnh1
truongnh2
Thực thi lệnh (commands) trong namespaces
Thực thi một lệnh trong một namespace:
$ sudo ip netns exec truongnh1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
Ví dụ ở trên, lệnh ip a
chạy trong namespace có tên truongnh1.
3. Ping 2 namespaces
Một kịch bản đơn giản được đưa ra:
- Mô phỏng 2 node (mỗi namespace sẽ đại diện cho 1 node)
- Kết nối 2 namespace tới một switch ảo (virtual switch)
- ping từ namespace này đến namespace kia
Tạo virtual switch
Cài đặt Open vSwitch và khởi động service của nó
$ sudo apt-get install openvswitch-switch
$ sudo /etc/init.d/openvswitch-switch start
Tiếp theo, tạo 1 virtual switch trong namespace mặc định của hệ thống có tên là my_switch.
$ sudo ovs-vsctl add-br my_switch
Kiểm tra xem my_switch đã được tạo thành công hay chưa?
$ sudo ovs-vsctl show
4e279dd7-5211-4f79-a8bc-76422c831e0b
Manager "ptcp:6640:127.0.0.1"
is_connected: true
Bridge br-ex
Controller "tcp:127.0.0.1:6633"
is_connected: true
fail_mode: secure
Port br-ex
Interface br-ex
type: internal
Port phy-br-ex
Interface phy-br-ex
type: patch
options: {peer=int-br-ex}
Bridge my_switch
Port my_switch
Interface my_switch
type: internal
Để kết nối namespace đến my_switch vừa được tạo, dùng cặp veth - sẽ được giái thích sơ lược như dưới đây:
veth (virtual ethernet)
veth là một loại thiết bị mạng mà luôn luôn đi theo 1 cặp (pairs). Có thể tưởng tượng rằng, từ pair nhắc đến ở đây như là một cái ống: mọi thứ gửi đến một đầu ống sẽ đi ra ngoài ở đầu bên kia.
$ sudo ip link add type veth
$ ip address
14: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 76:75:43:9f:a8:9e brd ff:ff:ff:ff:ff:ff
15: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether fa:92:ed:48:bd:b4 brd ff:ff:ff:ff:ff:ff
Có 2 device đi theo một cặp: mọi thứ khi được gửi đến veth0@veth1 sẽ đi ra ở veth1@veth0.
Bây giờ, khi xóa 1 trong 2 veth ở trên thì device còn lại cũng sẽ bị xóa theo.
$ sudo ip link del veth0
veth
Quay trở lại việc kết nối namespace với my_switch, tạo veth kết nối namespace truongnh1 với my_switch
$ sudo ip link add truongnh1-netns type veth peer name truongnh1-ovs
Câu lệnh trên đã xác định tên của mỗi thành phần trong cặp. Vì vậy, truongnh1-netns sẽ được thiết lập ở trong namespace truongnh1 và truongnh1-ovs sẽ kết nối với virtual switch.
$ ip a
16: truongnh1-ovs@truongnh1-netns: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether d2:e3:de:1d:22:e5 brd ff:ff:ff:ff:ff:ff
17: truongnh1-netns@truongnh1-ovs: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 92:fb:c5:a8:6a:df brd ff:ff:ff:ff:ff:ff
Đặt truongnh1-netns vào trong namespace truongnh1:
$ sudo ip link set truongnh1-netns netns truongnh1
Nhấn mạnh rằng: namespace là một môi trường mạng độc lập và namespace mặc định của hệ thống là tách biệt với các namespace khác. Nếu điều này là đúng, truongnh1-netns sẽ không được thấy trong namespace mặc định của hệ thống kể từ khi nó được đặt trong namespace truongnh1.
$ ip a
13: my_switch: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1
link/ether a2:71:a7:0e:62:40 brd ff:ff:ff:ff:ff:ff
16: truongnh1-ovs@if17: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether d2:e3:de:1d:22:e5 brd ff:ff:ff:ff:ff:ff link-netnsid 0
$ sudo ip netns exec truongnh1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
17: truongnh1-netns@if16: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 92:fb:c5:a8:6a:df brd ff:ff:ff:ff:ff:ff link-netnsid 0
Như đã thấy, truongnh1-netns hiện tại đã ở trong namespace truongnh1.
Kết nối veth còn lại trong cặp (truongnh1-ovs) với my_switch
$ sudo ovs-vsctl add-port my_switch truongnh1-ovs
$ sudo ovs-vsctl show
4e279dd7-5211-4f79-a8bc-76422c831e0b
Manager "ptcp:6640:127.0.0.1"
is_connected: true
Bridge my_switch
Port "truongnh1-ovs"
Interface "truongnh1-ovs"
Port my_switch
Interface my_switch
type: internal
truongnh1-ovs đã trở thành 1 port của my_switch.
Làm các bước tương tự với namespace truongnh2
$ sudo ip link add truongnh2-netns type veth peer name truongnh2-ovs
$ sudo ip link set truongnh2-netns netns truongnh2
$ sudo ovs-vsctl add-port my_switch truongnh2-ovs
Đến đây, mỗi namespace đã có một cặp veth vì thế my_switch có 2 port: truongnh1-ovs và truongnh2-ovs
Điều này có nghĩa là 2 namespace truongnh1 và truongnh2 đã có thể “thông nhau”? Câu trả lời là KHÔNG. Việc cần làm tiếp theo là gán địa chỉ cho nó và mang nó lên (bring devices up).
Bringing up devices
Trong namespace mặc định của hệ thống:
$ sudo ip link set truongnh1-ovs up
$ sudo ip link set truongnh2-ovs up
Trong namespace truongnh1 và truongnh2:
$ sudo ip netns exec truongnh1 ip link set dev lo up
$ sudo ip netns exec truongnh1 ip link set dev truongnh1-netns up
$ sudo ip netns exec truongnh2 ip link set dev lo up
$ sudo ip netns exec truongnh2 ip link set dev truongnh2-netns up
Gán địa chỉ IP
Bước tiếp theo sẽ gán địa chỉ IP cho các device truongnh1-netns và truongnh2-netns trong các namespace truongnh1, truongnh2 tương ứng.
$ sudo ip netns exec truongnh1 ip addr add 10.0.0.1/24 dev truongnh1-netns
$ sudo ip netns exec truongnh2 ip addr add 10.0.0.2/24 dev truongnh2-netns
$ sudo ip netns exec truongnh1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
17: truongnh1-netns@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 92:fb:c5:a8:6a:df brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.0.0.1/24 scope global truongnh1-netns
valid_lft forever preferred_lft forever
inet6 fe80::90fb:c5ff:fea8:6adf/64 scope link
valid_lft forever preferred_lft forever
Ping test
Các namespace truongnh1 và truongnh2 đã được nối với my_switch, các device được bật và gán địa chỉ IP. Thực hiện lệnh ping
từ truongnh1 đến truongnh2:
$ sudo ip netns exec truongnh1 ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.680 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.075 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.089 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.079 ms
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2997ms
rtt min/avg/max/mdev = 0.075/0.230/0.680/0.260 ms
4. Tổng hợp những dòng lệnh đã dùng trong bài viết
# List all namespace, except for the default/global one.
$ ip netns
# Execute command inside a specific namespace
$ ip netns exec <namespace_name> <command>
# Sets specified interface in the specified namespace
$ ip link set <interface> netns <namespace>
# Bring interface up
$ ip link set <interface> up
# Add veth pair
$ ip link add type veth
# Add veth pair by specifying the name for each peer
$ ip link add <veth-peer1> type veth peer name <veth-peer2>
# Adds a new bridge
$ ovs-vsctl add-br <bridge_name>
# Adds a new port in the specific bridge
$ ovs-vsctl add-port <bridge_name> <port_name>
Bài viết được dịch sang Tiếng Việt từ bài gốc tại: http://abregman.com.