技術
持續整合&持續交付. Docker、CircleCI、AWS、NodeJS (一)

持續整合&持續交付. Docker、CircleCI、AWS、NodeJS (一)

前言

傳統開發流程中,常常出現程式在我的電腦可以跑,但是換到你的電腦或正式環境就無法運作的狀況,因此發展出Docker 容器技術,可以使用統一的Dockerfile 來制定開發環境,並設定正式環境,解決常見環境設定問題。

解決了環境設定問題後,每次更新程式碼,都需要手動重新從Git上拉最新的程式碼下來部署,也是一件非常消耗時間成本的事情,因此便有了持續整合&持續交付(Continuous Integration & Continous Delivery),簡稱 CI&CD。CI&CD可以自動化測試,自動化部署,減少開發時的人工消耗。

網路上已有許多文章,但都已過時,因此參考其他文章並重新彙整

使用到的工具:

Git: 版本控制工具

Github: 程式碼託管

CircleCI: 自動化建置、測試、部署

Docker: 輕量級容器

AWS Elastic Beanstalk: 雲端平台

完整的部署流程圖如下

Node.js

使用Node.js作為範例,安裝Node文章請參考: Linux作業系統如何安裝最新或是指定版本的Node.js

創建一個專案目錄

$ mkdir hello-ci-workflow
$ cd hello-ci-workflow

在本地端執行 Node.js

初始化專案

$ npm init

此步驟會在hello-ci-workflow專案目錄下初始化一個Nodejs的執行環境,包含package.json等檔案。

在這邊我們用express框架作為測試,所以先安裝express

$ npm install express --save

此命令會將express框架儲存在專案目錄內,並寫入package.json

// package.json
{
  "name": "hello-ci-workflow",
  "main": "index.js",
  "dependencies": {
    "express": "^4.17.0"
  },
  "scripts": {
    "start": "node index.js"
  }
}

接著我們在index.js寫入一段程式碼

// index.js
var express = require('express') //把express引用進來
var app = express()
//設置Router
app.get('/', (req, res) => {
    res.send('Hello World!')
})
//把輸出port設置為3000
app.listen(3000, () => {
    console.log('Example app listening on port 3000!');
})
module.exports = app;

執行 npm startnode index.js 做第一次運作

$ npm start

打開瀏覽器,輸入網址 http://localhost:3000,看到結果

 

 

在本地端測試 Node.js

我們以Jest、supertest為測試套件,先進行安裝

Jest為測試軟體,supertest為模擬各種http request行為套件

$ npm install jest supertest --save-dev
// --save-dev: 寫入 package.json 的 devDependencies,正式上線環境不會被安裝

編寫測試程式 index.test.js

// index.test.js
const index = require('./index'); //把index.js import進來
const supertest = require('supertest'); // 把supertest import進來
test('HelloWorld', () => {
// 對index.js打一個get('/')的request,觀察他的respond
    supertest(index).get('/').then((res) => {
        expect(res.statusCode).toBe(200) 
        expect(res.body).toEqual(expect.any(Object)) 
        expect(res.body).toEqual(expect.objectContaining({
            result: expect.any(String)
        }))
        expect(res.body.result).toBe('Hello World!');
        done();
    });
})

簡單說明這個測試程式,為透過supertest打一個get request,並檢查他的respond是否有如你預期般地執行
檢查respond的Statue Code 是否為 200 (檢查是否正確的連線及回傳)
檢查respond的Body是否為一個物件
檢查respond的Body是否含有字串
檢查respond的Body是否回傳 Hello World!

在進行測試之前,修改package.json

// package.json
"scripts": {
    "test": "jest --coverage --forceExit",
    "start": "node index.js"
}

完成後的package.json

// package.json
{
  "name": "hello-ci-workflow",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "jest --coverage --forceExit"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.0"
  },
  "devDependencies": {
    "jest": "^24.9.0",
    "supertest": "^4.0.2"
  }
}

說明test部分為使用Jest作為測試框架,–coverage為顯示測試程式碼的涵蓋範圍,– forceExit的部分,則為當你測試完後強制結束測試。

