如何编写最佳dockerfile_编写Dockerfile的最佳实践之一

本文详细介绍了构建高效Docker镜像的最佳实践,包括使用Dockerfile,理解镜像层,优化构建过程,使用多阶段构建,管理构建上下文,减少镜像大小,利用缓存以及遵循容器化应用的最佳原则。强调了短暂容器的重要性,避免不必要的软件包,以及如何通过精简Dockerfile和使用LABEL,CMD,RUN,ADD,COPY,ENTRYPOINT,USER,WORKDIR和VOLUME指令来提高效率。
摘要由CSDN通过智能技术生成

预计阅读时间: 31分钟

本文档介绍了用于构建有效镜像的最佳实践和方法。

955fa81e43433b623b0cab1eac39edc8.png

Dockerfile命令

Docker通过读取Dockerfile的指令自动构建镜像,该文件是一个文本文件,其中依次包含构建给定镜像所需的所有命令。 Dockerfile遵循特定的格式和指令集,您可以在Dockerfile参考中找到该文件 。

db524a505fed18354a9da0066f812510.png

Docker镜像由只读层组成,每个只读层代表一个Dockerfile指令。 各个层堆叠在一起,每个层都是上一层的变化的增量。 考虑一下这个Dockerfile :

FROM ubuntu:18.04COPY . /appRUN make /appCMD python /app/app.py

每条指令创建一层:

· FROM从ubuntu:18.04 Docker镜像创建一个层。

· COPY从Docker客户端的当前目录添加文件。

· RUN使用make构建您的应用程序。

· CMD指定在容器内运行什么命令。

运行镜像并生成容器时,可以在基础层之上添加一个新的可写层 ("容器层")。 对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入这个可写容器层。

有关镜像层的更多信息(以及Docker如何构建和存储镜像),请参阅关于存储驱动程序 。

一般准则和建议

创建临时容器

Dockerfile定义的镜像应该生成尽可能短暂的容器。 "短暂"是指可以停止并销毁容器,然后对其进行重建和替换,并使用绝对的最小限度的设置和配置。

请参阅"十二因子应用程序"方法下的" 过程 " ,以了解以这种无状态方式运行容器的动机。

了解构建的上下文环境

发出docker build命令时,当前工作目录称为build context 。 默认情况下,假定Dockerfile位于此处,但是您可以使用文件标志( -f )指定其他位置。 无论Dockerfile实际位于何处,当前目录中文件和目录的所有递归内容都将作为构建上下文发送到Docker守护程序。

构建上下文示例

创建一个用于构建上下文的目录,并将其插入cd 。 将" hello"写入名为hello的文本文件,并创建一个在其上运行cat的Dockerfile。 从构建上下文( . )中构建镜像:

mkdir myproject && cd myprojectecho "hello" > helloecho -e "FROM busyboxCOPY /hello /RUN cat /hello" > Dockerfiledocker build -t helloapp:v1 ./

将Dockerfile和hello移到单独的目录中,并构建镜像的第二个版本(不依赖上次构建的缓存)。 使用-f指向Dockerfile并指定构建上下文的目录:

mkdir -p dockerfiles contextmv Dockerfile dockerfiles && mv hello contextdocker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context

疏忽地包含了构建镜像所不需要的构建上下文文件会导致较大的镜像体积。 这会增加生成镜像的时间,拉取和推送镜像的时间以及容器运行时的大小。 要查看您的构建上下文有多大,请在构建Dockerfile时查找如下消息:

Sending build context to Docker daemon  187.8MB

Pipe Dockerfile through stdin

Docker可以通过使用本地或远程构建上下文通过stdin Dockerfile来构建镜像。 通过stdin插入Dockerfile可以在不将Dockerfile写入磁盘的情况下执行一次性构建,或者在生成Dockerfile且以后不应持续的情况下使用。

为了方便起见,本节中的示例使用此处的文档 ,但是可以使用在stdin上提供Dockerfile任何方法。

例如,以下命令是等效的:

 echo -e 'FROM busyboxRUN echo "hello world"' | docker build – docker build -<

