一、测试环境的CI/CD搭建

在将整个统计服务(Node应用)作为一个 Docker 容器发布到服务器上时,这带来一个问题,即这个 Node 应用该如何访问宿主机上的 Nginx 日志呢?

测试环境的 CI/CD 部署方案如下图所示,Docker 容器通过 docker-host 访问宿主机的数据库,并利用volumes 将宿主机的 Nginx 日志映射到 Docker 容器中。
企业微信20210819085729.png

1.1 基于 Git Actions 搭建 CI/CD 环境

测试环境的 CI/CD 环境是基于 Git Actions 搭建的,整个 workflows 的配置流程如下:

  1. 安装依赖,并在云端检测代码提交是否符合代码规范、是否满足单元测试(保证质量);
  2. 登录到远程服务器(利用远程服务器私钥),进入相应的项目目录下;
  3. 切换开发分支,并拉取代码;
  4. 构建镜像,然后运行该镜像;

测试环境的 deploy-dev.yml 配置

  1. name: deploy for develop
  2. on:
  3. push:
  4. branches:
  5. - 'develop' # 只针对 develop 分支
  6. paths:
  7. - '.github/workflows/*'
  8. # - '__test__/**' # 测试环境不需要立即测试
  9. - 'app/**'
  10. - 'Dockerfile'
  11. - 'docker-compose.yml'
  12. - 'bin/*'
  13. jobs:
  14. deploy:
  15. runs-on: ubuntu-latest
  16. steps:
  17. - name: checkout
  18. uses: actions/checkout@v2
  19. - name: Use Node.js
  20. uses: actions/setup-node@v1
  21. with:
  22. node-version: 14
  23. # 进行云端代码检测:是否符合代码、是否满足单元测试
  24. - name: lint and test # 测试
  25. run: |
  26. yarn
  27. yarn lint
  28. yarn test
  29. # 设置临时 ssh key,用于登录服务器拉取代码
  30. - name: set ssh key
  31. # 将远程服务器的 RSA 添加至 Git Actions 的 secrets 中
  32. run: |
  33. mkdir -p ~/.ssh/
  34. echo "${{secrets.SSH_RSA}}" > ~/.ssh/id_rsa
  35. chmod 600 ~/.ssh/id_rsa
  36. # 查看远程服务器的公钥,并将公钥内容保存到known_hosts(ssh命令默认使用的文件是/.ssh/known_hosts)
  37. # ssh-keyscan 端口号要设置正确,否则会构建失败!!!
  38. ssh-keyscan -p 42xx2 xxxx.com >> ~/.ssh/known_hosts
  39. # 部署(登录远程服务器,拉取代码,构建镜像)
  40. - name: deploy
  41. # 在进行部署操作前,要先登录服务器,完成下列操作:
  42. # 操作一:将远程服务器的 RSA 存储至 GIT 全局设置中
  43. # 操作二:创建 /home/work/ii-server/ii-server 目录
  44. # 操作三:git clone git@github.com:username/cli-server.git -b develop
  45. run: |
  46. ssh -p 42xx2 root@xxxx.com "
  47. cd /home/work/ii-server/cli-server;
  48. git checkout develop;
  49. # 重新下载最新代码
  50. git pull origin develop;
  51. # 启动 docker-compose, 与 docker-compose.yml 中的 service 名字保持一致
  52. docker-compose build ii-server;
  53. docker-compose up -d;
  54. "
  55. # 删除临时 ssh key
  56. - name: delete ssh key
  57. run: rm -rf ~/.ssh/id_rsa

1.2 Dockerfile 文件配置

Dockerfile 配置文件的主要作用是设置容器时区、创建 nginx 日志目录、安装依赖包、设置docker-host,并将整个 Node 服务跑起来。其中 /spin/ip route 命令会列出当前系统的路由信息,并将 docker-host 域名解析默认的静态路由上。

