以下是一個針對無法互相訪問的 Server 進行 SQLite 資料庫檔案同步的特殊案例分享
前言
我們公司內有個古老的系統,雖然 code 幾乎不再變動,也僅剩少數幾人熟悉這個專案,但客戶使用的頻率仍然很高,所以決定要將這個服務導入 CI/CD 自動化部署,考量到人力成本的問題,為了不想再多花人力了解這個專案,我們的目標是在不改 code 的情況下,僅透過撰寫 yml 文件就能完成這項工作。
這個服務是用 Node.Js 運行的,Node.Js 的 Dockerfile & 部署指令還好找,雖然我沒有寫過 Node.Js 但也不至於太吃力,比較大的挑戰是在於這個系統的核心數據是用 SQLite 儲存的,並且需要大量變動以及需要一次性同步到各環境。
公司內部的各個環境間有嚴格的網域限制、層層登入權限管控,彼此不允許互相訪問,因此無法透過 ssh 來處理,原本的部署方式都是要透過人工登入手動 copy paste,每個過程都需要大量的人工操作,我計畫重構這個系統的部屬流程,讓它完全自動化處理,順便跟大家分享一下我採用的解決方案~
此篇不會描述到 Node.Js 的 CI/CD 做法,有興趣的小夥伴再自己另外找其他人的文章閱讀~
old 部屬流程
平常業務使用情境:
1. 客戶們會在系統 UI 上進行操作,陸續做一些調整
2. 調整完成後,請相關服務進行測試
3. 測試完畢並準備上線時,再請 RD 手動將這些調整部署到另一個環境
部屬限制評估
此次重構的目標是希望 SQLite 資料庫文件要能夠跨環境自動同步,礙於各種網域、帳號權限/系統權限等各種限制,它無法很直覺的下 SSH 命令存取,這大大的增加了處理上的困難
原本考慮透過 Gitlab 的 Job Artifacts 夾帶資料,但是 Artifacts default size 只有 100 MB,而對於 SQLite db 這種會一直長大的檔案,就算調大 Artifacts limit 也不是長久之計
在評估了公司環境的諸多限制後,我發現每個 Server 都有一個共通點 — 他們都能存取公司內部的 private Docker Registry!這讓我靈機一動,想到了一個解決方案 😲💡
使用 Docker Image 當作資料傳遞的載體
我打算將 SQLite 資料庫文件打包進 Docker Image 中來傳遞
當需要把 A 環境的 SQLite 資料庫文件同步到 B 環境時,只需進行 2 個步驟:
1. 在 A 環境中,把 SQLite 資料庫文件包進 docker image 中,push 到 Docker Registry
2. 到 B 環境中,自 Docker Registry pull image,把裡面的 SQLite 資料庫文件 copy 出來,放到服務存取的目標路徑
這樣一來,即使 A 環境與 B 環境無法直接溝通,也能夠克服檔案傳遞的問題了。
另外,用 Docker Image 來存放 db 檔案,也不用擔心容量限制的問題。
打包 SQLite db 的 Dockerfile
Dockerfile
FROM scratch
WORKDIR /app
ADD /data.db ./
這裡我使用最小的 Docker Image scratch
來當作 base image,這個 image 本身裡面是完全空的,不包含任何資料夾或檔案,這樣就可以最大程度的減少 image 體積,只含我要傳遞的 SQLite 資料庫文件
撰寫自動化處理的 gitlab-ci.yml
.gitlab-ci.yml
stages:
- build-a-db
- a-sync-b
build_a_db_job:
stage: build-a-db
script:
- cd /home/gitlab-runner/myServiceDb
- docker build -t docker.private.net/myService-db-a .
- docker push docker.private.net/myService-db-a
tags:
- gitlab-runner-a
sync_b_job:
stage: a-sync-b
script:
- docker create -d -p --name myService-db-container docker.private.net/myService-db-a
- docker cp myService-db-container:/app/data.db /home/gitlab-runner/myServiceDb
- docker rm myService-db-container
# Restart the service that uses this database file.
- docker service update --force --image docker.private.net/myService myService
tags:
- gitlab-runner-b
此 yml 將同步動作分為以下 2 個 job 處理:
build_a_db_job
:
此 job 會先到 A Server 作業
SQLite 資料庫文件存放於 Server 上 home/gitlab-runner/myServiceDb 之路徑
使用前一章的 Dockerfile 打包成 image,取名為 myService-db-a,並推送到 Docker Registry
sync_b_job
:
此 job 會到 B Server 作業
他會把 myService-db-a image pull 下來,並 create 一個 container,取名為 myService-db-container
接著使用 docker cp 命令,將 container 內的 data.db 複製到 B Server 的 home/gitlab-runner/myServiceDb 路徑
做完後刪除剛剛 create 的 container
並重啟使用此 SQLite 資料庫之服務
------------------------- END -------------------------