9.docker高级网络

9.1 网络通信基础

在网络通信中,两台主机的唯一标识为 IP 地址(V4/V6), (类似身份证号码) 基于 IP 地址实现全世界的网络互联。
计算机通信最初发展, 并未实现系统化与标准化, 不同厂商产出各自的网络来实现通信, 这样导致了计算机缺乏灵活性和可扩展性, 
为了解决该问题,ISO(国际标准化组织)制定了一个国际标准 OSI(开放式通信系统互联参考模型), (IETF(国际互联网工程任务组)TCP/IP 模型)
OSI 模型

9.2 局域网互联技术
9.2.1 以太网发展

1973 年, 美国加利福尼亚州的 Xerox 公司实现了最初的以太网(3Mbps)
1980 年, Xerox 与 DEC、Intel 实现了 10Mbps 以太网
1983 年, IEEE 标准委员会通过第一个 802.3 标准
1990 年, IEEE 通过了使用双绞线介质的以太网标准传输 10Mbps

9.2.2 局域网通信设备

集线器(HUB) , 物理层的设备
网桥又称桥接器,(Network Bridge), 数据链路层设备
交换机 Switch, 数据链路层设备(网管 snmp/telnet 与非网管)

9.2.3 局域网(二层)数据转发原理

192.168.1.10192.168.1.11 发送邮件, 根据 OSI 七层模型, 数据转发是需要封装数据包(1500 字节/分片重组 MTU)
1. 192.168.1.10 查询本机的地址缓存表(arp -a), 是否有 IP 地址对应的 mac 地址, 如果有直接发送数据包,如果没有则发送 ARP 广播
2. 192.168.1.10 向网络发送 ARP 广播, 询问 192.168.1.11 的 MAC 地址是多少?
3. 在未隔离广播域的交换机上的端口都会接收到这个广播.
4. 192.168.1.11 接收到请求以后,比对目标网络地址, 是否是地址, 然后再回应给 192.168.1.10
5. 192.168.1.10 收到 mac 地址以后, 封装数据包,发送到交换机, 交换机根据目标 mac 转发数据到192.168.1.11

9.3 容器通信基础

在Linux系统中Namespace (> 2.6.x 内核版本) 主要用于资源的隔离。在有了Namespace功能之后,在Linux系统中就可以抽象出多个网络子系统,并且各子系统间都有自己的网络设备,协议栈等, 彼此之间互不影响。
如果相互隔离的Namespace之间需要通信时,则用veth-pair来做连接作为连接桥梁,使原本互不相干的容器之间能够相互通信。
根据网络连接的方式与规模,可分为“直接相连”、“Bridge 相连” 和 “OVS 相连”。下面会详细讲解几种模式的实现方式。

9.3.1 直接连接

直接相连是最简单的方式,一对 veth-pair 直接将两个 namespace 连接在一起。
#同一宿主机

9.3.1.1配置不同NS, 相同网段的直接连接互通
9.3.1.2 创建不同的Namespace

# 创建 namespace
[root@docker01 ~]# ip netns a ns1
[root@docker01 ~]# ip netns a ns2

9.3.1.3 创建veth-pair(veth0/veth1)

#创建一对 veth-pair veth0 veth1
#创建 veth0 类型为 veth, 对端的地址为 veth1
[root@docker01 ~]# ip l a veth0 type veth peer name veth1

9.3.1.4 添加veth0/veth1至两个不同的NS

[root@docker01 ~]# ip l s veth0 netns ns1
[root@docker01 ~]# ip l s veth1 netns ns2

9.3.1.5 配置veth IP 地址

#给两个 veth0 veth1 配上 IP 并启用, 必须为一个网段
[root@docker01 ~]# ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
[root@docker01 ~]# ip netns exec ns1 ip l s veth0 up
[root@docker01 ~]# ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
[root@docker01 ~]# ip netns exec ns2 ip l s veth1 up

9.3.1.6 测试veth 连通性

#从 veth0 ping veth1
[root@localhost ~]# ip netns exec ns1 ping 10.1.1.3
PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.073 ms
64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.068 ms
--- 10.1.1.3 ping statistics ---
15 packets transmitted, 15 received, 0% packet loss, time 14000ms
rtt min/avg/max/mdev = 0.068/0.084/0.201/0.032 ms

