使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机

开篇,我们先来看一下远古时代的构建部署流程。想必大家对这个都不陌生:

  • 开发将源码经过编译、压缩打包生成打包文件
  • 将打包生成的文件上传服务器
显然这个流程不仅繁琐,而且效率也不高,开发每次发布都要耗费很长的时间在部署构建上面。
后面为了解决这个问题,就出现了CI/CD
接下来我们来聊一下什么是CI/CD?
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

CI/CDContinuous Intergration/Continuous Deploy 的简称,翻译过来就是持续集成/持续部署CD 也会被解释为持续交付(Continuous Delivery
再具体一点就是:
  • 持续集成:当代码仓库代码发生变更,就会自动对代码进行测试和构建,反馈运行结果。
  • 持续交付:持续交付是在持续集成的基础上,可以将集成后的代码依次部署到测试环境、预发布环境、生产环境中
聊了这么多,相信很多同学一定会说:
  • 这一般不都是运维搞的吗?
  • 和业务也不相关啊,了解它有什么用?
  • 全是服务器相关的东西,dockernginx、云服务器啥的,我该怎么学习呢?
很早之前,我也是这么想的,感觉与自己的业务也没啥关系,没有太大的必要去了解。
但是最近我在搞一个全栈项目(做这个项目是为了突破自己的瓶颈)时,就遇到了这些问题,发现陷入了知识盲区。
没办法,只能一顿恶补。
但是当我通过学习这些知识和在项目中实践这些流程后,我在知识面上得到了很大的扩展。对操作系统,对实际的构建部署,甚至对工程化拥有了全新的认识。
这里也放下前面提到的全栈项目的架构图吧:
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

这个大的项目以 low code为核心,囊括了 编辑器前端编辑器后端C端H5组件库组件平台后台管理系统前端后台管理系统后台统计服务自研CLI九大系统。
其中的编辑器前端在如何设计实现 H5 营销页面搭建系统文章中已经有很详细的说明。
目前整个项目做了 70%左右,过程中遇到了很多问题,也得到了很大的提升。后续会有一波文章是关于项目中的一个个小点展开的,也都是满满的干货。
回到本篇文章的主题:使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机。本文是以后台管理系统前端为依托详细说明了如何借助DockernginxGithub CI/CD能力自动化发布一个纯前端项目。选这个项目来讲解自动化发布测试机有两个出发点:
  • 后台管理系统业务较简单,可将重心放在自动化部署流程上
  • 纯前端项目更适用于大部分前端同学现状,拿去即用
整体思路 使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

前端代码,打包出来的是静态文件,可用nginx做服务。思路:
  • 构建一个Docker容器(有nginx
  • dist/目录拷贝到Docker容器中
  • 启动nginx服务
  • 宿主机端口,对应到Docker容器端口中,即可访问
核心代码变动:
  • nginx.conf(给Docker容器的nginx使用)
  • Dockerfile
  • docker-compose.yml
?? 本文将采用理论知识和实际相结合的方式,即先讲述一下对应知识点,同时会放一下与此知识点相关的项目代码或配置文件。
下面会依次讲解Dockerdocker-composesshgithub actions等知识点。
Docker 使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

Docker很早之前,在公众号的一篇文章谁说前端不需要学习 docker?就有过详细说明。这里简单再阐述下。
docker 可以看成是一个高性能的虚拟机,主要用于 linux 环境的虚拟化。开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 linux 机器上。容器完全使用沙箱机制,相互之间不会有任何接口。
在容器中你可以做任何服务器可以做的事,例如在有 node 环境的容器中运行 npm run build 打包项目,在有 nginx 环境的容器中部署项目等等。
centos 上安装 docker
由于这次的云服务器是centos的,所以这里就提一下如何在 centos 上安装 docker
$ sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo$ sudo yum install docker-ce docker-ce-cli containerd.io$ sudo systemctl start docker$ sudo docker run hello-world

dockerfile
docker 使用 Dockerfile 作为配置文件进行镜像的构建,简单看一个 node 应用构建的 dockerfile
FROM node:12.10.0WORKDIR /usr/appCOPY package*.json ./RUN npm ci -qyCOPY . .EXPOSE 3000CMD ["npm", "start"]

说明一下每个关键字对应的含义。
FROM 基于这个 Image 开始
WORKDIR 设置工作目录
COPY 复制文件
RUN 新层中执行命令
EXPOSE 声明容器监听端口
CMD 容器启动时执行指令默认值
看下项目中的Dockerfile文件:
# Dockerfile FROM nginx# 将 dist 文件中的内容复制到 /usr/share/nginx/html/ 这个目录下面 # 所以,之前必须执行 npm run build 来打包出 dist 目录,重要!!! COPY dist/ /usr/share/nginx/html/# 拷贝 nginx 配置文件 COPY nginx.conf /etc/nginx/nginx.conf# 设置时区 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone# 创建 /admin-fe-access.log ,对应到 nginx.conf CMD touch /admin-fe-access.log && nginx && tail -f /admin-fe-access.log

在这个文件里面,我们做了下面几件事:
1、我们用了 NginxDocker image 作为 base image
2、把打包生成的文件夹dist/的全部内容放进 Nginx Docker 的默认 HTML 文件夹,也就是/usr/share/nginx/html/里面。
3、把自定义的 Nginx 配置文件nginx.conf放进 Nginx Docker 的配置文件夹/etc/nginx/nginx.conf中。
4、设置时区。
5、创建 /admin-fe-access.log,启动nginx并使用tail -f模拟类似pm2的阻塞式进程。
这里提到了nginx.conf文件:
#nginx进程数,通常设置成和cpu的数量相等 worker_processes auto; #全局错误日志定义类型 #error_loglogs/error.log; #error_loglogs/error.lognotice; #error_loglogs/error.loginfo; #进程pid文件 #pidlogs/nginx.pid; #参考事件模型 events { #单个进程最大连接数(最大连接数=连接数+进程数) worker_connections1024; }#设定http服务器 http { #文件扩展名与文件类型映射表 includemime.types; #默认文件类型 default_typeapplication/octet-stream; #日志格式设定 #$remote_addr与 $http_x_forwarded_for用以记录客户端的ip地址; #$remote_user:用来记录客户端用户名称; #$time_local: 用来记录访问时间与时区; #$request: 用来记录请求的url与http协议; #$status: 用来记录请求状态;成功是200, #$body_bytes_sent :记录发送给客户端文件主体内容大小; #$http_referer:用来记录从那个页面链接访问过来的; #$http_user_agent:记录客户浏览器的相关信息; log_formatmain'$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # access_loglogs/access.logmain; sendfileon; #tcp_nopushon; #keepalive_timeout0; #长连接超时时间,单位是秒 keepalive_timeout65; #gzipon; #设定通过nginx上传文件的大小 client_max_body_size20m; #虚拟主机的配置 server { #监听端口 listen80; #域名可以有多个,用空格隔开 server_nameadmin-fe; #charset koi8-r; #定义本虚拟主机的访问日志 access_log/admin-fe-access.logmain; # 注意,在 Dockerfile 中创建 /admin-fe-access.log#入口文件的设置 location / { root/usr/share/nginx/html; #入口文件的所在目录 indexindex.html index.htm; #默认入口文件名称 try_files $uri $uri/ /index.html; } #error_page404/404.html; # redirect server error pages to the static page /50x.html error_page500 502 503 504/50x.html; location = /50x.html { roothtml; } } }

核心点就是监听80端口,定义日志文件为admin-fe-access.log,入口文件根目录为/usr/share/nginx/html,这些都是与Dockerfile中一一对应的。
说完了Dockerfile及其相关的配置文件,下面接着来看下docker中几个核心的概念。
docker 核心概念
docker中有三个非常重要的概念:
  • 镜像(image)
  • 容器(container)
  • 仓库(repository)
一张图来表明其中的关系:
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

如果把容器比作轻量的服务器,那么镜像就是创建它的模版,一个 docker镜像可以创建多个容器,它们的关系好比 JavaScript实例的关系。
镜像(image)常用命令:
  • 下载镜像:docker pull :
  • 查看所有镜像:docker images
  • 删除镜像:docker rmi
  • 上传镜像:docker push /:
如果 docker images出现 repository的情况,可以运行 docker image prune删除
容器(container)常用命令
  • 启动容器:docker run -p xxx:xxx -v=hostPath:containerPath -d --name
    • -p 端口映射
    • -v 数据卷,文件映射
    • -d 后台运行
    • --name 定义容器名称
  • 查看所有容器:docker ps(加-a显示隐藏的容器)
  • 停止容器:docker stop
  • 删除容器:docker rm (加-f强制删除)
  • 查看容器信息(如 IP 地址等):docker inspect
  • 查看容器日志:docker logs
  • 进入容器控制台:docker exec -it /bin/sh
镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说:镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。
docker-compose 使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

docker-compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
使用 compose 的最大优点是你只需在一个文件中定义自己的应用程序栈(即应用程序需要用到的所有服务),然后把这个 YAML 文件放在项目的根目录下,与源码一起受版本控制。其他人只需 clone 你的项目源码之后就可以快速启动服务。
通常适用于项目所需运行环境(对应多个docker容器)较多的场景,例如同时依赖于nodejsmysqlmongodbredis等。
这里放下docker-compose.yml文件:
version: '3' services: admin-fe: build: context: . dockerfile: Dockerfile image: admin-fe # 引用官网 nginx 镜像 container_name: admin-fe ports: - 8085:80 # 宿主机可以用 127.0.0.1:8085 即可连接容器中的数据库

基于上文的Dockerfile创建镜像,端口映射是8085:80,这里的8085是宿主机端口,80对应的是nginx暴露的 80 端口
常用命令
  • 构建容器:docker-compose build
  • 启动所有服务器:docker-compose up -d(后台启动)
  • 停止所有服务:docker-compose down
  • 查看服务:docker-compose ps
ssh 及云服务器 使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

首先说下云服务器,既然要一键部署测试机,那么肯定要有台测试机,也就是云服务器,这里我用的是阿里云CentOS 8.4 64位的操作系统。
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

有了服务器,那怎么登陆呢?
本地登陆云服务器的方式一般有两种,密码登陆和 ssh 登陆。但是如果采用密码登陆的话,每次都要输入密码,比较麻烦。这里采用ssh登陆的方式。关于如何免密登录远程服务器,可以参考SSH 免密登陆配置
此后每次登陆都可以通过ssh @的方式直接免密登陆了。
云服务器安装指定包 接着要给云服务器安装基础包,在CentOS安装指定包一般用的是yum,这个不同于npm
docker
# Step 1: 卸载旧版本 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # Step 2: 安装必要的一些系统工具 sudo yum install -y yum-utils # Step 3: 添加软件源信息,使用阿里云镜像 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # Step 4: 安装 docker-ce sudo yum install docker-ce docker-ce-cli containerd.io # Step 5: 开启 docker服务 sudo systemctl start docker # Step 6: 运行 hello-world 项目 sudo docker run hello-world

如果你像我一样,有Hello from Docker!的话那么Docker就安装成功了!
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

docker-compose
通过访问 https://github.com/docker/compose/releases/latest 得到最新的 docker-compose 版本(例如:1.27.4),然后执行一下命令安装 docker-compose
# 下载最新版本的 docker-compose 到 /usr/bin 目录下 curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/bin/docker-compose# 给 docker-compose 授权 chmod +x /usr/bin/docker-compose

安装完,命令行输入docker-compose version来验证是否安装成功:
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

node
首先确保可以访问到EPEL库,通过运行以下命令来安装:
sudo yum install epel-release

现在可以使用yum命令安装Node.js了:
sudo yum install nodejs

验证一下:
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

nginx
yum 安装 nginx 非常简单,输入一条命令即可:
$ sudo yum -y install nginx# 安装 nginx

git
同样也是使用yum来安装:
yum install git

最后来看一下github actions,也是串联起了上面提到的各个点。
github actions 使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

大家知道,持续集成由很多操作组成,比如拉取代码、执行测试用例、登录远程服务器,发布到第三方服务等等。GitHub 把这些操作就称为 actions
我们先来了解一下一些术语
  • workflow(工作流程):持续集成一次运行的过程,就是一个 workflow。
  • job(任务):一个 workflow 由一个或多个 jobs 构成,含义是一次持续集成的运行,可以完成多个任务。
  • step(步骤):每个 job 由多个 step 构成,一步步完成。
  • action(动作):每个 step 可以依次执行一个或多个命令(action)。
workflow 文件
GitHub Actions 的配置文件叫做 workflow 文件,存放在代码仓库的.github/workflows目录。
workflow 文件采用 YAML 格式,文件名可以任意取,但是后缀名统一为 .yml,比如 deploy.yml。一个库可以有多个 workflow 文件。 GitHub 只要发现 .github/workflows目录里面有 .yml文件,就会自动运行该文件。
workflow 文件的配置字段非常多,这里列举一些基本字段。
name name字段是 workflow 的名称。
如果省略该字段,默认为当前 workflow 的文件名。
name: deploy for feature_dev

on on字段指定触发 workflow 的条件,通常是pushpull_request
指定触发事件时,可以限定分支或标签。
on: push: branches: - master

上面代码指定,只有master分支发生push事件时,才会触发 workflow
jobs jobs字段,表示要执行的一项或多项任务。其中的runs-on字段指定运行所需要的虚拟机环境。
runs-on: ubuntu-latest

steps steps字段指定每个 Job 的运行步骤,可以包含一个或多个步骤。每个步骤都可以指定以下三个字段。
  • jobs..steps.name:步骤名称。
  • jobs..steps.run:该步骤运行的命令或者 action。
  • jobs..steps.env:该步骤所需的环境变量。
下面放一下项目中的.github/workflows/deploy-dev.yml文件:
name: deploy for feature_devon: push: branches: - 'feature_dev' paths: - '.github/workflows/*' - '__test__/**' - 'src/**' - 'config/*' - 'Dockerfile' - 'docker-compose.yml' - 'nginx.conf'jobs: deploy-dev: runs-on: ubuntu-lateststeps: - uses: actions/checkout@v2 - name: Use Node.js uses: actions/setup-node@v1 with: node-version: 14 - name: lint and test # 测试 run: | npm i npm run lint npm run test:local - name: set ssh key # 临时设置 ssh key run: | mkdir -p ~/.ssh/ echo "${{secrets.COSEN_ID_RSA}}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan "106.xx.xx.xx" >> ~/.ssh/known_hosts - name: deploy run: | ssh work@106.xx.xx.xx " cd /home/work/choba-lego/admin-fe; git remote add origin https://Cosen95:${{secrets.COSEN_TOKEN}}@github.com/Choba-lego/admin-fe.git; git checkout feature_dev; git config pull.rebase false; git pull origin feature_dev; git remote remove origin; # 构建 prd-dev # npm i; # npm run build-dev; # 启动 docker docker-compose build admin-fe; # 和 docker-compose.yml service 名字一致 docker-compose up -d; " - name: delete ssh key run: rm -rf ~/.ssh/id_rsa

这里概述一下:
1?? 整个流程在代码pushfeature_dev分支时触发。
2?? 只有一个job,运行在虚拟机环境ubuntu-latest
3?? 第一步使用的是最基础的action,即actions/checkout@v2,它的作用就是让我们的workflow可以访问到我们的repo
4?? 第二步是在执行工作流的机器中安装node,这里使用的actionactions/setup-node@v1
5?? 第三步是执行linttest
6?? 第四步是临时设置 ssh key,这也是为了下一步登录服务器做准备。
7?? 第五步是部署,这里面先是ssh登录服务器,拉取了最新分支代码,然后安装依赖、打包,最后启动docker,生成镜像。到这里测试机上就有了Docker服务。
8?? 最后一步是删除ssh key
最后来github看一下完整的流程:
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

其中deploy阶段算是核心了:
使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机
文章图片

总结 洋洋洒洒写了这么多,也不知道你看明白了不
如果有任何问题,欢迎评论区留言,看到后会第一时间解答
【使用Docker|使用Docker Compose、Nginx、SSH和Github Actions实现前端自动化部署测试机】后续会有很多关于这个项目的文章,也请大家多多关注~

    推荐阅读