本章介绍Docker Bridge 网络
网络基础知识回顾IP、子网掩码、网关、DNS、端口号
https://zhuanlan.zhihu.com/p/65226634
Note
面试常问的一个题目, 当你在浏览器中输入一个网址(比如www.baidu.com)并敲回车,这个过程后面都发生了什么
Internet如何工作的
https://www.hp.com/us-en/shop/tech-takes/how-does-the-internet-work
从数据包的角度详细解析
https://www.homenethowto.com/advanced-topics/traffic-example-the-full-picture/
网络常用命令
IP地址的查看
Windows
ipconfig
Linux
ifconfig
或者
ip addr
网络连通性测试
ping命令
PS C:\Users\Peng Xiao> ping 192.168.178.1
Pinging 192.168.178.1 with 32 bytes of data:
Reply from 192.168.178.1: bytes=32 time=2ms TTL=64
Reply from 192.168.178.1: bytes=32 time=3ms TTL=64
Reply from 192.168.178.1: bytes=32 time=3ms TTL=64
Reply from 192.168.178.1: bytes=32 time=3ms TTL=64
Ping statistics for 192.168.178.1:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 2ms, Maximum = 3ms, Average = 2ms
PS C:\Users\Peng Xiao>
telnet命令
测试端口的连通性
➜ ~ telnet www.baidu.com 80
Trying 104.193.88.123...
Connected to www.wshifen.com.
Escape character is '^]'.
HTTP/1.1 400 Bad Request
Connection closed by foreign host.
➜ ~
traceroute
路径探测跟踪
Linux下使用 tracepath
➜ ~ tracepath www.baidu.com
1?: [LOCALHOST] pmtu 1500
1: DESKTOP-FQ0EO8J 0.430ms
1: DESKTOP-FQ0EO8J 0.188ms
2: 192.168.178.1 3.371ms
3: no reply
4: gv-rc0052-cr102-et91-251.core.as33915.net 13.970ms
5: asd-tr0021-cr101-be156-10.core.as9143.net 19.190ms
6: nl-ams04a-ri3-ae51-0.core.as9143.net 213.589ms
7: 63.218.65.33 16.887ms
8: HundredGE0-6-0-0.br04.sjo01.pccwbtn.net 176.099ms asymm 10
9: HundredGE0-6-0-0.br04.sjo01.pccwbtn.net 173.399ms asymm 10
10: 63-219-23-98.static.pccwglobal.net 177.337ms asymm 11
11: 104.193.88.13 178.197ms asymm 12
12: no reply
13: no reply
14: no reply
15: no reply
16: no reply
17: no reply
18: no reply
19: no reply
20: no reply
21: no reply
22: no reply
23: no reply
24: no reply
25: no reply
26: no reply
27: no reply
28: no reply
29: no reply
30: no reply
Too many hops: pmtu 1500
Resume: pmtu 1500
➜ ~
Windows下使用 TRACERT.EXE
PS C:\Users\Peng Xiao> TRACERT.EXE www.baidu.com
Tracing route to www.wshifen.com [104.193.88.123]
over a maximum of 30 hops:
1 4 ms 3 ms 3 ms 192.168.178.1
2 * * * Request timed out.
3 21 ms 18 ms 19 ms gv-rc0052-cr102-et91-251.core.as33915.net [213.51.197.37]
4 14 ms 13 ms 12 ms asd-tr0021-cr101-be156-10.core.as9143.net [213.51.158.2]
5 23 ms 19 ms 14 ms nl-ams04a-ri3-ae51-0.core.as9143.net [213.51.64.194]
6 15 ms 14 ms 13 ms 63.218.65.33
7 172 ms 169 ms 167 ms HundredGE0-6-0-0.br04.sjo01.pccwbtn.net [63.223.60.58]
8 167 ms 168 ms 168 ms HundredGE0-6-0-0.br04.sjo01.pccwbtn.net [63.223.60.58]
9 168 ms 173 ms 167 ms 63-219-23-98.static.pccwglobal.net [63.219.23.98]
10 172 ms 170 ms 171 ms
curl命令
请求web服务的
http://www.ruanyifeng.com/blog/2019/09/curl-reference.html
Docker Bridge 网络
Warning
注意,本节实验需要在Linux环境下进行
Image Removed
创建两个容器
$ docker container run -d --rm --name box1 busybox /bin/sh -c "while true; do sleep 3600; done"
$ docker container run -d --rm --name box2 busybox /bin/sh -c "while true; do sleep 3600; done"
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4f3303c84e53 busybox "/bin/sh -c 'while t…" 49 minutes ago Up 49 minutes box2
03494b034694 busybox "/bin/sh -c 'while t…" 49 minutes ago Up 49 minutes box1
容器间通信
两个容器都连接到了一个叫 docker0 的Linux bridge上
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
1847e179a316 bridge bridge local
a647a4ad0b4f host host local
fbd81b56c009 none null local
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "1847e179a316ee5219c951c2c21cf2c787d431d1ffb3ef621b8f0d1edd197b24",
Image Added
创建两个容器
代码块 |
---|
$ docker container run -d --rm --name box1 busybox /bin/sh -c "while true; do sleep 3600; done"
$ docker container run -d --rm --name box2 busybox /bin/sh -c "while true; do sleep 3600; done"
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4f3303c84e53 busybox "/bin/sh -c 'while t…" 49 minutes ago Up 49 minutes box2
03494b034694 busybox "/bin/sh -c 'while t…" 49 minutes ago Up 49 minutes box1 |
容器间通信
两个容器都连接到了一个叫 docker0 的Linux bridge上。
展开 |
---|
代码块 |
---|
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
1847e179a316 bridge bridge local
a647a4ad0b4f host host local
fbd81b56c009 none null local
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "1847e179a316ee5219c951c2c21cf2c787d431d1ffb3ef621b8f0d1edd197b24",
"Created": "2021-07-01T15:28:09.265408946Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
" |
|
Created2021-07-01T15:28:09.265408946Z"Scope":"local",
Driverbridge", "EnableIPv6": false,
"IPAM":{"Driver":"default",
Optionsnull Config[false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
|
|
"Subnet": "172.17.0.0/16" Gateway"172.17.0.1"{
"03494b034694982fa085cc4052b6c7b8b9c046f9d5f85f30e3a9e716fad20741": {
|
|
}
]},
Internalfalse"072160448becebb7c9c333dce9bbdf7601a92b1d3e7a5820b8b35976cf4fd6ff",
|
|
"Attachable":false,
Ingressfalse "ConfigFrom":{
NetworkIPv4Address": "172.17.0.2/16",
|
|
},
ConfigOnlyfalse,
"Containers": {03494b034694982fa085cc4052b6c7b8b9c046f9d5f85f30e3a9e716fad207414f3303c84e5391ea37db664fd08683b01decdadae636aaa1bfd7bb9669cbd8de": {
"Name": " |
|
box1072160448becebb7c9c333dce9bbdf7601a92b1d3e7a5820b8b35976cf4fd6ff4cf0f635d4273066acd3075ec775e6fa405034f94b88c1bcacdaae847612f2c5",
"MacAddress": "02:42:ac:11:00: |
|
0203",
"IPv4Address": "172.17.0. |
|
23/16",
"IPv6Address": ""
} |
|
,4f3303c84e5391ea37db664fd08683b01decdadae636aaa1bfd7bb9669cbd8de "Name"com.docker.network.bridge.default_bridge": " |
|
box2 "EndpointID"com.docker.network.bridge.enable_icc": " |
|
4cf0f635d4273066acd3075ec775e6fa405034f94b88c1bcacdaae847612f2c5 "MacAddress"com.docker.network.bridge.enable_ip_masquerade": " |
|
02:42:ac:11:00:03 "IPv4Address"com.docker.network.bridge.host_binding_ipv4": " |
|
172173/16 "IPv6Address" "com.docker.network.bridge.name": "docker0",
|
|
}"com.docker.network.driver.mtu": "1500"
},
" |
|
OptionsLabels": {}
}
]
$ brctl show
bridge name |
|
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
enabled interfaces
docker0 8000.0242759468cf |
|
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0","com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu":"1500"
},
"Labels":{}
}
]Note 提示 |
---|
brctl 使用前需要安装, 对于CentOS, 可以通过 sudo yum install -y bridge-utils 安装. 对于Ubuntu, 可以通过 sudo apt
|
-get install -y bridge-utils-get install -y bridge-utils 。
|
容器对外通信
查看路由:
代码块 |
---|
$ ip route
default via 10.0.2.2 dev eth0 proto dhcp metric 100
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.200.0/24 dev eth1 proto kernel scope link src 192.168.200.10 metric 101 |
iptables转发规则:
代码块 |
---|
$ sudo iptables --list -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source |
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242759468cf no veth8c9bb82
destination
DOCKER all -- anywhere |
vethd8f9afb容器对外通信
查看路由
$iproute
defaultvia 10.0.2.2 dev eth0 proto dhcp metric 100
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.200.0/24 dev eth1 proto kernel scope link src 192.168.200.10 metric 101iptable [1] 转发规则
$ sudo iptables --list -t nat
Chain PREROUTING anywhere ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- |
anywhereanywhere ADDRTYPE match dst-type LOCAL
Chain |
INPUTPOSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 anywhere
Chain |
OUTPUTpolicyACCEPTreferences)
target prot opt source destination
|
DOCKERRETURN all -- anywhere anywhere |
端口转发
创建容器:
!loopback/8container run -d --rm --name web -p 8080:80 nginx
$ docker container |
ADDRTYPEmatch dsttype LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 anywhere
Chain DOCKER (2 references)
target prot opt source format '{{.NetworkSettings.IPAddress}}' web
$ docker container run -d --rm --name client busybox /bin/sh -c "while true; do sleep 3600; done"
$ docker container inspect --format '{{.NetworkSettings.IPAddress}}' client
$ docker container exec -it client wget http://172.17.0.2 |
查看iptables的端口转发规则:
代码块 |
---|
[vagrant@docker-host1 ~]$ sudo iptables -t nat -nvxL
Chain PREROUTING (policy ACCEPT 10 packets, 1961 bytes)
pkts bytes target prot opt |
destination
RETURNall--anywhereanywhere端口转发
创建容器
$dockercontainerrun-d--rm--nameweb-p8080:80nginx
$dockercontainerinspect--format '{{.NetworkSettings.IPAddress}}' web
$ docker container run -d --rmnameclientbusybox/bin/sh-c"whiletrue;dosleep3600;done"
$dockercontainerinspect--format '{{.NetworkSettings.IPAddress}}' client
$ docker container exec -it client wget http://172.17.0.2查看iptables的端口转发规则
[vagrant@docker-host1 ~]$ sudo iptables -t nat -nvxL
Chain PREROUTING 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT |
1019611901 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT |
152DOCKERall--**0.0.0.0/0 0.0.0.0/0ADDRTYPEmatchdst-typeLOCAL
Chain INPUT (policy ACCEPT 9 packets, 1901 bytes)
pkts destination
0 0 DOCKER all -- * |
bytestargetprotoptin outsource !127.0.0.0/8 ADDRTYPE match dst-type |
destinationOUTPUTPOSTROUTING (policy ACCEPT |
2120232 bytes)
pkts bytes target prot opt in out source destination
|
0 0 DOCKER * 0.00 !1278 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 4 packets, 232 bytes)
pktsbytestargetprotoptinoutsourcedestination
3202MASQUERADEall-- * tcp dpt:80
Chain DOCKER (2 references)
|
!docker0172.17.0.0/16 0.0.0.0/0
00MASQUERADEtcp--** 172.17.0.2172.17..2 tcp dpt:80
Chain DOCKER (2 references)
pktsbytestargetprot opt inoutsourcedestination
0RETURNall--docker0* 0.0.0.0/0 0.0.0.0/0
1 52 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80dpt:8080 to:172.17.0.2:80 |
创建和使用 bridge
提示 |
---|
参考docker network --help。 |
创建bridge:
代码块 |
---|
docker network create -d bridge mybridge
docker network ls
docker network inspect mybridge |
新创建的bridge有自己的网关和掩码。
使用bridge创建容器:
代码块 |
---|
docker container run -d --rm --name box0 busybox /bin/sh -c "while true; do sleep 3600; done"
docker container run -d --rm --name box1 --network mybridge busybox /bin/sh -c "while true; do sleep 3600; done" |
新创建的容器将使用mybridge的网段和ip地址。
也可以将容器连接到另一个bridge上:
代码块 |
---|
docker network connect mybridge box0
docker container inspect box0 |
进到box0的终端,查看ip地址,可以看到多了一个mybridge网段的网络接口。
也可以断开一个bridge:
代码块 |
---|
docker network disconnect mybridge box0 |
提示 |
---|
默认的bridge不带主机名解析功能,不可以ping主机名,也就是--name指定的容器名称。但新创建的bridge带有主机名解析功能,可以直接ping主机名。 |
提示 |
---|
创建bridge时可以指定网关和掩码,参考docker network create --help,示例如下: 代码块 |
---|
docker network create -d bridge --gateway 172.200.0.1 --subnet 172.0.0.0/16 mybridge |
|
Docker Host 网络
如果使用这个网络来创建容器,则容器和宿主机共享相同的网络环境,这可以省略NAT的步骤,但是因为所有Host网络的容器和宿主机都是是共享相同的网络环境,容易引起资源冲突,比如一个容器占用了80端口,那么另一个容器就不能再使用相同的端口了。
创建和使用 bridge
Docker Host 网络网络命名空间
Linux的Namespace(命名空间)技术是一种隔离技术,常用的Namespace有 user namespace, process namespace, network
namespace等namespace等。
在Docker容器中,不同的容器通过Network namespace进行了隔离,也就是不同的容器有各自的IP地址,路由表等,互不影响。
Note
准备一台Linux机器,这一节会用到一个叫 brtcl
的命令,这个命令需要安装,如果是Ubuntu的系统,可以通过 apt-get install bridge-utils
安装;如果是Centos系统,可以通过 sudo yum install bridge-utils
来安装
Image Removed
创建bridgeImage Added
创建bridge
代码块 |
---|
[vagrant@docker-host1 ~]$ sudo brctl addbr mydocker0
[vagrant@docker-host1 ~]$ brctl show
bridge name bridge id STP enabled interfaces
mydocker0 8000.000000000000 no
[vagrant@docker-host1 ~]$ |
准备一个shell脚本
脚本名字叫 |
#!/bin/bash
bridge=$1
namespace=$2
addr=$3
vethA=veth-$namespace
vethB=eth00
sudo ip netns add $namespace
sudo ip link add $vethA type veth peer name $vethB
sudo ip link set $vethB netns $namespace
sudo ip netns exec $namespace ip addr add $addr dev $vethB
sudo ip netns exec $namespace ip link set $vethB up
sudo ip link set $vethA up
sudo brctl addif $bridge $vethA |
脚本执行
代码块 |
---|
[vagrant@docker-host1 ~]$ sh add-ns-to-br.sh mydocker0 ns1 172.16.1.1/16
[vagrant@docker-host1 ~]$ sh add-ns-to-br.sh mydocker0 ns2 172.16.1.2/16 |
把mydocker0这个bridge
up起来up起来:
代码块 |
---|
[vagrant@docker-host1 ~]$ sudo ip link set dev mydocker0 up |
验证
代码块 |
---|
[vagrant@docker-host1 ~]$ sudo ip netns exec ns1 bash
[root@docker-host1 vagrant]# ip a
1: lo: |
<LOOPBACK><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: eth00@if6: |
<BROADCAST<BROADCAST,MULTICAST,UP,LOWER_ |
UP>UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether f2:59:19:34:73:70 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.1.1/16 scope global eth00
valid_lft forever preferred_lft forever
inet6 fe80::f059:19ff:fe34:7370/64 scope link
valid_lft forever preferred_lft forever
[root@docker-host1 vagrant]# ping 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=0.029 ms
64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=0.080 ms
^C
--- 172.16.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.029/0.054/0.080/0.026 ms
[root@docker-host1 vagrant]# |
对外通信
https://www.karlrupp.net/en/computer/nat_tutorial
锚 |
---|
| flask_redis_demo |
---|
| flask_redis_demo |
---|
|
Python Flask + Redis 练习
程序准备
准备一个Python文件,名字为 app.py
内容如下:
代码块 |
---|
from flask import Flask
from redis import Redis
import os
import socket
app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)
|
@app@app.route('/')
def hello():
redis.incr('hits')
return f"Hello Container World! I have been seen {redis.get('hits').decode('utf-8')} times and my hostname is {socket.gethostname()}.\n" |
准备一个Dockerfile:
准备一个Dockerfile 代码块 |
---|
FROM python:3.9.5-slim
RUN pip install flask redis && \
groupadd -r flask && useradd -r -g flask flask && \
mkdir /src && \
chown -R flask:flask /src
USER flask
COPY app.py /src/app.py
WORKDIR /src
ENV FLASK_APP=app.py REDIS_HOST=redis
EXPOSE 5000
CMD ["flask", "run", "-h", "0.0.0.0"] |
镜像准备
构建flask镜像,准备一个redis镜像。
代码块 |
---|
$ docker image pull redis
$ docker image build -t flask-demo .
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
flask-demo latest 4778411a24c5 About a minute ago 126MB
python 3.9.5-slim c71955050276 8 days ago 115MB
redis latest 08502081bff6 2 weeks ago 105MB |
创建一个docker bridge
代码块 |
---|
$ docker network create -d bridge demo-network
8005f4348c44ffe3cdcbbda165beea2b0cb520179d3745b24e8f9e05a3e6456d
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
2a464c0b8ec7 bridge bridge local
8005f4348c44 demo-network bridge local
80b63f711a37 host host local
fae746a75be1 none null local
$ |
创建redis container
创建一个叫 redis-server
的container,连到 demo-
network上network上:
代码块 |
---|
$ docker container run -d --name redis-server --network demo-network redis
002800c265020310231d689e6fd35bc084a0fa015e8b0a3174aa2c5e29824c0e
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
002800c26502 redis "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 6379/tcp redis-server
$ |
创建flask container
代码块 |
---|
$ docker container run -d --network demo-network --name flask-demo --env REDIS_HOST=redis-server -p 5000:5000 flask-demo |
打开浏览器访问 http://127.0.0.1:5000,应该能看到类似下面的内容,每次刷新页面,计数加1:
应该能看到类似下面的内容,每次刷新页面,计数加1 代码块 |
---|
Hello Container World! I have been seen 36 times and my hostname is 925ecb8d111a. |
总结
如果把上面的步骤合并到一起,成为一个部署脚本如果把上面的步骤合并到一起,成为一个部署脚本:
代码块 |
---|
# prepare image
docker image pull redis
docker image build -t flask-demo .
# create network
docker network create -d bridge demo-network
# create container
docker container run -d --name redis-server --network demo-network redis
docker container run -d --network demo-network --name flask-demo --env REDIS_HOST=redis-server -p 5000:5000 flask-demo |