docker笔记_docker的网络

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jt102605/article/details/86228690

一.docker网络底层基础

network namespace

network namespace 是 linux 内核提供的功能,是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自的网络栈信息。不管是虚拟机还是容器,运行的时候仿佛自己就在独立的网络中。

ip netns 是 ip命令的一个自命令,和 network namespace 有关的操作都是在ip netns下进行的,可以通过 ip netns help 可以查看相关操作的帮助信息。

[vagrant@localhost ~]$ ip netns help
Usage: ip netns list
       ip netns add NAME
       ip netns set NAME NETNSID
       ip [-all] netns delete [NAME]
       ip netns identify [PID]
       ip netns pids NAME
       ip [-all] netns exec [NAME] cmd ...
       ip netns monitor
       ip netns list-id

使用 "ip netns add  namespace名称" 可以创建一个network namespase, 如果相同名字的 namespace 已经存在,则会报 Cannot create namespace 的错误。 

ip netns 命令创建的 network namespace 会出现在 /var/run/netns/ 目录下,如果需要管理其他不是 ip netns 创建的 network namespace,只要在这个目录下创建一个指向对应 network namespace 文件的链接就行。

[vagrant@localhost ~]$ sudo ip netns add net1
[vagrant@localhost ~]$ cd /var/run/netns
[vagrant@localhost netns]$ ls
net1

对于每个 network namespace 来说,都会有自己独立的网卡、路由表、ARP 表、iptables 等和网络相关的资源。ip netns exec 命令可以在对应的 network namespace 中执行命令,比如我们要看一下这个 network namespace 中有哪些网卡。

[vagrant@localhost ~]$ sudo ip netns exec net1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

每个 namespace 在创建的时候会自动创建一个 lo 的 interface,它的作用和 linux 系统中默认看到的 lo 一样,都是为了实现 loopback 通信。如果希望 lo 能工作,不要忘记启用它:

[vagrant@localhost ~]$ sudo ip netns exec net1 ip link set lo up
[vagrant@localhost ~]$ sudo ip netns exec net1 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

两个network namespace 之间通信

有了不同 network namespace 之后,也就有了网络的隔离,默认情况下,network namespace 是不能和主机网络或者其他 network namespace 通信的。

linux 提供了veth pair, 来实现网络之间的通信.可以把 veth pair 当做是双向的 pipe(管道),从一个方向发送的网络数据,可以直接被另外一端接收到;或者也可以想象成两个 namespace 直接通过一个特殊的虚拟网卡连接起来,可以直接通信。

使用 ip link add type veth 可以创建一对 veth pair ,需要注意的是 veth pair 无法单独存在,删除其中一个,另一个也会自动消失。

下面我们来通过一对"veth pair"来实现两个network namespace之间的通信

1.创建两个network namespace分别命名为net1, net2

[vagrant@localhost ~]$ sudo ip netns add net1
[vagrant@localhost ~]$ sudo ip netns add net2
[vagrant@localhost ~]$ sudo ip netns list
net2
net1

2. 使用"ip link add type veth" 命令创建一对 "veth pair": veth0和veth1

[vagrant@localhost ~]$ sudo ip link add type veth
[vagrant@localhost ~]$ sudo ip link
4: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 32:45:a7:42:31:e7 brd ff:ff:ff:ff:ff:ff
5: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 4a:e8:98:00:13:12 brd ff:ff:ff:ff:ff:ff

新创建的veth pair是"DOWN"状态,并且只有MAC地址,没有IP地址,IP地址需要后期分配

创建 veth pair 的时候可以指定它们的名字,通过"ip link add veth-test1 type veth peer name veth-test2"命令可以创建两个名字分别为veth-test1 和veth-test2 的veth pair

3.使用"ip link set DEV netns NAME"命令,把这对 veth pair 分别放到两个network namespace里面

[vagrant@localhost ~]$ sudo ip link set veth0 netns net1
[vagrant@localhost ~]$ sudo ip link set veth1 netns net2
[vagrant@localhost ~]$ sudo ip netns exec net1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: veth0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 32:45:a7:42:31:e7 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[vagrant@localhost ~]$ sudo ip netns exec net2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 4a:e8:98:00:13:12 brd ff:ff:ff:ff:ff:ff link-netnsid 0

4.为veth pair 配置ip 地址,并启用

[vagrant@localhost ~]$ sudo ip netns exec net1 ip addr add 192.168.1.1/24 dev veth0
[vagrant@localhost ~]$ sudo ip netns exec net2 ip addr add 192.168.1.2/24 dev veth1
[vagrant@localhost ~]$ sudo ip netns exec net1 ip link set veth0 up 
[vagrant@localhost ~]$ sudo ip netns exec net2 ip link set veth1 up 
[vagrant@localhost ~]$ sudo ip netns exec net1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: veth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 32:45:a7:42:31:e7 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 192.168.1.1/24 scope global veth0
       valid_lft forever preferred_lft forever
    inet6 fe80::3045:a7ff:fe42:31e7/64 scope link 
       valid_lft forever preferred_lft forever
[vagrant@localhost ~]$ sudo ip netns exec net2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 4a:e8:98:00:13:12 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.2/24 scope global veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::48e8:98ff:fe00:1312/64 scope link 
       valid_lft forever preferred_lft forever

 此时,可以看到net1中的veth0和net2中的veth1都是"UP"状态,并且具备的ip地址

5.通过ping命令测试两个network namespace的连通性

[vagrant@localhost ~]$ sudo ip netns exec net1 ping 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.056 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.138 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.147 ms
64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=0.139 ms
^[^C
--- 192.168.1.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 0.056/0.120/0.147/0.037 ms
[vagrant@localhost ~]$ sudo ip netns exec net2 ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.054 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.127 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.135 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=0.135 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=0.114 ms
^[^C
--- 192.168.1.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3996ms
rtt min/avg/max/mdev = 0.054/0.113/0.135/0.030 ms

最终完成的拓扑结构:

 

多个network namespace 之间通信

通过veth pair可以实现两个network namespace之间的通信,但是当多个 namespace 需要通信的时候,就需要借助交换机和路由器了。因为这里要考虑的只是同个网络,所以只用到交换机的功能。

linux 提供了虚拟交换机的功能,linux中的bridge是一个虚拟网络设备,具有网络设备的特征,可以配置IP、MAC地址等,同时,它也是一个虚拟交换机,和物理交换机有类似的功能。

对于普通的网络设备来说,只有两端,从一端进来的数据会从另一端出去,如物理网卡从外面网络中收到的数据会转发给内核协议栈,而从协议栈过来的数据会转发到外面的物理网络中。而bridge不同,bridge有多个端口,数据可以从任何端口进来,进来之后从哪个口出去和物理交换机的原理差不多,要看mac地址。

下面我们通过bridge来实现多个network namespace之间的通信

1.创建一个bridge命名为br0, 并开启

[vagrant@localhost ~]$ sudo ip link add br0 type bridge
[vagrant@localhost ~]$ sudo ip link set dev br0 up
[vagrant@localhost ~]$ brctl show
bridge name	bridge id		STP enabled	interfaces
br0		8000.000000000000	no		
docker0		8000.0242574bb7c9	no		

新创建的br0的interfaces为空 

2.创建3个network namespace,分别命名为net0,net1,net2

[vagrant@localhost ~]$ sudo ip netns delete net1
[vagrant@localhost ~]$ sudo ip netns delete net2
[vagrant@localhost ~]$ sudo ip netns list
[vagrant@localhost ~]$ sudo ip netns add net0
[vagrant@localhost ~]$ sudo ip netns add net1
[vagrant@localhost ~]$ sudo ip netns add net2
[vagrant@localhost ~]$ sudo ip netns list
net2
net1
net0

3.创建veth pair,将其一端放入net0,为其设置ip地址,并开启,另一端放入br0,开启;

[vagrant@localhost ~]$ sudo ip link add type veth

[vagrant@localhost ~]$ sudo ip link set dev veth1 netns net0
[vagrant@localhost ~]$ sudo ip netns exec net0 ip link set dev veth1 name eth0
[vagrant@localhost ~]$ sudo ip netns exec net0 ip addr add 10.0.1.1/24 dev eth0
[vagrant@localhost ~]$ sudo ip netns exec net0 ip link set dev eth0 up

[vagrant@localhost ~]$ sudo ip link set dev veth0 master br0
[vagrant@localhost ~]$ sudo ip link set dev veth0 up

[vagrant@localhost ~]$ bridge link
7: veth0 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2 

4.对net1,net2做同样的操作

[vagrant@localhost ~]$ sudo ip link add type veth
[vagrant@localhost ~]$ sudo ip link set dev veth1 netns net1
[vagrant@localhost ~]$ sudo ip netns exec net1 ip link set dev veth1 name eth0
[vagrant@localhost ~]$ sudo ip netns exec net1 ip addr add 10.0.1.2/24 dev eth0
[vagrant@localhost ~]$ sudo ip netns exec net1 ip link set dev eth0 up

[vagrant@localhost ~]$ sudo ip link add type veth
[vagrant@localhost ~]$ sudo ip link set dev veth1 netns net2
[vagrant@localhost ~]$ sudo ip netns exec net2 ip link set dev veth1 name eth0
[vagrant@localhost ~]$ sudo ip netns exec net2 ip addr add 10.0.1.3/24 dev eth0
[vagrant@localhost ~]$ sudo ip netns exec net2 ip link set dev eth0 up


[vagrant@localhost ~]$ sudo ip link set dev veth2 master br0 
[vagrant@localhost ~]$ brctl show
bridge name	bridge id		STP enabled	interfaces
br0		8000.5a88ac7b8aa1	no		veth0
							veth2
docker0		8000.0242574bb7c9	no		

[vagrant@localhost ~]$ sudo ip link set dev veth3 master br0 
[vagrant@localhost ~]$ brctl show
bridge name	bridge id		STP enabled	interfaces
br0		8000.2e88ab9a3390	no		veth0
							veth2
							veth3
docker0		8000.0242574bb7c9	no		

[vagrant@localhost ~]$ sudo ip link set dev veth2 up 
[vagrant@localhost ~]$ sudo ip link set dev veth3 up

5.通过ping命令测试network namespace的连通性

[vagrant@localhost ~]$ sudo ip netns exec net0 ping 10.0.1.3
PING 10.0.1.3 (10.0.1.3) 56(84) bytes of data.
64 bytes from 10.0.1.3: icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from 10.0.1.3: icmp_seq=2 ttl=64 time=0.160 ms
64 bytes from 10.0.1.3: icmp_seq=3 ttl=64 time=0.181 ms
64 bytes from 10.0.1.3: icmp_seq=4 ttl=64 time=0.157 ms
64 bytes from 10.0.1.3: icmp_seq=5 ttl=64 time=0.149 ms
64 bytes from 10.0.1.3: icmp_seq=6 ttl=64 time=0.157 ms
64 bytes from 10.0.1.3: icmp_seq=7 ttl=64 time=0.186 ms
64 bytes from 10.0.1.3: icmp_seq=8 ttl=64 time=0.229 ms
64 bytes from 10.0.1.3: icmp_seq=9 ttl=64 time=0.163 ms
64 bytes from 10.0.1.3: icmp_seq=10 ttl=64 time=0.199 ms
^[^C
--- 10.0.1.3 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 8992ms
rtt min/avg/max/mdev = 0.099/0.168/0.229/0.032 ms

最终完成的拓扑结构:

 

二.docker中的网络

docker network ls          //查看网络
[vagrant@localhost ~]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
f39b54258a15        bridge              bridge              local
5271b8795813        host                host                local
49be2954c955        none                null                local

docker network inspect 网络ID  //网络ID查看某个网络详情

我们在使用docker run创建Docker容器的同时,系统会自动为容器创建一个和主机隔离开的network namespace, 以实现网络的隔离.

Docker中的网络模式主要有4种:Bridge Network,  Host Network,  None Network,  以及Overlay Network.  其中,前三种网络模式用于单机网络, Overlay用于多机网络.

在创建Docker容器时,可以用参数"--net=网络模式" 来指定Docker容器间的网络模式,默认的网络通信模式为Bridge模式.

Bridge Network

bridge network是默认Docker容器间的网络模式,其实现原理就是通过一个bridge实现的,在安装Docker后,Docker主机中会多出一个名为"docker0"的bridge,以用于docker容器间的网络通信

[vagrant@localhost ~]$ 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
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:57:4b:b7:c9 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

以bridge模式创建一个具有网络操作的docker容器时,系统会通过一对veth pair将docker容器和docker0连接起来,以便实现单机中docker容器之间的通信,其拓扑结构如图:

以上是单机中docker容器之间通信的原理,而单机中的docker容器要访问到外面的公网,则是通过NAT技术实现的,docker主机可以通过eth0访问外网,通过NAT将内部ip转换的成eth0上即可实现容器的外网访问.

NAT(Network Address Translation)允许一个整体机构以一个公用IP(Internet Protocol)地址出现在Internet上。它是一种把内部私有网络地址(IP地址)翻译成合法网络IP地址的技术,NAT在一定程度上,能够有效的解决公网地址不足的问题.

docker容器之间的 link 

在实际项目中,通常会有一种需求,即一个容器需要访问另一个容器,如一个后台服务的容器往往需要对一个数据库服务的容器进行访问.而一个容器要访问另一个容器则必须知道要访问容器的ip地址及端口号,一个服务的端口号一般是固定的,但ip地址通常不易确定.而在donker技术中,通过link机制可以解决此类问题.

在创建一个容器时,我们可以为其指定一个名字,另一个容器可以通过名字link到要访问的容器上,ip可能会变,但名字不会变.

//创建一个命为test1的容器
sudo docker run -d --name test1 busybox /bin/sh "while true; do sleep 3600; done"

//创建一个命为test2的容器,并将其link到test1上
sudo docker run -d --name test2 --link test1 busybox /bin/sh "while true; do sleep 3600; done"

//此时,test2可以直接通过名字"test1"来访问test1
docker exec -it test2 /bin/sh 
ping test1

//注:通过ip地址,test1和test2是可以互相访问的,因为它们均连接到了docker0上,但通过名字,只能是test2访问test1,link带有方向性的

除了默认的"docker0"这个docker bridge外,我们也可以手动自己为docker容器创建bridge

docker network create -d bridge my-dockerbridge

[vagrant@localhost ~]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
59165bca53f6        bridge              bridge              local
5271b8795813        host                host                local
35ff9971200a        my-dockerbridge     bridge              local
49be2954c955        none                null                local

此时,我们可以看到除了docker自带的bridge, host, none三个网络外,多出一个我们自己创建的网络.在创建docker容器时,我们可以通过参数 --network 指定容器连接到某个网络上,如果为指定,则默认连接到自带的bridge(docker0)上

docker run -d --name test3 --network my-dockerbridge busybox /bin/sh "while true; do sleep 3600; done"

我们也可以通过命令将已经连接在某个网络的容器,另外再连接到另一个网络上,将该容器连接到了两个网络上

docker network connect my-dockerbridge test2

值得注意的是,连接到用户自定义的bridge(非docker0)上的docker容器默认是起来的,即双方均可以通过名字访问对方

docker exec -it test3 /bin/sh
ping test2

docker exec -it test2 /bin/sh
ping test3

端口映射

将docker容器中某个服务的端口映射到其docker主机的某个端口上,则可以通过docker主机的端口获得容器提供的服务

docker run -d -p 80:80 ngixn

 

Host Network

将容器连接到host网络,则该容器没有自己独立的network namespace.其network namespace与其docker主机共享,容器的网络接口将和其docker主机的网络接口保持一致,

None Network

将容器连接到none网络,即意味着该容器是孤立的,即外部其它容器无法访问该容器,访问该容器的方法只有通过命令"docker exec -it dockerName /bin/sh"实现

Overlay Network

通过overlay的方式,可以实现在一个局域网中的多个docker主机上的容器之间的通信,overlay的具体实现是基于VXLAN技术实现的

相关参考:

linux 网络虚拟化:network namespace 简介

docker bridge网络

Docker的网络模式bridge、host、container other、overlay

docker的五种网络模式总结

猜你喜欢

转载自blog.csdn.net/jt102605/article/details/86228690