文章目录


一、准备

安装Docker时,最好不要安装在windows下,最好安装在linux中,否则容易出现各种各样的问题;
另外注意:Centos6不支持Docker,Centos7才支持;

二、Docker的安装

第一步:直接开启一个虚拟机,登录进去后,使用ip a命令查看ip地址,查看到当前虚拟机ip地址是192.168.11.10

在这里插入图片描述

然后用Xshell 7连接该虚拟机,连接地址填刚才的虚拟机ip地址;

在这里插入图片描述

第二步:在家目录下使用命令:curl -fsSL get.docker.com -o get-docker.sh ,该命令的作用是将docker的下载脚本拉取到本地;

①这个脚本的大小只有几kb,很快就能下载完成;这一步需要你联网;
下载成功后,我们执行ls命令就能看到,当前目录下有了一个"get-docker.sh"的脚本,这就代表下载成功;
在这里插入图片描述

第三步:执行命令:sudo sh get-docker.sh --mirror Aliyun,表示以管理员身份执行,–mirror Aliyun表示从阿里云下载;

①这一步会比较慢,请耐心等待;
②中间可能会报一些错,比如连接镜像失败等,你不管,等着就行;
③出现下图这个界面,就代表执行成功,只要不报红色的错误,就代表docker就在你的虚拟机上安装完毕了;
在这里插入图片描述

第四步:使用命令:sudo systemctl enable docker,该命令意思是将docker加入开机自启动的列表

①其他命令:
sudo systemctl start docker 开启docker(restart重启,stop关闭,status查看状态)

②这样就代表加入成功;
在这里插入图片描述

使用命令sudo systemctl start docker启动docker

在这里插入图片描述

第五步:启动完成后,不要先急着操作,因为docker服务最好运行在docker组里面,所以我们要先将当前用户加入到docker组里面;

执行命令:sudo groupadd docker,意思是创建名为docker的组;

执行命令:sudo usermod -aG docker $USER,意思是将当前用户加入docker组,多ler USER代表当前用户;

①由于我当前是使用管理员账户登录进来的,也就是root用户,所以当前用户就是root用户,也就是我们将root用户加入了docker组里了; 本身linux中是自带一个名为root的组的,root用户就是属于root组,也就是说:现在我们将root用户加入了docker组中;
②这里可能会提示"docker组已经存在",不用管它,继续执行命令即可;
在这里插入图片描述

第六步:执行命令: docker version, docker info,测试docker是否安装成功;

执行命令: docker version 查看docker的版本,这里可以看到docker是cs架构,有服务端,也有客户端;

在这里插入图片描述

执行命令: docker info查看docker的详细信息;

在这里插入图片描述

注意:只要你执行这两个命令能像上面一样正常显示,就代表你的docker安装没有问题;

三、Docker中的核心概念

1.镜像:Image,一个镜像就代表一个软件;

2.容器:Container,一个镜像运行一次就会生成一个容器,容器就是一个运行的软件服务;

①注意:一个镜像运行一次就会生成一个容器,比如我现在有一个tomcat镜像,我将其运行两次,我就会得到两个tomcat软件服务,也就是两个tomcat容器;也可以说我就有了两个tomcat服务器;

3.远程仓库(也叫互联网中心仓库):Respostory,仓库就是docker公司开发出来的,用来存储所有软件镜像的服务器,我们每次要下载一个软件,就可以到仓库中去下载;

①仓库的web地址:docker hub https://hub.docker.com/
注意:这个网页中,如果我们不需要把自己的软件镜像上传到docker仓库,我们就不需要注册;
在这里插入图片描述

4.如何下载镜像?

①直接在docker hub中搜索即可
在这里插入图片描述
②标注了OFFICIAL IMAGE标志的,就代表是官方推出的镜像,其他的都是像我们这种玩家上传的镜像,我们最好用官方的;
在这里插入图片描述

5.本地仓库:从远程仓库将镜像下载下来后,可以将镜像存储到本地仓库中,等到以后没网的时候,就不用再去远程仓库拉取了,直接在本地拉取即可;

四、Docker的架构图

在这里插入图片描述
① 我们从远程仓库下载镜像,使用的是pull命令,表示从远程仓库拉取;
②镜像被拉取下来后,会同步到我们的本地仓库;
③由于远程仓库是搭建在国外,访问起来很慢,所以我们要将其改成阿里云镜像仓库

五、docker如何配置阿里云镜像加速?

第一步:登录到阿里云官网

第二步:找到控制台,点击左上角的"三横杠"图标

在这里插入图片描述

第三步:找到容器镜像服务,或者直接搜索容器镜像服务

在这里插入图片描述

第四步:再点击最左边的镜像工具下面的镜像加速器;

在这里插入图片描述

第五步:在家目录下使用命令:sudo mkdir -p /etc/docker 创建目录,意思就是在家目录下创建了一个etc目录,又在etc下创建了一个docker目录;

第六步:再执行命令:

sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors":	["https://3j5z30rj.mirror.aliyuncs.com"]
}
EOF

以下就是执行成功的样子。
在这里插入图片描述

第六步: 执行命令:sudo systemctl daemon-reload,sudo systemctl restart docker

①以下就是执行成功的样子:
在这里插入图片描述
②注意避坑,如果你在执行sudo systemctl restart docker启动docker时报如下错:
在这里插入图片描述
多半是由于你刚才写的daemon.json配置文件错了,你可以先使用命令:sudo dockerd --debug检查一下哪里出了问题,如果确实是配置文件出现问题,就请删除后重新添加配置文件;
③注意:在执行写入配置文件的命令时,"registry-mirrors"前面一定不要自己加空格或者缩进符,否则等你写完,它会自己用key.json这几个字替换掉你的缩进符,这里非常坑;

sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors":	["https://3j5z30rj.mirror.aliyuncs.com"]
}
EOF

第七步:使用命令:docker info,对结果进行检验

①如果这里显示是阿里云,就代表你已经将远程仓库地址换成了国内的阿里云镜像地址,以后就是从阿里云仓库拉取镜像;
在这里插入图片描述

六、最后校验一次docker是否安装成功

执行命令:docker run hello-world,只要你执行成功,就代表安装docker成功
在这里插入图片描述

七. docker run + 镜像名称 :运行镜像的命令

以下面这个案例为例子:

[root@localhost docker]# docker run hello-world

①这里直接run了一个名为hello-world的镜像,首先docker会在本地仓库找有没有这个镜像,如果没有镜像就会到/etc/docker/目录下的daemon.json文件配置好的远程仓库或者镜像地址将镜像pull下来;

八.关于镜像的基本操作

1.查看本地仓库有哪些镜像:docker image ls 或者 docker images 或者 docker images -a

在这里插入图片描述
①名词解释:
RESPOSITORY表示镜像名称,不能理解为仓库;
TAG表示镜像的版本,这里hello-world镜像就是最新的版本;
其他几个名词比较简单,就不解释了;

2.根据名称查看本地仓库中的某些镜像:docker images 镜像名

[root@localhost docker]# docker images tomcat

这个意思就是:只查看本地仓库中跟tomcat有关的镜像,因为有可能你的本地仓库中镜像特别多,这样比较方便;

3.查看本地仓库中的关于某些镜像的镜像id:docker images tomcat -q

在这里插入图片描述
意思就是:查看本地仓库中关于tomcat的镜像,并且只展示出它们的镜像id;

4.下载一个镜像:docker pull 镜像名称 : 版本号(中间用冒号隔开)

注意:如果你在镜像名称后面没有跟版本号,那么就会默认下载最新版本的镜像;
①比如下面的docker pull redis,就是默认下载最新的redis;

[root@localhost docker]# docker pull redis

5.普通的删除镜像:docker image rm 镜像名(注意:镜像名需要由名称+tag组成) 或者 docker image rm 镜像id

注意:
①如果这个镜像被使用过一次,这种普通的删除就删除不掉;

6.强制删除镜像:docker image rm -f 镜像名,或者 docker image rm -f 镜像id