進行測試

$ npm test

看到測試結果為

執行完測試程式後,會看到「PASS」,但也會看到紅字「Uncoverd Lines」,代表在index.js裡面,第6, 10行的程式碼沒測到。 接著回到專案目錄下,會發現多了一個coverage資料夾, 點進去看會發現,它提供給你的覆蓋率參考檔案。

設定Git

$ git init
$ vi .gitignore

修改 .gitignore,加入以下兩行,代表忽略以下資料夾更動內容

coverage
node_modules

接著就可以送出第一次commit了

$ git add . 
$ git commit -m "initial commit"

Docker 設定

當你在本機開發好,要上正式環境時,還需要額外安裝web server、node.js 、database等,而Docker就是一次設定就可以通用的一個設定檔,詳細內容可以參考:Docker 實戰系列(一):一步一步帶你 dockerize 你的應用

看完Docker介紹後,了解到設定Docker都必須有一個dockerfile的設定檔,Dockerfile範例如下

# Dockerfile

# 從 [Docker Hub](https://hub.docker.com/) 安裝 Node.js image。
FROM node:12.2.0-alpine

# 設定 container 的預設目錄位置
WORKDIR /hello-ci-workflow

# 將專案根目錄的檔案加入至 container 中
# 安裝 npm package
ADD . /hello-ci-workflow
RUN npm install && npm cache clean --force

# 開放 container 的 3000 port
EXPOSE 3000
CMD npm start

上述的dockerfile 會將本地程式碼包進docker容器內,接著安裝npm 相關的packages,最後運行一個nodejs的環境。

接著來建立這個容器

$ docker build -t hello-ci-workflow . //建立一個名為hello-ci-workflow的映像檔
$ docker run hello-ci-workflow // 運行

 

 

 

使用Docker-compose

寫好dockerfile後,為了針對不同環境會有不同的設定值,所以我們額外寫一個docker-compose來快速啟動docker

$ vi docker-compose.yml
// docker-compose.yml

version: '3.3'
services:
  hello-ci-workflow:
    image: hello-ci-workflow
    build: .


首先指定docker-compose版本,接著幫這個service取名(取為hello-ci-workflow),並且設置 image名稱(設為 hello-ci-workflow),再來 build : . 的意思為,將現有資料夾裡的內容,build 成一個image打包起來。

測試docker-compose

$ docker-compose -f docker-compose.yml run hello-ci-workflow

會得到跟上面直接運行docker一樣的結果

 

 

 

修改docker-compose-test.yml

再來為了以後CI的需要,我們為了測試也寫一個docker-compose,來設置不同環境

$ vi docker-compose-test.yml
// docker-compose-test.yml

version: '3.3'
services:
  hello-ci-workflow:
    image: hello-ci-workflow
    command: npm test // 只增加了這行
    build: .

為了測試所以增加了一行 npm test,運行這個容器時,會直接進到測試環境。

docker-compose -f docker-compose-test.yml run hello-ci-workflow

會跟上述直接運行 npm test時,會有一樣的結果,代表正常運作

到這邊Docker部分告一段落

Github設定

在Github上創建完一個名為hello-ci-workflow的repository後,可以使用以下指令將本地端的程式碼push到github上進行程式碼託管

$ git remote add origin https://github.com/<Your_user_name>/hello-ci-workflow.git
$ git add . // 追蹤本機的修改檔案
$ git commit -m "add docker" // 加上此次的註解
$ git push -u origin master // 推送到github上

<Your_user_name> 改為你的github帳號

以上做完後,前往 https://github.com/<Your_user_name>/hello-ci-workflow就可以看到你的程式碼

CircleCI

集內容會講到與CricleCI關聯部分敬請期待下一集

 

參考資料:

DevOps Handbook中文版|打造世界級技術組織的實踐指南

Effective DevOps:使用AWS快速入門

DevOps:持續整合&持續交付(DockerCircleCIAWS

超新手的一日CI/CD初體驗,使用CircleCiGithub-Flow自動部署Node.JSAWS Elastic Beanstalk

 

發表迴響