Dockerfile的一些用法和最佳实践记录
记录一些在使用Dockerfile过程中遇到的用法和最佳实践。
COPY 和 ADD 命令的区别
COPY
和 ADD
都是 Dockerfile 的指令,都可以将文件或目录从主机复制到 docker 镜像中。但是,它们之间存在一些区别:
功能:
COPY
指令将从构建上下文中复制新的文件或目录,并将它们添加到镜像的文件系统中指定的路径。ADD
指令也有类似的功能,但是它还有两个额外的功能。首先,如果源文件是一个 tar 文件,ADD
将自动解压这个 tar 文件。其次,ADD
指令支持使用 URL 作为源文件,会自动下载这个 URL 指向的文件。使用建议: 由于
ADD
指令具有更多的功能,所以它的行为也更复杂,更不可预测。 Docker 官方建议,只有在你确实需要ADD
提供的额外功能时才使用它,否则默认使用COPY
指令。这样可以使 Dockerfile 更易于理解,更具可维护性。示例:
- 使用
COPY
指令:
1
COPY test.txt /data/
将当前目录下的 test.txt 文件复制到镜像的 /data/ 目录下。
- 使用
ADD
指令:
1
ADD https://example.com/test.txt /data/
将远程的 test.txt 文件下载并复制到镜像的 /data/ 目录下。
- 使用
多阶段构建
Dockerfile的多阶段编译是Docker 17.05版本以后引入的一种新特性,它可以让你在一个Dockerfile中使用多个FROM指令。每个FROM指令可以使用不同的基础镜像,并且开始一个新的构建阶段。每个阶段是完全独立的,可以被认为是一个临时的中间镜像。
多阶段构建的优点主要有两个:一是可以避免最终生产的Docker镜像变得过大;二是可以避免在构建过程中在镜像中留下不必要的工具和依赖。
以下是一个使用多阶段构建的例子,它首先使用golang
镜像来编译Go应用程序,然后在新的阶段使用基于alpine的较小镜像来运行该应用程序:
1 | # Stage 1: Build the Go binary |
在这个例子中,首先定义了一个名为builder
的构建阶段,它从golang:1.14.2
镜像开始,将源代码复制到镜像中,然后编译Go应用程序。
然后,开始第二个构建阶段,它从较小的alpine:latest
镜像开始,并从builder
阶段复制编译好的Go应用程序到新镜像中。
这样,最终得到的镜像中只包含了编译好的Go应用程序,而没有包含用于编译的Go编译器等额外的工具和依赖,使得镜像更加轻量化。
CMD和ENTRYPOINT有什么区别
CMD
设置默认的被容器执行的命令,并且可以有参数。如果 Docker 运行时(也就是docker run命令)指定了其他命令,CMD 命令会被忽略。
ENTRYPOINT
配置容器启动时运行的命令,让容器以应用程序或服务的形式运行。不同于 CMD,它不会被 docker run 的命令行参数覆盖
也正是这个原因,一般来说,推荐使用ENTRYPOINT
, 把所有需要的执行的命令都写进一个脚本,这样可以减少上线过程中的由于传参导致的问题。
如何获取一个docker image的SHA256
1 | docker inspect --format='{{index .RepoDigests 0}}' <docker image> | cut -d ':' -f 2 |
Docker Compose
Docker Compose 是一款用于定义和运行多容器 Docker 应用程序的工具,它允许用户通过一个 YAML 文件(通常名为 docker-compose.yml
)来配置整个应用的容器服务、网络、数据卷以及其他相关设置。Docker Compose 是 Docker 官方提供的编排工具,主要用于简化在单台机器上运行多个 Docker 容器的过程。
从我的实际工作经验来看,docker compose最大的好处有两个:依赖管理和环境切换。
Docker Compose 可以管理服务间的依赖关系,确保服务按照正确的顺序启动和停止。
也可以为不同的环境(如开发、测试、生产)编写不同的 docker-compose.yml
文件,并通过 -f
参数指定加载不同的配置文件。
下面是一个案例和讲解。
1 | version: '3.9' |
在这个例子中:
- 有一个名为
db
的服务,它是基于 Postgres 数据库镜像的容器。 backend
服务依赖于db
服务,意味着backend
服务会在db
服务启动并准备就绪后再启动。depends_on
关键字用于表达这种依赖关系。backend
服务需要连接到db
服务,所以它设置了DB_HOST
为db
,这是因为在同一个 Docker Compose 网络中,服务可以通过服务名进行互相访问。frontend
服务同样依赖于backend
服务,只有当backend
完全启动后才会启动frontend
服务。volumes
部分定义了一个持久化的数据卷db_data
,用于存储db
服务的数据,确保数据在容器重启时不丢失。- 最后,所有的服务都被分配到了默认网络
my_app_net
中,以便它们之间能够相互通信。