github-actions部署koa实现CI/CD

背景

一般公司的CI/CD流程相对来说较为专业、成熟且复杂,需要考虑通用性、安全性、可维护性等等,假如我们个人开发者也想要搞CI/CD,功能相对简单,使用的是github仓库,可以考虑github actions,接下来我就以一个koa的demo来实现CI/CD。

目标

我们要实现的流程包括,push之后将构建成为镜像,推送到docker hub中,服务器拉取docker hub最新镜像并在宿主机运行容器,钉钉能够监控CI/CD流程是否成功,并推送到钉钉群中。

image-20211222095638878.png

新建koa项目

初始化kao

初始一个空项目

# 创建文件夹,名称为koa-demo
mkdir koa-demo

# 初始化一个package.json描述文件,描述这个NPM包的所有相关信息,包括作者、简介、包依赖、构建等信息
npm init -y

接下来安装koa,在终端输入命令

npm install koa
# 或者
yarn add koa

安装完毕后需要在根目录下创建一个index.js文件,来使用一下koa

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3010);

koa项目初始化完毕后,我们要跑起来看看,找到package.json,在scripts中新增命令行,命令为start,意思为启动项目,执行它会映射为执行node index.js,在node环境下执行index.js文件的代码,index.js就是我们上面创建的文件。

  "scripts": {
    "start": "node index.js"
  },

验证项目是否成功,直接在终端输入

npm start
# 或者
yarn start

验证kao

然后在浏览器访问:localhost:3010,浏览器看到hello world字样即为成功。

img

小结

经过以上步骤,一个简单的koa项目创建完毕,并能够在本地运行正常运行起来,接下来需要将其打包构建成为一个镜像,最终成为容器运行起来。

部署koa项目

部署koa项目第一步需要先编写一个构建镜像的脚本文件。

dockerfile

我们的部署需要用到docker,首先需要在根目录下创建两个文件

  • Dockerfile
  • .dockerignore

Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。Docker通过docker build执行Dockerfile中的一系列命令自动构建image镜像。

.dockerignore就类似于git中的.gitignore在构建过程忽略的内容。

Dockerfile文件需要写入以下内容

# 制定node镜像的版本
FROM node:17
# 移动当前目录下面的文件到app目录下
ADD . /app/
# 进入到app目录下面,类似cd
WORKDIR /app
# 安装依赖
RUN npm install
# 对外暴露的端口,这里的3010需要和inde.js监听的端口一致
EXPOSE 3010
# 程序启动脚本,意思为 执行 npm start
CMD ["npm", "start"]
  • FROM:构建所需要的镜像,就是一个定制化的过程,那么就需要以一个镜像为基础,在其基础上进行修改定制。而FROM指令就是用来指定基础镜像,因此在Dockerfile中,FROM指令是必备指定,并且必需是第一条指令!

  • ADD:当from的镜像拉取下面之后,可以将后续的文件内容都放到该镜像指定的目录位置,比如上面的将运行的koa项目放入到了node镜像下的/app/目录下。

  • RUN:构建镜像时执行的命令。RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache选项,如:docker build --no-cache

  • CMD:Docker不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,就需要指定运行的程序及参数。所以,CMD指令的主要用途是为启动容器时指定运行的程序及参数,而RUN指令用于指定镜像构建时所要执行的命令。

.dockerignore文件写入一下内容,它的作用的过滤掉一些不必要的文件。

# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history
.idea
.node_modules
node_modules
.vscode

docker构建镜像

我本机安装了docker桌面端,所以可以在本地测试。docker桌面端下载地址

在项目根目录下,使用docker下的build命令构建镜像,-t为镜像的名字及标签

docker build -t koa-demo .

终端会输出下面内容

[+] Building 4.3s (10/10) FINISHED                                                                                 
 => [internal] load build definition from Dockerfile                                                          0.0s
 => => transferring dockerfile: 301B                                                                          0.0s
 => [internal] load .dockerignore                                                                             0.0s
 => => transferring context: 661B                                                                             0.0s
 => [internal] load metadata for docker.io/library/node:17                                                    2.5s
 => [auth] library/node:pull token for registry-1.docker.io                                                   0.0s
 => [internal] load build context                                                                             0.0s
 => => transferring context: 1.12kB                                                                           0.0s
 => CACHED [1/4] FROM docker.io/library/node:17@sha256:13621aa823b6b92572d19c08a75f7b1a061633089f37873f8b5be  0.0s
 => [2/4] ADD . /app/                                                                                         0.0s
 => [3/4] WORKDIR /app                                                                                        0.0s
 => [4/4] RUN npm install                                                                                     1.6s
 => exporting to image                                                                                        0.1s
 => => exporting layers                                                                                       0.1s
 => => writing image sha256:c33e69d13b82487230b435ee7ae117b209d14a11631a57c57ec4dee9a92f5f5e                  0.0s
 => => naming to docker.io/library/koa-demo
  • 上面执行build会触发dockerfile文件,从docker中指定一个node版本,这里指定为最新的17版本,然后从docker仓库中拉取对应版本的node,其实是执行了docker pull node:17

  • 接下来将koa项目所有文件(不包括被忽略的文件)添加进/app/目录下

  • 接下来进入app目录内容,将app设置为工作目录

  • 接下来安装依赖

