5.云原生之Docker容器网络介绍与实践

转载自:https://www.bilibili.com/read/cv15185166/?from=readlist



例如, 当在一台未经过特殊网络配置的centos 或 ubuntu机器上安装完docker之后, 在宿主机上通过ifconfig命令可以看到多了一块名为docker0的网卡;不难想到docker0就不只是一个简单的网卡设备了而是一个网桥。

$ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
$route -n
172.18.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0

#下图即为Docker默认网络模式(bridge模式)下的网络环境拓扑图,创建了docker0网桥,并以eth pair连接各容器的网络,容器中的数据通过docker0网桥转发到eth0网卡上。
#网桥(等同于交换机)

#在Linux中,可以使用brctl命令查看和管理网桥(需要安装bridge-utils软件包),比如查看本机上的Linux网桥以及其上的端口:
$yum install bridge-utils
$brctl show
bridge name     bridge id               STP enabled     interfaces
br-63791a62ad5a   8000.024258a66d6e       no        veth4ce6e0e
                                                    vethe5abf0f
br-6cde78afe495   8000.02420a2496c6       no      veth93e8dc0
                                                  vethfda7b14
docker0   8000.024216e63d3c       no

#docker0网桥是在Docker daemon启动时自动创建的,其IP默认为172.18.0.1/16,之后创建的Docker容器都会在docker0子网的范围内选取一个未占用的IP使用,并连接到docker0网桥上。

#除了使用docker0网桥外,还可以使用自己创建的网桥,比如创建一个名为br0的网桥,配置IP:
brctl  addbr br0
ifconfig  br0 18.18.0.1

#在Docker容器和外界通信的过程中,还涉及了数据包在多个网卡间的转发(如从docker0网卡转发到宿主机ens160网卡)
#需要内核将ip-forward功能打开即将ip_forward系统参数设1
echo 1 > /proc/sys/net/ipv4/ip_forward 



#随机开启端口
$sudo docker run -d -P centos python app.py

#查看转发的端口
$docker ps -l

#查看详细的日志信息
$sudo docker logs -f [NAMES]

#1.映射所有接口地址 [将本地的5000端口映射到容器的5000端口]
$sudo docker run -d -p 5000:5000 centos python app.py

#2.映射指定地址指定端口
$sudo docker run -d -p 127.0.0.1:5000:5000 centos python app.py

#3.映射指定地址到的任意端口 还能使用tcp标记来指定udp端口
$sudo docker run -d -p 127.0.0.1::5000/tcp centos python app.py

#查看端口映射配置
$sudo docker port [NAMES] 5000 #容器Port

#首先创建一个新的数据库容器,启动db容器的时候并没有使用-p与-P标记,避免了暴露数据库端口到外部网络上
$sudo docker run -d --name db tranining/postgres

#使db容器与web容器建立互联关系;
#--link name:alias  其中namd是链接的容器的名称,alias是这个链接的别名.
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

#使用 docker ps 来查看容器的连接
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STAT
US PORTS NAMES
349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db'
#web/db这表示web 容器链接到 db 容器,web 容器将被允许访问 db 容器的信息。


#使用env命令来查看web容器的环境变量
$sudo docker run --rm --name web1 --link db:db training/webapp env
#起点汇总DB_开头的环境变量是提供web容器连接到db使用,前缀采用大写的链接别名
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5000_TCP=tcp://172.17.0.5:5432
DB_PORT_5000_TCP_PROTO=tcp
DB_PORT_5000_TCP_PORT=5432
DB_PORT_5000_TCP_ADDR=172.17.0.5


#同时Docker还添加host信息到父容器的/etc/hosts文件
$sudo docker run -it --rm --link db:db training/webapp /bin/bash
cat /etc/hosts

实例演示:

$docker pull nginx
$docker run -d -p 80:80 --name web nginx
$docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
16207d8f2291        nginx               "nginx -g 'daemon of…"   19 seconds ago      Up 18 seconds       0.0.0.0:80->80/tcp   web
#容器有自己的内部网络和IP地址,使用docker inspect + 容器ID 可以获取所有的变量值;
$docker inspect web
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"MacAddress": "02:42:ac:12:00:02",

#然后打开80端口进行访问采用logs打印访问
$docker logs -f web
218.x.x.xx - - [08/May/2019:15:00:11 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0" "-"

#进行link关联容器使之能进行正常通信
[root@izwz9biz2m4sd3bb3k38pgz ~]$docker run -d --name web1 --link web:nginx_web alpine cat /etc/hosts
ebe1df8c1fb00462b127d36201d558a9f62507c81faea1ce6c4bf4b5ea6075e3

[root@izwz9biz2m4sd3bb3k38pgz ~]$docker run -d --name web2 --link web:nginx_web alpine env
baa9dfe5f64519eb5ccbd122fc191e0f40118a4ee28385a818f7ffe6e2e03639

[root@izwz9biz2m4sd3bb3k38pgz ~]$docker start -i web1
172.18.0.2      nginx_web 16207d8f2291 web  #成功添加web
172.18.0.3      ebe1df8c1fb0

[root@izwz9biz2m4sd3bb3k38pgz ~]$docker start -i web2
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=baa9dfe5f645
NGINX_WEB_PORT=tcp://172.18.0.2:80  #web2也成功添加web
NGINX_WEB_PORT_80_TCP=tcp://172.18.0.2:80
NGINX_WEB_PORT_80_TCP_ADDR=172.18.0.2
NGINX_WEB_PORT_80_TCP_PORT=80
NGINX_WEB_PORT_80_TCP_PROTO=tcp
NGINX_WEB_NAME=/web2/nginx_web
NGINX_WEB_ENV_NGINX_VERSION=1.15.12-1~stretch
NGINX_WEB_ENV_NJS_VERSION=1.15.12.0.3.1-1~stretch

(3)libnetwork库官方示例
执行流程:

#docker内置的默认默认网卡是是无法使用docker network rm进行删除;
$docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
f26c3ed5f7b0        bridge              bridge              local
fd3572aceb38        host                host                local
01da6464c812        none                null                local

#Step 1.创建名为backend、frontend两个网络
$docker network create backend
6cde78afe495a310bb27c5e1a50074b20e204bfa72e71bcaf6a4c37feb300b93
$docker network create frontend
63791a62ad5a5fe4aeb5926616b2ea1d65b490bb9fb718824b7cb1c408ae50c1
$docker network create -d bridge test-net
#-d:参数指定 Docker 网络类型,有 bridge、overlay/none。

#Step 2. 将c1与c2的容器加入到backend网络中,将c3容器加入到frontend网络中
$docker run -itd --name c1 --net backend alpine
729f2abef71ceaf831999d66264d05f78674d9cd2c235f84481a14b366698adb
$docker run -itd --name c2 --net backend alpine
26d47af2d39a1b00f767c60a68cd5f61f1cf5f48652cdcbcb0216968a3185f5e
$docker run -itd --name c3 --net frontend alpine
9cb94f7c66955ba5a95c90d08ce314da0e477f6eddbcea0329309ec36ca5a711


#Step 3. 分别进入c1和c3容器使用ping命令测试其与c2的连通性,因为c1和c2都在backend网络中,所以两者可以连通。但是因为c3和c2不在一个网络中,所以两个容器之间不能连通:
# 使用docker inspect c3 查看IP信息:
C1: 172.19.0.2
C2: 172.19.0.3
C3: "IPAddress": "172.20.0.2",

# 进入c1容器ping c2通、ping c3不通。其它两个容器就不进入演示了,大家自己可以试一下:
# docker exec -it c1 sh
# ip addr
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
# ping c2
PING c2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.050 ms
/ # ping 172.20.0.2
PING 172.20.0.2 (172.20.0.2): 56 data bytes


# Step 4. 将c2连接加入到front网络中,使用exec进入c2中查看网卡信息,测试c2与c3的连通性后,可以发现两者已经连通
$docker network connect frontend c2
$docker exec -it c2 sh
/ $ ip addr
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:13:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.3/16 brd 172.19.255.255 scope global eth0
24: eth1@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:14:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.20.0.3/16 brd 172.20.255.255 scope global eth1

/ $ ping c3
PING c3 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.100 ms


#新建网络
docker network create -d bridge my-net
#分别启动两个容器(然后进入互ping查看)
$docker run -it --rm --name busybox1 --network my-net busybox sh
$docker run -it --rm --name busybox1 --network my-net busybox sh

#秘诀就是 Docker 利用虚拟文件来挂载容器的 3 个相关配置文件,在容器中使用 mount 命令可以看到挂载信息
#可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 DNS 配置通过 /etc/resolv.conf 文件立刻得到更新。
/dev/mapper/cl-root on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota)
/dev/mapper/cl-root on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota)
/dev/mapper/cl-root on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota)
#Docker 1.2.0 开始支持在运行中的容器里编辑 /etc/hosts, /etc/hostname 和 /etc/resolv.conf 文件。
#但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被 docker commit 提交。

