使用Linux进行IP层网络管理的指     http://linux-ip.net/html/

# yum install iproute

http://linux-ip.net/html/tools-ip-route.html

https://segmentfault.com/a/1190000000638244

参考:https://cizixs.com/2017/02/10/network-virtualization-network-namespace/

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

介绍 network namespace 的基本概念和用法,network namespace 是 linux 内核提供的功能,借助 ip 命令来完成各种操作。ip 命令来自于 iproute2 安装包,一般系统会默认安装,如果没有的话,请读者自行安装。

NOTE:ip 命令因为需要修改系统的网络配置,默认需要 sudo 权限。这篇文章使用 root 用户执行,请不要在生产环境或者重要的系统中用 root 直接执行,以防产生错误。

ip 命令管理的功能很多,和 network namespace 有关的操作都是在子命令 ip netns 下进行的,可以通过 ip netns help 查看所有操作的帮助信息。

默认情况下,使用 ip netns 是没有网络 namespace 的,所以 ip netns ls 命令看不到任何输出。

# ip
Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }
ip [ -force ] -batch filename
where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |
tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |
netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |
vrf }
OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |
-h[uman-readable] | -iec |
-f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |
- | - | -I | -D | -B | - |
-l[oops] { maximum-addr-flush-attempts } | -br[ief] |
-o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |
-rc[vbuf] [size] | -n[etns] name | -a[ll] |?-c[olor]}
# 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

创建 network namespace 也非常简单,直接使用 ip netns add 后面跟着要创建的 namespace 名称。
如果相同名字的 namespace 已经存在,命令会报 Cannot create namespace 的错误。

# ip netns add r1
# ip netns list
    r1

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

有了自己创建的 network namespace,我们还需要看看它里面有哪些东西。对于每个 network namespace 来说,它会有自己独立的网卡、路由表、ARP 表、iptables 等和网络相关的资源。ip命令提供了 ip netns exec 子命令可以在对应的 network namespace 中执行命令,比如我们要看一下这个 network namespace 中有哪些网卡。更棒的是,要执行的可以是任何命令,不只是和网络相关的(当然,和网络无关命令执行的结果和在外部执行没有区别)。比如下面例子中,执行 bash 命令了之后,后面所有的命令都是在这个 network namespace 中执行的,好处是不用每次执行命令都要把 ip netns exec NAME 补全,缺点是你无法清楚知道自己当前所在的 shell,容易混淆。

[@node3 ~]# ip netns exec r1 ifconfig
[@node3 ~]# ip netns exec r1 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

[@node3 ~]# ip netns exec r1 bash
[@node3 ~]# 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 能工作,不要忘记启用它:

[root@node3 ~]# ip netns exec r1 ip link set lo up

默认情况下,network namespace 是不能和主机网络,或者其他 network namespace 通信的。

# ip link help
Usage: ip link add [link DEV] [ name ] NAME
[ txqueuelen PACKETS ]
[ address LLADDR ]
[ broadcast LLADDR ]
[ mtu MTU ] [index IDX ]
[ numtxqueues QUEUE_COUNT ]
[ numrxqueues QUEUE_COUNT ]
type TYPE [ ARGS ] ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ] ip link set { DEVICE | dev DEVICE | group DEVGROUP }
[ { up | down } ]
[ type TYPE ARGS ]
[ arp { on | off } ]
[ dynamic { on | off } ]
[ multicast { on | off } ]
[ allmulticast { on | off } ]
[ promisc { on | off } ]
[ trailers { on | off } ]
[ carrier { on | off } ]
[ txqueuelen PACKETS ]
[ name NEWNAME ]
[ address LLADDR ]
[ broadcast LLADDR ]
[ mtu MTU ]
[ netns { PID | NAME } ]
[ link-netnsid ID ]
[ alias NAME ]
[ vf NUM [ mac LLADDR ]
[ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]
[ rate TXRATE ]
[ max_tx_rate TXRATE ]
[ min_tx_rate TXRATE ]
[ spoofchk { on | off} ]
[ query_rss { on | off} ]
[ state { auto | enable | disable} ] ]
[ trust { on | off} ] ]
[ node_guid { eui64 } ]
[ port_guid { eui64 } ]
[ xdp { off |
object FILE [ section NAME ] [ verbose ] |
pinned FILE } ]
[ master DEVICE ][ vrf NAME ]
[ nomaster ]
[ addrgenmode { eui64 | none | stable_secret | random } ]
[ protodown { on | off } ] ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE] ip link xstats type TYPE [ ARGS ] ip link afstats [ dev DEVICE ] ip link help [ TYPE ] TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |
gre | gretap | ip6gre | ip6gretap | vti | nlmon | team_slave |
bond_slave | ipvlan | geneve | bridge_slave | vrf | macsec }

