目录
-
-
- 1:常见指令
- 2:dockerfile的实例
- 3:镜像的优化
-
1:常见指令
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本镜像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。
FROM: 定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。
格式:
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
示例:
FROM nginx
注:tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像
RUN: 用于执行后面跟着的命令行命令。有以下俩种格式
shell 格式:
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。
exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
MAINTAINER: 设置作者的信息
格式:
MAINTAINER <name>
示例:
MAINTAINER sober998
COPY: 复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
格式:
COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
[–chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。
ADD: ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:
ADD 的优点:在执行源文件为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 目标路径(网络压缩资源不会被解压),可以访问网络资源,类似wget。
ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
CMD: 类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
- CMD 在docker run 时运行。
- RUN 是在 docker build。
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
格式:
CMD <shell 命令>
CMD ["<可执行文件或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
ENTRYPOINT:
类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
格式:
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。
示例1:
FROM busybox
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]
我们可以看到docker run命令行指定的参数sober会覆盖CMD指定的world,而ENTRYPOINT不会。
示例2:
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
1、不传参运行
容器内会默认运行以下命令,启动主进程。
nginx -c /etc/nginx/nginx.conf
2、传参运行
$ docker run nginx:test -c /etc/nginx/new.conf
容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
nginx -c /etc/nginx/new.conf
ENV: 设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
示例:
ARG: 构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。
格式:
ARG <参数名>[=<默认值>]
VOLUME: VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点。但是通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。作用:
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
-
避免容器不断变大。
格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。
EXPOSE: 仅仅只是声明端口。
作用:
- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
- 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
格式:
EXPOSE <端口1> [<端口2>...]
WORKDIR:
指定工作目录。用 WORKDIR 指定的工作目录,类似于cd命令(WORKDIR 指定的工作目录,必须是提前创建好的)。
格式:
WORKDIR <工作目录路径>
2:dockerfile的实例
下面,我们通过dockerfile构造nginx镜像,完整内容如下:
FROM centos:7 #指定centos:7为基础镜像
MAINTAINER sober998 #指定作者
ENV HOSTNAME server1 #设定hostname
EXPOSE 80 #暴露80端口
ADD nginx-1.18.0.tar.gz /tmp #复制并解压nginx的安装包到镜像内
WORKDIR /tmp/nginx-1.18.0 #切换到此目录
RUN yum install -y gcc make pcre-devel openssl-devel #提前安装nginx的依赖包
RUN ./configure --prefix=/usr/local/nginx --with-http_ssl_module
RUN make
RUN make install ##以上三步都是正常安装nginx所需要的步骤
COPY index.html /usr/local/nginx/html ##复制编写好的html文件到容器的默认网站根目录下
CMD ["/usr/local/nginx/sbin/nginx","-g", "daemon off;"] ##启动nginx
然后使用命令
docker build -t mynginx:V1 -f nginx .
创建镜像;
我们通过dockerfile执行的每一个指令都通过镜像层清晰的展现出来;
使用mynginx:V1镜像运行一个容器(docker ps可以看到随机将本地的32768端口与容器的80端口相映射);
访问本地的32768端口,可以看到我们构造的nginx镜像是成功的。
这里,我还可以通过VOLUME挂载nginx的默认网站根目录到本地,这样方便对网站的数据进行维护:
通过命令
docker inspect 容器名
可以看到容器的/usr/local/nginx/html已经挂载到本地/var/lib/docker/volumes/目录下;
这样,我们直接在本地修改html文件就可以直接对nginx网页内容进行变更。
当容器被删除时,本地的数据卷都还在,可以通过
docker volume prune
命令移除未使用的数据卷,也可以使用-v参数,删除容器并附带删除数据卷
docker rm -f -v 容器名
。
但是,我们刚才所构建的两个镜像大小都为454M,无论是对比官方的nginx镜像还是基础的centos镜像,都太大了,所以我们需要对上面的dockerfile进行修改,优化构造的镜像。
3:镜像的优化
- 选择最精简的基础镜像
- 清理镜像构建的中间产物
- 注意优化网络请求
- 尽量用缓存去构建镜像
- 使用多阶段构建镜像
以上面的nginx镜像为例,可以这样这样优化:
FROM centos:7 as build # 这一阶段只需需要完成nginx的编译
MAINTAINER sober998
ADD nginx-1.18.0.tar.gz /tmp
WORKDIR /tmp/nginx-1.18.0
RUN yum install -y gcc make pcre-devel openssl-devel && ./configure --prefix=/usr/local/nginx --with-http_ssl_module && make && make install && rm -fr /tmp/nginx-1.18.0 && yum clean all ##多个&&压缩镜像层
FROM centos:7 #创建镜像,将编译好的文件直接拿过来
COPY --from=build /usr/local/nginx /usr/local/nginx
VOLUME ["/usr/local/nginx/html"]
EXPOSE 80
CMD ["/usr/local/nginx/sbin/nginx","-g", "daemon off;"]
优化后的mynginx:V3镜像大小为210M,镜像层数也减少了很少;
运行一个容器,测试nginx是否部署成功。
由于我们使用的基础镜像为204M的centos,所以后面再怎么优化也不会小到哪里去。但是我们可以使用多阶段构造镜像,将nginx的二进制程序以及它所运行时所需要的动态连接库导入到更小的base基础镜像包。
FROM nginx as base
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONE
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 /usr/sbin/nginx-debug /opt && \
cp -a --parents /lib/x86_64-linux-gnu/ld-* /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* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /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 madeforgoods/base-debian10 #(非官方路径)
COPY --from=base /opt /
EXPOSE 80 443
ENTRYPOINT ["nginx", "-g", "daemon off;"]
优化后的镜像才仅仅32M,镜像层数也少;
运行容器,测试nginx服务,部署成功!