编写Dockerfile的最佳实践1. 一般准则和建议1.1 创建临时容器1.2 了解构建环境上下文Context1.3 通过stdin发送Dockerfile1.3.1 使用来自 STDIN 的 DOCKERFILE 构建镜像而无需发送构建上下文1.3.2 从本地的构建上下文构建使用 STDIN 发送 DOCKERFILE1.3.3 从远程的构建上下文构建使用 STDIN 发送 DOCKERFILE2. 用 .dockerignore 排除3. 使用多阶段构建4. 不要安装不必要的软件包5. 解耦应用程序6. 最小化层数7. 排序多行参数8. 利用构建缓存9. Dockerfile指令9.1 FROM9.2 LABEL9.3 RUN9.4 CMD9.5 ENTRYPOINT9.6 EXPOSE9.7 ENV9.8 ADD或COPY9.9 VOLUME9.10 USER9.11 WORKDIR9.12 ONBUILD本文档介绍了用于构建有效镜像的最佳实践和方法。编写Dockerfile的最佳实践Docker通过从一个Dockerfile文本文件中读取指令来自动构建镜像该文本文件按顺序包含构建给定镜像所需的所有命令。一个Dockerfile遵循特定的格式和指令集您可以在Dockerfile 的reference中找到。Docker镜像由一些只读层组成每个只读层对应于一个Dockerfile指令。这些层是堆叠的每个层都是前一层的变化增量。考虑一下DockerfileFROM ubuntu:18.04 COPY./app RUNmake/app CMD python /app/app.py每条指令创建一层FROM从ubuntu:18.04Docker基础镜像创建一层。COPY从Docker客户端的当前目录添加文件。RUN使用make构建您的应用程序。CMD指定在容器中运行什么命令。运行镜像并生成容器时你可以在基础层之上添加一个新的可写层“容器层”。对运行中的容器所做的所有更改例如写入新文件修改现有文件和删除文件都将写入此可写容器层。有关镜像层以及Docker如何构建和存储镜像的更多信息请参阅 关于存储驱动程序。1. 一般准则和建议1.1 创建临时容器被Dockerfile所定义的镜像应生成尽可能是短暂的容器。“短暂”是指可以停止并销毁容器然后对其进行重建和替换并采用绝对的最低限度的设置和配置。请参阅“十二因子应用程序” 方法下的“ Porcesses ” 以了解以这种无状态方式运行容器的动机。1.2 了解构建环境上下文Context发出docker build命令时当前的工作目录称为构建上下文。默认情况下Dockerfile被认定位于当前bash进入的目录目录但是您可以使用文件标志-f指定其他位置。无论Dockerfile实际位于何处当前目录所以一定要注意构建的当前目录不然生成的镜像会很大中文件和目录的所有递归内容都将作为构建上下文发送到Docker守护进程。ps~ Docker 是C/S架构我们在客户端执行所有命令构建事实上是把Dockerfile和需要的文件发送到Docker daemon构建上下文示例为构建上下文创建一个目录并cd进入该目录。将“ hello”写入名为hello的文本文件中然后创建一个Dockerfile在其中运行cat。从构建上下文.中构建镜像mkdirmyprojectcdmyprojectechohellohelloecho-eFROM busybox\nCOPY /hello /\nRUN cat /helloDockerfiledockerbuild-thelloapp:v1.ps~ Dockerfile 内容注意这里COPY /hello / 的起始路径是myproject等价COPY hello /FROM busyboxCOPY /hello /RUN cat /hello移动Dockerfile和hello进入单独的目录并构建镜像的第二个版本不依赖于上次构建的缓存。使用-f以指向Dockerfile并指定构建上下文目录contextmkdir-pdockerfiles contextmvDockerfile dockerfilesmvhello contextdockerbuild --no-cache-thelloapp:v2-fdockerfiles/Dockerfile context包含构建镜像不需要的文件会导致较大的构建上下文和较大的镜像大小。这会增加生成镜像的时间pull和push镜像的时间以及容器运行时的大小。要查看您的构建上下文有多大请在构建您的Dockerfile时查找如下消息Sending build context to Docker daemon 187.8MB1.3 通过stdin发送Dockerfile具有本地或远程构建上下文的Docker通过stdin发送Dockerfile来构建镜像。通过stdin发送Dockerfile可以执行一次性构建无需编写Dockerfile到磁盘上或者生成Dockerfile但不持久化的情形。例如以下命令是等效的echo-eFROM busybox\nRUN echo hello world|dockerbuild -dockerbuild -EOF FROM busybox RUN echo hello world EOF您可以使用首选方法或最适合您的用例的方法替换示例。1.3.1 使用来自 STDIN 的 DOCKERFILE 构建镜像而无需发送构建上下文使用此语法可使用写入stdin的Dockerfile来构建映像而无需发送其他文件作为构建上下文。连字符-占据PATH的位置并指示Docker从stdin而不是目录中读取构建上下文仅包含Dockerfiledocker build [OPTIONS] -以下示例通过stdin发送Dockerfile的来构建镜像。没有文件作为构建上下文发送到守护进程。dockerbuild-tmyimage:latest -EOF FROM busybox RUN echo hello world EOF在Dockerfile不需要将文件复制到镜像中的情况下省略构建上下文可能很有用并且由于没有文件发送到守护进程因此可以提高构建速度。如果要通过从构建上下文中排除某些文件来提高构建速度请使用.dockerignore进行排除。注意如果使用此语法使用COPY或ADD的Dockerfile构建将失败。以下示例说明了这一点# create a directory to work inmkdirexamplecdexample# create an example filetouchsomefile.txtdockerbuild-tmyimage:latest -EOF FROM busybox COPY somefile.txt . RUN cat /somefile.txt EOF# observe that the build fails ... Step 2/3 : COPY somefile.txt . COPY failed: stat /var/lib/docker/tmp/docker-builder249218248/somefile.txt: no such file or directory1.3.2 从本地的构建上下文构建使用 STDIN 发送 DOCKERFILE使用此语法可使用本地文件系统上的文件并使用stdin输入Dockerfile来构建镜像。该语法使用-f或--file选项来指定可用的Dockerfile使用连字符-作为文件名指示Docker从stdin读取Dockerfiledocker build [OPTIONS] -f- PATH下面的示例使用当前目录.作为构建上下文并使用一个stdin传送的Dockerfile构建一个镜像# create a directory to work inmkdirexamplecdexample# create an example filetouchsomefile.txt# build an image using the current directory as context, and a Dockerfile passed through stdindockerbuild-tmyimage:latest -f-.EOF FROM busybox COPY somefile.txt . RUN cat /somefile.txt EOF1.3.3 从远程的构建上下文构建使用 STDIN 发送 DOCKERFILE用这种语法使用远程git仓库中的文件来构建镜像并且这种情形直接使用stdin发送Dockerfile。该语法使用-f或--file选项来指定可用的Dockerfile使用连字符-作为文件名指示Docker从stdin读取Dockerfiledocker build [OPTIONS] -f- PATH这种语法从一个不包含Dockerfile的远程仓库来构建镜像在一些情形下是非常有用的。或者你想使用一个自定义的Dockerfile构建而不用维持fork这个仓库。下面的例子使用一个stdin写入的Dockerfille构建一个镜像并且从Github的远程仓库添加hello.c文件。dockerbuild-tmyimage:latest -f- https://github.com/docker-library/hello-world.gitEOF FROM busybox COPY hello.c . EOF写在下面当使用远程Git存储库作为构建上下文构建映像时Docker会本地机器上git clone一个仓库并将这些文件作为构建上下文发送到守护进程。在这个特性中当你运行docker build时需要Docker主机安装git。2. 用 .dockerignore 排除要排除与构建无关的文件无需重组源存储库请使用.dockerignore文件。该文件支持类似于.gitignore文件的排除模式。有关创建一个的信息请参见 .dockerignore文件。3. 使用多阶段构建多阶段构建使您可以大幅度减小最终镜像的大小而不必努力减少中间层和文件的数量。由于镜像是在生成过程的最后阶段生成的因此可以利用生成缓存来最小化镜像层。例如如果您的构建包含多个层则可以将它们从更改频率较低以确保生成缓存可重用到更改频率较高的顺序排序安装构建应用程序所需的工具安装或更新库依赖项生成您的申请Go应用程序的Dockerfile可能类似于FROM golang:1.11-alpine AS build # Install tools required for project # Run docker build --no-cache . to update dependencies RUN apk add --no-cache git RUN 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 updated COPY Gopkg.lock Gopkg.toml /go/src/project/ WORKDIR /go/src/project/ # Install library dependencies RUN dep ensure -vendor-only # Copy the entire project and build it # This layer is rebuilt when a file changes in the project directory COPY . /go/src/project/ RUN go build -o /bin/project # This results in a single layer image FROM scratch COPY --frombuild /bin/project /bin/project ENTRYPOINT [/bin/project] CMD [--help]4. 不要安装不必要的软件包为了降低复杂性依赖性文件大小和构建时间请避免仅仅因为它们“很容易安装”而安装多余或不必要的软件包。例如您不需要在数据库镜像中包括文本编辑器。5. 解耦应用程序每个容器应该只有一个方面。将应用程序解耦到多个容器中可以更轻松地水平缩放和重复使用容器。例如一个Web应用程序堆栈可能由三个单独的容器组成每个容器都有自己的唯一镜像以分离的方式管理Web应用程序数据库和内存中的缓存。将每个容器限制为一个进程是一个很好的经验法则但这并不是一成不变的规则。例如不仅可以使用初始化进程来生成容器 而且某些程序还可以自行生成其他进程。例如Celery可以产生多个工作进程而Apache可以为每个请求创建一个进程。根据您的最佳判断使容器尽可能保持整洁和模块化。如果容器相互依赖则可以使用Docker容器网络来确保这些容器可以通信。6. 最小化层数在较旧的Docker版本中务必最小化镜像中的层数以确保其性能。添加了以下功能来减少此限制只有指令RUNCOPYADD创建层。其他指令创建临时的中间镜像并且不会增加构建的大小。尽可能使用多阶段构建并且仅将所需的工件复制到最终镜像中。这使您可以在中间构建阶段中包含工具和调试信息而无需增加最终镜像的大小。7. 排序多行参数只要有可能就可以通过字母数字排序多行参数来简化以后的更改。这有助于避免软件包重复并使列表更易于更新。这也使PRs(官网直译我也不知道具体含义)易于阅读和查看。在反斜杠\之前添加空格也有帮助。下面是来自一个示例buildpack-deps镜像RUN apt-get update apt-get install -y \ bzr \ cvs \ git \ mercurial \ subversion8. 利用构建缓存在构建镜像时Docker将逐步执行Dockerfile的指令 并按指定的顺序执行每个指令。在检查每条指令时Docker会在其缓存中查找可重用的现有镜像而不是创建新的重复的镜像。如果根本不想使用缓存则命令docker build上可以使用--no-cachetrue选项。但是如果您确实允许Docker使用其缓存那么了解何时可以找到匹配的镜像非常重要。Docker遵循的基本规则概述如下从已在缓存中的父镜像开始将下一条指令与从该基本镜像派生的所有子镜像进行比较以查看是否其中一个是使用完全相同的指令构建的。如果不是则高速缓存无效。在大多数情况下只需将Dockerfile中的指令与子镜像之一进行比较就足够了。但是某些说明需要更多的检查和解释。对于ADD和COPY指令将检查镜像中文件的内容并为每个文件计算一个校验和。在这些校验和中不考虑文件的最后修改时间和最后访问时间。在高速缓存查找期间将校验和与现有映像中的校验和进行比较。如果文件中的任何内容例如内容和元数据发生了更改则缓存将无效。除了ADD和COPY命令之外缓存检查不会查看容器中的文件来确定缓存是否匹配。例如在处理RUN apt-get -y update命令时不检查容器中更新的文件以确定是否存在缓存命中。在这种情况下仅使用命令字符串本身来查找匹配项。一旦缓存无效所有后续Dockerfile命令都会生成新镜像并且不使用缓存。9. Dockerfile指令这些建议旨在帮助您创建高效且可维护的Dockerfile。9.1 FROMDockerfile的FROM指令参考尽可能使用当前的官方镜像作为镜像的基础。我们建议使用Alpine镜像小而完整的Linux镜像因为它受到严格控制且尺寸较小当前小于5 MB同时仍是完整的Linux发行版。格式FROM [--platformplatform] image [AS name]或者FROM [--platformplatform] image[:tag] [AS name]或者FROM [--platformplatform] image[digest] [AS name]该FROM指令初始化一个新的构建阶段并为后续指令设置基本镜像。因此有效的Dockerfile必须从FROM指令开始。该镜像可以是任何有效的镜像——源自于公共库的容易pull的。在Dockerfile中ARG是仅先于FROM的指令ARG可以写在FROM前面ARG CODE_VERSIONlatest FROM base:${CODE_VERSION} CMD /code/run-app FROM extras:${CODE_VERSION} CMD /code/run-extrasFROM可以在一个Dockerfile红出现多次实现多阶段构建。每一个阶段可是使用AS name起名字后续可以使用FROM和COPY --fromname|index引用。tag和digest摘要就是镜像使用摘要算法生成的那一长串字符是可选的忽略他们会被赋予latest9.2 LABEL了解对象标签您可以在镜像上添加标签以帮助按项目组织镜像记录许可信息帮助自动化或其他原因。对于每个标签添加一行LABEL并以一个或多个键值对开头。以下示例显示了不同的可接受格式。内嵌包含解释性注释。带空格的字符串必须用引号引起来否则必须转义。内引号也必须转义。# Set one or more individual labels LABEL com.example.version0.0.1-beta LABEL vendor1ACME Incorporated LABEL vendor2ZENITH\ Incorporated LABEL com.example.release-date2015-02-12 LABEL com.example.version.is-production一个镜像可以有多个标签。在Docker 1.10之前建议将所有标签合并为一条LABEL指令以防止创建额外的层。现在不再需要此操作但仍支持组合标签。# Set multiple labels on one line LABEL com.example.version0.0.1-beta com.example.release-date2015-02-12上面也可以写成# Set multiple labels at once, using line-continuation characters to break long lines LABEL vendorACME\ Incorporated \ com.example.is-beta \ com.example.is-production \ com.example.version0.0.1-beta \ com.example.release-date2015-02-12请参阅了解对象标签 以获取有关可接受的标签键和值的准则。有关查询标签的信息请参阅“ 管理对象上的标签”中与过滤有关的项目 。另请参阅Dockerfile参考中的LABEL。9.3 RUNRUN指令的Dockerfile参考将长的或复杂的RUN语句用反斜杠分隔分割成多行以使您Dockerfile更具可读性可理解性和可维护性。APT-GET可能最常见的RUN用例是的应用apt-get。因为用它安装软件包所以该RUN apt-get命令需要注意一些陷阱。不要使用RUN apt-get upgrade或dist-upgrade因为许多来自父映像的“基本”程序包无法在无特权的容器内升级。如果基础镜像中的某个包过时了你应该联系它的维护者。如果你确定某个特定的包比如foo需要升级使用apt-get install -y foo就行该指令会自动升级 foo 包。永远记住将 RUN apt-get update 和 apt-get install 组合成⼀条 RUN 语句例如RUN apt-get update apt-get install -y \ package-bar \ package-baz \ package-foo在RUN语句中apt-get update单独使用会导致缓存问题并且后续apt-get install指令会失败。例如假设您有一个DockerfileFROM ubuntu:18.04 RUN apt-get update RUN apt-get install -y curl构建镜像后所有层都在Docker缓存中。假设您以后通过修改apt-get install添加额外的程序包FROM ubuntu:18.04 RUN apt-get update RUN apt-get install -y curl nginxDocker将初始指令和修改后的指令视为相同并重复使用先前步骤中的缓存。其结果是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-foo1.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 \ s3cmd1.1.* \ rm -rf /var/lib/apt/lists/*该s3cmd参数指定版本1.1.*。如果映像先前使用的是旧版本则指定新版本会导致apt-get update的缓存失效并确保安装新版本。在每行上列出软件包还可以防止软件包重复错误。另外当你通过移除/var/lib/apt/lists来清理apt缓存从而减小了镜像大小。由于该 RUN语句开头为apt-get update因此在apt-get install之前包缓存始终会被刷新。官方Debian和Ubuntu镜像会自动运行apt-get clean因此不需要显式调用。USING PIPES某些RUN命令将一个命令的输出管道传输到另一个命令的能力取决于使用管道字符|如以下示例所示RUN wget -O - https://some.site | wc -l /numberDocker使用/bin/sh -c解释器执行这些命令该解释器仅评估管道中最后一个操作的退出代码以确定成功。在上面的示例中只要wc -l命令成功即使该wget命令失败该构建步骤也会成功并生成一个新镜像。如果您希望由于管道中的任何阶段的错误导致命令失败请添加前缀set -o pipefail 以确保意外错误可以防止构建意外成功。例如RUN set -o pipefail wget -O - https://some.site | wc -l /number并非所有的shell程序都支持该-o pipefail选项。在诸如dash基于Debian的镜像上的shell之类的情况下请考虑使用exec形式的RUN显式选择确实支持该pipefail选项的shell 。例如RUN [/bin/bash, -c, set -o pipefail wget -O - https://some.site | wc -l /number]9.4 CMDCMD指令的Dockerfile参考CMD指令应该结合参数用于运行镜像内的软件。CMD几乎总是以的形式CMD [executable, param1, param2…]使用。因此如果镜像用于服务例如Apache和Rails则将运行CMD [apache2,-DFOREGROUND]。实际上对于任何基于服务的镜像都建议使用这种形式的指令。多数情况下CMD 都需要⼀个交互式的 shell (bash, Python, perl 等)例如CMD [perl, -de0]或者CMD [PHP, -a]。使用这种形式意味着当你执行类似docker run -it python时你会进⼊⼀个准备好的 shell 中。CMD在极少的情况下才会以CMD [param, param]形式与ENTRYPOINT协同使用除非你和你的镜像使用者都对ENTRYPOINT的工作方式十分熟悉。9.5 ENTRYPOINTENTRYPOINT指令的Dockerfile参考ENTRYPOINT最好的用法是设置镜像的主命令从而使该镜像像该命令一样运行然后CMD用作默认标志。让我们从命令行工具的镜像示例开始s3cmdENTRYPOINT [s3cmd] CMD [--help]现在可以像这样运行镜像以显示命令的帮助$ docker run s3cmd或使用正确的参数执行命令$ docker run s3cmd ls s3://mybucket这很有用因为镜像名称可以用作对二进制文件的引用如上面的命令所示。该ENTRYPOINT指令也可以与辅助脚本结合使用即使启动该工具可能需要一个以上的步骤也允许它和上述脚本有一样的功能。例如Postgres Official Image 使用以下脚本作为其ENTRYPOINT#!/bin/bashset-eif[$1postgres];thenchown-Rpostgres$PGDATAif[-z$(ls-A$PGDATA)];thengosu postgres initdbfiexecgosu postgres$fiexec$将应用程序配置为PID 1此脚本使用的exec bash命令 以使最终运行的应用程序成为容器的PID 1.这允许应用程序接收发送到所述容器任何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 bashCMD与ENTRYPOINT区别菜鸟教程演示CMD和ENTRYPOINT的使用9.6 EXPOSEDockerfile的EXPOSE指令参考该EXPOSE指令指示容器在其上侦听连接的端口。因此应为应用程序使用通用的传统端口。例如包含Apache Web服务器的镜像将使用EXPOSE 80而包含MongoDB的镜像将使用EXPOSE 27017等等。对于外部访问您的用户可以执行docker run一个标志指示如何将指定端口映射到他们选择的端口。对于容器链接Docker为从接收者容器到源容器即MYSQL_PORT_3306_TCP的路径提供了环境变量。9.7 ENVENV指令的Dockerfile参考为了使新软件更易于运行可以使用ENV更新容器所安装软件的PATH环境变量。例如ENV PATH /usr/local/nginx/bin:$PATH确保其CMD [nginx]正常工作。该ENV指令提供指定服务必需环境变量例如Postgres’sPGDATA。最后ENV还可以用于设置常用的版本号以便更容易维护版本如以下示例所示ENV PG_MAJOR 9.3 ENV PG_VERSION 9.3.4 RUN 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命令一样。这意味着即使您在以后的层中取消unset设置环境变量它也仍将保留在该层中并且其值也无法转储。您可以通过创建如下所示的Dockerfile然后对其进行构建test 执行test。FROM alpine ENV ADMIN_USERmark RUN echo $ADMIN_USER ./mark RUN unset ADMIN_USER$ docker run --rm test sh -c echo $ADMIN_USER mark为避免这种情况并真正取消设置环境变量请在RUN命令行中使用带有shell命令的命令来设置使用和取消设置环境变量。您可以使用;或分隔命令。如果您使用第二种方法并且其中一个命令失败则命令docker build也将失败这通常是个好主意。使用\作为Linux的Dockerfiles续行符提高可读性。您也可以将所有命令放入一个Shell脚本中并让该RUN命令仅运行该Shell脚本。FROM alpine RUN export ADMIN_USERmark \ echo $ADMIN_USER ./mark \ unset ADMIN_USER CMD sh$ docker run --rm test sh -c echo $ADMIN_USER9.8 ADD或COPY用于ADD指令的Dockerfile参考COPY指令的Dockerfile参考尽管ADD和COPY在功能上相似但是一般来说COPY是优选的。那是因为它比ADD透明。COPY仅支持将本地文件复制到容器中而ADD具有一些功能例如本地tar提取和远程URL支持并不立即显而易见。因此ADD最好的用途是将本地tar文件自动提取到镜像中如中所示ADD rootfs.tar.xz /。如果您有多个Dockerfile步骤使用了上下文中的不同文件COPY则应单独执行而不是一次执行。这样可以确保仅在特别需要的文件发生更改的情况下使每个步骤的构建缓存无效强制重新运行该步骤。例如COPY requirements.txt /tmp/ RUN pip install --requirement /tmp/requirements.txt COPY . /tmp/相较于把COPY . /tem/放到RUN之前这种会导致RUN步骤更少的缓存校验。由于镜像大小很重要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/things RUN 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对于不需要tar自动提取功能的其他项文件目录应始终使用COPY。9.9 VOLUMEVOLUME指令的Dockerfile参考该VOLUME指令应用于指明由Docker容器创建的任何数据库存储区、配置存储或文件/文件夹。对于镜像任何可变的和user-serviceable部分强烈推荐你使用VOLUME9.10 USERUSER指令的Dockerfile参考如果服务可以在没有特权的情况下运行请使USER更改为非root用户。开始之前通过RUN groupadd -r postgres useradd --no-log-init -r -g postgres postgres方式在Dockerfile中创建用户与组。考虑一个明确的UID / GID镜像中的用户和组被分配了不确定的UID / GID因为无论镜像如何重建都会分配“下一个” UID / GID。因此如果很关键则应分配一个明确的UID / GID。由于Go archive/tar软件包处理稀疏文件中的一个未解决的bug尝试在Docker容器内创建具有非常大的UID的用户可能会导致磁盘耗尽因为/var/log/faillog在容器层中填充了NULL\ 0字符。一种解决方法是将--no-log-init标志传递给useradd。Debian / Ubuntuadduser包装器不支持此标志。避免安装或使用sudo它具有不可预测的TTY和信号转发行为这可能会导致问题。如果您绝对需要sudo类似的功能例如将守护程序初始化为root但以非root运行方式运行请考虑使用“ gosu”。最后为了减少层次和复杂性请避免USER频繁地来回切换。9.11 WORKDIRWORKDIR指令的Dockerfile参考为了清楚和可靠起见您应始终使用绝对路径的WORKDIR。另外您应该使用WORKDIR而不是像那样RUN cd … do-something繁琐的指令这些指令难以阅读排除故障和维护。9.12 ONBUILDONBUILD指令Dockerfile参考一个ONBUILD命令将在Dockerfile构建完成后执行。 ONBUILD当前镜像的任何子镜像中执行。将该ONBUILD命令视为父Dockerfile给予子Dockerfile的指令。Docker会在任何子Dockerfile指令前构建执行ONBUILD命令ONBUILD从一个给定的镜像打镜像是有用的。For example, you would useONBUILDfor a language stack image that builds arbitrary user software written in that language within the Dockerfile, as you can see in Ruby’sONBUILDvariants.使用ONBUILD生成的镜像应获得一个单独的标签例如ruby:1.9-onbuild或ruby:2.0-onbuild。这ONBUILD中放入ADD或COPY要小心。如果新构建的上下文缺少要添加的资源则“ onbuild”映像将灾难性地失败。如上所述添加一个单独的标签可以允许Dockerfile作者做出选择从而缓解这种情况。
企业数字化 ERP 产品动态
相关推荐
3个坑让做宣传册模板的网站被黑,建站报价里藏着救命方案 3个坑让做宣传册模板的网站被黑,建站报价里藏着救命方案 改个需求建站公司拖一周?别光抱怨效率,先查查你用的 做宣传册模板的网站 底层代码是不是裸奔。很多老板只盯着 建站报价… · 2026/9/27 21:06:44
枣庄网站制作费用避坑指南:5个细节决定性价比 枣庄网站制作费用避坑指南:5个细节决定性价比 很多老板一上来就问:枣庄做网站到底多少钱?别急,先看看你现在的站点。是不是那种拖个模板就能生成的页面?看着花里胡哨,实际打开慢得像蜗牛,手机端排版还是乱的,客户点两下就跑了。… · 2026/9/27 21:06:44
别再乱找十大网站app排行榜,图解步骤搞定备案与上线 别再乱找十大网站app排行榜,图解步骤搞定备案与上线 备案流程一头雾水?别慌。很多后端初学者在搞定代码后,卡在“网站怎么合法展示”这一步,对着工信部后台发呆。今天咱们不谈虚的,直接拆解一个真实项目:如何从0到1搭建一个… · 2026/9/27 21:06:38
Open Interpreter 安装实战:从零到卸载的完整路线图(4 个场景一次讲清) Open Interpreter 安装实战:从零到卸载的完整路线图(4 个场景一次讲清) 【免费下载链接】openinterpreter A coding agent for open models like Kimi K3 and GLM 5.3 项目地址: https://gitcode.com/GitHub_Trending/op/openinterpreter … · 2026/9/27 21:29:08
毕业生必备:9款免费AI论文写作工具,一键生成开题报告与论文大纲(附TaoToken统一Key配置) /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 21:29:08
openclaw 本地部署实战:网关启动 + 本地模型接入完整步骤(TaoToken 配置版) /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 21:29:08
5个坑教你搞定婚介网站建设的策划最佳实践 5个坑教你搞定婚介网站建设的策划最佳实践 域名选错、服务器配置混乱,这是新手做婚介站最容易翻车的两个点。很多老板花几万块做完网站,打开速度比蜗牛还慢,甚至直接打不开,根本原因就在这。搞懂这俩,才是婚介网站建设的策划里的 最佳实践 。… · 2026/9/27 21:29:02
Humanizer OnDate 流式日期访问器详解:用 DateOnly 优雅表达“某月某日“ 开发工具 【免费下载链接】Humanizer Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities 项目地址: https://gitcode.com/gh_mirrors/hu/Humanizer 点击查看 免费下载 Human… · 2026/9/27 21:29:02
2014-2024年 上市公司知识组合能力KCA数据 +代码 1、数据介绍
知识组合能力(Knowledge Combination Ability, KCA)是创新管理领域刻画企业技术融合水平的核心指标,用于衡量企业在研发过程中对异质性知识要素进行整合、重构与跨界应用的效率。在技术融合日趋深化的背景下,企业创新… · 2026/9/27 21:29:02
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01