一、什么是Dockerfile
Dockerfile 是一个用于自动化构建 Docker 镜像的文本文件,其中包含了从一个基础镜像开始,到最终形成所需定制镜像的所有指令集。这个文件中的每一条指令都对应着构建镜像过程中的一个步骤或一层,指导 Docker 如何安装软件、设置环境变量、复制文件、开启端口以及配置运行时的各种参数等。说白了就是可以自定义自己的镜像。
二、使用 Dockerfile 定制镜像
1.下面以定制一个 ubuntu 镜像
在根目录下新建一个Dockerfile目录(名字随意),切换到该目录然后编辑一个名为Dockerfile(必须是这个名字)的文件,编写如下代码:
FROM ubuntu:latest
WORKDIR /app
COPY . /app
RUN apt-get update && apt-get install -y python3
ENV MY_VAR="value"
EXPOSE 8080
CMD ["python3", "app.py"]
过程如下:
2.指令解析
dockerfile指令大全
Dockerfile 指令 |
说明 |
---|---|
FROM |
指定基础镜像,用于后续的指令构建。 |
MAINTAINER |
指定Dockerfile的作者/维护者。 |
LABEL |
添加镜像的元数据,使用键值对的形式。 |
RUN |
在构建过程中在镜像中执行命令。 |
CMD |
指定容器创建时的默认命令。(可以被覆盖) |
ENTRYPOINT |
设置容器创建时的主要命令。(不可被覆盖) |
EXPOSE |
声明容器运行时监听的特定网络端口。 |
ENV |
在容器内部设置环境变量。 |
ADD |
将文件、目录或远程URL复制到镜像中。 |
COPY |
将文件或目录复制到镜像中。 |
VOLUME |
为容器创建挂载点或声明卷。 |
WORKDIR |
设置后续指令的工作目录。 |
USER |
指定后续指令的用户上下文。 |
ARG |
定义在构建过程中传递给构建器的变量,可使用 "docker build" 命令设置。 |
ONBUILD |
当该镜像被用作另一个构建过程的基础时,添加触发器。 |
STOPSIGNAL |
设置发送给容器以退出的系统调用信号。 |
HEALTHCHECK |
定义周期性检查容器健康状态的命令。 |
SHELL |
覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。 |
以下是一
FROM
FROM是Dockerfile中的第一个指令,也是一个必须的指令。它用于指定构建新镜像时所基于的基础镜像。基础镜像可以是官方的Docker镜像,也可以是其他人或组织发布在Docker Hub或其他容器注册表中的镜像。
格式:
FROM <image>
FROM <image>:<tag>
示例:
FROM nginx:1.25.1-alpine 默认不写使用latest版本的基础镜像
MAINTAINER
用于指定镜像的维护者信息。它的作用和用法与LABEL指令类似,用于为镜像添加作者、维护者、联系方式等元数据。
格式:
MAINTAINER <name>
示例:
MAINTAINER PENG
MAINTAINER xxx@qq.com
LABEL
用于向构建的镜像添加元数据(metadata)。这些元数据以键值对的形式存在,可以用来标注关于镜像的各种信息,比如版本号、作者、创建日期、描述等。元数据对于镜像的管理和追踪非常有用,它们可以帮助用户更好地理解和分类不同的镜像。
格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
LABEL version="1.0" \
author="John Doe" \
description="This image contains a web server with application XYZ" \
release-date="2024-05-15"
RUN
RUN是Dockerfile中的一个重要指令,用于在镜像中执行命令,以便在构建过程中安装软件包、配置环境、生成文件等。RUN指令执行的命令会在新的镜像层中运行,并且在后续构建中,只有在该层之前的内容发生变化时才会重新运行,利用了Docker的缓存机制,提高了构建速度。RUN指令可以接受多种格式的命令执行方式:Shell 格式:默认情况下,RUN指令使用Shell来执行命令。
shell格式:
RUN apt-get update && apt-get install -y python3
Exec格式:
RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "python3"]
注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:
FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
以上执行会创建 3 层镜像。可简化为以下格式:
FROM centos
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz
CMD
CMD是Dockerfile中的一个重要指令,用于定义容器启动时默认要执行的命令。一个Dockerfile中只能包含一个CMD指令,如果有多个,则只有最后一个CMD指令会生效。CMD指令有两种格式:Shell 格式:使用Shell格式时,命令会在Shell中执行。
shell格式:
CMD python app.py
Exec格式: 使用数组格式时,命令不会在Shell中执行,而是直接在容器中执行。
CMD ["python", "app.py"]
使用CMD指令可以为镜像定义一个默认的启动命令,当使用docker run命令启动容器时,如果没有指定其他命令,就会执行CMD中定义的命令。这使得在创建容器时无需手动指定要运行的命令,从而使容器的使用更加简便。如果在docker run命令中指定了其他命令,则会覆盖CMD指令中的默认命令。例如,如果在启动容器时执行以下命令,就会覆盖CMD中定义的默认启动命令:
docker run my_image python script.py
在上面的例子中,容器会运行python script.py命令,而不是默认的CMD指令中定义的命令。
ENTRYPOINT
ENTRYPOINT是Dockerfile中的一个重要指令,用于配置容器启动时的默认执行命令。它类似于CMD指令,但有一些关键的区别。ENTRYPOINT指令的格式与CMD指令类似,可以使用Shell格式或数组格式,但在使用时,需要注意以下几点:
- ENTRYPOINT指令的命令会在容器启动时始终执行,无论在docker run命令中是否指定了其他命令。它不会被覆盖,而是作为容器的主要执行命令。
- 如果在docker run命令中指定了其他命令,这些命令将作为ENTRYPOINT指令的参数进行传递。也就是说,ENTRYPOINT指令中的命令将成为执行时的前缀。
下面是一个使用ENTRYPOINT指令的简单示例:
FROM ubuntu:latest
ENTRYPOINT ["echo", "Hello"]
如果我们构建该镜像并运行容器,不提供其他参数,那么容器启动后将输出 "Hello":
$ docker build -t my_image .
$ docker run my_image
Hello
如果我们在运行容器时提供了其他参数,那么这些参数将作为ENTRYPOINT指令中命令的参数:
$ docker run my_image "World"
Hello World
注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
EXPOSE
仅仅只是声明端口。
作用:
- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
- 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
格式:
EXPOSE <端口1> [<端口2>...]
ENV
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
示例:
FROM ubuntu:latest
# 设置环境变量
ENV MY_NAME John Doe
ENV APP_HOME /app
# 创建目录并设置工作目录
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
# 复制应用程序到镜像中
COPY . .
# 在运行时输出环境变量
CMD echo "Hello, $MY_NAME"
在上面的例子中,我们使用了两个ENV指令来设置两个环境变量:MY_NAME和APP_HOME。在镜像构建过程中,这些环境变量会被设置为指定的值。然后,在容器启动时,CMD指令中的命令将使用$MY_NAME环境变量的值输出问候语。在运行容器时,你可以通过docker run命令的-e选项来覆盖环境变量的值。例如:
$ docker run -e MY_NAME="Alice" my_image
上述命令将覆盖默认的MY_NAME环境变量值,容器将输出 "Hello, Alice"。
ARG
构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。
格式:
ARG <参数名>[=<默认值>]
示例:
FROM ubuntu:latest
# 定义构建参数
ARG MY_ENV=production
# 使用构建参数设置环境变量
ENV ENVIRONMENT=$MY_ENV
在上面的例子中,我们通过ARG指令定义了一个名为MY_ENV的构建参数,并设置了其默认值为production。然后,在FROM指令之后,我们使用构建参数设置了一个名为ENVIRONMENT的环境变量。在构建镜像时,可以通过--build-arg选项来指定构建参数的值,例如:
$ docker build --build-arg MY_ENV=development -t my_image .
COPY
复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
格式:
COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
示例:
COPY hom* /mydir/
[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。
<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。
ADD
ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:
- ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
- ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
VOLUME
VOLUME于声明容器中的挂载点(数据卷)。数据卷是一个特殊的目录,它可以绕过联合文件系统(UnionFS),并在容器间共享数据。VOLUME指令的格式是VOLUME ["/path/to/directory"],其中/path/to/directory是挂载点的路径。可以在一个Dockerfile中使用多个VOLUME指令来声明多个挂载点。
格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
示例:
FROM ubuntu:latest
# 声明两个挂载点
VOLUME ["/app/data", "/app/logs"]
在上面的例子中,我们声明了两个挂载点/app/data和/app/logs,这样在运行容器时,可以将这两个挂载点映射到主机的文件系统中,以实现数据持久化和共享。在运行容器时,可以使用-v选项或--mount选项来将主机的目录或数据卷映射到容器的挂载点。例如:
$ docker run -v /host/data:/app/data -v /host/logs:/app/logs my_image
上述命令将主机的/host/data和/host/logs目录分别映射到容器中的/app/data和/app/logs挂载点,实现了主机和容器之间的数据共享。
WORKDIR
WORKDIR用于设置工作目录,也称为当前工作目录。在容器启动时,进程的当前工作目录将被设置为WORKDIR指令所指定的目录。我们使用WORKDIR指令将工作目录设置为/app,
FROM ubuntu:latest
# 设置工作目录
WORKDIR /app
# 容器启动时运行的命令
CMD ["python", "app.py"]
当容器启动时,进程的当前工作目录将自动设置为/app,这样在执行CMD指令时,不需要使用绝对路径来运行python app.py。
USER
USER用于指定在容器中运行镜像时要使用的非特权用户。默认情况下,Docker容器在启动时以root用户身份运行,这意味着容器内的进程具有最高权限。然而,为了加强安全性,避免潜在的安全风险,最好以非特权用户的身份运行容器中的应用程序。以下是一个简单的示例:
FROM ubuntu:latest
# 创建一个新用户并切换到该用户
RUN useradd -ms /bin/bash myuser
USER myuser
# 设置工作目录
WORKDIR /app
# 复制应用程序到工作目录
COPY . .
# 设置环境变量
ENV APP_ENV production
# 容器启动时运行的命令
CMD ["python", "app.py"]
在上面的例子中,我们使用useradd命令创建了一个名为myuser的新用户,并使用-ms /bin/bash选项指定了创建用户时使用的shell。然后,通过USER指令切换到了myuser用户。这样,在容器运行时,进程将以myuser用户的身份运行,而不是以root用户身份。
三、构建镜像
以下是一个简单的Dockerfile示例:
# 使用官方的Ubuntu 20.04镜像作为基础镜像
FROM ubuntu:20.04
# 设置工作目录
WORKDIR /app
# 复制应用程序到镜像中
COPY . .
# 安装应用程序所需的依赖
RUN apt-get update && apt-get install -y python3
# 设置环境变量
ENV APP_ENV production
# 容器启动时运行的命令
CMD ["python3", "app.py"]
构建镜像:docker build命令:
docker build -t my_image .
-t: 是指定镜像名
docker build命令会根据Dockerfile的内容,逐条执行其中的指令,并创建一个新的镜像。构建过程会根据每条指令的内容,生成新的镜像层。每条指令都会在上一层的基础上进行修改,最终构建出一个完整的镜像。基于参数构建镜像。
构建完后用命令docker images查看刚刚构建的镜像: