永久保存Container中SQL Server資料庫檔案的方式

在前一篇【在容器中備份及還原SQL Server資料庫,以及資料磁碟區容器(Docker Volume)的初步應用】有提到利用Docker Volume來保存container上所儲存的SQL Server資料及檔案,這是由於image只是一個檔案,不是程序,裡面封裝了要產生容器時的所有層層資訊,且image只能唯讀;而container不太一樣,container依據image產生,一個image可以建立多個container,很像程式中[class(類別)]與[object(物件)]的關係,但container是可以讀/寫的,所以container一旦被刪除,裡面的資料也會跟著消失,所以才會需要利用Docker Volume來保存資料。本篇將介紹兩種保存SQL Server資料庫檔案的方式,最後亦嘗試搭配MSSQL_DATA_DIR環境變數來變更容器中SQL Server的"預設資料目錄"(即 /var/opt/mssql/data)。

--(1) <情境一> 使用 docker run 命令搭配 "-v <host directory>:<container directory>" 來保存資料

--首先,在Linux虛擬機(192.168.56.121)上"沒有"安裝SQL Server on Linux的前提下,我們先自行建立以下目錄
# mkdir /var/opt/mssql
# mkdir /var/opt/mssql/data
# mkdir /var/opt/mssql/log
# mkdir /var/opt/mssql/secrets
# ll /var/opt/mssql

--建立容器(sql1)   --注意: 若Linux主機上已有安裝SQL Server on Linux並啟動,執行以下docker run指令來建立容器"sql1"會失敗
# docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=xxxxxxxx' --name=sql1 -p 1477:1433 -v /var/opt/mssql/data:/var/opt/mssql/data -v /var/opt/mssql/log:/var/opt/mssql/log -v /var/opt/mssql/secrets:/var/opt/mssql/secrets -d mcr.microsoft.com/mssql/server:2017-latest
# docker ps    --確認容器狀態為"Up"
# docker volume ls    --沒有volume被建立
# ls /var/opt/mssql/data    --可以在Linux主機目錄,看到該容器所屬SQL Server的mdf及ldf檔
# docker exec -it sql1 ls /var/opt/mssql/data    --查詢"容器目錄",與前面在"Linux主機目錄"看到的內容一模一樣
# touch /var/opt/mssql/data/text.txt    --故意在Linux主機目錄中,新增一個空檔text.txt
# ls /var/opt/mssql/data    --確認在Linux主機有多一個text.txt檔
# docker exec -it sql1 ls /var/opt/mssql/data    --直接查詢容器"sql1",也看到text.txt檔
# docker stop sql1    --停止容器
# docker rm sql1    --刪除容器
# ls /var/opt/mssql/data    --檢查檔案還是存在
--(2) <情境二> 使用 docker run 命令搭配 "-v <volume name>:<container directory>" 來保存資料

--使用 docker run 命令搭配 -v 參數來指定"磁碟區名稱"而非"主機目錄",藉以建立"資料磁碟區容器"
--建立另一個容器(sql2)來測試,使用-v sqlvolume:/var/opt/mssql,"sqlvolume"磁碟區名稱對應容器的"/var/opt/mssql"整個目錄,而非部分目錄

# docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=xxxxxxxx' --name=sql2 -p 1488:1433 -v sqlvolume:/var/opt/mssql -d mcr.microsoft.com/mssql/server:2017-latest
# docker ps    --確認容器狀態為"Up"
# docker exec -it sql2 ls /var/opt/mssql/data    --查詢"容器"的目錄
# docker exec -it sql2 touch /var/opt/mssql/data/text2.txt    --在容器內新增一個檔案"text2.txt"
# docker exec -it sql2 ls /var/opt/mssql/data    --確認容器內有看到"text2.txt"
# docker volume ls    --檢視資料磁碟區名稱
DRIVER              VOLUME NAME
local               sqlvolume

--問題來了,這個"sqlvolume"資料磁碟區名稱所對應的Linux主機實際路徑在哪裡呢?
# docker ps    --先查詢容器的Container ID
# docker inspect -f '{{.Mounts}}' 37ccdf16f597    --"37ccdf16f597"指的是容器"sql2"的Container ID
[{volume sqlvolume /var/lib/docker/volumes/sqlvolume/_data /var/opt/mssql local z true }]

# cd /var/lib/docker/volumes/sqlvolume/_data    --切換至Linux主機的對應路徑,Linux主機路徑為/var/lib/docker/volumes/sqlvolume/_data,container路徑為/var/opt/mssql
# ls
data  log  secrets
# ls /var/lib/docker/volumes/sqlvolume/_data/data    --檢查Linux主機的對應目錄,有查到檔案"text2.txt"
# docker exec -it sql2 ls /var/opt/mssql/data    --檢查container中的對應目錄,也有查到檔案"text2.txt"

