/

使用 docker 來輕鬆建構資料庫

緣起

自從使用了 Docker 之後,凡是需要測試新的服務或是把玩新的工具第一個直覺就是去找有沒有 docker image。

然而最近剛好手邊有一個 LINE Nofity 串接的案子,要管理不同使用者的 token 需要一個資料庫來儲存並且管理,於是打算將資料庫包進 Docker,方便日後部署或是備份檔案。

在打包的過程中學習到了一些知識,記錄起來方便日後查詢用,畢竟 Docker file 不是天天寫,時常會忘記。

目標

希望將 container 跑起來時,裡面的 database 是已經定義好 Schema 的情況,讓後端服務可以直接進行互動。長期目標是連資料庫內容都要映射到 container 之外,方便日後移轉。不過這篇文章不會提到。

Docker 那些容易混淆的事

最容易搞混的應該就是 RUN, CMD, ENTRYPOINT 這三個設定值

RUN 通常用來安裝套件,像是用來執行 apt 相關指令

CMD 用來執行啟動 container 之後的指令,但他可以被 docker run 之後的參數來取代,也就是 docker run 後面不加上使用者自訂的命令,就會執行 CMD 的內容

ENTRYPOINTCMD 類似,但是內容不會被 docker run 後面給的指令覆蓋掉,那些指令會附加在 ENTRYPOINT 之後

除此之外將檔案複製進 container 要用 COPY 還是 ADD 也有一些不同看法。ADD 基本上就是 COPY 的增強版,除了可以將檔案複製進容器,也可以填入一串網址。如果填入的來源是壓縮檔,那麼會自動進行壓縮。

所以沒有特別需求的話,單純想複製檔案進容器,還是推薦用 COPY 比較簡單明瞭一點。

撰寫 Docker file

1
2
3
4
5
FROM mariadb:10.4.8
COPY init.sql /docker-entrypoint-initdb.d/
ENV MYSQL_ROOT_PASSWORD root
ENV MYSQL_DATABASE token_db
EXPOSE 3306

在撰寫 Docker file 時,記得 image 要附上版本,否則預設是 latest。往後部署到其他伺服器有可能最新版本已經不再是這一版了,此時就可能出現不同版本行為不一的情況。建議一定要指定版本號。

我想要在容器跑起來時自動將 database schema 也一併創建好,此時可以將你的 .sql 檔案放入 /docker-entrypoint-initdb.d 這個目錄,此目錄中的 .sql 檔案會在資料庫啟動時自動被執行(在 MySQL image 也是),如此一來可以省下不少寫 script 去自動表格的時間。

接下來設定好環境變數讓你的 MariaDB  自動建立好資料庫以及設定 root 密碼,詳細有哪些環境變數可以設定,在 MariaDB 的 image 文件中

備份與復原

使用容器部署服務時,可以很方便地將服務進行轉移。但今天的服務是資料庫,有時候我們希望將資料庫的內容從容器內備份出來,才不會因為容器意外停止了而遺失資料。

做到這件事情的方法有很多,可以掛載目錄位置進容器,透過這樣的方式將資料庫相關的檔案與容器外的主機共用。但今天使用的方式是使用 MySQL 提供的 mysqldump 指令來將資料庫內容備份。

備份

docker exec /usr/bin/mysqldump -u root --password=root > backup.sql

backup.sql 的名稱可以隨意命名,它是將資料庫內容匯出的 sql 檔案。

復原

cat backup.sql | docker exec -i /usr/bin/mysql -u root --password=root

備份與復原可以幫助你在轉移服務到其他主機時,先將原本服務的資料庫內容取出,並且在其他主機回復內容。

持久化儲存

以上方法很明顯有一個問題,那就是你無法得知 container 在哪一個時候遇到意外而終止,這樣子也無法在適當的時機備份,有可能造成資料遺失!

想要達成持久化儲存勢必要使用到 VOLUME 功能,除此之外還需要一個背景知識,mariaDB(MySQL)的資料儲存位置在 /var/lib/mysql

要達成掛載的方式有很多種,在此使用個人覺得較為直覺的一種:

新增一個 volume 結構並命名為 db-data

docker create volume db-data

之後將容器跑起來時,指定容器內部的哪一個位置需要往外掛載至 db-data

docker run -it -d -v db-data:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root db

如此一來,容器內的 /var/lib/mysql 映射至外部的 db-data,不管容器存在與否 db-data 都會存在!

我們也可以用 docker volume inspect db-data 來查看該 VOLUME 的資訊

1
2
3
4
5
6
7
8
9
10
11
[
{
"CreatedAt": "2019-11-14T02:17:21Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/db-data/_data",
"Name": "db-data",
"Options": {},
"Scope": "local"
}
]

可以發現它在本機位置的真實路徑為 /var/lib/docker/volumes/db-data/_data

後記

有了 Docker 之後讓很多事情變得很方便,但是自己在學習撰寫 Dockerfile 的過程中以及部署 Docker 服務時,時常被一些類似的指令與行為搞混。希望藉由記錄建構環境流程,一方面加深自己印象,一方面讓更多有類似需求的夥伴可以參考 😊