当前位置:   article > 正文

Docker fille实例讲解_dockerfile run cat

dockerfile run cat

概述

这篇文章讲述一些 Dockerfile的 应用知识和建议。Dockerfile 中应该包含构建一个完整镜像需要的操作步骤。
Docker 会读取Dockerfile 的内容,然后自动构建镜像。如果你想了解更详细的内容,你可以阅读官网内容:
https://docs.docker.com/engine/reference/builder/。当然,你可以阅读我自己整理的内容,我将会分享相
关的链接。
注意:由于本人的词汇量有限,部分单词无法完成翻译,可能会直接保留英文。
  • 1
  • 2
  • 3
  • 4
  • 5

第一个 Dockerfile

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
  • 1
  • 2
  • 3
  • 4
  • 5

我们来解读一下 每一行的内容:

#syntax=docker/dockerfile:1
这一行定义一个 解析器语法, 这个定义 和 BuildKit 特性一起使用;
一般情况下,以 # 开始的行,Docker都会认为是注释行。

FROM ubuntu:18.04
Dockerfile第一个实际有效的操作,从 DockerHub 仓库寻找 ubuntu 版本为18.04的镜像,下载这个镜像,以这个镜像作为基础镜像进行构建。
官网给出说明,Dockerfile 第一个有效的构建指令必须是 FROM。

你可以指定下载镜像的远程仓库

  • FROM nexus3.system.cn.alibb:8087/com/alibb/group/itid/es/dc/ubuntu/ubuntu:18.04

COPY ./app
从主机目录 ./app 拷贝所有内容 到 镜像 WORKDIR 中;
一般的使用方法是 COPY <src> <dest>;

  • <src> : 本地机器的目录,它可以是一个绝对路径、具体文件名、WORKDIR相对路径;
    <dest>: 是指容器内部的目录,它可以是一个绝对路径,具体的文件名、也可以相对于 WORKDIR 的相对路径;

例子:

COPY /home/etc/ /home/etc/
COPY /home/etc/passwd /home/etc/passed
COPY …/src/resource/ src/resource 从当前目录的上级目录的 src/resource 拷贝所有内容 到 WORKDIR 目录下的 src/resource 目录中

更多详细的内容,可以阅读一下我整理关于 COPY 操作指引的内容:

RUN make /app

在镜像中执行命令 make /app。RUN 可以执行命令,该命令的生成的结果会立刻在镜像中生效。

CMD python /app/app.py

为正在构建的容器程序设置 容器启动命令。当我们使用 docker run 启动容器的时候,容器就会执行 这个命令。

当你运行一个 image 和 生成一个 container 时,你增加了新的 container layer在最上层。所有改变制定一个运行的 container,例如:创建一个新文件,修改已经存在的文件,和 删除文件,都被写入这个容器层。

关于更多 image layer的内容,请看看:
https://docs.docker.com/storage/storagedriver/

一般使用指南和建议

创建一个 快速容器

我们建议创建一个能在一分钟完成 stop destroy then rebuild and replace 的容易。

理解构建环境

当你运行 docker build 命令时,当前工作目录被称作构建条件(build context)。默认情况下,Dockerfile 被认为存放在当前目录下,但是你可以指定一个不同的位置使用标识 -f 。不管Dockerfile实际在什么位置,所有在当前目录下的文件和子目录内容都被发送到 Docker daemon ,这些内容将作为构建条件(build context)。

Build context example

创建一个目录,使用 cd 命令进入该目录。在目录下创建一个名称为 hello 的 text 文件,并往文件中写入"Hello World"。创建一个 Dockerfile ,在Dockerfile 中定义容器执行cat 命令。构建环境为当前目录。参考下面的指令:

mkdir myproject && cd myproject
echo "hello" > hello
echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
docker build -t helloapp:v1 .
  • 1
  • 2
  • 3
  • 4

移动 Dockerfile 和 hello ,让他们存放在不同的目录。构建一个第二版本的镜像。使用 -f 标识 指定构建Dockerfile 和 构建环境:

mkdir -p dockerfiles context
mv Dockerfile dockerfiles && mv hello context
docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context
  • 1
  • 2
  • 3