以上就是dockerfile执行的所以流程了,那么我们如何运行起来呢,这个就是CMD命令做的事情了,CMD是负责指定运行的程序和参数。

查看镜像

先不着急启动,我们先看看镜像是否构建成功了。

docker images
image-2.png

可以看到镜像的构建成功了的。

运行镜像

将镜像运行起来,会触发CMD命令,会将koa项目跑起来,并且我们要在浏览器中看到运行的koa输出内容。

docker run -d -p 8888:3010 koa-demo
  • -d 是后台运行,不要阻塞shell指令窗口

  • -p 来制定内外端口映射

  • 8888 将内部端口和外部端口3010做一个简单的映射,这里是 外部端口:内部端口

运行docker-demo镜像,并设置运行的内部端口为3010,因为koa设置的运行端口为3010所以要保持一致,8888是映射给容器外面的端口,即为用户访问的端口。

查看容器

docker ps
image-3.png

浏览器访问项目

容器启动成功后,docker就启动了一个进程。

在浏览器访问:http://localhost:8888

image-4.png

浏览器已经可以正常查看到运行的项目。

小结

编写了Dockerfile脚本文件,负责执行构建的流程,为了优化构建包.dockerignore大小忽略一下没必要的文件。

通过执行docker build命令触发Dockerfile脚本生成了一个koa-demo镜像,执行docker run运行了一个容器,最后我们可以在浏览器中访问运行中的koa项目。

github actions

项目在本地docker环境能够正常运行,接下来开始用到github actions。

使用github actions

在github中,如果已经有项目的可以先把项目提交,若没有项目,直接新建一个项目,随后将本地修改后的代码提交上去。

随后点击并打开Actions页面

image-5.png

会展示很多种构建类型,我们不用管它,只选择第一个,最简单的镜像构建就可以了。

image-6.png

选择第一个docker镜像后,会生成一些代码

image-7.png

镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,我们公司内部使用的是私有仓库,这里我们使用Docker Hub共有仓库即可。

因为我们需要将docker镜像构建完毕后推送到Docker Hub中,所以需要新增一个镜像推送,需要用到Docker Hub的账户和密码,需要去注册一下,点我去注册

docker-image.yml需要做一些简单的修改,内容如下:

name: Docker Image CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Build the Docker image
        uses: elgohr/Publish-Docker-Github-Action@master
        with:
          name: smallzip7799/koa-github-action
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

为了更好的理解,来讲解一下每一行的含义

  • line1:actions工作流的名称。

  • line3~7:当我们用main分支,将代码推送(push或者pull_request)到仓库的时候,会触发我们的工作流程(workflow)。

  • line8~11:我们定义了一个workflow工作流,由一个或者多个jobs构成,能一次持续集成的运行,完成多个任务。后面的每个工作流都在虚拟环境的新实例中运行,其中的runs-on字段指定运行所需要的虚拟环境。

  • line13steps指定每一个job的运行步骤,可以包含一个或者多个步骤。

  • line14:使用github提供的actions/checkout@v2,它的作用就是让我们的workflow可以访问到repo源码的内容。

  • line15:第二个步骤的名称,可以根据实际情况写个步骤简要描述,它会显示在构建流程中

  • line16:使用用户名为elgohr定制的Publish-Docker-Github-Action@master钩子执行docker镜像的构建和推送流程,最终推送到docker hub中。

  • line17~20:这里定义推送的docker hub所需要的参数

    • name:是推送到docker hub的镜像名称
    • username:registry的登录用户名,即为docker hub的用户名
    • password:registry的登录密码,即为docker hub的密码

其中的usernamepasword是属于私密内容,不应该直接显示在代码中,因此我们需要在github 的secrets设置一下,将usernamepassword设置为局部变量。

image-8.png

写完代码后,直接提交,第一次会执行一次构建流程。

image-9.png

显示打钩则代表构建成功了。

详细的pipeline流程,可以点击进去查看详情。

image-10.png

检查镜像

推送代码后,我们可以通过在docker hub里面检查已经构建成功的镜像。

image.png

又或者在本机docker桌面端中查看

image.png

部署到服务器

