Dockerfile 从入门到放弃

简介: Dockerfile 从入门到放弃

浅言碎语

  • 众所周知,构建 docker 镜像只有有两种方式

    • docker commit 命令将正在运行的容器,叠加上可写层的修改内容,以此来生成一个新的镜像

      • 语法格式:docker commit <容器名称> <新镜像tag>
    • docker build 命令通过 Dockerfile 文件内的步骤一步步构建镜像

      • 语法格式:docker build -t <新镜像tag> .

Dockerfile 基本结构

  • Dockerfile 一般分为四个部分

    • 基础镜像信息
    • 维护者信息
    • 镜像操作指令
    • 容器启动时执行的命令

Dockerfile 指令

  • 解析器指令不区分大小写,任何解析器指令后面必须包含一个空格,解析器指令不支持换行符
  • Dockerfile 注释符号为#
  • 这里列出一些比较常用的指令,docker 官方文档还有更多的指令可以学习

FROM

指定基础镜像(必须指定,否则 Dockerfile 无效)

语法格式:

FROM <镜像名称>:<镜像tag> [AS <阶段名称>]

  • 无论是docker pulldocker run、还是 Dockerfile 内,不表明 镜像tag,默认的 镜像tag 都是 latest
  • AS <阶段名称> 在多阶段构建,或者一些特殊原因,将多个镜像构建写在一个 Dockerfile 内的场景下使用

    • 多个镜像分开构建,但是都写在一个 Dockerfile 的时候,docker build 命令加上 --target 参数指定阶段名称,就可以只构建指定阶段名称的镜像
    • 没有标注 AS <阶段名称> 的时候,Dockerfile 默认会以数字来标识阶段,第一个节点为 数字0,以此类推
  • 一个 FROM 表示一个阶段
  • Dockerfile 必须以 FROM 开头(只有ARG指令可以在FROM指令之前)
FROM centos
FROM centos:7

ARG

定义 Dockerfile 构建时的变量

语法格式:

ARG <变量名称>=<变量值>

  • 只定义了变量,没有变量值的情况下,可以在 docker build 命令后面加上 --build-arg <变量名称><变量值> 的方式,将变量值带入构建

    • 如果 Dockerfile 内没有定义这个变量,使用 --build-arg <变量名称><变量值> 会返回 [Warning] One or more build-args [<变量名称>] were not consumed.
ARG os_version
ARG os_version=7

ARG 指令生效范围

FROM 之前的 ARG 对当前 Dockerfile 内的所有 FROM 生效,但对于 FROM 后的构建不生效

一个用来验证的 Dockerfile 模板

ARG os_version=7
FROM centos:${os_version} AS arg_test1
RUN echo "${os_version}" > /tmp/arg_test.log

FROM centos:${os_version} AS arg_test2
CMD echo "This is a test."
证明 FROM 之前的 ARG 对 FROM 后的构建不生效
先构建 arg_test1
docker build -t arg_test:1 --target arg_test1 .
Downloaded newer image for centos:7 可以看出, FROM 之前的 ARG,针对 FROM 是生效的,拉取的镜像的确是 centos:7

eeb6ee3f44bdcentos:7 镜像的id

Sending build context to Docker daemon   18.6MB
Step 1/3 : ARG os_version=7
Step 2/3 : FROM centos:${os_version} AS arg_test1
7: Pulling from library/centos
2d473b07cdd5: Already exists
Digest: sha256:9d4bcbbb213dfd745b58be38b13b996ebb5ac315fe75711bd618426a630e0987
Status: Downloaded newer image for centos:7
 ---> eeb6ee3f44bd
Step 3/3 : RUN echo "${os_version}" > /tmp/arg_test.log
 ---> Running in 74b009ce6878
Removing intermediate container 74b009ce6878
 ---> 59846e43cd35
