概述
1.Docker概述
1.1 Docker为什么会出现
一款产品:开发>上线。两套环境!应用环境、应用配置
开发——运维,问题:我在我的电脑上可以允许!版本更新的,导致服务不可用!对于韵味来说,考验就十分大?
环境配置是十分麻烦,每一个都要部署环境(集群Redis、ES、Hadoop…)!费时费力。
发布一个项目(jar+(Redis MySQL JDK ES)),项目能不能嗲上环境安装打包!
之前在服务器配置一个应用的环境Redis MYSQL ES Hadoop,配置超级麻烦,同时不能够跨平台。
Windows,最后发布到Linux!
传统:开发jar,运维来做。
现在:开发打包部署上线,一套流程做完!
java — apk-发布(应用商店)–张三使用apk–安装即可用!
java — jar(环境)— 打包项目带上环境(镜像)— (Docker仓库:商店)—运维:只需要下载我们发布的镜像 — 直接运行即可!
Docker给以上的问题,给出了解决方案!
JRE — 多个应用(端口冲突)— 原来都是交叉的!
隔离:Docker核心思想!打包装箱!每个箱子都是互相隔离的。
水果 生化武器
Docker 通过隔离机制,可以将服务器利用到极致!
本质: 所有的技术都是因为出现了一些问题,我们需要去解决,才去学习!
1.2 Docker的历史
2020年,几个搞IT的年轻人,就在美国成立一家公司dotCloud
,做一些pass的云计算服务!
开放源代码
2014年4月9日,Docker1.0发布。
虚拟机:虚拟化技术。几个G
容器:虚拟化技术。几个M,几kb
1.3 Docker的基本组成
**镜像(image):**docker镜像就好比一个模版,可以通过这个模版来创建容器服务,tomcat镜像>docker run >tomcat容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
**容器(container):**docker利用容器技术,独立运行一个或一个组应用,通过镜像来创建。
启动>停止>删除----基本命令!
目前就可以把这个容器理解为就是一个简易的Linux系统。
**仓库(repository):**仓库就是存放镜像的地方!
仓库分为公有仓库和私有仓库!
Docker Hub(默认是国外的仓库)
阿里云…都有容器服务器(配置镜像加速!)
1.4 Docker的安装与卸载
> 环境准备
1.CentOS 7
2.使用XShell连接远程服务器进行操作!
环境查看
#需要系统内核3.10以上
uname -r
#查看系统配置
cat /etc/os-release
安装docker
#1.卸载旧的版本
yum remove docker
docker-client
docker-client-latest
docker-common
docker-latest
docker-latest-logrotate
docker-logrotate
docker-engine
#2.需要的安装包
yum install -y yum-utils
#3.设置镜像仓库
yum-config-manager
--add-repo
https://download.docker.com/linux/centos/docker-ce.repo #默认是国外的服务器
#阿里云镜像仓库 这个地方用的CentOS
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
#更新yum软件包索引
yum makecache fast
#4.安装Docker引擎 docker-ce 社区版 docker-ee 企业版
yum install docker-ce docker-ce-cli containerd.io
#5.启动docker
systemctl start docker
#6.测试docker是否安装成功
docker version
#7.运行hello-world 映像来验证是否正确安装了Docker Engine 如果没有会从服务器上pull下来
docker run hello-world
#8.查看一下下载的这个Hello-world镜像
docker images
卸载docker
#1.卸载依赖(Docker Engine,CLI和Containerd软件包):
yum remove docker-ce docker-ce-cli containerd.io
#2.删除资源(主机上的映像,容器,卷或自定义配置文件不会自动删除。要删除所有图像,容器和卷)
rm -rf /var/lib/docker
#3.默认的资源路径
/var/lib/docker
阿里云镜像加速
1.登录阿里云>控制台>弹性计算>容器镜像服务
第一次登录需要开通
2.找到镜像加入地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tWj4gEYX-1611121613456)(https://www.xiaobustudy.cn/image/docker03.png)]
3.配置使用
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://pwbgre2h.mirror.aliyuncs.com"]
}
EOF
#重启
sudo systemctl daemon-reload
#启动
sudo systemctl restart docker
回顾Hello World运行流程
1.5 Docker底层原理
Docker是怎么工作的?
Docker是一个Client -Server 结构的系统,Docker的守护进程运行子啊主机上。通过Socket从客户端访问!
DockerServer接收到Docker—Client的指令,就会执行这个命令!
**Docker为什么比Vm虚拟机快? **
1.Docker有着比虚拟机更少的抽象层
2.Docker利用的是宿主机的内核,VM需要是Guest OS。
所以说,新建一个容器的时候,docker不需要像Vm一样重新加载一个操作系统的内核,避免引导。
虚拟机加载的Guest OS,分钟级别的,而Docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ThQAavKI-1611121613468)(https://www.xiaobustudy.cn/image/docker07.png)]
2.Docker常用命令
1.帮助命令
docker version #显示docker版本信息
docker info #显示docker的系统信息,包括镜像和容器的数量
docker --help #帮助命令
docker帮助文档的地址:https://docs.docker.com/engine/reference/run/
2.镜像命令
docker images #查看所有本地主机上的镜像
docker images --help
#可选项
-a,--all #列出所有的镜像
-q,--quiet #只显示镜像的ID
#docker search搜索镜像 dockerHub
docker search 镜像名
docker search mysql
#可选项
docker search mysql --filter=STARS=3000 #搜索stars不少于3000的mysql镜像
#下载镜像 默认下载最新的镜像
docker pull mysql
或
#指定版本镜像下载
docker pull mysql:5.7
注意:如果之前下过其他版本,Linux下载采用分层下载,对一些公共可以避免重复下载
#删除镜像 rm 删除+i 镜像 -f 过滤 镜像ID
docker rmi -f #IMAGE ID
#多个删除
docker rmi -f 镜像ID 镜像ID.....
#带参数删除 删除全部镜像
docker rmi -f $(docker images -aq)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpcCdKl7-1611121613469)(https://www.xiaobustudy.cn/image/docker09.png)]
#目录解释
REPOSITORY 镜像的仓库源--仓库名
TAG 镜像的标签--镜像的版本
IMAGE ID 镜像的ID
CREATED 镜像的创建时间
SIZE 镜像的大小
3.容器命令
说明:我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习
#下载镜像
docker pull centos
3.1新建容器,并启动
docker run [可选参数] image
#参数说明
--name=“Name” 容器名称 tomcat01 tomcat02 用来区分容器
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
容器端口
-p 随机指定端口
#测试,启动并进入容器
docker run -it centos /bin/bash
#退出容器 从容器中退出到主机
exit
#列出所有运行的容器
docker ps
#列出所有曾经已经运行过的容器
docker ps -a
#docker ps 命令
docker ps #列出当前正在运行的容器
docker ps -a #列出当前正在运行的容器+带出历史运行过的容器
docker -n=? #列出最近创建的容器 n表示要显示的几个
docker ps -a -n=1 #列出最近的这一个容器
docker ps -q #只显示容器的编号
docker ps -aq #显示所有的容器编号
3.2退出容器
#停止容器的运行并退出
exit
#直接退出,保持容器继续运行
ctrl+P+Q 键盘组合键
3.3删除容器
#删除指定的容器----运行的不能删除,会报错,如果要强制删除,docker rm -f 容器Id
docker rm 容器ID
#递归删除所有的容器 ---查处所有的容器ID并删除
docker rm -f $(docker ps -aq)
#可以通过管道符进行删除所有的容器
docker ps -a -q|xargs docker rm
3.4启动和停止容器的操作
docker start 容器ID #启动容器
docker restart 容器ID #重启容器
docker stop 容器ID #停止当前正在运行的容器
docker kill 容器ID #强制停止当前运行的容器
3.5常用其他命令
#命令 docker run -d 镜像名!
docker run -d centos
#问题:docker ps 发现centos停止了
#常见的坑:docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
3.6查看日志命令
docker logs -tf --tail 条数 容器 没有日志
#自己编写一个shell脚本
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker run -d centos /bin/sh -c "while true;do echo xiaobustudy;sleep 1;done"
#显示指定行数的日志
-tf #显示日志
--tail number #要显示的条数
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker logs -tf --tail 10 23d99d3e54d0
3.7 查看容器中的进程信息
#命令 docker top 容器ID
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker top 9661298d37e9
UID PID PPID C STIME TTY TIME CMD
root 439 423 0 10:37 ? 00:00:00
3.8 查看镜像的源数据
#命令 docker inspect 容器ID
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker inspect 9661298d37e9
[
{
"Id": "9661298d37e9a602cfe19f322fbc12103565f1e2ff76607e7662b79892d6a7d1",
"Created": "2020-08-20T02:37:29.215505996Z",
"Path": "/bin/bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 439,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-08-20T02:37:29.455879124Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:0d120b6ccaa8c5e149176798b3501d4dd1885f961922497cd0abef155c869566",
"ResolvConfPath": "/var/lib/docker/containers/9661298d37e9a602cfe19f322fbc12103565f1e2ff76607e7662b79892d6a7d1/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/9661298d37e9a602cfe19f322fbc12103565f1e2ff76607e7662b79892d6a7d1/hostname",
"HostsPath": "/var/lib/docker/containers/9661298d37e9a602cfe19f322fbc12103565f1e2ff76607e7662b79892d6a7d1/hosts",
"LogPath": "/var/lib/docker/containers/9661298d37e9a602cfe19f322fbc12103565f1e2ff76607e7662b79892d6a7d1/9661298d37e9a602cfe19f322fbc12103565f1e2ff76607e7662b79892d6a7d1-json.log",
"Name": "/thirsty_williamson",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Capabilities": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/5dad8eea3ba68507a87b2d4c4c94c91f543913cc65c6be7fafaf75e0b2cf2ac1-init/diff:/var/lib/docker/overlay2/fcf2a5d46ebdd6d092c63343f8cfa9af67ef48f72cb150a24b9af6219139e3dc/diff",
"MergedDir": "/var/lib/docker/overlay2/5dad8eea3ba68507a87b2d4c4c94c91f543913cc65c6be7fafaf75e0b2cf2ac1/merged",
"UpperDir": "/var/lib/docker/overlay2/5dad8eea3ba68507a87b2d4c4c94c91f543913cc65c6be7fafaf75e0b2cf2ac1/diff",
"WorkDir": "/var/lib/docker/overlay2/5dad8eea3ba68507a87b2d4c4c94c91f543913cc65c6be7fafaf75e0b2cf2ac1/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "9661298d37e9",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20200809",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "5a78ce9d7320c20fd1c0251cc8fef62ed0a3e6a50f064068697afec4912f3066",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/5a78ce9d7320",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "5e4f78f89eff35e5324b8474be42090625755f0596656ccc912cb818cd652d21",
"Gateway": "172.18.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:12:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "be711b219b1ba93cc5ebed83c1e81b3aa8621a0e244dd0e1425f3f194dbea418",
"EndpointID": "5e4f78f89eff35e5324b8474be42090625755f0596656ccc912cb818cd652d21",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:02",
"DriverOpts": null
}
}
}
}
]
3.9 进入当前正在运行的容器
#我们通常容器的使用都是后台方式运行的,需要进入容器,修改一些配置
#命令
docker exec -it 容器id bashShell
#测试
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9661298d37e9 centos "/bin/bash" 29 minutes ago Up 29 minutes thirsty_williamson
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker exec -it 9661298d37e9 /bin/bash
[root@9661298d37e9 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 02:37 pts/0 00:00:00 /bin/bash
root 14 0 0 03:07 pts/1 00:00:00 /bin/bash
root 27 14 0 03:07 pts/1 00:00:00 ps -ef
#方式二 ----正在执行当前的代码。。。。。
dockr attach 容器ID
#测试
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9661298d37e9 centos "/bin/bash" 32 minutes ago Up 32 minutes thirsty_williamson
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker attach 9661298d37e9
[root@9661298d37e9 /]# exit
exit
区别: docker exec :表示进入容器后开启一个新的终端,可以在里面操作(常用)
docker attach :表示进入容器正在执行的终端,不会启动新的进程
3.10 从容器上拷贝文件到主机上
#命令 docker cp 容器ID:容器内到路径 目的主机的路径
3.11 小结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SHSSMGdU-1611121613470)(https://www.xiaobustudy.cn/image/docker12.png)]
3.Docker 安装Nginx
1.搜索镜像
docker search nginx
2.下载镜像
docker pull nginx
3.查看镜像
docker images
4.启动nginx镜像
docker run -d --name nginx01 -p 8080:80 nginx
# -d 后台运行
--name 给容器命名
-p 宿主机端口:容器端口
5.查看启动镜像
docker ps
6.本机自测
curl localhost:3344
#测试
[root@iz2zea4lnj6d43ryhkg1lvz ~]# curl localhost:3344
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
7.进入容器
docker exec -it nginx01 /bin/bash
#查找配置文件
whereis nginx
4.Docker 安装Tomcat
1.安装Tomcat镜像
docker pull Tomcat
2.启动tomcat容器
docker run -d -p 3355:8080 --name tomcat01 tomcat
5.Docker部署ES+Kibana
问题:
#es 暴露端口较多
#es 十分耗内存
#es 的数据一般需要放置到安全目录!挂载
#下载ES 会很卡
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:tag
#查看CPU的状态
docker status
#增加es的内存限制
#停止容器es启动
docker stop 容器ID
#重新启动,添加内存限制 -e 环境配置
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS=“-Xms64m -Xmx512m” elasticsearch:tag
6.可视化面板
- portainer
# 搜索镜像
docker search portainer/portainer
# 拉取镜像
docker pull portainer/portainer
# 运行镜像
docker run -d -p 9000:9000 --restart=always -v /root/portainer:/data -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
-d #容器在后台运行
-p 9000:9000 # 宿主机9000端口映射容器中的9000端口
-v /var/run/docker.sock:/var/run/docker.sock # 把宿主机的Docker守护进程(docker daemon)默认监听的Unix域套接字挂载到容器中
-v /root/portainer:/data # 把宿主机目录 /root/portainer 挂载到容器 /data 目录;
–name dev-portainer # 指定运行容器的名称
-
Rancher(CI/CD时使用,持续集成)
7. 联合文件系统UFS、分层下载
联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。
联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。
1.Docker存储
Docker目前通过插件化方式支持多种文件系统后端.
Docker 中使用的 AUFS(AnotherUnionFS)就是一种联合文件系统。 AUFS 支持为每一个成员目录(类似 Git 的分支)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限, 同时 AUFS 里有一个类似分层的概念, 对只读权限的分支可以逻辑上进行增量地修改(不影响只读部分的)。
Docker镜像自身就是多个文件层组成,每一层有唯一的编号,可以通过docker history来查看一个镜像由哪些层组成.
2.多种文件系统比较
Docker 目前支持的联合文件系统包括 OverlayFS, AUFS, Btrfs, VFS, ZFS 和 Device Mapper。
Linux 发行版 | Docker 推荐使用的存储驱动 |
---|---|
Docker CE on Ubuntu | aufs, devicemapper, overlay2 (Ubuntu 14.04.4 +, 16.04 +), overlay, zfs, vfs |
Docker CE on Debian | aufs, devicemapper, overlay2 (Debian Stretch), overlay, vfs |
Docker CE on CentOS | devicemapper, vfs |
Docker CE on Fedora | devicemapper, overlay2 (Fedora 26 +), overlay (实验性支持), vfs |
在可能的情况下,推荐使用 overlay2 存储驱动,overlay2 是目前 Docker 默认的存储驱动,以前则是 aufs。你可以通过配置来使用以上提到的其他类型的存储驱动。
8.Commit镜像
docker commit 提交容器成为一个新的副本
docker commit -m=“提交的描述信息” -a=“作者” 容器ID 目标镜像名:[TAG]
9.容器数据卷
1.为什么要用到数据卷?
将应用和环境打包成一个镜像!
数据? 如果数据都在容器中,那么我们容器删除,数据就会丢失!**需求:数据可以持久化 **
Mysql,容器删了,删库跑路!需求:MySQL数据可以存储在本地!
容器之间有一个数据共享的技术,Docker容器中产生的数据,同步到本地!
这就是卷技术,目录的挂载,将我们容器内的目录,挂载到Linux上面!
总结;容器的持久化和同步操作!容器间的数据也是可以共享
9.1 使用数据卷
方式一:直接使用命令来挂载 -v
docker run -it -v 主机目录:容器内目录 镜像 /bin/bash
#测试
[root@iz2zea4lnj6d43ryhkg1lvz home]# docker run -d -it -v /home/test:/home centos /bin/bash
[root@35626545db5a /]# ctrl+P+Q
[root@iz2zea4lnj6d43ryhkg1lvz home]# ls
BookStack HTML MSAdmin MSAdmin.rar test
[root@iz2zea4lnj6d43ryhkg1lvz home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
261968fcbebd centos "/bin/bash" 19 seconds ago Up 18 seconds loving_austin
92f5a700b0ca nginx "/docker-entrypoint.…" 3 hours ago Up 3 hours 0.0.0.0:3344->80/tcp nginx01
[root@iz2zea4lnj6d43ryhkg1lvz home]# docker inspect 261968fcbebd
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LktZwQ5h-1611121613471)(https://www.xiaobustudy.cn/image/docker14.png)]
实战MySQL
#获取镜像
docker pull mysql:5.7
#官网命令 启动mysql时候需要配置密码
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
#启动容器 设置密码为123456 同时配置文件和mysql数据进行挂载,进行双向绑定且同步
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
具名挂载和匿名挂载
#匿名挂载
-v 容器内路径!
docker run -d -P --name nginx01 -v /ect/nginx nginx
#查看所有的Volume的情况 显示都是未命名的卷挂载
[root@iz2zea4lnj6d43ryhkg1lvz home]#docker volume ls
DRIVER VOLUME NAME
local 818eb49924f195ddd8b8d9cb4e8d3f7c51f48c81567e4d25ecaad5a47bd6e146
#具名挂载 起名字为juming
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker run -d -P --name nginx03 -v juming:/ect/nginx nginx
96b56be694f53713bf0fd06328d6d01f4b326e2d6aec9f42e536ad22fa46a7ff
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker volume ls
DRIVER VOLUME NAME
local 818eb49924f195ddd8b8d9cb4e8d3f7c51f48c81567e4d25ecaad5a47bd6e146
local juming
#查看具名挂载源数据
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker volume inspect juming
[
{
"CreatedAt": "2020-08-20T17:49:46+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
注意:所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/
下
#扩展
#ro 只读
#rw 读写
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker run -d -P --name nginx03 -v juming:/ect/nginx:ro nginx
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker run -d -P --name nginx03 -v juming:/ect/nginx:rw nginx
注意:一旦设置容器内的文件只读,容器内无法操作,只能通过宿主机进行操作
方式二:初识DockerFile
DockerFile就是用来构建Docker镜像的构建文件!
[root@iz2zea4lnj6d43ryhkg1lvz home]# mkdir docker-test-volume
[root@iz2zea4lnj6d43ryhkg1lvz home]# cd docker-test-volume/
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# pwd
/home/docker-test-volume
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# vim dockerfile1
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# cat dockerfile1
From centos
VOLUME ["volume01","volume02"]
CMD echo "----end----"
CMD /bin/bash
#构建
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker build -f /home/docker-test-volume/dockerfile1 -t xiaobu/centos:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : From centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 65d4c5317b7b
Removing intermediate container 65d4c5317b7b
---> 93a45ac26e8a
Step 3/4 : CMD echo "----end----"
---> Running in 3aa4af28e234
Removing intermediate container 3aa4af28e234
---> b6c7b75bd774
Step 4/4 : CMD /bin/bash
---> Running in 58e181672518
Removing intermediate container 58e181672518
---> f7522075b747
Successfully built f7522075b747
Successfully tagged xiaobu/centos:1.0
#查询镜像
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
xiaobu/centos 1.0 f7522075b747 About a minute ago 215MB
centos latest 0d120b6ccaa8 10 days ago 215MB
#启动自己的容器
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker run -it f7522075b747 /bin/bash
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-adRPKX9K-1611121613472)(https://www.xiaobustudy.cn/image/docker15.png)]
这个卷和外部肯定有一个同步的目录!
查看一下卷挂载的路径
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
adab1516ecd7 centos "/bin/bash" 17 hours ago Up 17 hours gallant_morse
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
xiaobu/centos 1.0 f7522075b747 20 minutes ago 215MB
centos latest 0d120b6ccaa8 10 days ago 215MB
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker run -it f7522075b747 /bin/bash
[root@7ea67ad1b45c /]# ls -l
total 56
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 360 Aug 21 01:56 dev
drwxr-xr-x 1 root root 4096 Aug 21 01:56 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Aug 9 21:40 lost+found
drwxr-xr-x 2 root root 4096 May 11 2019 media
drwxr-xr-x 2 root root 4096 May 11 2019 mnt
drwxr-xr-x 2 root root 4096 May 11 2019 opt
dr-xr-xr-x 83 root root 0 Aug 21 01:56 proc
dr-xr-x--- 2 root root 4096 Aug 9 21:40 root
drwxr-xr-x 11 root root 4096 Aug 9 21:40 run
lrwxrwxrwx 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 May 11 2019 srv
dr-xr-xr-x 13 root root 0 Apr 11 2018 sys
drwxrwxrwt 7 root root 4096 Aug 9 21:40 tmp
drwxr-xr-x 12 root root 4096 Aug 9 21:40 usr
drwxr-xr-x 20 root root 4096 Aug 9 21:40 var
drwxr-xr-x 2 root root 4096 Aug 21 01:56 volume01
drwxr-xr-x 2 root root 4096 Aug 21 01:56 volume02
[root@7ea67ad1b45c /]# cd volume01/
[root@7ea67ad1b45c volume01]# touch test.text
[root@7ea67ad1b45c volume01]# ls -l
total 0
-rw-r--r-- 1 root root 0 Aug 21 01:57 test.text
[root@7ea67ad1b45c volume01]# ls -l[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7ea67ad1b45c f7522075b747 "/bin/bash" About a minute ago Up About a minute determined_liskov
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# docker inspect 7ea67ad1b45c
#查看刚刚创建的文件是否同步
#复制到刚刚查看源文件的目录 并进入 查看文件是否同步
[root@iz2zea4lnj6d43ryhkg1lvz docker-test-volume]# cd /var/lib/docker/volumes/bba432689c0a7974b69d688310d5e44ab72034942e276bc5f096eb2f9d37734d/_data
[root@iz2zea4lnj6d43ryhkg1lvz _data]# ls -l
总用量 0
-rw-r--r-- 1 root root 0 8月 21 09:57 test.text
数据卷容器
容器之间数据挂载
实现两个Mysql数据同步或多个mysql数据同步
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eX7L0Qc8-1611121613474)(https://www.xiaobustudy.cn/image/docker18.png)]
#1.创建docker01
docker run -d -it --name docker01 xiaobu/centos:1.0
#2.创建Docker02 并绑定Docker01 实现数据同步
[root@iz2zea4lnj6d43ryhkg1lvz ~]# docker run -it --name docker02 --volumes-from docker01 xiaobu/centos:1.0
[root@cc651814bb24 /]# ls -l
total 56
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 360 Aug 21 02:44 dev
drwxr-xr-x 1 root root 4096 Aug 21 02:44 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Aug 9 21:40 lost+found
drwxr-xr-x 2 root root 4096 May 11 2019 media
drwxr-xr-x 2 root root 4096 May 11 2019 mnt
drwxr-xr-x 2 root root 4096 May 11 2019 opt
dr-xr-xr-x 85 root root 0 Aug 21 02:44 proc
dr-xr-x--- 2 root root 4096 Aug 9 21:40 root
drwxr-xr-x 11 root root 4096 Aug 9 21:40 run
lrwxrwxrwx 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 May 11 2019 srv
dr-xr-xr-x 13 root root 0 Apr 11 2018 sys
drwxrwxrwt 7 root root 4096 Aug 9 21:40 tmp
drwxr-xr-x 12 root root 4096 Aug 9 21:40 usr
drwxr-xr-x 20 root root 4096 Aug 9 21:40 var
drwxr-xr-x 2 root root 4096 Aug 21 02:34 volume01
drwxr-xr-x 2 root root 4096 Aug 21 02:34 volume02
#3.在docker01中创建文件 查看是否同步
[root@iz2zea4lnj6d43ryhkg1lvz /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc651814bb24 xiaobu/centos:1.0 "/bin/sh -c /bin/bash" 11 minutes ago Up 11 minutes docker02
66478bef4509 xiaobu/centos:1.0 "/bin/sh -c /bin/bash" 20 minutes ago Up 20 minutes docker01
[root@iz2zea4lnj6d43ryhkg1lvz /]# docker exec -it cc651814bb24 /bin/bash
[root@cc651814bb24 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
[root@cc651814bb24 /]# cd volume01
[root@cc651814bb24 volume01]# ls
docker01
发现数据已经同步,同时容器中已经在宿主机实现数据共享
可用inpsect 查看源数据 ,进入到挂载卷到文件夹中查看
注意:如果两个容器都共享一个数据卷,删除了docker01,docker02或者docker03也不会被删除
#多个mysql实现数据共享
#数据库01
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
数据库02
docker run -d -p 3311:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
#这个时候就可以实现两个容器数据同步!
10.DockerFile
dockerfile 是用来构建docker镜像的文件!命令参数脚本!
构建步骤:
1.编写一个dockerfile文件
2.docker build构建成为一个镜像
3.docker run 运行镜像
4.docker push 发布镜像(DockerHub、阿里云镜像仓库!)
DockerFile构建过程
基础知识
1.每个保留关键字(指令)都必须是大写字母
2.执行顺序:从上到下的顺序执行
3.#表示注释
4.每一个指令都会创建提交一个新的镜像层,并提交!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ANneWXE-1611121613475)(https://www.xiaobustudy.cn/image/docker19.jpg)]
dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单!
Docker逐渐成为企业交付的标准,必须要掌握!
- DockerFile:构建文件,定义一切都步骤,源代码。
- DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品!
- Docker容器:容器就是镜像运行起来提供的服务
DockerFile的指令
FROM #基础镜像,一切从这里开始 centos
MAINTAINER #镜像的作者是谁 姓名+邮箱
RUN #镜像构建的时候需要运行的命令
ADD #步骤:tomcat镜像,这个Tomcat压缩包!添加内容
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPOSE #暴露的端口配置
CMD #指定这个容器启动的时候运行的命令,只有一个会呗生效,可被替代
ENTRYPOINT #指定这个容器启动的时候运行的命令,可以追加命令
ONBUILD #当构建被继承DockerFile这个时候就会运行ONBUILD的指令,触发指令
COPY #类似ADD将我们的文件拷贝到镜像中
ENV #构建的时候设置环境变量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UP0IwZcq-1611121613476)(https://www.xiaobustudy.cn/image/docker20.png)]
实战测试----构建CentOS
DockerHub中99%的镜像都是从这个基础镜像过来的**“ FROM scratch ”**,然后配置需要的软件和配置来进行构建。
#创建一个自己的Centos
#1.编写dockerFile的文件
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# vim myDockerFile
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# cat myDockerFile
FROM centos
MAINTAINER xiaobu<s_xiaobu@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash
#2.通过这个文件构建镜像
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker build -f myDockerFile -t mycentos:0.1 .
成功时提示:
Successfully built 12a38b7b6cd6
Successfully tagged mycentos:0.1
#3.测试增加之后的镜像
#运行容器
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker run -it mycentos:0.1
#查看当前目录
[root@878f15a72381 local]# pwd
/usr/local
[root@878f15a72381 local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.4 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:ac:12:00:04 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
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
loop txqueuelen 1 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
#4.查看镜像的安装历史 docker history 容器ID
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 0.1 12a38b7b6cd6 27 minutes ago 287MB
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker history 12a38b7b6cd6
IMAGE CREATED CREATED BY SIZE COMMENT
12a38b7b6cd6 27 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
5602db21a30a 27 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
627d3e789e58 27 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
6a8921651bd6 27 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
33f86ca55be6 27 minutes ago /bin/sh -c yum -y install net-tools 14.3MB
d8923391d290 30 minutes ago /bin/sh -c yum -y install vim 57.2MB
e681daec62f6 30 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
d49f76c2fcb0 30 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
ea16459b8a7e 30 minutes ago /bin/sh -c #(nop) MAINTAINER xiaobu<s_xiaob… 0B
0d120b6ccaa8 10 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 10 days ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 10 days ago /bin/sh -c #(nop) ADD file:538afc0c5c964ce0d… 215MB
CMD和ENTRYPOINT的区别
#1.CMD
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# cat DockerFile2
FROM centos
CMD ["ls","-a"]
#构建cmdtest镜像
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker build -f DockerFile2 -t cmdtest .
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM centos
---> 0d120b6ccaa8
Step 2/2 : CMD ["ls","-a"]
---> Running in ec2c6a082ea3
Removing intermediate container ec2c6a082ea3
---> b392106df797
Successfully built b392106df797
Successfully tagged cmdtest:latest
#执行镜像
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker run cmdtest
.
..
.dockerenv
bin
dev
etc
home
...
#想追加一个命令 -l ls-al 怎么办?
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker run b392106df797 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: "-l": executable file not found in $PATH": unknown.
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker run b392106df797 ls -al
total 56
drwxr-xr-x 1 root root 4096 Aug 21 08:14 .
drwxr-xr-x 1 root root 4096 Aug 21 08:14 ..
-rwxr-xr-x 1 root root 0 Aug 21 08:14 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 Aug 21 08:14 dev
drwxr-xr-x 1 root root 4096 Aug 21 08:14 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
...
#2.测试ENTRYPOINT
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# vim Docker3
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# cat Docker3
FROM centos
ENTRYPOINT ["ls","-a"]
#构建镜像
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker build -f Docker3 -t entyrpointtest .
Sending build context to Docker daemon 4.096kB
Step 1/2 : FROM centos
---> 0d120b6ccaa8
Step 2/2 : ENTRYPOINT ["ls","-a"]
---> Running in 7792edbf11f7
Removing intermediate container 7792edbf11f7
---> b53a0fc5cf18
Successfully built b53a0fc5cf18
Successfully tagged entyrpointtest:latest
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
entyrpointtest latest b53a0fc5cf18 44 seconds ago 215MB
cmdtest latest b392106df797 17 minutes ago 215MB
mycentos 0.1 12a38b7b6cd6 59 minutes ago 287MB
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker run -it b53a0fc5cf18
. .dockerenv dev home lib64 media opt root sbin sys usr
.. bin etc lib lost+found mnt proc run srv tmp var
failed to resize tty, using default size
# 追加命令
[root@iz2zea4lnj6d43ryhkg1lvz dockerFile]# docker run -it b53a0fc5cf18 -l
total 56
drwxr-xr-x 1 root root 4096 Aug 21 08:25 .
drwxr-xr-x 1 root root 4096 Aug 21 08:25 ..
-rwxr-xr-x 1 root root 0 Aug 21 08:25 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 360 Aug 21 08:25 dev
drwxr-xr-x 1 root root 4096 Aug 21 08:25 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Aug 9 21:40 lost+found
drwxr-xr-x 2 root root 4096 May 11 2019 media
drwxr-xr-x 2 root root 4096 May 11 2019 mnt
drwxr-xr-x 2 root root 4096 May 11 2019 opt
dr-xr-xr-x 86 root root 0 Aug 21 08:25 proc
dr-xr-x--- 2 root root 4096 Aug 9 21:40 root
drwxr-xr-x 11 root root 4096 Aug 9 21:40 run
lrwxrwxrwx 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 May 11 2019 srv
dr-xr-xr-x 13 root root 0 Apr 11 2018 sys
drwxrwxrwt 7 root root 4096 Aug 9 21:40 tmp
drwxr-xr-x 12 root root 4096 Aug 9 21:40 usr
drwxr-xr-x 20 root root 4096 Aug 9 21:40 var
实战;Tomcat镜像
1.准备镜像文件tomcat压缩包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHQpNCYY-1611121613477)(https://www.xiaobustudy.cn/image/docker21.png)]
2.编写dockerf ile文件,官方命名:Dockerfile
,build会去自动寻找这个文件,就不需要-f指定了。
11.Docker网络
#获取容器内部的软连接的ip
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' xxx容器ID
最后
以上就是壮观音响为你收集整理的Docker学习的全部内容,希望文章能够帮你解决Docker学习所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复