Dockerfile的设置到现在都不知怎么做了,但又一次是官方的最佳实践1或官方参考2我想阅读和理解。
我将触及Dockerfile每条指令的含义,并指出有效使用缓存的注意事项。
*我在最佳实践中省略了关于“如何从标准输入docker build”的项目。

什么是 Dockerfile?

包含构建 Docker 映像的说明的文件。
基于Dockerfile创建镜像,基于该镜像创建容器。
因此,如果您已经有图像,则不需要Dockerfile

创建Dockerfile时的注意事项

现在让我们创建Dockerfile
在研究每个命令的含义之前,我们先来看看创建Dockerfile时应该注意什么。

有效利用缓存

Dockerfile 将从第一行开始按顺序执行。如果缓存中存在现有图像,它将被重用。缓存操作规则Dockerfile最佳实践3引自
* 如果您不想在构建映像时使用缓存,请添加docker build --no-cache=true 之类的选项。

  • 从已经在缓存中的父图像开始,将下一条指令与从该基础图像派生的所有子图像进行比较,以查看其中一个是否是使用完全相同的指令构建的。

  • 在大多数情况下,只需将Dockerfile 中的指令与其中一个子图像进行比较就足够了。

  • 对于ADDCOPY 指令,检查图像中文件的内容并为每个文件计算校验和。在这些校验和中考虑。在缓存查找期间,校验和与校验和进行比较在现有图像中。

  • 除了ADDCOPY 命令之外,缓存检查不会查看容器中的文件来确定缓存匹配。如果缓存命中存在。在这种情况下,只有命令字符串本身用于查找匹配。

翻译

  • 从已经缓存的父图像开始。然后它会比较所有子图像,以查看它们是否使用完全相同的指令构建。如果它是从不同的指令构建的,则缓存将失效。

  • 在大多数情况下,只需将Dockerfile 中的指令与子图像进行比较就足以检查缓存,但有些指令需要进一步检查和解释。

  • 对于ADDCOPY,将验证映像中文件的内容,并为每个文件计算校验和。它不考虑文件的最后修改或最后访问时间。当它遍历缓存时,它将校验和与现有图像的校验和进行比较。如果文件内容或元数据发生更改,缓存将失效。

  • 对于ADDCOPY 以外的指令,缓存检查会在不查看容器中的文件的情况下确定缓存是否匹配。例如,如果您运行RUN apt-get -y update,则更新后的容器内的文件将不会用于确定缓存命中。在这种情况下,它仅由指令字符串确定。

一旦缓存失效,所有后续指令将创建一个新图像并执行,而不再使用缓存。因此,为了有效利用缓存,最好在Dockerfile上面的那一行写上变化较少的指令。

好的
RUN apt-get update -qq
COPY . /myapp
坏的
COPY . /myapp
RUN apt-get update -qq

多阶段构建

尽可能利用多阶段构建。您可以显着减小最终图像文件的大小。让我们看一个例子4.

多阶段构建
# syntax=docker/dockerfile:1
FROM golang:1.16-alpine AS build

# プロジェクトに必要なツールをインストール
# `docker build --no-cache .`を実行して依存関係を更新
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# Gopkg.tomlとGopkg.lockで依存関係にあるプロジェクトを列挙
# これらのレイヤーはGopkgファイルが更新されたときのみリビルドされる
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# 依存関係にあるライブラリをインストール
RUN dep ensure -vendor-only

# プロジェクト全体をコピーしてビルド
# このレイヤーはprojectディレクトリにあるファイルが変更された場合にリビルドされる
COPY . /go/src/project/
RUN go build -o /bin/project

# ここを単一のレイヤーイメージで済ませることが可能
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]

虽然使用场景有限,但是可以将构建环境分离出来很方便。额外的文件可以从最后一层中删除。

不要安装任何你不需要的东西

避免安装“拥有”级别以减少图像复杂性和文件大小。例如,您不需要在数据库映像上安装 Vim。

.dockerignore

使用COPY . /myapp等时,这里指定不需要添加的文件。当我构建我的 Rails 应用程序时,我进行了以下设置:

.dockerignore
# Docker
**/.dockerignore
**/*Dockerfile

# Git
.git
.gitignore

# Vim
**/.*.sw[po]

# Node.js
/node_modules

