部署是指将代码发布到服务器的一种行为。自动化部署就是使用工具来帮助你实现部署的过程,无需亲自动手。

在没有学会自动化部署前,我是这样部署项目的:

  1. 执行测试 npm run test
  2. 构建项目 npm run build
  3. 将打包好的文件放到静态服务器。

偶尔一次两次还行,如果每次部署项目都这样,就会把很多时间浪费在重复的操作上。所以我们要学会自动部署,彻底解放双手。

自动部署(又叫持续部署 Continuous Deployment,英文缩写 CD)一般有两种触发方式:

  1. 定时触发。
  2. 监听 webhook 事件,事件触发时执行自动打包、部署等操作。

定时触发

定时触发,就是构建软件每隔一段时间自动执行打包、部署操作。

这种方式不太好,很有可能软件刚部署完开发就改代码了。为了看到新的页面效果,不得不等到下一次构建开始。另外还有一个副作用,假如开发一天都没更改代码,构建软件还是会不停的执行打包、部署操作,白白的浪费资源。

所以现在基本都是采用监听 webhook 事件的方式来进行部署。

监听 webhook 事件

webhook 钩子函数,就是在你的构建软件上进行设置,监听某一个事件(一般是监听 push 事件),当事件触发时,自动执行定义好的脚本。

例如 Github Actions,就有这个功能。

image-10

为了满足不同用户的需要,本章将使用 JenkinsGitHub Actions 来讲解如何部署前端项目。

  1. 第一部分讲解如何使用 Gitea 配置局域网 git 服务器,再使用 Jenkins 将 Gitea 下的项目部署到局域网服务器。
  2. 第二部分讲解如何使用 GitHub Actions 将 GitHub 项目部署到 GitHub Page 和阿里云服务器。

阅读本章内容并不需要你提前了解 Jenkins 和 GitHub Actions 的知识(它们俩都是部署工具),只要按照本章内容的指引,就能够实现自动化部署项目。

PS: 本人所用电脑操作系统为 windows,即以下所有的操作均在 windows 下运行。其他操作系统的配置大同小异,不会有太大差别。

Gitea + Jenkins 自动构建前端项目并部署到服务器

Gitea 用于构建 Git 局域网服务器,Jenkins 是 CI/CD 工具,用于部署前端项目。

配置 Gitea

  • 下载 Gitea,选择一个喜欢的版本,例如 1.13,选择 gitea-1.13-windows-4.0-amd64.exe 下载。
  • 下载完后,新建一个目录(例如 gitea),将下载的 Gitea 软件放到该目录下,双击运行。
  • 打开 localhost:3000 就能看到 Gitea 已经运行在你的电脑上了。
  • 点击注册,第一次会弹出一个初始配置页面,数据库选择 SQLite3。另外把 localhost 改成你电脑的局域网地址,例如我的电脑 IP 为 192.168.0.118
image-11
image-12
  • 填完信息后,点击立即安装,等待一会,即可完成配置。
  • 继续点击注册用户,第一个注册的用户将会成会管理员。
  • 打开 Gitea 的安装目录,找到 custom\conf\app.ini,在里面加上一行代码 START_SSH_SERVER = true。这时就可以使用 ssh 进行 push 操作了。
在这里插入图片描述
  • 如果使用 http 的方式无法克隆项目,请取消 git 代理。
git config --global --unset http.proxy
git config --global --unset https.proxy

配置 Jenkins

  • 需要提前安装 JDK,JDK 安装教程网上很多,请自行搜索。
image-13
  • 打开 Jenkins 下载页面。
  • 安装过程中遇到 Logon Type 时,选择第一个。
在这里插入图片描述
  • 端口默认为 8080,这里我填的是 8000。安装完会自动打开 http://localhost:8000 网站,这时需要等待一会,进行初始化。
  • 按照提示找到对应的文件(直接复制路径在我的电脑中打开),其中有管理员密码。
image-14
  • 安装插件,选择第一个。
image-15
  • 创建管理员用户,点击完成并保存,然后一路点击下一步。
在这里插入图片描述
  • 配置完成后自动进入首页,这时点击 Manage Jenkins -> Manage plugins 安装插件。
在这里插入图片描述
  • 点击 可选插件,输入 nodejs,搜索插件,然后安装。
  • 安装完成后回到首页,点击 Manage Jenkins -> Global Tool Configuration 配置 nodejs。如果你的电脑是 win7 的话,nodejs 版本最好不要太高,选择 v12 左右的就行。
