基于 GitLab CI 的前端工程CI/CD实践

CI/CD 是 Gitlab 提供的一整套持续集成、持续交付解决方案。html

概念:「持续集成(Continuous Integration)」、「持续交付(Continuous Delivery)」和「持续部署(Continuous Deployment)」,概念理解详细见文章:简单理解持续集成、持续交付、持续部署 谈谈持续集成,持续交付,持续部署之间的区别前端

近期在抽空把团队工程化这块作好,CI/CD只是其中的九牛一毛。在运维文开同窗协助配合下,以公司某项目前端工程作试验,实现了 CI 的过程,本质上CD也是支持了的,主要是看CD这个过程怎么作更好。自动触发了构建操做,仍是直接使用构建后的 artifacts 直接部署,走不走Jenkins后续方案等……下边简单介绍一下。vue

GitLab 的CI配置

前提:服务器部署配置了 Runner 。 如图,搞了一个共享型的 Runner,十几个前端工程均可以基于此 Runner 执行CI脚本。由于Runner是共享的,因此gitlab-ci.yml 中的 docker 镜像 image 建议保证每一个项目不一致,这样就能够共同使用一个 Runner 来并行执行多个项目CI,本质上在不一样的 docker 镜像中运行脚本,这样就不会冲突了。Runner: gitlab-runnernode

shore-runner

下边是举例 Angular 前端工程 在 Gitlab 上实践的 CI 脚本,目前只作了代码检查和自动构建过程检测。实现自动化检查代码是否规范,前端限制了一些代码拼写规范、console.log禁用、alert禁用tslint的约束,这些均可以在工程下自定义维护规则。aot构建是为了进一步检查一些编译问题。只要二者经过,Jenkins 构建100%是无差错的。react

配置:android

# GitLab CI/CD 前端 Angular 持续集成实践 : https://github.com/giscafer/front-end-manual/issues/27
# 由于共享Runner,这里不建议同样的版本号,避免同时运行的时候,相同docker镜像会出问题
image: node:latest
# image: node:10.4.1

# 变量定义
# https://docs.gitlab.com/ee/ci/variables/#using-predefined-environment-variables
variables:
 NODE_MODULES_VERSION: 'ng-starter-web-1.0.0' # node_modules版本号,每次升级依赖改一下这里的数值
 CURRENT_BRANCH: $CI_COMMIT_REF_NAME

# 缓存目录文件
# key是惟一值,重名会覆盖上一次的缓存
cache:
 key: '$NODE_MODULES_VERSION'
 paths:
 - node_modules/

stages:
 - init
 - lint
 - build
  # - deploy

install_packages:
 stage: init
 cache:
 key: '$NODE_MODULES_VERSION'
 paths:
 - node_modules/
 script:
    # 打印一下当前是什么分支而已
 - echo "NODE_MODULES_VERSION=$NODE_MODULES_VERSION"
 - echo "CURRENT_BRANCH=$CURRENT_BRANCH"
    # 设置 npm 的源,会快一些
 - npm config set registry http://registry.npm.taobao.org/
    # 安装全部依赖,也就是 node_modules
 - npm install --silent

lint_code:
 stage: lint
  # 定义缓存
 cache:
 key: '$NODE_MODULES_VERSION'
    # 下面的配置指示,咱们当前只拉取缓存,不上传,这样会节省很多时间
 policy: pull
    # 指定要缓存的文件/文件夹
 paths:
 - node_modules/
 script:
 - npm run lint
 only:
 - /^dev.*$/ # dev分支下只作lint语法检查

build:
 stage: build
 cache:
 key: '$NODE_MODULES_VERSION'
 policy: pull
 paths:
 - node_modules/
 script: npm run aot:test
  # artifacets 是打包你指定的文件或者文件夹,而后你能够经过 gitlab 的页面进行下载的
 artifacts:
    # artifacets 的名字
 name: '$CI_COMMIT_REF_NAME-dist'
    # artifacets 的过时时间,由于这些数据都是直接保存在 Gitlab 机器上的,过于久远的资源就能够删除掉了
 expire_in: 60 mins
    # 制定须要打包的目录,这里我把 dist 目录给打包了,准备发布到服务器
 paths:
 - dist/
 only:
 - master
#
## 部署任务
# deploy:
# stage: deploy
# # 该命令指定只有 master 分支才可以执行当前任务
# only:
# - master
# # 部署脚本,在下面的代码中,我用到了不少相似 ${AMAZON_PEM} 的变量,因为咱们的私钥、Ip 都算是不宜公开显示的信息,
# # 因此我用到了 Gitlab 的变量工具,在 repo 的 Setting > CI/CD > Secret variables 中,这些变量值只有项目管理员才有权限访问
# script:
# - 'ls -la'
# - 'ls -Rl dist'
# - 'echo "${AMAZON_PEM}" > amazon.pem'
# - 'chmod 600 amazon.pem'
# - 'scp -o StrictHostKeyChecking=no -i amazon.pem -r dist/* ${AMAZON_NAME_IP}:/usr/share/nginx/html/'


复制代码

Angular gitlab-ci.yml 配置还能够参考:stackoverflow.com/questions/4…nginx