# Rails
/log/*
!/log/.keep

/tmp/*
!/tmp/.keep

/.bundle
/vendor/bundle

最小化层数

按照官方说法5,在旧版本的 Docker 中,尽量减少层数以提高性能很重要。
* “旧版本”是否意味着我们不需要在新的 Docker 中专注于减少层数? “如果有人知道,请告诉我们!”
只有三个命令RUNCOPYADD 会创建一个新层。其他命令仅创建中间图像并且不会增加构建大小。如果可能,使用多阶段构建来减少空间量。

好的
RUN apt-get update -qq \
  && apt-get install -y nodejs postgresql-client \
  && rm -rf /var/lib/apt/lists/*
COPY foo bar /dest/path
坏的
RUN apt-get update -qq
RUN apt-get install -y nodejs postgresql-client
RUN rm -rf /var/lib/apt/lists/*
COPY foo /dest/path
COPY bar /dest/path

Dockerfile 的命令列表

让我们看一下Dockerfile中使用的命令(+α)。

# 句法

指定语法版本。官方推荐使用最新版1.x.x

# 句法
# syntax=docker/dockerfile:1

指定基本 Docker 映像。如果可能,请使用当前的官方图片。正式高山图像被推荐。 5MB 带有 musl libc 和 BusyBox! “这是一笔大买卖。” (自由的)

# 从
FROM ruby:2.6.3

标签

您可以为图像添加标签信息。这就像一个评论。写入许可证信息和项目版本。在 Docker 1.10 之前,建议在一行写多个LABEL 以减少层数,但不再需要了。

# 标签
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL vendor2=ZENITH\ Incorporated
LABEL com.example.release-date="2015-02-12"

RUN 执行任何命令。默认情况下,它在 Linux 上运行 /bin/sh -c,在 Windows 上运行 cmd /S /C。如果它很长或很复杂,如果你用\将它写成多行,它会更容易管理。

好的
RUN apt-get update -qq \
  && apt-get install -y nodejs postgresql-client \
  && rm -rf /var/lib/apt/lists/*
坏的
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client && rm -rf /var/lib/apt/lists/*

apt-get

apt-get 经常与RUN 一起使用。
我们还检查一下Dockerfile 中经常使用的-qq 选项。
apt-get-q 选项,q 表示安静。隐藏进度条等
如果指定了-q=2-qq,它也可以用作-y 选项。

另外,让我们用rm -rf /var/lib/apt/lists/* 删除缓存以减小图像文件的大小。不过 Debian 和 Ubuntu 官方镜像自动设置为apt-get clean,所以不需要显式清除缓存。很方便!
apt-get install 当你有更多的事情要做时,我建议按字母顺序对它们进行排序,并在每个之间放置一个换行符,以防止重复并更易于管理。

apt-get
RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && rm -rf /var/lib/apt/lists/*
apt-get 而不是 apt?

我在这里有点担心。
我以为 Ubuntu 推荐使用 apt,但 Dockerfile 通常使用 apt-get。
为什么? “apt的人有答案。”

适合的人(节选)
    SCRIPT USAGE AND DIFFERENCES FROM OTHER APT TOOLS
           The apt(8) commandline is designed as an end-user tool and it may change
           behavior between versions. While it tries not to break backward compatibility
           this is not guaranteed either if a change seems beneficial for interactive use.
   
           All features of apt(8) are available in dedicated APT tools like apt-get(8) and
           apt-cache(8) as well.  apt(8) just changes the default value of some options
           (see apt.conf(5) and specifically the Binary scope). So you should prefer using
           these commands (potentially with some additional options enabled) in your
           scripts as they keep backward compatibility as much as possible.
简略翻译
    使用法と他のaptツールとの違い
           apt(8)はエンドユーザー向けに作られており、
           インタラクティブな使用に有益ならば後方互換性が保証されない場合があります。

           apt(8)の機能はapt-get(8)やapt-cache(8)などのツールで全て利用できます。
           従って、できるだけ後方互換性を維持したスクリプトファイルで使うなら、
           apt-getやapt-cacheなどのツールを使った方が良いでしょう。

管道注意事项|?

RUN 经常使用管道命令。
例如:

管道
RUN wget -O - https://some.site | wc -l > /number

但是需要注意的是,在上面的例子中,即使wget失败了,如果wc成功了,整个命令都会被视为成功,并且会创建一个新的镜像。如果这不是您想要的,使用pipefail 将解决此问题。以下示例正确提取了wget 错误。

管道故障
RUN set -o pipefail && wget -O - https://some.site | wc -l > /number

工作目录

设置您的工作目录。如果目录不存在,则会创建,所以不需要使用RUN mkdir /app/root。虽然可以指定到现有 WORKDIR 的相对路径,但官方建议使用绝对路径。

复制

将文件或目录复制到容器的文件系统。
您还可以使用诸如COPY *.txt /dist/path 之类的通配符。
如果要指定包含空格的字符串,请编写:
COPY ["/src1", "/src2", ..., "/path/containing/one space"]
使用" 而不是' 作为引号,因为它将被解析为JSON 数组。

复制还是添加?

有一个名为ADD 的命令具有几乎相似的功能。有关如何正确使用它们的官方说明。6

虽然ADDCOPY 在功能上相似,但一般来说,COPY 是首选。具有一些不是立即显而易见的功能(如仅本地tar 提取和远程URL 支持)。因此,最好使用@987654403 @ 是本地 tar 文件自动提取到图像中,如 ADD rootfs.tar.xz /
“……”
对于不需要ADD 的tar 自动提取功能的其他项目(文件、目录),您应该始终使用COPY

翻译

ADDCOPY 是具有相似功能的命令,但在大多数情况下首选 COPY。因为该命令比ADD 更透明。 COPY 只支持将本地文件复制到容器的基本功能,而ADD 有一些特殊性。例如,在本地 tar 上使用 ADD 将提取和复制它,它支持远程 URL。这些功能乍一看并不明显。所以ADD是你想自动将本地tar文件解压到Docker镜像中时使用ADD rootfs.tar.xz /的好地方。
[剪辑]
如果您不需要此功能,最好使用COPY

暴露

指定容器侦听的端口。为了将容器连接在一起,Docker 设置一个环境变量以从接收容器路由回源(例如 MYSQL_PORT_3306_TCP)。

环境噪声

可以ENV <key>=<value>ENV <key> <value>的形式设置环境变量。
除了PATH 设置外,它通常用于通过仅指定版本号来管理命令,如下所示:

环境噪声
ENV PG_MAJOR=9.3
ENV PG_VERSION=9.3.4
RUN curl -SL https://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgres &&ENV PATH=/usr/local/postgres-$PG_MAJOR/bin:$PATH

请勿在 ENV 中设置密码

由于每次执行ENV都会创建一个中间镜像,所以即使后面的环境变量是unset,也可以引用为docker history。因此,请勿输入任何凭据信息,例如密码。

ARGs

ARG <name>[=<default value>] 允许您设置仅在构建时需要的变量。就个人而言,我经常像ARG APP_ROOT=/myapp 一样使用它。出于与ENV 相同的原因,请勿使用ARG 设置凭据。

体积

您可以决定挂载哪个目录,给它一个特定的名称,并将其用作从本机主机或其他容器挂载的外部卷。仅用于存储数据库、配置文件或挂载文件和容器创建的目录。

体积
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

例如,docker run 将创建一个在/myvol 创建新挂载点的映像,并将greeting 文件复制到新创建的卷。

用户

您可以使用USER <user>[:<group>]USER <UID>[:<GID>] 指定默认用户和组。映像中的用户和组被分配了重要的 UID 和 GID。如果重要,请明确指定 UID 和 GID。
如果你想要root,不要使用sudo。 TTY 和信令可能会出现意外问题。如果您绝对需要像sudo 这样的功能五宿考虑使用 .
此外,避免不必要地切换USER 以减少层数并使图像不那么复杂。

建立

您可以指定在Dockerfile 的构建完成后执行的指令。由ONBUILD 指定的指令在执行针对子图像的任何其他指令之前,对写入ONBUILD 的图像的任何子图像执行。
ONBUILD 在您想要针对指定图像进行构建时很有用。但是,在ONBUILD中使用ADDCOPY时,如果新构建的上下文中文件不存在,则原镜像的构建会失败,所以要小心。

入口点和 CMD

指定运行容器时的默认行为。即使CMDDockerfile中多次写入,也只会执行最后一个。关于如何使用ENTRYPOINTCMD的官方指导,因为行为有点复杂7我会引用

  1. Dockerfile 应至少指定CMDENTRYPOINT 命令之一。
  2. ENTRYPOINT 应在将容器用作可执行文件时定义。
  3. CMD 应该用作为 ENTRYPOINT 命令或在容器中执行临时命令定义默认参数的一种方式。
  4. CMD 将在使用替代参数运行容器时被覆盖。

    翻译

    1. 在 Dockerfile 中至少指定 CMDENTRYPOINT 之一。
    2. 将容器用作可执行文件时定义ENTRYPOINT
    3. 使用CMD 作为ENTRYPOINT 的默认参数或在容器中运行临时命令。
    4. 如果容器使用替代参数启动,CMD 参数将被覆盖。

      ENTRYPOINT 让我们为该图像设置主命令。例如,在 Rails 图像中,我得到的印象是 execENTRYPOINT 内执行,CMD 作为参数传递以启动 Rails 服务器。

      结尾

      就这样。谢谢你读到这里!

      参考

      Dockerfile 参考 | Docker 文档
      编写 Dockerfile 的最佳实践 | Docker 文档
      使用 Docker 机密管理敏感数据 | Docker 文档

      1. Dockerfile 参考 | Docker 文档

      2. Dockerfile 参考 | Docker 文档

      3. #leverage-build-cache 编写 Dockerfile 的最佳实践 | Docker 文档

      4. #use-multi-stage-builds 编写 Dockerfile 的最佳实践 | Docker 文档

      5. #minimize-the-number-of-layers 编写 Dockerfile 的最佳实践 | Docker 文档

      6. #add-or-copy 编写 Dockerfile 的最佳实践 | Docker 文档

      7. #understand-how-cmd-and-entrypoint-interact Dockerfile 参考 | Docker 文档


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308624532.html

相关文章:

  • 2021-11-04
  • 2021-06-27
  • 2021-11-22
  • 2022-12-23
  • 2021-11-14
  • 2018-06-27
  • 2022-12-23
  • 2021-06-23
猜你喜欢
  • 2021-11-04
  • 2022-02-15
  • 2021-11-02
  • 2022-01-05
  • 2021-11-14
  • 2019-12-14
相关资源
相似解决方案