在这里插入图片描述

创建静态服务器

  • 建立一个空目录,在里面执行 npm init -y,初始化项目。
  • 执行 npm i express 下载 express。
  • 然后建立一个 server.js 文件,代码如下:
const express = require('express')
const app = express()
const port = 8080

app.use(express.static('dist'))

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})

它将当前目录下的 dist 文件夹设为静态服务器资源目录,然后执行 node server.js 启动服务器。

由于现在没有 dist 文件夹,所以访问网站是空页面。

在这里插入图片描述

不过不要着急,一会就能看到内容了。

自动构建 + 部署到服务器

在这里插入图片描述
  • 打开 Jenkins 首页,点击 新建 Item 创建项目。
在这里插入图片描述
  • 选择源码管理,输入你的 Gitea 上的仓库地址。
在这里插入图片描述
  • 你也可以尝试一下定时构建,下面这个代码表示每 5 分钟构建一次。
在这里插入图片描述
  • 选择你的构建环境,这里选择刚才配置的 nodejs。
在这里插入图片描述
  • 点击增加构建步骤,windows 要选 execute windows batch command,linux 要选 execute shell
dbe1cb8cbf12889ec1cfa1d04809da67
  • 输入 npm i && npm run build && xcopy .\build\* G:\node-server\dist\ /s/e/y,这行命令的作用是安装依赖,构建项目,并将构建后的静态资源复制到指定目录 G:\node-server\dist\。这个目录是静态服务器资源目录。
在这里插入图片描述
  • 保存后,返回首页。点击项目旁边的小三角,选择 build now
在这里插入图片描述
  • 开始构建项目,我们可以点击项目查看构建过程。
在这里插入图片描述
  • 构建成功,打开 http://localhost:8080/ 看一下结果。
在这里插入图片描述
在这里插入图片描述
  • 由于刚才设置了每 5 分钟构建一次,我们可以改变一下网站的内容,然后什么都不做,等待一会再打开网站看看。
在这里插入图片描述
  • 把修改的内容提交到 Gitea 服务器,稍等一会。打开网站,发现内容已经发生了变化。
在这里插入图片描述

使用 pipeline 构建项目

使用流水线构建项目可以结合 Gitea 的 webhook 钩子,以便在执行 git push 的时候,自动构建项目。

  • 点击首页右上角的用户名,选择设置
在这里插入图片描述
  • 添加 token,记得将 token 保存起来。
在这里插入图片描述
  • 打开 Jenkins 首页,点击 新建 Item 创建项目。
在这里插入图片描述
  • 点击构建触发器,选择触发远程构建,填入刚才创建的 token。
在这里插入图片描述
  • 选择流水线,按照提示输入内容,然后点击保存
在这里插入图片描述
  • 打开 Jenkins 安装目录下的 jenkins.xml 文件,找到 <arguments> 标签,在里面加上 -Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true。它的作用是关闭 CSRF 验证,不关的话,Gitea 的 webhook 会一直报 403 错误,无法使用。加好参数后,在该目录命令行下输入 jenkins.exe restart 重启 Jenkins。
在这里插入图片描述
  • 回到首页,配置全局安全选项。勾上匿名用户具有可读权限,再保存。
在这里插入图片描述
在这里插入图片描述
  • 打开你的 Gitea 仓库页面,选择仓库设置
在这里插入图片描述
  • 点击管理 web 钩子,添加 web 钩子,钩子选项选择 Gitea
  • 目标 URL 按照 Jenkins 的提示输入内容。然后点击添加 web 钩子
在这里插入图片描述
在这里插入图片描述
  • 点击创建好的 web 钩子,拉到下方,点击测试推送。不出意外,应该能看到推送成功的消息,此时回到 Jenkins 首页,发现已经在构建项目了。
在这里插入图片描述
  • 由于没有配置 Jenkinsfile 文件,此时构建是不会成功的。所以接下来需要配置一下 Jenkinsfile 文件。将以下代码复制到你 Gitea 项目下的 Jenkinsfile 文件。jenkins 在构建时会自动读取文件的内容执行构建及部署操作。
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {  // window 使用 bat, linux 使用 sh
                bat 'npm i'
                bat 'npm run build'
            }
        }
        stage('Deploy') {
            steps {
                bat 'xcopy .\\build\\* D:\\node-server\\dist\\ /s/e/y' // 这里需要改成你的静态服务器资源目录
            }
        }
    }
}
  • 每当你的 Gitea 项目执行 push 操作时,Gitea 都会通过 webhook 发送一个 post 请求给 Jenkins,
