Docker学习笔记-进阶篇
文章目录容器数据卷什么是容器数据卷,用来解决是什么问题?使用数据卷方式一:指定具体的挂载路径方式二: 具名挂载和匿名挂载扩展:挂载时指定权限方式三:使用Dockerfile在生成镜像时就挂载镜像构建上下文数据卷容器Dockerfile什么是DockerfileDocker网络原理IDEA整合DockerDocker ComposeDocker Swarm(相当于简化版k8s)CI/CD Jenki
文章目录
容器数据卷
什么是容器数据卷,用来解决是什么问题?
问题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的流程 - 小结
更多推荐
所有评论(0)