自学内容网 自学内容网

Docker 网络

1. Docker 网络启动过程

Docker 服务启动时会首先在主机上自动创建一个 docker0 的虚拟网桥,网桥可以理解为一个软件交换机,负责挂载其上的接口之间进行包转发。

Docker 随机分配一个本地未占用的私有网段中的一个地址给 docker0 接口,默认是 172.17.0.0/16 网段。此后启动的容器会自动分配一个该网段的地址。

当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 互联接口。当向任一个接口发送包时,另外一个接口自动收到相同的包。互联接口的一端位于容器内,即 eth0;另一端在本地并被挂载到 docker0 网桥,名称以veth 开头(例如 vethae12ch)。通过这种方式,主机可以与容器通信,容器之间也可以相互通信。如此一来,Docker 就创建了在主机和所有容器之间一个虚拟共享网络。

# 查看网桥
# brctl 命令默认没有安装,安装命令: yum -y install bridge-utils 
[root@docker ~]# brctl show
bridge name        bridge id                STP enabled        interfaces
docker0                8000.02425d66988c        no                vethab7e230
                                                        vethae685cc
# 查看 docker0 接口
[root@docker ~]# ifconfig docker0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:5dff:fe66:988c  prefixlen 64  scopeid 0x20<link>
        ether 02:42:5d:66:98:8c  txqueuelen 0  (Ethernet)
        RX packets 2478196  bytes 478260325 (456.1 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2530684  bytes 573536572 (546.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

# 容器内接口 eth0@if211,宿主机上对应 ID 为 211 的接口。
[root@c961d4e7a9ca app]# 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
210: eth0@if211: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

[root@docker ~]# ip a 
211: vethae685cc@if210: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 3e:e7:73:e9:d9:13 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::3ce7:73ff:fee9:d913/64 scope link 
       valid_lft forever preferred_lft forever

2. Docker 网络的连通性

容器访问外部

容器默认指定了网关为docker0网桥上的docker0内部接口。docker0内部接口同时也是宿主机的一个本地接口。因此,容器默认情况下可以访问到宿主机本地网络。如果容器要想通过宿主机访问到外部网络,则需要宿主机进行辅助转发。

  • 开启内核包转发功能

在宿主机Linux系统中,检查转发是否打开,代码如下:

[root@docker ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

如果为0,说明没有开启转发,则需要手动打开:

[root@docker ~]# sysctl  -w net.ipv4.ip_forward=1
  • 配置 SNAT 规则

假设容器内部的网络地址为172.17.0.2,本地网络地址为10.0.2.2。容器要能访问外部网络,源地址不能为172.17.0.2,需要进行源地址映射(Source NAT,SNAT),修改为本地系统的IP地址10.0.2.2。

映射是通过iptables的源地址伪装操作实现的。查看主机nat表上 POSTROUTING 链的规则。该链负责数据包要离开主机前,改写其源地址:

[root@docker ~]# iptables -tnat  -nvL POSTROUTING
Chain POSTROUTING (policy ACCEPT 7920 packets, 499K bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0   

其中,上述规则将所有源地址在172.17.0.0/16网段,且不是从docker0接口发出的流量(即从容器中出来的流量),动态伪装为从系统网卡发出。

外部访问容器

容器允许外部访问,可以在docker [container] run 时候通过 -p 或 -P 参数来启用。

不管用哪种办法,其实也是在本地的 iptable 的 nat 表中添加相应的规则,将访问外部IP地址的包进行目标地址DNAT,将目标地址修改为容器的 IP 地址。

以一个开放 80 端口的 Nginx 容器为例,使用 -P 时,会自动映射本地 32768~65535 范围内的随机端口到容器的 80 端口:

# 指定 -P 参数运行一个 nginx 容器
[root@docker ~]# docker run --rm -P nginx:1.22.1
[root@docker ~]# iptables -t nat -nvL
Chain PREROUTING (policy ACCEPT 18 packets, 528 bytes)
 pkts bytes target     prot opt in     out     source               destination         
 8253  232K DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT 18 packets, 528 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 10 packets, 600 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DOCKER     all  --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT 10 packets, 600 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.4           172.17.0.4           tcp dpt:80

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.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.4:80

使用-p 80:80 时,与上面类似,只是本地端口也为 80:

[root@docker ~]# docker run --rm -p 80:80 nginx:1.22.1

[root@docker ~]# netstat -nltp 
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22122           0.0.0.0:*               LISTEN      1201/sshd           
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      28775/docker-proxy  
tcp6       0      0 :::80                   :::*                    LISTEN      28781/docker-proxy  
[root@docker ~]# 
[root@docker ~]# iptables -t nat -nvL
......
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.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:80 to:172.17.0.4:80

容器间互相访问

容器默认都连接在 Docker0 网桥上,都可以互相访问,相当于连在一台二层交换机上。

但是容器的 IP 地址是动态变化的,如果两个应用之间要互相访问就无法通过 IP 地址互相通信,Docker 提供两种方案来解决。

  • --link 传统方式,(目前 Docker 官网已不建议使用 )

我们部署一个 Wordpress 应用

下载镜像

[root@docker ~]# registry.cn-beijing.aliyuncs.com/xxhf/wordpress:php7.4
[root@docker ~]# docker tag registry.cn-beijing.aliyuncs.com/xxhf/wordpress:php7.4 wordpress:php7.4

[root@docker ~]# registry.cn-beijing.aliyuncs.com/xxhf/mysql:5.7
[root@docker ~]# docker tag registry.cn-beijing.aliyuncs.com/xxhf/mysql:5.7 mysql:5.7

[root@docker ~]# docker pull registry.cn-beijing.aliyuncs.com/xxhf/mysql-client
[root@docker ~]# docker tag registry.cn-beijing.aliyuncs.com/xxhf/mysql-client mysql-client

运行 mysql 容器

[root@docker ~]# docker run -d --name db_wordpress --restart always \
-e MYSQL_ROOT_PASSWORD=wordpress \
-e MYSQL_DATABASE=db_wordpress \
-p 3306:3306 mysql:5.7
[root@docker ~]# docker ps 
CONTAINER ID   IMAGE       COMMAND                  CREATED              STATUS              PORTS                                                  NAMES
ded235356e6f   mysql:5.7   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   db_wordpress

验证数据库正常运行

[root@docker ~]# docker run -d --link db_wordpress -it mysql-client -uroot -pwordpress -hdb_wordpress

[root@docker ~]# docker attach 3c5e8d2f9198
MySQL [(none)]> 
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db_wordpress       |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.001 sec)

MySQL [(none)]> use db_wordpress
Database changed
MySQL [db_wordpress]> show tables; 
Empty set (0.000 sec)

运行 WordPress 容器

[root@docker ~]# docker run -d --name wordpress --restart always --link db_wordpress \
-e WORDPRESS_DB_HOST=db_wordpress:3306 \
-e WORDPRESS_DB_USER=root \
-e WORDPRESS_DB_PASSWORD=wordpress \
-e WORDPRESS_DB_NAME=db_wordpress \
-p  80:80  wordpress:php7.4

如果安装过程有问题可以查看 wordpress 日志

[root@docker ~]# docker logs -f wordpress
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
No 'wp-config.php' found in /var/www/html, but 'WORDPRESS_...' variables supplied; copying 'wp-config-docker.php' (WORDPRESS_DB_HOST WORDPRESS_DB_NAME WORDPRESS_DB_PASSWORD WORDPRESS_DB_USER)
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
[Sun Oct 22 07:32:35.093635 2023] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.54 (Debian) PHP/7.4.33 configured -- resuming normal operations
[Sun Oct 22 07:32:35.093713 2023] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
120.245.113.53 - - [22/Oct/2023:07:36:00 +0000] "GET / HTTP/1.1" 302 404 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
120.245.113.53 - - [22/Oct/2023:07:36:00 +0000] "GET / HTTP/1.1" 302 404 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
120.245.113.53 - - [22/Oct/2023:07:36:00 +0000] "GET /wp-admin/install.php HTTP/1.1" 200 4667 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"

–link 是通过在本地解析文件 /etc/hosts 添加主机记录来实现DNS域名解析的。

[root@docker ~]# docker exec -it wordpress 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        db_wordpress 18722c02cb87
172.17.0.3        9aeb4e1dfe63
  • 自定义网络

创建一个用户定义网络

[root@docker ~]# docker network create wp-net 
e4e3d1ee70620fec8e4a5a22f9b0db737faaf5f6e8d67a2958f98f0fc86af975
[root@docker ~]# 
[root@docker ~]# docker network  ls 
NETWORK ID     NAME      DRIVER    SCOPE
0cc88dcd390d   bridge    bridge    local
385ab96da9ee   host      host      local
43634758fc70   none      null      local
e4e3d1ee7062   wp-net    bridge    local

重新运行 MySQL 和 WordPress 容器

docker run -d --name db_wordpress --restart always \
--network wp-net \
-e MYSQL_ROOT_PASSWORD=wordpress \
-e MYSQL_DATABASE=db_wordpress \
-p 3306:3306 mysql:5.7

docker run -d --name wordpress --restart always --network wp-net \
-e WORDPRESS_DB_HOST=db_wordpress:3306 \
-e WORDPRESS_DB_USER=root \
-e WORDPRESS_DB_PASSWORD=wordpress \
-e WORDPRESS_DB_NAME=db_wordpress \
-p  80:80  wordpress:php7.4

自定义网桥可以自动实现容器间的DNS解析,没有修改 /etc/hosts 文件

[root@docker ~]# docker exec  wordpress 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.18.0.3        24e49d0f2225

3. Docker 网络模式

host 模式

Docker使用了Linux的 Namespaces 技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。

例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。

当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

示例:

# 运行一个使用 host 网络模式的容器
[root@docker ~]# docker run -d --net=host nginx:1.22.1 

# 容器详情中没有 IP 地址 
[root@docker ~]# docker inspect 2fa38ed2c2f3 | grep -i ipaddr
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "",

# 宿主机可以看到 80 端口的监听 
[root@docker ~]# netstat -nltp | grep nginx 
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      4561/nginx: master  
tcp6       0      0 :::80                   :::*                    LISTEN      4561/nginx: master  

# 宿主机可以访问 nginx 服务 
[root@docker ~]# curl 127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>

bridge 模式

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口)。

Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

# docker0 网桥 
[root@docker ~]# brctl show 
bridge name        bridge id                STP enabled        interfaces
docker0                8000.024276df2867        no                veth15882fe

# docker0 接口
[root@docker ~]# ifconfig 
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
        inet6 fe80::42:76ff:fedf:2867  prefixlen 64  scopeid 0x20<link>
        ether 02:42:76:df:28:67  txqueuelen 0  (Ethernet)
        RX packets 11460  bytes 752403 (734.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 11923  bytes 90364552 (86.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

--network bridge:设置容器工作在bridge模式下,即将容器接口添加至docker0桥。

配置 docker0 网络配置:

编辑 /etc/docker/daemon.json ,添加以下内容:

{
  "bip": "192.168.1.1/24",
  "fixed-cidr": "192.168.1.0/25",
  "mtu": 1500,
  "default-gateway": "192.168.1.254",
  "dns": ["10.20.1.2","10.20.1.3"]
}

重启 docker

systemctl restart docker 

观察 docker0 ip 地址

ot@docker ~]# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.1.1  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::42:76ff:fedf:2867  prefixlen 64  scopeid 0x20<link>
        ether 02:42:76:df:28:67  txqueuelen 0  (Ethernet)
        RX packets 11460  bytes 752403 (734.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 11923  bytes 90364552 (86.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

none 模式

此模式下容器不参与网络通信,运行于此类容器中的进程仅能访问本地环回接口,仅适用于进程无须网络通信的场景中,例如备份,进程诊断及各种离线任务等。

--network=none:设置模式容器工作在none模式下。

示例:

# 使用 none 网络模式,容器内只有 loopback 接口
[root@docker ~]# docker run  -it --rm --network=none busybox 
/ # ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
/ # 

4. 网络 Namespace

Docker 是借助 Linux 内核技术 Namespace 来实现隔离的, Linux Namespaces 机制提供一种资源隔离方案。 PID,IPC,Network 等系统资源不再是全局性的, 而是属于某个特定的 Namespace。 每个 namespace 下的资源对于其他 namespace 下的资源都是透明, 不可见的。 因此在操作系统层面上看, 就会出现多个相同 pid 的进程。 系统中可以同时存在多个进程号为 0,1,2 的进程, 由于属于不同的 namespace, 所以它们之间并不冲突。 而在用户层面上只能看到属于用户自己 namespace 下的资源, 例如使用 ps 命令只能列出自己 namespace 下的进程。这样每个 namespace 看上去就像一个单独的 Linux 系统。

目前内核支持的Namespace类型:

Namespace 常用操作

  • 查看当前系统下的 namespace
[root@docker ~]# lsns 
        NS TYPE  NPROCS   PID USER   COMMAND
4026531836 pid      138     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531837 user     138     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531838 uts      138     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531839 ipc      138     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531840 mnt      136     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531856 mnt        1    48 root   kdevtmpfs
4026531956 net      138     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026532210 mnt        1   642 chrony /usr/sbin/chronyd
  • 查看某个进程的 namespace
[root@docker ~]# ls -la /proc/1/ns
total 0
dr-x--x--x 2 root root 0 Sep 14 09:20 .
dr-xr-xr-x 9 root root 0 Sep 14 09:20 ..
lrwxrwxrwx 1 root root 0 Oct 11 23:26 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Oct 11 23:26 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Sep 14 09:20 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Oct 11 23:26 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Oct 11 23:26 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Oct 11 23:26 uts -> uts:[4026531838]
  • 进入某个 namespace 执行命令
# 进入系统主进程下执行 ip addr 命令,查看到的结果跟在系统bash下 执行 ip addr 结果一样
[root@docker ~]# nsenter -t 1 -n 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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:16:3e:23:03:2f brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.93/24 brd 172.17.0.255 scope global dynamic eth0
       valid_lft 312935054sec preferred_lft 312935054sec
    inet6 fe80::216:3eff:fe23:32f/64 scope link 
       valid_lft forever preferred_lft forever
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:76:df:28:67 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:76ff:fedf:2867/64 scope link 
       valid_lft forever preferred_lft forever
  • 运行一个容器,在容器的 namespace 下执行命令
# 运行一个容器
[root@docker ~]# docker run -d nginx:1.22.1 
fba13bee996471204348f9545bc3b8f7ea790fcfe3a1d4cbecf9e37c2d69e0e3
[root@docker ~]# docker ps 
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS     NAMES
fba13bee9964   nginx:1.22.1   "/docker-entrypoint.…"   5 seconds ago   Up 4 seconds   80/tcp    hardcore_pare
# 查看容器的进程号
[root@docker ~]# docker inspect  fba13bee9964 | grep -i pid 
            "Pid": 18810,
            "PidMode": "",
            "PidsLimit": null,

# 在容器的 namespace 中执行 命令
[root@docker ~]# nsenter  -t 18810 -n 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
157: eth0@if158: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.2/24 brd 192.168.1.255 scope global eth0
       valid_lft forever preferred_lft forever

# 查询容器的 ip 地址 
[root@docker ~]# docker inspect  fba13bee9964 | grep -i ipaddr
            "SecondaryIPAddresses": null,
            "IPAddress": "192.168.1.2",
                    "IPAddress": "192.168.1.2",
  • 每个容器都在单独的 Namespace 里
[root@docker ~]# docker run --name c1 -d nginx:1.22.1 
cc9a70d5e234095f117723e8e4ff2017b4933cf23155dbef0ae6f60c93f55b51
[root@docker ~]# 
[root@docker ~]# docker run --name c2 -d nginx:1.22.1 
42cad1a6f4872e507ff0062d5c922eb0e49eec09be6ef2782ca38249a265dd46
[root@docker ~]# 
[root@docker ~]# docker inspect c1 | grep -i pid 
            "Pid": 10157,
            "PidMode": "",
            "PidsLimit": null,
[root@docker ~]# 
[root@docker ~]# docker inspect  c2 | grep -i pid 
            "Pid": 10253,
            "PidMode": "",
            "PidsLimit": null,
[root@docker ~]# 
[root@docker ~]# ll /proc/10157/ns 
total 0
lrwxrwxrwx 1 root root 0 Oct 22 17:09 ipc -> ipc:[4026532170]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 mnt -> mnt:[4026532168]
lrwxrwxrwx 1 root root 0 Oct 22 17:08 net -> net:[4026532173]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 pid -> pid:[4026532171]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 uts -> uts:[4026532169]
[root@docker ~]# 
[root@docker ~]# ll /proc/10253/ns
total 0
lrwxrwxrwx 1 root root 0 Oct 22 17:09 ipc -> ipc:[4026532233]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 mnt -> mnt:[4026532231]
lrwxrwxrwx 1 root root 0 Oct 22 17:08 net -> net:[4026532236]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 pid -> pid:[4026532234]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Oct 22 17:09 uts -> uts:[4026532232]

Docker网络 底层实现

启动一个运行在 none 模式的容器

[root@docker ~]# docker run --network=none -d nginx:1.22.1
2350cedb32389a9dc45c6282d63f4107e29e5f51b0eb728ab1bcbcdbc9338dac
[root@docker ~]# docker ps 
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS     NAMES
2350cedb3238   nginx:1.22.1   "/bin/sh -c /usr/sbi…"   17 seconds ago   Up 16 seconds             mystifying_lichterman
[root@docker ~]# 
[root@docker ~]# docker exec 2350cedb3238 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
       

进入容器 namespace 查看网络配置

# 方法一:获取容器进程ID
[root@docker ~]# docker inspect  2350cedb3238 | grep -i pid
            "Pid": 20198,
            "PidMode": "",
            "PidsLimit": null,
# 方法二:获取容器进程ID           
[root@docker ~]# docker inspect  -f '{{.State.Pid}}' 2350cedb3238
20198

[root@docker ~]# nsenter  -t 20198 -n 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

创建一个自定义网络 net0

[root@docker ~]# docker network create -d bridge --subnet 172.100.0.0/16 net0
ce1dc69bf1f0ac9a059db2e3c2145661f73d5d38ffae94f8989a33331755f0b9

以上命令创建了一个名为 net0 的网桥,网络 ID 是 ce1dc69bf1f0ac9a05…。该网络使用的是 bridge 模式,网段是 172.100.0.0/16, 该网络的第一个IP地址 172.100.0.1 分配给了网桥,网桥的名字可以通过下面命令查到。

[root@docker ~]# ip a 
......
26: br-ce1dc69bf1f0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:ba:8e:49:79 brd ff:ff:ff:ff:ff:ff
    inet 172.100.0.1/16 brd 172.100.255.255 scope global br-ce1dc69bf1f0
       valid_lft forever preferred_lft forever

如果想查看网桥的详细信息,可以使用 inspect 命令

[root@docker ~]# docker network inspect net0
[
    {
        "Name": "net0",
        "Id": "ce1dc69bf1f0ac9a059db2e3c2145661f73d5d38ffae94f8989a33331755f0b9",
        "Created": "2023-10-22T19:32:42.673939772+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.100.0.0/16"
                }
            ]
        },

为容器绑定 veth pair 网卡,并接入上面创建的网桥上

mkdir -p /var/run/netns

PID=$(docker inspect  -f '{{.State.Pid}}' 2928e880ee43)
IPADDR=172.100.0.2
NETMASK=255.255.0.0
GATEWAY=172.100.0.1


ln -s /proc/$PID/ns/net /var/run/netns/$PID

ip netns  ls 

# 创建一对veth pair,vetha 和 vethb 
ip link add vetha type veth peer name vethb
# 将 vetha 绑定到网桥 br-ce1dc69bf1f0 
brctl addif br-ce1dc69bf1f0 vetha
# 启用 vetha 
ip link set vetha up 
# vethb 放入指定的网络命名空间
ip link set vethb netns $PID
# 在指定的命名空间中将 vethb 改名为 eth0 
ip netns exec $PID ip link set dev vethb name eth0
# 启用 eth0 
ip netns exec $PID ip link set eth0 up 
# 为 eth0 添加 IP 地址
ip netns exec $PID ip addr add $IPADDR/$NETMASK dev eth0
# 添加默认网关
ip netns exec $PID ip route add default via $GATEWAY

验证容器内IP地址可以与外界正常通信 。

[root@docker ~]# docker ps 
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS     NAMES
2928e880ee43   nginx:1.22.1   "/bin/sh -c /usr/sbi…"   9 minutes ago   Up 9 minutes             hardcore_mirzakhani
[root@docker ~]# 
[root@docker ~]# docker exec 2928e880ee43 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
27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether aa:66:e4:26:a4:92 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.100.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
[root@docker ~]# 
[root@docker ~]# ping -c2 172.100.0.2
PING 172.100.0.2 (172.100.0.2) 56(84) bytes of data.
64 bytes from 172.100.0.2: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 172.100.0.2: icmp_seq=2 ttl=64 time=0.053 ms

--- 172.100.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.053/0.061/0.069/0.008 ms
   valid_lft forever preferred_lft forever
27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether aa:66:e4:26:a4:92 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.100.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
[root@docker ~]# 
[root@docker ~]# ping -c2 172.100.0.2
PING 172.100.0.2 (172.100.0.2) 56(84) bytes of data.
64 bytes from 172.100.0.2: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 172.100.0.2: icmp_seq=2 ttl=64 time=0.053 ms

--- 172.100.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.053/0.061/0.069/0.008 ms

原文地址:https://blog.csdn.net/zhangchen124/article/details/143948877

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!