前端工程化及自动化构建流程

1,385 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

前端工程化及自动化构建流程

背景

在开发个人小项目中,我们经常直接将写完的代码直接丢到服务器上跑就完事了😂,但在团队开发中,上面的这种快捷暴力的做法显然是不正确的。在团队开发中,我们经常会遇到这么些问题:

  1. 规范

    代码没有规范,每个人的风格各异,代码交付的质量不可控;commit提交没有规范,无法从每个人commit知晓提交的开发内容

  2. 效率

    不断的重复工作,没有技术积累和沉淀

  3. 流程

    缺乏研发流程,没有产品需求文档,没有迭代的需求管理

  4. 项目质量

    测试功能全靠人工发现与回归,费时费力;代码没有规范导致项目质量差

  5. 部署

    人工构建、部署复杂;依赖不统一;没有版本追踪、回滚

总体就是代码像一坨💩💩,给人一种混乱和不舒服的感觉。

工程化

上述的这些问题,可以通过引入工程化的体系来解决。

工程化🛠:以提高效率、降低成本、保障质量为目的,通过一系列的规范、流程、工具达到研发提效、自动化、保障质量、服务稳定、预警监控等。

引用网络上热门的一张图:

image.png

一套简单的DevOps流程,作为小团队搭建工程化的起点,性价比极高。

这里由于团队内部的常规基建都是用的成熟的框架且Git Flow每个平台有每个平台的规范,故不展开讨论。

自动化构建 CI / CD

通过软件开发的持续方法,您可以持续构建、测试和部署迭代代码更改。这种迭代过程有助于减少您基于有缺陷或失败的先前版本开发新代码的机会。使用这种方法,您可以努力减少从开发新代码到部署的人工干预,甚至根本不需要干预。

CI / CD的采用改变了开发人员和测试人员如何发布软件。

最初是瀑布模型,到敏捷开发,现在是DevOps,这是现代开发人员构建出色的技术路线。随着DevOps的兴起,出现了持续集成(Continuous Integration)持续交付(Continuous Delivery)持续部署(Continuous Deployment) 的新方法。

持续集成的重点是将各个开发人员的工作集合到一个代码仓库中。通常,每天都要进行几次,主要目的是尽早发现集成错误,使团队更加紧密结合,更好地协作。 持续交付的目的是最小化部署或释放过程中固有的摩擦。它的实现通常能够将构建部署的每个步骤自动化,以便任何时刻能够安全地完成代码发布(理想情况下)。 持续部署是一种更高程度的自动化,无论何时对代码进行重大更改,都会自动进行构建/部署。

在开始介绍下面内容前,先认识一些关键词:

Pipeline - 管道

一次Pipeline其实相当于一次构建任务,里面可以包含多个流程,如安装依赖、运行测试、编译、部署测试服务器、部署生产服务器等流程。任何提交或者merge request的合并都可以触发Pipeline

  • 一次pipeline相当于一次(构建任务),包含多个stage
  • 任何push提交或者merge request的合并都可以触发pipeline
 +------------------+  trigger  +----------------+               
 |   Commit / MR    +---------->+    Pipeline    |
 +------------------+           +----------------+

Stages - 构建阶段

Stages表示构建阶段,一次Pipeline中可以定义多个Stages。

 // .gitlab-cli.yml
 stages:
   - echo
   - build
   - deploy
  • 顺序:所有Stages会安装顺序运行,即当一个Stage完成后,下一个Stage才可以开始
  • 成功:所有Stages完成后,Pipeline才会成功
  • 失败:只要有一个失败,后续的Stages就不会再执行,整个Pipeline失败
 +--------------------------------------------------------+
 |  Pipeline                                              |
 |  +-----------+     +------------+      +------------+  |
 |  |  Stage 1  |---->|   Stage 2  |----->|   Stage 3  |  |
 |  +-----------+     +------------+      +------------+  |
 +--------------------------------------------------------+
 ​

Jobs - 构建的工作

