美文网首页
namespaces 学习笔记6:什么是 net namespa

namespaces 学习笔记6:什么是 net namespa

作者: 董泽润 | 来源:发表于2019-11-05 19:05 被阅读0次

    TL;DR 网络隔离是很重要的一个概念,大到云主机,小到 docker,都在用。本次学习笔记先做个实验,如何做网络隔离并通信。

    docker 网络架构

    docker 网络
    上图是 docker 经典的网格结构
    1. eth0 是宿主机网络接口,docker0 是构建在 eth0 物理网卡上的网桥设备
    2. 每个容器都有一对 veth pair 设备,一端放到容器中,做为 eth0,一端设置在宿主机网桥上用于通信
    3. 其中 iptables 用于做宿主机网络 snat, 并做容器到宿主机的端口映射

    手工实现

    1. 创建新的 net ns

    首先来说,docker 网络是在不同的 net namespace 中,所以首先要创建新的 net ns

    root@myali1:~# ip netns add ns1
    

    当前己经创建了新的 net ns, 另外为了测试我们还要设置对应的 dns

    root@myali1:~# mkdir -p /etc/netns/ns1
    root@myali1:~# echo "nameserver 8.8.8.8" > /etc/netns/ns1/resolv.conf
    root@myali1:~# cat /etc/netns/ns1/resolv.conf
    nameserver 8.8.8.8
    

    使用 list 查看当前 net ns 是否创建成功

    root@myali1:~# ip netns list
    ns1
    

    然后为 net ns1 添加 loopback 本地网络接口

    root@myali1:~# ip netns exec ns1 ip link set dev lo up
    root@myali1:~# ip netns exec ns1 ip addr
    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. 新建网桥

    root@myali1:~# brctl addbr lxcbr0
    root@myali1:~# brctl stp lxcbr0 off
    

    然后为新建的网桥设备添加 ip 地址,测试使用 192.168.10.1/24, 不与当前机器网段冲突即可。

    root@myali1:~# ifconfig lxcbr0 192.168.10.1/24 up
    root@myali1:~# ip addr
    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
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:16:3e:00:59:f2 brd ff:ff:ff:ff:ff:ff
        inet 172.24.213.39/20 brd 172.24.223.255 scope global dynamic eth0
           valid_lft 315359423sec preferred_lft 315359423sec
    4: lxcbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
        link/ether 0a:ac:41:a6:88:4e brd ff:ff:ff:ff:ff:ff
        inet 192.168.10.1/24 brd 192.168.10.255 scope global lxcbr0
           valid_lft forever preferred_lft forever
    

    3. 添加 veth pair

    首先 veth 也是虚拟的网格设备,成对出现的,一端放置在容器中,一端放在网桥,可以实现通信。

    root@myali1:~# ip link add veth-ns1 type veth peer name lxcbr0.1
    root@myali1:~# ip addr
    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
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:16:3e:00:59:f2 brd ff:ff:ff:ff:ff:ff
        inet 172.24.213.39/20 brd 172.24.223.255 scope global dynamic eth0
           valid_lft 315358965sec preferred_lft 315358965sec
    4: lxcbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
        link/ether 0a:ac:41:a6:88:4e brd ff:ff:ff:ff:ff:ff
        inet 192.168.10.1/24 brd 192.168.10.255 scope global lxcbr0
           valid_lft forever preferred_lft forever
    5: lxcbr0.1@veth-ns1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether ee:2a:c0:d5:df:04 brd ff:ff:ff:ff:ff:ff
    6: veth-ns1@lxcbr0.1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether 46:cc:62:01:7a:ef brd ff:ff:ff:ff:ff:ff
    

    注意看,添加完 veth pair 后出现两个网络接口,lxcbr0.1@veth-ns1veth-ns1@lxcbr0.1,这是两个方向的。然后我们把 veth-ns1 添加到容器中,也就是 net ns1 做为他的 eth0

    root@myali1:~# ip link set veth-ns1 netns ns1
    root@myali1:~# ip netns exec ns1  ip link set dev veth-ns1 name eth0
    root@myali1:~# ip netns exec ns1 ip addr
    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
    6: eth0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether 46:cc:62:01:7a:ef brd ff:ff:ff:ff:ff:ff link-netnsid 0
    

    此时我们发现 net ns1 中己经有了 eth0 设备接口,我们再设置与网桥同网段的 ip

    root@myali1:~# ip netns exec ns1 ip addr
    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
    6: eth0@if5: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
        link/ether 46:cc:62:01:7a:ef brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 192.168.10.11/24 brd 192.168.10.255 scope global eth0
           valid_lft forever preferred_lft forever
    

    然后我们再将 veth pair 另一段放在网桥上

    root@myali1:~# brctl addif lxcbr0 lxcbr0.1
    

    测试网络

    1. 测试网桥连通性

    以上几个步骤己经完成了配置,现在测试容器与宿主机连通性。

    root@myali1:~# ip netns exec ns1 ping 192.168.10.1
    

    如果发现 ping 不通,可能是网桥和 veth 没有 up 起来

    root@myali1:~# ifconfig lxcbr0 up
    root@myali1:~# ifconfig lxcbr0.1 up
    

    2. 测试宿主机连通性

    测试机本机 ip 172.24.213.39 为例

    root@myali1:~# ip netns exec ns1 ping 172.24.213.39
    connect: Network is unreachable
    

    此时不通,我们查看下容器中的路由

    root@myali1:~# ip netns exec ns1 route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth0
    

    只有直连路由,需要添加默认路由为网桥,然后再偿试

    root@myali1:~# ip netns exec ns1 route add default gw 192.168.10.1 dev eth0
    root@myali1:~# ip netns exec ns1 ping 172.24.213.39
    PING 172.24.213.39 (172.24.213.39) 56(84) bytes of data.
    64 bytes from 172.24.213.39: icmp_seq=1 ttl=64 time=0.040 ms
    
    --- 172.24.213.39 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1006ms
    rtt min/avg/max/mdev = 0.040/0.048/0.057/0.011 ms
    

    3. 测试外网连通性

    以测试 dns 8.8.8.8 为例

    root@myali1:~# ip netns exec ns1 ping 8.8.8.8
    PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
    ^C
    --- 8.8.8.8 ping statistics ---
    3 packets transmitted, 0 received, 100% packet loss, time 2042ms
    

    默认当然是不通的,需要用 iptables 在宿主机做 nat 转换

    root@myali1:~# iptables -t filter -A FORWARD -i lxcbr0 ! -o lxcbr0 -j ACCEPT
    root@myali1:~# iptables -t filter -A FORWARD -i lxcbr0 -o lxcbr0 -j ACCEPT
    root@myali1:~# iptables -t filter -A FORWARD -o lxcbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    root@myali1:~# iptables -t nat -A POSTROUTING -s 192.168.10.0/24 ! -o lxcbr0 -j MASQUERADE
    root@myali1:~# ip netns exec ns1 ping 8.8.8.8
    PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
    64 bytes from 8.8.8.8: icmp_seq=2 ttl=46 time=46.2 ms
    64 bytes from 8.8.8.8: icmp_seq=3 ttl=46 time=46.2 ms
    ^C
    --- 8.8.8.8 ping statistics ---
    4 packets transmitted, 2 received, 50% packet loss, time 3022ms
    rtt min/avg/max/mdev = 46.238/46.254/46.270/0.016 ms
    

    端口映射

    同样需要用 iptables 实现,我们先在 ns1 下启动测试服务

    root@myali1:~# ip netns exec ns1 python -m SimpleHTTPServer
    Serving HTTP on 0.0.0.0 port 8000 ...
    

    这个 python http 程序使用 ns1 namespace, 先测试使用容器 ip 访问

    root@myali1:~# curl -I http://192.168.10.11:8000
    HTTP/1.0 200 OK
    Server: SimpleHTTP/0.6 Python/2.7.15+
    Date: Tue, 05 Nov 2019 09:27:19 GMT
    Content-type: text/html; charset=UTF-8
    Content-Length: 1160
    

    现在要做端口映射,使得其他机器可以访问

    root@myali1:~# iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.10.11:8000
    root@myali1:~# iptables -t filter -A FORWARD -p tcp -m tcp --dport 8000 -j ACCEPT
    

    然后在另外一台机器测试,本机真实物理 ip 是 172.24.213.39

    root@worker1:~# curl -I http://172.24.213.39:80
    HTTP/1.0 200 OK
    Server: SimpleHTTP/0.6 Python/2.7.15+
    Date: Tue, 05 Nov 2019 09:38:19 GMT
    Content-type: text/html; charset=UTF-8
    Content-Length: 1160
    

    现在要做端口映射,使得本机可以访问

    root@myali1:~# iptables -t nat -A OUTPUT -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.10.11:8000
    root@myali1:~# iptables -t nat -A POSTROUTING -p tcp -m tcp --dport 8000 -j MASQUERADE
    

    然后分别是 172.24.213.39 与网桥 192.168.10.1 做测试

    root@myali1:~# curl -I http://192.168.10.1:80
    HTTP/1.0 200 OK
    Server: SimpleHTTP/0.6 Python/2.7.15+
    Date: Tue, 05 Nov 2019 09:42:03 GMT
    Content-type: text/html; charset=UTF-8
    Content-Length: 1160
    root@myali1:~# curl -I http://172.24.213.39
    HTTP/1.0 200 OK
    Server: SimpleHTTP/0.6 Python/2.7.15+
    Date: Tue, 05 Nov 2019 11:04:26 GMT
    Content-type: text/html; charset=UTF-8
    Content-Length: 1160
    

    小结

    网络这块还是比较复杂,另外端口映射如果用 iptables nat, 线上性能肯定炸锅。

    相关文章

      网友评论

          本文标题:namespaces 学习笔记6:什么是 net namespa

          本文链接:https://www.haomeiwen.com/subject/hujsmctx.html