[root@localhost docker]# docker image rm -f hello-world:latest

7.综合应用1:docker image rm -f $(docker images tomcat -q),意思是删除本地仓库中所有关于tomcat的镜像

8.综合应用2:docker image rm -f $(docker images -q),意思是删除本地仓库中所有镜像

这里会首先执行$()里面的命令,得到结果后,再将结果传给删除命令;

九. 容器的基本操作

1.docker ps,查看当前docker引擎中正在运行的容器有哪些。

如图所示,会返回一个结果列表
在这里插入图片描述在这里插入图片描述
①当我们把镜像跑起来,生成一个容器后,docker会给每个容器取一个名字,我们可以通过名字定位到该容器,也可以通过容器id定位到它;

2.docker ps -a,查看所有的容器,包括正在运行的和没有运行的。

3.docker ps -a -q 或者 docker ps -aq 或者 docker ps -qa,查看所有的容器的id,包括正在运行的和没有运行的。

4.docker ps -q ,查看正在运行的容器的id。

5.docker pull 镜像名,从远程仓库拉取一个镜像。

6.docker run 镜像名或者镜像id,运行一个容器。(我们不会用这种方式启动一个容器)

①如何克隆一个窗口?
当我们使用docker run tomcat:8.0运行起来一个tomcat后,
可以发现它跟我们自己平常启动一个tomcat没有区别,输出的信息都差不多,默认监听的端口也是8080;

在这里插入图片描述
但是这里会有一个问题:tomcat跑起来后,我们当前的会话窗口就阻塞起来了,就干不了其他事情,只有通过ctrl+c才能结束掉这个会话,如果我们想继续操作其他命令的话,就可以通过右键会话列表,再点击克隆会话,就可以新开一个窗口,我们就可以在这个新窗口干其他事了;
在这里插入图片描述

7.案例:

①我先用docker开启一个tomcat服务;
②然后尝试访问这个tomcat服务,当前我开启的虚拟机的ip是:192.168.11.10,因为tomcat的端口默认就是8080,所以我们在浏览器上直接访问192.168.11.10:8080,结果访问不上;
③访问不上的原因是,你的虚拟机的防火墙没关,可以先使用命令:systemctl status firewall,查看防火墙状态,再使用stop firewall 关掉防火墙
④检查防火墙状态
在这里插入图片描述
⑤关闭防火墙
在这里插入图片描述
⑥但是我们关闭防火墙之后,我们再访问依然访问不了;
这是因为docker是操作系统级别的隔离,你在docker中运行一个tomcat,相当于docker里单独给tomcat起了一个操作系统,而tomcat就是运行在这个操作系统中,它跟docker的宿主机的操作系统就不是同一个,里面的端口号8080跟外面宿主机的8080端口根本就不是同一个东西;
⑦注意:如果你又起了一个tomcat,那么docker又会再给这个tomcat起一个操作系统;
在这里插入图片描述
⑧所以我们根本不会用docker run这个命令跑一个容器,因为这种方式跑起来的容器,外部根本就访问不了它内部的服务;
解决办法是:我们必须在启动容器时,让容器内服务的端口与外部宿主机的端口做个一对一的映射,也就是容器服务的端口是8080,外部的8080端口跟它关联起来,这样操作后就可以访问容器内的服务了;
这时候就需要下一个命令了;

8. docker run -p 8080:8080 tomcat:8.0,意思就是启动tomcat8.0镜像,同时将宿主机的8080端口跟容器的8080端口做映射;8080:8080前面的8080是宿主机的端口,后面的8080是容器内服务的端口;注意这里的宿主机是指docker的宿主机,而不是我们真正的物理机,我们真正的物理机是windows系统,我们自己用vm开了一个虚拟机(linux系统),然后在这个linux系统中装了一个docker,而docker是操作系统级别的隔离,docker会为它的容器再创建一个操作系统;所以docker的宿主机的其实就是我们开的这个虚拟机192.168.11.10; 所以你才会看到我上面做实验,访问tomcat服务时,是用的192.168.11.10:8080,而不是localhost:8080,因为192.168.11.10:8080是访问虚拟机的8080端口,localhost:8080是访问我们真正物理机的8080端口;

8.1注意:docker run -p 8080:8080 tomcat:8.0里面可以跟多个-p,表示映射多个端口;

①比如:mq就有两个端口每一个15672,一个5672,我们就要把宿主机的15672,5762跟容器的15672,5762进行映射;
就要这么写docker run -p 15672:15672 -p 5672:5672 rabbitmq:latest

8.2注意:当使用docker run -p 8080:8080 tomcat:8.0命令,在将宿主机端口与容器内服务的端口做映射时,防火墙必须开启,否则会报错,如果我们关闭了,就使用命令:systemctl restart firewalld 重新启动防火墙即可;

在这里插入图片描述

8.3 如果没有开启防火墙就使用docker run -p 8080:8080 tomcat:8.0命令,则会报错,如下图所示;

[root@localhost ~]# docker run -p 8080:8080 tomcat:8.0
docker: Error response from daemon: driver failed programming external connectivity on endpoint adoring_antonelli (b6c3264ad1b0f17d3e3c4d0c6da0cdd6afe7abc6b834fe996d9738c4dd53e572):  (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 8080 -j DNAT --to-destination 172.17.0.2:8080 ! -i docker0: iptables: No chain/target/match by that name.
 (exit status 1)).

8.4 最后我们使用虚拟机的ip+8080端口号来访问tomcat,测试就能访问成功;

在这里插入图片描述

9. 防火墙的相关操作

9.1 systemctl status firewalld,检查防火墙状态 (注意wall后面有个d)

这就表示防火墙是开着的;
在这里插入图片描述

9.2 stop firewalld,关闭防火墙 (注意wall后面有个d)

9.3 systemctl restart firewalld 重新启动防火墙 (注意wall后面有个d)

10. 在docker中启动多个tomcat,该怎么做呢?

①比如我先使用docker run -p 8080:8080 tomcat:8.0命令,将虚拟机的8080端口跟第一个tomcat容器的8080端口进行映射,我要再开一个tomcat,此时就要使用docker run -p 8081:8080 tomcat:8.0命令,将虚拟机的8081端口映射跟第二个tomcat容器的8080端口进行映射;
②为什么第二个要用8081呢?因为虚拟机的8080已经被上一个tomcat占用了,为了避免端口冲突;
③那为什么第一个容器是8080端口,第二个容器也是80800端口,不怕它们端口冲突吗?
答:不用怕,因为在docker中,每个容器都有自己单独的操作系统,这就是操作系统级别的隔离,就算端口相同,也因为是不同的操作系统,所以不会出现端口冲突;

11. 后台启动容器 -d,举例:docker run -p 8080:8080 -d tomcat:8.0 加了一个-d就表示后台启动tomcat;

为什么要有后台启动?如果没有后台启动,你每在docker中启动一个容器,你就要新复制一个会话,很不方便,所以可以使用后台启动;

12. 启动容器时给容器取一个好记的名字 --name:举例:docker run -p 8080:8080 -d --name tomcat01 tomcat:8.0

生产环境中推荐给运行的容器起一个名字,好区分;

13.关闭某个容器:docker stop 容器id或者容器名称

14.启动某个容器:docker start 容器id或者容器名称

①注意这个命令跟docker run命令的区别,docker start是将已经运行过但是被现在被关闭了的容器再启动起来;docker run是跑一个镜像,这个镜像还没有形成一个容器;

15.重启某个容器:docker restart 容器id或者容器名称

16.暂停某个容器:docker pause 容器id或者容器名称

①容器暂停期间,它的服务是不可用的

17.恢复某个暂停的容器:docker unpause 容器id或者容器名称

18.杀死容器:docker kill 容器id或者容器名称

