使用 Docker Compose + Turborepo 部署 Monorepos & PNPM Workspace 中的 Nuxt3 应用

公司有很多项目,基本长得一样,但是也有客制化需求,且技术老大不喜欢也不注重前端工程化,基础设施更是别想了。当然不怪他,怪我菜,没能到一个足够好的公司。

本文我分享的不是最佳实践,纯个人情况和经验,不会适用于大部分人,希望你看完有所收获。

为什么是 Nuxt3

我是一个 Vue 开发者,Nuxt3 推出后就一直在关注,其中的 Layers 功能让我印象深刻。

这个功能总的来说就是你可以让你的 Nuxt 应用继承一个已经存在的 Nuxt3 应用,其中包括:

  • nuxt.config.ts 配置信息
  • 所有 components/ 下的组件
  • 所有 composables/ 下的可组合函数
  • 所有 plugins/ 下的插件
  • 所有 server/ 下的服务器相关逻辑
  • 所有 pages/ 下的页面
  • 所有 middleware/ 下的中间件
  • 所有 layouts/ 下的布局
  • 所有 public/ 下的文件
  • 所有 app/ 下的文件,即 app/router.options
  • app.vue 文件

这些功能很适合我的业务场景,即大量应用都几乎一模一样,但是也有客制化需求。

为此,抽离出一个 package,专门做一个 baseLayer,填充公共内容(api、库配置、公用逻辑和页面等),所有应用继承即可。

Monorepos && PNPM Workspace

项目结构如下:

这个没什么好说的,其中 packages 中主要为了抽出并复用:api、utils、eslint-config、stylelint-config,apps/ 中才是正菜。

pnpm-workspace.yaml 也很简单:

yml 复制代码
packages:
  - "apps/*"
  - "packages/*"

服务端渲染 SSR

先说结论,已弃用,原因是太消耗服务器资源(运行时、磁盘空间),本来就是小水管,如果跑个 Nginx 带几个静态页面不要太轻松,选择 ssr 意味着每个应用都要跑一个 node 进程,再加上使用 Docker 在服务器上做自动构建和部署(没有私有Hub),磁盘 IO 轻松爆炸。

当然,如果可以的话,我选 SSR,SEO 加 FCP 对于重内容的应用很香啊。

当然,本文还是先讲 SSR 的情况。

Turborepo

没别的原因,就看重了 turbo prune --scope $APP_NAME --docker 这一个命令,具体可以看这里

他可以有效缩短大型 workspace 的 Docker 构建时间,起码对我来说是有用的

Docker & Docker Compose

首先,我想解决的是:

  • 把本地一个一个打包
  • 然后压缩好丢服务器
  • 使用 node run 起来(ssr)

Docker 是不二之选,可以轻松解决环境安装、打包、运行的问题。

通用的 Dockefile

利用分布构建,有效减少镜像大小。

dockerfile 复制代码
// docker/nuxt/Dockerfile

FROM node:16.20-alpine as base
ARG APP_NAME
WORKDIR /app

FROM base as pnpm
RUN corepack enable

FROM base as workspace
RUN yarn global add turbo
COPY . .
RUN turbo prune --scope $APP_NAME --docker

FROM pnpm AS builder
COPY .gitignore .gitignore
COPY --from=workspace /app/out/json/ .
COPY --from=workspace /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN pnpm install
COPY --from=workspace /app/out/full/ .
COPY assets ./assets
RUN pnpm exec turbo run build --filter=$APP_NAME...

FROM base
COPY --from=builder /app/apps/$APP_NAME/.output .output
# Fixed https://github.com/nuxt/nuxt/issues/12493
RUN mkdir /app/.output/server/node_modules/@popperjs && mv .output/server/node_modules/@sxzz/popperjs-es .output/server/node_modules/@popperjs/core
EXPOSE 3000
CMD ["node", "/app/.output/server/index.mjs"]

docker-compose.yml

以单个 app 为例

开发环境

yml 复制代码
// docker-compose.dev.yml

version: '3.9'

services:
  app1:
    build:
      context: .
      dockerfile: ./docker/nuxt/Dockerfile
      args:
        APP_NAME: "app1"
    ports:
      - '49152:3000'
    environment:
      NUXT_PUBLIC_API_BASE_URL: http://dev.example.com/api

正式环境

yml 复制代码
// docker-compose.yml

version: '3.9'

services:
  app1:
    extends:
      file: ./docker-compose.dev.yml
      service: app1
    environment:
      NUXT_PUBLIC_API_BASE_URL: http://prod.example.com/api
    restart: always

部署

第一次

bash 复制代码
git clone monorepos.git 
cd monorepos
docker compose build && docker compose up -d

更新

bash 复制代码
git pull
docker compose up -d --force-recreate --build

总结

这套模式在有限的条件下运行了很久也很好。可是,当应用数量到了 8 个的时候,服务器开始卡了,磁盘开始满了,最终回到了本地打包 SPA,SFTP 到服务器。

相关推荐
还是大剑师兰特27 分钟前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解27 分钟前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~33 分钟前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding38 分钟前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT43 分钟前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓43 分钟前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶213643 分钟前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了43 分钟前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django
张张打怪兽1 小时前
css-50 Projects in 50 Days(3)
前端·css