配置中有几个关键点须要了解,如:git

变量 variables

用户能够自定义变量或者读取Gitlab系统自带的变量,用来动态在脚本中获取,也能够根据变量写一下if语句来执行不一样的逻辑,以下:github

build:
 stage: build
 script:
 - | if [ "$CI_COMMIT_REF_NAME" = "$ci_defined_secret_variable_deploy_branch" ]; then echo "build ran and conditional was true" fi  except:
 - master

stagetwo:
 stage: deploy
 script:
 - | echo "stage two ran"  only:
 variables:
 - $CI_COMMIT_REF_NAME == $ci_defined_secret_variable_deploy_branch
复制代码

下图是某工程下的构建配置 web

namespace

阶段 stages

定义 stage,stage 能够简单的理解为“步骤”,会顺序执行,若是上一步错了,那不会继续执行下一步,好比像上边定义的,第一步先 lint 检查代码规范,第二步构建。完整的阶段划分应该为:第一步先初始化,第二步检查代码规范,第三步进行单元测试,第四步构建,第五步就直接将项目部署到服务器

缓存 cache

GitLab CI/CD提供了一种 缓存机制,可用于在运行做业时节省时间。

定义全局的缓存策略,如上所说,每一个不一样的 stage,CI 都会从新启动一个新的容器,因此咱们以前 stage 中的文件都会消失,在前端开发中,就意味着每一个 stage 都要从新完整装一次 node_modules,这样的时间和网络成本都不低,因此咱们选择将这些文件缓存下来。

可是,缓存也要讲究实效性,例如我在第二次的提交中增长了一个库,那第二次的 CI 就不能再重复使用上一次的 node_modules 缓存了,在 .gitlab-ci.yml 中,咱们经过设置 cachekey 来区分不一样的缓存,从配置中能够看到,经过自定义变量 NODE_MODULES_VERSION 来标识 node_modules 的版本,决定是否下载新的依赖,每次工程修改依赖版本或者新增模块时,维护一下这个NODE_MODULES_VERSION 版本号就能够了。能够经过监听package.json 文件版本更新,而后脚本自动修改NODE_MODULES_VERSION版本号。如脚本:compare-pk.js

# 变量定义
# https://docs.gitlab.com/ee/ci/variables/#using-predefined-environment-variables
variables:
 NODE_MODULES_VERSION: 'ng-starter-web-1.0.0' # node_modules版本号,每次升级依赖改一下这里的数值
 CURRENT_BRANCH: $CI_COMMIT_REF_NAME
复制代码

任务 Job

剩下就是 Job 来定义脚本了,以上的东西都是给 Job 来使用的。下边举例详细说明:

# 这个是某个任务的名称,你能够随意起名
install_packages:
  # 指定该任务所属的步骤,每到一个步骤,该步骤所对应的全部任务都会并行执行
 stage: init
  # 指定要缓存的文件以及文件夹
 cache:
    # 这个属性是 gitlab 比较新版本里面加的特性,意思是在这一步,我只上传这个缓存,我不会拉取该缓存
 policy: push
    # 指定缓存的内容,在下面我缓存了 node_modules 这个文件夹,你还能够在下面继续添加文件或者文件夹
 paths:
 - node_modules/
  # 该任务要运行的脚本,顺序执行
  # 都是 bash 命令
  # 默认当前目录就是 repo 的根目录
 script:
    # 打印一下当前是什么分支而已
 - echo "CURRENT_BRANCH=$CURRENT_BRANCH"
    # 设置 npm 的源,会快一些
 - 'npm config set registry "https://registry.npm.taobao.org"'
    # 安装全部依赖,也就是 node_modules
 - "npm install"
复制代码

有缓存和无缓存CI速度对比

无缓存作一个lint检查须要约 8 分钟

uncache_job_time

有缓存则约一分半

cache_node_modules

更多配置项介绍见官方文档yaml/README

开发分支

PR: pull request 或 merge request

每次提交或者 PR 都会自动触发 job:lintPR 在代码lint或者测试没有经过的状况下,是默认没法合并的(按钮禁用,权限大的用户才能跳过检查,但不建议,除非你想出错)。

更方便的是,PR 能够设置为脚本执行经过后自动合并,固然若是须要 CR (code review) 的话,能够设置为手动合并。 若是连测试都没有经过的代码,就没有必要 CR 了。

TIM截图20190410144849

master 分支

能够在 master 分支作持续交付操做(CD), 主要就是自动化构建;将构建成功结果物,经过脚原本部署便可。若是还有后期的自动化接口或者组件测试,部署后执行测试,若是失败则回滚。按理是测试成功的代码,部署后就通常没有问题,除非是环境和数据引发的问题。

由于 master 分支是 dev 或者 test 分支 PR 合并过来的,因此他们的测试和代码检查通常都经过了的,固然,合并以前也会从新执行一次代码检查和测试,最后才会走构建的job。

image

Pipeline

Gitlab 的 Pipeline 下能够看到每次提交触发Job的执行状态,能够对执行日记查看,对应job执行成功或失败均可以发生通知给开发者。