测试环境的Dockfile文件

  1. # FROM 代表有一个基础镜像
  2. FROM node:14
  3. # 创建一个工作目录
  4. WORKDIR /app
  5. # 将当前目录下的所有文件或资源复制到镜像中(注:针对项目中的 node_modules 或
  6. # dist 目录等不需要拷贝到镜像的文件,可以借助.dockerignore)
  7. COPY . /app
  8. # 设置时区(非常重要!!!!)
  9. RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
  10. # 创建 nginx access.log 文件目录(文件目录如何映射到宿主机,可参照 docker-compose.yml 配置)
  11. RUN mkdir -p nginx_logs/event_analytics
  12. # 使用淘宝的镜像源进行加速
  13. RUN npm set registry https://registry.npm.taobao.org
  14. # 使用 --production 参数,或者当 NODE_ENV 环境变量被设置为 production,
  15. # yarn 将不会安装 package.json 中的 devDependencies。
  16. RUN yarn --production
  17. # CMD 用于执行脚本。相当于镜像运行起来之后,它就会执行CMD后的命令。
  18. # 1. docker-host 指向宿主机 ip,以方便 docker 内部访问宿主机;
  19. # 2. 执行脚本 npm run start(通过 egg-scripts 来运行线上环境);
  20. CMD /sbin/ip route|awk '/default/ { print $3,"\tdocker-host" }' >> /etc/hosts && npm run start

下面详细讲解下这条命令 /sbin/ip route|awk '/default/ { print $3,"\tdocker-host" }' >> /etc/hosts 的具体含义。

iproute 工具包
iproute 是 Linux 下一个网络管理工具包合集,用于取代先前的如 ifconfig、route、ifup、ifdown、netstat等历史网络管理工具。该工具包功能强大,它通过网络链路套接字接口与内核进行联系。

  1. ip link
  2. # 网络设备配置命令,如可以启用/禁用某个网络设备,改变 mtu 及 mac 地址等
  3. ip addr
  4. # 用于管理某个网络设备与协议(ip或ipv6)有关的地址。
  5. # 与 ip link 类似,不过增加了协议有关的管理(ip地址管理)
  6. ip route
  7. # 管理路由,如添加,删除

awk 文本分析工具
awk 是一个强大的文本分析工具,在其对数据分析并生成报告时,显得尤为强大。简单来说 awk 就是把文件逐行的读入,以空格为默认分隔将每行切片,切开的部分再进行各种分析处理。awk 它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。

使用方法

awk ‘{pattern + action}’ {filenames}

尽管操作可能会很复杂,但语法总是这样,其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。 pattern就是要表示的正则表达式,用斜杠括起来

通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。

示例1
命令:

  1. last -n 5 | awk '{print $3,"\tdocker-host"}'

结果输出:
企业微信20210824093324.png
其中:

  • $0 变量是指整条记录$1 表示当前行的第一个域,$2 表示当前行的第二个域,……以此类推。
  • print 是打印输出函数,其中print 函数的参数可以是变量、数值或者字符串。字符串必须用双引号引用,参数用逗号分隔。如果没有逗号,参数就串联在一起而无法区分。

示例2
命令:

  1. /sbin/ip route | awk '/default/ {print $3,"\tdocker-host"

结果输出
企业微信20210824100623.png

**/ect/hosts** 文件
Linux 的 /etc/hosts 这个文件告诉主机哪些域名或主机名分别对应哪些ip。比如文件中有这样的定义

  1. 192.168.102.136 dbfan aeolus

假设192.168.102.136是一台网站服务器,在网页中输入 http://dbfanhttp://aeolus 就会打开192.168.102.136的网页,其中 dbfan 是域名aeolus 是主机名别名

1.3 Docker Compose 配置

docker-compose.yml 配置文件主要作用是构建一个 Docker 镜像,并将容器内的目录映射到宿主机上。
测试环境的**docker-compose.yml**配置

  1. version: '3'
  2. # 服务
  3. services:
  4. # 服务 ii-server
  5. ii-server:
  6. # 服务除了可以基于指定的镜像,还可以基于一份 Dockerfile,在使用 up 启动之时执行构建任务,
  7. # 这个构建标签就是 build,它可以指定 Dockerfile 所在文件夹的路径。Docker Compose 将会
  8. # 利用它自动构建这个镜像,然后使用这个镜像启动服务容器。
  9. build:
  10. # context 选项是 Dockerfile 的文件路径
  11. context: .
  12. # 使用此 dockerfile 文件来构建,必须指定构建路径
  13. dockerfile: Dockerfile
  14. # 指定服务的镜像名称或镜像ID
  15. image: event-analytics-server
  16. # 为自定义的容器指定一个名词,而不是使用默认名称
  17. container_name: ii-server
  18. # 映射端口
  19. ports:
  20. # 将容器内部的服务端口 3002 映射到宿主机的 8080 端口上
  21. - 8080:3002
  22. # 挂载一个目录或者一个已存在的数据卷容器,可直接使用HOST:CONTAINER这样格式
  23. volumes:
  24. # 将宿主机目录/home/work/nginx_logs/event_analytics映射到容器目录/app/nginx_logs/event_analytics
  25. - /home/work/nginx_logs/event_analytics:/app/nginx_logs/event_analytics

