Docker是一个非常方便的工具,可以帮助实现跨平台的可重复构建和部署。这不仅适用于后端服务,也可以适用于单页应用。
如果静态SPA与其他docker化组件(微服务、数据库、缓存......)一起部署,这可能是有意义的,因为这些系统通常会依赖像kubernetes或其他容器协调工具进行部署和管理。
在这样的设置中,我们可能想把我们的SPA也打包在一个容器中,这样我们就可以使用与其他组件相同的工具。这可以通过使用一些HTTP服务器作为基础镜像并复制静态文件来实现。使用nginx,这可能是这样的。
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
COPY . /usr/share/nginx/html
EXPOSE 80
RUN chown nginx.nginx /usr/share/nginx/html/ -R
我们使用nginx base-image,复制一个自定义的nginx.conf ,把我们的SPA放到/usr/share/nginx/html 。在建立和运行这个配置后,我们将有一个Docker容器为我们的单页应用服务。
问题
到目前为止还不错,但现代的JavaScript应用程序,特别是使用react的时候,有一个复杂的构建管道来创建这些静态文件。 这种增加的复杂性是好是坏不在这篇文章的范围内,但现实是,现在的前端开发人员使用像webpack 、Babel 和许多其他的工具来创建他们的应用程序。
这就带来了一点问题,因为用上面的方法,我们仍然要在容器外构建我们的SPA,并带有build-pipeline所需要的所有依赖关系,这就粉碎了我们对可重复构建的梦想。
解决这个难题的办法是使用一个由nginx、nodeJS和所有其他我们需要的依赖项组成的基础图像,然后在里面构建应用程序。
这种方法,除了是一个丑陋的黑客之外,还有一个缺点,那就是生成的镜像会相当大,因为它包含了所有的构建依赖和人工制品,而这些对于运行应用程序是不必要的。
解决方案
幸运的是,Docker的工程师们在发布17.05 ,为这个问题提出了一个解决方案。他们称之为多阶段构建,其想法是,人们可以在同一个Docker文件中使用几个FROM ,并使用以前的FROM 。
因此,我们可以创建一个构建步骤,它使用FROM node:boron ,并运行我们的构建管道,然后在我们的第二个FROM nginx 部分中引用这个构建步骤的输出。这样,应用程序可以使用Docker构建,并具有跨平台的所有好处,但生成的镜像只包含最后一个FROM 部分中的内容。
一个Dockerfile ,用create-react-app创建的react应用可以是这样的。
FROM node:boron as builder
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app
RUN npm install
COPY . /usr/src/app
RUN npm run build
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
EXPOSE 80
RUN chown nginx.nginx /usr/share/nginx/html/ -R
如果我们在这个配置中使用docker build ,应用程序将使用create-react-app的npm run build 脚本建立,然后使用COPY --from=builder 命令复制到nginx容器中。
由此产生的容器将只包括nginx、我们的静态文件和配置--相当酷啊
顺便说一下,在这个例子中使用react和create-react-app的唯一原因是为了模拟一个有大量依赖关系的过于复杂的构建过程,然而这种方法也适用于任何有构建步骤并创建静态网站作为其输出的技术或框架(例如,像Hugo这样的静态网站生成器,或任何其他JS框架)。
总结
我喜欢Docker用于开发。多阶段构建(Multi-Stage Builds)也可以用于Go(和其他语言),人们可以在第一步构建应用程序,然后将静态二进制文件放在一个非常轻量级的容器中,根据应用程序和语言的不同,产生一个5-6MB的Docker-image。
如果不使用Docker,这种方法就没有什么意义,但在其他服务都使用Docker的情况下,这篇文章中描述的方法可能对自动化有帮助。
我个人(经过2年多的react开发)并不喜欢come-react-app默认的30个开发依赖项所增加的复杂性,我喜欢为我的项目使用更轻量级的设置,但我担心JavaScript框架的转码和整体构建管道会一直存在,Docker在这方面会有很大帮助。