9.3.2 Linux Bridge连接

Linux Bridge 相当于一个网桥,可以中转两个namespace的流量。
如下图,两对 veth-pair 分别将两个 namespace 连到 Bridge 上。

9.3.2.1 配置不同NS, 相同网络通过网桥的互通

# 创建 namespace
[root@docker01 ~]# ip netns a ns1
[root@docker01 ~]# ip netns a ns2

9.3.2.2 创建网桥br0

[root@docker01 ~]# ip l a br0 type bridge
[root@docker01 ~]# ip l s br0 up

创建两对 veth-pair
[root@docker01 ~]# ip l a veth01 type veth peer name br-veth0
[root@docker01 ~]# ip l a veth11 type veth peer name br-veth1

9.3.2.3 配置veth pair, 并且加入到独立的NS与相同的br0
9.3.2.3.1 #配置veth01的namespace为ns1, 设置br-veth0的桥接器为br0

[root@docker01 ~]# ip l s veth01 netns ns1
[root@docker01 ~]# ip l s br-veth0 master br0
[root@docker01 ~]# ip l s br-veth0 up

9.3.2.3.2 配置veth11的namespace为ns2, 设置br-veth1的桥接器为br0

[root@docker01 ~]# ip l s veth11 netns ns2
[root@docker01 ~]# ip l s br-veth1 master br0
[root@docker01 ~]# ip l s br-veth1 up

9.3.2.4 配置veth01与veth11 IP 地址

# 给两个 ns 中的 veth 配置 IP 并启用
[root@docker01 ~]# ip netns exec ns1 ip a a 11.1.1.2/24 dev veth01
[root@docker01 ~]# ip netns exec ns1 ip l s veth01 up
[root@docker01 ~]# ip netns exec ns2 ip a a 11.1.1.3/24 dev veth11
[root@docker01 ~]# ip netns exec ns2 ip l s veth11 up

9.3.2.5 测试连通性

#veth01 ping veth11
[root@localhost ~]# ip netns exec ns1 ping 11.1.1.3
PING 11.1.1.3 (11.1.1.3) 56(84) bytes of data.
64 bytes from 11.1.1.3: icmp_seq=1 ttl=64 time=0.060 ms
64 bytes from 11.1.1.3: icmp_seq=2 ttl=64 time=0.105 ms
--- 11.1.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.060/0.082/0.105/0.024 ms

9.3.3 通过OVS 连接

OVS 是第三方开源的 Bridge,功能比 Linux Bridge 要更强大

9.3.3.1 安装OVS

[root@localhost ~]# yum install wget openssl-devel \
python-sphinx gcc make python-devel openssl-devel kernel-devel graphviz kernel-debug-devel \
autoconf automake rpm-build redhat-rpm-config libtool python-twisted-core python-zope-interface \
PyQt4 desktop-file-utils libcap-ng-devel groff checkpolicy python-six selinux-policy-devel -y
[root@localhost ~]# mkdir -p /root/rpmbuild/SOURCES
[root@localhost ~]# cd /root/rpmbuild/SOURCES
[root@localhost ~]# wget http://openvswitch.org/releases/openvswitch-2.9.2.tar.gz
[root@localhost ~]# tar xvf openvswitch-2.9.2.tar.gz
[root@localhost ~]# rpmbuild -bb --nocheck openvswitch-2.9.2/rhel/openvswitch-fedora.spec
[root@localhost ~]# yum localinstall /root/rpmbuild/RPMS/x86_64/openvswitch-2.9.2-1.el7.centos.x86_64.rpm -y
[root@demo SOURCES]# systemctl start openvswitch.service
[root@demo SOURCES]# systemctl enable openvswitch.service

9.3.4 配置veth通过OVS连接通信
9.3.4.1 通过OVS命令创建一个bridge

[root@localhost ~]# ovs-vsctl add-br ovs-br

9.3.4.2 创建两对veth-pair

[root@localhost ~]# ip l a veth011 type veth peer name ovs-veth0
[root@localhost ~]# ip l a veth111 type veth peer name ovs-veth1

9.3.4.3 配置veth-pair添加到NS与OVS Bridge 中

