前端搞自动化:构建如Vercel版丝滑的自动化部署流程

1,980 阅读5分钟

前言

互联网发展到今天,已经完全不是10年前那种刀耕火种的状态。渐渐地,无论是大公司,还是小作坊团队,由于云技术的发展,他们都开始对基础设施的建设进行投入,这背后的原因很多,我这里提出几个关键的:

  • 程序员工资太高,要降低成本,必须要用机器来取代人
  • 行业人员流动大,程序员跳槽至竞对,会携带大量信息
  • 1 + 1 < 2,当公司到了一定规模,堆人的边际效应递减,甚至还会带来管理成本的急剧增长

所以,降本提效,是下一个十年互联网公司之间竞争的关键,谁能最大化组织效能,而不是依靠996和堆人,谁就能占据生产力优势,这在变化无常的互联网,是十分关键的。

祛魅

DevOps、Serverless、CI,这些词听起来高大上,很多设施只停留在大公司层面,小的个体还有团队很难去打破信息壁障去构建自己的开发基础设施。

下面我从一个前端出发,分享一下我基于Github,是如何构建一套属于自己的自动化部署流程,希望让上述那些词不再高大上,让它们更加接地气,让前端工程师也能轻松构建自动化流程,享受到遍历。

说明:非最佳实践,只是最适合业务的实践,如果有更好地实践,可以在评论里交流一下。

部署流程

ci

业务背景

写了一个小程序组件库(独立仓库),为此做了一个小程序官网(基于Nextjs,独立仓库),同时为了省事儿,要实现如下功能:

  • 组件库仓库更新readme或是package.json时,官网的markdown以及组件相关信息需要同步更新
  • 需要提供组件下载的功能,下载下来的是单个组件zip格式的压缩包
  • 需要提供ts、less|js、wxss这两种类型的组件压缩包

基于以上需求,所以我把小程序组件库这个独立仓库作为server的子仓库,通过node的文件读写能力,把组件库当作“数据库”,编写接口来获取组件readme.md、package.json以及zip压缩包。

server基于Nestjs + fastify构建,通过json5、fast-json-stringify来优化json相关性能。

Server部署脚本

Dockerfile.prod

FROM node:lts-alpine AS compile

WORKDIR /app

COPY . /app

RUN npm install -g cnpm --registry https://registry.npm.taobao.org && cnpm install && npm run build

RUN cd light-design && cnpm install && npm run build:es5

FROM node:lts-alpine AS deploy

WORKDIR /app

RUN apk add --no-cache zip

COPY --from=compile /app/dist /app/dist 
COPY --from=compile /app/node_modules /app/node_modules 
COPY --from=compile /app/package.json /app/package.json 
COPY --from=compile /app/light-design/miniprogram/data /app/light-design/miniprogram/data
COPY --from=compile /app/light-design/miniprogram/components /app/light-design/miniprogram/components
COPY --from=compile /app/light-design/miniprogram/components_es5 /app/light-design/miniprogram/components_es5
  • 部署在国内的话就用cnpm,快很多
  • 不要把项目源文件打包到镜像中

Dockerfile.dev

FROM node:lts-alpine

WORKDIR /app/

COPY package*.json ./ \ 
      yarn.lock ./

RUN yarn --registry=https://registry.npm.taobao.org

RUN cd light-design && yarn --registry=https://registry.npm.taobao.org && npm run build:es5

RUN apk add --no-cache zip

COPY . .

本地调试的话,就随意了,以好用为主。

docker-compose.yml

version: '3.8'

services: 
  main:
    container_name: main
    build:
      context: .
    ports:
      - ${DG_PT}:${DG_PT}
      - ${SV_PT}:${SV_PT}
    env_file: 
      - .env
    networks:
      - webnet
    depends_on: 
      - postgres
  
  postgres:
    container_name: ${DB_C_NM}
    image: postgres:13
    networks: 
      - webnet
    environment: 
      POSTGRES_PASSWORD: ${DB_PD}
      POSTGRES_USER: ${DB_US}
      POSTGRES_DB: ${DB_NM}
      PG_DATA: /var/lib/postgresql/data
    ports:
      - ${DB_PT}:${DB_PT}
    volumes:
      - pgdata:/var/lib/postgresql/data

networks:
  webnet:

volumes:
  pgdata:
  • 注意docker-compose版本,有些服务器安装的docker-compose版本较低,需要yml文件的version符合
  • 对于容器之间通信,不能用localhost + port,得用容器名称 + port

开发环境和生产环境使用各自的yml配置文件:

docker-compose.prod.yml

services: 
  main:
    build:
      dockerfile: Dockerfile.prod
    command: npm run deploy

docker-compose.dev.yml

services:
  main:
    build:
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
    command: npm run debug

上述yml配置文件中用到的变量在env中定义,其中涉及到数据库相关的信息,所以就展示了。

package.json

"init:submodule": "git submodule update --init --recursive",
"update:submodule": "git submodule sync && git submodule update --recursive --remote --merge",
"d:dev": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up",
"d:prod": "docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build --force-recreate"

.github/workflows/ci.yml

name: Deploy Actions

on:
  push:
    branches:
      - master
    paths-ignore:
      - test
      - README.md
      - LICENSE

jobs:
  Deploy:
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v2

      - name: Checkout Submodules
        uses: textbook/git-checkout-submodule-action@master
        with:
          remote: true

      - name: Upload To Server
        uses: burnett01/rsync-deployments@4.1
        with:
          switches: -avzr --delete
          path: ./
          remote_path: /app/
          remote_port: '22'
          remote_host: ${{ secrets.SSH_HOST }}
          remote_user: ${{ secrets.SSH_USERNAME }}
          remote_key: ${{ secrets.DEPLOY_KEY }}

      - name: Start Service
        uses: appleboy/ssh-action@master
        with:
          port: '22'
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.DEPLOY_KEY }}
          script: cd /app/ && npm run d:prod

源代码在这,由于最近在面试,所以限时public,后续会转回private。

组件库相关脚本

package.json

"build:es5": "webpack --mode production --config ./webpack/es5/webpack.config.js && cd webpack/es5 && node build",
"dev": "cross-env NODE_ENV=development webpack --mode development --watch --config ./webpack/webpack.config.common.js",
"build": "cross-env NODE_ENV=production webpack --mode production --config ./webpack/webpack.config.common.js",

执行npm run build:es5 会将miniprogram/components文件夹下面的组件打包成miniprogram/components_es5

.github/workflows/ci.yml

name: Deploy Actions

on:
  push:
    branches:
      - master
    paths-ignore:
      - test
      - README.md
      - LICENSE

jobs:
  Deploy:
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v2

      - name: Start Service
        uses: appleboy/ssh-action@master
        with:
          port: '22'
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.DEPLOY_KEY }}
          script: cd /app/ && npm run update:submodule && npm run d:prod

源代码在这,限时public。

结语

这样当你server或者组件库提交了新的代码,github actions会替你做好部署相关工作,你只需要,通过远程连接docker引擎查看部署状态即可。

基本上能做到如Vercel自动化部署版流程,后续基础设施在我建设完成之后会分享出来的。

参考文献