network namespace 之间通信

有了不同 network namespace 之后,也就有了网络的隔离,但是如果它们之间没有办法通信,也没有实际用处。要把两个网络连接起来,linux 提供了 veth pair 。可以把 veth pair 当做是双向的 pipe(管道),从一个方向发送的网络数据,可以直接被另外一端接收到;或者也可以想象成两个 namespace 直接通过一个特殊的虚拟网卡连接起来,可以直接通信。

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

# ip link add name veth1.1 type veth peer name veth1.2

# ip link sh

: lo: <LOOPBACK,UP,LOWER_UP> mtu  qdisc noqueue state UNKNOWN mode DEFAULT group default qlen
link/loopback ::::: brd :::::
: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu qdisc pfifo_fast state UP mode DEFAULT group default qlen
link/ether :0c::::a2 brd ff:ff:ff:ff:ff:ff
: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu qdisc noqueue state DOWN mode DEFAULT group default
link/ether ::1a:5e:9d:4e brd ff:ff:ff:ff:ff:ff
: veth1.2@veth1.1: <BROADCAST,MULTICAST,M-DOWN> mtu qdisc noop state DOWN mode DEFAULT group default qlen 1000 //此时veth1.1和veth1.2都是在宿主机上
link/ether ::f5:::1d brd ff:ff:ff:ff:ff:ff
: veth1.1@veth1.2: <BROADCAST,MULTICAST,M-DOWN> mtu qdisc noop state DOWN mode DEFAULT group default qlen
link/ether 0e:b8::dd:5d:5a brd ff:ff:ff:ff:ff:ff
# ip addr list
: veth1.@veth1.: <BROADCAST,MULTICAST,M-DOWN> mtu qdisc noop state DOWN group default qlen 1000 //此时都是没有启动的
link/ether d2:e2:d8:fd:: brd ff:ff:ff:ff:ff:ff
: veth1.@veth1.: <BROADCAST,MULTICAST,M-DOWN> mtu qdisc noop state DOWN group default qlen
link/ether 2a:b1:4b::fa:b8 brd ff:ff:ff:ff:ff:ff

创建 veth pair 的时候可以自己指定它们的名字,比如 ip link add veth1.1 type veth peer name veth1.2 创建出来的两个名字就是 veth1.1 和 veth1.2 。

如果 pair 的一端接口处于 DOWN 状态,另一端能自动检测到这个信息,并把自己的状态设置为 NO-CARRIER

创建结束之后,能看到名字为 veth1.1 和 veth1.2 两个网络接口。

# ip link help

Usage: ip link add [link DEV] [ name ] NAME
ip link set { DEVICE | dev DEVICE | group DEVGROUP }
[ { up | down } ]
[ type TYPE ARGS ]
[ arp { on | off } ]
[ dynamic { on | off } ]
[ multicast { on | off } ]
[ allmulticast { on | off } ]
[ promisc { on | off } ]
[ trailers { on | off } ]
[ carrier { on | off } ]
[ txqueuelen PACKETS ]
[ name NEWNAME ]
[ address LLADDR ]
[ broadcast LLADDR ]
[ mtu MTU ]
[ netns { PID | NAME } ]
[ link-netnsid ID ]
[ alias NAME ]
[ vf NUM [ mac LLADDR ]
TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |
gre | gretap | ip6gre | ip6gretap | vti | nlmon | team_slave |
bond_slave | ipvlan | geneve | bridge_slave | vrf | macsec }

接下来,要做的是把veth1.2放到namespace --> r1,把veth1.1留在宿主机上,这个可以使用 ip link set DEV netns NAME 来实现

# ip link set dev veth1.2 netns r1   //利用内核级的系统调用将网卡设备放到名称空间

# ip link sh

: lo: <LOOPBACK,UP,LOWER_UP> mtu  qdisc noqueue state UNKNOWN mode DEFAULT group default qlen
link/loopback ::::: brd :::::
: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu qdisc pfifo_fast state UP mode DEFAULT group default qlen
link/ether :0c::::a2 brd ff:ff:ff:ff:ff:ff
: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu qdisc noqueue state DOWN mode DEFAULT group default
link/ether ::1a:5e:9d:4e brd ff:ff:ff:ff:ff:ff
: veth1.1@if4: <BROADCAST,MULTICAST> mtu qdisc noop state DOWN mode DEFAULT group default qlen 1000 //此时宿主机空间只有veth1.1了
link/ether 0e:b8::dd:5d:5a brd ff:ff:ff:ff:ff:ff link-netnsid

