[案例分享] [CI/CD] SQLite CI/CD 跨環境同步方案

以下是一個針對無法互相訪問的 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 -------------------------