image

image

持续交付(Continuous Delivery)

持续交付在持续集成的基础上,将集成后的代码署到更贴近真实运行环境的「类生产环境」中。好比,咱们完成单元测试后,能够把代码部署到链接数据库的 Staging 环境中更多的测试。若是代码没有问题,能够继续手动部署到生产环境中。

从频繁提交代码、自动化测试(保证测试覆盖) -> 运行本地测试 -> 服务器运行测试 -> 部署到测试环境 -> 交付管理

而这些都应该是自动的,因此你须要知道的东西有: 如何编写测试(Junit、Qunit、BDD、TDD..)、自动化测试(Selenium..)、版本管理(git)、配置(feature toggle)、依赖管理、部署脚本等等。

从0起作好持续交付并不容易,涉及不少东西,从简单的作起吧。自动触发了构建操做,目前如何自动部署,走不走 Jenkins 后续方案讨论再定。能够保留 Jenkins 手动构建(出问题能够规避),也能够有自动化构建部署两种方案都有

后边又尝试了Gitlab Pages的CI/CD,构建后上传到远程服务器:

image: node:10.4.1

variables:
 NODE_MODULES_VERSION: 'wiki-web-1.0.0' # node_modules版本号,每次升级依赖改一下这里的数值
 CURRENT_BRANCH: $CI_COMMIT_REF_NAME

# 缓存目录文件
# key是惟一值,重名会覆盖上一次的缓存
cache:
 key: '$NODE_MODULES_VERSION'
 paths:
 - node_modules/

stages:
 - build
 - deploy

build:
 stage: build
 cache:
 paths:
 - node_modules/

 script:
 - npm install  --silent
 - npm run build

 artifacts:
 name: 'dist'
 expire_in: 60 mins
 paths:
 - dist
      # - docs/.vuepress/dist

 only:
 - master

deploy:
 stage: deploy
 environment:
 name: Production
 before_script:
    # - sed -i '/jessie-updates/d' /etc/apt/sources.list
    # https://superuser.com/questions/1423486/issue-with-fetching-http-deb-debian-org-debian-dists-jessie-updates-inrelease/1424377#1424377
 - printf "deb http://archive.debian.org/debian/ jessie main\ndeb-src http://archive.debian.org/debian/ jessie main\ndeb http://security.debian.org jessie/updates main\ndeb-src http://security.debian.org jessie/updates main" > /etc/apt/sources.list - apt-get update -qq && apt-get install -y -qq sshpass  script:
 - cd dist/
 - ls
 - sshpass -V
 - export SSHPASS=$USER_PASS
 - sshpass -e scp -P 端口 -o stricthostkeychecking=no -r . root@IP:/data/git_cd
    # - sshpass -e scp -o stricthostkeychecking=no -r . username@host.com:/var/www/html
    # - rsync -avz --delete -e"ssh -p 端口" ./ root@ip:/data/git_cd
 when: on_success
 only:
 - master

复制代码

是一个基于 vuepress 的工程,CI 自动构建后,会将打包后的 dist文件夹 上传到 artifacts , CD 操做的时候从这里那就行了。

TIM截图20190410102035

构建生成的附件能够经过 sshpassrsync 将附件上传到远程服务器。相关文章能够参考:

App CI

Android 和 IOS CI 环境搭建参考:

以上是网友的分享文章,前半部分工做主要是搭建 Runner 和 docker 环境,有更快速的方式来搭建。我在搭建 android 环境时,使用了共享性 Runner,image选用的是开源社区的 react-native-community/ci-sample docker 镜像,而后配置对应的执行脚本便可。越过了繁琐的 android 环境搭建,这就是 docker 的好处了。

android

TIM截图20190509194049

CI 工具集

经常使用的有如下几种:

  • Jenkins :借助 Jenkins 配合 gitlab 的 webhook 来作CI/CD
  • Circle CI : 强大,对 Github 友好
  • Travis CI:强大,对 Github 友好
  • Gitlab CI:Gitlab 自带 CI 环境,同样比较好用.(本文全都是在 gitlab ci 实践)。

详情了解:dockone.io/article/817…

Jenkins CI/CD 流程图

分享两个来自 ProcessOn 网友分享的 Jenkins CI/CD 流程图:

CICD后端

Jenkins CI

总结

把Runner搞成共享型的,前端的工程就不须要重复配置Runner了,后续逐步的改善便可。一个完整的ci配置应该包含这些过程:第一步先初始化,第二步检查代码规范,第三步进行单元测试,第四步构建,第五步就直接将项目部署到服务器

时间轴

整合 DevOps,CI/CD 实现是必须的,目前市场和工具方案都特别成熟,我的认为,小团队或者大团队都有必要去学习掌握,以便改善团队的效能问题。一切能脚本自动化的工做,都不该该人工参与。不管如何,频繁部署、快速交付以及开发测试流程自动化都将成为将来软件工程的重要组成部分。

欢迎讨论~


推荐:


Author: @giscafer,原文地址:front-end-manual/基于 GitLab CI 的前端工程CI/CD实践 , 欢迎讨论

相关文章
相关标签/搜索