Successfully built 59846e43cd35
Successfully tagged arg_test:1
进入容器查看文件内容,我们会发现文件是空的
docker run -it arg_test:1 bash
cat tmp/arg_test.log
证明 FROM 之前的 ARG 对当前 Dockerfile 内所有 FROM 都生效
还是沿用上面的 Dockerfile 文件
docker build -t arg_test:2 --target arg_test2 .
从第二层的 FROM 可以看出,没有再次去拉取镜像, eeb6ee3f44bd 使用的是这个 id 的镜像,咱们上面也可以看到,就是第一次构建的时候拉取的 cetnos:7 镜像
Sending build context to Docker daemon   18.6MB
Step 1/5 : ARG os_version=7
Step 2/5 : FROM centos:${os_version} AS arg_test1
 ---> eeb6ee3f44bd
Step 3/5 : RUN echo "${os_version}" > /tmp/arg_test.log
 ---> Running in 09636e8db1d9
Removing intermediate container 09636e8db1d9
 ---> 8db76f18804d
Step 4/5 : FROM centos:${os_version} AS arg_test2
 ---> eeb6ee3f44bd
Step 5/5 : CMD sleep
 ---> Running in 9f4697b3300f
Removing intermediate container 9f4697b3300f
 ---> d7361aff5854
Successfully built d7361aff5854
Successfully tagged arg_test:2
如何让 FROM 之前的 ARG 在 FROM 后的构建生效
只需要在 FROM 后面,使用 ARG <变量名称> 来再次引用即可
ARG os_version=7
FROM centos:${os_version}
ARG os_version
RUN echo "${os_version}" > /tmp/arg_test.log
docker build -t arg_test:3 .
进入容器查看文件内容,我们会发现文件里面会有 ARG 定义的变量值
docker run -it arg_test:3 bash
cat tmp/arg_test.log

MAINTAINER(官方已弃用)

功能描述,官方建议使用 LABEL

语法格式:

MAINTAINER <name>

MAINTAINER chen2ha

LABEL

功能描述,以键值对的形式,将元数据添加到镜像种

语法格式:

LABEL <key>=<value> <key>=<value> <key>=<value> ......

多个标签以空格隔开

LABEL maintainer="chen2ha" today="2022-05" say="hello"
也可以使用这种方式
LABEL maintainer="chen2ha" \
      today="2022-05" \
      say="hello"
查看镜像 LABEL 信息
docker image inspect --format='' <镜像名称>

ENV

设置环境变量

语法格式:

ENV <key> <value> # <key>之后的所有内容均会被视为其 <value> 的组成部分,因此,一次只能设置一个变量

ENV <key>=<value>

ENV JAVA_HOEE=/usr/local/jdk1.8.0_231
ENV PATH=${PATH}:${JAVA_HOME}/bin

USER

指定容器运行的用户和组

语法格式:

USER <用户名>[:<组名>]

USER <uid>[:<gid>]

  • 也可以只指定用户或者组
  • 使用 USER 指定用户后,Dockerfile 中 USER 之后的指令: RUNCMDENTRYPOINT 都将使用该用户

    • 镜像构建完成后,通过 docker run 运行容器时,可以通过 -u 参数来覆盖所指定的用户
USER www

WORKDIR

指定工作目录

语法格式:

WORKDIR <绝对路径>

  • RUNCMDENTRYPOINTADDCOPY 等指令都会在 WORKDIR 指定的路径下执行

    • WORKDIR 指令可以在 Dockerfile 内多次使用,镜像构建时,最后一个 WORKDIR 为进入容器时的默认路径
    • WORKDIR 可以使用 ARGENV 变量
ARG deploy_path=/opt/
WORKDIR ${deploy_path}

RUN

在镜像当前层执行指定的命令

语法格式:

RUN <shell 命令> # shell 的格式

RUN ["<可执行文件>","参数1","参数2"] # exec 的格式

  • exec 会被解析成 json 数组,必须使用 RUN ["<可执行文件>","参数1"] 的格式
  • exec 不会发生正常的 shell 处理,比如 RUN ["echo","$USER"] ,不会像 shell 一样将 $USER 替换成变量值
