docker专题(二)镜像、数据卷、网络及Dockerfile
前言
上一篇主要说docker基本命令,该片主要说镜像、容器、数据卷、网络及DockerFile
本文参考文章:http://c.biancheng.net/view/3137.html
Docker镜像
镜像和分层
Docker镜像由一些松耦合的只读镜像层组成如下图
Docker负责堆叠这些镜像层,并且将他们表示为单一统一的对象
docker inspect 命令查看 mysql和redis的镜像层
# 镜像层redis
"sha256:764055ebc9a7a290b64d17cf9ea550f1099c202d83795aa967428ebdf335c9f7",
"sha256:245c9d23f65373415922e53424032cabe7b282c5cf8f9f8070a7d1830fca6871",
"sha256:ebef6caacb966ed54c0c3facf2288fa5124452f2c0a17faa1941625eab0ceb54",
"sha256:0b7b774038f08ec329e4dd2c0be440c487cfb003a05fce87cd5d1497b602f2c1",
"sha256:a71d36a87572d637aa446110faf8abb4ea74f028d0e0737f2ff2b983ef23abf3",
"sha256:9e1fddfb3a22146392a2d6491e1af2f087da5e6551849a6174fa23051ef8a38f"
#mysql镜像层
"sha256:764055ebc9a7a290b64d17cf9ea550f1099c202d83795aa967428ebdf335c9f7",
"sha256:71a14cc55692a4edac5da18be420acf085c681ec95f7af5ff64bb1c236440d59",
"sha256:50854886015e8e202f3197c6a95ae60632a432aba673188bb7c3f9ee80ed2f30",
"sha256:1952fb2b0eb4d355f05a815c6d804243f089e05c0b8f3b1c1eaf0a1109e8293a",
"sha256:893f6aea2ce23f813c44180ccb7a41e8a4d151cb02f7c365cc4e837c0872b211",
"sha256:b8d0aeaeeee8ed55cb6bfc7e061d0bb0491e91024790c45d0c56b07c29cea080",
"sha256:d7cde20f3f6895bd6e6b2ef5767ee60d7034babe62093340ac22df5c8622db74",
"sha256:ae8fcba6091c17b6691f9a1c49621f193e2f475b3eee44d83b4f78b0d0fd31ae",
"sha256:1a4de2199d77be12009ed995b8db1c5a85d9a9b5e19673a8af4824de03f4ce0a",
"sha256:eeabd280c12ee308111950fd99ba8ffdd75130b6d948ba6a34fe6221b82f20dd",
"sha256:cd0c4980990f734130353adfa00f88ba57bc946b868468d633eeb75abd465fa0",
"sha256:027a41439ba438753333f44da1bc57b86e2211f708ace53343cc733151dc2380"
sha256表示用SHA256散列值来表示镜像层
并且两者有相同的一个镜像层,所以拉取mysql镜像的时候就不会再拉取已存在的镜像层了
在添加额外的镜像层同时,镜像始终保持当前所有镜像的组合
如下图:没个镜像层包含3个文件,而镜像包含来自两个镜像层的6个文件
当上层镜像中文件包含了底层镜像层中的文件,就会覆盖底层镜像中的文件,这样使得文件的更新作为一个新的镜像层添加到镜像去中
如下图:外部看来只有6个文件,因为最上层中的文件7是文件5的一个更新版本
Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统
如下为3层镜像堆叠并合并后对外统一提供的视图
共享镜像层
多个镜像之间可以并且确实会共享镜像层。这样可以有效节省空间并提升性能。
如之前拉取mysql的镜像的时候会出现already exists结尾的行
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
b4d181a07f80: Already exists
a462b60610f5: Pull complete
578fafb77ab8: Pull complete
524046006037: Pull complete
d0cbe54c8855: Pull complete
aa18e05cc46d: Pull complete
32ca814c833f: Pull complete
9ecc8abdb7f5: Pull complete
ad042b682e0f: Pull complete
71d327c6bb78: Pull complete
165d1d10a3fa: Pull complete
2f40c47d0626: Pull complete
Digest: sha256:52b8406e4c32b8cf0557f1b74517e14c5393aff5cf0384eff62d9e81f4985d4b
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
由此可见docker拉取镜像的时候可以识别出拉取的镜像中,哪几层镜像层已存在
镜像散列值(摘要)
镜像本身就是一个配置对象
,其中包含了镜像层的列表和一些元数据信息
镜像层才是实际数据存储的地方
镜像的唯一标识,是配置对象本身的散列值,每个镜像层的id也是镜像层本身内容的散列值
这就意味着修改镜像的内容或其中任意的镜像层都会引起散列值的变化
深入思考
当镜像传输的时候会对镜像进行压缩以节省网络带宽和仓库的存储空间,但是 压缩会改变镜像内容,是不是意味着镜像内容的散列值经过传输后会和镜像内容不一致呢!!!!那如果做防篡改校验是不是会就会无法通过!!!
所以 没个镜像层同时会包含一个分发散列值,这是一个压缩版镜像的散列值,用于校验镜像有没有在中途被篡改
Docker容器
容器和虚拟机
虚拟机和容器最大的区别是容器更快并且更轻量级——与虚拟机运行在完整的操作系统之上相比,容器会共享其所在主机的操作系统/内核
如下图为当个docker镜像启动多个容器的示意图
容器和虚拟机都是依赖于某个宿主机才能运行
如下示例中,如果一个宿主机需要安装4个业务应用的物理服务器
在虚拟机中需要 开启物理机并启动 Hypervisor 引导程序。一旦 Hypervisor 启动,就会占有机器上的全部物理资源,如 CPU、RAM、存储和 NIC。
Hypervisor 接下来就会将这些物理资源划分为虚拟资源,并且看起来与真实物理资源完全一致。
然后 Hypervisor 会将这些资源打包进一个叫作虚拟机(VM)的软件结构当中。这样用户就可以使用这些虚拟机,并在其中安装操作系统和应用。
前面提到需要在物理机上运行 4 个应用,所以在 Hypervisor 之上需要创建 4 个虚拟机并安装 4 个操作系统,然后安装 4 个应用。当操作完成后,结构如下图所示。
而在容器中不同
服务器启动之后,所选择的操作系统会启动。在 Docker 世界中可以选择 Linux,或者内核支持内核中的容器原语的新版本 Windows。
与虚拟机模型相同,OS 也占用了全部硬件资源。在 OS 层之上,需要安装容器引擎(如 Docker)。
容器引擎可以获取系统资源,比如进程树、文件系统以及网络栈,接着将资源分割为安全的互相隔离的资源结构,称之为容器。
每个容器看起来就像一个真实的操作系统,在其内部可以运行应用。按照前面的假设,需要在物理机上运行 4 个应用。
因此,需要划分出 4 个容器并在每个容器中运行一个应用,如下图所示。
区别在于
Hypervisor 是硬件虚拟化,会把硬件物理资源划分为虚拟资源
容器是操作系统虚拟化,会将系统资源划分为虚拟资源。
数据卷
数据主要分为两类,持久化的与非持久化的 。
非持久化的数据与容器的声明周期相同,即容器删除非持久化数据也就不存在了
持久化的数据是与容器解耦的,删除容器不会对持久化数据删除
容器卷
用户创建卷,然后创建容器,将卷挂在在容器文件系统的某个目录下,任何写到该目录下的内容都会写到卷中,即使容器被删除,卷中的数据也依旧存在
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker volume --help
Usage: docker volume COMMAND
Manage volumes
Commands:
#创建卷
create Create a volume
#查看卷信息
inspect Display detailed information on one or more volumes
#卷列表
ls List volumes
#移除所有未使用的卷
prune Remove all unused local volumes
#移除卷
rm Remove one or more volumes
Run 'docker volume COMMAND --help' for more information on a command.
#列表
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker volume ls
DRIVER VOLUME NAME
local 8bee22f1ce23212bd29f1eeed8802981e525ee5b5f740a07db666b9f1a94edfa
local baae75aa4fdefeff2d20ca5b894b574001ffe9b767bdc7a5863413d49634c127
local d810cec5560f7dd5e0be4deea1d7b6e54579781c62b9faf719fc880283328460
#查看
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker volume inspect 8bee22f1ce23212bd29f1eeed8802981e525ee5b5f740a07db666b9f1a94edfa
[
{
"CreatedAt": "2021-06-29T12:03:18+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint":
#默认路径在 /var/lib/docker/volumes/中 "/var/lib/docker/volumes/8bee22f1ce23212bd29f1eeed8802981e525ee5b5f740a07db666b9f1a94edfa/_data",
"Name": "8bee22f1ce23212bd29f1eeed8802981e525ee5b5f740a07db666b9f1a94edfa",
"Options": null,
"Scope": "local"
}
]
- Driver 和 Scope 都是 local。这意味着卷使用默认 local 驱动创建,只能用于当前 Docker 主机上的容器
- Mountpoint 属性说明卷位于 Docker 主机上的位置。
默认情况下,Docker 创建新卷时采用内置的 local 驱动。恰如其名,本地卷只能被所在节点的容器使用。使用 -d 参数可以指定不同的驱动。
第三方驱动可以通过插件方式接入。这些驱动提供了高级存储特性,并为 Docker 集成了外部存储系统。下图展示的就是外部存储系统被用作卷存储。驱动集成了外部存储系统到 Docker 环境当中,同时能使用其高级特性。
注意
运行容器的时候可以通过 -v来挂载
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker run -d --name myredis -v/home/test:/home redis
fabccbaddb12bbdfe0a1450ccd0fe24a46a6c63efec6befbcd85610cb96235ae
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# cd /home/test
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker volume ls
DRIVER VOLUME NAME
local 3ca1939b491da226a037f646a236c79962c2bfc19643c56f0bfa80bebd3d4b5f
local 8bee22f1ce23212bd29f1eeed8802981e525ee5b5f740a07db666b9f1a94edfa
local baae75aa4fdefeff2d20ca5b894b574001ffe9b767bdc7a5863413d49634c127
local d810cec5560f7dd5e0be4deea1d7b6e54579781c62b9faf719fc880283328460
#查看可以与上方卷对上
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker volume inspect 3ca1939b491da226a037f646a236c79962c2bfc19643c56f0bfa80bebd3d4b5f
[
{
"CreatedAt": "2021-07-01T13:45:48+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/3ca1939b491da226a037f646a236c79962c2bfc19643c56f0bfa80bebd3d4b5f/_data",
"Name": "3ca1939b491da226a037f646a236c79962c2bfc19643c56f0bfa80bebd3d4b5f",
"Options": null,
"Scope": "local"
}
]
#具名挂载,匿名挂载和指定路径挂载
-v 容器路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器路径 #指定路径挂载
Dockerfile
Dockerfile 包含了对当前应用的描述,并且能指导 Docker 完成镜像的构建。
- 对当前应用的描述
- 知道Docker完成应用的容器化
FROM #基础镜像名
# 基础镜像 ,会作为当前镜像的一个基础镜像层,当前应用的剩余内容会作为新增镜像层添加到基础镜像层之上。
RUN #<命令行命令>或["可执行文件", "参数1", "参数2"]
#用于执行后面跟着的命令行命令,有两种格式
#shell:RUN <命令行命令>等同于,在终端操作的 shell 命令。
#exec 格式:RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
#注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大
#可以通过&&符号链接命令,这样只会创建一层镜像
COPY #[--chown=<user>:<group>] 如COPY hom* /mydir/
#复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
ADD
#ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:
#ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
#ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定
CMD #CMD <shell 命令> CMD ["<可执行文件或命令>","<param1>","<param2>",...] CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
#类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
#CMD 在docker run 时运行。
#RUN 是在 docker build。
#作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
#注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
ENTRYPOINT #ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
#类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
#但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。
#优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
#注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
#可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。
#示例:
#假设已通过 Dockerfile 构建了 nginx:test 镜像:
#FROM nginx
#ENTRYPOINT ["nginx", "-c"] # 定参
#CMD ["/etc/nginx/nginx.conf"] # 变参
ENV #ENV <key> <value> ENV <key1>=<value1> <key2>=<value2>...
#设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
ARG #ARG<参数名>[=<默认值>]
#构建参数,与 ENV 作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
VOLUME #VOLUME ["<路径1>", "<路径2>"...] VOLUME <路径>
#定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
#作用:
#避免重要的数据,因容器重启而丢失,这是非常致命的。
#避免容器不断变大。
#在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点
EXPOSE #<端口1> [<端口2>...]
#仅仅只是声明端口。
#作用:
#帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
#在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口
WORKDIR #ORKDIR <工作目录路径>
#指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)
USER #USER <用户名>[:<用户组>]
#用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)
HEALTHCHECK #HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令 HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令 HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
#用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
ONBUILD #ONBUILD <其它指令>
#用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这是执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
LABEL #LABEL <key>=<value> <key>=<value> <key>=<value> ...
#LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式
FROM centos
MAINTAINER zhao56
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
EXPOSE 80
CMD /bin/bash
CMD echo $MYPATH
CMD echo "---end---"
docker build -t mycentos:0.1 .
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker run -it mycentos:1.0
---end---
发现CMD只执行了最后一个
构建的时候(9步)
[root@iZ2ze9fp041k3gtah0ztnmZ test]# clear
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker build -t mycentos:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/9 : FROM centos
---> 300e315adb2f
删除镜像的时候有(9层)
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker rmi mycentos:1.0
Untagged: mycentos:1.0
Deleted: sha256:3821dce9bb3c0fc9808bf421f519aaca9450bf1ac03705f202f4218d6e4a454a
Deleted: sha256:0cdd09fc153725e3811fbe2544376db65948b3c08e25fb3fe7862cb7db032bc2
Deleted: sha256:dce2b2ae0d725b292e197f07f5bfbfb7f716b9e61cec96745bd7fc7a1e302a68
Deleted: sha256:b8dd83ad663dc04ff34719fa81d558e41d231786c9c1bd46c3015cfd10d8bb49
Deleted: sha256:92eca46b69a45908df3782b8a51f867268d9156a3f7d90ac37add33d917c15bc
Deleted: sha256:c2353389a5f5d82ff91a711d40bcf5f3fd45d60443342ecc5b05f5c73530792b
Deleted: sha256:05fedea2a23e2ab17cb942fe9d1965134e958f0057e1d2b290809c28a31288bb
Deleted: sha256:45a22f51d04115e7329545659d245f572505c96fb8b1047618101ab676267d8d
Deleted: sha256:d40a9f5c5b479f4bffd5b16d803f981a7d1d4bec2c64d979abaa80d4c06a4045
Docker网络简介
在顶层设计中,Docker 网络架构由 3 个主要部分构成:CNM、Libnetwork 和驱动。
- CNM 是设计标准。在 CNM 中,规定了 Docker 网络架构的基础组成要素。
- Libnetwork 是 CNM 的具体实现,并且被 Docker 采用,Libnetwork 通过 Go 语言编写,并实现了 CNM 中列举的核心组件。
- 驱动通过实现特定网络拓扑的方式来拓展该模型的能力
CNM:为容器提供网络功能
抽象来讲,CNM 定义了 3 个基本要素:沙盒(Sandbox)、终端(Endpoint)和网络(Network)。
- 沙盒是一个独立的网络栈。其中包括以太网接口、端口、路由表以及 DNS 配置。
- 终端就是虚拟网络接口。就像普通网络接口一样,终端主要职责是负责创建连接。在 CNM 中,终端负责将沙盒连接到网络。
- 网络是 802.1d 网桥(类似大家熟知的交换机)的软件实现。因此,网络就是需要交互的终端的集合,并且终端之间相互独立
如下图展示了三个组建如何连接
下图展示CNM组建是如何与容器进行关联的--->沙盒被放置在容器内部,为容器提供网络连接
终端与常见的网络适配器类似,这意味着终端只能接入某一个网络。因此,如果容器需要接入到多个网络,就需要多个终端。
下图对前面的内容进行拓展,加上了 Docker 主机。虽然容器 A 和容器 B 运行在同一个主机上,但其网络堆栈在操作系统层面是互相独立的,这一点由沙盒机制保证。
#查看网络
[root@iZ2ze9fp041k3gtah0ztnmZ test]# 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 pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0e:aa:ee brd ff:ff:ff:ff:ff:ff
inet 172.26.145.205/20 brd 172.26.159.255 scope global dynamic eth0
valid_lft 314824635sec preferred_lft 314824635sec
inet6 fe80::216:3eff:fe0e:aaee/64 scope link
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:49:8f:e8:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:49ff:fe8f:e891/64 scope link
valid_lft forever preferred_lft forever
#发现安装docker后多了个docker0的网络
#运行一个容器 发现多了个网络veth2bcb8c0@if90,查看容器内部网络发现也有个eth0@if91成对
#容器内部分配的网络为172.17.0.2/16
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker run -d -it --name mycentos centos /bin/sh
2e4ea81a3346417466cb9f6c391bf5428e4470ad865d54e33dc0378a3c50c8a1
[root@iZ2ze9fp041k3gtah0ztnmZ test]# 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 pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0e:aa:ee brd ff:ff:ff:ff:ff:ff
inet 172.26.145.205/20 brd 172.26.159.255 scope global dynamic eth0
valid_lft 314824579sec preferred_lft 314824579sec
inet6 fe80::216:3eff:fe0e:aaee/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:49:8f:e8:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:49ff:fe8f:e891/64 scope link
valid_lft forever preferred_lft forever
91: veth2bcb8c0@if90: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 0a:19:7e:fd:fc:08 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::819:7eff:fefd:fc08/64 scope link
valid_lft forever preferred_lft forever
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker exec mycentos 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
90: eth0@if91: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
#新建一个mycentos1又会多一个veth4d446b1@if92
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker run -d -it --name mycentos1 centos /bin/sh
f9bc3e2499dea299c9c21423b174d64b4e654eb924e577a73ff22dc239ad71bc
[root@iZ2ze9fp041k3gtah0ztnmZ test]# 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 pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0e:aa:ee brd ff:ff:ff:ff:ff:ff
inet 172.26.145.205/20 brd 172.26.159.255 scope global dynamic eth0
valid_lft 314823775sec preferred_lft 314823775sec
inet6 fe80::216:3eff:fe0e:aaee/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:49:8f:e8:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:49ff:fe8f:e891/64 scope link
valid_lft forever preferred_lft forever
91: veth2bcb8c0@if90: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 0a:19:7e:fd:fc:08 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::819:7eff:fefd:fc08/64 scope link
valid_lft forever preferred_lft forever
93: veth4d446b1@if92: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 92:03:d6:20:b6:9f brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::9003:d6ff:fe20:b69f/64 scope link
valid_lft forever preferred_lft forever
#查看容器内部网络eth0@if93与外方成对分配172.17.0.3/16
[root@iZ2ze9fp041k3gtah0ztnmZ test]# docker exec mycentos1 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
92: eth0@if93: <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
Libnetwork
CNM 是设计规范文档,Libnetwork 是标准的实现。Libnetwork 是开源的,采用 Go 语言编写,它跨平台(Linux 以及 Windows),并且被 Docker 所使用。
在 Docker 早期阶段,网络部分代码都存在于 daemon 当中。daemon 变得臃肿,并且不符合 UNIX 工具模块化设计原则,即既能独立工作,又易于集成到其他项目。
所以,Docker 将该网络部分从 daemon 中拆分,并重构为一个叫作 Libnetwork 的外部类库。
现在,Docker 核心网络架构代码都在 Libnetwork 当中。Libnetwork 实现了 CNM 中定义的全部 3 个组件。此外它还实现了本地服务发现(Service Discovery)、基于 Ingress 的容器负载均衡,以及网络控制层和管理层功能。
驱动
如果说 Libnetwork 实现了控制层和管理层功能,那么驱动就负责实现数据层。比如,网络连通性和隔离性是由驱动来处理的,驱动层实际创建网络对象也是如此,其关系如下图所示。
在 Linux 上包括 Bridge、Overlay 以及 Macvlan,在 Windows 上包括 NAT、Overlay、Transport 以及 L2 Bridge。
第三方也可以编写 Docker 网络驱动。这些驱动叫作远程驱动,例如 Calico、Contiv、Kuryr 以及 Weave。
每个驱动都负责其上所有网络资源的创建和管理。举例说明,一个叫作“prod-fe-cuda”的覆盖网络由 Overlay 驱动所有并管理。这意味着 Overlay 驱动会在创建、管理和删除其上网络资源的时候被调用。
为了满足复杂且不固定的环境需求,Libnetwork 支持同时激活多个网络驱动。这意味着 Docker 环境可以支持一个庞大的异构网络。
Docker单机桥接网络
从名称中可以看到两点,单机意味着该网络只能在单个 Docker 主机上运行,并且只能与所在 Docker 主机上的容器进行连接
下图展示了两个均包含相同本地桥接网络 mynet 的 Docker 主机。虽然网络是相同的,但却是两个独立的网络。这意味着图中容器无法直接进行通信,因为并不在一个网络当中。
每个 Docker 主机都有一个默认的单机桥接网络。 除非读者通过命令行创建容器时指定参数--network,否则默认情况下,新创建的容器都会连接到该网络。
#docker 的网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
#桥接,docker默认的网络
228e778842de bridge bridge local
#与宿主机共享网络
d2d538fcf2a3 host host local
#不配置网络
563da460efd1 none null local
#查看默认网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "228e778842de1d2d5fd1dceecd54e1d785b0fedd03699af08b7466aba2ad9c31",
"Created": "2021-06-28T21:45:53.800156023+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
#之前创建的两个容器都在这分配的网络
"Containers": {
"2e4ea81a3346417466cb9f6c391bf5428e4470ad865d54e33dc0378a3c50c8a1": {
"Name": "mycentos",
"EndpointID": "0571346d1339f5499aa933c325c39b15ada4ef015532fea86ee625ebcce95bd2",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"f9bc3e2499dea299c9c21423b174d64b4e654eb924e577a73ff22dc239ad71bc": {
"Name": "mycentos1",
"EndpointID": "433c4e19b7bcad1f0d986abc85062eca47b379db4200388efb1716776bd84f2b",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
#映射到了内核中为“docker0”的 Linux 网桥
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
Docker 默认“bridge”网络和 Linux 内核中的“docker0”网桥。 如下图所示。
该网桥可以通过主机以太网接口的端口映射进行反向关联
自定义网络
#帮助文档
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# 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.
#清除之前创建的容器
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker rm -f $(docker ps -aq)
#创建mynet网络api
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network create --help
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
--config-from string The network from which to copy the configuration
--config-only Create a configuration only network
#驱动
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment
#创建网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network create -d bridge mynet
6ca40081787bea19bd8f7f1dbaa3898f77cf1b5a82f9de4898573900f38955cd
#出现一个新的网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
228e778842de bridge bridge local
d2d538fcf2a3 host host local
6ca40081787b mynet bridge local
563da460efd1 none null local
#查看本机网络多了一个br-6ca40081787b 分配172.18.0.1/16
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# 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 pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0e:aa:ee brd ff:ff:ff:ff:ff:ff
inet 172.26.145.205/20 brd 172.26.159.255 scope global dynamic eth0
valid_lft 314767585sec preferred_lft 314767585sec
inet6 fe80::216:3eff:fe0e:aaee/64 scope link
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:49:8f:e8:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:49ff:fe8f:e891/64 scope link
valid_lft forever preferred_lft forever
94: br-6ca40081787b: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:c5:7d:84:a9 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-6ca40081787b
valid_lft forever preferred_lft forever
#用linux中的brctl工具来查看linux网桥
#安装工具
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# yum install bridge-utils
#查看网桥发现有两个,docker0与br-6ca40081787b两个网桥,docker0为docker默认网络,第二个为新建的mynet
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# brctl show
bridge name bridge id STP enabled interfaces
br-6ca40081787b 8000.0242c57d84a9 no
docker0 8000.0242498fe891 no
目前主机上的网桥配置如下图
接下来创建一个新的容器并加入mynet
#运行centos容器加入mynet网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker run -d -it --name mycentos --network mynet centos /bin/sh
8bf24774bbcbc1cc831c1a8e113e70de354bf7603077b318d301e4eaa3c21ddd
#查看mynet网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "6ca40081787bea19bd8f7f1dbaa3898f77cf1b5a82f9de4898573900f38955cd",
"Created": "2021-07-02T09:17:14.806988061+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"8bf24774bbcbc1cc831c1a8e113e70de354bf7603077b318d301e4eaa3c21ddd": {
"Name": "mycentos",
"EndpointID": "134f9cb4624510e4174c994c64a26c7e866bb27aedbd6a324fc5fc5f01f4f915",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
#或者用如下方式查看
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network inspect mynet --format '{{json .Containers}}'
{
"8bf24774bbcbc1cc831c1a8e113e70de354bf7603077b318d301e4eaa3c21ddd": {
"Name": "mycentos",
"EndpointID": "134f9cb4624510e4174c994c64a26c7e866bb27aedbd6a324fc5fc5f01f4f915",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
}
#查看容器内网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker exec mycentos 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
97: eth0@if98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
#虚拟机上网络
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# 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 pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0e:aa:ee brd ff:ff:ff:ff:ff:ff
inet 172.26.145.205/20 brd 172.26.159.255 scope global dynamic eth0
valid_lft 314762679sec preferred_lft 314762679sec
inet6 fe80::216:3eff:fe0e:aaee/64 scope link
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:49:8f:e8:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:49ff:fe8f:e891/64 scope link
valid_lft forever preferred_lft forever
94: br-6ca40081787b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c5:7d:84:a9 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-6ca40081787b
valid_lft forever preferred_lft forever
inet6 fe80::42:c5ff:fe7d:84a9/64 scope link
valid_lft forever preferred_lft forever
98: veth4bef9d1@if97: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-6ca40081787b state UP group default
link/ether 22:96:bf:8b:33:97 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::2096:bfff:fe8b:3397/64 scope link
valid_lft forever preferred_lft forever
从Containers中可以看到8bf24774bbcbc1cc831c1a8e113e70de354bf7603077b318d301e4eaa3c21ddd
为容器的id,Name为mycentos,这说明mycenyos容器已经位于桥接网络mynet
之上了
#再次查看网桥,可以看到mycentos的网络接口连接到了br-6ca40081787b网桥
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# brctl show
bridge name bridge id STP enabled interfaces
br-6ca40081787b 8000.0242c57d84a9 no veth4bef9d1
docker0 8000.0242498fe891 no
如下图所示
如果在相同网络中继续接入新的容器是可以通过mycentos
的容器名称来ping通的,这是因为新容器都注册到了指定的Docker DNS服务,所以相同网络的容器可以解析其他容器的名称
提示:linux上默认的Bridge网络不持之通过Docker DNS服务进行域名解析,自定义桥接网络可以
来~上才艺
之前创建了mycentos 连接了mynet,所以现在创建mycentos1连接mynet ,创建mycentos2,mycentos3连接默认的Bridge
#docker run -d -it --name mycentos1 --network mynet centos /bin/sh
#docker run -d -it --name mycentos2 centos /bin/sh
#docker run -d -it --name mycentos3 centos /bin/sh
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
31cc30a4fdc2 centos "/bin/sh" 2 seconds ago Up 1 second mycentos3
3b7393ad6da6 centos "/bin/sh" 7 seconds ago Up 7 seconds mycentos2
531dbbdf5960 centos "/bin/sh" 13 seconds ago Up 12 seconds mycentos1
8bf24774bbcb centos "/bin/sh" 3 hours ago Up 3 hours mycentos
#进入mycentos,ping mycentos1可以通,名称也可以,ping mycentos2、mycentos3均无法连通
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker exec -it mycentos /bin/bash
[root@8bf24774bbcb /]# ping mycentos1
PING mycentos1 (172.18.0.3) 56(84) bytes of data.
64 bytes from mycentos1.mynet (172.18.0.3): icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from mycentos1.mynet (172.18.0.3): icmp_seq=2 ttl=64 time=0.069 ms
64 bytes from mycentos1.mynet (172.18.0.3): icmp_seq=3 ttl=64 time=0.077 ms
64 bytes from mycentos1.mynet (172.18.0.3): icmp_seq=4 ttl=64 time=0.069 ms
64 bytes from mycentos1.mynet (172.18.0.3): icmp_seq=5 ttl=64 time=0.073 ms
[root@8bf24774bbcb /]# ping mycentos2
ping: mycentos2: Name or service not known
[root@8bf24774bbcb /]# ping mycentos3
ping: mycentos3: Name or service not known
#进入mycentos2中 ping mycentos3发现ping不通,查看mycentos3 ip 直接ping ip发现可以ping通
[root@iZ2ze9fp041k3gtah0ztnmZ /]# docker exec mycentos3 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
103: eth0@if104: <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@iZ2ze9fp041k3gtah0ztnmZ ~]# docker exec -it mycentos2 /bin/bash
[root@3b7393ad6da6 /]# ping mycentos3
ping: mycentos3: Name or service not known
[root@3b7393ad6da6 /]# ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.092 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.072 ms
总结:默认的bridge可以通过ip连通,但是不能通过容器名称连通,但是自定义网络两者都可以
原因: 这是因为 mycentos容器运行了一个本地 DNS 解析器,该解析器将请求转发到了 Docker 内部 DNS 服务器当中 .
DNS 服务器中记录了容器启动时通过 --name 或者 --net-alias 参数指定的名称与容器之间的映射关系
但是!!如果不同网络之间想通信咋办!!!!
网络之间的连通
通过docker network connect来连通
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
#其中mycentos mycentos1 在mynet这个网络中, mycentos2 mycentos3在docker默认的网络中
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
31cc30a4fdc2 centos "/bin/sh" 27 minutes ago Up 27 minutes mycentos3
3b7393ad6da6 centos "/bin/sh" 27 minutes ago Up 27 minutes mycentos2
531dbbdf5960 centos "/bin/sh" 27 minutes ago Up 27 minutes mycentos1
8bf24774bbcb centos "/bin/sh" 3 hours ago Up 3 hours mycentos
#让mycentos2加入到mynet中
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker network connect mynet mycentos2
#发现ping mycentos2可以ping通,但是mycentos3不行
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker exec -it mycentos ping mycentos3
ping: mycentos3: Name or service not known
[root@iZ2ze9fp041k3gtah0ztnmZ ~]# docker exec -it mycentos ping mycentos2
PING mycentos2 (172.18.0.4) 56(84) bytes of data.
64 bytes from mycentos2.mynet (172.18.0.4): icmp_seq=1 ttl=64 time=0.074 ms
64 bytes from mycentos2.mynet (172.18.0.4): icmp_seq=2 ttl=64 time=0.075 ms
^C
本文来自博客园,作者:zhao56,转载请注明原文链接:https://www.cnblogs.com/zhao56/p/14963007.html