我们只希望包含一些必要的文件、库文件、配置文件在构建条件中(build context),其他不重要的内容尽量不放到构建条件中。这样可以提高构建镜像的效率,减小拉取、推送、初始化容器的时间。来看一看你的构建环境的大小,寻找一些跟下面类似的信息在构建的时候:

Sending build context to Docker daemon 187.8MB

Pipe Dockerfile through stdin (标准输入传输Dockerfile)

Docker 提供了一种方案,不需要写一个Dockerfile 文件,也能够使用Dockerfile 的特性来构建docker 镜像。这样的一种构建方式是通过标准输入(stdin) 传输 Dockerfile 的文件内容。在一些简单的一次性构建工作中,使用这种方式工作比较方便。

下面两个例子是等效的:

echo -e 'FROM busybox\nRUN echo "hello world"' | docker build -
  • 1
docker build -<<EOF
FROM busybox
RUN echo "hello world"
EOF
  • 1
  • 2
  • 3
  • 4

构建一个镜像使用 来自标准输入的 Dockerfile 信息,不需要发送构建条件信息
使用这个方法来构建镜像,不需要发送当前目录下的文件作为构建条件。连接符(-)获取位置路径,和指引Docker从标准输入获取构建条件,而不是读取当前目录。

docker build [OPTIONS] -

接下来的例子构建一个镜像,使用标准输入传输的Dockerfile 信息。没有文件作为构建条件发送给 docker daemon。

docker build -t myimage:latest -<<EOF
FROM busybox
RUN echo "hello world"
EOF
  • 1
  • 2
  • 3
  • 4

忽略构建条件是非常有用的,如果你不需要拷贝任何文件到镜像中,提高了构建速度,因为没有文件需要发送到 docker daemon。

如果你想提高构建速度,部分不必要的文件不发送到 docker daemon,可以考虑使用 .dockerignore 功能。

注意:使用这个方法构建镜像,如果构建指令包含 COPY 或者 ADD,构建会失败。下面的例子说明这个问题:

# create a directory to work in
mkdir example
cd example

# create an example file
touch somefile.txt

docker build -t myimage: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 directory
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

Build from a local build context, using a Dockerfile from stdin
使用在本地构建条件构建镜像,使用一个来自标准输入的Dockerfile信息
使用这个语法构建一个镜像,使用你本地文件系统的文件,但是使用一个来自标准输入的Dockerfile。
语法使用 -f (或者 --fille)可选标识,指定 Dockerfile, 使用一个连接符(-)作为文件输入,告知Docker 从标准输入读取Dockerfile信息。

docker build [OPTIONS] -f- PATH
下面的例子使用当前目录 作为构建环境,并通过标准输入传入Dockerfile 信息

# create a directory to work in
mkdir example
cd example

# create an example file
touch somefile.txt

# build an image using the current directory as context, and a Dockerfile passed through stdin
docker build -t myimage:latest -f- . <<EOF
FROM busybox
COPY somefile.txt ./
RUN cat /somefile.txt
EOF
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Build from a remote build context, using a Dockerfile from stdin
使用远程构建条件构建镜像,使用一个标准输入传输的Dockerfile 文件信息

使用这个语法来构建镜像,使用远程仓库的文件,使用标准输入传输的Dockerfile 文件信息。这种语法使用 -f (或 --file) 可选标识来指定 Dockerfile 的使用,使用一个连接符 (-) 告知 Docker 通过标准输入读取Dockerfile信息:

docker build [OPTIONS] -f- PATH
这个预防非常有用,当你想使用远程仓库内容来构建一个镜像,但是不想写Dockerfile,或者如果你想使用客户的Dockerfile 进行构建,不维护你自己的仓库。

下面的例子构建一个镜像,使用来自标准输入的Dockerfile 信息,添加 hello.c 文件从gitHub 远程仓库中。

docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF
FROM busybox
COPY hello.c ./
EOF
  • 1
  • 2
  • 3
  • 4

Under the hood
当使用Git 远程仓库作为构建环境构建镜像时,Docker 执行一个 git clone 命令下载仓库内容到本地机>器,发送相关文件作为构建环境到 docker daemon。这个特性需要 安装 git 。

使用 .dockerignore

对于不用于构建的文件 或者 不进行更新的文件,我们 配置 .dockerignore 文件 达到排除相关文件的效果。这个文件类似于 git 中的 .gitignore 文件。如果了解更多关于 .dockerignore 文件的使用,可以查看我整理的关于 .dockerfile 的资料。