#操作 veth011
#添加 veth011 到 ns1
[root@localhost ~]# ip l s veth011 netns ns1

#将 ovs-veth0 添加到 ovs-br 桥接器
[root@localhost ~]# ovs-vsctl add-port ovs-br ovs-veth0
[root@localhost ~]# ip l s ovs-veth0 up

#操作 veth1
#添加 veth1 到 ns2
[root@localhost ~]# ip l s veth111 netns ns2
#将 ovs-veth1 添加到 ovs-br 桥接器
[root@localhost ~]# ovs-vsctl add-port ovs-br ovs-veth1
[root@localhost ~]# ip l s ovs-veth1 up

9.3.4.4 配置veth IP 地址

[root@localhost ~]# ip netns exec ns1 ip a a 12.1.1.2/24 dev veth011
[root@localhost ~]# ip netns exec ns1 ip l s veth011 up
[root@localhost ~]# ip netns exec ns2 ip a a 12.1.1.3/24 dev veth111
[root@localhost ~]# ip netns exec ns2 ip l s veth111 up

9.3.4.5 测试连通性

# veth011 ping veth111
[root@localhost ~]# ip netns exec ns1 ping 12.1.1.3
PING 12.1.1.3 (12.1.1.3) 56(84) bytes of data.
64 bytes from 12.1.1.3: icmp_seq=1 ttl=64 time=0.311 ms
64 bytes from 12.1.1.3: icmp_seq=2 ttl=64 time=0.087 ms
--- 12.1.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.087/0.199/0.311/0.112 ms

9.4 Docker0 网桥详解

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口) ,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和宿主机都在同一个物理网络。
Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信,它还给出了 MTU(接口允许接收的最大传输单元) ,以太网通常是 1500Bytes,
或宿主机网络路由上支持的默认值。这些值都可以在服务启动的时候进行配置。
--bip=CIDR -- IP 地址加掩码格式,例如 192.168.100.5/24
--mtu=BYTES -- 覆盖默认的 Docker mtu 配置
也可以在配置文件中配置 DOCKER_OPTS,然后重启服务。 由于目前 Docker 网桥是Linux 网桥,用户可以使用 brctl show 来查看网桥和端口连接信息。

9.4.1 查看docker0网桥

9.4.2 安装网络工具