在这里插入图片描述

让它执行构建及部署操作。

小结

如果你的操作系统是 Linux,可以在 Jenkins 打包完成后,使用 ssh 远程登录到阿里云,将打包后的文件复制到阿里云上的静态服务器上,这样就能实现阿里云自动部署了。具体怎么远程登录到阿里云,请看下文中的 《GitHub Actions 部署到阿里云》 一节。

GitHub Actions 自动构建前端项目并部署到服务器

如果你的项目是 GitHub 项目,那么使用 GitHub Actions 也许是更好的选择。

部署到 GitHub Page

接下来看一下如何使用 GitHub Actions 部署到 GitHub Page。

在你需要部署到 GitHub Page 的项目下,建立一个 yml 文件,放在 .github/workflow 目录下。你可以命名为 ci.yml,它类似于 Jenkins 的 Jenkinsfile 文件,里面包含的是要自动执行的脚本代码。

这个 yml 文件的内容如下:

name: Build and Deploy
on: # 监听 master 分支上的 push 事件
  push:
    branches:
      - master
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest # 构建环境使用 ubuntu
    steps:
      - name: Checkout
        uses: actions/checkout@v2.3.1  
        with:
          persist-credentials: false

      - name: Install and Build # 下载依赖 打包项目
        run: |
          npm install
          npm run build

      - name: Deploy # 将打包内容发布到 github page
        uses: JamesIves/github-pages-deploy-action@3.7.1 # 使用别人写好的 actions
        with:  # 自定义环境变量
          ACCESS_TOKEN: ${{ secrets.VUE_ADMIN_TEMPLATE }} # VUE_ADMIN_TEMPLATE 是我的 secret 名称,需要替换成你的
          BRANCH: master
          FOLDER: dist
          REPOSITORY_NAME: woai3c/woai3c.github.io # 这是我的 github page 仓库
          TARGET_FOLDER: github-actions-demo # 打包的文件将放到静态服务器 github-actions-demo 目录下

上面有一个 ACCESS_TOKEN 变量需要自己配置。

  • 打开 GitHub 网站,点击你右上角的头像,选择 settings
在这里插入图片描述
  • 点击左下角的 developer settings
在这里插入图片描述
  • 在左侧边栏中,单击 Personal access tokens(个人访问令牌)
在这里插入图片描述
  • 单击 Generate new token(生成新令牌)
在这里插入图片描述
  • 输入名称并勾选 repo
在这里插入图片描述
  • 拉到最下面,点击 Generate token,并将生成的 token 保存起来。
在这里插入图片描述
  • 打开你的 GitHub 项目,点击 settings
在这里插入图片描述

点击 secrets->new secret

在这里插入图片描述

创建一个密钥,名称随便填(中间用下划线隔开),内容填入刚才创建的 token。

在这里插入图片描述
在这里插入图片描述


将上文代码中的:

ACCESS_TOKEN: ${{ secrets.VUE_ADMIN_TEMPLATE }}

替换成刚才创建的 secret 名字,替换后代码如下:

ACCESS_TOKEN: ${{ secrets.TEST_A_B }}

保存后,提交到 GitHub。

以后你的项目只要执行 git push,GitHub Actions 就会自动构建项目并发布到你的 GitHub Page 上。

GitHub Actions 的执行详情点击仓库中的 Actions 选项查看。

在这里插入图片描述
在这里插入图片描述

具体详情可以参考一下我的 demo 项目 github-actions-demo

构建成功后,打开 GitHub Page 网站,可以发现内容已经发布成功。

在这里插入图片描述

GitHub Actions 部署到阿里云

初始化阿里云服务器

  1. 购买阿里云服务器,选择操作系统,我选的 ubuntu
  2. 在云服务器管理控制台选择实例->更多->密钥->重置实例密码(一会登陆用)
  3. 选择远程连接->VNC,会弹出一个密码,记住它,以后远程连接要用(ctrl + alt + f1~f6 切换终端,例如 ctrl + alt + f1 是第一个终端)
  4. 进入后是一个命令行 输入 root(默认用户名),密码为你刚才重置的实例密码
  5. 登陆成功, 更新安装源 sudo apt-get update && sudo apt-get upgrade -y
  6. 安装 npm sudo apt-get install npm
  7. 安装 npm 管理包 sudo npm install -g n
  8. 安装 node 最新稳定版 sudo n stable

创建一个静态服务器

mkdir node-server // 创建 node-server 文件夹
cd node-server // 进入 node-server 文件夹
npm init -y // 初始化项目
npm i express
touch server.js // 创建 server.js 文件
vim server.js // 编辑 server.js 文件

将以下代码输入进去(用 vim 进入文件后按 i 进行编辑,保存时按 esc 然后输入 :wq,再按 enter),更多使用方法请自行搜索。

const express = require('express')
const app = express()
const port = 3388 // 填入自己的阿里云映射端口,在网络安全组配置。

app.use(express.static('dist'))

app.listen(port, '0.0.0.0', () => {
    console.log(`listening`)
})

执行 node server.js 开始监听,由于暂时没有 dist 目录,先不要着急。

注意,监听 IP 必须为 0.0.0.0 ,详情请看部署 Node.js 项目注意事项

阿里云入端口要在网络安全组中查看与配置。

在这里插入图片描述

创建阿里云密钥对

请参考创建 SSH 密钥对绑定 SSH 密钥对,将你的 ECS 服务器实例和密钥绑定,然后将私钥保存到你的电脑(例如保存在 ecs.pem 文件)。

打开你要部署到阿里云的 GitHub 项目,点击 setting->secrets。

在这里插入图片描述

点击 new secret

在这里插入图片描述

secret 名称为 SERVER_SSH_KEY,并将刚才的阿里云密钥填入内容。

在这里插入图片描述

点击 add secret 完成。

在你项目下建立 .github\workflows\ci.yml 文件,填入以下内容:

name: Build app and deploy to aliyun
on:
  #监听push操作
  push:
    branches:
      # master分支,你也可以改成其他分支
      - master
jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Install Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '12.16.2'
    - name: Install npm dependencies
      run: npm install
    - name: Run build task
      run: npm run build
    - name: Deploy to Server
      uses: easingthemes/ssh-deploy@v2.1.5
      env:
          SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
          ARGS: '-rltgoDzvO --delete'
          SOURCE: dist # 这是要复制到阿里云静态服务器的文件夹名称
          REMOTE_HOST: '118.190.217.8' # 你的阿里云公网地址
          REMOTE_USER: root # 阿里云登录后默认为 root 用户,并且所在文件夹为 root
          TARGET: /root/node-server # 打包后的 dist 文件夹将放在 /root/node-server

保存,推送到 GitHub 上。

以后只要你的项目执行 git push 操作,就会自动执行 ci.yml 定义的脚本,将打包文件放到你的阿里云静态服务器上。

这个 Actions 主要做了两件事:

  1. 克隆你的项目,下载依赖,打包。
  2. 用你的阿里云私钥以 SSH 的方式登录到阿里云,把打包的文件上传(使用 rsync)到阿里云指定的文件夹中。

如果还是不懂,建议看一下我的 demo

ci.yml 配置文件讲解

  • name,表示这个工作流程(workflow)的名称。
  • on,表示监听的意思,后面可以加上各种事件,例如 push 事件。

下面这段代码表示要监听 master 分支的 push 事件。当 GitHub Actions 监听到 push 事件发生时,它就会执行下面 jobs 定义的一系列操作。

name: Build app and deploy to aliyun
on:
  #监听push操作
  push:
    branches:
      # master分支,你也可以改成其他分支
      - master
jobs:
...
  • jobs,看字面意思就是一系列的作业,你可以在 jobs 字段下面定义很多作业,例如 job1job2 等等,并且它们是并行执行的。
jobs:
  job1:
  	...
  job2:
  	...
  job3:
	...

回头看一下 ci.yml 文件,它只有一个作业,即 build,作业的名称是自己定义的,你叫 good 也可以。

  • runs-on,表示你这个工作流程要运行在什么操作系统上,ci.yml 文件定义的是最新稳定版的 ubuntu。除了 ubuntu,它还可以选择 Mac 或 Windows。
image-16

steps,看字面意思就是一系列的步骤,也就是说这个作业由一系列的步骤完成。例如先执行 step1,再执行 step2...

setps 步骤讲解

setps 其实是一个数组,在 YAML 语法中,以 - 开始就是一个数组项。例如 ['a', 'b', 'c'] 用 YAML 语法表示为:

- a
- b
- c

所以 setps 就是一个步骤数组,从上到下开始执行。从 ci.yml 文件来看,每一个小步骤都有几个相关选项:

  1. name,小步骤的名称。
  2. uses,小步骤使用的 actions 库名称或路径,GitHub Actions 允许你使用别人写好的 Actions 库。
  3. run,小步骤要执行的 shell 命令。
  4. env,设置与小步骤相关的环境变量。
  5. with,提供参数。