使用多步骤构建

多步骤构建允许你 极大地减小你最终镜像的大小,不需要减小内部层数和文件。
因为一个镜像在最后一个步骤中被构建,你可以最小化镜像的层数。

举一个例子:如果你构建包含多层,你可以将他们排序 按照改变的频率低到高进行排序:

  • 安装构建需要的工具
  • 安装或更新依赖库
  • 生成你的程序

一个Go 程序的 Dockerfile 可能是这样的:

# syntax=docker/dockerfile:1
FROM golang:1.16-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 --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

不安装无关紧要的包

减少复杂性、依赖、文件大小、和构建时间,避开安装额外的或者不重要的包。例如,你不需要包含一个text 编辑器在一个 database 镜像中。

Decouple applications
解耦程序
每一个容器应该只专注做一件事情。解耦程序为多个容器让它更容易衡量和重用。对于实例,一个web 程序可能由三个程序组成,每一个都拥有自己的独立的镜像,去管理web程序,数据库 和 缓存。

限制每一个容器运行一个进程是一个非常好的想法,它不难实现而且很快。举一个例子,不只是init 进程孵化容器,一些项目可以孵化他们自己额外的进程。例如, Celery 可以孵化多个工作进程 和 Apache 每一个请求都会创建一个进程。

尽可能确保你的容器简单。如果容器之间相互依赖,你可以使用 Docker container network 来保证这些容器可以沟通。

最小化layer 数量

在旧的Docker 版本,让你的镜像使用最小的 layer是非常重要的,这样做能够确保他们的性能。接下来的特性可以减小这个限制:

  • 只有Docker 指令 RUN,COPY,ADD 创建 layer。其他Docker 指令 创建临时的临时镜像,不会增加构建大小。

  • 使用多层构建(multi-stage builds),只拷贝你需要的内容到最终的镜像中。你可以在一个中间的镜像中包含工具和调试信息,这样做最终的镜像就可以减少很多内容。

多个参数分行编辑

无论什么时候,按照字母数字整理多行参数能够方便以后修改。这样做让我们回避重复写参数 和 让我们更容易阅读和评审。在 \ 前使用一个空格,方便阅读。