#宿主机更改后立即生效(容器重新进入的时候)
$docker start -i 81fa
sh-4.2# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 192.168.10.254
nameserver 61.128.128.68
nameserver 114.114.114.114

容器访问控制
容器的访问控制,主要通过 Linux 上的 iptables 防火墙来进行管理和实现.
容器访问外部网络,需要本地系统的转发支持。在Linux 系统中,检查转发是否打开。

$sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

#如果在启动 Docker 服务的时候设定 --ip-forward=true, Docker 就会自动设定系统的 ip_forward 参数为 1。
$sysctl -w net.ipv4.ip_forward=1 

$sudo iptables -t nat -nL
#其中,上述规则将所有源地址在 172.17.0.0/16 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。
#MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址。
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16       !172.17.0.0/16


#使用端口映射的时候
chain DOCKER (2 references)
target     prot opt source               destination
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.17.0.2:80

docker 网桥
Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信,它还给出了 MTU(接口允许接收的最大传输单元),通常是 1500 Bytes,或宿主主机网络路由上支持的默认值,值都可以在服务启动的时候进行配置。

#由于目前 Docker 网桥是 Linux 网桥,用户可以使用 brctl show 来查看网桥和端口连接信息。
$brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242f0960e9f       no              veth4fe5b74

#每次创建一个新容器的时候,Docker 从可用的地址段中选择一个空闲的 IP 地址分配给容器的 eth0 端口。使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关。
$ sudo docker run -i -t --rm base /bin/bash
$ip addr show eth0
$ip route
default via 172.17.42.1 dev eth0
172.17.0.0/16 dev eth0  proto kernel  scope link  src 172.17.0.3

#自定义网桥:
#用户也可以指定网桥来连接各个容器,在启动 Docker 服务的时候,使用 -b BRIDGE或--bridge=BRIDGE 来指定使用的网桥。
1. 如果服务已经运行,那需要先停止服务,并删除旧的网桥。
$ sudo systemctl stop docker
$ sudo ip link set dev docker0 down #将网卡处于非工作状态
$ sudo brctl delbr docker0   #删除默认网桥
$ sudo brctl addbr bridge0   #然后创建一个网桥 bridge0。
$ sudo ip addr add 192.168.5.1/24 dev bridge0
$ sudo ip link set dev bridge0 up  #查看确认网桥创建并启动。 

注:brctl 命令在 Debian、Ubuntu /CentOS采用相同的包中可以使用 sudo apt-get install bridge-utils 来安装。

实践案例:
创建一个点到点连接,点到点链路不需要子网和子网掩码。
默认情况下,Docker 会将所有容器连接到由 docker0 提供的虚拟子网中,用户有时候需要两个容器之间可以直连通信,而不用通过主机网桥进行桥接。
解决办法很简单:创建一对 peer 接口,分别放到两个容器中,配置成点到点链路类型即可

$docker run -it --rm --net=none --name demo1 alpine sh
39ca23ce4417

$docker run -it --rm --net=none --name demo2 alpine sh
d4b1311349c1

#找到进程号,然后创建网络命名空间的跟踪文件。
$docker inspect -f '{{.State.Pid}}' d4b
6742
$docker inspect -f '{{.State.Pid}}' 39c
6807

sudo mkdir -p /var/run/netns
sudo ln -s /proc/6742/ns/net /var/run/netns/6742
sudo ln -s /proc/6807/ns/net /var/run/netns/6807

#创建一对A B peer 接口,然后配置路由
$ sudo ip link add A type veth peer name B

$ sudo ip link set A netns 6742
$ sudo ip netns exec 6742 ip addr add 10.1.1.1/32 dev A
$ sudo ip netns exec 6742 ip link set A up
$ sudo ip netns exec 6742 ip route add 10.1.1.2/32 dev A

$ sudo ip link set B netns 6807
$ sudo ip netns exec 6807 ip addr add 10.1.1.2/32 dev B
$ sudo ip netns exec 6807 ip link set B up
$ sudo ip netns exec 6807 ip route add 10.1.1.1/32 dev B

#现在这 2 个容器就可以相互 ping 通,并成功建立连接。点到点链路不需要子网和子网掩码。
容器$ ip addr 
posted @ 2022-06-30 14:43  哈喽哈喽111111  阅读(662)  评论(0编辑  收藏  举报