1.4 Nginx 配置

测试环境的 Nginx 配置

  1. # 自定义事件统计服务 Nginx 配置文件
  2. #
  3. # 注:
  4. # 1. 此处 nginx 配置文件跑在宿主机下,监听端口号与 Node 应用的端口号不是一致的
  5. # 2. 需在 /ect/nginx/nginx_conf 把当前配置include进去
  6. server {
  7. # 记得在服务器上开启相应的防火墙!!!
  8. listen 8080;
  9. server_name event_analytics;
  10. # 一定要在服务器上先创建目录以及相应的文件,否则会报错(日志格式为main格式)
  11. access_log /home/work/nginx_logs/event_analytics/access.log main;
  12. error_log /home/work/nginx_logs/event_analytics/error.log;
  13. # 静态资源目录地址(获取event.gif图片等)
  14. root /home/work/ii-server/cli-server/nginx_files;
  15. }

二、线上环境的CI/CD搭建

测试环境下,我们通过 push develop 分支来触发 github actions,并实现自动发布到测试机。但如果要上线该什么时机触发呢,是 push master 分支的时候吗?

在测试环境下基于 push develop 分支触发部署,这样简单方便,因为测试环境通常不需要回滚。而线上环境,使用 tag 触发部署而不是 master 分支,这是因为:

  • master 分支可能会被频繁合并,但不能频繁上线。push tag可以作为上线正式的“仪式”
  • 回滚时,使用 tag 更加方便。否则,就得在 master 分支去 revert commit 记录(操作很麻烦);

因此线上环境的 CI/CD 环境搭建主要基于:

  • git tag + github actions 实现上线和回滚;
  • 使用 Shell 脚本 release-it 进行改造,增加 changeLog

2.1 上线流程

部署线上服务
参考 bin/deploy.sh

  1. #!/bin/bash
  2. # 执行时需传入参数,即参数$1表示git tag,命令示例如`sh bin/deploy.sh v1.0.1`
  3. # 在 shell 代码中使用$1、$2即可依次获取参数
  4. serverPath="/home/prod/server"
  5. repoPath="/home/prod/server/cli-server" # 项目目录,要和repo同名
  6. repoGitUrl="git@github.com:xxxxx/cli-server.git"
  7. if [ ! -d "$serverPath" ]; then
  8. # 目录不存在,则创建
  9. echo ========================== mkdir "$serverPath" =============================
  10. mkdir "$serverPath"
  11. fi
  12. cd "$serverPath"
  13. if [ ! -d "$repoPath" ]; then
  14. # 如果repo目录不存在,则git clone (私有项目,需要 github 用户名和密码)
  15. echo ========================== git clone start =============================
  16. git clone "$repoGitUrl"
  17. echo ========================== git clone end =============================
  18. fi
  19. cd "$repoPath"
  20. # 撤销一切文件改动,否则会导致 pull 失败
  21. git checkout .
  22. # 下载最新master代码,tag 都是基于 master 分支提交的
  23. git pull origin main
  24. # 获取所有 tags,否则光执行 git pull origin master 获取不到新提交的代码
  25. git fetch --tags
  26. # 切换到当前tag,重要!!!
  27. git checkout "$1"
  28. echo ========================== git clone "$1" =============================
  29. # 安装依赖
  30. npm install --registry=https://registry.npm.taobao.org
  31. # 运行服务(To do: 存在服务短暂不可用的情况)
  32. npm run stop # 停止服务
  33. npm run start # 运行服务
  34. echo ========================== deploy success =============================

基于 push tag 触发

  1. # 创建新的标签(-a指定标签名,-m指定说明文字)
  2. git tag -a v1.0.1 -m "xxxx"
  3. # 把所有 tags 提交到远端
  4. git push --tags

参考 github actions 的 deploy-prod.yml
触发时机是在tags的时机下

2.2 回滚流程

一键生成 tag

手动操作,总有失误的时候。所以,要整合为一个命令,一键执行。

  1. # 创建新的 tag
  2. git tag -a v1.0.1 -m "xxxx"
  3. # 把所有tags提交到远端
  4. git push --tags

合成一条Shell脚本命令
参考 bin/create-version-tag.sh

由于window电脑不能执行shell脚本,可以使用release-it 来进行发布。

回滚
重新执行 github actions 任务。

可以基于Git Actions可以很清晰的进行回滚。

注意:

  • 要上线,必须有版本、有回滚,否则无法闭环
  • 上线是一件非常严肃的事情,从流程上就要严肃慎重对待