写点什么

【深入浅出 Docker 原理及实战】「原理实战体系」零基础 + 全方位带你学习探索 Docker 容器开发实战指南(Dockerfile 使用手册)

作者:洛神灬殇
  • 2024-01-16
    江苏
  • 本文字数:5429 字

    阅读完需:约 18 分钟

【深入浅出Docker原理及实战】「原理实战体系」零基础+全方位带你学习探索Docker容器开发实战指南(Dockerfile使用手册)

Dockerfile

Docker 是一套构建在 Linux 内核之上的高级工具,旨在帮助开发人员和运维人员更轻松地交付应用程序和依赖关系,实现跨系统和跨主机的部署。使用安全且轻量级的容器环境来实现这一目标。容器可以手动创建,也可以通过编写 Dockerfile 自动创建。开发人员和运维人员可以将应用程序及其依赖打包到容器中,实现应用程序的可移植性和环境一致性。


Dockerfile 是一系列命令和参数的脚本,应用于基础镜像,最终创建新镜像。它简化了整个流程,极大简化了部署工作。从 FROM 命令开始,Dockerfile 包含各种方法、命令和参数,最终生成可用于容器创建的新镜像。

Dockerfile 的语法

  • Dockerfile 指令不区分大小写,但强烈建议使用大写来表示指令名称。

  • 每行只能包含一条指令,不要把多个指令写在同一行上,否则这些指令将不会被执行。可以在指令之前使用"#"来添加注释或取消不需要的指令。

  • Dockerfile 指令可分为构建指令和设置指令两大类。构建指令用于构建镜像,设置指令用于配置和设置镜像内部的环境。

Dockerfile 语法示例

Dockerfile 语法由两部分构成,注释和命令+参数


Dockerfile 由一行行命令语句组成,并且支持用“#”开头作为注释,一般的,Dockerfile 分为四部分:基础镜像信息,维护者信息,镜像操作指令和容器启动时执行的指令。

注释的行块

command argument argument ..
复制代码
一个简单的例子:Print "Hello docker!"
RUN echo "Hello docker!"
复制代码

Dockerfile 命令介绍

Dockerfile 有十几条命令可用于构建镜像,接下来我们把 Dockerfile 里面常用的指令介绍一下。




FROM(指定一个基础镜像)

通常情况下,一个有效的 Dockerfile 的第一条指令应该是 FROM,用于指定基础镜像,image 指定的是任何合理存在的 Docker 镜像, 这句是放在最最开头的地方,也是 Dockerfile 的核心!


第一条指令必须为 FROM 指令,并且,如果在同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。

格式

FROM <image name> 或者指定镜像版本 FROM <image name>:<tag>


如果没有指定 tag ,latest 将会被指定为要使用的基础镜像版本,后续的指令将基于这个基础镜像构建你的应用程序或服务,请将 your_base_image:latest 替换为您实际使用的基础镜像的标识符和版本号。这样你就能够基于指定的基础镜像构建你的应用程序或服务。


它会在本地先找找有没有这个镜像,如果没有,它会去 docker 官方的仓库去找优先相关的或最接近的镜像下载到本地。



MAINTAINER(指定维护者的信息)

MAINTAINER 构建指令,告诉别人这个镜像是谁创建的。

格式

MAINTAINER <yourname> 作者名称将被录入到镜像里。



RUN

RUN 命令是 Dockerfile 执行命令的核心部分。它接受命令作为参数并用于创建镜像。将在当前 image 中执行任意合法命令并提交执行结果,命令执行提交后,就会自动执行 Dockerfile 中的下一个指令。

格式

每条指令将在当前镜像基础上执行,并提交为新的镜像。(可以用“\”换行)。


RUN <command> 或 RUN ["", "", ""]RUN <linuxcommand>  <anything linux shell>
复制代码


RUN 指令缓存不会在下个命令执行时自动失效。比如 RUN apt-get dist-upgrade -y 的缓存就可能被用于下一个指令. --no-cache 标志可以被用于强制取消缓存使用。


不像 CMD 命令,RUN 命令用于创建镜像(在之前 commit 的层之上形成新的层)。



ENV(构建指令,用于设置 image 的环境变量)

ENV 命令用于 docker 容器设置环境变量。这些变量以”key=value”的形式存在,并可以在容器内被脚本或者程序调用。这个机制给在容器中运行应用带来了极大的便利。

格式

指定环境变量,会被 RUN 指令使用,并在容器运行时保存。


ENV <key><value>
复制代码
案例

比如你的镜像要执行的程序需要使用到 java 的包,那么你可以使用这个方法,


ENV JAVA_HOME /server/lib/java-sdk
复制代码


ENV 设置的环境变量,可以使用 docker inspect 命令来查看。同时还可以使用 docker run --env <key>=<value>来修改环境变量。



USER(设置指令,设置 container 的用户)

USER 用来切换运行属主身份的。Docker 默认是使用 root,但若不需要,建议切换使用者身分,毕竟 root 权限太大了,使用上有安全的风险。

格式

指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。


USER <system username>USER 751
复制代码



WORKDIR

WORKDIR 用来切换工作目录的,用于设置 CMD 指明的命令的运行目录。为后续的 RUN 、 CMD 、 ENTRYPOINT 指令配置工作目录。(可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径, 则会基于之前命令指定的路径)


Docker 默认的工作目录是/,只有 RUN 能执行 cd 命令切换目录,而且还只作用在当下下的 RUN,也就是说每一个 RUN 都是独立进行的。如果想让其他指令在指定的目录下执行,就得靠 WORKDIR。WORKDIR 动作的目录改变是持久的,不用每个指令前都使用一次 WORKDIR。

格式
WORKDIR /path/to/workdirWORKDIR ~/
复制代码


Docker 默认的工作目录是/,只有 RUN 能执行 cd 命令切换目录,而且还只作用在当下下的 RUN,也就是说每一个 RUN 都是独立进行的。如果想让其他指令在指定的目录下执行,就得靠 WORKDIR。WORKDIR 动作的目录改变是持久的,不用每个指令前都使用一次 WORKDIR。



COPY

复制本地主机的 <src> ( 为 Dockerfile 所在目录的相对路径)到容器中的 <dest> (当使用本地目录为源目录时,推荐使用 COPY)

格式
COPY <src>  <dest>
复制代码


COPY 将文件从路径 <src> 复制添加到容器内部路径 <dest>。


<src> 必须是想对于源文件夹的一个文件或目录,也可以是一个远程的 url,<dest> 是目标容器中的绝对路径。所有的新文件和文件夹都会创建 UID 和 GID 。事实上如果 <src> 是一个远程文件 URL,那么目标文件的权限将会是 600。



ADD(构建指令,将宿主机的文件复制到镜像里)

ADD 命令有两个参数,源和目标。它的基本作用是从源系统的文件系统上复制文件到目标容器的文件系统。如果源是一个 URL,那该 URL 的内容将被下载并复制到容器中。

格式

复制指定的<src>到容器的<dest>中,<src>可以是 Dockerfile 所在的目录的一个相对路径;可以是 URL,也可以是 tar.gz(自动解压)


ADD  <src>  <dest># Usage: ADD [source directory or URL] [destination directory]ADD /my_app_folder /my_app_folder 
复制代码


  • <src> 必须是想对于源文件夹的一个文件或目录,也可以是一个远程的 url。

  • <dest> 是目标容器中的绝对路径。


所有的新文件和文件夹都会创建 UID 和 GID。事实上如果 <src> 是一个远程文件 URL,那么目标文件的权限将会是 600。



VOLUME

VOLUME 命令用于让你的容器访问宿主机上的目录,创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

格式
VOLUME ["/my_files"]
复制代码



EXPOSE(设置指令,设置 container 要映射到宿主机的端口)

EXPOSE 指令指定在 docker 允许时指定的端口进行转发,该参数是对外开放 container 的端口,使外部通过宿主机的 IP 以及容器所映射的端口来访问容器。但是必须保证所映射的端口没有被使用或占用。开放端口必须结合使用才能达到目的。

格式
EXPOSE <port>  [ <port> ...]EXPOSE 8080
复制代码