d2b7820e4276136a2497417ef77b76f5

综上所述,ci.yml 文件中的 setps 就很好理解了,下面从头到尾解释一边:

    steps:
    - uses: actions/checkout@v1
    - name: Install Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '12.16.2'
    - name: Install npm dependencies
      run: npm install
    - name: Run build task
      run: npm run build
    - name: Deploy to Server
      uses: easingthemes/ssh-deploy@v2.1.5
      env:
          SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
          ARGS: '-rltgoDzvO --delete'
          SOURCE: dist # 这是要复制到阿里云静态服务器的文件夹名称
          REMOTE_HOST: '118.190.217.8' # 你的阿里云公网地址
          REMOTE_USER: root # 阿里云登录后默认为 root 用户,并且所在文件夹为 root
          TARGET: /root/node-server # 打包后的 dist 文件夹将放在 /root/node-server
  1. 使用 actions/checkout@v1 库克隆代码到 ubuntu 上。
  2. 使用 actions/setup-node@v1 库安装 nodejs,with 提供了一个参数 node-version 表示要安装的 nodejs 版本。
  3. ubuntushell 上执行 npm install 下载依赖。
  4. 执行 npm run build 打包项目。
  5. 使用 easingthemes/ssh-deploy@v2.1.5 库,这个库的作用就是用 SSH 的方式远程登录到阿里云服务器,将打包好的文件夹复制到阿里云指定的目录上。

env 上可以看到,这个 actions 库要求我们提供几个环境变量:

  1. SSH_PRIVATE_KEY: 阿里云密钥对中的私钥(需要你提前写在 github secrets 上),
  2. ARGS: '-rltgoDzvO --delete',没仔细研究,我猜是复制完文件就删除掉。
  3. SOURCE:打包后的文件夹名称
  4. REMOTE_HOST: 阿里云公网 IP 地址
  5. REMOTE_USER: 阿里云服务器的用户名
  6. TARGET: 你要拷贝到阿里云服务器指定目录的名称

如果你想了解一下其他 actions 库的实现,可以直接复制 actions 库的名称去搜索引擎搜索一下,例如搜索 actions/checkout 的结果为:

c86a63a2ff60d493ea04a9c380459339

部署方式

部署有很多种方式,据我所知的有:蓝绿部署、滚动发布、灰度发布等等。当然,还有更简单的方式,直接停掉服务器,上传代码后再重新开启服务器。不过这种方式有一个很大的缺点:在服务器重启过程中,用户无法访问网站的服务,所以你可能会收到大量的投诉。

下面让我们来简单地了解一下这三种部署方式的区别吧(参考自脉冲云文档)。

蓝绿部署

3aaf991ad66c6070beba781024dda558

蓝绿部署是指在部署过程中同时运行两个版本的程序。部署新版本时,不停掉旧版本的服务器,然后等新版本运行起来后,再将流量切换到新版本。缺点是在部署过程中,需要配置双倍的服务器。

滚动发布

滚动发布是指在升级过程中,逐台逐台的替换旧版本服务器。先启动一台新版本的服务器,再停掉一台旧版本的服务器。这样在部署过程中只需要 N + 1 台的服务器。

bc3696dbf56407661330d3e8dfabb99b

灰度发布

灰度发布也叫金丝雀发布,起源是,矿井工人发现,金丝雀对瓦斯气体很敏感,矿工会在下井之前,先放一只金丝雀到井中,如果金丝雀不叫了,就代表瓦斯浓度高。

dfd738a930f2b24a1cfbc30897de059a

灰度发布在新开启一台服务器后,先不将流量切换过来。而是先由测试人员对其进行测试,如果运行没问题,再将流量切换过来。同时在运行期间收集各种数据,如果此时将新旧版本的数据进行对比,就是所谓的 A/B 测试。

当发现新版本运行良好后,再将剩下的服务器用同样的过程逐步替换。最后完全关掉旧版本的服务器,完成灰度发布。

如果在发布过程中发现新版本有问题,就可以将流量切回到旧版本服务器,这样将负面影响控制到最小。

小结

本章选用 Jenkins 和 GitHub Actions 来讲解自动化部署有两个原因:

  1. 免费。
  2. 使用人数比较多,很多坑都有现成的解决方案。

希望各位同学在学完本章内容后,能够运用在项目中。很多知识只有亲自实践后,才能理解它的好处。

参考资料