写点什么

自己动手写 Docker 系列 -- 6.3 手动配置容器网络 (下)

作者:
  • 2022 年 4 月 24 日
  • 本文字数:4377 字

    阅读完需:约 14 分钟

简介

网络部分较为复杂,本篇先利用之前写好的基础容器和网桥部分,加上手工给容器配置网络,让其容器与外部网络部分功能正常,为后面程序编写打下基础

源码说明

同时放到了 Gitee 和 Github 上,都可进行获取



本章节对应的版本标签是:5.8,防止后面代码过多,不好查看,可切换到标签版本进行查看

思路梳理

在以前的文章中,我们完成了基础容器的功能,在单机情况下基本可用,但没有网络那就不完善


网络部分目前就写了一个 IP 分配管理工具,和 network 命令,用于创建网桥


在本人尝试过程中,如果不对网络配置部分有一定的了解,对应后面网络部分的编写和理解会感到很懵


所以我们需要进行一定的热身,先手工给我们的容器配置上网络,后面再用代码完成这部分的功能


关于如何手工进行配置,在《自己动手写 Docker》和文章:好文 Linux Network Namespace 与 Bridge 网桥中,都有了很好的描述


跟着上面的链接走一遍了,基本上就通了(PS:如果在 ping 114.114.114.114 始终不行,可能是之前的网络配置过于乱了,将 iptables 规则进行清理或者重启下主机)


对于手动创建网络和配置网络,这里就不再重复,本文的重点放到如果应用上面的知识给目前的容器配置网络

手动配置容器网络

实验环境基于代码的标签:6.2


该部分代码,完成了基本的容器功能和网桥的创建,基于这个基础,我们要做的事情如下:


  • 单容器:

  • [x] 容器能访问宿主机网络(上篇中已完成)

  • [ ] 容器能访问外网

  • [ ] 多容器-同局域网能访问

  • [ ] 多容器-不同局域网能访问


下面我们继续来完善配置

添加 Iptables 规则,让容器能访问外网

上篇中我们完成了宿主机与容器的网络连通,但容器目前还是不能访问外网,我们需要添加如下的 iptables 规则


iptables -t nat -A POSTROUTING -s 162.16.0.0/24 -j MASQUEREAD
复制代码


目前宿主机的 iptables 规则如下,看到 docker 也配置了这样的一条命令


$ iptables  -t  nat  -nL --line-numbersChain PREROUTING (policy ACCEPT)num  target     prot opt source               destination1    DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)num target prot opt source destination
Chain OUTPUT (policy ACCEPT)num target prot opt source destination1 DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
# 下面看到docker也添加了一条这样的规则Chain POSTROUTING (policy ACCEPT)num target prot opt source destination1 MASQUERADE all -- 172.17.0.0/16 0.0.0.0/02 MASQUERADE all -- 182.18.0.0/24 0.0.0.0/03 MASQUERADE all -- 162.16.0.0/24 0.0.0.0/0
Chain DOCKER (2 references)num target prot opt source destination1 RETURN all -- 0.0.0.0/0 0.0.0.0/0
复制代码


下面我们从容器内访问下:114.114.114.114,看到成功访问


$ ./main exec bird ping 114.114.114.114PING 114.114.114.114 (114.114.114.114): 56 data bytes64 bytes from 114.114.114.114: seq=0 ttl=77 time=32.301 ms64 bytes from 114.114.114.114: seq=1 ttl=72 time=33.687 ms64 bytes from 114.114.114.114: seq=2 ttl=75 time=33.727 ms64 bytes from 114.114.114.114: seq=3 ttl=84 time=34.131 ms64 bytes from 114.114.114.114: seq=4 ttl=82 time=35.087 ms64 bytes from 114.114.114.114: seq=5 ttl=95 time=33.474 ms^C--- 114.114.114.114 ping statistics ---6 packets transmitted, 6 packets received, 0% packet lossround-trip min/avg/max = 32.301/33.734/35.087 ms
复制代码

新增同局域网内的容器,进行访问

和上篇基本一样的操作,将新起动的容器分配同上一个容器一样的局域网 ip:162.16.0.3


