万字长文深入理解Docker镜像分层原理、容器数据卷、网络通信架构(Docker系列第2章,共3章)

镜像分层的简单直观体现

在执行docker pull时,会发现多个Pull complete 字样,就能体现分层,如果是一个文件,只会有一个Pull complete 。

docker pull redis
Using default tag: latest
latest: Pulling from library/redis
a2abf6c4d29d: Already exists 
c7a4e4382001: Pull complete 
4044b9ba67c9: Pull complete 
c8388a79482f: Pull complete 
413c8bb60be2: Pull complete 
1abfd3011519: Pull complete 
Digest: sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

文件系统

  • 概念:文件系统是计算机系统中用于组织和管理数据存储的一种方式。它定义了数据如何存储、命名、访问和修改的方式。
  • 举例:如Windows自带的NTFS、FAT32、EXFAT,和Linux中的EXT3、EXT4这种的。
  • FAT32:有着很好的兼容性,但是不支持单个大于4GB的文件,U盘空间充足却放不下大文件,就是这个原因。可以格式化后换一个文件系统解决。
  • NTFS:NTFS是Windows操作系统中最常用的文件系统之一,它支持大容量硬盘和大文件,提供了较好的安全性、稳定性和恢复能力。
  • EXFAT:为了解决FAT32不支持单个大于4GB的文件而诞生。

操作系统的引导

  • 概括:操作系统的引导(Bootstrapping)是指在计算机启动时,通过加载操作系统的核心组件和必要的驱动程序,使得计算机能够进入操作系统的运行状态。引导过程通常包括以下几个步骤:
  1. 加电自检(Power-On Self-Test,POST):计算机启动时,会执行自检程序,检查硬件是否正常。如果自检过程中发现硬件故障,计算机可能会发出警报或者停止启动。
  2. BIOS/UEFI初始化:计算机启动后,会加载基本输入/输出系统(Basic Input/Output System,BIOS)或统一可扩展固件接口(Unified Extensible Firmware Interface,UEFI),进行硬件初始化和系统设置。
  3. 引导加载程序(Bootloader)启动:BIOS/UEFI会寻找引导加载程序,一般位于硬盘的引导扇区(Boot Sector)或其他可引导的介质(如USB闪存驱动器)。引导加载程序的主要任务是加载操作系统的内核镜像到内存中,并执行操作系统的初始化。
  4. 内核初始化:一旦操作系统的内核镜像被加载到内存中,引导加载程序将控制权转交给操作系统内核。内核开始初始化系统的各个部分,包括设备驱动程序、文件系统等。
  5. 用户空间初始化:内核完成初始化后,操作系统开始启动用户空间的进程,包括系统服务和用户登录界面。

联合文件系统UnionFileSystem

联合文件系统(UFS),是一种分层的高性能文件系统,支持对文件系统的修改作为一次提交来层层叠加。
docker用它作为镜像的存储方式,使得容器在运行时只需存储修改过的部分,而不是整个文件系统,从而节省存储空间并提高效率。

为什么docker用UFS

  • 共享和复用:UFS 允许多个容器共享相同的基础镜像层,这样可以节省存储空间,并减少容器的创建和启动时间。当多个容器共享相同的文件系统层时,这些层中的文件和资源只需存储一次,而不是每个容器都存储一份。这和代码是一样的,封装的方法拆分的越小,可复用性就会更好。
  • 写时复制(Copy-on-Write):UFS 允许容器在运行时进行读写操作,同时保留了镜像的不可变性。当容器对文件系统进行修改时,UFS 会将修改的内容写入新的层中,而不会直接修改原始镜像层,这样可以确保容器之间的隔离性,并且不会影响其他容器或原始镜像。
  • 快速启动和部署:由于 UFS 允许容器共享文件系统层并利用写时复制机制,因此容器的创建和启动速度非常快。Docker 可以快速地在现有镜像的基础上创建新的容器实例,并立即启动运行,这对于实现快速部署和横向扩展至关重要。
  • 轻量级:UFS 提供了一种轻量级的文件系统封装方式,它不需要完整地复制整个文件系统,而是在需要修改文件时才进行复制和修改,这样可以节省存储空间并减少资源消耗,下文会讲。

深入理解Docker镜像分层加载原理

  • 简易概括:在BootFS(宿主机提供)的基础上,利用UFS封装RootFS,使用镜像层的数据,叠加出一个可写的容器层。
    所以说,docker pull下来的CentOS、Ubuntu发行版,才很小,一个原因是省掉了BootFS,另一个原因是抛弃了很多非核心的组件,如vim等。


  • BootFS:引导文件系统(boot filesystem)是指用于引导操作系统的文件系统。这个文件系统通常包含了引导加载程序(boot loader)所需的文件,例如引导配置文件、内核以及其他引导所需的文件,docker用宿主机的内核,出处就是这里。

  • RootFS:在 Linux 中,rootfs(root file system)是指操作系统启动后的根文件系统,它包含了整个文件系统的目录结构和文件,提供了用户空间程序运行所需的基本文件和资源,例如根目录下的几个目录。

  • 镜像层:只读的,包含了应用程序运行所需的所有文件和依赖项。

  • 容器层:可写的,当容器启动时,一个新的容器层被加载到容器的顶部,用来支撑数据变化的存储。

上下层级关系是这样的:
docker的UFS管理了最上面的3层,利用写时复制(Copy-on-Write)的机制,当容器需要修改文件时,UFS 会在容器层中进行写操作,而不会直接修改镜像层中的文件。这样可以保证镜像的不可变性,同时允许容器进行灵活的读写操作。
新的容器又可以commit成一个新的镜像,这个新的镜像基础上,又可以堆叠更多的容器,层层累加。

              --容器层--
           -----镜像层------
      ----------RootFS----------
----------------BootFS----------------

容器数据卷

  • 极简概括:容器数据卷,容器卷、数据卷、一个概念,将宿主机中的目录或文件挂载到容器中,从而让容器和宿主机共同读写这块的数据。并且在容器销毁时,容器卷的数据不会丢失。
  • 用法:docker run --name name1 [-it/-d] -v /宿主机绝对路径:/容器内绝对路径 --privileged=true 镜像id [/bin/bash]
  • 备注:-v是volume的首字母,挂载目录默认是读写权限,但是目录本身容器内部删不掉(极少需要删除),如果宿主机没有这个目录,只要权限充足,它会自动创建。
  • 适用场景:
    • 数据备份:当容器被误删,或者容器内的组件因故障无法运行时,容器卷的数据保留的好好的,这算是一种容灾。
    • 数据互通:容器内的组件,和容器外的系统,可以共享这块数据的读写,实时的,这对开发者、运维人员很友好。
    • 同步日志和数据:很多组件,应用、日志、数据是可以分开的,所以就可以把日志,数据,放进多个数据卷。
  • 优点:
    • 上文的适用场景,就是两个优点。
    • 易于备份:通过使用容器卷,可以轻松地备份和恢复数据,而不需要备份整个容器。
  • 缺点:
    • 复杂化:对于新手,或者刚接触新服务器的运维人员,多容器下的容器卷管理,使得运维工作错综复杂。
    • 高可用问题:MySQL不推荐在docker中,其中一个就是数据的高可用问题,MySQL减少保障故障时数据丢失问题和维护高可用带来的性能问题,使用了3种不同的redo log刷盘机制作为保障,但是又加了一个容器卷环节,多了一个环节,就有可能在这上面出故障,尤其是MySQL这种需要环境极度稳定的组件。

使用容器卷记得添加--privileged=true

使容器具有完全可控的权限,防止出现Permission denied的错误。
注意是等号,不是空格,有个d。

容器数据卷实操

对数据同步的测试

docker run --name=zs_ubuntu --privileged=true -it -v /test:/test b3797fa08f5b /bin/bash
进入ubuntu容器后
cd /test
touch a
开启另一个宿主机终端
touch b
容器和宿主机互相ls,发现都有a、b两个文件

使用inspect命令查看挂载相关配置

docker inspect 容器id
找到Mounts段,
"Mounts": [
    {
        "Type": "bind",
        "Source": "/test",
        "Destination": "/test",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],


Type: 指定了数据卷的类型,这里是bind,表示这是一个绑定挂载,即将宿主机上的一个目录挂载到容器内部。
Source: 指定了数据卷的来源,这里是/test,表示宿主机上的目录路径。
Destination: 指定了数据卷在容器内的挂载目标路径,这里也是/test,表示容器内部的路径。
Mode: 这个字段可以用来指定挂载的权限模式,但在这个配置中为空,表示使用默认权限。
RW: 表示是否以读写模式挂载,这里是true,表示挂载为读写模式。
Propagation: 指定了挂载点的传播属性,这里是rprivate,表示挂载点将会被私有化传播,即只会影响到当前容器的进程,不会传播到其他容器。

故障情况下,容器卷内容的情况

停止容器
docker stop ea03c5ef1025
在宿主机内,创建文件
touch /test/c
开启
docker start ea03c5ef1025
进入容器
docker exec -it ea03c5ef1025 /bin/bash
此时发现c依旧存在
ls /test


宿主机误删容器(强制)
docker rm -f ea03c5ef1025
发现a、b、c3个文件都在。
ls /
重新创建新的容器
docker run --name=zs_ubuntu --privileged=true -it -v /test:/test b3797fa08f5b /bin/bash
进入容器后,发现a、b、c 3个文件都在。
ls /

容器数据卷的rw与ro两种读写权限

rw:默认值,双方各有读写权限。

执-v命令时,默认是读写权限,以下两个命令是相等的。
docker run --name=zs_ubuntu --privileged=true -it -v /test:/test b3797fa08f5b /bin/bash
docker run --name=zs_ubuntu --privileged=true -it -v /test:/test:rw b3797fa08f5b /bin/bash

ro:宿主机的文件可以同步给容器,但容器无法更改。

某些情况下,需要将宿主机映射的目录,让容器只有只读权限,read only,可以这样做:
docker run --name=zs_ubuntu --privileged=true -it -v /test:/test:ro b3797fa08f5b /bin/bash

进入容器,测试容器内写入
touch /test/d
touch: cannot touch 'd': Read-only file system

进入宿主机终端,测试文件写入:
touch /test/d
宿主机成功写入

进入容器,查看容器是否同步,发现已经同步
ls /test/d
a  b  c  d

容器数据卷的继承

卷的继承需要“--volumes-from 父容器卷” 来声明,具有父子关系的容器卷还是互相共享关系,父子间并没有强关联,意味着父容器停了或被删除,不影响子容器。

请空容器,方便测试。
docker rm -f 4432fa5f2d07
并删除/test下所有文件
rm -f /test/*
启动一个父容器
docker run --name parent --privileged=true -it -v /test:/test b3797fa08f5b /bin/bash
启动一个子容器
docker run --name son --privileged=true -it --volumes-from parent b3797fa08f5b /bin/bash
宿主机查看正在运行的容器实例
docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS         PORTS     NAMES
5ff477d6b3ec   b3797fa08f5b   "/bin/bash"   5 minutes ago   Up 4 minutes             son
a4da63aca520   b3797fa08f5b   "/bin/bash"   6 minutes ago   Up 6 minutes             parent

宿主机创建a文件
touch /test/a
两个容器上查看,发现a文件都存在。
touch /test

父级故障模拟,测试父子关联性,结果得出父子间没有强关联性。

在宿主机上停止父级容器
docker stop a4da63aca520
在宿主机上创建文件
touch /test/b
在son容器上执行ls,发现有a、b两个文件
ls /test
a b


在宿主机上删除父级容器
docker rm a4da63aca520
在宿主机上创建文件
touch /test/c
在son容器上执行ls,发现有a、b、c两个文件
ls /test
a b c

容器卷的多样化共享

多容用一卷:

强制删除所有容器,能不用则不用。
docker rm -f $(docker ps -aq)
删除宿主机下,容器卷目录所有文件
rm -f /test/*

开3个终端,创建3个容器
docker run --name one --privileged=true -it -v /test:/test b3797fa08f5b /bin/bash
docker run --name two --privileged=true -it -v /test:/test b3797fa08f5b /bin/bash
docker run --name three --privileged=true -it -v /test:/test b3797fa08f5b /bin/bash

查看是否开启成功
docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS          PORTS     NAMES
8ed7a2356d5c   b3797fa08f5b   "/bin/bash"   17 seconds ago   Up 10 seconds             three
ff2b2a92d984   b3797fa08f5b   "/bin/bash"   26 seconds ago   Up 25 seconds             two
220539fdf650   b3797fa08f5b   "/bin/bash"   54 seconds ago   Up 53 seconds             one

宿主机创建a文件
touch /test/a

三个容器分别执行ls /test,发现都有a
ls /teset
a

一容用多卷:

强制删除所有容器,能不用则不用。
docker rm -f $(docker ps -aq)
删除宿主机下,容器卷目录所有文件
rm -f /test/*
并额外创建2个目录
touch /test2 /test3
如下
ll / | grep test
drwxr-xr-x.   2 root root    6 4月  12 03:07 test
drwxr-xr-x    2 root root    6 4月  12 03:07 test2
drwxr-xr-x    2 root root    6 4月  12 03:07 test3

创建一个容器,并映射3个目录
docker run --name test --privileged=true -it -v /test:/test -v /test2:/test2 -v /test3:/test3 b3797fa08f5b /bin/bash
发现容器内也有3个目录
ll / | grep test
drwxr-xr-x.   2 root root    6 Apr 11 19:07 test/
drwxr-xr-x    2 root root    6 Apr 11 19:07 test2/
drwxr-xr-x    2 root root    6 Apr 11 19:07 test3/

容器内执行
touch /test/a /test2/a /test3/a
宿主机查看,发现文件依然共享
ls /test /test2 /test3
/test:
a

/test2:
a

/test3:
a

网卡相关

安装docker后,执行ifconfig,会看到以下内容:

  • ens33(老版本的叫eth0)是配置宿主机网络用的。
  • lo是本地回环地址,用于本地主机内部通信的虚拟网络接口。
  • docker就是安装docker时虚拟出来的网卡,用于docker容器间和宿主机的通信:
    • flags=4099<UP,BROADCAST,MULTICAST> mtu 1500: 这部分显示了网络接口的名称(docker0)、标志和 MTU(最大传输单元)值(1500)。
    • inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255: 这部分显示了网络接口的 IPv4 地址(172.17.0.1)、子网掩码(255.255.0.0)和广播地址(172.17.255.255)。这个地址范围是 Docker 桥接网络的默认范围。
    • inet6 fe80::42:37ff:fef9:25d7 prefixlen 64 scopeid 0x20: 这部分显示了网络接口的 IPv6 地址。
    • ether 02:42:37:f9:25:d7 txqueuelen 0 (Ethernet): 这部分显示了网络接口的 MAC 地址和一些其他信息。
    • RX packets 4197 bytes 250537 (244.6 KiB): 这部分显示了接收到的数据包和字节数。
    • TX packets 4317 bytes 14144344 (13.4 MiB): 这部分显示了发送的数据包和字节数。。
ifconfig

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:37ff:fef9:25d7  prefixlen 64  scopeid 0x20<link>
        ether 02:42:37:f9:25:d7  txqueuelen 0  (Ethernet)
        RX packets 4197  bytes 250537 (244.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4317  bytes 14144344 (13.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.180  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::2545:6607:f0f7:64cb  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:39:be:95  txqueuelen 1000  (Ethernet)
        RX packets 20615  bytes 15431772 (14.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 10726  bytes 954390 (932.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 32  bytes 2592 (2.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 32  bytes 2592 (2.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

执行ip a,也会看到类似的内容:

  • <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default: 这部分显示了网络接口的名称(docker0)、状态(DOWN)、标志(NO-CARRIER、BROADCAST、MULTICAST、UP)、MTU(最大传输单元)值(1500)以及队列规则(qdisc)等信息。
  • link/ether 02:42:37:f9:25:d7 brd ff:ff:ff:ff:ff:ff: 这部分显示了网络接口的 MAC 地址和广播地址。
  • inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever: 这部分显示了网络接口的 IPv4 地址(172.17.0.1)、子网掩码(/16),以及广播地址。valid_lft和 `preferred_lft 分别表示地址的有效生存时间和首选生存时间,设置为 forever 表示永久有效。
  • inet6 fe80::42:37ff:fef9:25d7/64 scope link valid_lft forever preferred_lft forever: 这部分显示了网络接口的 IPv6 地址和范围。
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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:39:be:95 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.180/24 brd 192.168.0.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::2545:6607:f0f7:64cb/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:37:f9:25:d7 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:37ff:fef9:25d7/64 scope link 
       valid_lft forever preferred_lft forever

Docker网络

docker网络顾名思义,为什么要去研究它?

  • 控制容器间通信:只有容器不通信,好比买了个手机不联网,技能无法充分发挥。
  • 降低对网络的运维成本:容器IP变动时(增加、减少容器数量等因素),可以通过服务名进行通信,保证通信不受影响。

Docker网络相关命令

在docker run时,可以使用docker run --network bridge ...去指定驱动程序。

docker network ls

NETWORK ID     NAME      DRIVER    SCOPE
f4481eb7078e   bridge    bridge    local
c10b6f388063   host      host      local
21c306a93cbb   none      null      local
  • NETWORK ID:网络唯一标识。
  • NAME:起个名。
  • DRIVER:Docker 网络所使用的驱动程序,一些常见的网络驱动程序包括:
    • bridge(桥接 默认值): 默认的 Docker 网络驱动程序,使用docker0网卡连接容器到宿主机的网络。
    • host(主机): 让容器直接使用宿主机的网络命名空间,与宿主机共享网络栈,使用这个,docker run -p就没有意义了。
    • overlay(覆盖): 允许在多个 Docker 宿主机上创建连接的容器网络,用于容器在不同宿主机间通信。
    • macvlan(MACVLAN): 允许容器拥有自己的 MAC 地址,并直接与物理网络相连。
    • none(无): 禁用容器的网络,使容器无法进行网络通信,很少用。
    • container(容器):新创建的容器不会创建自己的网卡,而是和一个指定容器共享IP和端口,很少用,容易端口冲突,且对容器有依赖性。
  • SCOPE:定义网络范围的作用域
    • local(本地 默认值): 这表示网络只在当前 Docker 宿主机上可见和可用,不会跨越宿主机边界。
    • global(全局): 这表示网络在所有 Docker 宿主机上都可见和可用,可以跨越宿主机边界。
    • swarm(集群): 这表示网络是 Docker Swarm 集群中的一部分,可以在整个 Swarm 集群中使用。

Docker对网络配置的增删改查操作:

执行网络联机的命令行帮助,会得到以下内容,无外乎就是增删改查
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 test_net
7b7c0cbc15c65a5ef4a0e1ad5e7d997ec04afb322afc9e67845c8735ccf1fb06
docker network ls
NETWORK ID     NAME       DRIVER    SCOPE
f4481eb7078e   bridge     bridge    local
c10b6f388063   host       host      local
21c306a93cbb   none       null      local
7b7c0cbc15c6   test_net   bridge    local

删:
docker network rm test_net
^[[Atest_net
docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
f4481eb7078e   bridge    bridge    local
c10b6f388063   host      host      local
21c306a93cbb   none      null      local

改:极少操作。

查:
docker inspect f4481eb7078e
返回一个json。

Docker网络通信架构

  • docker0:虚拟出来一块网卡,让容器之间、宿主机、进行通信。docker0是虚拟出来的硬件,bridge是通信的方式。
  • veth:veth(Virtual Ethernet)是Linux系统中一种虚拟网络设备,常用于 Docker 容器之间和容器与宿主机之间的通信,当创建一个Docker容器时,Docker会为容器分配一个唯一的网络命名空间,并为该容器创建一个veth pair设备。容器内的veth设备称为 eth0(或其他指定的网络接口名),而与之配对的宿主机端的veth设备可以在宿主机上看到,通常以 vethXXX 这样的形式命名。通过这种方式,容器可以通过自己的网络接口与宿主机上的其他容器或外部网络进行通信。Docker 使用 Linux 的网络命名空间和 veth 设备来隔离容器的网络栈,同时通过网络桥接(如 docker0)实现容器与宿主机网络的连接和通信。
  • eth0:这是每个容器实例或宿主机自身的内部网卡。
  • 注意:容器内的eth0和veth总是成双成对的出现。
  • 通信方式:无论是容器与容器之间,容器与宿主机之间,都需要通过docker0的bridge方式通信。

上一张经典的图:

开两个Nginx证明一下是否是多出来两个veth:

关闭宿主机nginx服务,nginx设置开启了服务
service nginx stop
起两个nginx容器
docker run -d -p 80:80 nginx
docker run -d -p 8080:80 nginx
确定容器是否真的跑起来
docker ps
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS         PORTS                                   NAMES
bf6321d659c4   nginx     "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   funny_williams
859b85ccf760   nginx     "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp       brave_vaughan

确定配置是否成功,或用curl访问也行
netstat -nlp | grep 80
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      22982/docker-proxy  
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      22838/docker-proxy  
tcp6       0      0 :::8080                 :::*                    LISTEN      22988/docker-proxy  
tcp6       0      0 :::80                   :::*                    LISTEN      22844/docker-proxy  
unix  2      [ ACC ]     STREAM     LISTENING     50191    6480/abrtd           /var/run/abrt/abrt.socket


重点来了,然后再执行ifconfig,会发现多出来两个veth开头的网卡:
ifconfig

veth7c2861b: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::e881:5aff:fe82:55ca  prefixlen 64  scopeid 0x20<link>
        ether ea:81:5a:82:55:ca  txqueuelen 0  (Ethernet)
        RX packets 35  bytes 4797 (4.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 52  bytes 7096 (6.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth8ed54b8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::1cb3:6eff:fe84:1563  prefixlen 64  scopeid 0x20<link>
        ether 1e:b3:6e:84:15:63  txqueuelen 0  (Ethernet)
        RX packets 31  bytes 4017 (3.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 37  bytes 4395 (4.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

同理,可以使用ip a命令
ip addr

14: veth7c2861b@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether ea:81:5a:82:55:ca brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::e881:5aff:fe82:55ca/64 scope link 
       valid_lft forever preferred_lft forever
18: veth8ed54b8@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 1e:b3:6e:84:15:63 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::1cb3:6eff:fe84:1563/64 scope link 
       valid_lft forever preferred_lft forever

想要验证,与eth0是否是成对出现的,但是nginx镜像缺少ip 或者ifconfig命令,apt-get update命令报错,导致无法验证。
实际上宿主机上看的veth7c2861b@if13和veth8ed54b8@if17中的if13与if17,都是与容器当中的网卡编号对应的。

当使用docker inspect 容器id时,就能看到该容器网络相关的信息。

docker inspect 6dc7f1d29a17 | tail -n 21
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "MacAddress": "",
                    "NetworkID": "f4481eb7078ebf9be90119f8a99f5869354ed14d9b093924da43bf1ba19b36a0",
                    "EndpointID": "",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DriverOpts": null,
                    "DNSNames": null
                }
            }

对于这个json,做个介绍:

  • "IPAMConfig": 这是用于管理IP地址的配置,通常在Docker网络模式为bridge时使用。
  • "Links": 这里是容器与其他容器之间链接的信息,但在这个例子中为null,表示没有其他容器链接到这个容器。
  • "Aliases": 这是容器的网络别名,也是null,表示没有设置别名。
  • "MacAddress": 这是容器的MAC地址,通常由Docker自动生成。
  • "NetworkID": 这是网络的唯一标识符,用于区分不同的Docker网络。
  • "EndpointID": 这是容器端点的唯一标识符,用于标识容器在网络中的位置。
  • "Gateway": 这是网络的网关地址,通常由Docker提供。
  • "IPAddress": 这是容器分配的IP地址。
  • "IPPrefixLen": 这是容器IP地址的前缀长度,通常是一个整数。
  • "IPv6Gateway": 这是IPv6网络的网关地址。
  • "GlobalIPv6Address": 这是容器的全局IPv6地址。
  • "GlobalIPv6PrefixLen": 这是容器的全局IPv6地址的前缀长度。
  • "DriverOpts": 这是网络驱动的选项配置,通常在特定网络驱动中使用。
  • "DNSNames": 这是容器的DNS名称列表,用于解析容器内部的 DNS 查询。

不同容器配置不同的Docker网络

  • 为了分门别类,方便管理。
  • 有了它,可以避免IP变动引起的故障,为docker容器编排做准备。
创建网络
docker network create zs_test
ee465a9eccd7d758e3b4a4281539352d8169364f860091928f3296129c6ac384

并查看
docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
f4481eb7078e   bridge    bridge    local
c10b6f388063   host      host      local
21c306a93cbb   none      null      local
ee465a9eccd7   zs_test   bridge    local

开两个终端分别执行
docker run --name centos01 --network zs_test -it 5d0da3dc9764 /bin/bash
docker run --name centos02 --network zs_test -it 5d0da3dc9764 /bin/bash

然后再任意终端上ping另一个容器的name参数
ping centos02
PING centos02 (172.23.0.3) 56(84) bytes of data.
64 bytes from centos02.zs_test (172.23.0.3): icmp_seq=1 ttl=64 time=0.082 ms
64 bytes from centos02.zs_test (172.23.0.3): icmp_seq=2 ttl=64 time=0.095 ms
64 bytes from centos02.zs_test (172.23.0.3): icmp_seq=3 ttl=64 time=0.090 ms

使用name参数,可以避免IP变动情况的发生。

热门相关:惊悚乐园   大文豪   四爷又被福晋套路了   强宠头号鲜妻:陆少,滚!   美漫大幻想