CICD 持续集成与持续交付部署
本文只作为个人入门学习CICD流程化,部分观点概念若有错误,请批评指出。
CI/CD 是一种在应用开发阶段引入自动化来频繁向客户交付应用的方法。他的核心概念是持续集成、持续交付和持续部署。它是作为一个面向开发和运营团队的解决方案,主要针对在集成新代码时所引发的问题。
整个流程大致如下:
以前的应用上线以及代码管理迭代需要运维人员手动处理,整个过程依赖shell脚本并且高度重复化,而引入自动化部署后,只需一键就能自动的将项目打包上传部署到服务器(个人认为其本质就是虚拟机下的shell脚本钩子),极大地简化了项目部署操作。Jenkins是最为常见的CICD工具(当初实习的时候项目组就使用的他,当时觉得特别神奇),通过学习后发现其更适用于大型项目,而且他占用内存较大,以本人的轻量应用服务器举例,2G的运行内存似乎带不动这个家伙,每次登录后台都需要卡很久。他有丰富的插件市场,功能非常强大,但是不适合初生牛犊的我。
因为其内存占用较大,个人实际条件有限,所以暂时放弃用jenkins学习CICD流程化,转向使用github提供的workflow。
老规矩 官方文档先放这
实操
利用workflow来实现个人项目的代码更新和自动上线,由于github的配置语言是yml,所以先来学习会yml语法。
yml部分语法
个人认为你可以把yml看做较为简单的json,二者一般都是运用在配置文件中。本部分内容仅为简单介绍,你可以阅读阮一峰的yaml文章,更全面的学习yml 文章链接
基本语法
yml使用空格的缩进来表示层级关系(有点类似python),他并没有限制你缩进多少,只要缩进不同就行(左侧对齐则认为是同级)
字符串
在yml中,不含空格的字符串可以不带双引号,而带空格的字符串需要带上双引号
prop: july
name: "hello: world"
对象
yml的对象和其他语言一样,都是键值对的形式 key: value
name: july
对应的JavaScript
{ name: "july" }
若将所有键值对写在一行,则需要加上括号
user: { name: july, age: 18 }
转为JavaScript
{ user: { name: "july", age: 18 }}
数组
在yml中,数组用连词线开头,并且连词线后必须跟上空格
user:
- july
- driecky
- master
转为JavaScript
{ user: ["july", "driecky", "master"] }
同样的 当采用行内写法时,需要带上括号
user: [july, driecky, master]
若数组中依然是数组,则这么写
-
- july
- driecky
- master
转为JavaScript
[ ["july", "driecky", "master"] ]
好了 上面这些简单的内容基本够我们使用了。
WorkFlow CICD 简单流程
先放官方文档链接
如同官网说的那样,我们只需要在github项目的根目录下配置.github/workflow/xxx.yml文件即可(文件名任意,github会自动在此目录下加载.yml结尾的文件,并处理为action执行脚本),而具体的文件内容需要我们根据实际情况参考官方文档来进行配置,这里以最简单的配置静态网页为例来看看大概过程。
咱们换种思维,先想想日常运维是怎么将项目部署上去的?
- 第一步 拿到开发人员提供的源码
- 第二步 链接远程服务器
- 第三步 配置好相应的运行环境
- 第四步 源码上传服务器
- 第五步 部署上线
总的来说大概就分为这五步,而我们的自动化流程也不过是通过编码的方式让计算机去帮我们实现这五步,说到底还是运行的是shell脚本(所以我觉得,根本核心是学会运用shell脚本,后面单独出一章来复习一下linux的脚本命令),而此workflow类似提供了一个小的虚拟机供我们执行命令,我们就可以在此虚拟机上进行ssh连接,scp文件传输等工作了。
那我们来看看如何实践吧
创建yaml文件
首先,在我们的项目主页中,点击action选项,我们的自动化流程构建就是通过此选项卡实现
可以看到,github本身提供了相当多的预制操作,我们可以根据不同项目类型来选择自己相关的预制项,但是在这里为了更好的学习,我们创建一个空的流程脚本
他默认为我们创建好了相关文件,我们接下来就要在这里面写我们的流程脚本了
脚本配置
首先,我们的流程脚本需要一个name属性来说明该脚本的作用
name: deploy_meta2d # 流程脚本名字,区分不同脚本以及作用
然后,我们需要在代码提交时就执行这个脚本让其帮我们自动构建部署项目,所以这里要用到github提供的事件监听配置了,on配置项用于配置监听事件,这里我们监听'push'事件,监听的分支为master分支
name: deploy_meta2d
on:
push: # 监听行为
branches: [ master ] # 监听分支
然后我们就可以通过jobs属性配置我们接下来要执行的任务了,工作流运行由一个或多个 jobs
组成,默认情况下并行运行。 若要按顺序运行作业,可以使用 jobs..needs
关键字定义对其他作业的依赖关系。这里我们只做简单的示例,配置一个jobs即可,一个jobs中分为多个step,我们需要配置每个step执行的命令然后按顺序执行我们想要的操作,首先我们需要将前端项目进行构建,通过什么构建呢,前面说到,github提供了类似虚拟机的机制,我们能够通过此虚拟机构建打包我们的前端代码,首先,我们此jobs是构建使用,所以,配置好build属性,在此属性下,我们要定义使用的虚拟机的版本,这里使用ubuntu的最新版,通过runs-on属性配置。
name: deploy_meta2d
on:
push: # 监听行为
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest # 指定虚拟机版本 具体有哪些可供使用见文档
好了,我们虚拟机也能用上了,接下来,我们需要执行我们的核心逻辑了
获取源码 触发构建指令
第一步,我们首先要让虚拟机获取到我们要打包部署的代码,怎么拉取呢,我们依然是通过一个脚本去执行,只不过为了减少Action编写的重复量,Github官方提供了Action官方市场,可以把通用的Action上传上去供大家直接引用。然后通过uses属性引用即可,在这里我们通过github官方提供的actions/checkout@v3这个action去拉取我们的项目代码到虚拟机中。
name: deploy_meta2d
on:
push: # 监听行为
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps: # 步骤,是个数组
- uses: actions/checkout@v3 # 通过uses属性引用需要执行的action 这里的作用是引入官方提供的actions/checkout@v3,从而拉去项目代码到虚拟机
拉取到项目代码后,我们需要进行打包操作,但是别急,咱们的虚拟机还没有相关环境呢,所以,我们需要为他装上nodeJs运行环境,同样使用官方提供的action即可,配置中的name属性用于指明该步骤的作用,方便我们后期对整个执行过程进行监控调试,我们还需要配置安装node的版本,通过with属性配置该命令的环境变量,然后该action能够读取后安装我们指定的版本了。
name: deploy_meta2d
on:
push: # 监听行为
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps: # 步骤,是个数组
- uses: actions/checkout@v3 # 通过uses属性引用需要执行的action 这里的作用是引入官方提供的actions/checkout@v3,从而拉去项目代码到虚拟机
- name: install nodejs # 说明该步骤的作用
uses: actions/setup-node@v2.5.1 # 通过官方提供的action为虚拟机安装node环境
with:
node-version: "14.X" # 环境变量,会读取此变量的值,并安装指定版本node,14.X 表示 14的最高版本。
环境安装好后,我们需要构建整个项目,现在我们就仿佛置身在一台新的配有nodejs的电脑中,首先,需要执行npm install下载项目依赖,在我们的step中配置run属性用于执行linux命令
name: deploy_meta2d
on:
push: # 监听行为
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps: # 步骤,是个数组
- uses: actions/checkout@v3 # 通过uses属性引用需要执行的action 这里的作用是引入官方提供的actions/checkout@v3,从而拉去项目代码到虚拟机
- name: install nodejs # 说明该步骤的作用
uses: actions/setup-node@v2.5.1 # 通过官方提供的action为虚拟机安装node环境
with:
node-version: "14.X" # 环境变量,会读取此变量的值,并安装指定版本node,14.X 表示 14的最高版本。
- name: install deps # 指明该step为安装依赖
run: npm install # 在环境中执行npm install 下载依赖
依赖下载好后,我们就需要进行build打包我们的项目了,生成最终的生产环境代码,通过npm run build执行
name: deploy_meta2d
on:
push: # 监听行为
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps: # 步骤,是个数组
- uses: actions/checkout@v3 # 通过uses属性引用需要执行的action 这里的作用是引入官方提供的actions/checkout@v3,从而拉去项目代码到虚拟机
- name: install nodejs # 说明该步骤的作用
uses: actions/setup-node@v2.5.1 # 通过官方提供的action为虚拟机安装node环境
with:
node-version: "14.X" # 环境变量,会读取此变量的值,并安装指定版本node,14.X 表示 14的最高版本。
- name: install deps # 指明该step为安装依赖
run: npm install # 在环境中执行npm install 下载依赖
- name: build app
run: npm run build # 进行项目打包
最后我们在根目录下生产了dist打包文件夹,接下来要做的就是将此文件上传到我服务器进行部署上线。
上传代码到服务器
linux文件传输使用的是scp命令,在这里我们为了方便避免重复性的劳动,也直接使用第三方提供的actions,我们可以在actions插件市场中搜索相关插件
最好选择星星最多的,然后再配置相关环境变量就可以啦,像这样:
name: deploy_meta2d
on:
push: # 监听行为
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps: # 步骤,是个数组
- uses: actions/checkout@v3 # 通过uses属性引用需要执行的action 这里的作用是引入官方提供的actions/checkout@v3,从而拉去项目代码到虚拟机
- name: install nodejs # 说明该步骤的作用
uses: actions/setup-node@v2.5.1 # 通过官方提供的action为虚拟机安装node环境
with:
node-version: "14.X" # 环境变量,会读取此变量的值,并安装指定版本node,14.X 表示 14的最高版本。
- name: install deps # 指明该step为安装依赖
run: npm install # 在环境中执行npm install 下载依赖
- name: build app
run: npm run build # 进行项目打包
- name: copy dist with scp
uses: appleboy/scp-action@v0.1.4
with:
host: ${{secrets.REMOTE_HOST}} # 指定服务器ip
username: ${{secrets.REMOTE_USERNAME}} # 指定登录用户
password: ${{secrets.REMOTE_PASSWORD}} # 指定登录密码
port: 22 # ssh 端口
source: "dist/" # 复制文件内容 即要传什么过去?
target: ${{secrets.REMOTE_TARGET}} # 上传路径 即上传到服务器的那个路径下
由于workflow文件在github中是公开的,所有人都能看到,所以将服务器ip,密码等以明文的方式写在这里就很危险,github提供了环境变量的形式,我们可以通过配置环境变量让脚本自动读取我们设置的值,这样可以安全的保护我们的隐私,可以在setting中的Secrets and variables选项中进行配置
服务端部署
写完后我们保存该文件,上面的操作,我们就成功的把我们的项目打包并且传到远程服务端了,只要服务端nginx配置好了相关路径,那么整个过程就基本结束了,我们还是看看服务端的部分配置吧:
server {
listen 443 ssl;
charset utf-8;
ssl_certificate cert/scs1694066741764_editor.xroot.top_server.crt; # 设置ssl证书与密匙
ssl_certificate_key cert/scs1694066741764_editor.xroot.top_server.key;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
root /root/dist; # 读取文件路径 与我们上传文件路径相同
index index.html index.htm
server_name editor.xroot.top;
location /{
try_files $uri $uri/ =404;
}
}
此nginx的配置能够在我们的dist文件更新后依然正确读取到相关文件信息,所以服务端基本不用动。
我们保存workflow文件后,发现action中已经执行了我们的脚本,点击后发现其执行成功,说明已经自动上传部署一次了。
测试
我们在本地修改部分文件重新提交代码
事件触发脚本执行成功
构建成功
访问网站,发现部署成功
到此这个简单的执行过程就已经讲解完毕了
总结
本内容算是入门的笔记吧,实际项目要比这复杂的多,作为一个了解action和接触CICD来说应该是足够的,此项目依然存在很多问题,比如项目测试没有考虑,服务宕机重启没有配置,总的来说只实现了部分CI操作吧,而关于动态网站的部署或者集群项目依然有很多东西需要学习,慢慢来吧,先写到这了。