# 新启动容器2$ ./main run -d -e bird=123 -name bird2 top{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-04-23T14:38:34+08:00"}{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-04-23T14:38:34+08:00"}{"level":"info","msg":"all command is : top","time":"2022-04-23T14:38:34+08:00"}{"level":"info","msg":"parent process run","time":"2022-04-23T14:38:34+08:00"}
# 新建veth,设置宿主机端的veth$ ip link add birdveth2 type veth peer name birdveth3$ ip link set dev birdveth2 master testbridge$ ip link set dev birdveth2 up
# 设置容器2内部的veth$ ps -aux|grep toproot 2769 0.0 0.0 539820 10860 ? Ssl 07:01 0:01 /usr/libexec/xdg-desktop-portalroot 2773 0.0 0.0 500052 29516 ? Ssl 07:01 0:00 /usr/libexec/xdg-desktop-portal-gtkroot 39207 0.0 0.0 1328 4 pts/4 S 09:47 0:01 toproot 64560 0.0 0.0 1328 4 pts/4 S 14:38 0:00 toproot 64749 0.0 0.0 12132 724 pts/4 R+ 14:47 0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox top
$ ln -s /proc/64560/ns/net /var/run/netns/bird2
$ ip netns listbird2bird (id: 2)k8svip (id: 1)
$ ip link set birdveth3 netns bird2$ ./main exec bird2 ip link set dev lo up$ ./main exec bird2 ip link set dev birdveth3 up$ ./main exec bird2 ip addr add 162.16.0.3/24 dev birdveth3
# 成功ping通网关$ ./main exec bird2 ping 162.16.0.1PING 162.16.0.1 (162.16.0.1): 56 data bytes64 bytes from 162.16.0.1: seq=0 ttl=64 time=0.135 ms64 bytes from 162.16.0.1: seq=1 ttl=64 time=0.152 ms^C--- 162.16.0.1 ping statistics ---2 packets transmitted, 2 packets received, 0% packet lossround-trip min/avg/max = 0.135/0.143/0.152 ms
# 容器2能成功联通容器1$ ./main exec bird2 ping 162.16.0.2PING 162.16.0.2 (162.16.0.2): 56 data bytes64 bytes from 162.16.0.2: seq=0 ttl=64 time=0.138 ms64 bytes from 162.16.0.2: seq=1 ttl=64 time=0.154 ms^C--- 162.16.0.2 ping statistics ---2 packets transmitted, 2 packets received, 0% packet lossround-trip min/avg/max = 0.138/0.146/0.154 ms

# 容器1能成功联通容器2$ ./main exec bird ping 162.16.0.3PING 162.16.0.3 (162.16.0.3): 56 data bytes64 bytes from 162.16.0.3: seq=0 ttl=64 time=0.090 ms64 bytes from 162.16.0.3: seq=1 ttl=64 time=0.154 ms^C--- 162.16.0.3 ping statistics ---2 packets transmitted, 2 packets received, 0% packet lossround-trip min/avg/max = 0.090/0.122/0.154 ms
$ ./main exec bird2 ip route add default via 162.16.0.1
$ ./main exec bird2 ping 114.114.114.114PING 114.114.114.114 (114.114.114.114): 56 data bytes64 bytes from 114.114.114.114: seq=0 ttl=82 time=32.376 ms64 bytes from 114.114.114.114: seq=1 ttl=92 time=37.692 ms^C--- 114.114.114.114 ping statistics ---2 packets transmitted, 2 packets received, 0% packet lossround-trip min/avg/max = 32.376/35.034/37.692 ms
复制代码


经过试验,我们处于同一个局域网的容器是能够联通的

配置 DNAT,让外部能够访问内部服务

首先我们在容器内监听 95 端口,然后在宿主机内访问 95 端口是能正常访问的


# 在容器内监听95端口,能收到相关指令$ ./main exec bird nc -lp 95jkjfdkskfjskd��������quitexitexit()quit()
# 在宿主机中进行telnet并发送指令,如上可以进行显示(ctrl + ],后输入quit,退出telnet)$ telnet 162.16.0.2 95Trying 162.16.0.2...Connected to 162.16.0.2.Escape character is '^]'.jkjfdkskfjskd^C^Cquitexitexit()quit()^]telnet> quitConnection closed.
复制代码


但是如同我们在使用 docker 的过程中,我们是映射了端口的,如:docker run -tid --name centos8-2 -p 90:90 centos:8 bash


在上面的示例中,我们访问容器所在宿主机 90 端口,就能访问到容器的 90 端口,下面我们就通过配置来达到这个目的


进行下面的配置,下面会将宿主机 95 端口上的请求,都转到容器 162.16.0.2 的 95 端口上


# 配置规则$ iptables -t nat -A PREROUTING -p tcp -m tcp --dport 95 -j DNAT --to-destination 162.16.0.2:95
# 查看规则,看到docker也是通过相关配置达到这个目的的$ iptables -t nat -nL --line-numbersChain PREROUTING (policy ACCEPT)num target prot opt source destination1 DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL2 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:95 to:162.16.0.2:95
Chain INPUT (policy ACCEPT)num target prot opt source destination
Chain OUTPUT (policy ACCEPT)num target prot opt source destination1 DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)num target prot opt source destination1 MASQUERADE all -- 172.17.0.0/16 0.0.0.0/02 MASQUERADE all -- 182.18.0.0/24 0.0.0.0/03 MASQUERADE all -- 162.16.0.0/24 0.0.0.0/04 MASQUERADE tcp -- 172.17.0.3 172.17.0.3 tcp dpt:90
Chain DOCKER (2 references)num target prot opt source destination1 RETURN all -- 0.0.0.0/0 0.0.0.0/02 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:90 to:172.17.0.3:90
复制代码


# 在宿主机上监听95端口nc -lp 95
# 新开shell或者在和宿主机同一个局域网的主机上进行telnet连接telnet 192.168.1.5 95
复制代码


重新启动容器监听后,看到相关的输出,证明外部也访问容器服务了


$ ./main exec bird nc -lp 95jkjfdskfjs
复制代码

总结

不同局域网的互通,尝试了下,有点难度,目前本人的知识理解还不能做到,只能后面再补上了,先通过实现这部分的功能

发布于: 刚刚阅读数: 2
用户头像

关注

还未添加个人签名 2018.09.09 加入

代码是门手艺活,也是门艺术活

评论

发布
暂无评论
自己动手写Docker系列 -- 6.3 手动配置容器网络(下)_Go_萧_InfoQ写作社区