Dockerfile快速上手

前言

本文内容是docker官网中的文档,加上自己的理解,创建Dockerfile的入门文章,附上原文链接:Best practices for writing Dockerfiles

Dockerfile的作用

Docker builds images automatically by reading the instructions from a Dockerfile – a text file that contains all commands, in order, needed to build a given image.
A Docker image consists of read-only layers each of which represents a Dockerfile instruction. The layers are stacked and each one is a delta of the changes from the previous layer.
Docker通过读取Dockerfile的指令来自动的构建镜像,Dockerfile包含了构建镜像所需的所有命令,Docker镜像由只读层组成,每个层都代表一个Dockerfile指令,这些层是堆叠的,每个层都是前一层变化的增量。如下Dockerfile:

FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py

每一行命令都是创建一个layer层。

  • FROM 以ubuntu:18.04镜像为基础创建新的镜像
  • COPY 把当前的目录复制到镜像中的app目录下,添加文件
  • RUN 执行make命令编译/app文件夹下的所有文件
  • CMD 运行python命令,即运行/app/app.py该python文件

该Dockerfile是创建了一个运行编写python的简单镜像。
Dockerfile的编写,每一层都是对上一层的修改,写入新文件,修改现有文件和删除文件。

一些参考和建议

创建“短暂”容器(Create ephemeral containers)

The image defined by your Dockerfile should generate containers that are as ephemeral as possible. By “ephemeral”, we mean that the container can be stopped and destroyed, then rebuilt and replaced with an absolute minimum set up and configuration.

定义的Dockerfile应该生成创建出尽可能“短暂”的容器,“短暂”的意思是容器可以被停止和销毁,通过最小化的配置设置来重建和替换。
给出参考:The Twelve-factor App
(这段的意思我理解为,创建出的容器尽量可以自己运行,不需要依赖其他的程序,将应用程序作为一个或多个无状态进程执行,类似于微服务,每个镜像容器都是运行单独的一个进程)

理解构建上下文(Understand Build Context)

When you issue a docker build command, the current working directory is called the build context.

当使用docker build命令时,当前的工作目录被称为build context 构建上下文,默认使用的是当前目录下的Dockerfile文件,可以通过参数-f(file flag)来指定Dockerfile的路径,但是不管Dockerfile的路径如何,构建上下文 build context 都是当前的工作目录。

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

创建文件夹 myproject且cd进入文件夹中,创建hello文件写入"hello"
编写Dockerfile,主要是输出hello文件中的内容。构建简单的镜像
(-t是指定构建的镜像名称,:后的是版本号,若未指定则是latest)

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

将Dockefile移动到新建的目录下,通过-f来指定目录下的Dockerfile来构建镜像

通过stdin管道构建Dockerfile(Pipe Dockerfile through stdin)
echo -e 'FROM busybox\nRUN echo "hello world"' | docker build -
docker build -<<EOF
FROM busybox
RUN echo "hello world"
EOF

两条命令相同,都是通过管道来构建镜像。使用 ‘-’ 来指示docker从stdin获取构建上下文而不是通过Dockerfile文件。

docker build [OPTIONS] -

下方示例,通过stdin的Dockerfile来构建镜像,没有文件被作为构建上下文发送到docker守护进程中。

docker build -t myimage:latest -<<EOF
FROM busybox
RUN echo "hello world"
EOF

注: 使用stdin来构建镜像时,使用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

解决方法也很简单,通过 -f- 就可以传入文件

docker build [OPTIONS] -f- PATH

也可以从远程的 build context 来构建镜像

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

注意: 使用git仓库时,docker会在机器下主动的 git clone 仓库,并且将这些文件作为build context,使用时主机必须已安装git。

使用 .dockerignore

.dockerignore类似于.gitignore,将文件在构建镜像中排除,这些文件不被docker传入镜像内。

使用多段式的构建(Use multi-stage builds)

Multi-stage builds allow you to drastically reduce the size of your final image, without struggling to reduce the number of intermediate layers and files.
多段式的构建会大大减小构建出的镜像大小,并且不需减少中间层和文件的数量。
例:如果构建的容器包含很多层,可以从很少更改的层到频繁更改的层进行排序:

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

例如一个Go的应用程序构建,分成多步骤编写:

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 --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
不安装不必要的包

To reduce complexity, dependencies, file sizes, and build times, avoid installing extra or unnecessary packages just because they might be “nice to have.” For example, you don’t need to include a text editor in a database image.
为了减少复杂性,依赖,文件大小,构建时间,尽量避免安装额外的不必要的包。例如:不必要在数据库的镜像里添加富文本编辑器。

解耦应用 (Decouple applications)

每个容器都应该只解决一个问题,将应用程序分离到多个容器中可以更容易地水平扩展和重用容器。例如,Web应用程序堆栈可能包含三个独立的容器,每个容器都有自己独特的映像,以分离的方式管理Web应用程序,数据库和内存缓存。
(该部分也类似于微服务场景,将容器分开部署。)

层数最小化(Minimize the number of layers)

In older versions of Docker, it was important that you minimized the number of layers in your images to ensure they were performant. The following features were added to reduce this limitation:
在老版本的Docker中,镜像中最小化层数确保性能是非常重要的。通过以下的几点来减少该限制:

  • Only the instructions RUN, COPY, ADD create layers. Other instructions create temporary intermediate images, and do not increase the size of the build.

    只有在RUN COPY ADD命令上创建新的层,其他的命令创建临时中间镜像,并且不会增加构建的大小。

  • Where possible, use multi-stage builds, and only copy the artifacts you need into the final image. This allows you to include tools and debug information in your intermediate build stages without increasing the size of the final image

    在可能的情况下使用多层构建,将仅需的文件复制到最终镜像中,允许您在中间构建阶段中包含工具和调试信息,而不会增加最终镜像的大小。

多行参数排序 (Sort multi-line arguments)

Whenever possible, ease later changes by sorting multi-line arguments alphanumerically. This helps to avoid duplication of packages and make the list much easier to update.

尽可能通过按字母顺序排序多行参数更易于以后的更改。这有助于避免重复包并使列表更容易更新

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion
利用构建缓存(Leverage build cache)

When building an image, Docker steps through the instructions in your Dockerfile, executing each in the order specified. As each instruction is examined, Docker looks for an existing image in its cache that it can reuse, rather than creating a new (duplicate) image.

构建镜像时,Docker会一步一步的执行Dockerfile的命令,检查每条指令时,Docker会在缓存中查找存在的镜像来重用,而不是创建一个新的重复镜像。

If you do not want to use the cache at all, you can use the--no-cache=true option on the docker build command. However, if you do let Docker use its cache, it is important to understand when it can, and cannot, find a matching image.

使用命令docker build --no-cache=true则不使用缓存镜像。
使用缓存时,需要理解何时能否找到匹配的镜像。
Docker的基本规则:

  • Starting with a parent image that is already in the cache, the next instruction is compared against all child images derived from that base image to see if one of them was built using the exact same instruction. If not, the cache is invalidated.

    在缓存里的父镜像开始,将下一条指令与基于该镜像得到的所有子镜像对比,是否使用到了相同的指令构建,否则缓存失效。

  • In most cases, simply comparing the instruction in the Dockerfile with one of the child images is sufficient. However, certain instructions require more examination and explanation.

    在大多数情况下,将Dockerfile其中一个子图像中的指令简单地进行比较就足够了。但是,某些说明需要更多的检查和解释

  • For the ADD and COPY instructions, the contents of the file(s) in the image are examined and a checksum is calculated for each file. The last-modified and last-accessed times of the file(s) are not considered in these checksums. During the cache lookup, the checksum is compared against the checksum in the existing images. If anything has changed in the file(s), such as the contents and metadata, then the cache is invalidated.

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

  • Aside from the ADD and COPY commands, cache checking does not look at the files in the container to determine a cache match. For example, when processing a RUN apt-get -y update command the files updated in the container are not examined to determine if a cache hit exists. In that case just the command string itself is used to find a match.

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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值