①注意docker stop跟docker kill的区别,docker stop命令只是发出了一个我要结束容器的指令,后台会慢慢结束,有可能这个慢慢结束的时间内,后台已经做了很多的东西,这种关闭容器的方式比较温柔;但是docker kill是直接杀死容器,不给后台处理时间,立即杀死,这种方式比较粗暴;

19.docker rm 容器id或者容器名称

普通的删除容器,注意这种方式只能删除已经停止的容器,正在运行的容器不能删除;

20.docker rm -f 容器id或者容器名称

强制删除容器,这种方式是强制删除,即使这个容器当前是运行状态也能删除;

21.进阶:删除所有容器:docker rm -f $(docker ps -a -q),先通过docker ps -a -q得到所有容器的id,再传给外层,由外层进行删除;

22.docker logs -f -t 容器id或者容器名称;

实时查看容器内部的日志:
①注意:-f表示实时展示日志,没加-f就不是实时的;
② -t,表示展示日志的时候带有时间,注意这里的时间分为宿主机的时间,与容器内的时间,如果没加-t,就只会展示容器内的时间,加了就会多展示宿主机的时间;
这两个时间可能是不同步的,可以进行设置,但是我目前不知道怎么操作;

23.docker exec -it 容器id或者容器名称 bash

进入容器的内部,-it表示以交互模式进行;
①解释一下进入容器内部是什么意思:我们现在做的这些操作,都是在docker的宿主机,也就是虚拟机中进行的操作,比如我们执行的docker run -p这些操作,实际上都是在宿主机中执行的;如果我们想要进入docker容器里那个操作系统看一看,就可以使用这个命令:docker exec -it 容器id或者容器名称 bash;
②必须加上it,表示以交互模式进入,否则会报错
③容器名称或者id后面必须加上bash,我们平时通过xshell窗口跟操作系统进行交互,实际上就是bash命令窗口,所以这里要加上bash,告诉我是要跟容器内部的操作系统进行bash命令窗口的交互;
在这里插入图片描述
出现下图这样的就表示进入了容器的操作系统:
在这里插入图片描述

24.exit

退出容器的内部,直接输入exit回车即可,就又可以回到docker宿主机的操作系统;

25.docker cp 容器id:容器中文件或目录 要存放到的主机目录

将容器中的文件或者目录拷贝到宿主机中:

26.docker cp 主机文件或目录 容器id:要存放到的容器中的目录

将宿主机中的文件或者目录拷贝到容器中:

27.进阶:我要部署一个web项目(war包项目)该怎么做呢?(重要)

①首先我们需要知道的是,我们要部署一个项目,只需要将这个项目放到tomcat容器的webapps目录下就可以了;

28.docker top 容器id或者名称

查看容器内运行的进程;

29.docker inspect 容器id或者名称

查看容器的详细信息,inspect 是检查的意思;

30.容器的数据卷机制:

①什么是数据卷?Data Volume
数据卷的作用是用来实现容器中的数据跟宿主机中数据进行同步的;
详细解释:比如我现在有一个war包,我要对其进行部署,首先我要将其发布到我们的虚拟机上,然后启动tomcat容器,再将war包通过docker cp命令将war包从虚拟机上的目录,拷贝到tomcat容器中的/webapps目录下,这样我们的war包就发布成功了;
但是这有一个问题:如果我的war包进行了更新,那么每次更新,我都要通过docker cp命令将war更新到tomcat容器中,这样就显得很麻烦;
所以docker就提供了一种机制:叫数据卷机制:当你将文件从宿主机的A目录拷贝到容器中的B目录时,无论A目录中的该文件发生变化,还是B目录中发生变化,都会同步影响另一个目录中的该文件,就达到了一个更新的目的,不用我们每次再docker cp了;
②但是需要注意,我们需要将宿主机中的目录跟容器中的目录进行绑定,文件的数据同步才能生效,否则这根本就是两个毫不相干的目录,凭什么让他们产生联系?
③那我们到底什么时候进行数据卷的绑定呢?
注意:一定要在该容器首次运行的时候进行绑定,后面再绑定是不会生效的;

30.1. 数据卷的绑定方式一:

①使用绝对路径进行绑定:
只需要执行命令:docker run -v 宿主机目录的绝对路径:容器内目录的绝对路径 -p 8080:8080 -d --name tomcat01 tomcat:8.0,-v表示Volume;
②注意:这里绝对路径是指/root/user/local这种绝对路径,我们在windows中绝对路径一般是C:/user/admin这样的,但是在linux系统中是没有C盘,D盘的,所以Linux系统中绝对路径就不是以盘号开头了;

30.2 数据卷的绑定方式二:

①使用给数据卷取别名的方式绑定:
只需要执行命令:docker run -v aaa:容器内目录的绝对路径 -p 8080:8080 -d -name tomcat01;
②注意:这里的aaa就是给数据卷取的别名,你随便取什么名字都可以;在方式一中就是要指定宿主机中目录的绝对路径,但是用第二种方式,就是数据卷帮我们维护宿主机目录了,我们就不用管了,只需要取一个别名即可;

31. 将容器制作成一个镜像

31.1 为什么需要将容器制作成镜像?

举个例子:当我们把项目部署到一个tomcat容器后,当我们以后还想到其他地方部署这个项目,我们就可以将已经已经部署了好项目的容器制作成镜像,下次就直接pull下来即可使用,就不用我们再单独部署一次了,就很方便;

31.2 docker commit -m “关于该镜像的说明信息” -a “该镜像的作者信息” 容器id或者容器名 你自己取的镜像名称:镜像的版本号

commit命令,将容器制作成镜像;
举例:docker commit -m “deploy tets proect in webapps” -a “haixu” e77 tomcat-test:8.0

32. 镜像的恢复和备份机制

32.1 docker save 镜像名 -o 存放目录/文件名.tar,备份镜像

①其实就是将镜像打包,打成的包是tar包,也就是linux下的压缩包;
-o表示output,也就是输出成tar包;
②给tar包取名时,我们强烈建议你带上原镜像的名称跟tag标签,比如你要将tomca:8.0镜像打包成tar包,我建议你给tar包取名为tomcat-8.0.tar,这样等你以后在恢复镜像的时候,你才知道你这个tar包到底是个什么东西,是什么版本;
③文件名前面要跟存放目录,否则docker也不知道你要把生成好的tar包放在哪里;
举例:docker save tomcat:8.0 -o /root/tomcat-8.0.tar 就表示你要将tar包放在/root目录下;
注意:如果你没有加目录的话,生成的tar包会默认放在当前目录;

32.2 docker load -i 文件名.tar,恢复镜像,

①其实就是将被制作成tar包的镜像还原,也就是重新载入到linux系统中;
-i表示inout,也就是载入的意思;
②你自己将镜像打成tar包后,可以将其传给其他同事,他下载下来后,只需要使用load命令就能这个tar包恢复成镜像,这就是load命令的意义;

十. docker镜像的原理

1.镜像为什么这么大?

我们自己到官网下载的tomcat仅10M左右,为什么从docker hub下载下来的tomcat镜像到达了356M之多?
这是因为每一个容器都有自己独立的操作系统,还有对应的软件服务,比如你这个容器是mysql容器,那么这个容器中就会包含一个精简版的linux系统,还有mysql服务;容器又是由镜像运行得来的,所以追根溯源,镜像中就应该包含linux系统跟对应软件服务的一些必要的文件;所以镜像会很大;

2.镜像是什么?

在这里插入图片描述

3.联合文件系统

docker中有个base镜像的概念,什么叫base镜像呢?
以tomcat镜像为例,它里面不仅仅包含了tomcat这个软件服务,还包含了一个最基础的linux系统镜像,还有jdk的运行环境的镜像;这些底层的镜像就叫做base镜像,所以说一个tomcat镜像就是由一个个的base镜像一层一层套起来,然后最外层就包裹的是tomcat镜像;

十一. docker安装mysql

1. 第一步:从docker hub拉取mysql镜像

docker pull mysql:5.6

mysql主流是5.x版本跟8.x版本,中间没有6跟7,这里以5.6为例;

2.第二步:给mysql设置密码,容器自启动,数据卷持久化

doocker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=115409 -d--name mysql01 --restart=always -v /root/mysql/data:/var/lib/mysql mysql:5.6

①-e,e表示环境变量,-e表示对容器中的某个环境变量设置值,这就是对mysql的密码进行设置,如果不进行设置,你是无法使用mysql的,账户名不用设置,默认就是root;

②- -restart=always表示你每次重启docker后,本容器都会跟着一起启动,不用你开启docker后,再单独run一次容器;

③-v,数据卷,表示要设置容器内目录跟宿主机目录之间的映射;
第一点:为什么要设置数据卷?
因为mysql容器是要用来存储数据的,如果不设置数据卷,那么你这个mysql容器重启或者关闭后,里面的数据就全部丢失了,只要你设置了数据卷,将mysql容器内存储数据的那个目录跟宿主机的目录做映射,就算外面你容器重启后,mysql的数据还是被保存在了宿主机你选择的目录中;所以数据卷的主要作用就是用来做数据的持久化的,因为将数据给容器自己管理并不安全,必须要保存到宿主机中;
第二点:上面命令中的 -v /root/mysql/data:/var/lib/mysql
冒号后面的/var/lib/mysql,这个是mysql:5.6这个镜像中用来存储数据的目录,是在docker hub中查出来的,以后再下一个镜像,如果不知道这个镜像中数据是存放在哪个目录的,就到docker hub里面取查;
冒号前面的 /root/mysql/data,这个目录是我们自己取的一个绝对路径(随便我们怎么取),我们要把容器中的数据存储到哪里,这里填哪个路径,这里我们也可以不指定,直接取一个别名也行,比如aaa:/var/lib/mysql 或者 mysqldata:/var/lib/mysql,docker会自己帮我们创建一个默认的路径,然后将数据存储在里面;

3. 将mysql中的数据转换为sql文件

3.1 为什么要转换为sql文件?

在上面的例子中,我们通过数据卷的方式,在宿主机中指定了mysql容器数据存放的位置,这样的好处是,就算你的mysql容器被停了或者重启了,等你下次再开一个mysql容器时,docker依然可以宿主机存放数据库数据的目录中读取数据,数据依然不会丢失;
但是设想一个场景,加入我要将我A宿主机上的mysql容器中的数据,转移到另一台B宿主机上的mysql容器中,此时再使用数据卷就不能达到目的了,因为已经跨机器了;
解决办法就是:将A宿主机中mysql容器里的数据转换为可以执行的sql文件,再将这个sql文件交给B宿主机执行一次,就能达到数据迁移的目的了;

3.2 如何转换?

1.备份mysql容器中的全部数据,这种方式会把包括库的信息,表信息,以及表中的数据,都保存为sql文件:
docker exec mysql容器id或者名称 sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /rooot/all-databases.sql

①docker exec mysql容器id或者名称 sh 就表示进入mysql容器,并且以sh的模式进入,以bash模式进去就是进入后,要跟bash命令窗口做交互,而以sh模式进入,就不是做交互了,而只是为了进去执行脚本文件,sh是脚本的缩写;

②那具体是执行哪个脚本呢?就是被-c '脚本’包裹起来的脚本,这里必须要有一个 -c ,然后跟一对单引号,被单引号包起来的就是要执行的脚本;

③’exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"’ 这个脚本中,exec应该是表示执行的意思,我也不太清楚,但是不重要,记住这里要加exec就行;
mysqldump是一个关键字,表示将某些东西转换为sql文件,
那到底要转换什么为sql文件呢?–all databases就表示要把当前mysql容器中的一切内容都保存为sql文件,比如所有的库信息,表信息,以及所有数据;(注意,一个mysql中可能有好几个数据库,这条命令会把所有的库完整的保存下来)

④ > /rooot/all-databases.sql ,> 这个箭头符号的意思,就是要将前面的数据存储在哪里,箭头后面跟着的,就是sql文件存放的位置;
/rooot/all-databases.sql,箭头后面要跟宿主机中的绝对路径,最后你随便给这个sql文件取一个好记的名字,mysql容器执行完这个命令后,会自己给你创建出这个sql文件,并往里面写入数据;

2.只备份其中某个库的数据
docker exec mysql容器id或者名称 sh -c 'exec mysqldump --databases 库名 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /rooot/your-databse.sql

①只备份其中某个库的数据时:就使用–all-databases了,而是使用–databases 库名来指定你要将mysql容器中的哪个库备份为sql文件,其他的备份全部库没有区别;

3.备份某个库的表结构等信息,但是不要表中的数据
docker exec mysql容器id或者名称 sh -c 'exec mysqldump --no-data --databases 库名 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /rooot/your-databse.sql

①如果你只想将mysql容器里面的库信息,表结构保存为sql,不想保存表里面的数据,就在命令里加上–no-data,就不会保存数据;

十二.docker安装redis

1.第一步:从docker hub拉取redis镜像

docker pull redis:5.0.12

2.第二步:写一个配置文件

2.1 新建一个redis.conf文件,在里面写入内容:

appendonly yes
requirepass 115409
bind 0.0.0.0
第一行表示:开启redis的持久化,
第二行表示给redis设置密码为115409,
第三行表示开放redis的访问权限,写成0.0.0.0时就表示所有外部机器都能连接我的redis,只要他密码输入正确;默认值是127.0.0.1,也就是只有本机可以访问该redis;
①注意:
redis默认的持久化方式是rdb,采用快照的方式进行持久化,但是rdb会存在数据丢失,所以在你使用docker安装redis时,只要你将appendonly设置为了yes,那么docker就让你的redis自动采用aof的方式进行持久化,aof的持久化一般有两种模式:一种是实时的把对redis的所有操作命令就计入appendonly.aof这个文件中,另一种是每隔一秒把对redis的所有操作命令就计入appendonly.aof这个文件中,docker安装redis时也是采用的第二种,因为第二种实时性要求太高,对机器负担大;
②另外:
只要你将配置文件改成了appendonly yes,那么你在使用docker安装redis时,它就会在/data这个目录下生成一个appendonly.aof文件来记录对redis的操作记录;/data目录时你使用docker exec命令进入redis容器的起点目录;
③注意:为什么我们要自己新建一个redis.conf文件?
实际上在使用docker安装redis时,docker已经在背后准备好了一个redis的配置文件,只不过它默认的配置文件,是没有指定redis密码的,也没有开启redis的持久化,所以我们要自己新建一个redis.conf,然后让docker读取这个配置文件,用我们自己配置文件里面的配置覆盖掉它本来的配置文件;
但是注意:这里并不是全部覆盖,而是我这里写了什么配置,才会覆盖什么配置,我没覆盖的配置,docker还是采用它默认的配置;

2.2 启动redis,并让其加载配置文件

①在xshell界面,点击新建文件传输;
在这里插入图片描述
②点击后,会弹出一个sftp的会话,左键按住新建好的redis.conf,拖进sftp界面中,就能将该文件上传到虚拟机上,然后你再使用ls命令即可查看到该文件。
在这里插入图片描述
③然后在/root目录下创建redisconf目录,将redis.conf通过mv命令移动redisconf目录下(这一步很重要,不移动进去会导致redis读取不到该配置文件)

mkdir redisconf
mv redis.conf redisconf

④再使用docker run命令将redis跑起来

docker run --name=redis01 --restart=always -d -p 6379:6379 -v /root/redisconf:/data redis:5.0.12 redis-server /data/redis.conf

注意:这里数据卷为什么要使用-v /root/redisconf:/data?
这是因为:首先redis的持久化(这里采用的是aof的持久化方式)是通过appendonly.conf文件记录操作命令的方式实现的,并且要通过redis.conf开启持久化,所以实际上redis的持久化并不是将数据存储起来,只是将操作记录存了起来;那么我们在使用数据卷时,只需要将redis生成的appendonly.aof这个文件保存到我自己的宿主机上就可以了,而appendonly.aof文件是默认生成在redis容器的/data目录下的,所以数据卷这里:冒号右边的目录要填/data,左边其实可以随便填;

另外:redis-server其实是一段脚本的简写,表示要读取后面的/data目录下的/redis.conf配置文件;
如果你写成redis-server --appendonly yes就是将docker中redis默认配置文件中的appendonly设置成yes(本身是no),但是这样就不会去读取我们自己新建的这个redis.conf了;

注意:当我们进入redis容器后

docker exec -it redis01 bash

我们可以使用redis-cli命令调起redis的操作台,对redis进行操作,比如下面的例子

root@1bb6e89b4e3a:/data# redis-cli
127.0.0.1:6379> set name "许海"
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 115409
OK
127.0.0.1:6379> set name "许海"
OK
127.0.0.1:6379> get name
"\xe8\xae\xb8\xe6\xb5\xb7"
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> 

十三. 容器间的通信

基于docker部署的容器,每一个容器之间都是相互独立的,但是往往有时候,我们需要容器之间进行通信,比如我们在tomcat的容器中部署了一个应用,这个应用需要访问另一个mysql容器,这个时候就需要容器之间通信了;
这里跟我们之前旧的通信方式不同,我们以前都是在同一台机器中安装tomcat,mysql,它们之间的通信就是通过localhost就可以了;但是到了现在docker里面,容器跟容器之间就是相互独立的,是操作系统级别的隔离,它们之间相当于完全另一台机器了;
所以docker给我们提供了一种允许容器之间互相访问的方式:也就是docker提供的一种网络配置,来进行通信;

1.虚拟网桥

1)当我们在虚拟机上安装docker时,docker会自动在这个虚拟机上安装一个名为docker0的虚拟网桥(虚拟网桥如果你不理解,可以把它当成一个虚拟网卡),也就是说一旦你在虚拟机上安装了docker,那么它就会在这台机器上安装一个新的名为docker0的虚拟网卡;
我们可以把这个虚拟网卡理解为一个交换机,它会挂载到机器的网口之间进行转发;
当我们在虚拟机上安装docker后,我们再使用ip空格a命令查看ip时,我们就可以看到一个名为docker0的网卡,它的默认ip地址是172.17开头的ip地址,这个ip是docker根据RFC1918协议来生成的;
在这里插入图片描述

2. docker容器间的通信原理:

2.1 原理:

当我们启动一个容器时,也就是在执行docker run时,docker会自动给这个容器创建一对接口(这对接口是什么你不用管),其中一个接口叫eth0,另一个接口叫veth1(不一定叫veth1,但是一定是以veth开头的),eth0接口会被放入刚才启动的这个容器中,veth1接口会被放入到虚拟网卡docker0中;

2.2 eth0与veth1接口的特性

eth0与veth1接口之间,当其中一个接口接收到信息,另一个接口也会接收到同样的信息;

当我们启动好一个tomcat容器后,我们使用虚拟机ip:8080来访问这个tomcat容器时,这其中的调用链路到底是怎样的呢?
比如我自己启动的虚拟机的ip是192.168.11.10,我们使用192.168.11.10:8080来访问tomcat主页时,首先这个请求会打到虚拟机的虚拟网卡docker0上,然后又到达veth1接口,关于这个请求的信息就到了veth1接口,所以eth0接口也会接收到同样的信息,请求就被打入到tomcat容器内了;(在这个过程中,容器内的端口一定要跟虚拟机的端口进行映射,否则请求打到eth0接口后,就不知道要找哪个容器内的端口了,因为整个容器就相当于一个机器,也会很多端口);

根据上面的过程,不仅仅可以实现容器与虚拟机之间的通信,也可以实现容器与容器间的通信,比如下图tomcat01容器内要访问tomcat02容器内部,它发消息到eth0接口,消息就可以被转发到veth1接口,然后从veth1接口转发到veth2,veth2接口收到消息后,tomcat02容器中的eth0接口就收到tomcat01容器的请求了;
在这里插入图片描述
看过上面的解释后,我们就知道为啥docker0虚拟网卡可以被看成是一个交换机了,这完全就是干的交换机的工作;

2.3 我们来验证容器之间可以进行通信23.22

1)比如我们先启动3个tomcat容器,如下图所示
在这里插入图片描述
根据上面学习的知识可知,这三个容器都会连接到docker0虚拟网卡中;
2)我们再使用docker inspect 容器id/容器名,来查看这几个tomcat容器的ip地址;
我们拉到最下面,找到Networks属性,可以看到tomcat01的ip地址为172.17.0.2;同理也可以看到tomact02,tomcat03的地址为:172.17.0.3,172.17.0.4;
在这里插入图片描述
3)在tomcat01中访问tomcat02,tomcat03
我们再使用docker exec -it 容器id或者容器名称 bash 命令以交互模式进入到tomcat01容器中,

docker exec -it tomcat01 bash

进去之后,我们再使用curl命令发送http请求,因为tomcat01,02,03三个容器都连接到docker0虚拟网卡上,所以我们可以通过ip地址访问其他容器,命令如下:

root@c5a7ea281baa:/user/local/tomcat# curl http://172.17.0.3:8080

(这个8080是指tomgcat02容器内的8080端口,而不是虚拟机的8080端口,这一点很重要)
执行curl命令后,我们就能访问tomcat02了,正常来说,我们在windows浏览器中访问这个地址,能看到tomcat的首页,但是此时我们是在虚拟机中进行访问,那么访问出来的,实际上就是html页面;

所以我们此时能看到的,应该是这个html代码页面:
在这里插入图片描述

2.4使用容器名进行容器间通信

经过上面的学习,我们可以看到每个容器启动后都会有一个ip地址,容器之间可以借助docker0来使用ip进行互相访问,但是此时存在一个问题,因为容器启动后,它的ip地址是动态分配的,也就是说当容器被关掉,再次重启后,它被分配的ip地址是随机的,那么我们每次在进行容器间通信时,都通过ip地址去访问就变的不靠谱了,所以docker运行我们通过容器名进行访问其他容器;
也就是说当两个容器是在同一个虚拟机中(同一台机器中),那么它们之间就可以通过容器名进行通信;
但是这里有个很坑爹的设定,docker0可以支持容器间使用ip进行通信,但是使用docker0不支持容器间使用容器名通信,如果你要使用容器名通信,你必须自定义虚拟网桥不能使用docker0;

2.5自定义虚拟网桥

2.5.1 关于网桥的几个问题

1)为什么要自定义虚拟网桥?
除了上面要使用容器名进行访问的原因,还有一个重要的原因,一台虚拟机中,我们可能会启动好几个项目,这些项目会启动很多中间件的容器,比如mysql,redis,es等,这些容器都会被链接到docker0中,但是网桥上的资源是有限的,也就是说,同一时间它能通行的数据是有限的,当你其中一个项目的通信数据量特别大,它就会占用docker0大量的资源,那么另一个项目所能使用的资源就相应的减少,所以我们并不能让所有项目都使用同一个网桥,所以我们需要自定义网桥,最好是一个项目一个单独的网桥;
2)如何自定义网桥实现网桥中一组容器的通信?
注意:一个网桥只能保证跟自己相邻的容器间可以互相通信,不同网桥之间是没法互相通信的;

2.5.2 docker中网络的类型
docker network ls

使用docker network ls命令,我们可以查看到当前docker中的所有网络;
如果你之前什么网络都还没创建过,那么你可以看到弹出三个网络,这是默认就有的三个网络,Name表示当前网络的名称,Driver表示当前网络的类型;
bridge:网桥类型,为什么叫网桥类型?这是因为这个网络类型是用来连接容器之间的通信的,所以称之为桥;
host:这个是主机类型,这个类型没什么用;
null:表示当前docker没有网络,这个类型也没什么用;
所以我们要实现容器间通信的话,网络类型必须是bridge;
在这里插入图片描述

2.6.在docker中关于网桥的命令

