使用 Docker & Drone 建立簡易自動部署流程 — Part2

John
11 min readOct 16, 2018

--

對,終於把 Part2 寫出來了

上一篇中我們提到了如何安裝 Docker 以及 Drone 的基礎部建,接下來我們要以一個簡易的 Node.js API Project 來說明如何在 Drone 上建立一套簡易的 Continuous Delivery 流程。

1. Express API 專案準備

首先我們先建立一個簡單的 Express.js 專案,並建立 server.js 檔案內容如下:

當使用者訪問 http://localhost:3000 時,程式就會回應一個帶有姓名跟年紀的 JSON Object。

接著我們使用 mocha 這套 Node.js 單元測試的 Framework,撰寫一個簡單的測試,請在專案目錄中建立 test.js 檔案內容如下:

在此測試中,我們檢查 server.js 提供的 API 是否正確回傳所預期的 JSON Object,若正確回傳則通過此單元測試。

2. 撰寫 Drone YAML檔案

首先要了解的是,Drone 的部屬流程是依據一個 .drone.yml 的檔案來執行的,在這個檔案中,我們可以自行定義所需要的自動部署流程的每個步驟,以及部屬完成後的訊息通知等。

以下來看一個簡單的例子:

在上面的範例中,可以看到在整個 pipeline 流程中定義了 build 以及 notify 兩個部署的流程,當 Drone 依據此 YAML 檔案來進行自動部署時,會執行以下步驟的流程:

1. 啟動一個 Base image 為 Node.js 8.11.3 alpine 版本的 Docker Container

2. 從來源 Project 的版本控制系統 git clone 此 Project

3. 執行 npm install 以及 npm test 的命令

4. 若成功通過,則啟用 Drone 的 Slack Plugin 通知該部署的相關成功資訊至指定的channel

而根據每個專案需要以及團隊可能有不同的流程安排,我們可以在每個專案的 .drone.yml 檔分別撰寫不同的部署流程,當然也能夠在每個步驟成功或是失敗時分別定義不同的結果,詳細內容可以參考官方文件說明。

回到範例的 Node.js 專案,若今天想要在專案建置及測試完成後以 Slack 通知我們專案的測試沒有問題,我們可以利用 Drone 所提供的 Slack Plugin 來撰寫通知的流程,但為了要將 Drone 與 Slack 做連接綁定,首先要先在你想要收取通知的 Slack Channel 建立一個 Webhook。

請先到 https://my.slack.com/services/new/incoming-webhook 選擇想要收到通知訊息的 Channel 或是 User:

按下 Add 後,將產生的 Webhook URL 根據 Drone Slack Plugin 的說明填寫至 專案的 YAML 檔案中,以下就是撰寫完成的 .drone.yml 檔案:

接著將撰寫好的 YAML 檔案 Push 至 Gitea,就能看到 Drone 的部署流程開始進行動作:

並且可以在設定好的 Slack Channel 收到相對應的訊息:

3. 建立 Ubuntu Service 供部署使用

由於我們希望在 Drone 完成建置以及測試的步驟後,能夠在 Docker 內建立一個運行此 API Service 的 Container,因此我們需要在 Ubuntu 內建立一個基本的容器建立腳本,並希望能達到如下的流程需求:

  1. 從目標專案 Git pull 最新的修改內容
  2. 將專案使用 Docker build 建立成 Docker image
  3. 使用產生的 docker image 啟動 docker container 提供 API 服務

為了使專案可以透過 docker build 的方式建立 image,請先在專案內新增一個 Dockerfile 檔案,並輸入以下內容:

FROM node:8.11.3-alpine
WORKDIR /app
ADD . /app
RUN npm install
EXPOSE 3000
CMD npm start

此檔案可以透過 Docker build 方式建立一個 base 為 node 環境的 docker image,並將建置及執行此專案的流程建立完成。

接著回到 Ubuntu Service 的建置,要達到剛剛所提到的流程的目標,必須使用 Ubuntu 的 systemd 這個系統服務管理器,首先請在 terminal 內輸入以下指令:

$ sudo vim /etc/systemd/system/projectName.service

projectName可以替換為所需服務的名稱,打開 vim 編輯器之後請輸入以下內容:

[Unit]
Description=projectName Container
Requires=docker.service
After=docker.service
[Service]
Restart=always
ExecStart=/usr/bin/docker run --name=projectName -p 3000:3000 stu60610/projectName
ExecStop=/usr/bin/docker stop -t 2 projectName
ExecStopPost=/usr/bin/docker rm -f projectName
[Install]
WantedBy=default.target

此檔案為建立一 Ubuntu Service 的建置內容,其中在 [Service] 的部分請根據各專案需求不同更改 docker container 的起始設定( Port 或是 Image 名稱等 ),在這邊我們依據範例的設定將 Port 3000 對應給 docker 外部伺服器的 服務端口。

儲存此檔案後,請再輸入以下兩個指令,讓 systemd 重整以及將此 Service 設定為開機時自動啟用:

$ systemctl daemon-reload
$ systemctl enable projectName.service

4. 建立 Deploy Script

接下來的步驟就是建立最重要的部署腳本了,腳本的用途在於當 Drone 完成建置流程後,會透過 ssh 方式連線到需要部署 API Service 的主機,並執行此腳本完成部署任務。

首先我們先在本機目錄下將專案 git clone 回希望放置的位置,在之後每次執行腳本時會先 git pull 此專案更新的程式碼以及檔案,使得建置出來的 Docker image 可以保持在最新的狀態。

接著要在系統內建置要用來執行的 shell script,請在 terminal 內輸入以下指令:

$ vim deploy.sh

並輸入以下內容:

#!/bin/bash
echo “Updating staging Server”
echo “stopping projectName.service”
sudo systemctl stop projectName.service
# remove all outdated images and containers
echo “removing outdated/dangling images and containers”
sudo docker rmi stu60610/projectName:latest
# create new image for projectName
echo “create new image for projectName
cd /home/stu60610/projectName
git pull origin master
sudo docker build -t=”stu60610/projectName” .
# restart service which will use the newly pulled image
echo “restarting projectName service”
sudo systemctl start projectName.service
# App is updated!
echo “projectName successfuly updated!”

將專案位置以及細部設定調整完成後儲存此腳本,並執行此指令使得此腳本可以被執行:

$ sudo chmod +x deploy.sh

接著你可以嘗試執行此腳本,一切無誤的話你就可以在 Docker container 列表中看到你的專案已經成為一個獨立 Container 運作中了。

5. 修改完整 .drone.yml 檔案

最後一個步驟,我們要將整個 Drone 的流程跟剛剛寫的 shell script 結合起來。首先請打開前面編輯過的 .drone.yml 檔案,在這邊我們要增加 ssh 連線到目標主機的步驟,我們使用第三方所開發的 SSH Plugin,並依照說明添加 pipeline 如下:

在 ssh-deploy 裡可以看到有相關目標主機的連線資訊需要填寫,其中帳號以及密碼由於是較敏感的資訊,建議使用 Drone Secret 的方法儲存在 Drone 的 repository 內,並在 YAML 裡使用變數的方式提取。

你可以使用 Drone cli 設定 Secret,請在 terminal 內輸入下方命令:

$ drone secret add \
--name ssh_username \
--value your_server_username \
--image appleboy/drone-ssh \
--repository stu60610/projectName
$ drone secret add \
--name ssh_password \
--value your_server_password \
--image appleboy/drone-ssh \
--repository stu60610/projectName

並在 Drone Web 介面確認是否有正確設定 Secret:

接著將修改好的專案 push 至 Gitea,就能看到 Drone 成功地將專案部署到我們的目標主機囉!

而Slack 也收到了部署成功的訊息:

結論

在以前手動部署 API Service 的時候,由於遇到了許多問題,所以一直試圖建立一個可靠的流程來改善,透過這次使用 Docker + Drone 的方式建立了第一個簡易自動部署的流程後,也更加了解到了容器化以及 CI/CD 對於團隊開發所帶來的優點,在團隊專案有多人開發時,也能透過測試以及自動化整合的方式對專案做最有效的版本控管,並且將版本功能更換的影響降至最低。

希望這個系列的文章對正在觀看的你有幫助,如果有需修正的地方或是有其他問題建議,也歡迎在下方留言!

資料來源

--

--