# ip netns exec r1 ifconfig -a    //一个设备只能属于一个名称空间,查看r1名称空间信息

lo: flags=<LOOPBACK>  mtu
loop txqueuelen (Local Loopback)
RX packets bytes (0.0 B)
RX errors dropped overruns frame
TX packets bytes (0.0 B)
TX errors dropped overruns carrier collisions veth1.2: flags=4098<BROADCAST,MULTICAST> mtu 1500
ether ::f5:::1d txqueuelen (Ethernet)
RX packets bytes (0.0 B)
RX errors dropped overruns frame
TX packets bytes (0.0 B)
TX errors dropped overruns carrier collisions

# ip netns exec r1 ip link set dev veth1.2 name eth0   //更改网卡名称

# ifconfig veth1.1 10.1.0.1/24 up    //激活网卡
# ifconfig

veth1.: flags=<UP,BROADCAST,MULTICAST>  mtu
inet 10.1.0.1 netmask 255.255.255.0 broadcast 10.1.0.255
ether 0e:b8::dd:5d:5a txqueuelen (Ethernet)
RX packets bytes (0.0 B)
RX errors dropped overruns frame
TX packets bytes (0.0 B)
TX errors dropped overruns carrier collisions

# ip netns exec r1 ifconfig eth0 10.1.0.2/24 up

# ip netns exec r1 ifconfig -a

eth0: flags=<UP,BROADCAST,RUNNING,MULTICAST>  mtu
inet 10.1.0.2 netmask 255.255.255.0 broadcast 10.1.0.255
inet6 fe80:::f5ff:fe02:391d prefixlen scopeid 0x20<link>
ether ::f5:::1d txqueuelen (Ethernet)
RX packets bytes (656.0 B)
RX errors dropped overruns frame
TX packets bytes (656.0 B)
TX errors dropped overruns carrier collisions
# ping 10.1.0.2    //此时用宿主机是可以ping通的
PING 10.1.0.2 (10.1.0.2) () bytes of data.
bytes from 10.1.0.2: icmp_seq= ttl= time=0.153 ms
bytes from 10.1.0.2: icmp_seq= ttl= time=0.041 ms
# ping 10.1.0.1
PING 10.1.0.1 (10.1.0.1) () bytes of data.
bytes from 10.1.0.1: icmp_seq= ttl= time=0.054 ms
bytes from 10.1.0.1: icmp_seq= ttl= time=0.036 ms

# ip netns add r2   //添加名称空间r2

# ip link set dev veth1.1 netns r2   //把veth1.1放到新建的名称空间r2上

# ip netns exec r2 ifconfig -a

# ip netns exec r2 ifconfig veth1.1 10.1.0.3/24 up    //配置IP并激活

# ip netns exec r2 ping 10.1.02   //ping r1 的网卡IP是通的

以上的网络拓扑结构

在宿主机中创建两个namespace --> r1和r2,并利用ip netns创建一对虚拟网卡设备veth1.1和veth1.2,在网卡设备没有放进namespace的时候,这对设备是在宿主机上的,但是并没有启动,此时是可以对这对网卡配置IP并启动的。

注意:当veth1.1放进namespace r1,而veth1.2仍然在宿主机上时,如果r1上的veth1.1和veth1.2通信时,相当于r1和宿主机通信。

当把这对网卡设备放进namespace中时,在宿主上是无法查看到的,只能在namespace中可以查看,因为一个设备只支持一个名称空间

四种网络模型

第二种网络模型的配置即bridge网络模型

# docker run --name t1 -ti --rm busybox:latest    //第二种网络模型,拥有自己的网络接口
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
697743189b6d: Pull complete
Digest: sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f
Status: Downloaded newer image for busybox:latest
/ # ifconfig
eth0 Link encap:Ethernet HWaddr ::AC:::
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (1.1 KiB) TX bytes: (0.0 B) lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (0.0 B) TX bytes: (0.0 B)
 --rm    Automatically remove the container when it exits

下面对第二种网络模型进行显式配置

# docker run --name t1 -it --network bridge --rm busybox:late   //使用network指定bridge网络模式与不指定网络模式是一样的,因为docker的默认网络模式就是bridge

# docker ps -a   //查看是否有运行的容器

# docker run --name t1 -it --network none --rm busybox:latest    //无网络模式

# docker run --name t1 -it --network bridge --rm busybox:late    //bridge模式是用来对外通信的,即使不对宿主机之外的主机进行通信,也可以对统一宿主机内的其他容器进行通信,

主机间通信很多会使用主机名

/ # hostname
d291645c7bac    //docker容器的ID号就是docker的主机名,可以使用#docker ps查看,也可以手动指定主机名

# docker run --name t1 -it --network bridge -h t1.beisen --rm busybox:latest   //直接指定主机名

/ # hostname   //注意:这个容器想要通过主机名的方式访问其他主机/容器,有种方式:一:通过DNS解析主机名,二:通过本地的hosts文件
t1.beisen

/ # cat /etc/hosts
   127.0.0.1 localhost
   ::1 localhost ip6-localhost ip6-loopback
   fe00::0 ip6-localnet
   ff00::0 ip6-mcastprefix
   ff02::1 ip6-allnodes
   ff02::2 ip6-allrouters
   172.17.0.2 t1.beisen t1   //对自己解析没有问题

/ # cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8   //这里所用的DNS解析服务器是宿主机的DNS服务器,如果能与8.8.8.8通信,那么此处的DNS服务器就可以解析此容器的主机名

/ # nslookup -type=A www.baidu.com
Server: 8.8.8.8
Address: 8.8.8.8: Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com
Name: www.a.shifen.com
Address: 61.135.169.121
Name: www.a.shifen.com
Address: 61.135.169.125

想要对某一主机的访问不是通过DNS解析服务器,而是通过/etc/hosts文件来访问

# docker run --help   //非常有帮助

Usage:    docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

Run a command in a new container

Options:
--add-host list Add a custom host-to-IP mapping (host:ip)
-a, --attach list Attach to STDIN, STDOUT or STDERR
--blkio-weight uint16 Block IO (relative weight), between and , or to
disable (default )
.........
--dns list Set custom DNS servers
--dns-option list Set DNS options
--dns-search list Set custom DNS search domains //指明搜索域
--entrypoint string Overwrite the default ENTRYPOINT of the image........
-v, --volume list Bind mount a volume
--volume-driver string Optional volume driver for the container
--volumes-from list Mount volumes from the specified container(s)
-w, --workdir string Working directory inside the container

# docker run --name t1 -it --network bridge -h t1.beisen --dns 114.114.114.114 --rm busybox:latest   //指定域名服务器

/ # cat /etc/resolv.conf
nameserver 114.114.114.114

# docker run --name t1 -it --network bridge -h t1.beisen --dns 114.114.114.114 --dns-search ilinux.io --rm busybox:latest    //指定搜索域

/ # cat /etc/resolv.conf
search ilinux.io
nameserver 114.114.114.114

如果期望在/etc/hosts文件中自动解析www.baidu.com这样的主机名该如何操作?

Usage:    docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container
Options:
--add-host list Add a custom host-to-IP mapping (host:ip) //生成hosts文件的解析记录

# docker run --name t1 -it --network bridge -h t1.beisen --dns 114.114.114.114 --dns-search ilinux.io --add-host www.baidu.com:1.1.1.1 --rm busybox:latest

//这里直接在容器外通过它的文件系统向内注入信息,而不用启动容器后再进行编辑

/ # cat /etc/hosts
127.0.0.1 localhost
:: localhost ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02:: ip6-allnodes
ff02:: ip6-allrouters
1.1.1.1 www.baidu.com
172.17.0.2 t1.beisen t1

开放内网通信

假如容器在bridge网络模式下,docker容器提供的nginx服务,那么nginx是向外提供服务的。但是默认情况下这个容器是隐藏在docker bridge之后的,容器的IP地址172.17网络是个私有地址。

比如在node2上启动一个容器,IP为172.17.0.2,在node3上是无法ping通此IP的。因此需要把想运行在容器中的nginx这样的服务暴露到对外通信的网络中去。

暴露的方式有四种:

     Opening inbound communication

# docker images

REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
studycolumn/httpd v0.2 24577eceda35 days ago .2MB //将此版本启动一个容器,将此容器的80端口映射至宿主机的一个随机端口
beisen/httpd v0.- 439595aa4b0f days ago .2MB
redis -alpine 23d561d12e92 weeks ago .5MB
nginx 1.14-alpine 66952fd0a8ef weeks ago 16MB
busybox latest 3a093384ac30 weeks ago .2MB
quay.io/coreos/flannel v0.10.0-amd64 f0fad859c909 months ago .6MB

1、-p <containerPort>

将指定的容器端口映射至主机所有地址的一个动态端口

注意:"主机所有端口"表示一个宿主机上可能会有多个IP,那么 -p 80会把此宿主机的所有IP的 32768 端口都映射为 80 端口。

2、-p <hostPort>::<containerPort>   

将容器端口<containerPort>映射至指定的主机端口<hostPort>