我们来看看一个关于 buildpack-deps 镜像的例子

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  && rm -rf /var/lib/apt/lists/*
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Leverage build cache 构建缓存

在构建一个镜像的时候,Docker 按照 Dockerfile 的操作进行构建。当每一个操作被检查后,Docker 从他的缓存中寻找一个可以被重复使用的镜像,这样做比创建一个新的镜像要快多。

如果你不想使用缓存(cache),你可以使用 --no-cache-true 标识 在docker build 命令中。无论怎样,如果你让Docker 使用cache,你必须清楚知道什么时候可以匹配到一个镜像,什么时候无法匹配到一个镜像。基本原则是:

  • 从已经存在于缓存的父镜像开始,下一步对比所有派生子镜像是不是使用相同的指令生成,如果不是,不重用镜像。

  • 在大多数情况下,简单对比Dockerfile的指令和子镜像的指令是有效的。明确的指令需要更多的测试和解释。

  • 对于 ADD 和 COPY 操作,在镜像中的每一个文件的内容都会被测试且生成一个 checksum。文件的最后修改和最后访问时间不会被考虑在内。在缓存匹配阶段,计算好的checksum 会和存在的镜像checksum 进行比较。如果checksum 不一致,Docker 认为文件内容、metadata 等内容可能被修改,会选择重新创建镜像。

  • ADD 和 COPY 命令的题外话,缓存检查不看容器内的文件来确定缓存是否匹配。举例:当执行一个 RUN apt-get -y update 命令更新容器的文件,这些更新的内容不会被用于匹配缓存容器的条件。在这个例子中,只有命令字符串被用来匹配镜像。

一旦缓存找不到期望的镜像,Dockerfile 操作就会生成一个新的镜像,缓存不会被使用。

Dockerfile instructions

下面的建议可以帮助你快速创建一个有效的、可维护的Dockerfile。

FROM
详细的内容,可以查看我整理关于 FROM 操作的说明

无论什么时候,使用当前办公镜像作为你的基础镜像。我们建议 Alpine 镜像因为它被严格控制大小(当前只有 6MB)。

LABEL
详细内容,可以查看我整理关于 LABEL 操作的说明。

你可以增加标签到你的镜像中,标签信息可以是版本信息、证书内容等等。添加标签的方法,在Dockerfile 一行中,以 LABEL 开始,跟着一个或多个键值对。下面是标签的使用例子:

字符串如果有空格,必须使用双引号包含或者使用转义字符(\)。字符串内的双引号,必须使用转义字符。

# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL vendor2=ZENITH\ Incorporated
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

一个镜像可以有多个标签。在 Docker 1.10 版本之前,我们建议将所有的标签操作写在同一行 LABEL 操作中,防止额外的layer被创建。当然这已经不重要了。

# Set multiple labels on one line
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"
  • 1
  • 2

上面的内容可以被重写为:

# Set multiple labels at once, using line-continuation characters to break long lines
LABEL 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"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

关于 Label 更多的内容,请阅读下面的官网内容:
https://docs.docker.com/config/labels-custom-metadata/
https://docs.docker.com/config/labels-custom-metadata/#manage-labels-on-objects
https://docs.docker.com/engine/reference/builder/#label

RUN
相关的详细信息,请看我整理管理 RUN 的内容
分解复杂的RUN 语句为多行,使用 \ 字符,让你的Dockerfile 的可读性更强、更好理解、更容易维护。

apt-get
在RUN 操作中使用最多的可能就是 apt-get。因为它用来安装包,关于 RUN apt-get 命令有多个重点需要知道。
将 apt-get update 和 apt-get install 组合为一行语句,类似于相面的例子:

RUN apt-get update && apt-get install -y \
  package-bar \
 package-baz \
 package-foo  \
  && rm -rf /var/lib/apt/lists/*
  • 1
  • 2
  • 3
  • 4
  • 5

单独使用 apt-get update 在RUN 语句中会出现缓存问题,后面的apt-get安装操作会失败。
加入我们有一个Dockerfile:

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl
  • 1
  • 2
  • 3
  • 4

在构建镜像后,所有layer在Docker缓存中。然后你修改apt-get install 来增加一个额外的包

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl nginx
  • 1
  • 2
  • 3
  • 4

Dockers 看到初始化和修改操作完全一样,他会使用之前生成、存在缓存的镜像。结果是,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.*
  • 1
  • 2
  • 3
  • 4

Version pinning 强制构建检索一个指定版本 不管缓存中的的内容。这个方法可以减少失败。
下面是一个很好的 RUN 使用范例:

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/*
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

s3cmd 参数指定了版本1.1.*。如果镜像之前使用了一个旧版本,现在就会安装一个新版本。将安装的内容分隔开来,可以更清晰展示。

另外,当你清理 apt 缓存——删除 /var/lib/apt/lists ,减小镜像大小,从apt 缓存不被存储在一个layer。
以 apt-get update 作为 apt 命令的开头,每次 apt-get install 都会安装最新版本的包。
办公版的 Debian 和 Ubuntun 镜像自动运行 apt-get clean ,所以它不需要显示定义。

Using pipes
一些 RUM 命令 使用管道输出的命令,我们来看看下面的例子

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

Docker 执行这些命令使用 /bin/sh -c 解析器 ,这个解析器只判断管道最后一个命令的退出吗来判断是否成功。在上面的语句最终执行成功尽管 wget -O - https:/some.site 执行失败,但是 wc -l>/number 执行成功了,所以docker会认为它是成功的。

如果你希望管道命令在任何一个环节失败,它都要被判断为失败,你可以在之前加上 set -o pipefail。

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

请记住,不是所有 shell 都支持 -o pipefail 这个选项。
例如 Debian-based 镜像的 dash shell,请考虑使用 exec 格式的 RUN 来显示选择一个 shell 支持 pipefail 选项。 具体可以参考下面的例子:

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

CMD
想要看更详细的内容,请查看我整理关于 CMD 的内容
CMD 操作指令是用来指定启动容器运行程序的命令,可以带一些参数。 CMD 基本都使用这样的格
式:CMD[“executable”,“param1”,“param2”…]。因此,如果镜像时一个服务,例如 Apache 和 Rails ,>你将运行一些命令 CMD[“apache2”,"-DFOREGROUND"]。我们建议所有服务类的镜像都使用这个格式>配置 CMD 内容。

在其他情况, CMD 需要明确给出一个交互的 shell ,例如 bash ,python 和 perl。看看下面的例子 :
CMD[“perl”, “-de0”], CMD[“python”],或 CMD[“php”,"-a"]。使用这个格式,意味着当你执行一些命令类似于 : docker run -it python , 你将遇到异常在一个有用的shell。 CMD 应该很少使用 CMD[“param”,“param”] 结合 ENTRYPOINT ,除非你和你的用户已经很懂 ENTRYPOINT 是怎么工作了。

EXPOSE
更详细的信息,请查看我整理的内容
EXPOSE 操作指令指定监听端口。结果,你应该使用 公共的、传统的端口。举例子:一个镜像包含Apache web server 应该使用 EXPOSE 80 ,镜像包含 MongoDB 应该使用 EXPOSE 27017 等等。
对于外部访问,你的用户可以执行 docker run 带一个标识指定一个端口映射关系。For container linking, Docker provides environment variables for the path from the recipient container back to the source (ie, MYSQL_PORT_3306_TCP).

ENV
详细内容,请查看我整理的相关内容
让新的软件更容易运行,你可以使用 ENV 来更新 PATH 环境变量。例如 : ENV PATH=/usr/local/nginx/bin:$PATH 确保 CMD[“nginx”] 能够工作。

ENV 操作指令是非常有用的,它提供一种方法为你的服务设置需要的环境变量。
最后,ENV 可以用来了设置公共的 版本信息,因此 版本信息可以更容易被使用

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
  • 1
  • 2
  • 3
  • 4

类似于拥有了一个静态变量在项目中,这个方案让你改变一个 ENV 操作指令来自动更新你的容器版本信息。
每一个 ENV 行创建一个 新的中间layer,就像 RUN 命令。这意味着 尽管你不设置环境变量在一个未来layger,你仍然维持这一 layer 和 它的值可以被 抛弃。你可以创建一个 Dockerfile 来测试一下:

# syntax=docker/dockerfile:1
FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER

docker run --rm test sh -c 'echo $ADMIN_USER'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

为了防止这种情况,真的不设置环境变量,使用一个 RUN 带shell 命令来设置、使用和无效变量在一个单独的层,你可以使用命令 ; 或者 && 来隔离他们。如果你使用第二种方法,其中一个命令会失败,docker 构建也失败。这样通常是一个好方法。使用 \ 作为一个换行连接符 在 Linux 的 Dockerfiles 提供可读性。你可以把所有的命令都放到一个 shell 脚本,使用一个个RUN 命令来运行这个脚本。

# syntax=docker/dockerfile:1
FROM alpine
RUN export ADMIN_USER="mark" \
  && echo $ADMIN_USER > ./mark \
  && unset ADMIN_USER
CMD sh

docker run --rm test sh -c 'echo $ADMIN_USER'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

ADD or COPY
更详细的资料,请看看我整理的资料
尽管 ADD 和 COPY 的功能类似,一般情况下,我们更希望您使用 COPY 。这是因为COPY 更加透明比 ADD。 COPY 只支持简单的拷贝操作,拷贝本地文件到容器中, ADD 有一些不那么直接、明显的特性。如果你拷贝一个 tar 包到容器中,最好使用 ADD 操作指令,他会自动进行解压操作,好像 ADD rootfs.tar.xz / 。

如果你有多个Dockerfile 步骤,他们使用不同的文件, 分别COPY 他们比一次性全部拷贝他们要好。这样确保每一个步骤的构建缓存是独立的如果指定需要的文件改变。
我们来看看下面的例子:

COPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/
  • 1
  • 2
  • 3

Results in fewer cache invalidations for the RUN step, than if you put the COPY . /tmp/ before it.

因为镜像大小的关系,使用 ADD 去 拿一个远程包是不被赞同的;你应该使用 curl 或 wget 来代替。你可以删除你不在需要的文件在你解压包和你不必须增加另外一个layer 之后。我们来看看下面的例子,你最好不要使用这种方式:

ADD https://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
  • 1
  • 2
  • 3

你可以使用下面的方式来代替:

RUN mkdir -p /usr/src/things \
   && curl -SL https://example.com/big.tar.xz \
   | tar -xJC /usr/src/things \
   && make -C /usr/src/things all
  • 1
  • 2
  • 3
  • 4

对于其他项目,你不需要使用 ADD 自动解压,你应该一直使用 COPY。
For other items (files, directories) that do not require ADD’s tar auto-extraction capability, you should always use COPY.

ENTRYPOINT
详细内容,可以去看看我整理的 CMD 和 ENTRYPOINT 的内容
ENTRYPOINT 主要用途是设置 镜像的主要命令,同时,CMD 设置 默认的标识(flag) 或入参。一般情况下,ENTRYPOINT 和 CMD 这两个操作指令是联合一起使用的。
让我们来看看下面这个例子 :

ENTRYPOINT ["s3cmd"]
CMD ["--help"]
  • 1
  • 2


使用命令 docker run s3cmd 后,镜像内实际执行等效于 : s3cmd --help
或者,我们试试使用正确的入参来执行一个命令:

docker run s3cmd ls s3://mybucket
  • 1

这个操作是很有用的,因为镜像名可以重复作为引用 binary 。
ENTRYPOINT 也可以用于组装一个帮助脚本,允许它的功能了类似于上面的命令,甚至当启动一个工具需要多一步。
我们来看看下面的例子,Postgres 办公镜像使用下面的脚本作为 ENTRYPOINT:

#!/bin/bash
set -e

if [ "$1" = 'postgres' ]; then
  chown -R postgres "$PGDATA"

   if [ -z "$(ls -A "$PGDATA")" ]; then
       gosu postgres initdb
   fi

   exec gosu postgres "$@"
fi

exec "$@"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Configure app as PID 1

这个脚本使用 exec Bash 命令,因此最后运行的程序会成为 容器中PID=1 的进程。容器中 PID=1的进程可以接收来自 Unix操作系统的信号。

这个帮助脚本被拷贝到容器,然后通过 ENTRYPOINT 设置的主命令运行:

COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]
  • 1
  • 2
  • 3

脚本与用户可以通过多种方式进行交互:

  • 可以简单启动 Postgres :docker run postgres
  • 可以被用于运行 Postgres 和 传递参数给服务器:docker run postgres postgres --help
  • 可以被用于启动所有不同的工具:docker run --rm -it postgres bash

VOLUME
详细的内容,可以查看我整理的资料
VOLUME 操作指引应该被用于暴露数据库存储范围,配置存储信息或创建文件/目录。我们强烈建议使用 VOLUME 对那些不稳定的 或 用户服务的部分。

USER
更多详细的内容,请参考我整理的内容
如果一个服务运行不需要权限,使用 USER 使用一个 非 root 用户。创建一个用户和组在Dockerfile,我们可以参考这个语句:RUN groupadd -r postgress &&& useradd --no-log-init -r -g postgress postgress
考虑使用一个 显式的 UID/GID。
用户 和 组 在构建镜像时会被默认注册一个 UID/GID,如果你觉得有必要,你应该显式定义一个 UID/GID。

因为一个未能解决的bug 在 Go 语言 打包和解压文件时,尝试创建一个用户,使用一个非常大的 UID,Docker容器可能导致disk耗尽因为 /var/log/faillog 在container层被填入NULL(\0)字符串。一个临时方案是使用 --no-log-init 标识来创建用户。但是 Debian/Ubuntu 的 adduser 命令不支持这个标识。

不要安装或使用sudo ,因为sudo有不可以预测的TTY 和信号传递行为,为造成问题。如果你必须使用类似sudo的功能,例如使用root 初始化daemon,但是使用non-root运行,可以考虑使用 gosu。
最后,减少layers 和 复杂性,必要频繁使用 USER切换用户。

WORKDIR
详细信息,请查看我整理的资料
为确保清晰可靠,你应该一直使用绝对路径来使用 WORKDIR。同时,你应该使用 WORKDIR 代替指令: RUN cd … && do-something,这种方法可读性差,难以定位问题,可维护性差。

ONBUILD
更多详细的内容,请查看我整理的信息
ONBUILD 指令是将操作延后到子镜像。如果我在dockerfile使用了ONBUILD指令,在构建阶段不会执行。当另外一个Dockerfile引用这个镜像时,这些ONBUILD的操作才会执行。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/57974
推荐阅读
相关标签
  

闽ICP备14008679号