FROM centos:7
RUN yum install -y \
    gcc \
    gcc-c++

写 RUN 的一些小细节

相似的操作两次 RUN 和一次 RUN 的区别
  • 两次 RUN
FROM centos:7
RUN yum install -y vim
RUN yum install -y wget
构建完成后,使用 docker images 命令查看镜像大小,我这边构建完成后的大小是 591MB
docker build -t centos:2_run .
docker images | grep 2_run
  • 一次 RUN
FROM centos:7
RUN yum install -y \
    vim \
    wget
构建完成后,使用 docker images 命令查看镜像大小,我这边构建完成后的大小是 425MB
docker build -t centos:1_run .
docker images | grep 1_run
  • 使用 yum clean 清除缓存
FROM centos:7
RUN yum install -y \
    vim \
    wget && \
    yum clean all
构建完成后,使用 docker images 命令查看镜像大小,我这边构建完成后的大小是 284MB
docker build -t centos:1_run_clean .
docker images | grep run_clean
  • 可以看出,都是 yum 操作,写两次 RUN一次 RUN,镜像大小可以相差 174MB,如果一个 RUN 加上 yum clean all 清除 yum 缓存,镜像大小可以相差 307MB

COPY

复制文件到镜像内

语法格式:

COPY [--chown=<用户名>:<组名>] <源路径> <目标路径>

  • --chown参数只适用于 Linux 镜像,不适用于 Windows 镜像

    • --chown 可以写 用户名 或者 uid组名 或者 gid
  • COPY 指令在 Dockerfile 内支持通配符,使用 go filepath 规则
  • COPY --from <阶段名称> 可以从多构建环境中的其他镜像内复制文件
  • COPY --from <镜像> 可以从任何镜像内复制文件

    • 不管是那种方式,源路径文件必须要存在
  • COPY 指令源路径如果是宿主机上的文件,源文件的一级目录必须要和 Dockerfile 文件处于同级目录
  • COPY 指令的目标路径如果不是绝对路径,那目标路径将会以WORKDIR路径为相对路径
COPY xxx.sh /usr/local/src/
# 从其他镜像复制文件
COPY --from=quay.io/coreos/etcd:v3.3.9 /usr/local/bin/etcd /usr/local/bin/
从其他阶段复制文件(多阶段构建)
FROM centos:7 as build
# <此处省略构建过程>

FROM centos:7
COPY --from=build /build/build_server /build

ADD

复制文件到镜像内

语法格式:

ADD [--chown=<用户名>:<组名>] <源路径> <目标路径>

  • --chown参数只适用于 Linux 镜像,不适用于 Windows 镜像

    • --chown 可以写 用户名 或者 uid组名 或者 gid
  • ADD 指令在 Dockerfile 内支持通配符,使用 go filepath 规则
  • ADD 指令相比较 COPY 指令,多了两个功能

    • 如果源文件是压缩包类型的文件

      • 需要解压,使用 ADD 指令会自动解压,无需使用 RUN 指令执行解压操作
      • 不需要解压,使用 COPY 参数
    • 如果源文件是网络类型文件

      • 直接使用 ADD 参数,可以从网络下载源文件,如果是压缩包类型,也会自动解压

        • ADD 指令不支持身份验证,网络源文件如果需要身份验证,还是需要使用 RUN wget 或者 RUN curl
        • 不推荐使用 ADD 将网络源文件放到镜像内,会多增加一层解压操作,导致镜像会变大,推荐使用 RUN wget xxx && tar xf xxx && rm -f xxx 可以减少层数以及镜像的大小
# 添加所有以"hom"开头的文件
ADD hom* /mydir/
# 添加 "test.tar.gz" 到 WORKDIR/relativeDir/
ADD test.tar.gz relativeDir/

EXPOSE

容器启动时监听的端口(默认 tcp 协议),可以指定为 udp 协议

