一、数据卷管理

用户在使用 Docker 的过程中,势必需要查看容器内应用产生的数据,或者 需要将容器内数据进行备份,甚至多个容器之间进行数据共享,这必然会涉及 到容器的数据管理

(1)Data Volume (数据卷)

(2)Data Volume Dontainers --- 数据卷容器

Data Volume ---数据卷

Data Volume 本质上是 Docker Host 文件系统中的目录或文件,使用类似 与 Linux 下对目录或者文件进行 mount 操作。数据卷可以在容器之间共享 和重用,对数据卷的更改会立马生效,对数据卷的更新不会影响镜像,卷会一 直存在,直到没有容器使用

Data Volume 有以下特点:

a)Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)。

b)容器可以读写 volume 中的数据。

c)volume 数据可以被永久的保存,即使使用它的容器已经销毁。

Data Volume的使用:

通过-v 参数格式为 <host path>:<container path>

a)利用 nginx的镜像运行一个容器,并在容器内创建一个数据卷挂载到容器 的 /web 目录上

[root@localhost ~]# docker run -dti -v /web nginx:latest /bin/bash
d4e002045c2bd022a826dbd2805b121dd43c41443d86eeb6515f616bf52c8549

b)运行一个容器,本地创建/date目录挂载到容器的/var/log/目录上

docker run -dti -v /data:/var/log nginx /bin/bash

docker run -dti -v /data:/var/log centos:latest /bin/bash
25773c239584614e301644db01b7275377887c9bbefe8c54cf5cd6be2917ab54

DataVolumeDontainers --- 数据卷容器

如果用户需要在容器之间共享一些持续更新的数据,最简单的方法就是使用数据 卷容器,其实数据卷容器就是一个普通的容器,只不过是专门用它提供数据卷供 其他容器挂载使用

Data Volume Dontainers使用:

a)创建一个名为 dbdata 的数据卷,并在其中创建一个数据卷挂载到 /dbdata

docker run -dti -v /dbdata --name dbser centos:7.0

--name 参数为给容器指定名字为dbser方便记忆

[root@localhost ~]# docker run -dti -v /dbdata --name dbser centos:latest
82b264b29e56700afebaa7acec8c69309964f27dafd271454048b8b7a113720e

b)其他容器使用--volume-from 去挂载dbdata容器中的/dbdata数据卷  eg :创建 db1&db2 两个容器, 并挂载 /dbdata 数据卷到本地

docker run -dti --volumes-from dbserver --name db1 centos:7.0

docker run -dti --volumes-from dbserver --name db2 centos:7.0

此时,容器 db1 和 db2 同时挂载了同一个数据卷到本地相同 /dbdata 目录。三个容器任何一个目录下的写入,都可以时时同步到另外两个

