1. 首页
  2. Docker

Dockerfile镜像制作

 

构建镜像的方式有两种:一种是基于容器制作,另一种就是通过Dockerfile。Dockerfile其实就是我们用来构建Docker镜像的源码,当然这不是所谓的编程源码,而是一些命令的组合。
Dockerfile的作用:它可以让用户个性化定制Docker镜像。因为工作环境中的需求各式各样,一般的镜像很难满足实际的需求。
 
注意:
1.Dockerfile中所用的所有文件一定要和Dockerfile文件在同一级父目录下,可以为Dockerfile父目录的子目录。
2.Dockerfile中相对路径默认都是Dockerfile所在的目录
3.Dockerfile中一定要惜字如金,能写到一行的指令,一定要写到一行,原因是分层构建,联合挂载这个特性。
Dockerfile中每一条指令被视为一层
4.Dockerfile中指明大写(约定俗成)
 
Dockerfile制作镜像规范和技巧如下:
  • 精简镜像用途:尽量让每个镜像的用途都比较集中、单一,避免构造大而复杂、多功能的镜像;
  • 选用合适的基础镜像:过大的基础镜像会造成构建出臃肿的镜像,一般推荐比较小巧的镜像作为基础镜像;
  • 提供详细的注释和维护者信息: Dockerfile也是一种代码,需要考虑方便后续扩展和他人使用;
  • 正确使用版本号:使用明确的具体数字信息的版本号信息,而非latest,可以避免无法确认具体版本号,统一环境;
  • 减少镜像层数:减少镜像层数建议尽量合并RUN指令,可以将多条RUN指令的内容通过&&连接;
  • 及时删除临时和缓存文件:这样可以避免构造的镜像过于臃肿,并且这些缓存文件并没有实际用途;
  • 提高生产速度:合理使用缓存、减少目录下的使用文件,使用.dockeringore文件等;
  • 调整合理的指令顺序:在开启缓存的情况下,内容不变的指令尽量放在前面,这样可以提高指令的复用性;
  • 减少外部源的干扰:如果确实要从外部引入数据,需要制定持久的地址,并带有版本信息,让他人可以重复使用而不出错。
 
Dockerfile中有十几种常用指令:
1、FROM
FROM指令必须为Dockerfile文件开篇的第一个非注释行,用于指定构建镜像所使用的基础镜像,后续的指令运行都要依靠此基础镜像所提供的的环境。
 
2、MAINTAINER
指定作者。Dockerfile并不限制MAINTAINER指令的位置,但是建议放在FROM指令之后,在较新的docker版本中,已经被LABEL替代。
 
3、LABEL
功能是为镜像指定标签;你也可以直接在该指令下指定作者。
语法格式 :
LABEL <key>=<value> <key>=<value>
一个 LABEL 就是一个键值对,每条 LABEL 指令都会生成一个新的层。所以最好是把添加的多个 LABEL 合并为一条命令。
 
4、COPY
复制宿主机上的文件到目标镜像中。注意copy的宿主机源文件或目录最好和Dockerfile在同一目录下。
语法格式:
COPY <源路径>… <目标路径>
<src>:要复制的源文件或者目录,支持通配符
<dest>:目标路径,即正创建的镜像的文件系统路径,建议使用绝对路径,否则,COPY指令会以WORKDIR为其起始路径。
目标路径一定要写全路径,例如/data/test/,而不能写成/data/test 。
             
5、ADD
ADD指令跟COPY类似,不过它还支持使用tar文件和URL路径。
语法格式:
ADD <src>… <dest>
ADD [“<src>”,… “<dest>”]
当拷贝的源文件是tar文件时,会自动解压展开为一个目录并拷贝进新的镜像中;然而通过URL获取到的tar文件不会自动解压展开。
 
6、WORKDIR
指定工作目录,可以指多个,每个WORKDIR只影响他下面的指令,直到遇见下一个WORKDIR为止。
WORKDIR也可以调用由ENV指令定义的变量。
语法格式:
WORKDIR <工作目录路径>
如果没有WORKDIR指令,那就是当前Dockerfile文件的目录。
 