注意:docker中关于网络的命令都是docker network 什么 什么;

2.6.1 创建网络的命令
docker network create -d bridge 你自定义的网络名称

1)使用-d来指定网络类型,bridge表示创建出的网络是桥类型,也就是网桥;
其实如果你不加-d bridge,默认创建出来的就是bridge,因为另外两种类型基本没什么用;

2.6.2 查看当前docker中的网络
docker network ls
2.6.3 查看某个网络的详情
docker network inspect 网络名

在这里插入图片描述

2.6.4 删除某个网络

删除某个网络

docker network rm 网络名

删除所有没有用到的网络

docker network prune
2.6.5 运行容器时给它指定一个网络

注意:在指定数据卷时,我们需要在启动容器时指定,否则数据卷就无效,但是指定网络不一样,你可以在启动容器时指定,也可以在启动后让这个容器加入某个网络;
1)启动容器时指定它属于某个网络

docker run --network 网络名称 ......

在使用docker run命令时,加上杠杠network空格网络名称就可以了,就可以指定这个容器属于哪个网络;
2)让某个容器加入某个网络
使用docker network connect命令即可将一个没加入网络的容器加入到指定网络中;

docker network connect 网络名称 容器名称(或者容器id)
2.6.6 验证自定义网络是否生效

1)第一步:我们启动两个容器,一个容器是tomcat01,另一个是tomcat02,并且把这两个容器都添加到ems这个网络中;
在这里插入图片描述
2)我们再查看ems这个网络的详情

docker network inspect ems

我们再查看ems这个网络的详情时,可以看到Name这个属性,也就是网络的名称为:ems
再往下可以看到Containers这个属性,这个属性里存储的是当前网络中的所有容器,我们可以看到里面容器有两个,就是我们刚才添加的tomcat01,tomcat02;
除此之外,我们还可以看到这两个容器的IPv4Address属性,也就是这个两个容器被分配的ip地址;
在这里插入图片描述
3)我们再进入其中某个容器,对另一个容器进行访问
我们先进入tomcat02这个容器中;

docker exec -it tomcat02 bash

再使用ip地址进行访问;

curl http://172.20.0.2:8080

可以看到,我们依然可以看到tomcat首页的html页面,显然,自定义网络后也是可以通过ip地址访问的;
在这里插入图片描述
我们再使用容器名进行访问

curl http://tomcat01:8080

可以看到,我们依然能得到tomcat首页的html页面,显然成功了;
在这里插入图片描述

十四. 高级数据卷

学习完上面的内容后,docker基本就学习完了,还剩下高级数据卷没学,也就是数据卷的高级部分,这部分学习完之后就进入docker-compose的学习;

1. 回顾数据卷

1)数据卷的英文单词是volume
2)数据卷的作用是:将容器内的目录或文件跟宿主机中的目录或文件进行映射,从而达到我们操作容器内目录可以影响到宿主机目录的目的(一般用作数据的存储);
3)数据卷语法:docker run后面跟上 -v 宿主机中路径:容器内路径
4)数据卷绝对路径与别名:
4.1)绝对路径
我们可以使用绝对路径的数据卷,也就是宿主机中路径是绝对路径
比如下面这个命令就是将绝对路径/root/datas跟容器内的/user/local/tomcat/webapps路径进行映射;

docker run -v /root/datas/:/user/local/tomcat/webapps ......tomcat:8.0

4.2)别名
使用别名创建数据卷,你就把绝对路径的位置改成随便一个名字,你叫aa也行,叫bb也行

docker run -v bb:/user/local/tomcat/webapps ......tomcat:8.0

注意:
①别名是由docker给我们进行维护的,如果你创建数据卷时没有给它起一个别名,那么docker就会自动给它生成一个别名;
②使用别名创建数据卷,与使用绝对路径创建数据卷还有一个区别:使用别名创建,docker会将容器中的原始数据保留下来,而使用绝对路径创建则不会保留;
③当我们使用别名创建数据卷时,我们并没有指定宿主机中哪个路径跟容器路径进行映射,docker会自动在宿主机中创建一个目录来跟容器内路径进行映射;我们可以使用docker volume inspect 数据卷别名 的命令来查看宿主机中到底是哪个路径跟容器内路径进行映射;

2. 关于数据卷的命令

2.1 别名详解:

其实别名就代表docker自身维护的一个数据卷,此话如何理解?

2.2 查看docker中维护的所有数据卷

docker volume ls

使用docker volume ls命令后,我们就能看到当前docker中维护的所有数据卷,下图中那些名称为aaaa,esdata,esData的数据卷,就是我们用别名创建的数据卷,如果我们在创建数据卷时没有起别名,那么docker就会自动生成一个很长的唯一id作为别名,就像下图这样;
在这里插入图片描述

2.3 查看docker中某个数据卷的详情

docker volume inspect 数据卷别名
docker inspect 数据卷别名   //这个命令volume也可以省略

比如下图中,我们查看aaaa这个数据卷的详情,我们就能看到该数据卷的创建时间,数据卷类型为local,mountpoint挂载点:也就是数据卷映射的宿主机目录为/var/lib/docker/volumes/aaaa/data
在这里插入图片描述

2.4 删除某个数据卷

docker volume rm 数据卷别名

注意:当你把数据卷删除后,这个数据卷对应的宿主机目录也会被删除;

2.5 创建一个别名数据卷

docker volume create 数据卷别名

创建后,docker会在根目录下的var目录下的lib目录下的docker目录下的volumes目录下创建出这个数据卷。
为什么会有这个命令?有时候我们想提前把数据卷创建出来,后面启动容器时再用,并不是在启动容器时才创建数据卷,就可以用这个命令;

十五. Dockerfile(重点中的重点)

1. Dockerfile是什么?

Dockerfile可以认为是对镜像的描述文件,它属于脚本;

2. Dockerfile的作用?

通过Dockerfile文件,我们可以生成一个属于自己的镜像;

2.1 回顾docker的全流程

在了解Dockerfile的作用前,我们可以先回顾一下docker的全部流程;
首先Docker公司在全世界范围内维护了一个docker hub仓库,
1)我们要从仓库拉取镜像,可以使用pull命令,
2)再使用run命令,可以把镜像跑起来成为一个个容器,
3)我们自己写的一个软件,我想把它转化为一个镜像,就可以使用commit命令,
4)某个镜像我想把它拷贝一份,以便我把它发给我的同事去使用,可以使用save命令将其保存为tar文件;
5)我又想把tar文件恢复为镜像文件,就可以使用load-i命令
6)我们把一个软件通过commit命令将其从容器转换为了镜像(我们自己写的软件也可以看做是一个容器),我们要是想把这个镜像发布到docker hub仓库中,就可以使用push命令;
在这里插入图片描述
那么Dockerfile的作用,其实就跟commit命令差不多,可以将一个软件服务从容器状态转换为一个镜像;只不过Dockerfile要比commit命令更加灵活;
关于构建一个镜像,docker中也有专门的命令,叫做build,但是这个将Dockerfile构建build成一个镜像;
也就是以后我们只需要写Dockerfile文件,写好后,再build一下,就可以将其构建成一个镜像;

2.2 为什么要使用Dockerfile?

看一个场景:假如我们有一个需求,我们自己写了一个属于自己业务的软件,我们想要发布部署这个软件,那么此时我们就要先去找一个具备tomcat,jdk的环境,否则我们自己的软件部署上去也是无法运行的,那么此时我们可以去docker hub中下一个tomcat镜像,再下一个jdk镜像等等一系列其他镜像,然后再把我们自己的软件部署上去,但是显然这也很蛋疼,仔细想想,我们自己在开发的时候,自己的电脑上的环境本身就是这个软件最适合的运行环境了,为什么我们不直接把当前这个环境跟软件总体上打成一个镜像呢?这样就免去了我们一步步构建环境的步骤了,所以这就有了Dockerfile的用武之地;