Jobs表示构建工作,表示某个Stage里面执行的工作。我们可以再Stages里面定义多个Jobs

  • 顺序:相同Stage中的Jobs会并行执行
  • 成功:相同Stage中的Jobs都执行成功,该Stage才会成功
  • 失败:只要有一个Job失败,那么Stage失败,整个Pipeline失败
 +-------------------------------------------------+
 |  stage1                                         |
 |  +-----------+  +------------+  +------------+  |
 |  |    Job 1  |  |   Job 2    |  |   Job 3    |  |
 |  +-----------+  +------------+  +------------+  |
 +-------------------------------------------------+

YAML基础

  1. 纯量

    • null:用 ~ 表示

    • 引用:

      • & :建立锚点
      • *:用来引用 & 代表的内容
      • << :表示合并到当前数据
 defaults: &defaults --------------------------------------------------- &
   adapter:  postgres
   host:     localhost
 development:
   database: myapp_development
   <<: *defaults ------------------------------------------------------- *
 test:
   database: myapp_test
   <<: *defaults
 等同于
 defaults:
   adapter:  postgres
   host:     localhost
 development:
   database: myapp_development
   adapter:  postgres ------------------------------------------------- * 表示 & 代替的内容
   host:     localhost
 test:
   database: myapp_test
   adapter:  postgres
   host:     localhost
  1. 数组:使用一组连词线开头的行,构成一个数组
 stages:
   - build
   - develop
   - release
  1. 其余的自觉查阅。

在组内,我们使用GitLab CI / CD,CI / CD流程概览如下:

  1. 确保服务器上有GitLab Runner,比如Docker。
  2. 在项目根目录创建一个 .gitlab-cli.yml文件。

在.gitlab-cli.yml文件中,我们可以配置GitLab CI / CD 的特定指令,如:

  • runner应该执行和job的结构和顺序
  • 在特殊条件下,runner应该做什么样的决定

当代码被push到gitlab仓库后或者merge-request时,gitlab会去解析.gitlab-cli.yml的文件,调用相应的runner来执行pipeline中stage中的job。

在开发中,当我们需要验证.gitlab-cli.yml是否有语法错误,使用CI Lint。如果想要在私有服务器中部署代码时,还需要SSH key才能访问它。

.gitlab-cli.yml中配置所常用的参数关键字如下:

关键字描述
script可以用runner执行的Shell脚本,runner需要在服务器中安装gitlab runner
stages定义pipeline中的stage(即定义构建阶段),是一个数组
stage一个job的流程,默认test
variables定义一个变量
image使用docker镜像。也可用:image: name 和 image: entrypoint
tags通过标签管理或匹配runner,即该job用哪个runner去执行
cache在后续运行之间应缓存的文件列表。也可用cache: paths/key/untracked/policy
only指定当前job适用的git refs(分支、Tag)列表
expect除了git的哪些分支,其他都使用该job
artifacts将这个job生成的依赖传递给下一个job 用于不同stage之间传递结果,通用的做法是将build阶段打包出来的文件定义为artifacts,这样在deploy阶段就可以使用了 expire_in: artifacts的过期时间,因为这些数据都是保存在gitlab机器上的,过于久远的资源就可以删除掉了 paths:路径是相对于项目目录($ CI_PROJECT_DIR【这里是gitlab ci预定义的环境变量】)的,不能直接在其外部链接。可以使用遵循通配符模式和filepath.Match的通配符

到此为止,我们来分析一下项目中用到的.gitlab-cli.yml(已做脱敏):

 # .gitlab-cli.yml
 # 定义构建场景
 stages:
   - build
   - develop
 # 定义变量
 variables:
   REGISTRY: https://pkg.xxx.com/abc/api/npm/npmjs
   DOCKER_IMAGE: pkg.xxx.com/docker/test_ubuntu18.04:latest
   NODE_IMAGE: pkg.xxx.com/docker/language/node:lts-slim
   NAME: release/${CI_PROJECT_NAME}
   DEV_NAME: ${CI_PROJECT_NAME}-test
   TAG: ${CI_COMMIT_TAG}
   DIR: pkg.xxx.com/docker-pro/paas
 # 定义的job名:build
 build:
   stage: build
   # docker镜像
   image:
     name: ${NODE_IMAGE}
   # 执行脚本
   script:
     - yarn config set registry $REGISTRY
     - yarn
     - yarn build
   # 将这个job生成的依赖传递给下一个任务
   artifacts:
     paths:
       - dist/
       - deploy/
   # 该job用这个runner去执行
   tags:
     - gitops-k8s-product-runners
   # 在develop和tags分支上可用
   only:
     - develop
     - tags
 
 develop:
    # docker镜像
   image:
     name: ${DOCKER_IMAGE}
   stage: develop
    # 在develop分支上可用
   only:
     - develop
   when: on_success
   # 定义该job依赖哪一个job
   dependencies:
     - build
   # 开始执行脚本前所需执行脚本
   before_script:
     - export VERSION=latest
     - export NAME=$DEV_NAME
     - echo "before_script"
     - cp deploy/* .
     - ls
   # 执行脚本
   script:
     - docker build -t=$DIR/$NAME:$VERSION .
     - docker push $DIR/$NAME:$VERSION
     - docker rmi -f $DIR/$NAME:$VERSION
   # 该job用这个runner去执行
   tags:
     - gitops-k8s-product-runners

同时我们发现,项目中除了.gitlab-cli.yml文件,还发现有.gitignore文件,让我们继续详解其中的内容:

.gitignore

有些文件我们希望Git忽略,不要在我们的版本库中跟踪它,这些文件包括许多自动生成的或特定于平台的文件,以及其他本地配置文件,如:

  1. 含有敏感信息的文件
  2. 编译出的代码,如.dll.class
  3. 系统文件,如.DS_StoreThumbs,db
  4. 含有临时信息的文件,如日志、缓存等。
  5. 生成的文件,如dist文件夹

虽然我们可以使用 git rm 命令停止跟踪一个文件,比如 git rm --cached,但是太麻烦了,我们仅仅需要使用.gitignore 文件就可以告诉Git不要跟踪哪些文件。

.gitignore文件的基本规则

  1. 任何以哈希(#)开头的行都是注释。
  2. `` 字符可以转义特殊字符。
  3. / 字符表示该规则只适用于位于同一文件夹中的文件和文件夹。
  4. 星号(*)表示任意数量的字符(零个或更多)。
  5. 两个星号(**)表示任意数量的子目录。
  6. 一个问号(?)代替零个或一个字符。
  7. 一个感叹号(!)会反转特定的规则(即包括了任何被前一个模式排除的文件)。
  8. 空行会被忽略,所以你可以用它们来增加空间,使你的文件更容易阅读。
  9. 在末尾添加 / 会忽略整个目录路径。

示例:

 # dependencies
 /node_modules
 /npm-debug.log*
 /yarn-error.log
 # /yarn.lock
 /package-lock.json
 ​
 # production
 /dist
 /build/dist
 ​
 # misc
 .DS_Store
 ​
 # umi
 /src/.umi
 /src/.umi-production
 /src/.umi-test
 /.env.local
 public/config.js

至此,在我们将项目代码提交推送到gitlab中的远程存储库中的功能分支,推送会触发项目的CI/CD管道。然后,gitlab 跑CI/CD:

  • 运行自动化脚本(顺序或并行):

    • 构建或者测试我们的项目代码
    • 在 Review App 中能预览更改

实施后将按预期工作:

  • 获得code reviewed和批准

  • 将feature分支合并到默认分支上

    • GitLab CI/CD 会自动将更改的代码部署到生产环境。

如果出现问题,可以回滚更改。 GitLab 工作流程示例

此工作流显示了GitLab流程中的重要步骤,我们可以在gitlab UI中可视化所有的步骤。

总结

遵循工程化的开发流程,并形成规范,能够极大地提高团队效率。

参考链接: