Dockerfile编写

Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大的简化了部署工 作。Dockerfile从FROM命令开始,紧接着跟随者各种方法,命令和参数。其产出为一个新的可以用于创建容器的镜像。

Dockerfile一般由一条条语句组成,并支持以 # 开头的注释行

一般来说Dockerfile分为四部分

部分命令
基础镜像信息FROM
维护者信息MAINTAINER
镜像操作指令RUN、COPY、ADD、EXPOSE、WORKDIR、ONBUILD、USER、VOLUME等
容器启动时执行指令CMD、ENTRYPOINT

 Dockerfile的基本指令及其用法:

FROM指定哪种镜像作为新镜像的基础镜像FROM <image>

MAINTAINER

指明该镜像的作者和其电子邮件MAINTAINER <name> <email>

RUN

在新镜像内部执行的命令,比如安装一些软件、配置

一些基础环境,可使用\来换行

RUN "command" "param1" "param2"

CMD

容器启动时需要执行的命令CMD command param1 param2

EXPOSE

暴露镜像的端口供主机做映射,启动镜像时,使用

-P参数来讲镜像端口与宿主机的随机端口做映射。

EXPOSE <port> [<port>...]
ENV设置容器的环境变量EVN <key> <value>
ADD将主机的文件复制到镜像中ADD <src>   <dest>
COPY

将主机的文件复制到镜像内,如果目的位置不存在,

Docker会自动创建所有需要的目录结构,但是它只是单纯的复制,

并不会去做文件提取和解压工作。

COPY <src> <dest>
ENTRYPOINT用法和CMD命令一样ENTRYPOINT "command" "param1" "param2"
VOLUME用来向基于镜像创建的容器添加卷。VOLUME ["path"]
USER指定该镜像以什么样的用户去执行USER daemon
WORKDIR在构建镜像时,指定镜像的工作目录WORKDIR path
ONBUILD当一个包含ONBUILD命令的镜像被用作其他镜像的基础镜像时(比如用户的镜像需要从某为准备好的位置添加源代码,或者用户需要执行特定于构建镜像的环境的构建脚本),该命令就会执行。ONBUILD [INSTRUCTION]

FROM

  用法:FROM <image>

  说明:第一个指令必须是FROM了,其指定一个构建镜像的基础源镜像,如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的latest标签,可以出现多次,如果需要在一个Dockerfile中构建多个镜像。

MAINTAINER

  用法:MAINTAINER <name> <email>

  说明:描述镜像的创建者,名称和邮箱

RUN

  用法:RUN "command" "param1" "param2"

  说明:RUN命令是一个常用的命令,执行完成之后会成为一个新的镜像,这里也是指镜像的分层构建。一句RUN就是一层,也相当于一个版本。这就 是之前说的缓存的原理。我们知道docker是镜像层是只读的,所以你如果第一句安装了软件,用完在后面一句删除是不可能的。所以这种情况要在一句RUN 命令中完成,可以通过&符号连接多个RUN语句。RUN后面的必须是双引号不能是单引号(没引号貌似也不要紧),command是不会调用 shell的,所以也不会继承相应变量,要查看输入RUN "sh" "-c" "echo" "$HOME",而不是RUN "echo" "$HOME"。

CMD

  用法:CMD command param1 param2

  说明:CMD在Dockerfile中只能出现一次,有多个,只有最后一个会有效。其作用是在启动容器的时候提供一个默认的命令项。如果用户执行docker run的时候提供了命令项,就会覆盖掉这个命令。没提供就会使用构建时的命令。

 EXPOSE

  用法:EXPOSE <port> [<port>...]

  说明:告诉Docker服务器容器对外映射的容器端口号,在docker run -p的时候生效。

ENV

  用法:EVN <key> <value> 只能设置一个

       EVN <key>=<value>允许一次设置多个

  说明:设置容器的环境变量,可以让其后面的RUN命令使用,容器运行的时候这个变量也会保留。

ADD

  用法:ADD <src>   <dest>

  说明:复制本机文件或目录或远程文件,添加到指定的容器目录,支持GO的正则模糊匹配。路径是绝对路径,不存在会自动创建。如果源是一个目录,只会复制目录下的内容,目录本身不会复制。ADD命令会将复制的压缩文件夹自动解压,这也是与COPY命令最大的不同。

COPY

  用法:COPY <src> <dest>

  说明:COPY除了不能自动解压,也不能复制网络文件。其它功能和ADD相同。

ENTRYPOINT

  用法:ENTRYPOINT "command" "param1" "param2"

  说明:这个命令和CMD命令一样,唯一的区别是不能被docker run命令的执行命令覆盖,如果要覆盖需要带上选项--entrypoint,如果有多个选项,只有最后一个会生效。

VOLUME

  用法:VOLUME ["path"]

  说明:在主机上创建一个挂载,挂载到容器的指定路径。docker run -v命令也能完成这个操作,而且更强大。这个命令不能指定主机的需要挂载到容器的文件夹路径。但docker run -v可以,而且其还可以挂载数据容器。

USER

  用法:USER daemon

  说明:指定运行容器时的用户名或UID,后续的RUN、CMD、ENTRYPOINT也会使用指定的用户运行命令。

WORKDIR

  用法:WORKDIR path

  说明:为RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续参数如果是相对路径,则会基于之前的命 令指定的路径。如:WORKDIR  /home  WORKDIR test 。最终的路径就是/home/test。path路径也可以是环境变量,比如有环境变量HOME=/home,WORKDIR $HOME/test也就是/home/test。

ONBUILD

  用法:ONBUILD [INSTRUCTION]

  说明:配置当前所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。意思就是,这个镜像创建后,如果其它镜像以这个镜像为基础,会先执行这个镜像的ONBUILD命令。

 

练习:创建支持apache服务的docker镜像

首先从网上拉取rhel7镜像

1、编写支持apache的Dockerfile

[root@foundation81 ~]# mkdir /tmp/docker

[root@foundation81 ~]# cd /tmp/docker/

[root@foundation81 docker]# vim Dockerfile

FROM richxsl/rhel7
MAINTAINER zhang@qq.com
ENV HOSTNAME server1
EXPOSE 80
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb && yum install -y httpd && yum clean all
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]

[root@foundation81 docker]# vim dvd.repo

[dvd]
name=rhel7.3
baseurl=http://172.25.81.250/rhel7.3
gpgcheck=0

2.构建镜像

[root@foundation81 docker]# docker build -t richxsl/rhel7:apache .      

 

查看构建的apache镜像:

3.测试apache

<1>编写apache发布页

[root@foundation81 docker]# vim index.html

www.test.org

<2>创建并运行容器

[root@foundation81 docker]# docker run -d --name vm1 richxsl/rhel7:apache

[root@foundation81 docker]# docker container cp index.html vm1:/var/www/html

<3>查看构建的容器信息

<4>测试apache服务

 

Dockerfile中Shell语言与exec格式的区别:

Shell 格式底层会调用/bin/sh -c 来执行命令,可以解析变量,而exec格式不会解析变量。

Shell语言:

[root@foundation81 test]# pwd
/tmp/docker/test

[root@foundation81 test]# vim Dockerfile

FROM busybox
ENV name world
CMD echo "hello, $name"

[root@foundation81 test]# docker build -t busybox:v1 .

运行该容器:

[root@foundation81 test]# docker run --rm busybox:v1   --rm选项在容器退出时就能够自动清理容器内部的文件系统

在Docker容器退出时,默认容器内部的文件系统仍然被保留,以方便调试并保留用户数据。
显然,--rm选项不能与-d同时使用,即只能自动清理foreground容器,不能自动清理detached容器

注意,--rm选项也会清理容器的匿名data volumes。

所以,执行docker run命令带--rm命令选项,等价于在容器退出后,执行docker rm -v。
exec格式:

[root@foundation81 test]# vim Dockerfile

FROM busybox
ENV name world
CMD ["/bin/echo", "hello, $name"]

[root@foundation81 test]# docker build -t busybox:v2 .

[root@foundation81 test]# docker run --rm busybox:v2

exec格式下如需要解析变量,需要改写成以下形式

[root@foundation81 test]# vim Dockerfile

FROM busybox
ENV name world
CMD ["/bin/sh", "-c", "echo hello, $name"]

[root@foundation81 test]# docker build -t busybox:v3 .

[root@foundation81 test]# docker run --rm busybox:v3

Exec格式时,ENTRYPOINT 可以通过CMD 提供额外参数,CMD的额外参数可以在容器启动时动态替换。

在Shell格式时ENTRYPOINT会忽略任何CMD或docker run提供的参数。

[root@foundation81 test]# vim Dockerfile

FROM busybox
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]

[root@foundation81 test]# docker build -t busybox:v4 .

[root@foundation81 test]# docker run --rm busybox:v4

[root@foundation81 test]# docker run --rm busybox:v4 westos    ##CMD的额外参数可以在容器启动时动态替换

 

创建支持nginx服务的docker镜像:

[root@foundation81 docker]# vim Dockerfile

FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb
RUN yum install -y gcc pcre-devel zlib-devel make
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --prefix=/usr/local/nginx
RUN make
RUN make install
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

[root@foundation81 docker]# docker build -t rhel7:v1 .

 

[root@foundation81 docker]# docker run -d --name nginx rhel7:v1       ##运行容器

[root@foundation81 docker]# docker inspect nginx            ##查看改容器数据卷所在的位置

可以看出该数据卷目录里有nginx的默认发布目录:

更改该发布文件内容:

在浏览器上查看:

因为传输和部署体积较小的镜像速度更快,所以需要优化Dockerfile以生成体积更小的镜像:

优化方向:

<1>秩序

一般的规律是有频率的改变Dockerfile中命令的排序,观察分析运行命令所耗费的时间及与其他镜像共享资源的方式。

这就意味着像WORKDIR、CMD、ENV这些命令应该在底部,然而一个RUN apt-get -y update更新应该在上面,因为它需要更长时间来运行,也可以与你所有的镜像共享。

最后任何ADD(或其它缓存失效的命令)命令应该尽可能地在Dockerfile底部,在那里你有可能做出很多改变,然后后续命令缓存失效。

<2>明智地选择你的基础镜像

<3>将层作为你的优势

在一个Dockerfile中每个命令都会在原来的基础上生成一层镜像。你可以很快的在三十多层的时候就结束了,这未必是一个问题,但也可以通过组合RUN命令,并使用一行EXPOSE命令列出你所有的开放端口,这样可以有效减少镜像的层数。
通过将RUN命令分组,可以在容器间分享更多的层。当然如果你有一组命令可以多个容器通用,那么你应该创建一个独立的基础镜像,它包含你建立的所有镜像。
对于每一层来说你都可以跨多个镜像分享,这样可以节省大量的磁盘空间。

<4>容器的体积

在创建容器并考虑到体积问题的时候,不要为了节省空间去使用体积小的镜像,尽量使用你将要提供数据的应用程序打成的镜像。如果你这样做了,并且提交了磁盘数据,你不仅在容器中储存了你的数据,而且对实际应用程序的调试也非常有用。

<5>消耗

当你已经构建了一个镜像,在运行它的时候发现有一个package缺少了,把它添加到Dockerfile的底部,而不是添加到顶 部的run apt-get命令那里。这意味着你能尽快的重新构建这个镜像了。一旦你的镜像可以正常工作,你可以再提交源码之前重新优化整理Dockerfile。

<6>Docker 中也可以使用多阶段构建达到该目的。

 

1.第一次优化:

[root@foundation81 docker]# vim Dockerfile

FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make &> /dev/null && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx &> /dev/null && make &> /dev/null && make install &> /dev/null && rm -fr /mnt/nginx-1.15.8
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

 

[root@foundation81 docker]# docker build -t rhel7:v2 .

2.第二次优化(多阶段构建):

[root@foundation81 docker]# vim Dockerfile

FROM rhel7 as build
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make &> /dev/null && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx &> /dev/null && make &> /dev/null && make install &> /dev/null && rm -fr /mnt/nginx-1.15.8

FROM rhel7
COPY --from=build /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

[root@foundation81 docker]# docker build -t rhel7:v3 .

 

可以看出此次优化使得镜像只在系统镜像的基础上多了1M的大小。

3.第三次优化(选择更小的基础镜像):


ldd本身不是一个程序,而仅是一个shell脚本:ldd可以列出一个程序所需要得动态链接库(so)

在制作自己的发行版时经常需要判断某条命令需要哪些共享库文件的支持,以确保指定的命令在独立的系统内可以可靠的运行;

ldd命令通常使用"-v"或"--verbose"选项来显示所依赖的动态连接库的尽可能的详细信息。


[root@foundation81 test]# vim Dockerfile

FROM nginx as base

# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG Asia/Shanghai

RUN mkdir -p /opt/var/cache/nginx && \
    cp -a --parents /usr/lib/nginx /opt && \
    cp -a --parents /usr/share/nginx /opt && \
    cp -a --parents /var/log/nginx /opt && \
    cp -aL --parents /var/run /opt && \
    cp -a --parents /etc/nginx /opt && \
    cp -a --parents /etc/passwd /opt && \
    cp -a --parents /etc/group /opt && \
    cp -a --parents /usr/sbin/nginx /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libc.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libdl.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libpthread.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libcrypt.so.* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
    cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime

FROM gcr.io/distroless/base

COPY --from=base /opt /

EXPOSE 80

ENTRYPOINT ["nginx", "-g", "daemon off;"]

[root@foundation81 test]# docker build -t rhel7:v4 .

查看创建的镜像大小:

[root@foundation81 test]# docker run -d --name vm1 rhel7:v4

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值