[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aff1e64343bf centos:latest "/bin/bash" minutes ago Up minutes db2
893450737ade centos:latest "/bin/bash" minutes ago Up minutes db1
82b264b29e56 centos:latest "/bin/bash" minutes ago Up minutes dbser 在db2中创建jam文件,写入0330
[root@localhost ~]# docker exec -it aff1e64343bf /bin/bash
[root@aff1e64343bf dbdata]# echo "" >jam
[root@aff1e64343bf dbdata]# cat jam 在db1中查看是否存在jam文件
[root@localhost ~]# docker exec -it 893450737ade /bin/bash
[root@893450737ade /]# cd dbdata/
[root@893450737ade dbdata]# ls
jam
[root@893450737ade dbdata]# cat jam 在dbser中查看jam文件是否存在
[root@localhost ~]# docker exec -it 82b264b29e56 /bin/bash
[root@82b264b29e56 /]# ls
dbdata
[root@82b264b29e56 /]# cd dbdata/
[root@82b264b29e56 dbdata]# ls
jam
[root@82b264b29e56 dbdata]# cat jam

二、docker网络介绍

大量的互联网应用服务需要多个服务组件,这往往需要多个容器之间通过网络 通信进行相互配合

docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络 docker 目前提供了映射容器端口到宿主主机和容器互联机制来为容器提供网络服务,在启动容器的时候,如果不指定参数,在容器外部是没有办法通过网络来访问容器内部的网络应用和服务的

docker 安装时会自动在host上创建三个网络,我们查看一下docker网络:

docker network ls

[root@localhost ~]# docker network list
NETWORK ID NAME DRIVER SCOPE
460b8a01b798 bridge bridge local
d4ae07a1372e host host local
63bead0d7ffc none null local

二、docker--none网络

none 网络就是什么都没有的网络。挂在这个网络下的容器除了lo,没有其他任何网卡。容器创建时,可以通过 --network=none 指定使用 none 网络。

none 网络的应用

封闭的网络意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。

比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密 码被窃取。

三、docker--host网络

连接到 host 网络的容器,共享 docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过 --network=host 指定使用 host 网络

在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的。host 网络的使用场景又是什么呢?

直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率 有较高要求,就可以选择 host 网络

当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。

Docker host 的另一个用途是让容器可以直接配置 host 网路。比如某些跨 host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进 行配置,比如管理 iptables

四、docker--bridge网络

docker 安装时会创建一个 命名为 docker0 的 linux bridge。如果不指定 --network,创建的容器默认都会挂到 docker0 上

当前 docker0 上没有任何其他网络设备,我们创建一个容器看看有什么变化

[root@localhost docker]# brctl show
bridge name bridge id STP enabled interfaces
docker0 .0242ec43479a no vethb2a3b02  #以密钥对的形式挂到docker0

一个新的网络接口 vethb2a3b02 被挂到了 docker0 上,vethb2a3b02 就是 新创建容器的虚拟网卡。

进入刚才运行的容器查看网络,容器有一个网卡 eth0@if9

[root@localhost docker]# docker exec -it 5449774b40e5 /bin/sh
/ # ip a
: lo: <LOOPBACK,UP,LOWER_UP> mtu qdisc noqueue qlen
link/loopback ::::: brd :::::
inet 127.0.0.1/ scope host lo
valid_lft forever preferred_lft forever
: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu qdisc noqueue
link/ether ::ac::: brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/ brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

实际上 eth0@if9 和 vethb2a3b02是一对 veth pair

veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线 连接起来的一对网卡,网卡的一头(eth0@if9)在容器中,另一头 (vethb2a3b02)挂在网桥 docker0 上,其效果就是将 eth0@if9 也挂在了 docker0 上

可以看到,eth0@if9 已经配置了 IP 172.17.0.3,为什么是这个网段呢?

看一下 bridge 网络的配置信息:

docker network inspect bridge

Config": [
{
"Subnet": "172.17.0.0/16"

可以看到bridge 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1, 在docker0上

容器创建时,docker 会自动从 172.17.0.0/16 中分配一个 IP,这里 16 位的 掩码保证有足够多的 IP 可以供容器使用,(主机位是16位)

五、创建 user--defined网络

我们可通过 bridge 驱动创建类似前面默认的 bridge 网络

1)利用bridge驱动创建名为my-net2网桥(docker会自动分配网段):

docker network create --driver bridge my-net2

可以看到新创建的网络也自动被分配了子网ip。

[root@localhost ~]# docker network create --driver bridge net2
2edd5371dd2fbcb32c9c2b2a9420cb806e406edc011379c7c7c70f53d6c74585
[root@localhost ~]# docker network list
NETWORK ID NAME DRIVER SCOPE
460b8a01b798 bridge bridge local
d4ae07a1372e host host local
2edd5371dd2f net2 bridge local
63bead0d7ffc none null local
[root@localhost ~]# docker network inspect net2
。。。
Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"

删除网桥:brctl delbr 网桥名

(4)利用bridge驱动创建名为net3网桥(user-defined网段及网关)

[root@localhost ~]# docker network create --driver bridge --subnet 172.18.2.0/ --gateway 172.18.2.1 net3

报错:Error response from daemon: Pool overlaps with other one on this address space

这是因为新创建的网络自定义的子网ip与已有的网络子网ip冲突。

只需要重新定义子网ip段就行

[root@localhost ~]# docker network create --driver bridge --subnet 192.18.2.0/ --gateway 192.18.2.1 net3
3f028deb3aa0b37ee69e3a04a0edb14c00b1933a415c2d0961a9eec7b4b2ecb1

重新查看网络配置信息:

[root@localhost ~]# docker network inspect net3
"Config": [
{
"Subnet": "192.18.2.0/24",
"Gateway": "192.18.2.1"

可以看到自定义的网络配置成功。

(5)启动容器使用新建的net3网络

docker run -itd --network=net3(网络名) 镜像名 /bin/sh(环境名)

[root@localhost ~]# docker run  -itd --network=net3 busybox:latest /bin/sh
632e2d8245ade9e0a8ee9cf13d7105f19a2d28728e3d38b65423eb34ef38ac4c
[root@localhost ~]# docker exec -it 632e2d8245ad /bin/sh
/ # ip a
: lo: <LOOPBACK,UP,LOWER_UP> mtu qdisc noqueue qlen
link/loopback ::::: brd :::::
inet 127.0.0.1/ scope host lo
valid_lft forever preferred_lft forever
: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu qdisc noqueue
link/ether ::c0::: brd ff:ff:ff:ff:ff:ff
inet 192.18.2.3/24 brd 192.18.2.255 scope global eth0
valid_lft forever preferred_lft forever   

可以看到使用新建网络net3启动的容器会自动分配一个net3子网段内的ip。

6)启动容器使用net3网络并指定ip(只有使用 --subnet 创建的网络才 能指定静态 IP,如果是docker自动分配的网段不可以指定ip)

[root@localhost ~]# docker run  -itd --network=net3 --ip 192.18.2.4 busybox:latest /bin/sh

当然,这个指定的ip也要在net3的子网范围内。

[root@localhost dbdate]# docker run -itd --network=bridge --ip 172.17.0.3 busybox:latest /bin/sh

docker: Error response from daemon: user specified IP address is supported on user defined networks only.

如上的报错就是因为运行容器时选中的网络不是使用--subnet定义的网络,所以不能指定ip。

(7)让已启动不同vlan的ningx容器,可以连接到net2(其实在nigx中新建了my-net2的网卡)

docker run -itd --network=net3 busybox:latest  /bin/sh

docker network connect net2   容器名(id)

[root@localhost ~]# docker run  -itd --network=net3 busybox:latest /bin/bash
e42da501655faa6e2067a6d9ee6c49016ec49bb0dc750b78a82f2540751840c5
[root@localhost docker]# docker network connect net2 0d3e78d4298c
[root@localhost docker]# docker exec -it 0d3e78d4298c /bin/sh
/ # ip a
: lo: <LOOPBACK,UP,LOWER_UP> mtu qdisc noqueue qlen
link/loopback ::::: brd :::::
inet 127.0.0.1/ scope host lo
valid_lft forever preferred_lft forever
: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu qdisc noqueue
link/ether ::c0::: brd ff:ff:ff:ff:ff:ff
inet 192.18.2.4/ brd 192.18.2.255 scope global eth0
valid_lft forever preferred_lft forever
: eth1@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu qdisc noqueue
link/ether ::ac::: brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/ brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever

可以看到此容器同时拥有了net2和net3的子网ip。

(8)使用--name指定启动容器名字,可以使用docker自带DNS通信,但只能 工作在user-defined 网络,默认的 bridge 网络是无法使用 DNS 的。

root@localhost docker]# docker run -itd --network=net3 --name=b1 busybox
203fc1c4fb19dc1212da8f97dc8654b5b928f9ab1406bed4c39a31f76b686d4c
[root@localhost docker]# docker run -itd --network=net3 --name=b2 busybox
5dfabb01d5d718429e382acb4115a1d8c4c7e2e89c302b899bcf5923be1df527
[root@localhost docker]# docker exec -it b1 /bin/sh
/ # ping b2
PING b2 (192.18.2.7): data bytes
bytes from 192.18.2.7: seq= ttl= time=0.417 ms
bytes from 192.18.2.7: seq= ttl= time=0.110 ms
bytes from 192.18.2.7: seq= ttl= time=0.099 ms

在创建容器时使用相同网络,并且给容器命名的话

意思是给两个容器之间做了域名解析和配置相同网段,所以能ping通

(9)容器之间的网络互联

  a). 首先创建一个 db 容器

[root@localhost ~]# docker run -itd --name db busybox

b). 创一个 web 容器,并使其连接到 db

[root@localhost ~]# docker run -itd --name web --link db:dblink busybox /bin/sh

-link db:dblink 实际是连接对端的名字和这个链接的名字,也就是和 db 容器 建立一个叫做 dblink 的链接

c).查看链接的情况

docker ps -a

[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
141e370eea76 busybox "/bin/sh" seconds ago Up seconds web
9f89a1945f6b busybox "sh" seconds ago Up seconds db

d)进入web容器,使用 ping 命令来测试网络链接的情况

[root@localhost ~]# docker exec -it 141e370eea76 /bin/sh/ # ping db
PING db (172.17.0.4): data bytes
bytes from 172.17.0.4: seq= ttl= time=0.445 ms

发现可以ping通,连接有效

    e)尝试进入db容易,使用ping命令测试

[root@localhost docker]# docker exec -it 9f89a1945f6b /bin/sh
/ # ping web
ping: bad address 'web'

发现无法ping通,因此可以推断出容器之间的网络连通是单向的,即哪个容器建立了连接,哪个容易才能连接到另一个容器,反之不行。

(10)容器端口映射 在启动容器的时候,如果不指定参数,在容器外部是没有办法通过网络来访问容 器内部的网络应用和服务的

当容器需要通信时,我们可以使用 -P (大) &&-p (小)来指定端口映射

-P : Docker 会随机映射一个 49000 ~ 49900 的端口到容器内部开放的网络端口

p :则可以指定要映射的端口,并且在一个指定的端口上只可以绑定一个容器。

支持的格式有

iP : HostPort  : ContainerPort

IP : : ContainerPort

IP : HostPort :

如果不指定就随机

查看映射

docker port 容器名

拓展:用脚本删除所有容器

[root@localhost docker]# for id in `docker ps -a | grep a | awk -F " +" '{print $1}'` ;do docker rm -f $id ;done

a)映射所有接口地址,此时绑定本地所有接口上的 5000 到容器的 5000 接口, 访问任何一个本地接口的 5000 ,都会直接访问到容器内部

docker run -dti -p 5000:5000 nginx /bin/bash

b)多次使用可以实现多个接口的映射

[root@localhost docker]# docker run -itd -p : -p : nginx /bin/bash

查看容器信息,发现新容器的的22,23端口都映射到了宿主机的5000和5001端口。

[root@localhost docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ea3b9412d96d nginx "/bin/bash" seconds ago Up seconds /tcp, 0.0.0.0:5000->22/tcp, 0.0.0.0:5001->23/tcp hardcore_darwin

c)映射到指定地址的指定接口 此时会绑定本地 192.168.4.169 接口上的 5000 到容器的80 接口

docker run -dti -p 192.168.253.9:5000:80 nginx /bin/bash

[root@localhost docker]#  docker run -dti -p 192.168.253.9:: nginx /bin/bash
[root@localhost docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
132746ef0996 nginx "/bin/bash" About a minute ago Up About a minute 192.168.253.9:5000->80/tcp practical_lichterman

d) 映射到指定地址的任意接口 此时会绑定本地 192.168.4.169 接口上的任意一个接口到容器的 5000 接口