我们把软件开发好后,然后编写Dockerfile文件,编写好后,通过build命令就可以将我们的软件跟环境构建成一个镜像,然后再通过运维的手段,将这些镜像部署到服务器上,这就很方便;

3. Dockerfile怎么用?

1)首先需要在指定位置创建一个Dockerfile文件,然后在这个文件中编写Dockerfile相关语法;(说是指定位置,其实你想在哪里创建都行)
2)再通过ocker build命令来构建镜像

docker build -t aa:1.0 .      //点儿这个位置是用来指定Dockerfile文件所在位置的,点儿的意思就代表是当前目录

-t后面跟 镜像名称:tag,tag就是版本号
比如docker build -t aa:1.0,意思就是,我要构建一个名为aa的镜像,软件版本是1.0;
然后还要在后面跟上个点儿 “.”,这是用来指定Dockerfile文件所在位置的,点儿"."代表当前目录:也就是说你在哪个目录中执行docker build命令,它就会用当前目录中的Dockerfile来构建镜像;

4. Dockerfile构建镜像的流程

4.1 Dockerfile的上下文目录(context目录)

先假定我们写好的Dockerfile文件放在了虚拟机下的/root目录中,那么这个/root就叫当前Dockerfile文件的上下文目录,英文名是context目录;

4.2 docker的C/S架构

我们知道Docker是一个C/S的架构,我们把Docker安装后,肯定后台运行着Docker的server端,我们其实就作为docker的client端

4.3 docker build的过程是怎样的?

当我们使用docker build命令时,这个命令会将当前Dockerfile文件的context目录下的所有文件都打包一起发送给docker server端;(所以根据这个特性,当我们在使用docker build命令时,跟Dockerfile不想关的文件一定不要放在context目录中,否则多个几百兆,1个G的不想关文件,构建起来肯定很慢);
注意:当你有时候你的文件确实没有办法,确实这个context目录就有很多不想关的文件跟目录,你没有办法避免,那么此时你可以使用dockerIgnore来忽略掉那些你不想提交给docker server的文件跟目录,那么在构建镜像的时候,docker就会忽略掉这些文件跟目录,但是dockerIgnore的用法我目前不太清楚,后期需要的时候,可以百度一下;
在这里插入图片描述

4.4 docker是如何解析Dockerfile命令的?

1)首先我们需要知道:在Dockerfile语法中,它只允许我们一行写一句命令,你写两句就不符合Dockerfile的语法;
2)docker解析到第一行Dockerfile命令,就会给第一行命令生成一个临时镜像,
解析到第二行,就会给第二行生成一个临时镜像,
解析到第三行(假设第三行就是最后一行),此时就不会再生成临时镜像,而是生成一个最终的镜像,我们此时使用docker image ls命令就能看到这个最终的镜像,但是是看不到这些临时镜像的;
4)为什么要使用临时镜像?
其实是为了加快解析的速度,docker会将每一行的临时镜像放入docker server端的缓存区中,假设我们在写Dockerfile时,本来我们以为三行就能写完,但是后来我们发现必须在最后一行前面加一行才能写完,所以我们第二次执行Dockerfile时,docker就不会重新将之前旧的三行解析成临时镜像了,而是到缓存区中拿之前就生成好的临时镜像,这样构建速度就更快了;
在这里插入图片描述

5. Dockerfile的相关指令

1.FROM :表示当前镜像是基于哪个镜像的,第一个指令必须是FROM

2.MAINTAINER :表示镜像维护者的姓名和邮箱地址

3.RUN :构建镜像时需要运行的命令

4.EXPOSE :指定当前容器哪些端口号对外暴露

5.WORKDIR:指定这个镜像被创建成容器后,终端默认登录进来的工作目录(一个落脚点),也就是你使用docker exec -it 容器id或者容器名称 bash命令后,进入容器的首个目录;

6.ENV :这个指令用来:在构建镜像的过程中设置一些环境变量

7.ADD :将宿主机目录下的文件,拷贝到镜像中,注意这个ADD命令会自动处理URL和解压tar包;

8.COPY :类似于ADD,拷贝文件和目录到镜像中

9.VOLUME:表示指定当前镜像被跑起来成为容器后,可以有哪些数据卷;

9.CMD:指定镜像被跑成容器后,启动容器时要运行的命令;

9.ENTRYPOINT:ENTRYPOINT跟CMD一样,都是用来指定这个镜像被转为容器后,启动容器时要运行的命令;

6. 创建Dockerfile演示

1.创建Dockerfile文件

我们随便进入一个目录下,使用vim Dockerfile命令即可创建一个Dockerfile文件,这里不用指定后缀

vim Dockerfile

强烈建议在创建Dockerfile时,D大写f小写;
在这里插入图片描述
使用vim命令后,我们就会进入到Dockerfile的编辑页面,如下:
在这里插入图片描述

2. 编辑Dockerfile

2.1 编辑Dockerfile需要知道的前置知识

我们前面提起过,Dockerfile文件每一行都只能有一句命令,这个到底如何理解呢?
看下图中这些保留字,我们在上面也提过,
1)首先每一行必须由这些保留字开头,后面跟参数;
2)每一个保留字都被视作单独的一行,所以每一行有且只能有一个保留字;
3)FROM保留字必须出现在Dockerfile的第一行,也就是整个Dockerfile的第一个字必须是FROM;
在这里插入图片描述

2.2 FROM命令详解

1)FROM命令表示当前镜像是基于哪个镜像来构建的,
比如我们现在要将我们自己的软件构建成镜像,我们自己的软件肯定要依赖于某个操作系统,所以这里就可以基于Centos镜像来构建;

2.3 正式编辑Dockerfile

1)
我们先查看自己本地的所有镜像,有一个centos:latest镜像;
在这里插入图片描述
2)
我们再进入Dockerfile文件,第一行就可以写:

FROM centos:latest

我们只写这样一句后,其实就可以去build了,但是这样构建没有什么意义,因为你这样创建出来的镜像,跟原来的centos:latest没有任何区别,根本没什么用,所以还要继续写;
3)
我们继续写run命令

# 这个from表示,我当前dockerfile构建出来的镜像,是基于我本地的centos:latest这个镜像的,也就是以它为基础。
FROM centos:latest
# 在dockerfile中,警号#表示注释
RUN yum install -y vim
# RUN 命令还可以写成数组的形式,跟上面是一样的效果
RUN ["yum","install","-y","vim"]   

理解这句命令前,我们需要理解一个东西:看下图,
我先使用docker images命令查看到当前docker中有一个centos:latest的镜像:
在这里插入图片描述
我们把centos:latest这个镜像跑起来一下
在这里插入图片描述
跑起来后,我们再使用docker ps查看当前正在运行的容器,但是我们会发现此时运行中的容器并没有这个centos容器,这是为什么呢?
在这里插入图片描述
之前我们启动一个容器,比如启动一个rabbitmq容器,启动后,为什么我们能看到rabbitmq容器一直在运行呢?
首先还是那句话,我们知道rabbitmq容器内部有一个操作系统,由docker镜像的原理可知,操作系统本身也是一个镜像,rabbitmq容器启动后能保持运行,是因为这个操作系统镜像中有rabbitmq镜像,操作系统先被启起来,然后又由于rabbitmq镜像,导致操作系统被阻塞起来,所以rabbitmq才能够保持运行;但是如果你只是一个单独的centos镜像,里面其他什么东西都没有,那么你启动centos镜像后,就没有东西会阻塞操作系统,所以centos镜像一启动,立马就会关闭掉,所以使用docker ps就看不到centos容器了;

再看到我们前面写的Dockerfile文件,我们加了RUN yum install -y vim命令,首先centos:latest这个镜像里面肯定是白板一块,它里面肯定是没有安装vim这个工具的,假如我想让我们基于centos:latest构建出来的镜像拥有vim,那么我们就可以使用RUN yum install -y vim命令,docker解析到这里时,就会自动执行yum install -y vim,就会把vim给你安装好;

