一、Docker简介
1、背景
物理机时代
一个物理机上安装操作系统,然后直接运行我们的软件。也就是说你电脑上直接跑了一个软件,并没有开虚拟机什么的,资源极其浪费。
缺点
-部署慢
-成本高
虚拟机都不开,直接上物理机部署应用,那成本能不高嘛
-资源浪费
硬件资源没有合理利用
-不易迁移和增加机器
每次迁移都要重新安装一模一样的运行环境等
-受限于硬件
虚拟机时代
一个物理机上虚拟化出来多个虚拟机,每个虚拟机上进行安装操作系统,然后在虚拟机上直接运行我们的软件。比如阿里云、腾讯云等云服务器都是虚拟机。
特点
-多部署
一个物理机上创建多个虚拟机,每个虚拟机的ip都不同,一个软件可以在不同虚拟机上进行部署。
-资源隔离
>每个虚拟机都由独立分配的内存磁盘等硬件资源,通过Hypervisor分配。
-易扩展
随时随地都能在一个物理机上开出新的虚拟机。成本也低。
-成本相对较低
缺点
-虚拟机需要安装操作系统
>假设我们的应用程序只有几KB,或者几十MB,但是却要在虚拟机上安装一个几百兆的操作系统来跑。
容器化时代
虚拟化技术是物理硬件的隔离,容器化技术是app层面的隔离,两者不冲突。往往容器化时代都是基于虚拟机基础之上做的资源隔离。它可以基于虚拟机的操作系统部署多服务,不再需要额外安装操作系。
特点
-标准化的迁移方式
开发者将环境和程序打包给运维,运维直接执行就完事了。
-统一的参数配置
-自动化部署
-应用集群监控
-开发与运维之间的沟通桥梁
传统虚拟机和Docker的比较
2、Docker是什么
docker官方地址:https://www.docker.com/
Docker is an open platform for developing,shipping,and running applications.Docker enables you to separate your applications from your infrastructure so you can deliver software quickly.With Docker,you can manage your infrastructure in the same ways you manage your applications.By taking advantage of Docker’s methodologies for shipping,testing,and deploying code quickly,you can significantly reduce the delay between writing code and running it in production.
Docker最初是dotCloud公司创始人Solomon Hykes在法国期间发起的一个公司内部项目,于2013年3月以Apache 2.0授权协议开源,主要项目代码在GitHub上进行维护。
Docker使用Google公司推出的Go语言进行开发实现。
Docker是linux容器的一种封装,提供简单易用的容器使用接口。它是最流行的Linux容器解决方案。
Docker的接口相当简单,用户可以方便的创建、销毁容器。
Docker将应用程序与程序的依赖,打包在一个文件里面。运行这个文件就会生成一个虚拟容器。程序运行在虚拟容器里,如同在真实物理机上运行一样,有了Docker,就不用担心环境问题了。
Docker concepts
Docker is a platform for developers and sysadmins to**build,run,and share**applications with containers.The use of containers to deploy applications is called*containerization*.Containers are not new,but their use for easily deploying applications is.
Containerization is increasingly popular because containers are:
-Flexible:Even the most complex applications can be containerized.
灵活:即使是最复杂的应用也可以集装箱化。`
-Lightweight:Containers leverage and share the host kernel,making them much more efficient in terms of system resources than virtual machines.
轻量级:容器利用并共享主机内核,使它们在系统资源方面比虚拟机效率更高`
-Portable:You can build locally,deploy to the cloud,and run anywhere.
便携式:您可以在本地构建,部署到云,并在任何地方运行`
-**Loosely coupled**:Containers are highly self sufficient and encapsulated,allowing you to replace or upgrade one without disrupting others.
松耦合:容器是高度自给自足和封装的,允许您更换或升级一个容器,而不会中断其他容器。`
-**Scalable**:You can increase and automatically distribute container replicas across a datacenter.
可扩展:您可以在数据中心增加并自动分发容器副本。`
-**Secure**:Containers apply aggressive constraints and isolations to processes without any configuration required on the part of the user.
安全:容器对进程应用严格的约束和隔离,而不需要用户进行任何配置。`
**以上引自docker官方文档:https://docs.docker.com/get-started/**
3、Docker应用场景
-web应用的自动化打包和发布
-自动化测试和持续集成、发布
-在服务型环境中部署和调整数据库或其他应用
4、Docker三大概念
Docker架构
1.镜像image
docker镜像就是一个只读模板,比如,一个镜像可以包含一个完整的centos,里面仅安装apache或用户的其他应用,镜像可以用来创建docker容器,另外docker提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。
2.容器container
docker利用容器来运行应用,容器是从镜像创建的运行实例,它可以被启动,开始、停止、删除、每个容器都是互相隔离的,保证安全的平台,可以把容器看做是简易版的linux环境(包括root用户权限、镜像空间、用户空间和网络空间等)和运行在其中的应用程序。
3.仓库repository
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务。
一个Docker Registry中可以包含多个仓库
Repository
;每个仓库可以包含多个标签
Tag
;每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以latest作为默认标签。
仓库分为两种,公有仓库和私有仓库,最大的公共仓库是docker Hub,存放了数量庞大的镜像供用户下载,国内的docker pool,这里仓库的概念与Git类似,registry可以理解为github这样的托管服务。
5、Docker优势
1.更高效的利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker对系统资源的利用率更高。
无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
2.更快速的启动时间
传统的虚拟机技术启动应用服务往往需要数分钟,而Docker容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
3.一致的运行环境
开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些bug并未在开发过程中被发现。
而Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现“这段代码在我机器上没问题啊”这类问题。
4.持续交付和部署
对开发和运维DevOps人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用Docker可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过Dockerfile来进行镜像构建,并结合持续集成
Continuous Integration系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合持续部署Continuous Delivery/Deployment系统进行自动部署。
而且使用Dockerfile使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
5.更轻松的迁移
由于Docker确保了执行环境的一致性,使得应用的迁移更加容易。Docker可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。
因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
```
二、Docker安装
1、环境准备
```shell
系统环境:docker最低支持centos7且在64位平台上,内核版本在3.10以上
版本:Docker Community Edition(CE)社区版,Enterprise EditionEE企业版(包含了一些收费服务)
```
2、安装步骤
step1:使用yum命令进行安装
老版本安装方式$yum install-y docker安装的版本是1.13.12017-2-8更新,不推荐
新版本安装:**Docker官方文档:https://docs.docker.com/engine/install/centos/**步骤摘要:
#1.卸载旧版本
$sudo yum remove docker\
docker-client\
docker-client-latest\
docker-common\
docker-latest\
docker-latest-logrotate\
docker-logrotate\
docker-engine
#2.安装yum工具包包
-y表示不询问使用默认配置进行安装,等待提示安装完毕!
$sudo yum install-y yum-utils
#3.设置镜像的仓库
默认是从国外的,推荐使用国内的
$sudo yum-config-manager\
--add-repo\
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
#更新yum软件包索引
可省略
#yum makecache fast
#4.安装docker相关的docker-ce社区版而ee是企业版
$sudo yum install-y docker-ce docker-ce-cli containerd.io
#5.启动docker服务
$sudo systemctl start docker
#6.使用docker version查看docker版本
$sudo docker version
#7.测试
$sudo docker run hello-world
```
step2:查看是否安装成功
yum list installed|grep docker
备注:命令为查看yum安装完成文件列表并筛选docker安装信息如下
step3:启动docker服务
```
systemctl start docker
```
step4:查看是否启动成功
```
systemctl status docker
```
如果没有出现activerunning
说明启动失败
解决方法:修改docker文件,具体命令:vi/etc/sysconfig/docker,在--selinux-enabled后面添加=false。如下图所示:
# 再次执行
systemctl start docker
systemctl status docker
Step5:查看docker版本
docker version
Step6:测试
docker run hello-world
3、配置镜像加速
方式一:阿里云镜像加速
A.打开阿里云网址https://cr.console.aliyun.com
B.使用支付宝或淘宝账号登录
C.搜索容器镜像服务
D.复制红色区域内容
E.新建文件夹mkdir-p/etc/docker
F.编辑文件vim/etc/docker/daemon.json把刚刚复制的内容写进去保存
G.重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker
方式二:腾讯云镜像加速
腾讯云为您提供如下DockerHub加速器,以方便您快速拉取DockerHub平台上的容器镜像。
此加速器地址需在腾讯云云服务器中配置才可生效,请勿通过浏览器直接访问,请参照以下教程进行配置。
#1.创建或修改/etc/docker/daemon.json文件,并写入以下内容:
{
"registry-mirrors":[
"https://mirror.ccs.tencentyun.com"
]
}
#2.依次执行以下命令,重新启动Docker服务。
$sudo systemctl daemon-reload
$sudo systemctl restart docker
#Ubuntu16.04请执行sudo systemctl restart dockerd命令
**腾讯云容器服务官方地址:https://cloud.tencent.com/document/product/457/9113**
方式三:DaoCloud加速器
简介:DaoCloud加速器是广受欢迎的Docker工具,解决了国内用户访问Docker Hub缓慢的问题。DaoCloud加速器结合国内的CDN服务与协议层优化,成倍的提升了下载速度。
#一条命令加速(记得重启docker)
curl-sSL https://get.daocloud.io/daotools/set_mirror.sh|sh-s http://f1361db2.m.daocloud.io
注意:此时可能会导致启动失败,原因实在配置镜像加速时在配置文件中多加了一个",",删除重启即可。
三、Docker命令
1、帮助命令
docker version#显示docker的版本信息。
docker info#显示docker的系统信息,包括镜像和容器的数量
docker命令--help#帮助命令
2、镜像命令
docker images#查看所有本地主机上的镜像可以使用docker image ls代替
Usage:docker images[OPTIONS][REPOSITORY[:TAG]]
-q#只显示镜像id
-a#列出本地所有镜像(含中间映像层)
--digests#显示镜像的摘要信息
--no-trunc#显示镜像完整信息
[root VM_0_5_centos~]#docker images-a
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/hello-world latest bf756fb1ae65 5 months ago 13.3 kB
docker search#搜索镜像
Usage:docker search[OPTIONS]TERM
-f#根据指定条件删选镜像
[root VM_0_5_centos~]#docker search mysql-f stars=5000#搜索MySQL镜像stars超过5000的
INDEX NAME STARS
docker.io docker.io/mysql 9623
docker pull#下载镜像docker image pull
Usage:docker pull[OPTIONS]NAME[:TAG|DIGEST]
[root VM_0_5_centos~]#docker pull tomcat:8.0#如果不指定tag,默认下载最新版本
[root VM_0_5_centos~]#docker images-a
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/tomcat 9.0.36-jdk8-openjdk b79665757bae 4 days ago 530 MB
docker.io/tomcat latest 2eb5a120304e 4 days ago 647 MB
docker.io/hello-world latest bf756fb1ae65 5 months ago 13.3 kB
docker.io/tomcat 8.0 ef6a7c98d192 21 months ago 356 MB
docker rmi#删除镜像docker image rm
Usage:docker rmi[OPTIONS]IMAGE[IMAGE...]
Remove one or more images
Options:
-f,--force#强制删除
[root VM_0_5_centos~]#docker rmi tomcat#删除最新版本的tomcat
[root VM_0_5_centos~]#docker rmi ef6a7c98d192#根据镜像id删除指定的tomcat
[root VM_0_5_centos~]#docker rmi-f$
docker images-aq
#强制删除全部的镜像
3、容器命令
[root VM_0_5_centos~]#docker container--help
Usage:docker container COMMAND
Manage containers
Options:
--help Print usage
Commands:
attach Attach to a running container
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes on a container's filesystem
exec Run a command in a running container
export Export a container's filesystem as a tar archive
inspect Display detailed information on one or more containers
kill Kill one or more running containers
logs Fetch the logs of a container
ls List containers
pause Pause all processes within one or more containers
port List port mappings or a specific mapping for the container
prune Remove all stopped containers
rename Rename a container
restart Restart one or more containers
rm Remove one or more containers
run Run a command in a new container
start Start one or more stopped containers
stats Display a live stream of containers
resource usage statistics
stop Stop one or more running containers
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
wait Block until one or more containers stop,then print their exit codes
Run'docker container COMMAND--help'for more information on a command.
**启动容器**
docker run镜像id#新建容器并启动
Usage:docker run[OPTIONS]IMAGE[COMMAND][ARG...]
Options:
-d,--detach Run container in background and print
-e,--env list Set environment variables
default[]
-i,--interactive Keep STDIN open even if not attached
-p,--publish list Publish a container's port
s
to the host
default[]
-P,--publish-all Publish all exposed ports to random ports
--rm Automatically remove the container when it exits
-t,--tty Allocate a pseudo-TTY
-v,--volume list Bind mount a volume
default[]
--volume-driver string Optional volume driver for the container
--volumes-from list Mount volumes from the specified container
s
default[]
[root VM_0_5_centos~]#docker run-it centos/bin/bash#启动centos容器并进入交互
[root VM_0_5_centos~]#docker run-d centos#后台启动centos容器,需要注意:docker容器使用后台运行,就必须要有要一个前台进程,docker发现当前容器没有前台进程使用,就会自动停止
**进入容器**
docker exec-it容器id bashshell#进入当前正在运行的容器
开启一个新的终端
docker attach容器id#进入当前正在运行的容器
直接进入正在运行的终端
**查看运行的容器**
docker ps#列出所有运行的容器docker container list
Usage:docker ps[OPTIONS]
Options:
-a,--all Show all containers
default shows just running
-f,--filter filter Filter output based on conditions provided
--format string Pretty-print containers using a Go template
--help Print usage
-n,--last int Show n last created containers
includes all states
default-1
-l,--latest Show the latest created container
includes all states
--no-trunc Don't truncate output
-q,--quiet Only display numeric IDs
-s,--size Display total file sizes
[root VM_0_5_centos~]#docker ps-a
**退出容器**
exit#容器停止退出
ctrl+P+Q#容器不停止退出
**删除容器**
docker rm容器id#删除指定的容器,不能删除正在运行的容器,如果要强制删除rm-rf
docker rm-f$
docker ps-aq
#删除所有的容器
docker ps-a-q|xargs docker rm#删除所有的容器
**容器管理**
docker start容器id#启动容器
docker restart容器id#重启容器
docker stop容器id#停止当前正在运行的容器
docker kill容器id#强制停止当前容器
4、其他
[root VM_0_5_centos~]#docker logs--help
Usage:docker logs[OPTIONS]CONTAINER
Fetch the logs of a container
Options:
--details Show extra details provided to logs
-f,--follow Follow log output
--help Print usage
--since string Show logs since timestamp
--tail string Number of lines to show from the end of the logs
default"all"
-t,--timestamps Show timestamps
[root VM_0_5_centos~]docker run-d centos/bin/sh-c"while true;do echo hello;sleep 1;done"#模拟日志
-tf#显示日志信息(一直更新)
--tail number#需要显示日志条数
docker logs-t--tail n容器id#查看n行日志
docker logs-ft容器id#跟踪日志
docker top容器id#查看容器中的进程信息
docker inspect容器id#查看镜像元数据
#将指定容器内资源拷贝到主机
docker cp容器id:容器内路径主机目的路径
[root VM_0_5_centos html]#docker cp 2d30adc4ee87:/etc/nginx/nginx.conf.
#将主机内资源拷贝到指定容器
docker cp主机目的路径容器id:容器内路径
[root VM_0_5_centos html]#docker cp test.html d1cf09d31eda:/usr/local
docker save centos>/opt/centos.tar.gz#导出docker镜像至本地
docker load</opt/centos.tar.gz#导入本地镜像到docker镜像库
docker stats#查看docker容器使用内存情况
四、Docker安装软件
1、安装mysql
#1.搜索镜像search上hub.docker.com搜索
[root localhost docker]#docker search mysql
#2.拉取镜像pull
[root localhost docker]#docker pull mysql:5.7
#3、运行测试
#-d后台运行
#--name给容器命名
#-p宿主机端口:容器内部端口
[root localhost docker]#docker run-d-p 3355:3306-e MYSQL_ROOT_PASSWORD=root--name mysql1 mysql:5.7
#测试
关闭防火墙或授权远程访问
外部连接访问正常
#进入容器命令:docker exec-it容器id/bin/bash
------------------------------容器启动进行数据挂载-----------------------------
#mysql容器正常启动数据挂载成功
$docker run-d-p 3344:3306-v/data/mysql/conf:/etc/mysql/conf.d-v/data/mysql/data:/var/lib/mysql-e MYSQL_ROOT_PASSWORD=root--name mysql01 mysql:5.7
#开放3344端口及安全组,在容器内部创建用户并授权,测试OK
2、安装tomcat
#拉取镜像
[root localhost docker]#docker pull tomcat
#创建容器运行
[root localhost docker]#docker run-d-p 8080:8080--name mytomcat tomcat
#测试访问(记得关闭防火墙此时会发现访问成功出现404响应,原因是webapps下面没有资源)
ip:8080
#进入容器
可以使用容器名称或容器id
[root localhost docker]#docker exec-it mytomcat/bin/bash
#将webapps.dist下的内容复制到webapps下面,重新访问即可看到正常页面
root ebef54554573:/usr/local/tomcat#cp-r webapps.dist/*webapps/
#部署项目到tomcat
[root localhost docker]#docker cp demo.war mytomcat:/usr/local/tomcat/webapps/
#重启tomcat容器
[root localhost docker]#docker restart mytomcat
3、安装nginx
#直接安装nginx镜像运行nginx容器
[root localhost docker]#docker run-d-p 80:80--name mynginx nginx
#进入容器复制文件到容器同上,不在累述
#数据挂载
把容器文件挂载到宿主机上面,不用每次进入容器内部修改文件,在容器外部宿主机修改文件会自动同步到容器内部,并且当删除容器后,宿主机上的文件不会被删除,相当于对容器内部文件进行持久化操作。
[root localhost docker]#docker run-d-p 8033:80-v/data/nginx/html:/usr/share/nginx/html--name mynginx nginx
#在/data/nginx/html目录下创建test.html
[root localhost html]#vim test.html
#测试成功
[root localhost html]#curl localhost:8033/test.html
--------------------------------------------------------------------------------------
[root localhost docker]#docker run-dit-p 8081:80-v/data/nginx/conf:/etc/nginx-v/data/nginx/log:/var/log/nginx-v/data/nginx/html:/usr/share/nginx/html--name nginx1 nginx/bin/bash
#说明:当挂载容器内安装软件配置目录容易出现很多问题,比如:1.后台启动即退出2.成功启动后无法正常访问
[root VM_0_5_centos conf]#curl localhost:8081
curl:
56
Recv failure:Connection reset by peer
4、安装elasticsearch
[root VM_0_5_centos conf]#docker run-d-p 9200:9200-p 9300:9300-e"discovery.type=single-node"-e ES_JAVA_OPTS="-Xms64m-Xmx512m"--name elasticsearch elasticsearch:7.6.2
[root VM_0_5_centos conf]#curl localhost:9200
{
"name":"b7546b5af212",
"cluster_name":"docker-cluster",
"cluster_uuid":"ZLQNTv0ZRbuQAXw4ZLJezw",
"version":{
"number":"7.6.2",
"build_flavor":"default",
"build_type":"docker",
"build_hash":"ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"build_date":"2020-03-26T06:34:37.794943Z",
"build_snapshot":false,
"lucene_version":"8.4.0",
"minimum_wire_compatibility_version":"6.8.0",
"minimum_index_compatibility_version":"6.0.0-beta1"
},
"tagline":"You Know,for Search"
}
五、Docker镜像
操作系统分为内核和用户空间。对于Linux而言,内核启动后,会挂载root文件系统为其提供用户空间支持。而Docker镜像Image
,就相当于是一个root文件系统。比如官方镜像centos:7.6就包含了完整的一套centos 7.6最小系统的root文件系统。
Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数如匿名卷、环境变量、用户等。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
因为**镜像包含操作系统完整的root文件系统**,其体积往往是庞大的,因此**在Docker设计时将其设计为分层存储的架构**。镜像只是一个虚拟的概念,其**实际体现并非由一个文件组成,而是由一组文件系统组成**,或者说,由**多层文件系统联合组成**。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
**分层存储的特征还使得镜像的复用、定制变的更为容易**。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
镜像使用分层存储,便于资源共享,可以使用docker image inspect命令查看
[root VM_0_5_centos~]#docker image inspect nginx
#docker commit提交容器成为一个新的副本
docker commit-m="描述信息"-a="作者"容器id目标镜像名:[TAG]
[root VM_0_5_centos~]#docker commit-a="shuaige"-m="add test.html"8fc157a704d4 nginx3:1.0
sha256:31b98e5522104b8a30b2cf587cc0494a265f4de9df3a7b04c45f78cb5b9001ef
[root VM_0_5_centos~]#docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx3 1.0 31b98e552210 14 seconds ago 132 MB
docker.io/nginx latest 2622e6cca7eb 5 days ago 132 MB
六、Docker数据挂载
按照Docker最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用Volume数据卷、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主
或网络存储发生读写,其性能和稳定性更高。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
数据卷的特性
-数据卷可以在容器之间共享和重用
-对数据卷的修改会立马生效
-对数据卷的更新,不会影响镜像
-数据卷默认会一直存在,即使容器被删除
数据卷命令
#创建一个数据卷
docker volume create数据卷名称
#查看所有的数据卷
docker volume ls
#查看指定数据卷的信息
docker volume inspect数据卷名称
#创建一个tomcat容器使用数据卷挂载容器内目录
[root VM_0_5_centos conf]#docker run--name my-tomcat-d-p 8080:8080-v test:/wepapps tomcat:latest
#删除数据卷如果需要在删除容器的同时移除数据卷,可以在删除容器的时候使用dockerrm-v这个命令。
docker volume rm数据卷名称
#清理无主的数据卷
docker volume prune
使用数据卷进行数据挂载
#docker run-it-v主机目录:容器内目录-p主机端口:容器内端口
-v,--volume list Bind mount a volume
default[]
--volume-driver string Optional volume driver for the container
--volumes-from list Mount volumes from the specified container
s
default[]
[root VM_0_5_centos~]#docker run-it-v/data/test:/home centos/bin/bash
#通过docker inspect容器id查看
[root VM_0_5_centos test]#docker inspect 523d102d8586
"Mounts":[
{
"Type":"bind",
"Source":"/data/test",
"Destination":"/home",
"Mode":"",
"RW":true,
"Propagation":"rprivate"
}
]
#启动mysql容器进行数据挂载
[root VM_0_5_centos test]#docker run-d-p 3306: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
#查看mysql容器id
[root VM_0_5_centos test]#docker ps
CONTAINER ID IMAGE PORTS NAMES
0c10d400ecd8 mysql:5.7 0.0.0.0:3306->3306/tcp,33060/tcp mysql01
#进入mysql容器
[root VM_0_5_centos test]#docker exec-it 0c10d400ecd8/bin/sh
#mysql-uroot-p
Enter password:
#进入mysql创建新用户并授权访问
mysql>create user tom identified by"123456";
mysql>grant all on*.*to tom;
mysql>flush privileges;
#开放端口[安全组]
或关闭防火墙
外部测试OK
**挂载的几种具体方式**
#匿名挂载-v容器内路径
只写容器内的路径,没有指定宿主机的路径
[root VM_0_5_centos conf]#docker run-d-P-v/etc/nginx--name nginx01 nginx
#查看所有的volume的情况
[root VM_0_5_centos conf]#docker volume ls
DRIVER VOLUME NAME
local 8a2947a4d63c5c71db7dd331a487cd2dcfacc65022302f42030f7deea799b203
#具名挂载-v卷名:容器内路径
[root VM_0_5_centos conf]#docker run-d-P-v my-nginx:/etc/nginx--name nginx02 nginx
[root VM_0_5_centos conf]#docker volume ls
DRIVER VOLUME NAME
local my-nginx
#查看一下这个卷信息
[root VM_0_5_centos conf]#docker volume inspect my-nginx
[
{
"Driver":"local",
"Labels":null,
"Mountpoint":"/var/lib/docker/volumes/my-nginx/_data",
"Name":"my-nginx",
"Options":{},
"Scope":"local"
}
]
**注意:所有的docker容器内的卷,没有指定目录的情况下都是在`/var/lib/docker/volumes/xxxx/_data`下,如果指定了目录,docker volume ls是查看不到的**
#ro rw改变读写权限
ro#readonly只读说明这个路径只能通过宿主机来操作,容器内部无法操作!
rw#readwrite可读可写
docker run-d-P-v nginx03:/etc/nginx:ro--name nginx03 nginx
docker run-d-P-v nginx04:/etc/nginx:rw--name nginx04 nginx
数据卷容器
多个容器同步数据
案例:多个mysql数据共享
mysql01和mysql02共享/data/mysql/data目录下数据
---------------------------------目前存在问题,待解决----------------------------------
$docker run-d-p 3344:3306-v/data/mysql/conf:/etc/mysql/conf.d-v/data/mysql/data:/var/lib/mysql-e MYSQL_ROOT_PASSWORD=root--name mysql01 mysql:5.7
$docker run-d-p 3355:3306--volumes-from mysql01-e MYSQL_ROOT_PASSWORD=root--name mysql02 mysql:5.7
#挂载配置文件出现报错
ERROR 2002
HY000
:Can't connect to local MySQL server through socket'/var/run/mysqld/mysqld.sock'
2
容器之间的配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。但是一旦持久化到了本地,这个时候,本地的数据是不会删除的!
七、DockerFile构建私有镜像
镜像的定制实际上就是定制每一层所添加的配置、文件。我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,这个脚本就是Dockerfile。
Dockerfile是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
DockerFile构建镜像步骤
1、编写一个DockerFile文件
2、docker build构建成为一个镜像
3、docker run运行镜像
4、docker push发布镜像(DockerHub、阿里云仓库
DockerFile详解
FROM#基础镜像,一切从这里开始构建
MAINTAINER#镜像是谁写的,姓名+邮箱
RUN#镜像构建的时候需要运行的命令
COPY#类似ADD,将我们文件拷贝到镜像中
ADD#步骤,tomcat镜像,这个tomcat压缩包!添加内容到同目录
WORKDIR#镜像的工作目录
VOLUME#挂载的目录
EXPOSE#暴露端口配置
CMD#指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT#指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD#当构建一个被继承DockerFile这个时候就会运行ONBUILD的指令,触发指令。
ENV#构建的时候设置环境变量!
**注意:每个关键字
指令)都必须是大写字母,自上到下执行,每一个指令都会创建提交一个新的镜像层。**
**FROM指定基础镜像**
所谓定制镜像,一定是以一个镜像为基础,在其上进行定制。基础镜像是必须指定的,而FROM就是指定基础镜像,因此一个Dockerfile中FROM是必备的指令,并且必须是第一条指令。
在Docker Hub上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如nginx、redis、mysql、tomcat等;可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如ubuntu、debian、centos、alpine等,这些操作系统的软件库为我们提供了更广阔的扩展空间。除了选择现有镜像为基础镜像外,Docker还存在一个特殊的镜像,名为scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
**FROM scratch**
如果你以scratch为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。对于Linux下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此**直接FROM scratch会让镜像体积更加小巧**。使用Go语言开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为Go是特别适合容器微服务架构的语言的原因之一。
**RUN执行命令**
RUN指令是用来执行命令行命令的。由于命令行的强大能力,RUN指令在定制镜像时是最常用的指令之一。其格式有两种:.
shell格式:RUN<命令>
RUN echo'<h1>Hello,Docker!</h1>'>/usr/share/nginx/html/index.html
exec格式:RUN["可执行文件","参数1","参数2"]
RUN tar-xzf redis.tar.gz-C/usr/src/redis-strip-components=1
RUN make-C/usr/src/redis
RUN make-C/usr/src/redis install
**使用Dockerfile定制nginx镜像**
[root VM_0_5_centos docker]#vim Dockerfile
FROM nginx
RUN echo'<h1>Hello,Docker!</h1>'>/usr/share/nginx/html/index.html
[root VM_0_5_centos docker]#docker build-t mynginx:1.0.
[root VM_0_5_centos docker]#docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mynginx 1.0 ae280f7cccc1 About a minute ago 132 MB
[root VM_0_5_centos docker]#docker run-d-p 8081:80--name mynginx1 mynginx:1.0
[root VM_0_5_centos docker]#curl localhost:8081
<h1>Hello,Docker!</h1>
**COPY复制文件**
格式:
●COPY<源路径>...<目标路径>
●COPY["<源路径1>",..."<目标路径>"]
COPY指令将从构建上下文目录中<源路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置。比如:
COPY package.json/usr/src/app/
源路径;可以是多个,甚至可以是通配符,如:
COPY hom*/mydir/
COPY hom?.txt/mydir/
**ADD更高级的复制文件**
ADD指令和COPY的格式和性质基本一致。但是在COPY基础上增加了一些功能。比如**<源路径>可以是一个URL**,这种情况下,Docker引擎会试图去下载这个链接的文件放到目标路径去。
在Docker官方的Dockerfile最佳实践文档中要求,尽可能的使用COPY,因为COPY的语义很明确,就是复制文件而已,而ADD则包含了更复杂的功能,其行为也不一定很清晰。**最适合使用ADD的场合,就是所提及的需要自动解压缩的场合。**因此在COPY和ADD指令中选择的时候,可以遵循这样的原则,**所有的文件复制均使用COPY指令,仅在需要自动解压缩的场合使用ADD。**
**CMD容器启动命令**
CMD指令的格式和RUN相似,也是两种格式:
●shell格式:CMD<命令>
●exec格式:CMD["可执行文件","参数1","参数2"..]
●参数列表格式:CMD["参数1","参数2..]。在指定了ENTRYPOINT指令后,用CMD指定具体的参数。
Docker不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD指令就是用于指定默认的容器主进程启动命令的。
**ENTRYPOINT入口点**
ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及参数。ENTRYPOINT在运行时也可以替代,不过比CMD要略显繁琐,需要通过docker run的参数--entrypoint来指定。当指定了ENTRYPOINT后,CMD的含义就发生了改变,不再是直接的运行其命令,而是将CMD的内容作为参数传给ENTRYPOINT指令,换句话说实际执行时,将变为:ENTRYPOINT"CMD"。
#CMD和ENTRYPOINT的区别
CMD#指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT#指定这个容器启动的时候要运行的命令,可以追加命令
**ENV设置环境变量**
格式有两种:
●ENV key value
●ENV key1=value1 key2=value2...
这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。
```
ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"
```
**$VERSION**
#使用环境变量
下列指令可以支持环境变量展开:ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD。
**ARG构建参数**
格式:ARG<参数名>[=<默认值>]
构建参数和ENV的效果一样,都是设置环境变量。所不同的是,ARG所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用ARG保存密码之类的信息,因为docker history还是可以看到所有值的。Dockerfile中的ARG指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令docker build中用--build-arg<参数名>=<值>来覆盖。
**VOLUME定义匿名卷**
格式:
VOLUME["<路径1>","<路径2>..]
VOLUME<路径>
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷volume中,为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在Dockerfile中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
VOLUME/data
这里的/data目录就会在运行时自动挂载为匿名卷,任何向/data中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。
**eg:docker run-d-V mydata:/data XXXX
在这行命令中,就使用了mydata这个命名卷挂载到了/data这个位置,替代了Dockerfile中定义的匿名卷的挂载配置。**
**EXPOSE声明端口**
格式:EXPOSE<端口1>[<端口2...]。
EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。
在Dockerfile中写入这样的声明有两个好处:
1.是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;
2.在运行时使随机端口映射时,也就是docker run-P时,会自动随机映射EXPOSE的端口。
**WORKDIR指定工作目录**
格式:WORKDIR<工作目录路径>。
使用WORKDIR指令可以来指定工作目录或者称为当前目录,以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR会帮你建立目录。之前提到一些初学者常犯的错误是把Dockerfile等同于Shell脚本来书写,这种错误的理解还可能会导致出现下面这样的错误:
RUN cd/app
RUN echo"hello">world.txt
如果将这个Dockerfile进行构建镜像运行后,会发现找不到/app/world.txt文件。
**原因:**在Shell中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令,而在Dockerfile中,这两行RUN命令的执行环境根本不同,是两个完全不同的容器。
这就是对Dockerfile构建分层存储的概念不了解所导致的错误。
每一个RUN都是启动一个容器、执行命令、然后提交存储层文件变更。
第一层RUN cd/app的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。
因此如果需要改变以后各层的工作目录的位置,那么应该使用WORKDIR指令。
**USER指定当前用户**
格式:USER<用户名>
USER指令和WORKDIR相似,都是改变环境状态并影响以后的层。WORKDIR是改变工作目录,USER则是改变之后层的执行RUN,CMD以及ENTRYPOINT这类命令的身份。
当然,和WORKDIR一样,USER只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
RUN groupadd-r redis&useradd-r-g redis redis
USER redis
RUN["redis-server"]
**HEALTHCHECK健康检查**
格式:HEALTHCHECK[选项]CMD<命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,可以屏蔽掉其健康检查指令
HEALTHCHECK指令是告诉Docker应该如何进行判断容器的状态是否正常,这是Docker1.12引入的新指令。通过该指令指定一行命令,用这行命令来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。
一个镜像指定了HEALTHCHECK指令后,用其启动容器,初始状态会为starting,在执行健康检查成功后变为healthy,如果连续一定次数失败,则会变为unhealthy。
HEALTHCHECK支持下列选项:
●--interval=<间隔>:两次健康检查的间隔,默认为30秒;
●--timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认30秒;
●--retries=<次数>:当连续失败指定次数后,则将容器状态视为unhealthy,默认3次。
为了帮助排障,健康检查命令的输出包括stdout以及stderr都会被存储于健康状态里,可以用docker inspect来查看。
**ONBUILD**为他人做嫁衣裳
格式:ONBUILD<其它指令>。
ONBUILD是一个特殊的指令,它后面跟的是其它指令,比如RUN,COPY等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
Dockerfile中的其它指令都是为了定制当前镜像而准备的,唯有ONBUILD是为了帮助别人定制自己而准备的。
#1.编写Dockerfile文件重新构建centos镜像
[root VM_0_5_centos docker]#vim dockerfile-centos
FROM centos
MAINTAINER shuaige<669104343 qq.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.执行docker build命令进行构建
#命令docker build-f文件路径-t镜像名:[tag].
[root VM_0_5_centos docker]#docker build-f mydockerfile-centos-t mycentos:1.0.
#3.启动容器并进入测试ifconfig、vim
[root VM_0_5_centos docker]#docker run-it mycentos:1.0
制作tomcat镜像
#资源准备创建/usr/local/docker/tomcat目录,将jdk、tomcat、readme压缩包放到该目录下
#1.编写Dockerfile文件
[root VM_0_5_centos tomcat]#vim Dockerfile
FROM centos
MAINTAINER shuaige<669104343 qq.com>
COPY readme/usr/local/readme
ADD jdk-8u192-linux-x64.tar.gz/usr/local/
ADD apache-tomcat-8.0.53.tar.gz/usr/local/
RUN yum-y install vim
ENV MYPATH/usr/local
WORKDIR$MYPATH
ENV JAVA_HOME/usr/local/jdk1.8.0_192
ENV CATALINA_HOME/usr/local/apache-tomcat-8.0.53
ENV PATH$PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib
EXPOSE 8080
CMD/usr/local/apache-tomcat-8.0.53/bin/startup.sh&&tail-F/usr/local/apache-tomcat-8.0.53/logs/catalina.out
#2.构建镜像
[root VM_0_5_centos tomcat]#docker build-t mytomcat:1.0.
#3.运行镜像
docker run-d-p 8080:8080-v/data/tomcat/webapps:/usr/local/apache-tomcat-8.0.53/webapps-v/data/tomcat/logs/:/usr/local/apache-tomcat-8.0.53/logs--name tomcat01 mytomcat:1.0
#4.本地发布项目测试测试OK
http://49.234.90.25:8080/test/index.html
其他制作镜像方式
**docker save和docker load**
Docker还提供了docker load和docker save命令,用以将镜像保存为一个tar文件,然后传输到另一个位置.上,再加载进来。这是在没有Docker Registry时的做法,现在已经不推荐,镜像迁移应该直接使用Docker Registry,无论是直接使用Docker Hub还是使用内网私有Registry都可以。
例如:保存nginx镜像
docker save nginx|gzip>nginx-latest.tar.gz
然后我们将nginx-latest.tar.gz文件复制到了到了另一个机器上,再次加载镜像:
docker load-i nginx-latest.tar.gz
八、Docker镜像发布
1、Docker Hub
目前Docker官方维护了一个公共仓库Docker Hub,其中已经包括了数量超过15,000的镜像。大部分需求都可以通过在Docker Hub中直接下载镜像来实现。
**注册登录**
可以在https://hub.docker.com免费注册一个Docker账号。在命令行界执行docker login输入用户名及密码来完成在命令行界面登录DockerHub,通过docker logout退出登录。
**拉取镜像**可以通过docker search命令来查找官方仓库中的镜像,并利用docker pull命令来将它下载到本地。
**推送镜像**
用户也可以在登录后通过docker push命令来将自己的镜像推送到DockerHub。
#0.查询将要上传的镜像ID
[root VM_0_5_centos~]#docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/hello-world latest bf756fb1ae65 5 months ago 13.3 kB
#1.给将要上传的镜像打标签,注意仓库名一定要写正确
#docker tag镜像ID用户名/仓库名
[root VM_0_5_centos~]#docker tag bf756fb1ae65 shuaige66/hello-world
#2.登录docker仓库
[root VM_0_5_centos~]#docker login
#3.推送镜像到Docker Hub
[root VM_0_5_centos~]#docker push shuaige66/hello-world
2、阿里云仓库
#1.登录阿里云Docker Registry用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码。
$sudo docker login--username=18271267916 registry.cn-hangzhou.aliyuncs.com
#2.从Registry中拉取镜像
$sudo docker pull registry.cn-hangzhou.aliyuncs.com/shuaige/test:[镜像版本号]
#3.将镜像推送到Registry请根据实际镜像信息替换示例中的[ImageId]和[镜像版本号]参数。
$sudo docker login--username=18271267916 registry.cn-hangzhou.aliyuncs.com
$sudo docker tag[ImageId]registry.cn-hangzhou.aliyuncs.com/shuaige/test:[镜像版本号]
$sudo docker push registry.cn-hangzhou.aliyuncs.com/shuaige/test:[镜像版本号]
#示例
#1.使用"docker images"命令找到镜像,将该镜像名称中的域名部分变更为Registry专有网络地址。
[root VM_0_5_centos docker]#docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mynginx 1.0 ae280f7cccc1 5 hours ago 132 MB
#2.使用"docker tag"命令重命名镜像,并将它通过专有网络地址推送至Registry。
docker tag ae280f7cccc1 registry.cn-hangzhou.aliyuncs.com/shuaige/mynginx:1.0
#3.推送到阿里云镜像仓库
docker push registry.cn-hangzhou.aliyuncs.com/shuaige/mynginx:1.0
3、私有仓库
有时候使用Docker Hub这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用。比如,基于公司内部项目构建的镜像。
docker-registry是官方提供的工具,可以用于构建私有的镜像仓库。
**安装运行docker-registryv**
通过获取官方registry镜像来运行。默认情况下,仓库会被创建在容器的/var/lib/registry目录下,可以通过-v参数来将镜像文件存放在本地的指定路径。
[root VM_0_5_centos~]#docker run-d-p 5000:5000--restart=always-v/opt/data/registry:/var/lib/registry--name registry registry
**在私有仓库上传、搜索、下载镜像**
创建好私有仓库之后,就可以使用docker tag来标记一个镜像,然后推送它到仓库。
#先在本机查看已有的镜像。
[root VM_0_5_centos~]#docker images
#使用docker tag将hello-world:latest这个镜像标记为127.0.0.1:5000/hello-world:latest
[root VM_0_5_centos~]#docker tag hello-world:latest 127.0.0.1:5000/hello-world:latest
#使用docker push上传标记的镜像
[root VM_0_5_centos~]#docker push 127.0.0.1:5000/hello-world:latest
9c27e219663c:Pushed
latest:digest:sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042 size:525
#用curl查看仓库中的镜像
[root VM_0_5_centos docker]#curl 127.0.0.1:5000/v2/_catalog
{"repositories":["hello-world"]}
#如果可以看到{"repositoris":["hello-world"]},表明镜像已经被成功上传了。
#先删除已有镜像,再尝试从私有仓库中下载这个镜像。
docker image rm 127.0.0.1:5000/hello-world:latest
docker pull 127.0.0.1:5000/hello-world:latest
注意:
如果你不想使用127.0.0.1:5000作为仓库地址,比如想让本网段的其他主机也能把镜像推送到私有仓库。你就得把例如192.168.74.149:5000这样的内网地址作为私有仓库地址,这时你会发现依然无法成功推送镜像。
**原因:**Docker默认不允许非HTTPS方式推送镜像。我们可以通过Docker的配置选项来取消这个限制。
```json
{"regstry-mirror":["https://registry.docker-cn.com"],
"insecure-registries":["192.168.74.149:5000"]}
```
参考网址
#docker版本及内核兼容性选择
https://blog.csdn.net/qq767852841/article/details/106520589/
https://www.jianshu.com/p/348dca0e314c
https://docs.docker.com/engine/release-notes/prior-releases/
#docker使用
https://www.cnblogs.com/peng104/p/10296717.html
https://www.cnblogs.com/zhangxingeng/p/11236968.html
https://blog.csdn.net/forezp/article/details/82454600
https://www.javazhiyin.com/62555.html
https://www.javazhiyin.com/?post_type=post&s=docker
https://blog.csdn.net/xiaozecheng/article/details/106145593
https://blog.csdn.net/xiaozecheng/article/details/106165491