当前构建过程中,只包含了镜像的构建,并推送到docker hub公共仓库,还没有做到在服务器中的部署流程。这些操作需要使用到:

  • 一个个人服务器
  • 服务器中启动openssh-server

如果有个人服务器可以直接看下一步,如果没有服务器,可以去腾讯云、阿里云、华为云这些平台购买一个便宜的服务器用来学习,这里推荐不到25岁的小伙伴们,去腾讯云购买学生服务器,一年只需要108rmb,购买地址

购买好服务器,第一步是安装好openssh-server,第二步是安装好docker环境,由于我购买的是CentOS7.6,不同服务器安装方式不太一样,具体安装教程参见官网

新增部署流程

github actions默认生成的workflow工作流只包含了镜像推送,要实现服务器的部署和钉钉推送需要再新增两个工作流

name: Docker Image CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Build the Docker image
        uses: elgohr/Publish-Docker-Github-Action@master
        with:
          name: smallzip7799/koa-github-action
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      # 部署服务器
      - name: executing remote ssh commands using password
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          password: ${{ secrets.PASSWORD }}
          port: 22
          script: |
            docker pull smallzip7799/koa-github-action
            docker inspect -f '{{.Name}}' koa-docker
            if [ $? == 0 ] ;then
              echo "docker exist"
              docker container stop koa-docker
              docker container rm koa-docker
              docker images remove smallzip7799/koa-github-action
            else
              echo "docker don't exist"
            fi
            docker run -d -p 8888:3010 --name koa-docker smallzip7799/koa-github-action

      # 发送钉钉消息
      - name: dingtalk robot message
        uses: fifsky/dingtalk-action@master
        with:
          url: ${{ secrets.DINGTALK_WEBHOOK}}
          type: markdown
          content: |
            ## 监控提示: Success 
              构建成功了

为了都能理解,这里解释新增的两个工作流:

  • line32executing remote ssh commands using password是当前工作流的名称或者描述,可以自己修改。
  • line24:这里使用了第三方用户封装好的actions钩子,它的作用是通过ssh登录远程服务器。(持续集成由很多操作组成,比如抓取代码、运行测试、登录远程服务器,发布到第三方服务等等。GitHub 把这些操作就称为 actions,如果需要某个 action,不必自己写复杂的脚本,直接引用他人写好的 action 即可,actions仓库)。
  • line25with表示actions拥有参数。
  • line26~line29host是服务器公网的ip地址,usernamepassword分别是docker注册是账户名和密码,post是ssh访问端口,默认为22。其中hostusernamepassword建议使用局部变量,不要直接现实在代码中。
  • line30~line41:执行shell命令
    • docker pull smallzip7799/koa-github-actions:拉取docker hub上面的镜像
    • docker inspect -f '{{.Name}}' koa-docker:检查是否存在名字为koa-docker的容器
    • if [ $? == 0 ] ;then$?是代表上一条命令的返回值,判断是否存在
    • docker container stop koa-docker:将koa-docker容器暂停
    • docker container rm koa-docker:删除koa-docker容器
    • docker run -d -p 8888:3010 --name koa-docker smallzip7799/koa-github-action:运行镜像,设置对外端口8888,映射到内部端口3010,设置容器的名称为koa-docker
  • line43dingtalk robot message工作流名称
  • line44fifsky/dingtalk-action@master,使用钉钉消息通知actions
  • line46~line50url为钉钉配置的webhooktypemarkdown,内容中包括了钉钉通知配置的关键词和内容。参见钉钉机器人

重新构建

修改完毕后,提交代码,等待github actions工作流完成,如果顺利将会在钉钉收到通知

image.png

打开浏览器输入服务器IP地址+端口号,会看到

image.png

以上即为github actions实现的koa项目CI/CD。

小结

使用ssh远程到服务器,并在服务器中执行docker拉取和运行的命令,将项目运行起来。

利用钉钉群中的机器人生成webhook钩子,实现消息群通知。

总结

github actions部署koa项目最核心的地方是编写workflow工作流,工作流中可以做非常多的操作,如抓取代码、运行测试、登录远程服务器,发布到第三方服务等等,当一些操作相对复杂或者具有相似性,可以直接引用他人写好的 action,亦或自己写run命令。

当前项目是基于node运行环境,若项目为静态文件,比如vue项目、react项目则需要使用其他的运行环境,如nginx等,原因是vue、react会打包成dist或者build可运行文件,将其运行不需要使用到进程服务,可以直接在浏览器访问,因此只需要将其放置到代理服务器中,将访问文件地址暴露给外网访问即可。

参考文献

  • 阮一峰github actions入门

  • github actions官网文档

  • GitHub Marketplace page

你可能感兴趣的:(github-actions部署koa实现CI/CD)