--[補充]:
# docker inspect sql2  --不用 "-f " 參數,可以檢視全部內容
        "Mounts": [
            {
                "Type": "volume",
                "Name": "sqlvolume",
                "Source": "/var/lib/docker/volumes/sqlvolume/_data",
                "Destination": "/var/opt/mssql",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }

--(3)移除資料磁碟區容器
--如果您刪除"資料磁碟區容器",則會「永久」 刪除容器中的所有 SQL Server 資料。
# docker volume rm sql1data    --"sql1data"為"資料磁碟區名稱",請慎用。
--除了這些容器技術,您也可以使用標準的 SQL Server 備份和還原技術。 您可以使用備份檔案來保護資料,或將資料移至另一個 SQL Server 執行個體。
--如果您建立備份,請務必在容器外部建立或複製備份檔案。 否則,如果移除容器,也會一併刪除備份檔案。
--(4)從容器內複製檔案至外部Linux主機(用docker cp指令)
# docker ps 
# docker exec -it sql2 ls /var/opt/mssql/data    --檢查容器"sql2"是否有text2.txt檔案
# ll /var/opt/mssql/data/    --目前Linux主機只有text.txt,沒有text2.txt
# docker cp sql2:/var/opt/mssql/data/text2.txt /var/opt/mssql/data/text2.txt    --將容器"sql2"的text2.txt複製到Linux主機的相對目錄中
# ll /var/opt/mssql/data/    --查詢檔案是否成功複製到外部Linux主機

--將外部Linux主機的檔案複製到容器中(一樣用docker cp指令)
# touch /var/opt/mssql/data/text3.txt    --在Linux主機建一個空檔text3.txt
# ll /var/opt/mssql/data/    --確認是否建立成功
# docker cp /var/opt/mssql/data/text3.txt sql2:/var/opt/mssql/data/text3.txt    --將Linux主機的text3.txt複製到容器"sql2"中的相對目錄中
# docker exec -it sql2 ls /var/opt/mssql/data/    --查詢檔案是否有成功複製到容器"sql2"中
--(5)利用"MSSQL_DATA_DIR"環境變數,更改SQL Server的"預設資料目錄"(預設在/var/opt/mssql/data)
# docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=xxxxxxxx" -e "MSSQL_DATA_DIR=/my/file/path" --name sql3 -v sqlvolume2:/my/file/path -p 1499:1433 -d mcr.microsoft.com/mssql/server:2017-latest
# docker exec -it sql3 bash    --path目錄的owner是root

root@5aedfd6df4d3:/# ls -l /my/file
total 0
drwxr-xr-x. 2 root root 6 Apr 21 09:20 path

root@5aedfd6df4d3:/# ls -l /my/file/path/    --因為"使用者自訂資料庫"尚未新增,所以看不到資料庫檔案
total 0

root@5aedfd6df4d3:/# ls -l /var/opt/mssql/data/    --系統資料庫還是放在"/var/opt/mssql/data/"中
total 53120
-rw-r-----. 1 root root  4194304 Apr 21 09:27 master.mdf
-rw-r-----. 1 root root  2097152 Apr 21 09:27 mastlog.ldf
-rw-r-----. 1 root root  8388608 Apr 21 09:27 model.mdf
-rw-r-----. 1 root root  8388608 Apr 21 09:27 modellog.ldf
-rw-r-----. 1 root root 14024704 Apr 21 09:20 msdbdata.mdf
-rw-r-----. 1 root root   524288 Apr 21 09:20 msdblog.ldf
-rw-r-----. 1 root root  8388608 Apr 21 09:20 tempdb.mdf
-rw-r-----. 1 root root  8388608 Apr 21 09:27 templog.ldf

# docker volume ls
# docker inspect -f '{{.Mounts}}' sql3    注意: 類型是"volume",不是"bind"
[{volume sqlvolume2 /var/lib/docker/volumes/sqlvolume2/_data /my/file/path local z true }]
# cd /var/lib/docker/volumes/sqlvolume2/_data    
# ls    --因為使用者資料庫尚未新增,所以看不到檔案

--此時,使用SSMS連上container("192.168.56.121,1499")使用UI介面建立資料庫"testdb",注意預設備份路徑已變更為"/my/file/path"

# ls /var/lib/docker/volumes/sqlvolume2/_data   --資料庫檔案在Linux主機出現了
testdb_log.ldf  testdb.mdf

# docker exec -it sql3 bash    --登入容器"sql3"
root@5aedfd6df4d3:/# ls /my/file/path/    --資料庫檔案在container中出現了
testdb.mdf  testdb_log.ldf

--(6)測試完畢,刪除相關檔案
--移除"容器"
# docker ps -a
# docker stop sql2 sql3
# docker rm sql2 sql3

-移除"資料磁碟區"
# docker volume ls
# docker volume rm sqlvolume sqlvolume2
# docker volume ls

 

Jay Huang