3、-p <ip>::<containerPort>   //中间两个冒号,两个冒号中间表示宿主机使用什么端口,如果为空就表示随机端口

将指定的容器端口<containerPort>映射至主机指定<ip>的动态端口

4、-p <ip>:<hostPort>:<containerPort>

将指定的容器端口<containerPort>映射至主机指定<ip>的端口<hostPort>

#  docker run --name myweb --rm -p 80 studycolumn/httpd:v0.2

# docker inspect myweb

。。。。。。

"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.7",

。。。。。。

# curl 172.17.0.7
   <h1>Busybox server<h1>   //在本机内访问容器的nginx服务是可以的,但这只是主机内部的访问,我们想要的效果是外部的物理主机访问此宿主机的IP时,容器内运行的nginx程序提供的web服务器应有相应

但是外部的网络访问容器所在的宿主机IP时,应该访问哪个端口?因为80端口是容器内的程序提供的,而不是宿主机直接提供的。

# iptables -t nat -vnL   //可以使用此命令在宿主机上进行查看

Chain PREROUTING (policy ACCEPT  packets,  bytes)
pkts bytes target prot opt in out source destination
DOCKER all -- * * 0.0.0.0/ 0.0.0.0/ ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT packets, bytes)
pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT packets, bytes)
pkts bytes target prot opt in out source destination
DOCKER all -- * * 0.0.0.0/ !127.0.0.0/ ADDRTYPE match dst-type LOCAL Chain POSTROUTING (policy ACCEPT packets, bytes)
pkts bytes target prot opt in out source destination
MASQUERADE all -- * !docker0 172.17.0.0/ 0.0.0.0/
MASQUERADE tcp -- * * 172.17.0.7 172.17.0.7 tcp dpt: Chain DOCKER ( references)
pkts bytes target prot opt in out source destination
RETURN all -- docker0 * 0.0.0.0/ 0.0.0.0/
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.7:80 //此规则是在上面创建容器是 -p 80 自动生成的,当容器消失时,这条规则会自动删除

在浏览器中输入宿主机的IP+映射端口:192.168.128.131:32768 即可访问容器中nginx程序提供的web服务

查看映射端口的另一种方法

# docker port myweb      //查看映射端口的另一种方法
  80/tcp -> 0.0.0.0:32769   //0.0.0.0表示宿主机的所有端口,注意在容器被删除后,再创建同样的容器,映射的随机端口是不断增长的,比如上面映射的端口是32768,推出后再次创建容器映射的随机端口就是32769了。

# docker kill myweb   //此时容器myweb被删除,iptables规则就不存在了

如果希望指定映射端口或者IP,而不是自动生成iptables规则,不是映射为一个动态端口,而是映射为固定的地址。

3、-p <ip>::<containerPort>   //中间两个冒号,两个冒号中间表示宿主机使用什么端口,如果为空就表示随机端口

将指定的容器端口<containerPort>映射至主机指定<ip>的动态端口

#  docker run --name myweb --rm -p 192.168.128.131::80 studycolumn/httpd:v0.2

# docker port myweb
  80/tcp -> 192.168.128.131:32768

如果宿主机的80端口没有被使用,也可以指定宿主机的80端口

#  docker run --name myweb --rm -p 80:80 studycolumn/httpd:v0.2

# docker port myweb
  80/tcp -> 0.0.0.0:80

4、-p <ip>:<hostPort>:<containerPort>

将指定的容器端口<containerPort>映射至主机指定<ip>的端口<hostPort>

#  docker run --name myweb --rm -p 192.168.128.131:80:80 studycolumn/httpd:v0.2

# docker port myweb
  80/tcp -> 192.168.128.131:80

第三种网络模型:联盟式容器 Joined containers

# docker images

REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
studycolumn/httpd v0. 24577eceda35 days ago .2MB
beisen/httpd v0.- 439595aa4b0f days ago .2MB
redis -alpine 23d561d12e92 weeks ago .5MB
nginx 1.14-alpine 66952fd0a8ef weeks ago 16MB
busybox latest 3a093384ac30 weeks ago .2MB
quay.io/coreos/flannel v0.10.0-amd64 f0fad859c909 months ago .6MB

# docker run --name b2 -it --rm busybox

/ # ifconfig
eth0 Link encap:Ethernet HWaddr ::AC:::
inet addr:172.17.0.7
Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (516.0 B) TX bytes: (0.0 B)

# docker run --name b2 --network container:b1 -it --rm busybox   //上面启动的容器地址与本次启动的容器地址是不同的,因为本次创建的容器是共享了b1的IP,但是b1和b2之间的文件系统等还是隔离的。

