|
|
Docker 自身的4种网络工作方式,和一些自定义网络模式:1 I7 o" j7 n6 G4 k% _/ _" j! b6 L
安装 Docker 时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host% y0 Y4 k" L) E+ E# g' y1 k
host:容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的IP和端口。
7 y" s& G6 W. U3 C8 lContainer:创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围。
5 s$ k6 S/ E5 ~" i3 QNone:该模式关闭了容器的网络功能。) z! z6 O J% b; {
Bridge:此模式会为每一个容器分配、设置 IP 等,并将容器连接到一个 docker0 虚拟网桥,通过 docker0 网桥以及 Iptables nat 表配置与宿主机通信。. F7 ?5 R" K+ \, g7 Y& W
以上都是不用动手的,真正需要配置的是自定义网络。4 q& E+ I, M- E( }- _
一、前言9 L A s1 r+ U8 H8 Y. i+ o% `
当你开始大规模使用 Docker 时,你会发现需要了解很多关于网络的知识。
5 q' b& M# @1 _/ V2 E; xDocker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。
4 z! \; S7 B0 d6 D9 s/ N+ D" l6 ^然而,Docker 同样有着很多不完善的地方,网络方面就是 Docker 比较薄弱的部分。
1 H! u2 }& s1 r因此,我们有必要深入了解 Docker 的网络知识,以满足更高的网络需求。
; K* w# }& \5 b: K本文首先介绍了 Docker 自身的4种网络工作方式,然后介绍一些自定义网络模式。8 k7 k9 L+ C8 ~) i+ N: ~+ e( d
二、默认网络' j; u" o0 @+ S5 [# j0 i X( {
当你安装 Docker 时,它会自动创建三个网络。你可以使用以下 docker network ls命令列出这些网络:
/ a2 g. f0 T' r& e: V& Z$ docker network ls
0 \& K' C& ~/ u% [1 Z/ BNETWORK ID NAME DRIVER
1 k) |, z% P9 r" ]0 U6 Q h) ?7fca4eb8c647 bridge bridge
9 J! u- w2 G. F" |1 _9f904ee27bf5 none null' `7 R( a1 K* q- ?" @
cf03ee007fb4 host host/ ^( l8 A9 ^5 F }; j8 e6 \
Docker 内置这三个网络,运行容器时,你可以使用该 –network 标志来指定容器应连接到哪些网络。
5 ^) ~2 l' |8 C% M: \+ ]# x该 bridge 网络代表 docker0 所有 Docker 安装中存在的网络。除非你使用该 docker run --network=<NETWORK> 选项指定,否则 Docker 守护程序默认将容器连接到此网络。
- k6 ^2 E+ N+ Z l% A我们在使用 docker run 创建 Docker 容器时,可以用 –net 选项指定容器的网络模式,Docker 可以有以下 4 种网络模式:
% J! r. p& g9 B" f+ M: X+ J$ \host 模式:使用 –net=host 指定。
! r2 w$ y* }( y) D$ r8 knone 模式:使用 –net=none 指定。1 B- ^/ P' D7 P8 r* X' C9 T
bridge 模式:使用 –net=bridge 指定,默认设置。1 `" q( n5 M# G( U) w! Q
container 模式:使用 –net=container:NAME_or_ID 指定。
$ ~/ p! L: J2 u2 ?! P2 m6 Y$ G下面分别介绍一下 Docker 的各个网络模式。9 r: h2 {! i) e, `1 }
2.1 Host
% z* I* |& Q5 ?2 {; @9 |: U3 |3 k相当于 Vmware 中的桥接模式,与宿主机在同一个网络中,但没有独立 IP 地址。 m3 ?, ]' ^5 n1 S
众所周知,Docker 使用了 Linux 的 Namespaces 技术来进行资源隔离,如 PID Namespace 隔离进程,Mount Namespace 隔离文件系统,Network Namespace 隔离网络等。
0 \; y( K4 h0 O一个 Network Namespace 提供了一份独立的网络环境,包括网卡、路由、Iptable 规则等都与其他的 Network Namespace 隔离。
3 n7 S" M( r* L! g( |" O一个 Docker 容器一般会分配一个独立的 Network Namespace。2 t& u: Q/ g9 w% M9 I5 Q
但如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。- s: g! z. `! C! Y4 t5 V
容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
) V. m1 D+ r5 \2 g7 \+ v1 e例如,我们在 10.10.0.186/24 的机器上用 host 模式启动一个含有 nginx 应用的 Docker 容器,监听 tcp80 端口。+ D1 U4 g, D, W. @) z, l9 e# S& t
# 运行容器;
" [$ A- {: C# g5 a$ docker run --name=nginx_host --net=host -p 80:80 -d nginx
' R4 G. F& Q6 \5 P: A74c911272942841875f4faf2aca02e3814035c900840d11e3f141fbaa884ae5c
- J2 j3 l) P3 k; l3 w8 h5 L# 查看容器;
7 g, m. p, s0 ~ W$ docker ps 5 r, X& m; H, l* A* B/ r1 F
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1 z: q ~* u$ y74c911272942 nginx "nginx -g 'daemon ..." 25 seconds ago Up 25 seconds nginx_host
$ z( D* q$ j9 l当我们在容器中执行任何类似 ifconfig 命令查看网络环境时,看到的都是宿主机上的信息。
( O& r' [8 n- F6 ~/ |7 Y而外界访问容器中的应用,则直接使用 10.10.0.186:80 即可,不用任何 NAT 转换,就如直接跑在宿主机中一样。
3 k0 D0 m8 O2 \0 }但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
0 i) d6 x+ O& J: [$ r$ netstat -nplt | grep nginx9 L, ^8 G6 a8 u- w7 r8 q* R
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 27340/nginx: master5 b2 C/ U+ w, a9 q6 h
2.2 Container3 V4 [$ O. o8 u. t+ ^
在理解了 host 模式后,这个模式也就好理解了。
9 Z3 b7 R( J- V f/ z% L! [这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。" x, p7 B7 h2 v3 f: F
新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。7 n3 f1 H/ U' ?( f. c, i
同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。+ x" F% w: l6 V5 o. q9 v
2.3 None
) M3 T: T) o1 K( N- t' r该模式将容器放置在它自己的网络栈中,但是并不进行任何配置。; a% G1 I+ D( `/ r8 i# ?' ]! e
实际上,该模式关闭了容器的网络功能,在以下两种情况下是有用的:容器并不需要网络(例如只需要写磁盘卷的批处理任务)。* x" k* ^( m1 r5 I; z; ?
overlay& ?0 j( R. @& k! j, G
在 docker1.7 代码进行了重构,单独把网络部分独立出来编写,所以在 docker1.8 新加入的一个 overlay 网络模式。Docker 对于网络访问的控制也是在逐渐完善的。
1 x: P1 R8 ] e" m/ U2.4 Bridge+ Y, o! z, L7 [6 Y- w8 s' l
相当于 Vmware 中的 Nat 模式,容器使用独立 network Namespace,并连接到 docker0 虚拟网卡(默认模式)。
- G) T5 |6 K/ p# } }; G2 `通过 docker0 网桥以及 Iptables nat 表配置与宿主机通信;
, c. h. N8 [+ ?0 j1 @bridge 模式是 Docker 默认的网络设置,此模式会为每一个容器分配 Network Namespace、设置 IP 等,并将一个主机上的 Docker 容器连接到一个虚拟网桥上。下面着重介绍一下此模式。
1 S& }5 M5 s- `* I1 W2 |* K7 P三、Bridge 模式" m. W, ~, A, N6 b
3.1 Bridge 模式的拓扑5 x7 B# w7 i( a" b/ ^& o
当 Docker server 启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。' f( K( ^5 ~ I* I( c
虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。% V' i& |: g, b Q$ I3 B
接下来就要为容器分配 IP 了,Docker 会从 RFC1918 所定义的私有 IP 网段中,选择一个和宿主机不同的IP地址和子网分配给 docker0,连接到 docker0 的容器就从这个子网中选择一个未占用的 IP 使用。
5 o4 }) j# D6 c6 A( H" Z: o5 w如一般 Docker 会使用 172.17.0.0/16 这个网段,并将 172.17.0.1/16 分配给 docker0 网桥(在主机上使用 ifconfig 命令是可以看到 docker0 的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。
" Y8 d' a8 ~6 _" B+ D单机环境下的网络拓扑如下,主机地址为 10.10.0.186/24。
+ A; T" e3 X5 E2 f: D3 C" p5 K, ?! [
3.2 Docker:网络模式详解! X' L+ Z% L* O2 l2 i/ s
Docker 完成以上网络配置的过程大致是这样的:+ C5 a& s5 }7 h' N0 c$ }9 T
在主机上创建一对虚拟网卡 veth pair 设备。veth 设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth 设备常用来连接两个网络设备。
! U: Y5 x1 y2 i! y3 J9 @8 K+ P6 CDocker 将 veth pair 设备的一端放在新创建的容器中,并命名为 eth0。另一端放在主机中,以 veth65f9 这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中,可以通过 brctl show 命令查看。
) }) @" y4 {, _5 z( Y6 F$ brctl show. F' o2 M; g: b8 F+ m3 v3 I
bridge name bridge id STP enabled interfaces5 s" H. }- k, R8 D. h8 `
docker0 8000.02425f21c208 no5 M8 K$ A* b- v1 ^
从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。+ _7 t B: y6 C6 l: Q
# 运行容器;
' v% P1 w L$ C% f) A$ docker run --name=nginx_bridge --net=bridge -p 80:80 -d nginx
) l# q" S8 X* l( @/ w9582dbec7981085ab1f159edcc4bf35e2ee8d5a03984d214bce32a30eab4921a
+ J' A# m, a* I- c. q0 H8 N; }# i# 查看容器;
' y. t) ^+ r7 W, x0 E$ docker ps( d6 W8 X7 h+ v, l5 Q3 n# p
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES X2 `+ C' t# a; C2 r0 _ u
9582dbec7981 nginx "nginx -g 'daemon ..." 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp nginx_bridge
; ]* p: ^2 |4 V( A% k, H m# 查看容器网络;
* j2 J9 s3 D$ C' ~3 V9 C/ c# u$ docker inspect 9582dbec7981
( E' a9 R; i ^1 f9 w% R! K0 d"Networks": {0 x* o% F6 }* Y- E) o9 D
"bridge": { k5 h+ ~' B. A$ ~* y& n
"IPAMConfig": null,
( D# |" e, A$ s3 F; {. l "Links": null,
' M6 c. r5 x- C- S9 b$ h "Aliases": null,) G y* n) d; V, G K( w- B
"NetworkID": "9e017f5d4724039f24acc8aec634c8d2af3a9024f67585fce0a0d2b3cb470059",
3 \1 ]8 v: I9 |6 {* y ?# ] "EndpointID": "81b94c1b57de26f9c6690942cd78689041d6c27a564e079d7b1f603ecc104b3b",
+ W+ X3 T- e9 L "Gateway": "172.17.0.1",' V5 t/ ]1 v4 f* V9 q& w
"IPAddress": "172.17.0.2",1 A$ b* o$ Z8 @) f2 X" k
"IPPrefixLen": 16,
$ r( {( t' `2 Y7 m "IPv6Gateway": "",
5 k% Z' Z& |# L0 t+ ?) d "GlobalIPv6Address": "",
$ J( s* \0 }# l( e5 f9 E "GlobalIPv6PrefixLen": 0,
/ Y- o7 z6 ~& _- J "MacAddress": "02:42:ac:11:00:02"
* N3 u+ e1 u! O- N }
+ E; ` a' u( k* j+ t" U/ a}% ?' K3 _0 F: _) Q, j4 h$ L
$ docker network inspect bridge- w# y; F! b6 ^1 P9 p2 J9 V; r' j
[
# I6 P, y4 _3 r* s& h$ M& ^- r {- ?+ [- p0 g g, p: ?, o! E- Y
"Name": "bridge",( r/ ~. Z; m* A0 m$ N
"Id": "9e017f5d4724039f24acc8aec634c8d2af3a9024f67585fce0a0d2b3cb470059", L: K" }9 p9 ?- | W& Z) E/ J
"Created": "2017-08-09T23:20:28.061678042-04:00",: U& a3 V, Q/ f# I M
"Scope": "local",, G# y' e9 M! k. W
"Driver": "bridge",8 {2 j! b: y& k. b. I& s" [
"EnableIPv6": false,
$ [2 V' V, J+ l. Q! D4 ~4 T "IPAM": {9 `2 N( L4 A: Y' c6 t
"Driver": "default",
! u1 L, d, a% p `# N2 P- d "Options": null,$ s, B: P7 [5 T# S
"Config": [' U# }$ s/ \7 m$ `
{
5 x9 L+ d/ }3 i6 O* D- R "Subnet": "172.17.0.0/16"
7 c o* T, n' Z E! _+ M4 R! K5 R }
) l p) M$ A: |% ~7 A ]
2 i3 M$ {7 s# c1 t+ d/ e },
3 p; K$ X3 B0 Q: G "Internal": false,
8 q7 q) I" r1 S3 [1 V "Attachable": false,
3 U. _! P6 R# R7 ^ "Ingress": false,
; f' p. d: A5 W "Containers": {$ P& G& e3 _+ \2 t2 a+ w' f
"9582dbec7981085ab1f159edcc4bf35e2ee8d5a03984d214bce32a30eab4921a": {1 M* k( n( }9 O+ s W
"Name": "nginx_bridge",
! l- R7 C+ P) r$ d1 C9 ] "EndpointID": "81b94c1b57de26f9c6690942cd78689041d6c27a564e079d7b1f603ecc104b3b",; O2 Q" i* Y: L$ O2 @, I7 N
"MacAddress": "02:42:ac:11:00:02",( f; ^0 k, h. {2 h' F* q
"IPv4Address": "172.17.0.2/16",
8 U0 j, v" c+ v% E5 L8 B8 {: M6 q "IPv6Address": ""
7 W, N3 o- G* J) D' N" z }
; }3 F+ u7 W% Z. Z: @. E },% l. A9 R+ ], k# b
"Options": {
* U' [ `7 L' L% n/ |/ i4 f% A+ ^; ~ "com.docker.network.bridge.default_bridge": "true",
3 K( R- A, @' l! n& } "com.docker.network.bridge.enable_icc": "true",5 d( {% i9 K5 W' Z
"com.docker.network.bridge.enable_ip_masquerade": "true",
$ q+ B# S' J7 G7 m4 d$ @4 ~ A "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
4 Q& ?9 j- Q7 p- L* ^9 G$ l "com.docker.network.bridge.name": "docker0",5 W" C0 c. W; ~& n8 r2 t
"com.docker.network.driver.mtu": "1500"4 }' f% f% r" e) F4 g
},) L' ~( H* Q. O" ~
"Labels": {}9 h7 S1 T8 Y- |6 o; d* D' e3 W! C
}3 F- g8 N2 `6 P* `; @8 R
]) v% \/ J0 \2 T% n
网络拓扑介绍完后,接着介绍一下 bridge 模式下容器是如何通信的。. z4 c% y$ ?& X( n' Z
3.3 bridge 模式下容器的通信" t% @5 e" z3 ^
在 bridge 模式下,连在同一网桥上的容器可以相互通信(若出于安全考虑,也可以禁止它们之间通信,方法是在 DOCKER_OPTS 变量中设置 –icc=false,这样只有使用 –link 才能使两个容器通信)。' m! H" y7 P; P
Docker 可以开启容器间通信(意味着默认配置 –icc=true ),也就是说,宿主机上的所有容器可以不受任何限制地相互通信,这可能导致拒绝服务攻击。进一步地,Docker 可以通过 –ip_forward 和 –iptables 两个选项控制容器间、容器和外部世界的通信。
' z, z# G3 V4 Z) y9 T容器也可以与外部通信,我们看一下主机上的 Iptable 规则,可以看到这么一条
" ~+ R* s+ o- K) I, @8 F* c-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
* A# } m6 g9 O这条规则会将源地址为 172.17.0.0/16 的包(也就是从 Docker 容器产生的包),并且不是从 docker0 网卡发出的,进行源地址转换,转换成主机网卡的地址。* u; Q8 O0 A9 o
这么说可能不太好理解,举一个例子说明一下。
?3 {! _9 ] A① 假设主机有一块网卡为 eth0,IP 地址为 10.10.101.105/24,网关为 10.10.101.254。
0 B2 z9 _2 R8 L1 F! x$ u② 从主机上一个 IP 为 172.17.0.1/16 的容器中 ping 百度(180.76.3.151)。
7 @ I1 G( ]. F& D# x0 d③ IP 包首先从容器发往自己的默认网关 docker0,包到达 docker0 后,也就到达了主机上。/ J6 f0 @8 x3 q9 k, W. W% G( z
④ 然后会查询主机的路由表,发现包应该从主机的 eth0 发往主机的网关 10.10.105.254/24。
& y! n' p3 V4 Y$ G⑤ 接着包会转发给 eth0,并从 eth0 发出去(主机的 ip_forward 转发应该已经打开)。
! m& N! n9 M5 k4 s这时候,上面的 Iptable 规则就会起作用,对包做 SNAT 转换,将源地址换为 eth0 的地址。
- L+ P9 m6 U; n这样,在外界看来,这个包就是从 10.10.101.105 上发出来的,Docker 容器对外是不可见的。
% y! A4 Q) L/ t, k7 \& T0 \( C4 _% w那么,外面的机器是如何访问 Docker 容器的服务呢?我们首先用下面命令创建一个含有 web 应用的容器,将容器的 80 端口映射到主机的80端口。# `/ |& L9 W2 I8 Y- y3 o
$ docker run --name=nginx_bridge --net=bridge -p 80:80 -d nginx' n+ g+ g% h* m1 |8 o. R5 n
然后查看 Iptable 规则的变化,发现多了这样一条规则:
" M" X$ C) l9 w( P-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:803 T- W( [4 t$ s* R+ H. i
此条规则就是对主机 eth0 收到的目的端口为 80 的 tcp 流量进行 DNAT 转换,将流量发往 172.17.0.2:80,也就是我们上面创建的 Docker 容器。所以,外界只需访问 10.10.101.105:80 就可以访问到容器中的服务。
' o5 k5 _3 {% z' z* a除此之外,我们还可以自定义 Docker 使用的 IP 地址、DNS 等信息,甚至使用自己定义的网桥,但是其工作方式还是一样的。
9 n4 c0 F$ Q3 _8 O+ L: S四、自定义网络" k9 y0 [) d* r/ v: _; [2 ?& l
建议使用自定义的网桥来控制哪些容器可以相互通信,还可以自动 DNS 解析容器名称到 IP 地址。8 l( Y- t8 p& J4 V0 E J
Docker 提供了创建这些网络的默认网络驱动程序,你可以创建一个新的 Bridge 网络,Overlay 或 Macvlan 网络。你还可以创建一个网络插件或远程网络进行完整的自定义和控制。
& r3 E+ w) N1 P' f* F你可以根据需要创建任意数量的网络,并且可以在任何给定时间将容器连接到这些网络中的零个或多个网络。; ]& [9 V2 u" f z: H
此外,您可以连接并断开网络中的运行容器,而无需重新启动容器。当容器连接到多个网络时,其外部连接通过第一个非内部网络以词法顺序提供。
& y! s. O8 K/ h4 N接下来介绍 Docker 的内置网络驱动程序。
; I; R D) w5 v# u4.1 bridge1 e3 _# [4 k5 L' N& h' E
一个 bridge 网络是 Docker 中最常用的网络类型。桥接网络类似于默认 bridge 网络,但添加一些新功能并删除一些旧的能力。以下示例创建一些桥接网络,并对这些网络上的容器执行一些实验。
1 y+ n4 O7 B& N! u$ Q4 C$ docker network create --driver bridge new_bridge/ z2 j: l+ X- D( T% R0 M' h
创建网络后,可以看到新增加了一个网桥(172.18.0.1)。
! m; c* q# H* ]7 D5 A$ ifconfig
6 i% C) j+ {! G4 J9 |) Xbr-f677ada3003c: flags=4099<UP,BROADCAST,MULTICAST> mtu 15003 B3 Y. Z7 l4 V) w& Z% W
inet 172.18.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
: H6 [% h5 ?, d, o1 f ether 02:42:2f:c1:db:5a txqueuelen 0 (Ethernet)
6 o' t' S0 a9 g% q RX packets 4001976 bytes 526995216 (502.5 MiB)
" y. _, [( m; F6 G9 w2 I RX errors 0 dropped 35 overruns 0 frame 0* R" C& G& h% F2 v9 b+ m2 ]
TX packets 1424063 bytes 186928741 (178.2 MiB)) w. r4 m2 g+ [% q
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
$ |- s5 c! I. b1 ]' \. jdocker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
& @- t) _4 H6 U# l* i% `6 C inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
6 s4 c$ R" f: U9 ~# @6 H inet6 fe80::42:5fff:fe21:c208 prefixlen 64 scopeid 0x20<link>( x# _1 c/ ?2 ^
ether 02:42:5f:21:c2:08 txqueuelen 0 (Ethernet)& D8 _/ z5 X7 z( x3 W
RX packets 12 bytes 2132 (2.0 KiB)$ @' U6 R( f- A* u# B
RX errors 0 dropped 0 overruns 0 frame 0
" d( ~/ L8 p0 i) o# Y TX packets 24 bytes 2633 (2.5 KiB). j. W- z& o: ~& a0 j3 f
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
, h q/ z6 T. o% L% h* ?4.2 Macvlan
+ ~& Y) I0 X; l2 E0 I' o8 ^2 ?, AMacvlan 是一个新的尝试,是真正的网络虚拟化技术的转折点。% i3 K/ }6 M/ U" Z0 x5 r |8 b( R
Linux 实现非常轻量级,因为与传统的 Linux Bridge 隔离相比,它们只是简单地与一个 Linux 以太网接口或子接口相关联,以实现网络之间的分离和与物理网络的连接。4 f1 k2 y5 w& `, P8 S4 x4 [
Macvlan 提供了许多独特的功能,并有充足的空间进一步创新与各种模式。. ]0 N" n8 J: {. w% c, J+ S: q
这些方法的两个高级优点是绕过 Linux 网桥的正面性能以及移动部件少的简单性。
9 C5 l* @6 c, s1 s2 _* V删除传统上驻留在 Docker 主机 NIC 和容器接口之间的网桥留下了一个非常简单的设置,包括容器接口,直接连接到 Docker 主机接口。
6 l# S' X+ P- w, @1 {6 ^: W由于在这些情况下没有端口映射,因此可以轻松访问外部服务。
4 {! p" N9 u3 T; |% [4 j4.2.1 Macvlan Bridge 模式示例用法" b8 E. K, n3 E1 p: @: m( ?
Macvlan Bridge 模式每个容器都有唯一的 MAC 地址,用于跟踪 Docker 主机的 MAC 到端口映射。8 }8 Y# A3 Q3 p$ z
Macvlan 驱动程序网络连接到父 Docker 主机接口。示例是物理接口,
- R, x P( ?% H例如:
0 Q+ ?2 c8 N. b7 y2 Meth0,用于 802.1q VLAN 标记的子接口 eth0.10(.10代表VLAN 10)或甚至绑定的主机适配器,将两个以太网接口捆绑为单个逻辑接口。 指定的网关由网络基础设施提供的主机外部。
; S8 ^% m! E w每个 Macvlan Bridge 模式的 Docker 网络彼此隔离,一次只能有一个网络连接到父节点。+ y9 A3 f) X4 k/ \& ?: l* y7 ?
每个主机适配器有一个理论限制,每个主机适配器可以连接一个 Docker 网络。0 z3 o# _; o8 Y$ \+ x, n
同一子网内的任何容器都可以与没有网关的同一网络中的任何其他容器进行通信 macvlan bridge。: _2 |! I- ?8 V
相同的 docker network 命令适用于 vlan 驱动程序。' v2 d: r2 ^; E$ l: J8 Y3 M+ W8 V, }
在 Macvlan 模式下,在两个网络/子网之间没有外部进程路由的情况下,单独网络上的容器无法互相访问。这也适用于同一码头网络内的多个子网。$ P" m* G% v# |/ B* x1 O' Y( G2 a8 p" V
在以下示例中,eth0在docker主机网络上具有IP地址172.16.86.0/24,默认网关为172.16.86.1,网关地址为外部路由器172.16.86.1。 O% g* \3 y. | U7 z6 ^
# O7 i7 Q8 c: ?) `
注意对于 Macvlan 桥接模式,子网值需要与 Docker 主机的 NIC 的接口相匹配。例如,使用由该 -o parent= 选项指定的 Docker 主机以太网接口的相同子网和网关。* b( ?0 D! ?* ^; {3 }9 y
此示例中使用的父接口位于 eth0 子网上 172.16.86.0/24,这些容器中的容器 docker network 也需要和父级同一个子网 -o parent=。
$ S) U! E' J8 M7 g网关是网络上的外部路由器,不是任何 ip 伪装或任何其他本地代理。
2 B+ R/ W5 o" n L4 O1 \! ^驱动程序用 -d driver_name 选项指定,在这种情况下 -d macvlan。/ g6 o- e! M9 h5 ^+ O3 X2 \
父节点 -o parent=eth0 配置如下:
- O4 g0 v5 a9 V Y' @/ t( z% r$ ip addr show eth0$ P$ ^ t) Q+ c( T3 _% j# f+ ^
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 10008 ]) o i! b9 _. [6 {# e
inet 172.16.86.250/24 brd 172.16.86.255 scope global eth0
! b: J$ {, L/ D [, f创建 macvlan 网络并运行附加的几个容器:
# O' e: M( b: @: H; I# Macvlan (-o macvlan_mode= Defaults to Bridge mode if not specified)
4 G+ g4 h) V4 g0 h' E d/ X1 a3 Edocker network create -d macvlan \ ~) l/ S" ~5 K+ ]- A }# {
--subnet=172.16.86.0/24 \+ _6 p# `1 B/ `2 K! ]2 _4 u5 }0 }
--gateway=172.16.86.1 \- d/ B2 z+ l) w
-o parent=eth0 pub_net1 ^6 E' `. B) c& m4 ^
# Run a container on the new network specifying the --ip address.7 W- B6 U' V+ ]0 I* N- C# X( C
docker run --net=pub_net --ip=172.16.86.10 -itd alpine /bin/sh
" y$ b6 a, a8 M; n% Z2 G# Start a second container and ping the first
- I. ~6 ]7 E8 q; N$ Y/ c9 U4 E# idocker run --net=pub_net -it --rm alpine /bin/sh) M* s, |: U" q
ping -c 4 172.16.86.10
5 P4 b* M7 M* h' e8 O2 @3 q5 b看看容器 ip 和路由表:
0 y9 \8 D# v4 P7 A1 A1 Kip a show eth05 P! e4 }; ` \9 r
eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
O6 w2 c. t* o* l( b) `/ U5 U& R link/ether 46:b2:6b:26:2f:69 brd ff:ff:ff:ff:ff:ff7 c) Y% g" d1 R
inet 172.16.86.2/24 scope global eth0* L! u6 h8 w% m8 o3 Q D4 A9 D
ip route
' Z5 {0 O! j1 L) {, _4 S& V. n* ? default via 172.16.86.1 dev eth03 C S& D d" b( y
172.16.86.0/24 dev eth0 src 172.16.86.2. }. {8 y& Q6 p1 T, Y
# NOTE: the containers can NOT ping the underlying host interfaces as' t2 A7 g5 X m J. L7 C. N
# they are intentionally filtered by Linux for additional isolation.
' G( L7 Z% [% N8 v( C, S# In this case the containers cannot ping the -o parent=172.16.86.250! W5 O& m! n. Y3 j4 Y
4.2.2 Macvlan 802.1q Trunk Bridge 模式示例用法
7 L U* r1 ^; P- r. @: Z: Y- ^2 hVLAN(虚拟局域网)长期以来一直是虚拟化数据中心网络的主要手段,目前仍在几乎所有现有的网络中隔离广播的主要手段。- d: F- y7 b9 h1 ~8 l. j
常用的 VLAN 划分方式是通过端口进行划分,尽管这种划分 VLAN 的方式设置比较很简单,但仅适用于终端设备物理位置比较固定的组网环境。
% t4 }6 y- Y5 y# e; H8 v1 T随着移动办公的普及,终端设备可能不再通过固定端口接入交换机,这就会增加网络管理的工作量。
6 Q! A# F7 H, N比如,一个用户可能本次接入交换机的端口 1,而下一次接入交换机的端口 2,由于端口1和端口 2 属于不同的 VLAN,若用户想要接入原来的 VLAN 中,网管就必须重新对交换机进行配置。
. P2 h5 l( R# p7 h显然,这种划分方式不适合那些需要频繁改变拓扑结构的网络。
* v( M" @1 F% |+ V而 MAC VLAN 可以有效解决这个问题,它根据终端设备的 MAC 地址来划分 VLAN。这样,即使用户改变了接入端口,也仍然处在原 VLAN 中。
. d, I! R* M# ? b% f) mMac vlan 不是以交换机端口来划分 vlan。因此,一个交换机端口可以接受来自多个 mac 地址的数据。一个交换机端口要处理多个 vlan 的数据,则要设置 trunk 模式。
" [2 m; x4 X1 A在主机上同时运行多个虚拟网络的要求是非常常见的。Linux 网络长期以来一直支持 VLAN 标记,也称为标准 802.1q,用于维护网络之间的数据路由隔离。连接到 Docker 主机的以太网链路可以配置为支持 802.1q VLAN ID,方法是创建 Linux 子接口,每个子接口专用于唯一的VLAN ID。
% M6 R2 o z5 A5 ~+ l7 L8 \3 X- \8 Y& X1 \* y2 x
创建 Macvlan 网络(VLAN ID 10)1 F1 a+ \* k0 k# o' d
$ docker network create \8 z h$ E' m4 H- I8 H2 n
--driver macvlan \, n; `) [$ L' W. ~- j7 n8 ^2 K3 |! V
--subnet=10.10.0.0/24 \
" M$ o. ]) o0 K2 Z% B7 H9 z5 ~2 A; [ --gateway=10.10.0.253 \
# V3 }8 ] y8 q" a -o parent=eth0.10 macvlan10( E7 V8 f: y) r: O( T; p' |! P
# 开启一个桥接 Macvlan 的容器
0 y1 { z/ [& O$ docker run --net=macvlan10 -it --name macvlan_test1 --rm alpine /bin/sh
/ y* w7 @) U! P, V# D7 {" o6 k: Z/ X/ # ip addr show
5 ~3 L, E. V5 m' r1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN' J/ K% O- s, v5 F6 S
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002 z# ^$ }1 a U, m! l
inet 127.0.0.1/8 scope host lo
) z; j% P' O8 j) A' Y" W% E* p valid_lft forever preferred_lft forever
0 ?4 f) f3 H' `) d+ p8 R' d1 d+ `/ @5 R8 c21: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN" e0 ]) u7 m3 I2 K% M& @& |
link/ether 02:42:0a:0a:00:01 brd ff:ff:ff:ff:ff:ff
- F, D8 d; f1 r Q inet 10.10.0.1/24 scope global eth0
: }3 h0 H4 C2 Y* E5 E9 X/ f, \ valid_lft forever preferred_lft forever" r: Z: D. z4 [+ v9 n9 O9 p4 Q
# 可以看到分配了一个 10.10.0.1 的地址,然后看一下路由地址。4 v7 s" F X3 |
/ # ip route
/ y5 q8 Q& J v# F7 U/ Sdefault via 10.10.0.253 dev eth0
# J* B! |: V6 E2 K10.10.0.0/24 dev eth0 src 10.10.0.1% @: @: B* P* U' ?1 _! z
然后再开启一个桥接 Macvlan 的容器:4 u/ H$ {6 P6 }% A# a9 T, T
$ docker run --net=macvlan10 -it --name macvlan_test2 --rm alpine /bin/sh: s% h: Y/ P2 e
/ # ip addr show' o9 k+ |8 \3 e" m! x& s: S
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
- F! p, i" l+ \6 R link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
( C! v2 M9 G$ O( } inet 127.0.0.1/8 scope host lo' p8 n3 |, w& ~+ G
valid_lft forever preferred_lft forever
. T( x4 U, r0 }# v1 q. @2 V) u22: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN: g$ O7 M( h" m$ G7 J4 Q
link/ether 02:42:0a:0a:00:02 brd ff:ff:ff:ff:ff:ff
9 J/ A3 G; b5 d: [' T: q4 R inet 10.10.0.2/24 scope global eth0
6 G) G6 ? _9 G+ M' @6 ~0 U valid_lft forever preferred_lft forever
" T+ `0 E4 n: B- z; |4 s# 可以看到分配了一个 10.10.0.2 的地址,然后可以在两个容器之间相互 ping,是可以 ping 通的。
( T4 s% T5 a4 t+ e8 r/ # ping 10.10.0.1
( W& C( h3 P' G+ ~PING 10.10.0.1 (10.10.0.1): 56 data bytes
0 f1 A5 a; b& J1 d" h$ `64 bytes from 10.10.0.1: seq=0 ttl=64 time=0.094 ms' }( |5 x6 J8 q! D- F' B. m# \
64 bytes from 10.10.0.1: seq=1 ttl=64 time=0.057 ms5 s7 y" |) T* ]: J% v. X' M
经过上面两个容器的创建可以看出,容器IP是根据创建网络时的网段从小往大分配的。
' S5 i! W, f3 [6 o7 ?+ C) K当然,在创建容器时,我们也可以使用 –ip 手动执行一个 IP 地址分配给容器,如下操作。" k* b' w1 G/ i2 F% Z5 w) e3 `8 U2 K
$ docker run --net=macvlan10 -it --name macvlan_test3 --ip=10.10.0.189 --rm alpine /bin/sh3 u. _5 D' q# D1 J+ I( R+ Q
/ # ip addr show eth0) z0 h/ O+ v3 O) _% m
24: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
, _& p. l' C3 Z1 H5 ^& s link/ether 02:42:0a:0a:00:bd brd ff:ff:ff:ff:ff:ff
" y- X# g3 s- G/ u8 _1 A& [" c inet 10.10.0.189/24 scope global eth0/ }2 [" m- _3 U. ]
valid_lft forever preferred_lft forever
' h' x+ ~& j" ^VLAN ID 20
& |2 b" @% m* Q" T" h _) n, y接着可以创建由 Docker 主机标记和隔离的第二个 VLAN 网络,该 macvlan_mode 默认是 macvlan_mode=bridge,如下:! ~2 n3 L, K) f$ ^( h
$ docker network create \
8 V' t% b: ?# t! a# E --driver macvlan \
& [: }6 C m) `; S. ` --subnet=192.10.0.0/24 \7 O6 c* ]! |1 c
--gateway=192.10.0.253 \
( P* ^, k2 ^0 x- c -o parent=eth0.20 \3 H4 J, T L: \% {9 s4 Z
-o macvlan_mode=bridge macvlan20" A B$ n# |# [9 T, R7 C! x* s& o* P
当我们创建完 Macvlan 网络之后,在 docker 主机可以看到相关的子接口,如下:1 A" m( f0 G+ o; V8 Z, k3 |
$ ifconfig# @- d! U) H, P- h3 m
eth0.10: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
* t/ M# T% W9 d ether 00:0c:29:16:01:8b txqueuelen 0 (Ethernet)
" ^1 M& z! K0 b( G5 e) k# B( X RX packets 0 bytes 0 (0.0 B)7 N/ g4 G# g) _* Z: f$ x4 p. j
RX errors 0 dropped 0 overruns 0 frame 0: @! b9 {; D$ B7 h% I
TX packets 18 bytes 804 (804.0 B)" c2 [8 C8 ^" m
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 i5 A+ p; x5 X- ]
eth0.20: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500! [' A" h( E8 p9 k4 u5 X- r4 ^
ether 00:0c:29:16:01:8b txqueuelen 0 (Ethernet)
, B( x. a' X6 k RX packets 0 bytes 0 (0.0 B)
2 X5 ?) v2 j9 s s RX errors 0 dropped 0 overruns 0 frame 0
* I5 }- Y: y, v9 ^4 R TX packets 0 bytes 0 (0.0 B)
4 I( B) X* T: }# b TX errors 0 dropped 0 overruns 0 carrier 0 collisions 00 I$ E! u- r4 w9 M- b/ X, ]
# 在 /proc/net/vlan/config 文件中,还可以看见相关的 Vlan 信息,如下:3 a4 w2 e1 q( U2 V
$ cat /proc/net/vlan/config6 H: W, ~' c& e; i- d/ l, E
VLAN Dev name | VLAN ID
2 ^( n; W' x) t2 i c5 F. oName-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD' E6 E$ a6 t7 Z) M
eth0.10 | 10 | eth0. X5 M3 x1 e$ K1 G
eth0.20 | 20 | eth0, ~8 Z& j7 ?1 J1 x5 p. `/ `
|
|