背景
一般公司的CI/CD流程相對來說較為專業、成熟且復雜,需要考慮通用性、安全性、可維護性等等,假如我們個人開發者也想要搞CI/CD,功能相對簡單,使用的是github倉庫,可以考慮github actions,接下來我就以一個koa的demo來實現CI/CD。
目標
我們要實現的流程包括,push
之后將構建成為鏡像,推送到docker hub中,服務器拉取docker hub最新鏡像并在宿主機運行容器,釘釘能夠監控CI/CD流程是否成功,并推送到釘釘群中。
新建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
字樣即為成功。
小結
經過以上步驟,一個簡單的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
可以看到鏡像的構建成功了的。
運行鏡像
將鏡像運行起來,會觸發CMD命令,會將koa項目跑起來,并且我們要在瀏覽器中看到運行的koa輸出內容。
docker run -d -p 8888:3010 koa-demo
-d 是后臺運行,不要阻塞shell指令窗口
-p 來制定內外端口映射
8888 將內部端口和外部端口3010做一個簡單的映射,這里是 外部端口:內部端口
運行docker-demo
鏡像,并設置運行的內部端口為3010,因為koa設置的運行端口為3010所以要保持一致,8888是映射給容器外面的端口,即為用戶訪問的端口。
查看容器
docker ps
瀏覽器訪問項目
容器啟動成功后,docker就啟動了一個進程。
在瀏覽器訪問:http://localhost:8888
瀏覽器已經可以正常查看到運行的項目。
小結
編寫了Dockerfile腳本文件,負責執行構建的流程,為了優化構建包.dockerignore
大小忽略一下沒必要的文件。
通過執行docker build
命令觸發Dockerfile腳本生成了一個koa-demo鏡像,執行docker run
運行了一個容器,最后我們可以在瀏覽器中訪問運行中的koa項目。
github actions
項目在本地docker環境能夠正常運行,接下來開始用到github actions。
使用github actions
在github中,如果已經有項目的可以先把項目提交,若沒有項目,直接新建一個項目,隨后將本地修改后的代碼提交上去。
隨后點擊并打開Actions頁面
會展示很多種構建類型,我們不用管它,只選擇第一個,最簡單的鏡像構建就可以了。
選擇第一個docker鏡像后,會生成一些代碼
鏡像構建完成后,可以很容易的在當前宿主機上運行,但是,如果需要在其它服務器上使用這個鏡像,我們就需要一個集中的存儲、分發鏡像的服務,我們公司內部使用的是私有倉庫,這里我們使用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
字段指定運行所需要的虛擬環境。line13:
steps
指定每一個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的密碼
其中的username
和pasword
是屬于私密內容,不應該直接顯示在代碼中,因此我們需要在github 的secrets設置一下,將username
和password
設置為局部變量。
寫完代碼后,直接提交,第一次會執行一次構建流程。
顯示打鉤則代表構建成功了。
詳細的pipeline流程,可以點擊進去查看詳情。
檢查鏡像
推送代碼后,我們可以通過在docker hub里面檢查已經構建成功的鏡像。
又或者在本機docker桌面端中查看
部署到服務器
當前構建過程中,只包含了鏡像的構建,并推送到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 ??????
構建成功了
為了都能理解,這里解釋新增的兩個工作流:
-
line32:
executing remote ssh commands using password
是當前工作流的名稱或者描述,可以自己修改。 -
line24:這里使用了第三方用戶封裝好的
actions
鉤子,它的作用是通過ssh登錄遠程服務器。(持續集成由很多操作組成,比如抓取代碼、運行測試、登錄遠程服務器,發布到第三方服務等等。GitHub 把這些操作就稱為 actions,如果需要某個 action,不必自己寫復雜的腳本,直接引用他人寫好的 action 即可,actions倉庫)。 -
line25:
with
表示actions
擁有參數。 -
line26~line29:
host
是服務器公網的ip地址,username
和password
分別是docker注冊是賬戶名和密碼,post
是ssh訪問端口,默認為22。其中host
、username
、password
建議使用局部變量,不要直接現實在代碼中。 -
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
-
-
line43:
dingtalk robot message
工作流名稱 -
line44:
fifsky/dingtalk-action@master
,使用釘釘消息通知actions
-
line46~line50:
url
為釘釘配置的webhook
,type
為markdown
,內容中包括了釘釘通知配置的關鍵詞和內容。參見釘釘機器人
重新構建
修改完畢后,提交代碼,等待github actions工作流完成,如果順利將會在釘釘收到通知
打開瀏覽器輸入服務器IP地址+端口號,會看到
以上即為github actions實現的koa項目CI/CD。
小結
使用ssh遠程到服務器,并在服務器中執行docker拉取和運行的命令,將項目運行起來。
利用釘釘群中的機器人生成webhook鉤子,實現消息群通知。
總結
github actions部署koa項目最核心的地方是編寫workflow工作流,工作流中可以做非常多的操作,如抓取代碼、運行測試、登錄遠程服務器,發布到第三方服務等等,當一些操作相對復雜或者具有相似性,可以直接引用他人寫好的 action,亦或自己寫run命令。
當前項目是基于node運行環境,若項目為靜態文件,比如vue項目、react項目則需要使用其他的運行環境,如nginx等,原因是vue、react會打包成dist或者build可運行文件,將其運行不需要使用到進程服務,可以直接在瀏覽器訪問,因此只需要將其放置到代理服務器中,將訪問文件地址暴露給外網訪問即可。