告诉 Docker 服务端暴露端口,在容器启动时需要通过 -p 做端口映射。Dockerfile 里面我们设置 80 端口


EXPOSE 80
复制代码


在运行的时候,我们要进行映射。


docker run -it -p 80:80 centos7/lynk   //开放80端口映射到容器80端口
复制代码


或指定其他端口进行映射


docker run -it -p 8080:80 centos7/lynk  //开放8080端口映射到容器80端口
复制代码



CMD(设置指令,用户设置 container 启动时候执行的操作)

和 RUN 命令相似,CMD 可以用于执行特定的命令。和 RUN 不同的是,这些命令不是在镜像构建的过程中执行的,而是在用镜像构建容器后被调用。


指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 指令,如果指定了多条指令,则最后一条执行。(会被启动时指定的命令覆盖)

格式

CMD ["","",""]


Dockerfile.中只能有一个 CMD 指令。 如果你指定了多个,那么最后个 CMD 指令是生效的。CMD 指令的主要作用是提供默认的执行容器。这些默认值可以包括可执行文件,也可以省略可执行文件。当你使用 shell 或 exec 格式时, CMD 会自动执行这个命令。


Usage 1: CMD application "argument", "argument", ..CMD "echo" "Hello docker!"
复制代码



ONBUILD(设置指令,在子镜像中执行)

ONBUILD 指定的命令在构建的时候并不执行,而是在他的子镜像中执行。其实就是让指令延迟執行,延迟到下一个使用 FROM 的 Dockerfile 在建立 image 时执行,只限延迟一次。

格式
ONBUILD <下一个要运行的Dockerfile关键字>
复制代码


ONBUILD 的使用情景是在建立镜像时取得最新的源码 (搭配 RUN) 与限定系统框架。配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令



ARG

ARG 是自 Docker 1.9 版本引入的指令。它用于定义在构建镜像过程中使用的变量,在镜像构建完成后,这些变量就会失效。换句话说,ARG 定义的变量只在构建镜像时有效,不会在运行镜像时存在。


在 Dockerfile 中,可以使用 ARG 指令来定义一个或多个变量,然后在后续的指令中引用这些变量。这使得您可以在构建过程中传递参数或配置选项,方便地定制化镜像构建过程。

格式
ARG variable_name[=default_value]
复制代码


在构建镜像时,可以通过在 docker build 命令中使用 --build-arg 选项来传递具体的值给 ARG 变量。


其中,variable_name 是变量的名称,可以使用该变量在后续的指令中引用。default_value 是可选的默认值,表示如果在构建过程中没有传递具体的值,将使用默认值。


docker build --build-arg app_version=2.0 -t my_image .
复制代码


# 定义一个名为 "app_version" 的变量,设置默认值为 "1.0"ARG app_version=1.0# 在后续的指令中使用该变量RUN echo "Application version: $app_version"
复制代码


这将使用传递的值覆盖默认值,使得在构建过程中可以动态地定制化变量。



LABEL

LABEL 指令用于给 Docker 镜像添加元数据标签,用于描述镜像相关的信息。这些标签可以包括作者、版本、描述、维护者、构建日期等。


LABEL 指令通常位于 Dockerfile 的顶部或镜像构建过程的初始化阶段,用于提供关于镜像的信息,帮助用户更好地了解和管理镜像。这些标签可以在镜像仓库中查看,也可以通过docker inspect命令查看。


以下是一个 LABEL 指令的示例:


# 设置镜像的元数据标签LABEL maintainer="John Doe <john.doe@example.com>"LABEL version="1.0"LABEL description="This is a sample Docker image"
复制代码


通过使用 LABEL 指令,您可以为镜像指定元数据,提供关键的信息,使镜像更易于管理和使用。这对于团队协作、镜像版本控制和文档化都非常有用。



ENTRYPOINT

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。(每个 Dockerfile 中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效)

格式
ENTRYPOINT ["","",""]
复制代码


是指定 Docker image 运行成 instance (也就是 Docker container) 时,要执行的命令或者文件。

Dockerfile 文件案例

