前言
前置知识
- Linux
- Docker
- Nginx
- Github
可以干嘛
作为一套面向开发和运维团队的解决方案,CI/CD 主要解决集成新代码和向用户频繁交付应用的问题。
更直接地说,就是可以解放开发人员的双手,将时间和精力专注于代码本身。
CI/CD是什么
CI/CD(Continuous Intergration/Continuous Delpoy),持续集成/持续部署,或者持续集成/持续交付(Continuous Delivery),是一种在开发阶段引入自动化来频繁交付应用的方法。从前端的角度看,CICD的流程中涉及:
- CI:代码push到托管平台之后的lint测试、单元测试
- CD:将build后的项目丢到远端 Nginx 的静态资源目录下
构建/部署
前后端分离的开发模式中,前端项目经常会使用框架进行开发,经由 Webpack(或者其他构建工具) 打包后的SPA应用(代码),本质上都是静态资源,只需要把它们都放到 Nginx的静态资源目录下,配好相关的路径,即可完成部署。
前端项目的构建、部署、上线流程,从 简陋疏散 到 完善严谨 ,大致经历了以下几个阶段:
手动挡
#mermaid-svg-F5IbSPZaGA9fZE47 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-F5IbSPZaGA9fZE47 .error-icon{fill:#552222;}#mermaid-svg-F5IbSPZaGA9fZE47 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-F5IbSPZaGA9fZE47 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-F5IbSPZaGA9fZE47 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-F5IbSPZaGA9fZE47 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-F5IbSPZaGA9fZE47 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-F5IbSPZaGA9fZE47 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-F5IbSPZaGA9fZE47 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-F5IbSPZaGA9fZE47 .marker.cross{stroke:#333333;}#mermaid-svg-F5IbSPZaGA9fZE47 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-F5IbSPZaGA9fZE47 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-F5IbSPZaGA9fZE47 .cluster-label text{fill:#333;}#mermaid-svg-F5IbSPZaGA9fZE47 .cluster-label span{color:#333;}#mermaid-svg-F5IbSPZaGA9fZE47 .label text,#mermaid-svg-F5IbSPZaGA9fZE47 span{fill:#333;color:#333;}#mermaid-svg-F5IbSPZaGA9fZE47 .node rect,#mermaid-svg-F5IbSPZaGA9fZE47 .node circle,#mermaid-svg-F5IbSPZaGA9fZE47 .node ellipse,#mermaid-svg-F5IbSPZaGA9fZE47 .node polygon,#mermaid-svg-F5IbSPZaGA9fZE47 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-F5IbSPZaGA9fZE47 .node .label{text-align:center;}#mermaid-svg-F5IbSPZaGA9fZE47 .node.clickable{cursor:pointer;}#mermaid-svg-F5IbSPZaGA9fZE47 .arrowheadPath{fill:#333333;}#mermaid-svg-F5IbSPZaGA9fZE47 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-F5IbSPZaGA9fZE47 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-F5IbSPZaGA9fZE47 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-F5IbSPZaGA9fZE47 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-F5IbSPZaGA9fZE47 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-F5IbSPZaGA9fZE47 .cluster text{fill:#333;}#mermaid-svg-F5IbSPZaGA9fZE47 .cluster span{color:#333;}#mermaid-svg-F5IbSPZaGA9fZE47 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-F5IbSPZaGA9fZE47 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
本地build项目
sftp上传远程服务器
配置nginx访问路径
- 本地执行
yarn build
构建项目 - 使用 transmit 或其他支持 sftp的软件上传打包后的项目(当然也有其他方式)
- 修改 Nginx 的 nginx.conf 文件,配置项目的访问路径
手动部署操作起来很简单,但缺点也很明显,每次构建完都要人为地进行部署的动作,一方面减少了实际敲代码的时间,另一方面,人工操作免不了会有疏忽出错的时候。
自动挡
随着工程化的发展和工具链的成熟,项目部署不再像以前简单粗暴。前端代码的健壮性、可靠性越来越被重视,项目发布前往往需要 代码约束 和 代码测试 ,校验通过后服务器拉取最新的代码,进行 build 和 nginx 配置后才算完成整个部署的过程。
#mermaid-svg-zEaoFFn8VK5op9FE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-zEaoFFn8VK5op9FE .error-icon{fill:#552222;}#mermaid-svg-zEaoFFn8VK5op9FE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zEaoFFn8VK5op9FE .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-zEaoFFn8VK5op9FE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zEaoFFn8VK5op9FE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zEaoFFn8VK5op9FE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zEaoFFn8VK5op9FE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zEaoFFn8VK5op9FE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zEaoFFn8VK5op9FE .marker.cross{stroke:#333333;}#mermaid-svg-zEaoFFn8VK5op9FE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zEaoFFn8VK5op9FE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zEaoFFn8VK5op9FE .cluster-label text{fill:#333;}#mermaid-svg-zEaoFFn8VK5op9FE .cluster-label span{color:#333;}#mermaid-svg-zEaoFFn8VK5op9FE .label text,#mermaid-svg-zEaoFFn8VK5op9FE span{fill:#333;color:#333;}#mermaid-svg-zEaoFFn8VK5op9FE .node rect,#mermaid-svg-zEaoFFn8VK5op9FE .node circle,#mermaid-svg-zEaoFFn8VK5op9FE .node ellipse,#mermaid-svg-zEaoFFn8VK5op9FE .node polygon,#mermaid-svg-zEaoFFn8VK5op9FE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zEaoFFn8VK5op9FE .node .label{text-align:center;}#mermaid-svg-zEaoFFn8VK5op9FE .node.clickable{cursor:pointer;}#mermaid-svg-zEaoFFn8VK5op9FE .arrowheadPath{fill:#333333;}#mermaid-svg-zEaoFFn8VK5op9FE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zEaoFFn8VK5op9FE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zEaoFFn8VK5op9FE .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-zEaoFFn8VK5op9FE .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-zEaoFFn8VK5op9FE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zEaoFFn8VK5op9FE .cluster text{fill:#333;}#mermaid-svg-zEaoFFn8VK5op9FE .cluster span{color:#333;}#mermaid-svg-zEaoFFn8VK5op9FE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zEaoFFn8VK5op9FE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
lint校验
单元测试
push远端仓库
服务器拉取最新代码
build构建项目
配置nginx
- 代码扫描
yarn lint
检查代码是否规范 yarn unit
进行单元测试git push
提交更改到远端仓库- 登录服务器,
git pull
拉取最新代码 yarn build
构建项目- 配置 nginx 访问路径
这个阶段,我们借助一些工具,能够减少代码不规范或隐藏bug的问题。但所有的操作还是得一行一行命令去敲,项目真正的部署也还是需要手动去操作服务器。
其实完全可以将上面的操作细节都集成到一个 shell 脚本里,通知执行 shell 也能减少很多重复的工作。
CI/CD
上面提到,借助shell也能使得一部分操作自动化,但无论是代码扫描、单元测试还是项目的构建,都还是在本地的开发机上进行(或者说跟开发强耦合),有没有办法将这些附属的操作抽离出来,放到另外的专有环境下进行呢?
现在很流行的 DevOps 理念中,CI/CD的那一环就能很好地实现。
DevOps是一种思想理念,强调软件开发测试运维的一体化,目标是减少各个部门之间的沟通成本,从而实现软件的快速高质量的发布。CI/CD是一套实践方案,实现软件的构建测试部署的自动化。
CI/CD实践 —— 前端项目自动化部署
流程架构
想要达到的效果
远程主分支代码发生改变,拉取主分支代码进行构建,完成后通过 ssh 上传到测试/生产服务器。
Github + Jenkins 的实现链路
#mermaid-svg-kYrig4bhz9C5FZZN {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-kYrig4bhz9C5FZZN .error-icon{fill:#552222;}#mermaid-svg-kYrig4bhz9C5FZZN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kYrig4bhz9C5FZZN .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-kYrig4bhz9C5FZZN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kYrig4bhz9C5FZZN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kYrig4bhz9C5FZZN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kYrig4bhz9C5FZZN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kYrig4bhz9C5FZZN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kYrig4bhz9C5FZZN .marker.cross{stroke:#333333;}#mermaid-svg-kYrig4bhz9C5FZZN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kYrig4bhz9C5FZZN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kYrig4bhz9C5FZZN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-kYrig4bhz9C5FZZN .actor-line{stroke:grey;}#mermaid-svg-kYrig4bhz9C5FZZN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-kYrig4bhz9C5FZZN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-kYrig4bhz9C5FZZN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-kYrig4bhz9C5FZZN .sequenceNumber{fill:white;}#mermaid-svg-kYrig4bhz9C5FZZN #sequencenumber{fill:#333;}#mermaid-svg-kYrig4bhz9C5FZZN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-kYrig4bhz9C5FZZN .messageText{fill:#333;stroke:#333;}#mermaid-svg-kYrig4bhz9C5FZZN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kYrig4bhz9C5FZZN .labelText,#mermaid-svg-kYrig4bhz9C5FZZN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-kYrig4bhz9C5FZZN .loopText,#mermaid-svg-kYrig4bhz9C5FZZN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-kYrig4bhz9C5FZZN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kYrig4bhz9C5FZZN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-kYrig4bhz9C5FZZN .noteText,#mermaid-svg-kYrig4bhz9C5FZZN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-kYrig4bhz9C5FZZN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kYrig4bhz9C5FZZN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kYrig4bhz9C5FZZN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kYrig4bhz9C5FZZN .actorPopupMenu{position:absolute;}#mermaid-svg-kYrig4bhz9C5FZZN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-kYrig4bhz9C5FZZN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kYrig4bhz9C5FZZN .actor-man circle,#mermaid-svg-kYrig4bhz9C5FZZN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-kYrig4bhz9C5FZZN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
VSCode
Github
Jenkins
服务器
浏览器
提交代码
合并到master
通知
执行任务
拉取代码
构建项目
推送到服务器
配置nginx访问路径
访问项目部署地址
VSCode
Github
Jenkins
服务器
浏览器
前端自动化部署
Github Actions 的实现链路
#mermaid-svg-xgIkMtvgpPkQFN6p {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-xgIkMtvgpPkQFN6p .error-icon{fill:#552222;}#mermaid-svg-xgIkMtvgpPkQFN6p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xgIkMtvgpPkQFN6p .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-xgIkMtvgpPkQFN6p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xgIkMtvgpPkQFN6p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xgIkMtvgpPkQFN6p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xgIkMtvgpPkQFN6p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xgIkMtvgpPkQFN6p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xgIkMtvgpPkQFN6p .marker.cross{stroke:#333333;}#mermaid-svg-xgIkMtvgpPkQFN6p svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xgIkMtvgpPkQFN6p .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-xgIkMtvgpPkQFN6p text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-xgIkMtvgpPkQFN6p .actor-line{stroke:grey;}#mermaid-svg-xgIkMtvgpPkQFN6p .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-xgIkMtvgpPkQFN6p .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-xgIkMtvgpPkQFN6p #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-xgIkMtvgpPkQFN6p .sequenceNumber{fill:white;}#mermaid-svg-xgIkMtvgpPkQFN6p #sequencenumber{fill:#333;}#mermaid-svg-xgIkMtvgpPkQFN6p #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-xgIkMtvgpPkQFN6p .messageText{fill:#333;stroke:#333;}#mermaid-svg-xgIkMtvgpPkQFN6p .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-xgIkMtvgpPkQFN6p .labelText,#mermaid-svg-xgIkMtvgpPkQFN6p .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-xgIkMtvgpPkQFN6p .loopText,#mermaid-svg-xgIkMtvgpPkQFN6p .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-xgIkMtvgpPkQFN6p .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-xgIkMtvgpPkQFN6p .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-xgIkMtvgpPkQFN6p .noteText,#mermaid-svg-xgIkMtvgpPkQFN6p .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-xgIkMtvgpPkQFN6p .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-xgIkMtvgpPkQFN6p .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-xgIkMtvgpPkQFN6p .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-xgIkMtvgpPkQFN6p .actorPopupMenu{position:absolute;}#mermaid-svg-xgIkMtvgpPkQFN6p .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-xgIkMtvgpPkQFN6p .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-xgIkMtvgpPkQFN6p .actor-man circle,#mermaid-svg-xgIkMtvgpPkQFN6p line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-xgIkMtvgpPkQFN6p :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
VSCode
Github
服务器
浏览器
提交代码
合并到master
触发钩子
执行 build 任务
推送到服务器
配置nginx访问路径
访问项目部署地址
VSCode
Github
服务器
浏览器
前端自动化部署
搭建过程
Github + Jenkins
docker环境的搭建
- 安装 docker
# 安装 docker 的依赖库,-y 选项表示所有的 Is this OK[y/d/N],都会自动选择yyum install -y yum-utils device-mapper-persistent-data lvm2 # 添加 docker cd 软件源信息sudo yum-config-manager --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo# 安装 docker ce sudo yum install docker-ce
- 启动 docker
sudo systemctl enable docker # 设置开机自启sudo systemctl start docker /# 启动docker
docker centos 安装指南
安装docker-compose
docker-compose 用于定义和运行多容器 docker 应用程序,使用 yml 文件配置应用所需的所有服务。
- 安装 docker-compose
sudocurl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- 提升权限
sudochmod +x /usr/local/bin/docker-compos
docker-compose 安装指南
创建 docker-compose 应用
这里为了方便, nginx 通过容器的方式去启动(不会影响到我目前的 nginx ),jenkins 就还是放在根项目部署的服务器上(方便后续直接通过 shell 复制构建好的项目)
(拉取nginx镜像,编写目录数据卷映射)
- 拉取 nginx 镜像
docker pull nginxdocker images # 查看安装的镜像
- 创建数据卷目录,以便挂载到容器里
+ compose # docker-compose 配置目录 - docker-compose.yml + nginx + conf # nginx 配置 - nginx.conf + html # nginx 静态资源 - index.html
- 编写 docker-compose.yml
version: '3'services: cicd_nginx: restart: always image: nginx container_name: nginx ports: - 3300:80 - 3301:433 volumes: - ../nginx/html:/usr/share/nginx/html - ../nginx/conf/nginx.conf:/etc/nginx/nginx.conf - ../nginx/log:/var/log/nginx - ../nginx/localtime:/etc/localtime:ro
- 启动
docker-compose up -ddocker-compose stop //停止nginx和jenkins
- 公网查看 nginx
Jenkins基本配置
- 前往 jenkins 容器挂载的数据卷中获得初始密码
cat /home/cicd_demo/jenkins/jenkins_home/secrets/initialAdminPassword
> 这个密码只会显示一次,之后如果忘记密码需要重置 - 输入密码进入页面之后,选择推荐安装可以看到,jenkins 会自动帮我们安装很多插件,比如最常用的 git
- 新建账户
- 进入到主页后,先前往 Manage Jenkins - Manage Plugins 安装需要用到的插件,目前就只需要安装 NodeJS
- 前往全局工具配置,安装需要的不同版本的 node 环境
- 配置 github在配置之前,我们先要到 GitHub 生成 Personal access token。头像 - Settings - Developer settings - Personal access tokens - Generate new token,按下图勾选需要的权限 还记得我们要实现的效果吗?当主分支有新的代码提交,就要通知 jenkins 去拉取代码并进行构建。既然是通知,那么肯定就需要用到 Webhook。这里并不需要手动创建 Webhock,jenkins提供的插件会帮我们创建。接下来继续配置插件,**Manage Jenkisn - Config System - **,找到 Github 配置的部分
点击添加凭证,选择 Jenkins,点击后会弹出一个添加凭据的窗口,Type 选择为 Secret text,将我们刚才生成的 Personal access token 复制到 Secret 一栏中,点击添加
添加后在 Credentials 一栏选中 Secret text,勾选 Manage Hook,点击 Test connection,如果正确显示了GitHub 用户名,就说明配置成功了。
经过上面几步后,就完成了两件事情,Node 环境的配置和 Github Webhock 的添加,下面就可以开始新建任务了。
新建一个Item
- 回到首页,新建一个自由风格的任务
- 勾选 GitHub project,输入项目地址。将下面的 Source Code Management 选中为 Git,将你要构建部署的项目的 clone 地址填到 Repository URL 一栏中。如果是公开的仓库,Credentials可以选择无。这里我准备的是一个私有的仓库,还需要添加一个可以访问访问 Github账户 的凭证,添加方法类似上面配置 Github Webhock 。这里选择 ssh private key的方式
- 设置构建触发器和构建环境
- 编写构建shell
经过上面的步骤,就算完成一个 Item了,当 Github主分支有新的代码提交,就会触发构建:
Github Actions
Github Actions 是 Github 提供的持续集成服务,可以使我们的 repo 获得一些自动化的能力。
举例来说:
- 每次 repo 有新的提交或 pr 就自动执行 build
- 在 repo 中定时执行自定义的脚本
- 将代码打成镜像,自动提交到镜像仓库中
一些概念
workflow
:一次执行过程,每个workflow
使用一个配置文件维护job
:workflow
的分解,可串行存在依赖,也可并行执行step
:job
的分解,即具体执行的步骤action
:执行过程的封装,可以自定义,也可以使用 Github 社区定义好的action
artifact
:workflow
运行时产生的中间文件,包括日志、测试结果等event
:触发workflow
的事件,也可以理解为生命周期钩子
如何配置
配置 CI/CD 任务的过程本质上是向 runner 描述如下内容:
- 什么时候执行
- 执行什么操作
这里的 workflow 配置文件就是用来做这个工作的
workflow 的触发
每个 workflow 的配置文件都需要定于
on
字段,用以描述在何种情况下(
event
)下触发
job
的执行
event
可以分为 3 类:
- 定时事件:由定时任务触发的事件
- 手动触发事件:在 actions 页面中手动触发的时间
- Webhook 事件:Github 自身的钩子函数触发的事件,通常伴随着 git 操作
在这里,我们只会用到最后一种,来描述 git 操作触发的 CI/CD 行为
自动化部署的 workflow
下面是我在项目中经常使用的部署模板,我给它们加上了详细的注释
name: build and deploy # workflow 名称on:push:# push 事件触发branches:[ dev ]# 只在 dev 分支有新的 push 情况下触发jobs:build:# job 名称runs-on: ubuntu-latest # 执行 workflow 所需的操作系统环境steps:-name: checkout
uses: actions/checkout@v2 # 使用切换分支的 action 操作-name: build
run: yarn && yarn build # 构建命令-name: deploy
uses: easingthemes/ssh-deploy@main # 使用 ssh 上传文件的 action 操作env:SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}# ssh 密钥对中的私钥ARGS:"-rltgoDzvO"SOURCE:"dist"# 要进行上传的文件目录REMOTE_HOST: ${{ secrets.REMOTE_HOST }}# 服务器主机 ipREMOTE_USER: ${{ secrets.REMOTE_USER }}# 服务器用户名TARGET: ${{ secrets.REMOTE_TARGET }}# 要进行部署的生产环境目录-name: print env
run: printenv # 打印环境变量-name: build success
if: ${{ success() }}# ${{ }} 可以使用上下文参数,success() 表示当上一步执行成功时返回 trueuses: chf007/action-wechat-work@master # 使用发送企业微信消息的 actionenv:WECHAT_WORK_BOT_WEBHOOK: ${{secrets.WX_WEBHOOK}}# github token,需要自己在 repo 的 settings 中生成with:msgtype: news
articles:'[{"title":"💯👨💻 Success Deploy ! 🎉🎉🎉","description":"click here to visit the test site ~","url":"http://xxxx:8080/","picurl":"https://xxxx.com/xxxx/figure-bed/raw/master/images/202203101823814.jpeg"}]'mentioned_list:'[@all"]'-name: build failure
if: ${{ failure() }}uses: chf007/action-wechat-work@master
env:WECHAT_WORK_BOT_WEBHOOK: ${{secrets.WX_WEBHOOK}}with:msgtype: markdown
content:|
# 💤🤷♀️ Deploy Failure 🙅♂️💣
> (⋟﹏⋞) from github action messagementioned_list:'[@all"]'
注:上面 ssh 部署的方式使用了 rsa 秘钥对中的私钥,不知道怎么生成的话请参考服务器上的-Git-生成-SSH-公钥
pr 合并消息提醒的 workflow
name: pull request
on:pull_request:branches:[ master, dev ]jobs:pr:runs-on: ubuntu-latest
steps:-name: checkout
uses: actions/checkout@v2
# 获取 pr 信息,添加到环境变量-name: set pr info
run:|
echo PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }') >> $GITHUB_ENV
echo PR_FROM=$(echo $GITHUB_HEAD_REF ) >> $GITHUB_ENV
echo PR_TO=$(echo $GITHUB_B_REF ) >> $GITHUB_ENV
echo PR_TITLE=$(jq --raw-output .pull_request.title "$GITHUB_EVENT_PATH") >> $GITHUB_ENV
echo PR_URL=$(jq --raw-output .pull_request.html_url "$GITHUB_EVENT_PATH") >> $GITHUB_ENV
echo PR_USER=$(jq --raw-output .pull_request.user.login "$GITHUB_EVENT_PATH") >> $GITHUB_ENV
echo PR_COMMITS_NUM=$(jq --raw-output .pull_request.commits "$GITHUB_EVENT_PATH") >> $GITHUB_ENV-name: print env
run: printenv
-name: pull request
uses: chf007/action-wechat-work@master
env:WECHAT_WORK_BOT_WEBHOOK: ${{secrets.WX_WEBHOOK}}with:msgtype: markdown
content:|
# 😍 Pull Request 🤤 ! (^-^)V
> [${{ env.PR_TITLE }} #${{ env.PR_NUMBER }}](${{ env.PR_URL }})
> ${{ env.PR_USER }} wants to merge ${{ env.PR_COMMITS_NUM }} commits
> into <font color=#2F8CDB>${{ env.PR_TO }}</font> from <font color=#008000>${{ env.PR_FROM }}</font>
代码 push 并发起 pr,触发 workflow
尝试将本地的代码推送到远端,可以看到确实触发了部署的操作
发起一个从开发分支到主分支的合并,同样的,也能触发对应的 workflow
总结
经过以上的步骤,也就实现了一个最小化自动化部署的实践,但这仅仅只是 CI/CD 庞杂管道流程中的一环,很多时候还会结合 docker、k8s、sentry、gitlab等实现多平台协同。上面用到的 Jenkins、Github Actions ,也只是工具中的两种,类似的还有国人开发的 Gitea、Onedev等各类集成了 CI/CD 的代码自建托管平台;企业中,也很有可能会自建一套完整的系统去满足内部的要求。
上面两种方案虽然使用起来很方便,但其内部的实现,想必还是十分复杂的,后面有时间也会继续学习的。
参考
使用GithubActions自动化工作流
花半天时间,轻松打造前端CI/CD工作流
从零搭建docker+jenkins+node.js自动化部署环境
版权归原作者 棉花糖还是不甜的好吃 所有, 如有侵权,请联系我们删除。