1 docker
1.1两个容器间的挂载
root@xiaoni-01:/home# docker run -it --name centos03 --volumes-from centos02 xiaoni61/centos
root@xiaoni-01:/home/mysql/data/test# docker run -d -p 3311:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
2 dockerfile
步骤:
- 编写一个dockerfile 文件
- docker build构建一个镜像
- docker run运行
- docker push 发布镜像(dockerhub 或者阿里云镜像仓库)
例如官方编写的dockerfile(centos)
dockerhub中点击具体版本跳转到github中dockerfile地址
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20201113" \
org.opencontainers.image.title="CentOS Base Image" \
org.opencontainers.image.vendor="CentOS" \
org.opencontainers.image.licenses="GPL-2.0-only" \
org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
CMD ["/bin/bash"]
2.1 dockerfile构建
指令:
FROM # 基础镜像
MAINTAINAER # 镜像是谁写的 姓名+邮箱
RUN # 需要运行时命令
ADD # 比如假如tomcat镜像等等,添加内容
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 保留端口配置(对外)
CMD # 指定这个容器要运行的命令,但只有一个会生效
ENTRYPOINT # 指定这个容器要运行的命令,可以追加
ONBUILD # 当构建一个dockerfile文件中运行onbuild,进行触发
COPY # 类似ADD 把文件拷贝到镜像中
ENV # 构建的时候配置环境变量
2.2 实战测试
Docker hub中 镜像基本上都是从FROM scratch开始
现在动手写一个
2.2.1 编写Dockerfile的文件
FROM centos:centos7 # 基于centos
# !!!如果这里写centos会默认下载最新版,但最新版的源于2021年12月31日停止服务。
MAINTAINER xiaioni61<zhilong_li61@163.com>
ENV MYPATH /user/local # 定义
WORKDIR $MYPATH # 确定初始工作目录
RUN yum -y install vim # 下载vim以及网络工具
RUN yum -y install net-tools
EXPOSE 2000 # 暴露端口
CMD echo $MYPATH # 打印消息
CMD echo "------end------"
CMD /bin/bash # 进入bash命令行
2.2.2 通过该文件构建镜像
docker build -f mydockerfile-centos -t mycentos:0.1 . # -f 指定文件 -t 确定镜像名字以及版本号 别忘了最后的点
!!!运行过程中若遇到问题,可以参照我的另一篇文章进行解决
dockerfile build中的种种问题
2.2.3 测试运行
进入后就直接进入工作路径:/user/local
也可以看到ifconfig已经可以使用。
可以查看该镜像的历史:
2.3 CMD和ENTRYPOINT
2.3.1 CMD
FROM centos:centos7
CMD ["ls","-a"]
构建运行:
若我想追加一个命令:
则产生报错(因为CMD命令只能执行最后一个!)
2.3.2 ENTRYPOINT
FROM centos:centos7
CMD ["ls","-a"]
可以接连使用命令
2.4 实战:tomcat镜像
准备好JDK和tomcat
Dockerfile:
FROM centos:centos7
MAINTAINER xiaoni61<zhilong_li61@163.com>
COPY README.txt /user/local/README.txt
ADD apache-tomcat-8.5.76.tar.gz /usr/local
ADD jdk-8u321-linux-aarch64.tar.gz /usr/local
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_321
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.76
ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.76
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /user/local/apache-tomcat-9.0.60/bin/startup.sh && tail -F /url/local/apache-tomcat-9.0.60/bin/logs/cataline.out
构建:
因为Dockerfile是官方的名字 则不需要-f参数
root@xiaoni-01:/home/mytomcat# docker build -t diytomcat .
进行挂载:
docker run -d -p 9090:8080 --name xiaonitomcat -v /home/xiaoni/build/tomcat/test:/usr/local/apache-tomcat-9.0.60/webapps/test -v /home/xiaoni/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.60/logs diytomcat
但是我发现,生成容器,但是当用docker exec进入时,却不断的告诉我
Error response from daemon: Container dffcb8aa2d6ae10bf7eeeecb969ff06b4cf782908cce9fd3061a883334efb3fe is not running
没有运行。。。此时我又开始了我不断的debug之路。
- 先是重新下载了符合unbantu系统的jdk和tomcat版本,重新尝试后依然不行
- 再通过 docker ps -a 和docker logs发现问题
root@xiaoni-01:/home/xiaoni/build/tomcat# docker logs dffcb8aa2d6ae
/bin/sh: /user/local/apache-tomcat-9.0.60/bin/startup.sh: No such file or directory
参考了tomcat启动问题,在dockerfile中添加了他多给出的指令,暂时依然不行。
现在我换了一个思路,去掉一切可能出错的部分,从头再来!
显示DockerFile的编写,去掉的最后的CMD部分:
FROM centos:centos7
MAINTAINER xiaoni61<zhilong_li61@163.com>
COPY README.txt /user/local/README.txt
ADD apache-tomcat-8.5.76.tar.gz /usr/local
ADD jdk-8u321-linux-aarch64.tar.gz /usr/local
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_321
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.76
ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.76
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
run时不进行挂载,直接
docker run -d -p 9090:8080 --name xiaonitomcat diytomcat
依然不行。。
看了Docker容器一起动就退出的解决方案使用方案二
docker run -dit -p 9090:8080 --name xiaonitomcat -v /home/xiaoni/build/tomcat/test:/usr/local/apache-tomcat-9.0.60/webapps/test -v /home/xiaoni/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.60/logs diytomcat
ok!!成功!!可以生成容器并进入
再手动实行原来dockerfile中CMD的内容,ok!路径正确,运行start.sh。ok!tomcat运行成功
这就很奇怪了,明明都是一样的,这样才运行成功。
又重新把原dockerfile中的CMD指令加上,依然ok
通过docker inspect指令:
看到也成功的挂载了!说明这个问题是第一次run是参量的设置问题
挂一个运行起来的tomcat:
2.5 发布自己的镜像
2.5.1 dockerhub
在dockerhub注册,并确定能登录,查看docker login指令
root@xiaoni-01:~# docker login --help
Usage: docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
登录完可以提交即可
先进行改名:
例:我的账户名:aaaa
我的镜像:docker_name
docker tag docker_name aaaa/docker_name
在本例中使用
docker tag diytomcat xiaoni61/diytomcat //改名
显示这样即可,到结束
root@xiaoni-01:~# docker push xiaoni61/diytomcat
Using default tag: latest
The push refers to repository [docker.io/xiaoni61/diytomcat]
8a7699585e0e: Pushing [> ] 537.6kB/215.6MB
73b7c44c4c0a: Pushing [> ] 1.061MB/315.9MB
e0794b7f2680: Pushing [=> ] 522.8kB/16.02MB
2719350fab01: Pushed
174f56854903: Pushing [> ] 527.9kB/203.9MB
2.5.2 阿里云镜像服务
登录阿里云
找到容器镜像服务
创建命名空间
1.
2.
3.
4.看操作说明:
2.6 docker和dockerfile小结
3 docker网络
3.1 理解docker0
3.1.1 操作
先清空所有环境
docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)
测试
ip addr
创建一个容器,
容器内:
主机ping容器:
root@xiaoni-01:~# 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.037 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.028 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.045 ms
64 bytes from 172.17.0.3: icmp_seq=5 ttl=64 time=0.046 ms
64 bytes from 172.17.0.3: icmp_seq=6 ttl=64 time=0.039 ms
64 bytes from 172.17.0.3: icmp_seq=7 ttl=64 time=0.047 ms
3.1.2 原理
我们每次启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡docker0桥接模式,使用的技术是evth-pair技术。
再在主机上测试 ip addr
发现:
可以发现,在主机和容器内分别使用ip addr,出现一对网卡,这里显示为133-132。
我们发现这个容器所带来的网卡是一对一对的,veth-pair就是一对的虚拟设备接口,他们成对出现,一段连着协议,一段彼此相连。相当于一个桥梁
容器和容器之间也可以相互通信
并不是直连,通过主机(路由器)转发
重要删除容器,对应的虚拟网卡也会消失。
3.2 --link
场景:编写一个微服务,database url=ip,项目不重启,数据库ip换了,我们希望可以处理这个问题,是否可以用名字来访问容器。
[root@9559c91ca761 /]# ping centos03
ping: centos03: Name or service not known //在centos02中不能直接pingcentos03
root@xiaoni-01:~# docker run -dit -P --name centos04 --link centos03 centos:7 //进行连接
581451397c27245af4152c7c27f96bc5bf15c097629c424f7fccbe1d9de922ac
root@xiaoni-01:~# docker exec -it 581451397c272 ping centos03 //直接ping
PING centos03 (172.17.0.2) 56(84) bytes of data.
64 bytes from centos03 (172.17.0.2): icmp_seq=1 ttl=64 time=0.076 ms
64 bytes from centos03 (172.17.0.2): icmp_seq=2 ttl=64 time=0.056 ms
64 bytes from centos03 (172.17.0.2): icmp_seq=3 ttl=64 time=0.063 ms
64 bytes from centos03 (172.17.0.2): icmp_seq=4 ttl=64 time=0.067 ms
64 bytes from centos03 (172.17.0.2): icmp_seq=5 ttl=64 time=0.058 ms
64 bytes from centos03 (172.17.0.2): icmp_seq=6 ttl=64 time=0.050 ms
64 bytes from centos03 (172.17.0.2): icmp_seq=7 ttl=64 time=0.057 ms
但是centos03没办法平通centos04。
探究:
查看bridge,也就是docker0的信息,此时ID为ac51e0364ebf
root@xiaoni-01:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
581451397c27 centos:7 "/bin/bash" 35 minutes ago Up 35 minutes centos04
b77ab5cb0c8a centos:7 "/bin/bash" 37 minutes ago Up 37 minutes centos03
9559c91ca761 centos:7 "/bin/bash" 3 hours ago Up 3 hours centos02
root@xiaoni-01:~# docker exec -it 581451397c27 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 centos03 b77ab5cb0c8a //!!!!!!!! 直接写死了
172.17.0.4 581451397c27
究其本源:–link就是我们在hosts配置中增加了一个172.17.0.2 centos03 b77ab5cb0c8a
但是现在不推荐使用–link了
docker0的问题:不支持容器名连接访问。
3.3 自定义网络
查看所有docker网络
网络模式
- bridge:桥接 (默认)
- none:不配置网络
- host:和宿主机共享网络
- container: 容器网络连通
测试
root@xiaoni-01:~# docker run -dit -P --name centos01 centos:7
f948cd4f9bbe4176cfd1b82cdd509fdaaa83b5f9c69d2cbf8b342c8c90ad6691
//实际上有默认参数 --net bridge,而这个就是我们的docker0
root@xiaoni-01:~# docker run -dit -P --name centos02 --net bridge centos:7
11bc32b2b27cb77d725753ad07bc0eaa1a3c8ef9a5e072f2bf2b5ddc3a7edbf9
docker0特点:默认、域名不能访问, 只有–link可以打通链接。
so我们可以自定义一个网络。
//查看docker network help!
root@xiaoni-01:~# 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 help!
root@xiaoni-01:~# 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@xiaoni-01:~# docker network create -d bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
6fc5cc03bae2c6fc45f81967af5d3564ebf7d6bf7c6c3c52d5b2dcaade4deaa2
- -d Driver to manage the Network (default “bridge”)
- –subnet 配置子网掩码
- –gateway 192.168.0.1 配置网关 这也就是你们加wifi ip地址的原因
此时重新启动两个容器:
再查看mynet
此时在一个自定义网络内的都可以ping通
root@xiaoni-01:~# docker exec -it centos-net-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.098 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 192.168.0.3: icmp_seq=3 ttl=64 time=0.058 ms
64 bytes from 192.168.0.3: icmp_seq=4 ttl=64 time=0.062 ms
64 bytes from 192.168.0.3: icmp_seq=5 ttl=64 time=0.061 ms
64 bytes from 192.168.0.3: icmp_seq=6 ttl=64 time=0.062 ms
64 bytes from 192.168.0.3: icmp_seq=7 ttl=64 time=0.061 ms
64 bytes from 192.168.0.3: icmp_seq=8 ttl=64 time=0.058 ms
64 bytes from 192.168.0.3: icmp_seq=9 ttl=64 time=0.068 ms
64 bytes from 192.168.0.3: icmp_seq=10 ttl=64 time=0.061 ms
64 bytes from 192.168.0.3: icmp_seq=11 ttl=64 time=0.060 ms
64 bytes from 192.168.0.3: icmp_seq=12 ttl=64 time=0.060 ms
^C
--- 192.168.0.3 ping statistics ---
12 packets transmitted, 12 received, 0% packet loss, time 11265ms
rtt min/avg/max/mdev = 0.058/0.064/0.098/0.010 ms
root@xiaoni-01:~# docker exec -it centos-net-01 ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.038 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.041 ms
64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=0.041 ms
64 bytes from 192.168.0.2: icmp_seq=4 ttl=64 time=0.040 ms
64 bytes from 192.168.0.2: icmp_seq=5 ttl=64 time=0.039 ms
^C
--- 192.168.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4088ms
rtt min/avg/max/mdev = 0.038/0.039/0.041/0.008 ms
我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络。
3.4 网络连通
现在想让tomcat-01 ping tomcat-net-01
答案是不可以!
此时可以用到docker connect指令
root@xiaoni-01:~# 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
//测试!
root@xiaoni-01:~# docker network connect mynet centos01
//观察
root@xiaoni-01:~# docker network inspect mynet
connect指令直接将centos01加到mynet中
对比:
所以网卡和网卡不能连接,但容器和网卡可以打通
3.5 Redis集群部署
root@xiaoni-01:~# docker network create redis --subnet 172.38.0.0/16
1f0c51c508dd646dfb944f20ca10a08a7eef9349c854dc26e3837db09f2d2e17
root@xiaoni-01:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
ac51e0364ebf bridge bridge local
0d2821b36efd host host local
e6c472ed0a40 none null local
1f0c51c508dd redis bridge local
脚本:
下面两个顺序运行
脚本1:
#通过脚本一次创建6个redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >>/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
脚本2:
# 通过脚本一次启动6个redis容器
for port in $(seq 1 6); \
do \
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
done
启动完成界面如下:
Unable to find image 'redis:5.0.9-alpine3.11' locally
5.0.9-alpine3.11: Pulling from library/redis
cbdbe7a5bc2a: Pull complete
dc0373118a0d: Pull complete
cfd369fe6256: Pull complete
3e45770272d9: Pull complete
558de8ea3153: Pull complete
a2c652551612: Pull complete
Digest: sha256:83a3af36d5e57f2901b4783c313720e5fa3ecf0424ba86ad9775e06a9a5e35d0
Status: Downloaded newer image for redis:5.0.9-alpine3.11
58c157220ba914ecff9cd2779150c90685673c9c7718fec9aaa5863ad445d7f6
8a173eb39bcd9b8276fbc64cf64ca257ed358cbb93c2de9b46a77f69abaa2c32
4df19869f8d2dd84dbc880ac1edd30aa1b7c811a0d7befaa7fe9ef44842de89a
c3d6ab1ed87a1ff4d11d083d7908dc6ee6d129d2f19c75c6f87cc84ea0e88f51
7b8e2bc1198aa4293c93c8aa26db6893a3e03e27b8a1b54919f7379c3595fb8d
9d879670c2357c88d148079f28acd8955065464983f7c0e9952dd4dd7f3c5279
root@xiaoni-01:/mydata/redis/node-1/conf# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9d879670c235 redis:5.0.9-alpine3.11 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6376->6379/tcp, :::6376->6379/tcp, 0.0.0.0:16376->16379/tcp, :::16376->16379/tcp redis-6
7b8e2bc1198a redis:5.0.9-alpine3.11 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6375->6379/tcp, :::6375->6379/tcp, 0.0.0.0:16375->16379/tcp, :::16375->16379/tcp redis-5
c3d6ab1ed87a redis:5.0.9-alpine3.11 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6374->6379/tcp, :::6374->6379/tcp, 0.0.0.0:16374->16379/tcp, :::16374->16379/tcp redis-4
4df19869f8d2 redis:5.0.9-alpine3.11 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6373->6379/tcp, :::6373->6379/tcp, 0.0.0.0:16373->16379/tcp, :::16373->16379/tcp redis-3
8a173eb39bcd redis:5.0.9-alpine3.11 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6372->6379/tcp, :::6372->6379/tcp, 0.0.0.0:16372->16379/tcp, :::16372->16379/tcp redis-2
58c157220ba9 redis:5.0.9-alpine3.11 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6371->6379/tcp, :::6371->6379/tcp, 0.0.0.0:16371->16379/tcp, :::16371->16379/tcp redis-1
需要注意的是Redis没有/bin/bash 只有**/bin/sh**
so:
创建集群
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
选择yes
创建完成!
进入集群:
/data # redis-cli -c
127.0.0.1:6379>
查看集群信息:
set a b
发现是第三个redis处理:
此时4号是3号的替补,那我们把3号停了,开始测试
发现是从4号机器取到的,则说明正确。
cluster nodes //查看节点
可以发现,此时3号fail,4号作为替补开始使用。
4 SpringBoot微服务打包Docker镜像
- 构建springboot项目
- 打包应用
Maven中双击package - 编写dockerfile
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
- 构建镜像
root@xiaoni-01:/home/idea# docker run -dit -P --name springboot-lzl666 springboot-lzl
- 发布运行
在浏览器也访问成功!!
感谢狂神!! 下面附上其b站视频地址。有docker任何问题也可以csdn站内私信我,一起学习交流!!
https://www.bilibili.com/video/BV1og4y1q7M4?p=39&spm_id_from=pageDriver
没有白走的路,再小的帆也能远航!