个比较简单的 Dockerfile,以下是对每个指令的分析:


FROM docker.io/centosMAINTAINER The CentOS Test Images - testRUN mkdir -p /usr/appRUN lsRUN pwdCOPY /jdk /usr/app/jdk/ADD tomcat/ /usr/app/tomcat/ADD hadoop/ /usr/app/hadoop/ENV JAVA_HOME /usr/app/jdkENV PATH $JAVA_HOME/bin:$PATH#ADD /soft/jdk /#ADD /soft/tomcat /#ADD /soft/hadoop /# Volumes for systemd# VOLUME ["/run", "/tmp"]# Environment for systemd# ENV container=docker# For systemd usage this changes to /usr/sbin/init# Keeping it as /bin/bash for compatibility with previous#CMD ["/bin/bash"]
复制代码


  1. FROM docker.io/centos

  2. 基础镜像选择为 docker.io/centos,即从 Docker Hub 上的 CentOS 镜像开始构建。

  3. MAINTAINER The CentOS Test Images - test

  4. 设置镜像的维护者信息为 "The CentOS Test Images - test"。请注意,MAINTAINER 指令已经被标记为废弃,推荐使用 LABEL 指令代替。

  5. RUN mkdir -p /usr/app

  6. 在镜像中创建一个目录 /usr/app

  7. RUN ls

  8. 在构建过程中执行 ls 命令,用于列出当前目录的内容。这可能是为了在构建过程中验证镜像的构建环境。

  9. RUN pwd

  10. 在构建过程中执行 pwd 命令,用于打印当前工作目录的路径。同样,这可能是为了在构建过程中验证镜像的构建环境。

  11. COPY /jdk /usr/app/jdk/

  12. 将主机上项目根目录下的 jdk 文件夹复制到镜像中的 /usr/app/jdk/ 目录。

  13. ADD tomcat/ /usr/app/tomcat/

  14. 将主机上项目根目录下的 tomcat 文件夹复制到镜像中的 /usr/app/tomcat/ 目录。与 COPY 指令类似,但 ADD 还支持从 URL 中复制文件。

  15. ADD hadoop/ /usr/app/hadoop/

  16. 将主机上项目根目录下的 hadoop 文件夹复制到镜像中的 /usr/app/hadoop/ 目录。

  17. ENV JAVA_HOME /usr/app/jdk

  18. 设置环境变量 JAVA_HOME/usr/app/jdk,指定了 Java 的安装路径。

  19. ENV PATH $JAVA_HOME/bin:$PATH

  20. $JAVA_HOME/bin 加入到 $PATH 环境变量中,使得可以直接使用 java 命令。

  21. 注释这个 Dockerfile 的作用是构建一个基于 CentOS 的镜像,并在镜像中安装了 JDK、Tomcat 和 Hadoop。



总结介绍

Dockerfile 是一种类似于 Linux 下 shell 脚本的文本文件,用于定义 Docker 镜像。通过编写 Dockerfile 并执行一条命令,您可以简单地构建和配置镜像。使用 Dockerfile,您可以自定义服务器结构和配置,轻松构建完整的环境。Dockerfile 的设计目标是使镜像配置变得简单和便捷,提供可重复和可移植的镜像构建过程。


它具有可靠性和简单性,使应用程序和服务的分发变得更加简单。使用 Dockerfile 可以轻松管理和配置镜像,让您专注于其他重要任务。请注意,Dockerfile 只能被 Docker 解释器识别和使用,不能被非 Docker 环境所使用。

发布于: 刚刚阅读数: 5
用户头像

洛神灬殇

关注

🏆 InfoQ写作平台-签约作者 🏆 2020-03-25 加入

👑 后端技术架构师,前优酷资深工程师 📕 个人著作《深入浅出Java虚拟机—JVM原理与实战》 💻 10年开发经验,参与过多个大型互联网项目,定期分享技术干货和项目经验

评论

发布
暂无评论
【深入浅出Docker原理及实战】「原理实战体系」零基础+全方位带你学习探索Docker容器开发实战指南(Dockerfile使用手册)_Docker_洛神灬殇_InfoQ写作社区