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可運行文件,將其運行不需要使用到進程服務,可以直接在瀏覽器訪問,因此只需要將其放置到代理服務器中,將訪問文件地址暴露給外網訪問即可。

參考文獻

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容