/ # ifconfig
eth0 Link encap:Ethernet HWaddr ::AC:::
inet addr:172.17.0.4
Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (1.2 KiB) TX bytes: (0.0 B)

/ # echo "hello world" > /tmp/index.html   //注意这里是容器:b2
/ # httpd -h /tmp/   //启动httpd,并把根目录放到/tmp

在b1上访问容器的回环地址lo,可以看到容器b2提供的web服务,所以b1和b2两者是共享网络空间

/ # wget -O - -q 127.0.0.1
hello world

第四种网络模型:开放式网络,直接共享宿主机的网络

下面把两个容器全部退出,然后重新创建容器b2

# docker run --name b2 --network host -it --rm busybox   //此次创建的容器b2共享的是宿主机的网络

/ # ifconfig
docker0 Link encap:Ethernet HWaddr ::8F:0E:6A:
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
inet6 addr: fe80:::8fff:fe0e:6a12/ Scope:Link
UP BROADCAST RUNNING MULTICAST MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (9.0 KiB) TX bytes: (12.2 KiB) ens33 Link encap:Ethernet HWaddr :0C::EE::1E
inet addr:192.168.128.131 Bcast:192.168.128.255 Mask:255.255.255.0
inet6 addr: fe80::f528:::13a7/ Scope:Link
UP BROADCAST RUNNING MULTICAST MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (102.1 MiB) TX bytes: (324.4 MiB) lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::/ Scope:Host
UP LOOPBACK RUNNING MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (0.0 B) TX bytes: (0.0 B)
/ # echo "hello world" > /tmp/index.html
/ # httpd -h /tmp/
/ # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0.0.0.0: 0.0.0.0:* LISTEN
tcp 127.0.0.1: 0.0.0.0:* LISTEN
tcp 0 0 :::80 :::* LISTEN //可以看出这里监听的是本主机的80端口,所以外网主机可以直接访问容器提供的web服务
tcp ::: :::* LISTEN
tcp ::: :::* LISTEN

基于 --network host 这种方式的好处在于:不用安装httpd程序,当宿主机出问题的时候,可以直接把容器迁移到其他主机上,直接共享宿主机的网络

自定义docker桥,分别使用不同网段的地址

如何更改默认docker0上的网络地址

自定义docker0桥的网络属性信息:/etc/docker/daemon.json文件
{
"bip": "192.168.1.5/24", //只要修改bip,其他选项即可通过计算得知,除了dns
"fixed-cidr": "10.20.0.0/16", //注意:只要不是最后一行,行尾一定要有 ","
"fixed-cidr-v6": "2001:db8::/64",
"mtu": ,
"default-gateway": "10.20.1.1",
"default-gateway-v6": "2001:db8:abcd::89",
"dns": ["10.20.1.2","10.20.1.3"]
}

核心选项为bip,即bridge ip之意,用于指定docker0桥自身的IP地址,系统会自动推算出这个bridge ip所属网络,
并把这个网络当作随后加入这个bridge的所有容器的默认网络。

演示:修改默认docker0桥的网络IP

# docker kill NAME   //杀死所有运行的容器

# systemctl stop docker

# vim /etc/docker/daemon.json

{
"registry-mirrors": ["https://registry.docker-cn.com","https://7g3yx938.mirror.aliyuncs.com"], //注意一定要有逗号
"bip": "10.0.0.1/16" //添加此行
}

# systemctl start docker
# ifconfig

docker0: flags=<UP,BROADCAST,MULTICAST>  mtu 
inet 10.0.0.1
netmask 255.255.0.0 broadcast 10.0.255.255 //docker0的默认IP地址已经更换,此时docker0的DNS服务器的IP是宿主机的,因为没有指明
inet6 fe80:::1aff:fe5e:9d4e prefixlen scopeid 0x20<link>
ether ::1a:5e:9d:4e txqueuelen (Ethernet)
RX packets bytes (535.0 B)
RX errors dropped overruns frame
TX packets bytes (1.3 KiB)
TX errors dropped overruns carrier collisions

dockerd守护进程的C/S,其默认仅监听Unix SOcket格式的地址,所以docker仅支持本地通信,路径是:/var/run/docker.sock;

所以不同宿主机上的容器是不能互相管理的

如果想要docker服务器可以被外网访问,需要使用TCP套接字,/etc/docker/daemon.json:
"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]   //本来只有此路径:unix:///var/run/docker.sock,这里再添加tcp://0.0.0.0:2375表示监听本机tcp协议的所有IP地址的2375端口
也可向docker直接传递"-H|--host"选项;

# systemctl stop docker

# vim /etc/docker/daemon.json

{
"registry-mirrors": ["https://registry.docker-cn.com","https://7g3yx938.mirror.aliyuncs.com"],
"bip": "10.0.0.1/16",
"host": ["tcp://0.0.0.0:2375","unix:///var/run/docker.sock"] //添加此行
"storage-driver": "devicemapper" //此行可以先不添加,如果有此报错信息:start request repeated too quickly for docker.service 再添加

}

# systemctl start docker

自定义网络类型 --> bridge

# docker info

...

Network: bridge host macvlan null overlay    //docker有多种类型(overlay是叠加网络)

...

# docker network --help

Usage:    docker network COMMAND

Manage networks

Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks Run 'docker network COMMAND --help' for more information on a command.

# docker network create --help

Usage:    docker network create [OPTIONS] NETWORK

Create a network

Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
--config-from string The network from which copying the configuration
--config-only Create a configuration only network
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment

# docker network create -d bridge --subnet "172.26.0.0/16" --gateway "172.26.0.1" mybr0    //注意:mybr0是网络,而不是网络接口

# docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
f1f7dae9d2d8 bridge bridge local
c7a430036ed4 host host local
c1eea5d428d2 mybr0 bridge local
e1bfb53c06c1 none null local

# ifconfig    //br-c1eea5d428d2是网络接口

br-c1eea5d428d2: flags=<UP,BROADCAST,MULTICAST>  mtu
inet 172.26.0.1 netmask 255.255.0.0 broadcast 172.26.255.255
ether ::::6a: txqueuelen (Ethernet)
RX packets bytes (5.4 KiB)
RX errors dropped overruns frame
TX packets bytes (5.4 KiB)
TX errors dropped overruns carrier collisions

# ifconfig br-c1eea5d428d2 down   //重命名网络接口:br-c1eea5d428d2,修改之前必须使此网络接口下线

# ip link set dev br-c1eea5d428d2 name docker1   //重命名

# ifconfig docker1 up  //启动网络接口

docker1: flags=<UP,BROADCAST,MULTICAST>  mtu
inet 172.26.0.1 netmask 255.255.0.0 broadcast 172.26.255.255
ether ::::6a: txqueuelen (Ethernet)
RX packets bytes (0.0 B)
RX errors dropped overruns frame
TX packets bytes (0.0 B)
TX errors dropped overruns carrier collisions

创建容器是直接把这个网络加入就可以了,但是由于更改了网络接口名称,在把网络mybr0加入到容器时出了一些问题,不确定是否是修改网络接口的问题,所以再次创建了网络mybr1

# docker run --name t1 -it --net mybr1 busybox:latest  //--net是--network的缩写

/ # ifconfig   //此时就有两个桥接时网络
eth0 Link encap:Ethernet HWaddr ::AC:1B::
inet addr:172.27.0.2 Bcast:172.27.255.255 Mask:255.255.0.0

UP BROADCAST RUNNING MULTICAST MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (1.0 KiB) TX bytes: (0.0 B)

下面再创建一个容器,使用docker0网络,即原有的bridge网络

# docker run --name t2 -it --net bridge busybox:latest    //使用docker默认的桥接网路

/ # ifconfig
eth0 Link encap:Ethernet HWaddr ::0A:::
inet addr:10.0.0.2 Bcast:10.0.255.255 Mask:255.255.0.0

UP BROADCAST RUNNING MULTICAST MTU: Metric:
RX packets: errors: dropped: overruns: frame:
TX packets: errors: dropped: overruns: carrier:
collisions: txqueuelen:
RX bytes: (1.2 KiB) TX bytes: (0.0 B)

此时容器t1和t2是否能通信?是可以通信的,因为网卡1 mybr0和网卡2 docker0同时都有一个接口与宿主机相连接,只需要在宿主机上打开核心转发功能即可

# cat /proc/sys/net/ipv4/ip_forward   //核心转发功能已经打开
1

/ # ping 172.27.0.2   //在容器t2上ping容器t1的IP,此时无法ping通,因为宿主机上存在很多iptables规则

# iptables -vnL --line-number   //分析iptables规则后发现有一条规则是组织容器间通信

Chain DOCKER-ISOLATION-STAGE- ( references)
num pkts bytes target prot opt in out source destination
1 4 336 DROP all -- * br-fa8eef203943 0.0.0.0/0 0.0.0.0/0 //修改此条iptables规则的target为ACCEPT
DROP all -- * docker0 0.0.0.0/ 0.0.0.0/
DROP all -- * br-c1eea5d428d2 0.0.0.0/ 0.0.0.0/
RETURN all -- * * 0.0.0.0/ 0.0.0.0/

# iptables -t filter -R DOCKER-ISOLATION-STAGE-2 1 -j ACCEPT    //此规则可参考:http://www.zsythink.net/archives/1517

/ # ping 172.27.0.2    //此时在容器t2(10.0.0.2)上即可ping通容器t1
PING 172.27.0.2 (172.27.0.2): 56 data bytes
64 bytes from 172.27.0.2: seq=0 ttl=63 time=1.385 ms
64 bytes from 172.27.0.2: seq=1 ttl=63 time=0.264 ms

可以想象为什么有iptables规则阻止两个容器通信,是因为既然是两个网段就是不让通信的。

5、Docker容器网络的更多相关文章

  1. Docker容器网络篇

    Docker容器网络篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Docker的网络模型概述 如上图所示,Docker有四种网络模型: 封闭式网络(Closed conta ...

  2. 【转】理解Docker容器网络之Linux Network Namespace

    原文:理解Docker容器网络之Linux Network Namespace 由于2016年年中调换工作的原因,对容器网络的研究中断过一段时间.随着当前项目对Kubernetes应用的深入,我感觉之 ...

  3. 两台主机间docker容器网络互通

    服务器1: 网络172.30.0.0/16 服务器2: 网络172.31.0.0/16 服务器1和服务器2上的docker容器网络之间是无法互通的,如果需要互通,需要做以下配置: 服务器1上执行: i ...

  4. docker容器网络bridge

    我们知道docker利用linux内核特性namespace实现了网络的隔离,让每个容器都处于自己的小世界里面,当这个小世界需要与外界(宿主机或其他容器)通信的时候docker的网络就发挥作用了,这篇 ...

  5. Docker容器网络-基础篇

    开源Linux 一个执着于技术的公众号 Docker的技术依赖于Linux内核的虚拟化技术的发展,Docker使用到的网络技术有Network Namespace.Veth设备对.Iptables/N ...

  6. Docker容器网络配置

    Docker容器网络配置 1.Linux内核实现名称空间的创建 1.1 ip netns命令 可以借助ip netns命令来完成对 Network Namespace 的各种操作.ip netns命令 ...

  7. docker容器网络

    1.我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式: · host模式,使用--net=host指定 · containe ...

  8. 初学Docker容器网络不得不看的学习笔记

    一.关于Docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后 ...

  9. Docker 容器网络

    默认网络 当安装docker时,它会自动创建3个网络.可以使用docker network ls 来查看.   zane@zane-V:~$ docker network ls NETWORK ID ...

随机推荐

  1. 全排列 permutation

    给定一个数字列表,返回其所有可能的排列 lintcode package www.dxb.com; import java.util.List;import java.util.ArrayList; ...

  2. python函数带()与否

    一.不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不须等该函数执行完成二.带括号(参数或者无参),调用的是函数的执行结果,须等该函数执行完成的结果 进程和线程的target=fun ...

  3. 学习animate.css包含了一组炫酷、有趣、跨浏览器的动画

    1.animate.css包含了一组炫酷.有趣.跨浏览器的动画,可以在你的项目中直接使用. 第一步:引入animate.css样式文件或者引入某些平台的CDN文件: <head> < ...

  4. 18. C# 转换

    1.重载转换运算符 到目前为止,我们使用的类型转换,包括隐式类型转换和显示类型转换都是在两个相关的类中进行的,这几个类要么是简单类型之间的转换,比如int 隐式转换成double,要么是有继承关系,或 ...

  5. TextField widgets require a Material widget ancestor

    TextField widget需要被包裹在Scaffold widget中,否则会报错

  6. Java基础之数组详解

    数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同. Java 语言中提供的数组是用来存储固定大小的同类型元素. 你可以声明一个数组变量,如 numbers[1 ...

  7. c#中WebApi开发遇到的坑

    一.如何新建一个webApi项目 打开VS→找到解决方案→新建项目→类库或web应用程序→选择空的WebApi项目→在Global.asax文件的Application_Start方法中注册WebAp ...

  8. Vue学习之路---No.3(分享心得,欢迎批评指正)

    同样的,我们先来回顾一下昨天学习的内容: 1.利用v-once来组织双向绑定 2.filter{}过滤器的使用(详情请看上一章) 3.computed(计算属性),利用computed属性实现filt ...

  9. JAVA多线程实现的四种方式(转自https://www.cnblogs.com/felixzh/p/6036074.html)

    Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...

  10. shell的输入参数

    $#  参数格式 $0 $1 $2 ...第一个,第二个参数...