容器数据卷

什么是容器数据卷,用来解决是什么问题?

问题1:将应用+环境打包成镜像后,如果数据都在容器中,那么容器删除后,数据就会丢失!(比如MySQL的容器,删了这个容器就相当于删库跑路了…)所以现在就有了相应的需求:数据如何持久化?

容器数据卷技术,就是用来将容器内的数据同步到本地。实际上就是目录的挂载:将容器内的目录,挂载到Linux主机上。

总结一句话:容器数据卷技术就是用来实现容器的数据持久化和同步,以及容器间的数据共享。

使用数据卷

方式一:指定具体的挂载路径

docker run -itd -v 主机目录:容器内目录

数据同步的测试1:容器正在运行的情况下
在主机上改动,会自动同步给容器内;同样的,在容器内的改动,也会自动同步给主机。
在这里插入图片描述
docker inspect <容器id>可看到数据卷挂载的信息:
在这里插入图片描述

数据同步的测试2:容器停止的情况下

修改主机上的文件,待容器自动后,会发现自动同步的。

综上,使用容器数据卷后,直接在本机上修改文件即可。(以前不知道这个技术,每次改容器里面的文件,都要进入容器里。另外,容器里不一定有安装vim,要安装还得配置软件源…而且在容器里安装好vim后,还没有自己平时用得比较顺手的vim插件。

数据同步的实战:MySQL数据同步

[root@VM-8-8-centos ~]# docker run --name=mysql_v5.7  -itd -v /root/mysql_v5.7_1d7aba917169/conf:/etc/mysql/conf.d -v /root/mysql_v5.7_1d7aba917169/data:/var/lib/mysql  -p 13306:3306 -e MYSQL_ROOT_PASSWORD=xxxxxx  mysql:5.7

在这里插入图片描述
假如把容器删了,本地的数据卷并没有丢失,实现了数据持久化。
在这里插入图片描述

方式二: 具名挂载和匿名挂载

1、匿名挂载:不指定卷名(会自动生成)
// docker run -v 容器路径

docker run -itd -P --name nginx01 -v /etc/nginx nginx:latest

在这里插入图片描述

2、具名挂载:即指定卷名
// docker run -v 本机路径:容器路径

docker run -itd -P --name nginx02 -v nginx02_vol:/etc/nginx nginx:latest

在这里插入图片描述

所有docker容器中的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/<卷名>/_data目录下。
可通过以下命令查看卷所在的路径

docker volume inspect <卷名>

在这里插入图片描述

扩展:挂载时指定权限

ro:readonly //只读。说明目录只能通过宿主机来操作,而不能进入容器后去操作。
rw: readwrite //可读可写(默认)。说明目录可以通过宿主机或容器内来操作。

//示例:
docker run -itd -P --name nginx02 -v jumping-nginx:/etc/nginx:ro nginx:latest
docker run -itd -P --name nginx02 -v jumping-nginx:/etc/nginx:rw nginx:latest

在这里插入图片描述

方式三:使用Dockerfile在生成镜像时就挂载

创建一个简单的Dockerfile来构建我们的镜像:

FROM centos:7.6.1810

VOLUME ["volume01", "volume02"]

CMD echo "------------ end -------------"

CMD /bin/bash

在这里插入图片描述
使用docker build创建镜像:

[root@VM-8-8-centos 1]# docker build -f Dockerfile -t nobody-mac/centos:1.0 .

在这里插入图片描述

镜像构建上下文

注意:上面命令docker build最后面还跟了一个.符号。该符号
. 表示当前目录,而 Dockerfile 就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile 所在路径,这么理解其实是不准确的。这里其实是在指定上下文路径。那么什么是上下文呢?

首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。

当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

如果在 Dockerfile 中这么写:

COPY ./package.json /app/

这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 上下文(context) 目录下的 package.json。

因此,COPY 这类指令中的源文件的路径都是相对路径。这也是初学者经常会问的为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。

现在就可以理解刚才的命令 docker build -f Dockerfile -t nobody-mac/centos:1.0 . 中最后的这个 .,实际上是在指定上下文的目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。

如果观察 docker build 输出,我们其实已经看到了这个发送上下文的过程:
在这里插入图片描述
理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 COPY /opt/xxxx /app 不工作后,于是干脆将 Dockerfile 放到了硬盘根目录去构建,结果发现 docker build 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 docker build 打包整个硬盘,这显然是使用错误。

一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。

那么为什么会有人误以为 . 是指定 Dockerfile 所在目录呢?这是因为在默认情况下,如果不额外指定 Dockerfile 的话,会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile。

这只是默认行为,实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用 -f ../Dockerfile.php 参数指定某个文件作为 Dockerfile。

当然,一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

数据卷容器

前面提到,容器数据卷技术不仅用来实现容器的数据持久化和同步,还用来实现容器间的数据共享。
在这里插入图片描述
实战1

创建3个容器,实现数据共享。

1、使用前面的Dockerfile创建的镜像,先启动一个容器。

[root@VM-8-8-centos ~]# docker run -itd --name centos01 nobody-mac/centos:1.0

在这里插入图片描述
2、创建容器centos02,同时继承centos01的数据卷。此时centos01就是一个数据卷容器。

[root@VM-8-8-centos ~]# docker run -itd --name centos02 --volumes-from  centos01 nobody-mac/centos:1.0

通过docker inspect <容器id>可以看到,centos01和centos02这两个容器的数据卷指向的是同一个本地路径。
在这里插入图片描述
3、同理,创建容器centos03,继承centos01的数据卷。

[root@VM-8-8-centos ~]# docker run -itd --name centos03 --volumes-from  centos01 nobody-mac/centos:1.0

在这里插入图片描述
经测试可证明:即使容器centos01被删除,centos02和centos03还是能访问他们共享的数据卷。说明共享卷其实是通过拷贝的方式来实现的,是一种备份机制。

在这里插入图片描述

实战2

多个MySQL容器实现数据共享

在这里插入图片描述

结论:

  • 容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
  • 但是一旦将数据持久化到了本地,此时本地的数据是不会被删除的。

Dockerfile

什么是Dockerfile

Dockerfile就是用来构建docker镜像的构建文件。

构建步骤:
1、编写一个Dockerfile文件;
2、使用docker build构建成为镜像;
3、可以通过docker push将镜像发布出去(Docker Hub、阿里云镜像仓库等)。

以centos为例,来看看官方镜像的Dockerfile文件。
在这里插入图片描述在这里插入图片描述
很多官方镜像都是基础包,很多程序/命令都没有,我们通常会在此基础上构建自己所需的镜像。

Dockerfile的构建过程

基础
1、每个关键字(指令)都必须是大写字母;
2、从上到下按顺序执行;
3、#符号表示注释;
4、每一个指令都会创建并提交一个新的镜像层;
在这里插入图片描述
Dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写Dockerfile文件,这个文件十分简单。
Docker镜像逐渐成为企业交付的标准,必须要掌握。

Dockerfile:构建文件,定义了一切的步骤,源代码;
DockerImage:通过Dockerfile构建生成的镜像,最终发布和运行的产品。

Dockerfile的指令

FROM                  //以某个镜像为基础
MAINTAINER      //镜像是谁写的,姓名+邮箱
RUN                    //镜像构建时需要执行的命令
ADD                    //比如要在centos镜像的基础上添加tomcat镜像:添加tomcat压缩包(这里的压缩包格式最好是tar.gz,用zip经测试不行),它会自动解压。
COPY                  //类似ADD,将我们的文件拷贝到镜像中
WORKDIR          //镜像的工作目录
VOLUME            //挂载的目录
EXPOSE            //暴露端口
CMD                   //指定该容器启动时要执行的命令,只有最后一个会生效,可被替代
ENTRYPOINT    //指定该容器启动时要执行的命令,可以追加命令
ONBUILD           //当构建一个被继承的Dockerfile这个时候就会运行ONBUILD的指令。
ENV                   //构建的时候设置环境变量

在这里插入图片描述

实战1:创建一个自己的CentOS镜像

1.1、编写Dockerfile文件

FROM centos:latest
MAINTAINNER nobody<nobody@test.org>

ENV MYPATH /usr/local
WORKDIR $MYPATH

RUN yum install -y vim
RUN yum install -y net-tools

EXPOSE 80

CMD echo $MYPATH
CMD echo "-------- end --------"
CMD /bin/bash

1.2、docker build构建

[root@VM-8-8-centos mydockerfile-centos]# docker build -f Dockerfile -t mycentos:0.1 .

在这里插入图片描述
在这里插入图片描述
3、docker run启动

# docker run -itd --name mycentos_v0.1  mycentos:0.1

启动容器后,进入容器,可看到当前目录就是我们指定的/usr/local,且可以执行ifconfig等网络相关的命令。
在这里插入图片描述
使用docker history可以显示指定镜像的构建过程(即镜像的变更历史):

docker history <镜像ID>

在这里插入图片描述

因此,在拿到一个镜像时,可以使用docker history命令研究下它是如何构建的。

CMD和ENTRYPOINT指令的区别

CMD //指定该容器启动时要执行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT //指定该容器启动时要执行的命令。可以追加命令。

  • CMD

测试:
编写Dockerfile如下:

FROM centos:latest
CMD ["ls","-la"]

创建镜像:

docker build -f Dockerfile -t cmdtest:0.1 .

启动镜像,可以发现容器启动时执行了ls -la命令。

docker run -it  --name cmdtest_v0.1 cmdtest:0.1

在这里插入图片描述
假如想在CMD ["ls", "-la"] 的基础上,在启动容器时,在docker run命令后增加-t参数,从而实现容器启动时执行ls -la -t命令,是否可以行呢?

docker run -it  --name cmdtest_v0.1 cmdtest:0.1 -t

可以看到结果报错了,如下图:
在这里插入图片描述
这是因为docker run最后的-t参数,被当做一个命令,并将原来的命令ls -la给替换了。然后-t并不是一个linux命令,故报错。

  • ENTRYPOINT

如果将上面的例子使用ENTRYPOINT来代替CMD呢?

FROM centos:latest
ENTRYPOINT ["ls","-la"]

在这里插入图片描述

Dockerfile中的指令有不少是相似,需要了解它们的区别。最好的学习方法就是对比它们,然后测试。

实战2:创建一个自己的Tomcat镜像

2.1、准备镜像文件tomcat的压缩包,jdk的压缩包(亲测,只有tar.gz格式的压缩包可以,zip的不行)
在这里插入图片描述

2.2、编写Dockerfile文件(建议按官方命名Dockerfile,如果它在当前目录下,docker build就不需要用-f指定Dockerfile文件,它会自动寻找当前目录下的Dockerfile)

FROM centos:7.6.1810
MAINTAINER  nobody<nobody@test.org>

COPY readme.txt  /usr/local/share/readme.txt

ADD apache-tomcat-9.0.21.tar.gz /usr/local/share/
ADD jdk-8u211-linux-x64.tar.gz /usr/local/share/

RUN yum install -y vim
RUN chmod u+x /usr/local/share/apache-tomcat-9.0.21/bin/*.sh

ENV MYPATH /usr/local/share
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/share/jdk1.8.0_211
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/share/apache-tomcat-9.0.21
ENV CATALINA_BASE /usr/local/share/apache-tomcat-9.0.21
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin


EXPOSE 8080

CMD /usr/local/share/apache-tomcat-9.0.21/bin/startup.sh && tail -F /usr/local/share/apache-tomcat-9.0.21/logs/catalina.out

2.3、docker build构建镜像

docker build -f Dockerfile -t mydiytomcat:1.0 .

2.4、docker run启动

docker run -itd --name mydiytomcat_v1.0_n1 -p 18080:8080 -v /root/dockerfile-test/mydockerfile-tomcat/tomcat-webapps/test:/usr/local/share/apache-tomcat-9.0.21/webapps/test -v /root/dockerfile-test/mydockerfile-tomcat/tomcat-logs/test:/usr/local/share/apache-tomcat-9.0.21/logs mydiytomcat:1.0

2.5、浏览器访问
在这里插入图片描述
2.6、在宿主机挂载的数据卷上进行webapp的开发,然后浏览器访问:
在这里插入图片描述在这里插入图片描述
到此,项目部署成功!

问题:docker commit和docker build(使用Dockerfile) 两种镜像构建方式的区别?

下面做个测试进行比较。

1、docker commit方式

1.1、拉取官方镜像ubuntu:18.04,并启动。
1.2、进入该容器,执行命令:apt update && apt install -y openssh-server
1.3、退出容器,对当前容器进行提交构建镜像,docker commit
1.4、通过docker history 查看该镜像的历史提交记录,发现并未包含在容器内做的apt update && apt install -y openssh-server操作。
在这里插入图片描述

2、docker build方式

2.1、编写Dockerfile文件,内容如下:

FROM ubuntu:18.04
RUN apt update && apt install -y openssh-server
CMD ["/bin/bash"]

2.2、通过docker build构建镜像。
2.3、通过docker history查看该镜像的历史提交记录,发现包含了安装openssh-server的操作记录。
在这里插入图片描述

通过以上对比显然使用Dockerfile的docker build更好。docker commit的缺点如下:
(1) 需要在容器内操作麻烦,效率低。
(2) 这一点很重要,其他人或者过一段时间后自己也不知道这个镜像是怎么做出来的,都安装了什么。但是使用Dockerfile构建的镜像,我们看到是执行了apt update && apt install -y openssh-server命令。
 
既然使用docker commit这么不方便,那我们为什么还要学习它呢?其实仔细想一下docker build的每一步构建出来的镜像是不是就是通过docker commit构建出来的。因此学习docker commit可以让我们更好的理解docker build。

发布自己的镜像

发布到 Docker Hub

1、地址:https://hub.docker.com/,注册账号
2、确定账号可以登录
3、提交镜像。

  • docker login登录账号
[root@VM-8-8-centos test]# docker login --help

Usage:  docker login [OPTIONS] [SERVER]

Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.

Options:
  -p, --password string   Password
      --password-stdin    Take the password from stdin
  -u, --username string   Username

在这里插入图片描述

  • docker tag 创建自己的tag
    在这里插入图片描述

  • docker push 提交镜像到Docker Hub
    在这里插入图片描述
    在这里插入图片描述

发布到阿里云容器服务

官方帮助文档:https://help.aliyun.com/document_detail/60997.html

1、登录阿里云容器镜像服务控制台。
2、创建命名空间
在这里插入图片描述

3、创建自己的镜像仓库【这个步骤可建可不建立,因为在后面push之前,docker tag对镜像进行重命名时可以直接指定
在这里插入图片描述
这里选择本地仓库:
在这里插入图片描述
或者不在控制台建立仓库,而是通过docker tag命名的时候指定仓库名后,再push镜像即可:
在这里插入图片描述
push成功后,在阿里云控制台便能看到:
在这里插入图片描述

docker的流程 - 小结

在这里插入图片描述

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