您可以使用首选方法或最适合您的用例的方法替换示例。

使用来自stdin的Dockerfile构建镜像,而无需发送构建上下文

使用此语法使用stdin的Dockerfile构建镜像,而无需发送其他文件作为构建上下文。 连字符( - )占据PATH的位置,并指示Docker从stdin而不是目录中读取构建上下文(仅包含Dockerfile ):

docker build [OPTIONS] -

以下示例使用通过stdin传递的Dockerfile构建镜像。 没有文件作为构建上下文发送到守护程序。

docker build -t myimage:latest -<

在您的Dockerfile不需要将文件复制到镜像中的情况下,省略构建上下文会很有用,并且由于没有文件发送到守护程序,因此可以提高构建速度。

如果要通过从构建上下文中排除某些文件来提高构建速度,请使用.dockerignore进行排除 。

注意 :如果使用此语法,尝试构建使用COPY或ADD的Dockerfile将会失败。 以下示例说明了这一点:

# create a directory to work inmkdir examplecd example # create an example filetouch somefile.txt docker build -t myimage:latest -<

使用stdin中的Dockerfile,从本地构建上下文进行构建

使用此语法使用本地文件系统上的文件,但使用stdin的Dockerfile来构建镜像。 该语法使用-f (或--file )选项指定要使用的Dockerfile ,并使用连字符( - )作为文件名来指示Docker从stdin读取Dockerfile :

docker build [OPTIONS] -f- PATH

以下示例使用当前目录( . )作为构建上下文,并使用Dockerfile构建镜像,并使用here文档通过stdin传递该文件 。

# create a directory to work inmkdir examplecd example # create an example filetouch somefile.txt # build an image using the current directory as context, and a Dockerfile passed through stdindocker build -t myimage:latest -f- . <

使用stdin中的Dockerfile从远程构建上下文进行构建

使用此语法使用stdin的Dockerfile使用远程git存储库中的文件构建镜像。 该语法使用-f (或--file )选项指定要使用的Dockerfile ,并使用连字符( - )作为文件名来指示Docker从stdin读取Dockerfile :

docker build [OPTIONS] -f- PATH

如果您要从不包含Dockerfile的存储库中构建镜像,或者想要使用自定义Dockerfile进行构建而又不维护自己的存储库派生,则此语法很有用。

以下示例使用stdin的Dockerfile构建镜像,并从GitHub上的" hello-world" Git存储库添加hello.c文件。

docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <

释义

当使用远程Git存储库作为构建上下文构建镜像时,Docker在本地计算机上执行存储库的git clone ,并将这些文件作为构建上下文发送到守护程序。 此功能要求将git安装在运行docker build命令的主机上。

用.dockerignore排除

要排除与构建无关的文件(无需重构源存储库),请使用.dockerignore文件。 该文件支持类似于.gitignore文件的排除模式。 有关创建一个的信息,请参见.dockerignore文件 。

使用多阶段构建

多阶段构建使您可以大幅度减小最终镜像的大小,而不必努力减少中间层和文件的数量。

由于镜像是在构建过程的最后阶段生成的,因此可以利用构建缓存来最小化镜像层。

例如,如果您的构建包含多个层,则可以将它们从更改频率较低(以确保生成缓存可重用)到更改频率较高的顺序排序:

· 安装构建应用程序所需的工具

· 安装或更新库依赖项

· 生成您的申请

Go应用程序的Dockerfile可能类似于:

FROM golang:1.11-alpine AS build # Install tools required for project# Run `docker build --no-cache .` to update dependenciesRUN apk add --no-cache gitRUN go get github.com/golang/dep/cmd/dep # List project dependencies with Gopkg.toml and Gopkg.lock# These layers are only re-built when Gopkg files are updatedCOPY Gopkg.lock Gopkg.toml /go/src/project/WORKDIR /go/src/project/# Install library dependenciesRUN dep ensure -vendor-only # Copy the entire project and build it# This layer is rebuilt when a file changes in the project directoryCOPY . /go/src/project/RUN go build -o /bin/project # This results in a single layer imageFROM scratchCOPY --from=build /bin/project /bin/projectENTRYPOINT ["/bin/project"]CMD ["--help"]

不要安装不必要的软件包

为了降低复杂性,依赖性,文件大小和构建时间,请避免仅仅因为它们"很容易安装"而安装多余或不必要的软件包。 例如,您不需要在数据库镜像中包括文本编辑器。

解耦应用

每个容器应该只有一个关注点。 将应用程序解耦到多个容器中,可以更轻松地水平缩放和重复使用容器。 例如,一个Web应用程序堆栈可能由三个单独的容器组成,每个容器都有自己的唯一镜像,以分离的方式管理Web应用程序,数据库和内存中的缓存。

将每个容器限制为一个进程是一个很好的经验法则,但这并不是一成不变的规则。 例如,不仅可以使用初始化进程来生成容器,而且某些程序还可以自行生成其他进程。 例如, Celery可以产生多个工作进程,而Apache可以为每个请求创建一个进程。

根据您的最佳判断,使容器保持清洁和模块化。 如果容器相互依赖,则可以使用Docker容器网络来确保这些容器可以通信。

减少层数

在较旧的Docker版本中,务必最小化镜像中的层数以确保其性能。 添加了以下功能来减少此限制:

· 只有指令RUN , COPY , ADD才能创建层。 其他说明创建临时的中间镜像,并且不会增加构建的大小。

· 尽可能使用多阶段构建 ,并且仅将所需的工件复制到最终镜像中。 这使您可以在中间构建阶段中包含工具和调试信息,而无需增加最终镜像的大小。

多行参数排列

尽可能通过字母数字排序多行参数来简化以后的更改。 这有助于避免软件包重复,并使列表更易于更新。 这也使PR易于阅读和查看。 在反斜杠( )之前添加空格也有帮助。

这是来自buildpack-deps镜像的示例:

RUN apt-get update && apt-get install -y   bzr   cvs   git   mercurial   subversion

利用构建缓存

在构建镜像时,Docker将Dockerfile执行Dockerfile的指令, Dockerfile指定的顺序执行每个指令。 在检查每条指令时,Docker会在其缓存中寻找一个可以重用的现有镜像,而不是创建一个新的(重复的)镜像。

如果根本不想使用缓存,则可以在docker build命令上使用--no-cache=true选项。 但是,如果您确实让Docker使用其缓存,那么了解何时可以找到匹配的镜像非常重要。 Docker遵循的基本规则概述如下:

· 从已在缓存中的父镜像开始,将下一条指令与从该基本镜像派生的所有子镜像进行比较,以查看是否其中一个是使用完全相同的指令构建的。 如果不是,则高速缓存无效。

· 在大多数情况下,仅将Dockerfile的指令与子镜像之一进行比较就足够了。 但是,某些说明需要更多的检查和解释。

· 对于ADD和COPY指令,将检查镜像中文件的内容,并为每个文件计算一个校验和。 在这些校验和中不考虑文件的最后修改时间和最后访问时间。 在高速缓存查找期间,将校验和与现有镜像中的校验和进行比较。 如果文件中的任何内容(例如内容和元数据)发生了更改,则缓存将无效。

· 除了ADD和COPY命令以外,缓存检查不会查看容器中的文件来确定缓存是否匹配。 例如,在处理RUN apt-get -y update命令时,不会检查容器中RUN apt-get -y update的文件,以确定是否存在缓存命中。 在这种情况下,仅使用命令字符串本身来查找匹配项。

缓存无效后,所有后续Dockerfile命令都会生成新镜像,并且不使用缓存。

Dockerfile说明

这些建议旨在帮助您创建高效且可维护的Dockerfile 。

FROM

Dockerfile的FROM指令参考

尽可能使用当前的官方镜像作为镜像的基础。 我们建议使用Alpine镜像,因为它受到严格控制且尺寸较小(当前小于5 MB),同时仍是完整的Linux发行版。

LABEL

了解对象标签