#如果已经安装 Docker CE 则无需要再安装
[root@node-2 ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
[root@node-2 ~]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
[root@node-2 ~]# yum install docker-ce-19.03.6 docker-ce-cli-19.03.6 containerd.io -y
#安装网络查看工具
[root@node-2 ~]# yum install bridge-utils

9.4.3 查看网络
#查看系统网络接口

[root@node-2 ~]# ip a
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: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen1000
link/ether 00:0c:29:14:38:91 brd ff:ff:ff:ff:ff:ff
inet 192.168.91.136/24 brd 192.168.91.255 scope global noprefixroute ens32
valid_lft forever preferred_lft forever
inet6 fe80::7dfd:e7a3:7683:20e7/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:2f:4c:4a:2b 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

9.4.4 查看网桥

[root@node-2 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02422f4c4a2b no

9.4.5 启动nginx demo容器

#运行 nginx demo
[root@node-2 ~]# docker run --name nginx-demo -p 8081:80 -d nginx

9.4.6 获取容器进程id

[root@node-2 ~]# docker inspect -f '{{.State.Pid}}' nginx-demo
1370

9.4.7 查看命名空间

#系统所支持的网络命名空间(在内核中根据进程号隔离)
[root@node-2 ~]# ll /proc/1370/ns/
total 0
lrwxrwxrwx 1 root root 0 Nov 6 11:51 ipc -> ipc:[4026532449]
lrwxrwxrwx 1 root root 0 Nov 6 11:51 mnt -> mnt:[4026532447]
lrwxrwxrwx 1 root root 0 Nov 6 11:50 net -> net:[4026532443]
lrwxrwxrwx 1 root root 0 Nov 6 11:51 pid -> pid:[4026532450]
lrwxrwxrwx 1 root root 0 Nov 6 11:51 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Nov 6 11:51 uts -> uts:[4026532448]

9.4.8 通过进程id 获取到网络命名空间

[root@demo ~]# ll /proc/1370/ns/net
lrwxrwxrwx 1 root root 0 Mar 23 23:35 /proc/1370/ns/net -> net:[4026532443]

9.4.9 查看系统网络接口

#查看网络结构, 多了一块网卡
[root@node-2 ~]# ip a
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: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen1000
link/ether 00:0c:29:14:38:91 brd ff:ff:ff:ff:ff:ff
inet 192.168.91.136/24 brd 192.168.91.255 scope global noprefixroute ens32
valid_lft forever preferred_lft forever
inet6 fe80::7dfd:e7a3:7683:20e7/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:2f:4c:4a:2b 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:2fff:fe4c:4a2b/64 scope link
valid_lft forever preferred_lft forever
5: veth7ebd9ef@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state
UP group default
link/ether b2:12:dc:e7:a5:17 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::b012:dcff:fee7:a517/64 scope link
valid_lft forever preferred_lft forever

9.4.9.1 查看网卡桥接

[root@demo ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242c736b32f no veth7ebd9ef

9.4.9.2 查看Iptables

#iptables 存在有地址转换
[root@node-2 ~]# iptables -L -n -t nat
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:8081 to:172.17.0.2:80

9.5 创建 Docker 自定义网桥

Docker 自定义网桥
除了默认的 docker0 网桥,用户也可以指定网桥来连接各个容器。
在启动 Docker 服务的时候,使用 -b BRIDGE或--bridge=BRIDGE 来指定使用的网桥。
如果Docker服务已经运行,那需要先停止服务。

9.5.1 自定义网桥逻辑架构图

容器通过自定义的网桥bridge0访问外网

9.5.2 停止docker 服务

[root@node-2 ~]# service docker stop

9.5.3 建立自定义网桥bridge0

#设置bridge0网段为192.168.6.0/24
#添加网桥
[root@node-2 ~]# brctl addbr bridge0
#设置网桥 IP 地址段
[root@node-2 ~]# ip addr add 192.168.6.1/24 dev bridge0
#设置网桥状态 UP
[root@node-2 ~]# ip link set dev bridge0 up

9.5.4 查看网络
#查看系统增加桥接器bridge0

[root@node-2 ~]# ip a
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: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:14:38:91 brd ff:ff:ff:ff:ff:ff
inet 192.168.91.136/24 brd 192.168.91.255 scope global noprefixroute ens32
valid_lft forever preferred_lft forever
inet6 fe80::7dfd:e7a3:7683:20e7/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:2f:4c:4a:2b 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:2fff:fe4c:4a2b/64 scope link
valid_lft forever preferred_lft forever
6: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 12:e7:be:6c:aa:1c brd ff:ff:ff:ff:ff:ff
inet 192.168.6.1/24 scope global bridge0
valid_lft forever preferred_lft forever
inet6 fe80::10e7:beff:fe6c:aa1c/64 scope link tentative
valid_lft forever preferred_lft forever

9.5.5 查看系统所有桥接器

[root@node-2 ~]# brctl show
bridge name bridge id STP enabled interfaces
bridge0 8000.000000000000 no
docker0 8000.02422f4c4a2b no

9.5.6 查看网桥状态

[root@node-2 ~]# ip addr show bridge0
6: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 12:e7:be:6c:aa:1c brd ff:ff:ff:ff:ff:ff
inet 192.168.6.1/24 scope global bridge0
valid_lft forever preferred_lft forever
inet6 fe80::10e7:beff:fe6c:aa1c/64 scope link
valid_lft forever preferred_lft forever

9.5.7 配置Docker使用新网桥bridge0

[root@node-2 ~]# cat /etc/docker/daemon.json
{
"bridge": "bridge0"
}
#完整配置
{
"bridge": "bridge0",
"registry-mirrors": ["https://plqjafsr.mirror.aliyuncs.com"],
"data-root": "/data/docker",
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true",
"overlay2.size=1G"
]
}

9.5.8 重启docker 服务

#重载
[root@node-2 ~]# systemctl daemon-reload
[root@node-2 ~]# service docker restart

9.5.8.1 启动容器

#启动一个已经退出的容器
[root@demo ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
400fb9f1f415 centos "/bin/bash" 27 hours ago Exited (1) 26 hours ago happy_lumiere
#启动容器
[root@demo ~]# docker start 400fb9f1f415
#进入终端
[root@demo ~]# docker exec -it 400fb9f1f415 /bin/bash
[root@400fb9f1f415 /]# ip a
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
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:06:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.6.2/24 brd 192.168.6.255 scope global eth0
valid_lft forever preferred_lft forever

9.5.9 运行容器,是否可以使用新网桥

9.5.9.1 重新运行容器

[root@node-2 ~]# docker run -it --name centos-d1 centos /bin/bash

9.5.9.2 使用 ping 测试网络连通性

/ # ping www.baidu.com
PING www.baidu.com (163.177.151.109): 56 data bytes
64 bytes from 163.177.151.109: seq=0 ttl=127 time=9.102 ms
64 bytes from 163.177.151.109: seq=1 ttl=127 time=9.358 ms
64 bytes from 163.177.151.109: seq=2 ttl=127 time=9.284 ms

9.5.9.3 查看容器ip

#新容器使用bridge0网段192.168.6.0/24, docker0为172.17.0.0/16 网段
[root@node-2 ~]# docker run -ti centos
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
30: eth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:c0:a8:06:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.6.3/24 brd 192.168.6.255 scope global eth0
valid_lft forever preferred_lft forever

9.5.9.4 查看系统Iptables 转发规则

MASQUERADE:
通过MASQUERADE自动实现SNAT,把192.168.6.0网段的地址, 转换发送到外网。
也就是说在自定义添加了网桥以后, docker 会自动添加网桥的网段到iptables SNAT 的规则中,实现容器启动以后能够自动的连接到外网。

[root@node-2 ~]# iptables -L -n -t nat
……….省略
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 192.168.6.0/24 0.0.0.0/0

9.6 容器 None 网络模式添加网卡

Docker 的网络实现其实就是利用了 Linux 上的网络名字空间隔离技术和虚拟网络设备(veth pair)连接技术,实现容器对外部网络的访问。

9.6.1 创建容器流程回顾(桥接模式)

Docker 创建一个容器的时候,会执行如下操作:
创建一对虚拟接口(veth pair),分别放到本地主机和新容器中;
本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,比如veth65f9;
容器一端放到新容器中,并修改名字作为 eth0,这个接口只在容器的名字空间可见;
从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 veth65f9。
完成这些之后,容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。

9.6.1.1 本案例实现的效果

为容器busybox容器添加自定义的网卡,容器可以通过网卡连接到外部网络。

9.6.2 操作过程
9.6.2.1 创建一个容器, 设置网络模式是none

#启动一个 /bin/bash 容器,指定 --net=none 参数。
[root@demo ~]# docker run -it --name=demo-none --net=none busybox

9.6.3 查看容器IP

#此时的容器是没有任何IP
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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

9.6.4 获取容器id

#在本地主机查找容器的进程 id
[root@node-2 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38078e9a638d busybox "sh" 13 minutes ago Up 13 minutes youthful_chandrasekhar

9.6.5 获取到容器的进程id

[root@node-2 ~]# docker inspect -f '{{.State.Pid}}' demo-none
13034

9.6.6 创建网络命名空间挂载点

[root@node-2 ~]# pid=13034

#创建网络空间挂载点(本地文件目录)
[root@node-2 ~]# mkdir -p /var/run/netns

#绑定网络命名空间到挂载点
[root@node-2 ~]# ln -s /proc/$pid/ns/net /var/run/netns/$pid

9.6.7 查看需要添加的桥接器的网段与桥接器名称

#检查桥接网卡的 IP 和子网掩码信息。
[root@node-2 ~]# ip addr show bridge0
6: bridge0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
inet 192.168.6.1/24 scope global bridge0 #容器网关地址
valid_lft forever preferred_lft forever
inet6 fe80::10e7:beff:fe6c:aa1c/64 scope link
valid_lft forever preferred_lft forever

9.6.8 建立veth-pair

namespace 之间需要通信,用 veth-pair 来做桥梁创建一对 “veth pair” 接口 A 和 B,绑定 A 到网桥 bridge0,并启用。
A 为: veth 接口(宿主机), B 为容器网卡

[root@node-2 ~]# ip link add A type veth peer name B
#设置 A veth 桥接到 bridge0
[root@node-2 ~]# brctl addif bridge0 A
#启动 veth A
[root@node-2 ~]# ip link set A up

9.6.9 设置网络与ip

#将B放到容器的网络命名空间,命名为 eth10,启动它并配置一个可用 IP(桥接网段)和默认网关。
#添加网卡 B 到网络命名空间 13034
[root@node-2 ~]# ip link set B netns $pid
#设置容器网卡的名称为 eth10
[root@node-2 ~]# ip netns exec $pid ip link set dev B name eth10
#设置网卡的状态为 up
[root@node-2 ~]# ip netns exec $pid ip link set eth10 up
#设置容器网卡的 ip 地址
[root@node-2 ~]# ip netns exec $pid ip addr add 192.168.6.100/24 dev eth10
#设置默认路由
[root@node-2 ~]# ip netns exec $pid ip route add default via 192.168.6.1

9.6.9.1 查看容器ip

#查看容器是否有 eth10
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
20: eth10@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue qlen 1000
link/ether 8e:6b:02:17:36:0a brd ff:ff:ff:ff:ff:ff
inet 192.168.6.100/24 scope global eth10
valid_lft forever preferred_lft forever

9.6.9.2 查看宿主机网桥

[root@node-2 ~]# brctl show
bridge name bridge id STP enabled interfaces
bridge0 8000.1651b5e3adba no A
docker0 8000.02422f4c4a2b no

9.6.9.3 查看系统网卡

[root@node-2 ~]# ip a
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: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:14:38:91 brd ff:ff:ff:ff:ff:ff
inet 192.168.91.136/24 brd 192.168.91.255 scope global noprefixroute ens32
valid_lft forever preferred_lft forever
inet6 fe80::7dfd:e7a3:7683:20e7/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:2f:4c:4a:2b 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:2fff:fe4c:4a2b/64 scope link
valid_lft forever preferred_lft forever
6: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 16:51:b5:e3:ad:ba brd ff:ff:ff:ff:ff:ff
inet 192.168.6.1/24 scope global bridge0
valid_lft forever preferred_lft forever
inet6 fe80::10e7:beff:fe6c:aa1c/64 scope link
valid_lft forever preferred_lft forever
21: A@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master bridge0 state UP
group default qlen 1000
link/ether 16:51:b5:e3:ad:ba brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::1451:b5ff:fee3:adba/64 scope link
valid_lft forever preferred_lft forever

9.7 运行容器 IP 添加地址

#拉取镜像 CentOS
[root@ demo ~]# docker pull centos

9.7.1 运行容器

#前台运行centos
[root@demo ~]# docker run --name=centos-IP1 -it centos /bin/bash

9.7.2 获取到容器启动进程号

[root@ demo ~]# docker inspect -f '{{.State.Pid}}' centos-IP1
13575

9.7.3 容器修改之前的ip

[root@95d78806950d /]# ip a
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
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:06:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.6.3/24 brd 192.168.6.255 scope global eth0
valid_lft forever preferred_lft forever

9.7.4 创建容器网络命名空间挂载点

[root@ demo ~]# pid=13575
#无法直接操作必须要有网络空间挂载点
[root@demo ~]# ip netns exec $pid ip a
Cannot open network namespace "13575": No such file or directory
[root@ demo ~]# mkdir -p /var/run/netns
#创建网络空间挂载点
[root@ demo ~]# ln -s /proc/$pid/ns/net /var/run/netns/$pid
#查看现在容器 IP
[root@ demo ~]# ip netns exec $pid ip a

9.7.5 设置容器新IP

#设置容器的ip 地址
[root@ demo ~]# ip netns exec $pid ip addr add 192.168.6.10/24 dev eth0

9.7.6 容器内部检验IP

#检验新的ip 地址是否添加
[root@ demo ~]# ip netns exec $pid ip a

9.8 总结 Docker 实现原理

1)底层硬件支撑, 也可以用虚拟机运行Docker。
2)在硬件或虚拟机内安装支持容器运行的操作系统
3) Docker 通过NS、Cgroups与OverlayFs 技术实现容器的加载启动.
4)容器通过利用系统中的Docker0网桥与外部网络互通。

 

posted @ 2022-06-06 11:55  从此重新定义啦  阅读(69)  评论(0编辑  收藏  举报