开发流程
作为开发人员,我们希望将开发环境与生产环境尽可能地匹配,以确保我们构建的内容在部署时能够正常工作。
我们还希望能够快速开发,这意味着我们希望构建速度要快,也希望可以使用调试器之类的开发工具。容器是整理我们的开发环境的一种好方法,但是我们需要正确定义 Dockerfile 以便能够与我们的容器快速交互。
增量构建
Dockerfile 是用于构建容器镜像的一个声明清单。Docker 构建器将每个步骤的结果作为镜像层进行缓存的同时,缓存可能会无效,从而导致使缓存无效的步骤以及所有后续步骤都需要重新运行,并重新生成相应的层。
当 COPY
或 ADD
引用构建上下文中的文件发生变化时,缓存会失效。所以构建步骤的顺序可能会对构建的性能产生非常大的影响。 让我们看一个在 Dockerfile 中构建 NodeJs 项目的示例。在这个项目中,在 package.json 文件中指定了一些依赖项,这些依赖项是在运行 npm ci
命令时获取的。
最简单的 Dockerfile 文件如下所示:
代码语言:javascript复制FROM node:lts
ENV CI=true
ENV PORT=3000
WORKDIR /code
COPY . /code
RUN npm ci
CMD [ "npm", "start" ]
复制代码
每当构建上下文中的文件发生变化时,我们按照上述结构构建 Dockerfile 都会导致在 COPY 这一行使得缓存失效。也就是说除了会花费很长时间得 package.json 文件以外的其他任何文件发生了变更得话,都将会重新获取依赖项放置到 node_modules 目录下面去。
为了避免这种情况发送,只在依赖项发生变更时(即,当 package.json
或 package-lock.json
更改时)才重新获取依赖,我们应该考虑将依赖项安装与应用程序的构建和运行分开。
优化后得 Dockerfile 如下所示:
代码语言:javascript复制FROM node:lts
ENV CI=true
ENV PORT=3000
WORKDIR /code
COPY package.json package-lock.json /code/
RUN npm ci
COPY src /code/src
CMD [ "npm", "start" ]
复制代码
使用这种分离的方式,如果 package.json
或 package-lock.json
文件没有变更,则缓存将用于 RUN npm ci
指令生成的这一层。这意味着,当我们编辑应用程序源代码并进行重建时,就不会重新下载依赖项,从而节省了很多时间