2

我刚刚完成了我的 Angular 应用程序的基本管道,它在 docker 的 Node 映像中运行。因此,流程如下:推送到 Gitlab > Hook 到 Jenkins 构建 > 部署脚本到docker build镜像并推送到 Quay > 发布脚本以提示 Rancher 服务升级容器并刷新镜像 > 完成。

现在,我遇到的问题是基础节点映像非常大,这意味着当我推送一个简单的更改时,完成构建管道需要很长时间(约 8 分钟)。对于每一个微小的变化,这似乎都是不合理的,推动 Quay 以及随后发布到 Rancher 平台意味着我将大约 250mb 移动到 quay 和 250mb 到 Rancher。

我计划部署几个“微服务”,但是如果每次我想将一个部署到开发环境并每次移动那么多数据,这似乎有点适得其反......我做错了什么,什么我错过了,在构建/部署/托管基于容器的服务时是否有任何最佳实践指南?

4

1 回答 1

9

首先是一些关于图像、构建、注册表和客户端的信息。

图像和图层

Docker 镜像构建与层一起工作。您的每一步都Dockerfile提交一个覆盖在前一个之上的层。

FROM node                   ---- a6b9ffdcf522
RUN apt-get update -y        --- 72886b467bd2
RUN git clone whatever        -- 430615b3487a
RUN npm install                - 4f8ddac8d3b5 mynode:latest

构成图像的每一层都由 sha256 校验和单独标识。IMAGE IDin是其中docker images -a的一小段。

在构建主机上运行dockviz images -t将使您更好地了解可以构建的层树。在构建运行时,您可以看到一个分支增长,然后最终层被标记,但该层保留在树中并保持与其父级的链接。

构建缓存

默认情况下,每个构建步骤都会缓存 Docker 构建。如果RUNdocker 文件中的命令未更改或COPY您正在复制的源文件未更改,则该构建步骤不需要再次运行。该层保持不变,sha256 校验和 ID 和 docker 尝试构建下一层。

当 docker 进入需要重建的步骤时,dockviz 呈现的图像“树”将分支以创建具有新校验和的新层。此后的任何步骤都需要再次运行并在新分支上创建一个层。

登记处

注册管理机构也了解这种分层。如果您只更改新标记图像中的最顶层,那是唯一需要上传到注册表的层(有一些警告,它最适合使用最近的 docker-1.10.1+ 和注册表 2.3+ ) 注册表将已经拥有构成您的新“图像”的大部分图像 ID 的副本,并且只需要发送新图层。

客户

Docker 注册表客户端以相同的方式处理层。拉取图像时,它实际上会下载构成图像的各个层(blob)。docker pull您可以从您或docker run新图像时打印的图像 ID 列表中看到这一点。同样,如果大多数层都相同,那么更新将只需要下载那些已更改的最顶层,从而节省宝贵的时间。

最小化构建时间

所以你要关注的事情是

  • 保持图像尺寸小
  • 利用构建缓存
  • 使用常见的“标记”父图像。

保持图像尺寸小

节省时间的主要方法是首先什么都不做。图像中的数据越少越好。

如果您可以避免使用完整的操作系统,请执行此操作。当您可以在busyboxoralpine映像上运行应用程序时,它会让 Docker 之神微笑。alpine + 一个 node.js 构建小于 50MB。Go 二进制文件也是最小化大小的一个很好的例子。它们可以静态编译,并且没有依赖关系,因此甚至可以在空白scratch图像上运行。

利用 Docker 构建缓存

将最频繁更改的人工制品(很可能是您的代码)作为Dockerfile. 如果构建必须为一个小的文件更改更新完整的 50MB 数据,从而使构建步骤的缓存无效,则构建将变慢。

总会有一些更改使整个缓存无效(例如更新基础node映像)。这些你只需要偶尔忍受一次。

构建中不经常更新的任何其他内容都应该放在Dockerfile.

利用常见的“标记”父图像

尽管从 Docker 1.10 开始,镜像校验和已经得到了一定程度的修复,但使用公共父镜像可以保证您将从相同的共享镜像 ID 开始,无论何时使用该镜像FROM

在 Docker 1.10 之前,镜像 ID 只是一个随机的 uuid。如果您在多个主机上运行构建,则根据构建它们的主机,所有层都可能无效和替换。即使这些层实际上是同一回事。

当您有多个服务和多个Dockerfile大致相同的 s 时,公共父图像也有帮助。每当您开始在多个 s 中重复构建步骤时Dockerfile,将这些步骤拉出到一个公共父映像中,以便在您的所有服务之间绝对共享层。node通过使用图像作为基础,您基本上已经得到了这个。

Node.js 技巧

如果您npm install在代码部署每个构建之后运行,并且您有许多依赖项,那么npm install会导致大量重复工作,而实际上每个构建并没有太大变化。node_modules在代码更改之前建立一个工作流可能是值得的。然后npm install只需要在package.json更新时运行您

FROM node
WORKDIR /app
COPY package.json /app/package.json
RUN npm install && rm -rf ~/.npm
COPY . /app/
CMD [ "node", "/app/server.js" ]

分阶段构建

如果你依赖带有原生模块的 npm 包,你有时需要在容器中安装一个完整的构建链来运行npm install. 分阶段构建现在可以轻松地将构建映像与运行映像分开。

FROM node:8 AS build
WORKDIR /build
RUN apt-get update \
 && apt-get install build-essential;
COPY package.json /build/package.json
RUN npm install; \
 && rm -rf ~/.npm;

# Stage 2 app image
FROM node:8-slim
WORKDIR /app
COPY --from=build /build/node_modules /app/node_modules
COPY . /app/ 
CMD [ "node", "/app/server.js" ]

其他事情

确保您的构建主机具有 ssd 和良好的互联网连接,因为有时必须进行完整的重建,所以越快越好。AWS 通常运行良好,因为您拉取和推送的包和图像也可能托管在 AWS 上。AWS 还提供镜像注册服务 (ECR),仅用于存储成本。

于 2016-05-27T15:41:44.773 回答