FROM centos:latest
RUN yum install -y vim
2.4 继续编写DockerFile文件
①expose命令:

用来声明dockerfile构建出来的容器,对外暴露的到底是哪个端口,仅仅只是声明作用,假如你自己的应用默认是8080端口,那么当宿主机用-p指定端口来映射我这个容器时,肯定也是将容器内部的8080端口映射出来,也就是说我容器暴露的8080端口是不会因为你expose指定的是哪个端口而改变的,所以expose只是一个声明的作用,目的是为了让其他人在查看到这个dockerfile文件时,一眼就能知道我们这个容器到底暴露的是哪一个端口。

FROM centos:latest
RUN yum install -y vim
EXPOSE 8081
#表示告诉使用者,我们这个dockerfile构建出来的镜像,会对外暴露5672,8081这两个端口。
EXPOSE 5672 
②workdir命令:自定义进入容器的落脚点

如果你不配置workdir,默认进入的就是容器的根目录,也就是/目录。
写法一:

workdir /data/apps #表示默认落点就是容器中的根目录下的data下的apps目录,如果这个目录在容器中根本就不存在,那么在第一次进入时,会自动创建出这样一个目录。

写法二:

#你也可以配置多个workdir
workdir /data #第一个workdir是绝对路径
workdir apps #第二个workdir是相对路径,意思是:落脚点是/data下的apps目录,这个相对路径,是相对第一个workdir来说的,所以说第一个workdir必须是绝对目录。
③add命令与copy命令:都可以将宿主机中dockerfile所在目录的文件拷贝到镜像中
  • 基本用法1:
add aa.txt /data/apps
#因为add、copy命令都是会去从dockerfile上下文目录中去找文件,如果你的aa.txt就是在dockerfile上下文目录中,你直接写文件名即可。
#意思是将aa.txt文件添加到容器中的/data/apps目录下。
  • 基本用法2:
add ./aa.txt /data/apps #也可以写./表示当前目录,当前目录就是上下文目录。
  • 基本用法3:
workdir /data
add ./aa.txt ./apps 

这个意思就是将dockerfile上下文目录中的aa.txt添加到当前目录下的apps目录中,这里的当前目录指的是容器根目录下的data目录,因为你上面用workdir指定了落脚点目录为/data。

  • 基本用法4:
workdir /data
add ./aa.txt ./apps 
workdir apps
add ./bb.txt ./apps 

这样写的话,那么第二个add中的./apps 中的当前目录就是data下面的apps目录。

  • copy与add的区别:
    copy相对来说功能比较简单,只是对于文件,目录的拷贝,add功能更强大,它后面还可以写一个url,它会自动去这个url下载文件,并将这个文件拷贝到指定位置。
    用法:
add 文件url 容器内目标目录

举例:
在这里插入图片描述

我现在将一个tomcat压缩包的下载地址,给到了add,我仅仅只是将tomcat放到容器内还不够,我想要实现:等tomcat压缩包被下载下来后,我自动对其解压,该怎么做呢?
只需要在下一句跟一个run,然后将解压命令tar -zxvf 压缩包名 跟在后面即可,意思就是下载好后,就要立即解压。
我又在后面跟了run rm -rf tomat压缩包名,表示解压完成后,就会将这个压缩包删除。
我最后又跟了一个run mv apache-tomcat-9.0.48 tomcat-9.0.48 ,表示删除压缩包后,我又会将tomcat目录的文件名从apache-tomcat-9.0.48改成tomcat-9.0.48。

④ env命令:在构建镜像时设置环境变量,设置环境变量的目的是为了复用。

写法:

env BASE_PATH = /data/app
或
env BASE_PATH  /data/app  中间用等于或空格都可以,表示创建了一个环境变量BASE_PATH  (规范是要大写),然后让BASE_PATH等于/data/app

用途举例:比如我在用workdir命令来指定落脚点时,我可能不止指定一次,那么每次都写一遍路径是很烦的,所以我直接在前面将这个路径定义成环境变量,dockerfile后面的内容要用到它时,直接引用即可:
如:

env BASE_PATH = /data/app
workdir $BASE_PATH  #注意,引用时要在前面加上$

另外:workdir不仅仅可以指定落脚点,在指定落脚点的同时,也会切换容器目录,这一点很重要。

⑤volume:声明数据卷目录,告诉别人我这个容器内的挂载目录是哪一个,因为你正常来说,一个镜像你不告诉别人挂载目录是哪个,别人也不知道该怎么做,就很麻烦,它跟expose都只是声明的意思,不做限制,你非要挂载其他目录也行。
⑥cmd与entrypont::cmd表示命令行,这两个命令的作用都是用来指定容器启动时默认执行的指令是什么。

以redis的镜像为例:当你docker run redis的镜像时,为什么这个镜像就会启动一个redis呢?就是因为它使用cmd、entrypoint默认执行了redis-server这个命令来启动redis。
同理:我们自己的项目中,肯定是包含我们项目的jar包的,我们想达到的效果就是,我只要一把我自己项目的镜像跑起来,就自动使用java -jar执行这个jar包。

  • 注意:CMD指令在dockerfile中可以存在多个,但是只有最后一个生效。entrypoint可以存在多个,都会生效。
  • 用法1,后面直接跟命令
cmd java -jar jar包名.jar

用法2:后面跟数组,推荐使用,因为数组形式的,可以更换参数

cmd ["java","-jar","jar包名.jar"]  

注意:数组里有不能写环境变量,是读取不了的。

  • 真正的用法:在entrypoint中写固定的参数,cmd中写可变的参数。
entrypoint ["java","-jar"]
cmd ["aa.jar"]

我每次启动容器,那么就固定会执行java -jar命令,但是后面跟的jar包是可能会变的,所以就把aa.jar放到cmd中,启动容器时,默认就就会执行aa.jar;
等有一天我的jar包换了,我就可以把新jar包传过去,

docker run 我的镜像:版本号 bb.jar

通常这会配合数据卷使用,我将新的jar包bb.jar放到我的宿主机中,此时容器内就有这个bb.jar了,然后再用bb.jar作为参数替换掉旧的aa.jar,再执行时,就会启动bb这个jar包.
但是这种写法,你只能使用数组形式,cmd后面直接跟命令的话,是无法更换参数的。
还要注意要把entrypoint写在cmd的上面,顺序很重要,因为docker是从上往下一行行读取的。

3.使用dockerfile来构建应用。

第一步:先将我们的项目打包
第二步:下载jre镜像,
注意1:不用下载jdk镜像,jre是运行环境,我们只需要java 运行环境即可,这样可以节省一点空间,选linux版本下的jre镜像。
注意2:要选openjdk,它表示是开源的jdk。
在这里插入图片描述

第三步:编写dockerfile

FROM openjdk:8-jre #基于openjdk的jre进行构建
ENV APP_PATH = /apss
WORKDIR /apps   # 指定一个落脚点/apps用于存放我们的jar包。
ADD myjar.jar $APP_PATH/app.jar # 将我们的jar包添加到容器中的/apps目录下,同时将其改名为app.jar
EXPOSE 8081
ENTRYPOINT ["java","-jar"]
CMD ["app.jar"]

第四步:构建

docker build -t myapp:1.0 .

第五步:docker images查看生成出来的镜像
第六步:启动我们项目需要的mysql,redis等容器,
1)启动mysql

docker run -p 3306:3306 -d --name mysql8.0 -e MYSQL_ROOT_PASSWORD=root -v mysqldata:/var/lib/mysql mysql:8.0

2)启动redis
3)创建网桥

docker network create -d bridge mynetwork

4)将mysql,redis等加到同一个网桥中

docker network connect mynetwork mysql8.0;
docker network connect mynetwork redis6;

第七步:启动我们自己项目生成的镜像,将这个项目与mysql,redis等加到同一个网桥中。

docker run -p 8081:8081 -d --name myapp --network mynetwork myapp:1.0

注意:启动我们自己的项目时,不需要配置数据卷,只有其他中间件需要配置。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