DockerFile详解
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明
1. docker 语法与构建
-
由一行行命令语句组成
-
执行是从上到下顺序执行
-
并且支持已 # 开头的注释行
-
该指令不区分大小写。然而,惯例是让他们 大写,以便更轻松地将它们与参数区分开来。
-
格式
INSTRUCTION arguments
-
关于空格
一般来说指令前面的空格将被忽略,但是指令参数中的空格不会
# 下面的是等效的 RUN echo hello RUN echo world RUN echo hello RUN echo world # 下面参数中空格不会被忽略 RUN echo "\ hello\ world"
1.1. 命令大全
指令 | 说明 |
---|---|
ADD | 添加本地或远程文件和目录。 |
ARG | 使用构建时变量。 |
CMD | 指定默认命令。 |
COPY | 复制文件和目录。 |
ENTRYPOINT | 指定默认可执行文件。 |
ENV | 设置环境变量。 |
EXPOSE | 描述应用程序正在侦听的端口。 |
FROM | 从基础映像创建新的生成阶段。 |
HEALTHCHECK | 在启动时检查容器的运行状况。 |
LABEL | 向图像添加元数据。 |
MAINTAINER | 指定图像的作者。 |
ONBUILD | 指定在生成中使用映像的说明。 |
RUN | 执行构建命令。 |
SHELL | 设置映像的默认 shell。 |
STOPSIGNAL | 指定退出容器的系统调用信号。 |
USER | 设置用户和组 ID。 |
VOLUME | 创建卷装载。 |
WORKDIR | 更改工作目录。 |
1.2. build
docker buildx build [OPTIONS] PATH | URL | -
参数 / 参数缩写 | 描述 |
---|---|
--add-host | 添加自定义的主机到 IP 映射(格式:"host:ip") |
--allow | 允许额外的特权授权(例如:"network.host", "security.insecure") |
--annotation | 向镜像添加注解 |
--attest | 证明参数(格式:"type=sbom,generator=image") |
--build-arg | 设置构建时变量 |
--build-context | 额外的构建上下文(例如:name=path) |
--builder | 覆盖配置的构建实例(默认 "default") |
--cache-from | 外部缓存源(例如:"user/app:cache", "type=local,src=path/to/dir") |
--cache-to | 缓存导出目的地(例如:"user/app:cache", "type=local,dest=path/to/dir") |
--call | 设置评估构建的方法("check", "outline", "targets")(默认 "build") |
--cgroup-parent | 设置 "RUN" 指令期间的父 cgroup |
--check | 简写为 "--call=check" |
-f, --file | Dockerfile 的名称(默认:"PATH/Dockerfile") |
--iidfile | 将镜像 ID 写入文件 |
--label | 为镜像设置元数据 |
--load | 简写为 "--output=type=docker" |
--metadata-file | 将构建结果元数据写入文件 |
--network | 设置 "RUN" 指令期间的网络模式(默认 "default") |
--no-cache | 构建镜像时不使用缓存 |
--no-cache-filter | 指定不缓存的阶段 |
-o, --output | 输出目的地(格式:"type=local,dest=path") |
--platform | 设置构建的目标平台 |
--progress | 设置进度输出类型("auto", "plain", "tty", "rawjson") |
--provenance | 简写为 "--attest=type=provenance" |
--pull | 始终尝试拉取所有引用的镜像 |
--push | 简写为 "--output=type=registry" |
-q, --quiet | 抑制构建输出并在成功时打印镜像 ID |
--sbom | 简写为 "--attest=type=sbom" |
--secret | 暴露给构建的秘密(格式:"id=mysecret[,src=/local/secret]") |
--shm-size | 构建容器的共享内存大小 |
--ssh | 暴露给构建的 SSH 代理套接字或密钥(格式:"default or < id >[=< socket >、< key > [,< key >]]") |
-t, --tag | 名称和可选的标签(格式:"name:tag") |
--target | 设置要构建的目标构建阶段 |
--ulimit | Ulimit 选项(默认 []) |
2. 指令说明
2.1 FORM
有以下3中形式
-
FROM [--platform=< platform >] < image > [AS < name >]
-
FROM [--platform=< platform >] < image >[:< tag >] [AS < name >]
-
FROM [--platform=< platform >] < image >[@< digest >] [AS < name >]
参数说明:
- --platform: **这是一个可选参数,用于指定要构建的镜像的平台。**这可以是操作系统和架构的组合,例如 linux/amd64 或 windows/amd64。如果未指定,Docker 将使用构建上下文的平台。
- image: 这是基础镜像的名称,可以是 Docker Hub 上的镜像名称,也可以是本地镜像的名称。此外,它还可以是一个完整的 URL 指向一个私有仓库的镜像。
- AS < name >: 这也是一个可选参数,用于给基础镜像指定一个别名。这个别名可以在 Dockerfile 中的其他指令中使用,以引用这个特定的镜像。
示例:
# 1. 使用官方的 Ubuntu 镜像作为基础镜像
FROM ubuntu:latest
# 2. 指定构建目标平台为 linux/amd64
FROM --platform=linux/amd64 ubuntu:20.04
# 3. 使用官方的 Python 镜像,并给它一个别名 "python"
FROM python:3.8-slim AS python
2.2. ENV
2.1.1. 设置环境变量
- 设置单个环境变量
ENV < key > = < value >
- 设置多个环境变量
ENV < key1 > = < value1 > < key2 > < value2 > ...
- 设置多个值的列表
ENV < key > = < value1 value2 value3 ... >
2.1.2. 使用环境变量
- 使用环境变量
${KEY}
2.1.3. 继承环境变量
使用 ENV 指令来覆盖基础镜像中已经设置的环境变量,新设置的值将在构建的镜像中生效
2.1.4. 示例
# 设置单个环境变量
ENV MY_VAR="my_value"
# 设置多个环境变量
ENV MY_VAR1="value1" MY_VAR2="value2"
# 设置多个值的列表
ENV MY_VAR="value1 value2 value3"
#使用环境变量的值
RUN echo ${MY_VAR}
2.3. ADD
ADD 指令用于将文件、目录、远程文件URL 或者 tar 压缩文件从一个或多个源复制到容器的文件系统。这个指令非常有用,因为它不仅可以复制本地文件,还可以从远程源拉取文件。
-
将本地文件或目录复制到容器中的指定路径
ADD < src > < dest >
-
从远程 URL 复制文件到容器中的指定路径
ADD < remote > < dest >
2.3.1. 示例
# 将当前目录下的 config.txt 文件复制到容器的 /etc/config 目录
ADD config.txt /etc/config/
# 将当前目录下的 data 目录复制到容器的 /data 目录
ADD data/ /data/
# 从指定的 URL 下载文件并复制到容器的 /tmp 目录
ADD http://example.com/remotefile.tar.gz /tmp/
# 将本地的 tar 文件自动解压到容器的 /app 目录
ADD archive.tar.gz /app/
2.4. WORKDIR
指令用于设置容器内的当前工作目录,相当于 shell 中的 cd 命令。这个指令对于容器内的命令非常重要,因为它定义了执行命令时的当前目录。
注:
- 如果指定的目录不存在,WORKDIR 指令会创建所有需要的中间目录。
- 在 Dockerfile 中,每个 WORKDIR 指令都会影响后续指令的执行环境。
示例
# 1. 设置工作目录
WORKDIR /app
# 2. 使用相对路径
WORKDIR /app
WORKDIR src
在设置了 /app 为工作目录之后,第二个 WORKDIR 指令将工作目录更改为 /app/src。
# 3. 与其他指令结合使用
WORKDIR /app
COPY . /app
RUN make /app
在这个例子中,首先设置了工作目录为 /app,然后将当前目录(构建上下文中的)中的所有文件复制到容器的 /app 目录中,最后在 /app 目录下执行 make 命令。
2.5. RUN
它用于执行任何所需的命令行命令,并将其结果(如安装的软件包或文件)作为镜像的一部分保存下来。RUN 指令可以视为构建过程中的一个独立步骤,每个步骤都会创建一个新的镜像层。
语法
-
shell 形式:RUN <命令>
使用 shell 语法执行命令。默认情况下,shell 形式使用 /bin/sh -c 作为 shell。
-
exec 形式:RUN ["可执行文件", "参数1", "参数2", ...]
明确指定命令和参数,通常用于需要明确区分命令和参数的情况,或者使用像 /bin/bash 这样的特定 shell。
注意事项:
-
RUN 指令执行的命令会在新的层上执行,这意味着每个 RUN 指令都会创建一个新的镜像层,可能导致镜像体积变大。因此,应尽量合并多个命令到单个 RUN 指令中,以减少层数。
-
使用 && 连接多个命令时,它们会在同一层上顺序执行。例如,RUN apt-get update && apt-get install -y curl 只会创建一个层。
-
如果需要执行的命令较长或包含多个行,可以使用反引号 ` 或者 $() 来包围命令,例如:
RUN apt-get update && \ apt-get install -y \ curl \ git
-
使用 RUN 指令时,应避免使用交互式命令,因为 Docker 容器默认不应该运行交互式进程。
2.5.1. 示例
# 安装软件包
RUN apt-get update && apt-get install -y curl
# 执行简单的命令:
RUN echo 'Hello, Docker!'
# 使用 exec 形式
RUN ["/bin/bash", "-c", "echo 'Using exec form'"]
2.6. EXPOSE
暴漏端口
示例
# 1.暴露单个端口:
EXPOSE 80
# 2. 暴露多个端口:
EXPOSE 80 443
# 3. 使用协议指定端口:
EXPOSE tcp://80 udp://53
#4. 暴露端口范围:
EXPOSE 3000-3005
2.7 CMD
用于指定容器启动时默认执行的命令。如果 Dockerfile 中没有 CMD 指令,Docker 容器将不知道执行什么命令,可能会返回错误或简单地退出。
- CMD ["executable", "param1", "param2"...]
- CMD command param1 param2 ...
# 执行简单的命令:
CMD ["echo", "Hello, World!"]
# 启动一个服务:
CMD ["apache2", "-D", "FOREGROUND"]
# 使用 shell 形式:
CMD echo "The container is running"
# 传递环境变量:
CMD ["sh", "-c", "echo $MY_ENV_VAR"]
# 结合其他指令:
FROM ubuntu
RUN apt-get update && apt-get install -y nginx
CMD ["nginx", "-g", "daemon off;"]
3. 示例创建一个tomcat镜像
准备:
- 下载apache-tomcat-9.0.91.tar.gz包放在Dockerfile包下
- 下载jdk-11.0.2_linux-x64_bin.tar.gz包放在Dockerfile包下
# 使用debian做基础镜像
FROM debian
# 设置作者名与邮箱
LABEL maintainer="jelly<xxxxx@qq.com>"
# 添加Tomcat
ADD ./apache-tomcat-9.0.91.tar.gz /usr/local/tomcat
# 添加javajdk
ADD ./jdk-11.0.2_linux-x64_bin.tar.gz /usr/local/jdk_11
# 配置java环境变量
ENV JAVA_HOME=/usr/local/jdk_11/jdk-11.0.2
ENV CLASSPATH=${JAVA_HOME}lib/dt.jar:${JAVA_HOME}lib/tools.jar
# 设置一个Tomcat环境变量
ENV CAT_HOME=/usr/local/tomcat/apache-tomcat-9.0.91
ENV PATH=$PATH:${JAVA_HOME}/bin:${CAT_HOME}/lib:${CAT_HOME}/bin
# 设置工作目录
WORKDIR ${CAT_HOME}
# #安装VIM
# RUN apt update && \
# apt-get install vim
#配置端口
EXPOSE 8080
# 启动tomcat
ENTRYPOINT [ "catalina.sh","run" ]
CMD ["/bin/bash"]
开始build
root@jelly-linux:/home/jelly/test docker build . -t mytomcat:1.0
[+] Building 0.2s (9/9) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 776B 0.0s
=> [internal] load metadata for docker.io/library/debian:latest 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/4] FROM docker.io/library/debian:latest 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 104B 0.0s
=> CACHED [2/4] ADD ./apache-tomcat-9.0.91.tar.gz /usr/local/tomcat 0.0s
=> CACHED [3/4] ADD ./jdk-11.0.2_linux-x64_bin.tar.gz /usr/local/jdk_11 0.0s
=> CACHED [4/4] WORKDIR /usr/local/tomcat/apache-tomcat-9.0.91 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:b0936fce56f463ad483551274b514454b987780cccc178a437d0a6bac922645c 0.0s
=> => naming to docker.io/library/mytomcat:1.0
创建容器并启动
docker run -it -d --name mytomcat_test -p 8080:8080 mytomcat:1.0
9dfd35be48d2ecbf5e09715fc9be2b3d4f7470bb7169de36d02bf8e3b99c42d8
3.1 遇到的问题
-
dockerfile文件名称问题
- 名字为Dockerfile(官方默认文件名称)时可以直接 "docker build . -t mytomcat:1.0" 其中文件名称可以忽略
- 文件名称不是官方默认名称时使用-f指定文件名称。例如:文件名称为TomcatDockerfiledocker build -f TomcatDockerfile . -t mytomcat:1.0
-
容器启动后会自动关闭
- dockerfile 文件中有ENTRYPOINT或CMD执行命令发生错误,可以通过docker logs 容器名称/id 来查看日志定位问题
- 一个docker容器同时只能管理一个进程,这个进程退出后,容器也就退出了。例如上述例子中奖ENTRYPOINT [ "catalina.sh","run" ] 改为ENTRYPOINT [ "startup.sh","run" ]就会导致这个镜像所创建的容器启动后会关闭。因为这个容器管理了startup.sh启动进程,当这个教程执行完成,进程关闭,随后容器也关闭。
评论区