微前端模块联邦实践系列(5) - Path to Production

355 阅读4分钟

本系列大纲

Webapck module federation作为Run-time Integration的一种实现方式,由于每个子项目在运行时动态加载,那么每个子项目也必须满足单独开发、单独部署的原则。

因此本文中,主要讲解如何进行各个项目的CI/CD。

:本文中各个项目目前是组织在一个文件夹下的,只是为了方便管理,如果是不同团队维护,完全是可以分开作为三个不同的项目来管理的。

本文使用的是Vercel这个专注于前端开发与部署的云平台,使用的是free plan,只是每个月有固定限额,国内可能要FQ才能使用。如果无法科学上网,可以使用国内的某些云代替,比如阿里云、腾讯云等等,另外如果实在没有可以薅的羊毛,本文最后也提供一种在本地部署的方式。

持续集成工具采用的是 github workflow,用于在代码push之后,进行持续集成与部署。

deploy workflow (posts/albums/container)

整个app的持续集成与部署主要分为以下步骤

  1. posts (albums) 生产环境的打包配置文件
// apps/posts/config/webpack.prod.js (albums类似)

const HtmlWebpackPlugin = require('html-webpack-plugin')
const { ModuleFederationPlugin } = require('webpack').container
const packageJson = require('../package.json')

module.exports = {
  mode: 'production',
  output: {
    filename: '[name].[contenthash].js',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
    }),
    new ModuleFederationPlugin({
      name: 'posts',
      filename: 'remoteEntry.js',
      exposes: {
        './PostsIndex': './src/bootstrap',
      },
      shared: packageJson.dependencies,
    }),
  ],
}

生产环境中,主要将modedevelopment变成了production,这样webpack在打包时,会采用一系列生产环境的预设方式来进行打包,主要包括代码压缩、混淆,优化chunk文件的拆分和按需加载等等。

目前运行build之后,会在当前目录生成dist文件夹,如果你的代码组织方式和我不一样,出现了路径访问不对的情况,可以尝试修改webpack -> output -> publicPath来达到相同目的,这里不再深入讲解。

  1. container的生产环境配置

container的webpack配置与上述的基本相同,只是需要动态地去配置两个子app的domain

// apps/container/config/webpack.prod.js

new ModuleFederationPlugin({
  name: 'container',
  remotes: {
    postsApp: `posts@${process.env.MFE_POSTS_DOMAIN}remoteEntry.js`,
    albumsApp: `albums@${process.env.MFE_ALBUMS_DOMAIN}remoteEntry.js`,
  },
  shared: packageJson.dependencies,
})

两个domain从环境变量里面来,在子app部署之后,会生成对应的域名。

  1. 添加打包命令
// apps/posts/package.json (albums/container类似)

"scripts": {
  "serve": "webpack serve --config config/webpack.dev.js",
  "build": "webpack --config config/webpack.prod.js"
}
  1. 编写workflow
## .github/workflows/deploy-posts.yaml (albums/container类似)

name: Deploy Posts App
env:
  VERCEL_ORG_ID: ${{ secrets.MFE_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.MFE_POSTS_PROJ_ID }}
on:
  workflow_dispatch:
  push:
    branches:
      - main
    paths:
      - 'apps/posts/src/**'
jobs:
  Deploy-Production:
    runs-on: ubuntu-latest

    defaults:
      run:
        working-directory: ./apps/posts

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install Vercel CLI
        run: npm install --global vercel@latest

      - name: Pull Vercel Environment Information
        run: vercel pull --yes --environment=production --token=${{ secrets.MFE_ACCESS_TOKEN }}

      - name: Build Posts Project Artifacts
        run: vercel build --prod --token=${{ secrets.MFE_ACCESS_TOKEN }}

      - name: Deploy Posts Project Artifacts to Vercel
        run: vercel deploy --prebuilt --prod --token=${{ secrets.MFE_ACCESS_TOKEN }}

编写完成之后,以后再推送代码到main分支之后,就会自动触发build以及deploy to vercel的操作。这里有几点是说明一下

  • 代码中需要的secrets需要在项目的settings中配置,记住是项目的settings中,不是个人账户的settings

workflow_secrets

一切部署完成后,在vercel的控制台会看到3个不同的app

vercel_dashboard

workflows

deploy_workflows

Local deploy

如果你没有免费的羊毛可以薅,那么可以尝试本地部署,方法类似。这里我采用 http-server 在不同的端口起了3个web服务,使用 pm2 进行apps管理。

编写local-deploy.sh

#!/bin/bash

# startup posts app
cd apps/posts
npm install
npm run build
cd dist
pm2 start http-server --name "posts" -- -p 4201 -d ./ --cors
sleep 2
cd ../../

# startup albums app
cd albums
npm install
npm run build
cd dist
pm2 start http-server --name "albums" -- -p 4202 -d ./ --cors
sleep 2
cd ../../

# startup container app
cd container
npm install
MFE_POSTS_DOMAIN=http://localhost:4201/ MFE_ALBUMS_DOMAIN=http://localhost:4202/ npm run build
cd dist
pm2 start http-server --name "container" -- -p 4200 -d ./
cd ../../../

pm2 status

配置package.json的scripts

"scripts": {
  "deploy:local": "chmod +x ./local-deploy.sh && ./local-deploy.sh",
  "deploy:stop:all": "pm2 stop all",
  "deploy:delete:all": "pm2 delete all",
  "deploy:restart:all": "pm2 restart all"
}

运行npm run deploy:local之后

pm2_local_deploy

最后在localhost:4200可以访问到最后的网页

local_deploy_4200