docker使用小记3-dockerfile自定义镜像
最近公司项目需要使用docker进行项目部署,现记录下这段时间的学习内容,本章主要记录dockerfile来自定义镜像。当然我们还可以用commit通过容器来自定义镜像,但是我这次基本都是用dockerfile来生成镜像,commit等以后有用到再作记录。
一、dockerfile命令
FROM 指定基础镜像,放在第一行,其格式为:
1 #语法: 2 FROM <image> 3 FROM <image>:<tag> 4 FROM <image>:<digest>
若想构建一个最小的镜像,不想基于其他任何镜像时。可直接
1 FROM scratch
MAINTAINER指令允许你设置生成这个镜像的作者。
1 MAINTAINER lzq <xxx@xx.com>
也可以使用LABEL设置镜像相关信息
1 LABEL author="作者:xxx" 2 LABEL version="版本:v0.1" 3 LABEL desc="说明:test"
要查看时,可以使用docker inspect 镜像名或id 进行查看
ENV 设置环境变量,如java_home等。
1 ENV <key> <value> 2 ENV <key>=<value> ...
VOLUME 定义匿名卷,在我们运行容器时,有些数据是需要被持久化保存的,比如mysql容器中的数据,tomcat容器中的logs日志等,但是一般来说容器内的数据会随着容器的删除而消失,就算是用相同镜像再去生成容器,运行时产生的数据也不会存在。所以当我们需要对某些数据进行持久化时,可以在生成容器时加上【-v 卷名或主机目录:容器目录 】的参数来对容器某个或多个目录进行挂载,这样的话就算之后容器被删除,其被挂载的目录数据仍会存在于主机之中,而且挂载之后对卷或主机目录的修改会同步到容器对应目录中,反之亦然。而如果挂载的主机卷或者目录一开始就存在数据,则无论容器对应目录内有无数据,都会被隐藏,显示的是挂载主机卷或目录存在的数据。一般来说-v参数如果没有指定卷名或者主机目录,只指定容器目录的话,那么docker会自动为容器生成一个匿名卷,这样的话效果跟在dockerfile中用VOLUME命令是一样的,但每次都靠执行run命令生成容器时去添加-v参数的话有时会遗漏。所以我们可以在dockerfile文件使用VOLUME命令先定义匿名卷,对需要持久化的目录进行挂载,这样的话如果生成容器时忘了使用-v参数去挂载容器重要目录,docker也会生成匿名卷将数据保存。而如果在run时使用-v参数对dockerfile中使用VOLUME挂载的目录进行定义时(如指定卷名或者主机目录),则docker不会生成匿名卷,而是根据当前指定的卷名或主机目录对容器目录进行挂载。
例如这是一份docekrfile文件:
1 FROM centos:latest 2 RUN groupadd -r redis && useradd -r -g redis redis 3 RUN yum -y update && yum -y install epel-release && yum -y install redis && yum -y install net-tools 4 5 RUN mkdir -p /config && chown -R redis:redis /config 6 7 8 VOLUME /share/data #声明容器中/share/data为匿名卷 9 10 11 EXPOSE 6379
那么使用该Dockerfile构建镜像的为
1 #docker build -t image-redis //构建镜像image-redis 2 ...... 3 #docker run -itd -name redis1 -v /data:/share/data image-redis //运行一个容器并且将当前机器的/data目录绑定到容器的匿名卷中 4 ..... 5 #docker run -itd -name redis2 image-redis //运行一个容器但是不绑定目录到容器的匿名卷,这时候在/var/lib/docker/volumes(不同版本目录不一样)中就会创建一个目录绑定匿名卷 6 .....
删除数据卷
数据卷
是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷
,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷
。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v
这个命令。
数据卷可能会占据很多空间,可以使用以下命令清理掉没有容器使用的数据卷
。谨慎操作,这需要你确认现在暂时没有使用
数据卷在以后也不会再使用,里面也没有有价值的数据。
1 docker volume prune
COPY 复制文件,主要就是构建镜像时,进行拷贝文件到镜像的指定路径下,格式为:
1 COPY <源路径>... <目标路径> 2 COPY ["<源路径1>",... "<目标路径>"]
ADD 更高级的复制文件,ADD
指令和 COPY
的格式和性质基本一致。但是在 COPY
基础上增加了一些功能。比如<源路径>
可以是一个 URL
,这种情况下,Docker
引擎会试图去下载这个链接的文件放到<目标路径>
去,而如果原路径是个压缩包的话,ADD会在复制完毕后在目标目录将文件进行解压。
1 ADD jdk-8u45-linux-x64.tar.gz /usr/local
EXPOSE 设置监听端口,为镜像设置监听端口,容器运行时会监听改端口,格式为:
1 EXPOSE <port> [<port>/<protocol>...] 2 EXPOSE 80 3 EXPOSE 80/udp
RUN 执行命令,在镜像的构建过程中执行特定的命令,并生成一个中间镜像。格式:
1 RUN <command> 2 或者 3 RUN ["executable", "param1", "param2"]
这也是很常用的一个功能了。
第一种后边直接跟shell命令
- 在linux操作系统上默认 /bin/sh -c
- 在windows操作系统上默认 cmd /S /C
第二种是类似于函数调用。
可将executable
理解成为可执行文件,后面就是两个参数。
两种写法比对:
1 RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME 2 RUN ["/bin/bash", "-c", "echo hello"]
注意:多行命令不要写多个RUN
,原因是Dockerfile
中每一个指令都会建立一层.多少个RUN就构建了多少层镜像,会造成镜像的臃肿、多层,不仅仅增加了构件部署的时间,还容易出错。
RUN
书写时的换行符是\
CMD 启动时命令,功能为容器启动时要运行的命令,有三种写法:
1 1. CMD ["executable","param1","param2"] 2 2. CMD ["param1","param2"] 3 3. CMD command param1 param2
第三种比较好理解了,就时shell
这种执行方式和写法,第一种和第二种其实都是可执行文件加上参数的形式:
举例说明两种写法:
1 CMD [ "sh", "-c", "echo $HOME" ] 2 CMD [ "echo", "$HOME" ]
补充细节:这里边包括参数的一定要用双引号,就是双引号"
,不能是单引号。千万不能写成单引号
。原因是参数传递后,docker
解析的是一个JSON array
。
ENTRYPOINT 启动默认命令,ENTRYPOINT
用于给容器配置一个可执行程序。也就是说,每次使用镜像创建容器时,通过 ENTRYPOINT
指定的程序都会被设置为默认程序。ENTRYPOINT
有以下两种形式:
1 ENTRYPOINT ["executable", "param1", "param2"] 2 ENTRYPOINT command param1 param2
ENTRYPOINT
与 CMD
非常类似,不同的是通过docker run
执行的命令不会覆盖 ENTRYPOINT
,而docker run
命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT
。Dockerfile
中只允许有一个 ENTRYPOINT
命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT
指令。docker run
运行容器时指定的参数都会被传递给ENTRYPOINT
,且会覆盖 CMD 命令指定的参数。如,执行docker run <image> -d
时,-d
参数将被传递给入口点。
也可以通过docker run --entrypoint
重写 ENTRYPOINT
入口点。
WORKDIR 指定工作目录,用于在容器内设置一个工作目录:
1 WORKDIR /opt/docker/workdir
通过WORKDIR
设置工作目录后,Dockerfile
中其后的命令RUN
、CMD
、ENTRYPOINT
、ADD
、COPY
等命令都会在该目录下执行。
USER 指定当前用户,用于指定运行镜像所使用的用户:
1 USER lzq
使用USER
指定用户后,Dockerfile
中其后的命令RUN
、CMD
、ENTRYPOINT
都将使用该用户。镜像构建完成后,通过docker run
运行容器时,可以通过-u
参数来覆盖所指定的用户。
二、使用dockerfile构建自定义镜像
通过上面的命令我们可以编写一个dockerfile来自定义我们的镜像,现在我们来构建一个tomcat镜像,首先我们得准备jdk和tomcat,我们可以在dockerfile中去下载,但我这里已经下载了下来了
dockerfile当前目录:
dockerfile内容:
该文件内容定义了:
1、使用了centos作为基础镜像
2、复制并解压了jdk,定义了JAVA环境变量
3、复制并解压了tomcat,定义了CATALINA_HOME
4、设置8080监听接口
5、定义tomcat的webapps目录为匿名卷
6、复制web文件夹中的文件到容器webapps目录中
7、设置tomcat的bin目录为工作目录
8、设置容器启动时默认启动tomcat
接下来我们用这个文件生成一个镜像:
1 docker build -t tomcat .
这样就生成了一个自定义tomcat镜像,我们可以运行docker images查看
可见tomcat镜像已经生成,接下来我们用该镜像生成一个容器:
1 docker run -itd -p 9000:8080 --name tomcat tomcat
容器已经生成,我们可以输入docker ps 查看正在运行的容器:
可见容器已经在运行,此时我们打开浏览器,输入ip:9000并回车,可以看到
出现如上页面,说明我们容器中的tomcat也随着容器启动而启动了。