docker run -dti -p 192.168.4.169::5000 nginx /bin/bash

e) 使用接口标记来指定接口的类型

docker run -dti -p 192.168.4.169::5000/UDP nginx /bin/bash

指定接口位udp类型

(11)实验:通过端口映射实现访问本地的 IP:PORT 可以访问到容器内的 web

a)将容器80端口映射到主机8080端口,注意末尾不要加环境变量

docker run -itd -p 80:80 --name nginx nginx:latest    

b) 查看刚运行docker

docker ps -a

[root@localhost docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0382dfbbd606 nginx:latest "nginx -g 'daemon of…" seconds ago Up seconds 0.0.0.0:->/tcp nginx

c) 进入容器

docker exec -it nginx /bin/sh,并在 容器内部编辑网页文件 index.html

# cd /usr/share/nginx/
# ls
html
# cd html
# ls
50x.html index.html
# echo '' > index.html

e)到宿主机上打开浏览器输入 IP:PORT 访问验证

http://192.168.253.9:80

一些报错:

1)[root@localhost /]# docker run -itd --name busy busybox:latest /bin/bash
e105747eb16e52da2637379d75e221f46812955c929696234abbf1321fd56da3
docker: Error stat /bin/bash: no such file or directory": unknown

这是因为环境变量不可用,换成/bin/sh或者不加环境变量

2)[root@localhost /]# docker run -itd --name busy busybox:latest /bin/sh
docker: Error response from daemon: Conflict. The container name "/busy" is already in use by container "e105747eb16e52da2637379d75e221f46812955c929696234abbf1321fd56da3". You have to remove (or rename) that container to be able to reuse that name.

这是因为容器名已经存在,需要删除已经存在的容器名

3)[root@localhost /]# docker network create --driver bridge my-net2
Error networks have overlapping IPv4

这是因为网桥冲突,删除冲突的网桥。
brctl delbr 网桥
systemctl restart docker

4)error:executable file not found in $PATH": unknown
这是因为命令的顺序错误

5)root@localhost /]# docker run -it --network=my-net3 --ip 192.168.253.13 busybox:latest /bin/sh
docker: Error response from daemon: Invalid address 192.168.253.13: It does not belong to any of this network's subnets.
这是因为自定义的ip不在可使用ip的范围内。

6)Error No chain/target/match by that name.
这是因为没有重启docker,或者关闭firewalld和iptables(不建议)

7)docker: Error response from daemon: driver failed programming external connectivity on endpoint vibrant_kepler (0838e9c00e2ffcec24bbd333000cc4de9bd0fa2420c1c330ddb77dfd9d1d534f): Bind for 192.168.253.9:5000 failed: port is already allocated.

这是因为宿主机的端口已经被别的服务占用。

8)宿主机映射到容器之后,使用宿主机ip:port来访问容器的httpd或者nginx服务

报错连接被拒绝

这是因为端口映射的时候加入了环境变量,去掉环境变量即可。

网络排查命令:

iptables -t nat -L

ip r

tcpdump -i docker0 -n icmp

tcpdump -i eth0 -n icmp

 
 