您可以在镜像上添加标签,以帮助按项目组织镜像,记录许可信息,帮助自动化或出于其他原因。 对于每个标签,添加一行以LABEL开头并带有一个或多个键值对的行。 以下示例显示了不同的可接受格式。 内嵌包含解释性注释。

带空格的字符串必须用引号引起来, 否则必须转义空格。 内引号( " )也必须转义。

# Set one or more individual labelsLABEL com.example.version="0.0.1-beta"LABEL vendor1="ACME Incorporated"LABEL vendor2=ZENITH IncorporatedLABEL com.example.release-date="2015-02-12"LABEL com.example.version.is-production=""

一幅镜像可以有多个标签。 在Docker 1.10之前,建议将所有标签合并为一个LABEL指令,以防止创建额外的层。 不再需要此操作,但仍支持组合标签。

# Set multiple labels on one line

LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"

上面也可以写成:

 # Set multiple labels at once, using line-continuation characters to break long linesLABEL vendor=ACME Incorporated       com.example.is-beta=       com.example.is-production=""       com.example.version="0.0.1-beta"       com.example.release-date="2015-02-12"

请参阅了解对象标签以获取有关可接受的标签键和值的准则。 有关查询标​​签的信息,请参阅" 管理对象上的标签"中与过滤有关的项目。 另请参阅Dockerfile参考中的LABEL 。

RUN

RUN指令的Dockerfile参考

将多行长或复杂的RUN语句分割成多行,并用反斜杠分隔,以使您的Dockerfile更具可读性,可理解性和可维护性。

APT-GET

RUN的最常见用例可能是apt-get的应用程序。 因为它安装软件包,所以RUN apt-get命令需要注意一些陷阱。

避免使用RUN apt-get upgrade和dist-upgrade ,因为许多来自父镜像的"基本"软件包都无法在无特权的容器内升级。 如果父镜像中包含的软件包已过期,请联系其维护者。 如果您知道有一个特定的软件包foo需要更新,请使用apt-get install -y foo自动更新。

始终在同一RUN语句中将RUN apt-get update与apt-get install结合在一起。 例如:

RUN apt-get update && apt-get install -y     package-bar     package-baz     package-foo

在RUN语句中单独使用apt-get update会导致缓存问题,并且后续的apt-get install说明将失败。 例如,假设您有一个Dockerfile:

FROM ubuntu:18.04RUN apt-get updateRUN apt-get install -y curl

构建镜像后,所有层都在Docker缓存中。 假设您稍后通过添加额外的软件包来修改apt-get install :

FROM ubuntu:18.04RUN apt-get updateRUN apt-get install -y curl nginx

Docker将初始指令和修改后的指令视为相同,并重复使用先前步骤中的缓存。 结果,由于构建使用了缓存版本,因此不执行apt-get update 。 由于未运行apt-get update ,因此您的构建可能会获得curl和nginx软件包的过时版本。

使用RUN apt-get update && apt-get install -y可确保您的Dockerfile安装最新的软件包版本,而无需进一步的编码或手动干预。 这种技术称为"缓存清除"。 您还可以通过指定软件包版本来实现缓存清除。 这称为版本固定,例如:

RUN apt-get update && apt-get install -y     package-bar     package-baz     package-foo=1.3.*

版本固定会强制构建检索特定版本,而不管缓存中的内容是什么。 该技术还可以减少由于所需包装中的意外更改而导致的故障。

下面是格式正确的RUN指令,演示了所有的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/*

s3cmd参数指定版本1.1.* 。 如果镜像先前使用的是旧版本,则指定新版本会导致apt-get update的缓存崩溃,并确保安装新版本。 在每行上列出软件包还可以防止软件包重复中的错误。

另外,当通过删除/var/lib/apt/lists清理apt缓存时,由于apt缓存未存储在层中,因此会减小镜像大小。 由于RUN语句以apt-get update开头,因此始终在apt-get install之前刷新程序包缓存。

官方的Debian和Ubuntu镜像会自动运行apt-get clean ,因此不需要显式调用。

使用管道

某些RUN命令取决于使用管道字符( | )将一个命令的输出管道传输到另一个命令的能力,如以下示例所示:

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

Docker使用/bin/sh -c解释器执行这些命令,该解释器仅评估管道中最后一个操作的退出代码以确定成功。 在上面的示例中,即使wget命令失败,只要wc -l命令成功,此构建步骤也会成功并生成一个新镜像。

如果希望由于管道中的任何阶段的错误而导致命令失败,请在set -o pipefail &&添加set -o pipefail &&以确保意外的错误可以防止构建意外进行。 例如:

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

并非所有的外壳程序都支持-o pipefail选项。

如果是基于Debian的镜像上的dash外壳,请考虑使用RUN的exec形式显式选择一个确实支持pipefail选项的外壳。 例如:

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]

CMD

CMD指令的Dockerfile参考

应使用CMD指令来运行镜像所包含的软件以及所有参数。 CMD几乎应始终以CMD ["executable", "param1", "param2"…] 。 因此,如果镜像用于服务(例如Apache和Rails),则将运行CMD ["apache2","-DFOREGROUND"] 。 实际上,建议将这种形式的指令用于任何基于服务的镜像。

在大多数其他情况下,应为CMD提供交互式外壳,例如bash,python和perl。 例如, CMD ["perl", "-de0"] , CMD ["python"]或CMD ["php", "-a"] 。 使用这种形式意味着当您执行诸如docker run -it python ,您将被放入可用的外壳中,随时可以使用。 除非您和您的预期用户已经非常熟悉ENTRYPOINT工作原理,否则CMD很少以CMD ["param", "param"]的形式与ENTRYPOINT结合使用。

EXPOSE

有关EXPOSE指令的Dockerfile参考

EXPOSE指令指示容器在其上侦听连接的端口。 因此,应为应用程序使用通用的传统端口。 例如,包含Apache Web服务器的镜像将使用EXPOSE 80 ,而包含MongoDB的镜像将使用EXPOSE 27017 ,依此类推。

对于外部访问,您的用户可以执行带有标志的docker run ,该标志指示如何将指定端口映射到他们选择的端口。 对于容器链接,Docker提供了环境变量,用于从接收者容器到源容器的路径(即MYSQL_PORT_3306_TCP )。

ENV

ENV指令的Dockerfile参考

为了使新软件更易于运行,可以使用ENV为容器安装的软件更新PATH环境变量。 例如, ENV PATH /usr/local/nginx/bin:$PATH确保CMD ["nginx"]正常工作。

ENV指令还可用于提供特定于您要容器化的服务的必需环境变量,例如Postgres的PGDATA 。

最后, ENV还可以用于设置常用的版本号,以便更容易维护版本凹凸,如以下示例所示:

ENV PG_MAJOR 9.3ENV PG_VERSION 9.3.4RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

类似于在程序中具有恒定变量(与硬编码值相反),此方法使您可以更改单个ENV指令以自动神奇地修改容器中软件的版本。

每条ENV线都会创建一个新的中间层,就像RUN命令一样。 这意味着即使您在以后的层中取消设置环境变量,它也仍将保留在该层中,并且其值也无法转储。 您可以通过创建如下所示的Dockerfile,然后对其进行构建来进行测试。

FROM alpineENV ADMIN_USER="mark"RUN echo $ADMIN_USER > ./markRUN unset ADMIN_USER $ docker run --rm test sh -c 'echo $ADMIN_USER'mark

为避免这种情况,并真正取消设置环境变量,请在外壳程序中使用RUN命令和shell命令,以在单个层中全部设置,使用和取消设置该变量。 您可以使用;分隔命令; 或&& 。 如果您使用第二种方法,并且其中一个命令失败,则docker build也将失败。 这通常是个好主意。 将用作Linux Dockerfiles的行连续字符可提高可读性。 您还可以将所有命令放入一个Shell脚本中,并让RUN命令运行该Shell脚本。

FROM alpineRUN export ADMIN_USER="mark"     && echo $ADMIN_USER > ./mark     && unset ADMIN_USERCMD sh$ docker run --rm test sh -c 'echo $ADMIN_USER'

ADD or COPY

· 用于ADD指令的Dockerfile参考

· COPY指令的Dockerfile参考

尽管ADD和COPY在功能上相似,但通常来说COPY是首选。 那是因为它比ADD更透明。 COPY仅支持将本地文件基本复制到容器中,而ADD某些功能(如仅本地tar提取和远程URL支持)并不是立即显而易见的。 因此,与ADD rootfs.tar.xz /一样, ADD rootfs.tar.xz /的最佳用途是将本地tar文件自动提取到镜像中。

如果您有多个使用不同上下文的文件的Dockerfile步骤,请单独COPY而不是一次全部COPY 。 这样可以确保仅在特别需要的文件发生更改时,才使每个步骤的构建缓存无效(强制重新运行该步骤)。

例如:

COPY requirements.txt /tmp/RUN pip install --requirement /tmp/requirements.txtCOPY . /tmp/

与放置COPY . /tmp/相比,导致RUN步骤的缓存失效更少COPY . /tmp/ COPY . /tmp/之前。

由于镜像大小很重要,因此强烈建议不要使用ADD从远程URL获取软件包。 您应该使用curl或wget代替。 这样,您可以在提取文件后删除不再需要的文件,而不必在镜像中添加另一层。 例如,您应该避免做以下事情:

ADD http://example.com/big.tar.xz /usr/src/things/RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/thingsRUN make -C /usr/src/things all

相反,请执行以下操作:

RUN mkdir -p /usr/src/things     && curl -SL http://example.com/big.tar.xz     | tar -xJC /usr/src/things     && make -C /usr/src/things all

对于不需要ADD的tar自动提取功能的其他项目(文件,目录),应始终使用COPY 。

ENTRYPOINT

ENTRYPOINT指令的Dockerfile参考

ENTRYPOINT的最佳用法是设置镜像的主命令,以使该镜像像该命令一样运行(然后使用CMD作为默认标志)。

让我们从命令行工具s3cmd的镜像示例开始:

ENTRYPOINT ["s3cmd"]CMD ["--help"]

现在可以像这样运行镜像以显示命令的帮助:

$ docker run s3cmd

或使用正确的参数执行命令:

$ docker run s3cmd ls s3://mybucket

这很有用,因为镜像名称可以用作对二进制文件的引用,如上面的命令所示。

ENTRYPOINT指令也可以与辅助脚本结合使用,即使启动该工具可能需要一个以上的步骤,也可以使其与上述命令类似地工作。

例如, Postgres Official Image使用以下脚本作为其ENTRYPOINT :

#!/bin/bashset -e if [ "$1" = 'postgres' ]; then    chown -R postgres "$PGDATA"     if [ -z "$(ls -A "$PGDATA")" ]; then        gosu postgres initdb    fi     exec gosu postgres "$@"fi exec "$@"

将应用程序配置为PID 1

该脚本使用exec Bash命令,以便最终运行的应用程序成为容器的PID1。这使该应用程序可以接收发送到该容器的所有Unix信号。 有关更多信息,请参见ENTRYPOINT参考 。

将帮助程序脚本复制到容器中,并在容器启动时通过ENTRYPOINT运行:

COPY ./docker-entrypoint.sh /ENTRYPOINT ["/docker-entrypoint.sh"]CMD ["postgres"]

该脚本允许用户以多种方式与Postgres进行交互。

它可以简单地启动Postgres:

$ docker run postgres

或者,它可以用于运行Postgres并将参数传递给服务器:

$ docker run postgres postgres --help

最后,它也可以用于启动一个完全不同的工具,例如Bash:

$ docker run --rm -it postgres bash

VOLUME

VOLUME指令的Dockerfile参考

VOLUME指令应用于公开由Docker容器创建的任何数据库存储区,配置存储或文件/文件夹。 强烈建议您将VOLUME用于镜像的任何可变和/或用户可维修的部分。

USER

USER指令的Dockerfile参考

如果服务可以在没有特权的情况下运行,请使用USER更改为非root用户。 首先在Dockerfile使用RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres等创建用户和组。

考虑一个明确的UID / GID

为镜像中的用户和组分配了不确定的UID / GID,因为无论镜像重建如何,都将分配"下一个" UID / GID。 因此,如果很关键,则应分配一个明确的UID / GID。

由于Go存档/ tar软件包处理稀疏文件中的一个未解决的错误 ,尝试在Docker容器内创建具有非常大的UID的用户可能会导致磁盘耗尽,因为容器层中的/var/log/faillog充满了NULL( 0)字符。 一种解决方法是将--no-log-init标志传递给useradd。 Debian / Ubuntu adduser包装器不支持此标志。

避免安装或使用sudo因为它具有不可预测的TTY和信号转发行为,可能会导致问题。 如果您绝对需要类似于sudo功能,例如将守护程序初始化为root却以非root身份运行,请考虑使用" gosu" 。

最后,为减少层次和复杂性,请避免频繁来回切换USER 。

WORKDIR

WORKDIR指令的Dockerfile参考

为了清楚和可靠,您应该始终为WORKDIR使用绝对路径。 另外,您应该使用WORKDIR而不是增加诸如RUN cd … && do-something类的指令,这些指令难以阅读,排除故障和维护。

ONBUILD

Dockerfile的ONBUILD指令参考

当前Dockerfile构建完成后,将执行ONBUILD命令。 ONBUILD在FROM当前镜像派生的任何子镜像中执行。 将ONBUILD命令视为父Dockerfile给子Dockerfile 。

Docker构建在子Dockerfile任何命令之前执行ONBUILD命令。

ONBUILD给定镜像构建的镜像, ONBUILD非常有用。 例如,您可以将ONBUILD用于语言堆栈镜像,该镜像可在ONBUILD中构建用该语言编写的任意用户软件,如Ruby的ONBUILD变体所示 。

使用ONBUILD构建的镜像应获得单独的标签,例如: ruby:1.9-onbuild或ruby:2.0-onbuild 。

将ADD或COPY放入ONBUILD时要小心。 如果新构建的上下文缺少要添加的资源,则" onbuild"镜像将灾难性地失败。 如上所述,添加一个单独的标签,可以通过允许Dockerfile作者做出选择来缓解这种情况。

官方镜像示例

这些官方镜像具有示例性Dockerfile :

· Go [ttps://hub.docker.com/_/golang/]

· Perl [https://hub.docker.com/_/perl/]

· Hy [https://hub.docker.com/_/hylang/]

· Ruby [https://hub.docker.com/_/ruby/]


译文地址:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

参考资料:

· Dockerfile Reference

· More about Base Images

· More about Automated Builds

· Guidelines for Creating Official Images

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编写 Dockerfile 时,有以下几个原则可以遵循: 1. 最小化镜像:选择一个尽可能小的基础镜像,避免包含不需要的软件或文件。这能够减少镜像大小和减少潜在的安全风险。 2. 明确指定版本:对于每个使用的软件包或依赖项,明确指定版本号,以确保在构建过程中使用的是稳定和一致的版本。 3. 分层构建:利用 Docker 的分层构建机制,将不经常变化的操作放在靠前的层,以便利用缓存加速构建过程。 4. 优化层顺序:根据操作的频率排列各层,使得那些较不频繁变动的层放在前面,这样在构建或更新时可以更快地利用缓存。 5. 清理不需要的文件:在每个构建步骤之后,删除不再需要的临时文件、软件包缓存和其他不必要的内容,以减小最终镜像的大小。 6. 使用多阶段构建:对于复杂的项目,可以使用多个构建阶段来分离构建环境和生产环境所需的组件,从而减小最终镜像的大小。 7. 容器化最小化特权:避免在容器中以 root 用户运行应用程序,最小化容器的特权,以提高安全性。 8. 使用环境变量:将应用程序中的配置项提取为环境变量,以便在容器启动时进行配置,增加灵活性和可移植性。 以上原则可以帮助您编写更高效、更安全和更易维护的 Dockerfile。当然,在实践中,也要根据具体的项目需求和最佳实践进行适当调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值