在 Docker 容器内部还是外部运行 Grunt / Gulp?

19

我正在尝试确定一个好的构建流程的惯例,用于使用grunt/gulp部署在docker容器中的nodejs应用程序。

我对以下序列感到非常满意:

  • 在容器外使用grunt(或gulp)进行构建
  • 将./dist文件夹添加到容器中
  • 在容器内部运行npm install(带--production标志)

但是在我找到的每个示例中,都采用了不同的方法:

  • 将./src文件夹添加到容器中
  • 在容器内运行npm install(具有开发依赖项)
  • 在容器内运行bower install(如果需要)
  • 在容器内运行grunt(或gulp)

我认为,第一种方法可以生成更轻量和更高效的容器,但是所有现有的示例都使用第二种方法。我是否遗漏了什么?


你能指出几个例子吗? - Adrian Mouat
你是什么意思,Adrian?Dockerfile的例子是什么? - santi
1
是的。为了让我说“是”,我需要额外的字符。 - Adrian Mouat
你能分享一下选项1的Dockerfile吗? - user_mda
3个回答

4
我想建议第三种方法,针对静态生成网站,使用分离的构建镜像。
在这种方法中,你的主要Dockerfile(位于项目根目录中)成为一个构建和开发镜像,基本上做了第二种方法中的所有工作。但是,在运行时你需要覆盖CMD命令,将构建后的dist文件夹打包成dist.tar或类似的文件。
然后,你有另一个文件夹(例如image),其中有一个Dockerfile。该镜像的作用仅是提供dist.tar的内容。因此,我们使用docker cp / dist。然后Dockerfile只安装我们的Web服务器,并添加ADD dist.tar /var/www。
简而言之:
  • 构建builder Docker镜像(这将为您提供一个没有Web服务器的工作环境)。此时,应用程序已经构建完成。我们可以使用grunt serve或其他命令在开发中运行容器,以启动内置的开发服务器。
  • 与其运行服务器,我们覆盖默认命令来打包我们的dist文件夹。类似于tar -cf /dist.tar /myapp/dist
  • 现在,我们有了一个临时容器,其中包含一个/dist.tar文件。使用docker cp <container_id_from_tar_run> /dist.tar ./image/将其复制到我们称为image的实际部署Docker文件夹中。
  • 现在,我们可以使用docker build ./image构建小型Docker镜像,而无需所有开发依赖项。

我喜欢这种方法,因为它仍然是全部Docker。这种方法中的所有命令都是Docker命令,您真的可以缩小最终部署的实际镜像。

如果您想查看这种方法的图像效果,请查看https://github.com/gliderlabs/docker-alpine,该图像使用构建器图像(在builder文件夹中)构建tar.gz文件,然后将其复制到相应的Dockerfile文件夹中。

那个gliderlabs的例子太高级了,我无法挑选出与上述问题相关的部分。我喜欢这个概念,但是这个例子太复杂了,难以理解。 - Scotty

3
我看到的唯一区别是,你可以在第二种方法中复制完整的grunt安装。
使用第一种方法,您依赖于可能在不同环境中以不同方式完成的本地操作。
一个容器应该基于一个易于复制的镜像而不是依赖于包含“所需内容”的主机文件夹(不知道那部分是如何完成的)。
如果grunt映像带来的构建环境开销太大,您可以:
  • create an image "app.tar" dedicated for the installation (I did that for Apache, that I had to recompile, creating a deb package in a shared volume).
    In your case, you can create an archive ('tar') of the app installed.
  • creating a container from a base image, using the volume from that first container

    docker run --it --name=app.inst --volumes-from=app.tar ubuntu untar /shared/path/app.tar
    docker commit app.inst app
    
最终结果是一个包含应用程序文件系统的图像。
这是方法1和2的混合体。

我明白你的观点,但这意味着你必须将每个容器都转换成一个构建环境,最终部署到生产环境中。感觉有点太繁琐了,不是吗?你对在构建过程之后添加一些清理程序有什么看法?从Docker的角度来看,这样做是否高效? - santi
@scarmuega 我同意。我已经编辑了我的答案来解决你的问题。 - VonC
为什么要创建一个app.tar并将其作为Docker运行的要求,而不是构建可部署的Docker镜像,然后将其放入Docker仓库中呢?创建可部署的Docker镜像使您可以从需要它的任何地方轻松拉取和运行镜像,并且它非常稳定。使用这种方法,您的容器也可以在一秒左右启动。 - Darryl
@Darryl 我在下一步中构建可部署的镜像,从一个小的基础镜像开始。如果我直接分发 构建 应用程序的镜像,那么其中将包含太多的开销(因为里面有太多仅用于构建的文件)。 - VonC
在许多 Dockerfile 中,我看到的是添加清理任务以删除不必要的内容,使镜像变得轻巧。 - Darryl
@Darryl 我同意,但是(清理过程)是我在构建时想要避免的:gcc或其他构建工具安装了很多东西,我不想因为一些我可能错过并未删除的文件而产生任何副作用。我更喜欢从一个干净的镜像开始,并在其中安装。 - VonC

1
一种解决方案的变体是有一个“父 -> 子”关系,使项目的构建非常快。我会像这样拥有dockerfile:
FROM node
RUN mkdir app
COPY dist/package.json app/package.json
WORKDIR app
RUN npm install

这将处理节点依赖项的安装,并有另一个Dockerfile来处理应用程序的“安装”,例如:
FROM image-with-dependencies:v1
ENV NODE_ENV=prod
EXPOSE 9001
COPY dist .
ENTRYPOINT ["npm", "start"]

通过这种方式,您可以继续开发,并且构建 Docker 镜像的速度将比需要“重新安装”节点依赖项时更快。如果您在节点上安装了新的依赖项,只需重新构建依赖项镜像即可。

我希望这能帮助到某些人。

敬礼


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接