1.引言
前端容器化是一种将前端应用程序打包成容器的技术,使其可以在不同的环境中快速、高效地部署和运行。
2.背景
前后端分离的趋势已形成现状,前端工程复杂度叠加增长,新、老项目部署依赖的环境和Node.js版本会存在差异,生产环境下构建混淆后的脚本、静态资源文件依赖环境部署服务进行访问,前端工程未能形成"单体工件"部署,容器的出现大大简化了部署流程。
前端容器化可以方便的管理前端环境变量注入、运行环境(不同项目依赖不同的node环境,node的版本兼容是个很大的问题)、节约服务器成本、更快捷方便的版本回滚、多架构部署、CI/CD自动化集成部署、DevOps等等,好处只有多到你想不到(此处手动偷笑)。
本文基于React项目结合Docker,分享在前端引入容器技术带来的变革。
3.容器化在github的运用
github推出了github-action来做容器化的ci/cd,我下面展示用github-action做一个npm自动化发包的示例:
- 在项目根目录下新建.github/workflows/ci.yml文件
- 去npm官网申请一个token(具体怎么去申请,请自己去搜索解决)
- 将这段代码贴入ci.yml文件
- push代码到master分支,就会自动走ci/cd进行部署啦!
4.基于docker构建前端镜像
在学习前端项目ci/cd构建之前,让我们先学习下前端镜像怎么构建
4.1 安装docker
点此处坐飞机去安装docker安装完成后执行以下命令查看docker版本,尽量带buildx的版本
4.2 编写Dockerfile
这里先需要普及一个前端工程知识,我们都知道一个基于npm的项目,需要一个package.json文件,然后执行npm run install下载包,npm run build打包,打包出来的文件其实是不能直接运行的,需要启动一个node服务运行,所以我们就写一个最基本的基于node和nginx的镜像,示例如下
在项目根目录下添加nginx配置文件,取名为nginx.conf,内容如下
在项目根目录下添加docker配置文件,取名为Dockerfile,内容如下
接下来使用docker build打包镜像(如果有桌面工具,打包成功后docker桌面工具的images栏目能看到), docker run执行镜像(如果有桌面工具,运行成功后docker桌面工具的containers栏目能看到), docker run运行成功后可以打开浏览器输入:http://localhost 进行查看
4.3 如何基于Dockerfile做pnpm缓存
这里我引用一段话: 使用多阶段构建,构建的镜像中只包含了目标文件夹 dist,但仍然存在一些问题,当 package.json 文件变动时,RUN npm i && rm -rf ~/.npm 这一层会重新执行,变更多次后,生成了大量的中间层镜像。
为解决这个问题,进一步的我们可以设想一个类似 数据卷 的功能,在镜像构建时把 node_modules 文件夹挂载上去,在构建完成后,这个 node_modules 文件夹会自动卸载,实际的镜像中并不包含 node_modules 这个文件夹,这样我们就省去了每次获取依赖的时间,大大增加了镜像构建效率,同时也避免了生成了大量的中间层镜像。
此处课代表总结一下:就是尽量减少中间层镜像的可能,最小化docker映像大小和构建时间
由于我使用的是pnpm进行npm包管理,所以我去翻阅了pnpm官方文档关于此优化如下:
于是本着依葫芦画瓢的精神,还有使用生产化的nginx配置,我找了同事写的nginx包装镜像,同样你可以执行docker build、docker run进行验证,然后我改造后的代码如下:
4.4 如何利用buildx制作多架构镜像
docker buildx的工具,说白了就是给你提供一个能力,当你的宿主机是x86 64的架构时,你想构建镜像为ARM64的架构,就需要这个工具,给人的感觉有点类似交叉编译,诸如:go build的交叉编译,在win10下编译可执行程序,可用于特定linux平台
buildx本质上调用了 buildkit 的 api,构建是在 buildkit 的环境中进行的。 是否支持多架构,取决于 buildkit 的环境,如果需要 buildkit支持多架构,需要在宿主机执行(当然这个不是必须的,按构建的需求进行控制,Docker桌面版无需进行此项设置):
我们这里改造上面Dockerfile代码,使它支持多架构,由于platform是实验性质,所以需要先执行docker pull docker/dockerfile 拉取镜像
在执行打包镜像命令之前,我们先查看下我们机器默认的 builder 实例
使用buildx执行上面的脚本打包镜像会报错如下
由于 Docker 默认的 builder 实例不支持同时指定多个 --platform,我们必须首先创建一个新的 builder 实例。同时由于国内拉取镜像较缓慢,我们可以使用配置了镜像加速地址的 dockerpracticesig/buildkit:master 镜像替换官方镜像。
如果你有私有的镜像加速器,可以基于 https://github.com/docker-practice/buildx 构建自己的 buildkit 镜像并使用它。
我们选择适用于国内环境的方案的命令进行创建, 可以看到多了name为 mybuilder-cn 的实例
在不同架构运行该镜像,可以得到该架构的信息。
5.如何利用容器化做前端环境变量注入
- 前端虽然对环境变量的需求场景不多,但是基本的api baseURL, appName, env这些是需要的
- 如果是微前端的场景,那么需要的其他网站url都是环境变量,还挺多的
- 依稀记得刚入行时,前端区分测试环境,线上环境就是直接通过域名进行判断,例如: inlcudes(url, ".com")?, 然后得到isProd去获取项目中配置的不同环境的变量,这样显得很low
- 后面出了vue, react这种框架,可以在npm run dev时候指明--prod,然后通过process拿到isProd,去获取对应配置
- 现在可以直接通过容器化注入环境变量,然后使用nginx将容器中的环境变量注入前端项目html的meta标签content中, 然后从meta标签获取变量
- 如果是monorepo项目,npm run build的时候,Dockerfile里面也需要通过容器中的环境变量获取打包哪个项目
- 测试环境通过ts文件配置环境变量,然后项目启动时组合这些环境变量信息,生成config default.yml, ci/cd时k8s自动将default.yml中配置的环境变量写入容器中
- 线上环境直接提供UI页面配置环境变量,然后调用api,后端api通过k8s将变量写入容器中
- 如何通过k8s读取配置的环境变量并写入容器中且听下回分解
下面贴一段生产场景下的Dockerfile示例代码(公司应该不会砍我吧!),这里省略k8s如何往容器中注入环境变量,只考虑如何从容器中读取环境变量(假设环境变量已经注入容器中)
下面贴一段nginx如何将环境变量写入html中
下面贴一段前端如何从html meta标签中读取环境变量
最后的最后,都贴了Dockerfile了,那就一不做二不休,贴个真实场景下的ci文件源码吧!!!
6.前端应用的Kubernetes部署
Kubernetes是一个开源的容器编排平台,可以实现自动化部署、扩展和管理容器化应用程序。以下是将前端应用部署到Kubernetes集群的步骤:
6.1 创建Deployment
6.2 创建Service
6.3 部署到Kubernetes集群
使用kubectl命令部署应用到Kubernetes集群:
现在,您的前端应用已经在Kubernetes集群中运行,并可以通过LoadBalancer类型的Service外部访问。
7.关于前端React项目架构
7.1 核心技术
- 打包编译 - vite
- 包管理 - pnpm
- 编程语言 - typescript
- 前端框架 - react
- 路由 - react-router
- UI组件库 - antd
- cssinjs(不考虑性能开销) - emotion
- 全局数据共享 - zustand
- 自动生成api - openapi
- 网络请求 - axios
- 数据请求利器 - react-query
- 通用hook(可不用) - ahooks
- 错误边界 - react-error-boundary
- 前端日志(暂未集成) - sentry-javascript
- hack - babel
- 代码检查 - eslint
- ts代码检查插件 - typescript-eslint
- 代码美化 - prettier
- git钩子 - husky
- commit格式化 -commitlint
7.2 基于openapi自动获取api请求函数
7.3 调用接口(react-query), 支持自动loading和接口请求联动
8.前端React代码CLI
代码地址:https://github.com/rookie-luochao/create-vite-app-cli
9.结语
- 介绍了gitlab-action的在前端npm领域的基本配置
- 介绍了前端Dockerfile文件的编写,以及pnpm在docker的优化方案,如何利用buildx生成前端多架构镜像
- 介绍了生产场景下前端环境变量的使用
- 介绍了前端React项目技术架构方案