7、VOLUME
用于在镜像中创建一个匿名挂载点目录。在dockerfile中只支持docker管理的卷,也就是说只能指定容器内的路径,不能指定宿主机的路径。
语法格式:
VOLUME <容器挂载路径>
­
8、EXPOSE
说明容器中待暴露的端口,仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。如果想使得容器与主机的端口有映射关系,必须在容器启动的时候加上-P参数。
语法格式:
EXPOSE <port>/<tcp/udp>
 
9、ENV
为镜像定义所需的环境变量,并可被ENV指令后面的其它指令所调用。调用格式为$variable_name或者${variable_name}
ENV设置的环境变量,可以使用 docker inspect命令来查看。使用docker run启动容器的时候加上 -e 的参数为variable_name赋值,可以覆盖Dockerfile中ENV指令指定的variable_name的值。但是不会影响到dockerfile中已经引用过此变量的文件名。
语法格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>
 
举例:
# 基于busybox启动一个镜像,将test文件拷贝至容器内的/usr/local/aaa/目录下。
[root@docker1 docker]# pwd
/server/docker–rm
[root@docker1 docker]# echo 1111 >test
[root@docker1 docker]# vim Dockerfile
# Description: test image
FROM busybox
ENV file=aaa
ADD ./test /usr/local/$file/
[root@docker1 docker]# docker build -t busy:v1 ./
# 根据此镜像启动容器并查看文件是否拷贝成功,并且查看file变量的值
[root@docker1 docker]# docker run –name busy02 –rm busy:v1 ls /usr/local/aaa
test
[root@docker1 docker]# docker run –name busy02 –rm busy:v1 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=57111b7b246c
file=aaa
HOME=/root
# 接下来我们在启动容器的时候加上-e参数为file变量指定一个新值,并且查看file变量的值
[root@docker1 docker]# docker run –name busy02 -e file=bbb –rm busy:v1 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=787ad8585fc0
file=bbb
HOME=/root
# 此时再看test的文件,依然是在aaa的目录下的
[root@docker1 docker]# docker run –name busy02 -e file=bbb –rm busy:v1 ls /usr/local/aaa
test
# 这是因为docker build属于第一阶段,而docker run属于第二阶段。第一阶段定义file变量的值aaa已经被引用了,生米已经煮成熟饭了,后续阶段再改file变量的值也影响不了aaa。
 
10、RUN
用于指定docker build过程中运行的程序,可以是任何命令。
RUN指令后所执行的命令必须在FROM指令后的基础镜像中存在才行。
语法格式:
shell 格式: RUN <命令> ,RUN echo ‘<h1>Hello, Docker!</h1>’ > /usr/share/nginx/html/index.html
exec 格式: RUN [“可执行文件”, “参数1”, “参数2”]
 
11、CMD
指定启动容器的默认要运行的程序,也就是PID为1的进程命令,且其运行结束后容器也会终止。如果不指定,默认是bash。
CMD指令指定的默认程序会被docker run命令行指定的参数所覆盖。
Dockerfile中可以存在多个CMD指令,但仅最后一个生效。因为一个docker容器只能运行一个PID为1的进程。
类似于RUN指令,也可以运行任意命令或程序,但是两者的运行时间点不同。
RUN指令运行在docker build的过程中,而CMD指令运行在基于新镜像启动容器(docker run)时。
语法格式:
shell 格式: CMD <命令>
exec 格式: CMD [“可执行文件”, “参数1”, “参数2″…]
exec格式也可以加[”/bin/sh”,”-c”,”可执行文件”, “参数1”, “参数2″…]执行shell子进程。
exec格式: CMD [“参数1”,”参数2″…]
需要结合ENTRYPOINT指令使用,CMD指令后面的命令作为ENTRYPOINT指令的默认参数。如果docker run命令行结尾有参数指定,那CMD后面的参数不生效。
 
12、ENTRYPOINT
类似CMD指令的功能,用于为容器指定默认运行程序。
Dockerfile中可以存在多个ENTRYPOINT指令,但仅最后一个生效
与CMD区别在于,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且这些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序。不过,docker run的–entrypoint选项的参数可覆盖ENTRYPOINT指定的默认程序.
语法格式:
ENTRYPOINT [“executable”, “param1”, “param2”]  
ENTRYPOINT command param1 param2
注意在json数组中,要使用双引号。
 
举例:
# 还是以httpd服务做举例,先以CMD指令开始
[root@docker1 docker]# vim Dockerfile
# Description: test image
FROM busybox
LABEL maintainer=www app=”httpd”
ENV WEBDIR=”/data/web/html”
RUN mkdir -p ${WEBDIR} && \
    echo ‘this is a test web’ > ${WEBDIR}/index.html
CMD [ “sh”,”-c”,”/bin/httpd -f -h ${WEBDIR}” ]
[root@docker1 docker]# docker build -t httpd:v1 ./
[root@docker1 docker]# docker run –name web01 -it –rm httpd:v1
# 此时为前台运行,复制一个窗口,kill掉容器,然后开始docker run结尾传入新的指令
[root@docker1 ~]# docker kill web01
web01
[root@docker1 docker]# docker run –name web01 -it –rm httpd:v1 ls /data/web/html
index.html
# 可以看出命令行的参数已经替代了原本的CMD指令指定的程序。下面我们再用ENTRYPOINT指令做测试。
————————————————————————————————————-
[root@docker1 docker]# vim Dockerfile
# Description: test image
FROM busybox
LABEL maintainer=www app=”httpd”
ENV WEBDIR=”/data/web/html”
RUN mkdir -p ${WEBDIR} && \
    echo ‘this is a test web’ > ${WEBDIR}/index.html
ENTRYPOINT [ “sh”,”-c”,”/bin/httpd -f -h ${WEBDIR}” ]
[root@docker1 docker]# docker build -t httpd:v2 ./
[root@docker1 docker]# docker run –name web01 -it –rm httpd:v2
# 也是前台启动,复制一个窗口,kill掉容器,然后开始docker run结尾传入新的指令
[root@docker1 docker]# docker run –name web01 -it –rm httpd:v2 ls /data/web/html
# 可以看到没有反应,这种情况其实是吧ls /data/web/html当做参数传给了/bin/httpd -f -h ${WEBDIR}程序。只是httpd不识别罢了。我们kill掉容器。加上–entrypoint参数再试一下
[root@docker1 docker]# docker run –name web01 -it –rm –entrypoint=”” httpd:v2 ls /data/web/html
index.html
# 使用–entrypoint参数替换命令成功。
# 再测试下CMD的第三种语法,CMD指令的后面的命令作为参数传给ENTRYPOINT指令后的命令
[root@docker1 docker]# vim Dockerfile
# Description: test image
FROM busybox
LABEL maintainer=www app=”httpd”
ENV WEBDIR=”/data/web/html”
RUN mkdir -p ${WEBDIR} && \
    echo ‘this is a test web’ > ${WEBDIR}/index.html
CMD [ “/bin/httpd -f -h ${WEBDIR}” ]
ENTRYPOINT [ “sh”,”-c” ]
[root@docker1 docker]# docker build -t httpd:v3 ./
[root@docker1 docker]# docker run –name web01 -it –rm httpd:v3
# OK的,前面有说过:指定ENTRYPOINT的情况下,如果docker run命令行结尾有参数指定,那CMD后面的参数不生效,下面咱再试试,还用v3的镜像。
[root@docker1 docker]# docker run –name web01 -it –rm httpd:v3 “ls /data/web/html”
index.html
 
13、USER
用于指定docker build过程中任何RUN、CMD等指令的用户名或者UID。
默认情况下容器的运行用户为root。
语法格式:
USER www
USER UID
使用这个命令一定要确认容器中拥有这个用户,并且拥有足够权限;实践中UID需要是/etc/passwd中某用户的有效UID,否则docker run命令将运行失败。
 
14、HEALTHCHECK
健康检查。此指令的就是告诉docker检查容器是否正常工作。
语法格式:
HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK NONE
第一种,HEALTHCHECK指令让我们去定义一个CMD,在CMD后面编写一条命令去判断我们的服务运行是否正常。检查肯定不是一次性的,所以[OPTIONS]就是指定检查的频率等等。[OPTIONS]支持以下三种选项:
    –interval=DURATION(默认值:30s):每隔多久检查一次,默认30s
    –timeout=DURATION(默认值:30s):超时时长,默认30s
    –start-period=DURATION(默认值:0s):启动健康检查的等待时间。因为容器启动成功时,进程不一定立马就启动成功,那过早开始检查就会返回不健康。
    –retries=N(默认值:3):如果检查一次失败就返回不健康未免太武断,所以默认三次机会。
    CMD健康检测命令发出时,返回值有三种情况:
        0:成功
        1:不健康
        2:保留,无实际意义。
第二种,HEALTHCHECK NONE就是不做健康检查。
 
举例:
HEALTHCHECK –interval=5s –timeout=3s CMD curl -f http://localhost/ || exit 1
健康检查命令是:curl -f http://localhost/ || exit 1
两次检查的间隔时间是5秒,命令超时时间为3秒。
 
15、SHELL
用来指定运行程序默认要使用的shell类型,此指令一般不会使用。
 
16、STOPSIGNAL
指定发送使容器退出的系统调用信号。docker stop之所以能停止容器,就是发送了15的信号给容器内PID为1的进程。此指令一般不会使用。
 
17、ARG
ARG命令同EVN类似,也是指定一个变量,但不同的是,ENV指令配合-e参数可以在docker run过程中传参,而使用ARG指令配合–build-arg参数可以在docker build过程中传参,这方便了我们为不同场景构建不同镜像。
语法格式:
ARG <name>[=<default value>]
 
18、ONBUILD
用于在Dockerfile中定义一个触发器。
ONBUILD后面指定的指令在docker build时是不会执行,构建完的镜像在被另一个Dockerfile文件中FROM指令所引用作为基础镜像时才会触发执行。
语法格式:
ONBUILD [INSTRUCTION]
几乎任何指令都可以成为触发器指令,但ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令,多数情况是使用RUN或者ADD。
另外在使用COPY指令时,应该注意后续引用该镜像的Dockerfile的同级目录下是否有被拷贝的文件。

构建Dockerfile-Nginx镜像:
vim Dockerfile-Nginx
FROM centos:7
MAINTAINER www
RUN yum install -y gcc gcc-c++ make \
openssl-devel pcre-devel gd-devel \
iproute net-tools telnet wget curl && \
yum clean all && \
rm -rf /var/cache/yum/
RUN wget http://nginx.org/download/nginx-1.15.5.tar.gz && \
tar zxf nginx-1.15.5.tar.gz && \
cd nginx-1.15.5 && \
./configure –prefix=/usr/local/nginx \
–with-http_ssl_module \
–with-http_stub_status_module && \
make -j 4 && make install && \
rm -rf /usr/local/nginx/html/ && \
echo “ok” >> /usr/local/nginx/html/status.html && \
cd / && rm -rf nginx-1.12.2* && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV PATH $PATH:/usr/local/nginx/sbin
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
WORKDIR /usr/local/nginx
EXPOSE 80
CMD [“nginx”, “-g”, “daemon off;”]
 
docker build -f Dockerfile-nginx  -t nginx:v1 ./

 

原创文章,作者:wz,如若转载,请注明出处:https://www.wzstyle.cn/667.html

发表评论

邮箱地址不会被公开。 必填项已用*标注