docker进阶——数据管理与网络的更多相关文章

  1. Docker进阶一:网络篇

    理解Docker0 查看本地ip ip addr [root@VM-0-6-centos ~]# ip addr #本机回环地址 1: lo: <LOOPBACK,UP,LOWER_UP> ...

  2. 【云计算】Docker云平台—Docker进阶

    Docker云平台系列共三讲,此为第二讲:Docker进阶 参考资料: 五个Docker监控工具的对比:http://www.open-open.com/lib/view/open1433897177 ...

  3. Docker进阶

    Docker进阶 Docker容器命名与重命名 命名 docker run -it -p 80:80 --name containerName img:tag /bin/bash 重命名 docker ...

  4. Docker的数据管理(上)

    Docker的数据管理(上) 1.管理docker容器中数据 2.容器互联(使用centos镜像) 1.管理docker容器中数据: 管理Docker 容器中数据主要有两种方式:数据卷(Data Vo ...

  5. Docker的数据管理、网络通信和dockerfile

    Docker的数据管理.网络通信和dockerfile 目录 Docker的数据管理.网络通信和dockerfile 一.Docker的数据管理 1. 数据卷 1.1 数据卷定义 1.2 数据卷配置 ...

  6. Docker的4种网络模式

    我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式: · host模式,使用--net=host指定. · container ...

  7. Docker实践(2)—虚拟网络

    1 docker(container)的虚拟网络 docker的虚拟网络结构: host创建一个虚拟bridge,每个container对应一个虚拟网络设备(TAP设备),与bridge一起构成一个虚 ...

  8. docker学习3-虚拟网络模式

    一.虚拟机网络模式 在理解docker网络隔离前,先看下之前虚拟机里对网络的处理,VirtualBox中有4中网络连接方式: NAT Bridged Adapter Internal Host-onl ...

  9. Docker进阶之六:网络管理

    一.默认网络 安装Docker时会自动创建三个网络:docker network ls 列出网络: # docker network ls NETWORK ID NAME DRIVER SCOPE 5 ...

随机推荐

  1. 【学习总结】快速上手Linux玩转典型应用-第2章-linux简介

    课程目录链接 快速上手Linux玩转典型应用-目录 目录 1. 什么是Linux 2. Linux能够做什么事情 3. Linux的学习方法 4. 忘掉Windows的所有东西 1. 什么是Linux ...

  2. MySQL索引的分类、结构、使用场景

    MySQL索引分类 1.主键索引:设定为主键后数据库会自动建立索引,innodb为聚簇索引 语法: 随表一起建索引: CREATE TABLE customer (id INT(10) UNSIGNE ...

  3. 欢迎使用Markdown编辑器

    #欢迎使用Markdown编辑器 你好 ,这是我第一次使用Markdown编辑器所展示的页面.如果你想学习如何使用Markdown编辑器,可以仔细阅读这篇文章,了解一下Markdown的基本语法知识. ...

  4. mysql分组函数及其用例

    功能:用作统计使用,又称为聚合函数或统计函数或组函数 分类:sum 求和.avg 平均值.max 最大值 .min 最小值 .count 计算个数 特点: 1.sum.avg一般用于处理数值型,max ...

  5. python生成图片验证码

    import PIL from PIL import Image from PIL import ImageDraw,ImageFont import random def get_random_co ...

  6. 04-A的LU分解

    一.矩阵$AB$的逆 $(AB)^{-1}=B^{-1}A^{-1}$,顺序正好相反 二.$A=LU$ 如矩阵: $\left[\begin{array}{ll}{2} & {1} \\ {8 ...

  7. Python自动化学习--批量执行.py用例

    这段时间在摸索自动化,学到执行测试用例的时候发现,执行单用例的时候很简单,如果想多条用例执行的话就没那么简单了,经过几番查找,找到如下方法: unittest模块中的TestLoader类有一个dis ...

  8. css3-background clip 和background origin

    1.background-origin background-origin 里面有3个参数 : border-box | padding-box | content-box; border-box,p ...

  9. Java 内存结构之虚拟机栈

    2.虚拟机栈 定义:虚拟机栈(Java Virtual Machine Stacks)就是每个线程运行需要的内存空间,栈由一个一个的栈帧(Frame)组成,栈帧就是每个方法运行时需要的内存(方法的参数 ...

  10. 【UOJ#349】[WC2018] 即时战略

    题目链接 题意 一开始已知一号点. 每次可以选定一个已知点和一个未知点,然后交互库会返回从已知点出发到达未知点路径上的第二个点. 要求在有限步之内知道每一个点. 次数要求: 链的情况要求 \(O(n) ...