语法格式:

EXPOSE <端口>/<协议>

  • 仅仅只是申明,仍然需要 docker run -p 来映射端口,docker run -p <宿主机端口>:<容器内端口>/<协议> 可以覆盖容器内申明的协议
EXPOSE 80/tcp
EXPOSE 80/udp

CMD 和 ENTRYPOINT

容器启动时需要执行的命令

ENTRYPOINT 语法格式:

ENTRYPOINT ["<可执行文件>","<参数1>","<参数2>"]

ENTRYPOINT ["<shell命令>","<参数1>","<参数2>"]

CMD 语法格式:

CMD ["<可执行文件>","<参数1>","<参数2>"]

CMD ["<shell命令>","<参数1>","<参数2>"]

CMD ["<参数1>","<参数2>"] # 只有设置了 ENTRYPOINT 指令才可以使用这种方式

  • CMDENTRYPOINT 之间协作的规则

    • 至少指定一个 CMDENTRYPOINT 指令

      • 多条 CMDENTRYPOINT 都只有最后一条生效
    • 最好是将 CMD 作为 ENTRYPOINT 的参数来使用

      • 因为 docker run 命令带入的启动参数会覆盖 CMD ,而不会覆盖 ENTRYPOINT
  • CMDRUN 是有区别的

    • CMD 是容器运行的时候要执行的命令
    • RUN 是容器构建的时候要执行的命令

全剧终

  • 其实 Dockerfile 里面还有其他指令,比如 VOLUMEONBUILDSTOPSIGNALHEALTHCHECKSHELL [看官方介绍,应该是对与 windows 的 powershell 比较友好],感兴趣的可以去官方看资料(留点头发)
  • 在官方文档,有提及到 Dockerfile cache 机制.dockerignore 文件 等一些功能,后期有机会在学习(留点头发)

目录
相关文章
|
6月前
|
关系型数据库 MySQL Java
Docker Compose详细教程(从入门到放弃)
Docker Compose详细教程(从入门到放弃)
1282 0
|
6月前
|
缓存 Shell Go
dockerfile笔记
dockerfile笔记
70 0
|
6月前
|
缓存 Shell Docker
Docker Review - dockerfile 入门篇
Docker Review - dockerfile 入门篇
77 0
|
Kubernetes API Docker
Docker入门之开篇
相信很多人和我开始时一样,听过Docker,听过Kubernetes,听过容器也听过集群,貌似很多高大上的技术都耳熟能详,但自己其实却并不了解,甚至不知道他们是什么?能干什么? 最初,我以为Docker以及k8s等技术只能用在大数据以及云等的场景,甚至学习上也需要专门的环境,但是,我发现我错了,而且错的很彻底,不知道你是否也这么认为过,觉得这些技术在平时的开发中根本用不到。如果是这样的,那我现在就告诉你,其实并不是这样的,下面就跟我一起来了解一下Docker吧。
Docker入门之开篇
|
运维 Java 应用服务中间件
Docker——Dockerfile的理解 & 案例实操
Docker——Dockerfile的理解 & 案例实操
Docker——Dockerfile的理解 & 案例实操
|
NoSQL Linux Redis
docker快速入门(三)---- DockerFile相关案例
docker快速入门(三)---- DockerFile相关案例
98 0
|
Docker 容器
Dockerfile笔记
照着写就行
111 0
Dockerfile笔记
|
Ubuntu 开发工具 Docker
Dockerfile使用介绍(入门教程)
Dockerfile 是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可。
339 0
Dockerfile使用介绍(入门教程)
|
缓存 网络协议 Ubuntu
〖Docker指南⑤〗学习Dockerfile,看这一篇就够了
Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本。
872 1
〖Docker指南⑤〗学习Dockerfile,看这一篇就够了
|
负载均衡 监控 网络协议
Docker-Compose 从入门到放弃
Docker-Compose 从入门到放弃
872 0