現在許多的程式設計都使用了json格式的檔案作為設定檔的儲存格式,可以跨平台在Linux環境執行的.Net Core也不例外,伴隨著appsettings.json檔案中許多的設定,不管是預設的設定,或是不同開發需求自行加入進去的設定值,全部都是json格式。
為了在docker啟動container的時候可以根據繫結的環境參數(environment, -e)值做出相對應的修改,因此需要有個方便的作法可以在container執行的時候動態修改json檔案。
這一篇文章就來介紹如何在Linux中透過jq套件修改json文件。
先決條件
Container中需要先安裝jq套件,所以必須在Dockerfile中先透過apt-get install -y jq指令(Ubuntu/Debian)安裝jq套件,以便Docker Image中有jq套件可以使用。
使用方式
假設在Dockerfile中定義了ENV LOG_LEVEL=Error,並且appsettings.json檔案中有段Serilog的設定如下:
{
"Serilog": {
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "MyApp.log",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} RequestID:{RequestID} User:{UserID} {RequestPath} {SourceContext} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
"rollingInterval": "Day"
}
}
],
"Enrich": [ "FromLogContext" ]
}
}
可以看到在Serilog中有個MinimumLevel屬性設定的是Debug,這個設定會記錄大量的Log記錄,但是有時候在執行container的時候並不想要記錄這麼詳細的資訊,以降低檔案大小,減少儲存空間的耗用時,有個LOG_LEVEL的ENV設定就可以方便的調整設定值。
現在假設有個.Net Core的程式叫MyApp,Build完之後有個MyApp.dll檔案,在Dockerfile中設定了ENV和複製相關的執行檔案之外,還需要一個.sh檔案(Linux中的Shell檔,相當於Windows的.cmd或.bat),假設這個檔案叫做app-init.sh。
接下來在app-init.sh檔案中要做下面這幾件事:
- 判斷container是不是第一次執行,以避免每次都進行修改appsettings.json的動作。
- 透過jq套件將appsettings.json的內容讀入記憶體。
- 找到MinimumLevel屬性,將值修改為LOG_LEVEL這個ENV所設定的值。
- 將jq套件修改後的內容儲存為appsettings.temp.json。
- 將原本的appsettings.json改名為appsettings.ori.json。
- 將前面的appsettings.temp.json改名為appsettings.json。
- 產生一個用來判斷是否執行過app-init.sh的檔案,例如:inited。
- 啟動MyApp。
假設Dockerfile中設定的WORKDIR是/app,第7點的檔案是放在/app/inited,那麼app-init.sh的程式碼內容大概會是下面這個樣子:
if [ -f "/app/inited" ]; then
echo "Container inited, skip this shell."
else
cat appsettings.json #這行只是先輸出原本的設定值,用來查看用的
jq '.Serilog.MinimumLevel=env.LOG_LEVEL' appsettings.json > appsettings.temp.json
cat appsettings.temp.json #查看儲存的檔案內容
mv appsettings.json appsettings.ori.json #第5點,將原本的設定檔改名
mv appsettings.temp.json appsettings.json #第6點,將jq儲存的暫存檔改為正式檔名
echo "Container inited." > /app/inited #第7點,產生判斷是否init過的檔案
fi
dotnet MyApp.dll #第8點,啟動MyApp
透過上面的app-init.sh檔案,在Container第一次執行的時候就會根據設定的內容修改appsettings.json檔案,產生的inited檔案可以避免container restart之後重複執行init的動作。
透過這樣的技巧,也可以修改appsettings.json中的資料庫連線字串,如果是像Serilog底下的WriteTo屬性是陣列(集合)型式的,則是用[0]、[1]這樣的方式設定,範例如下:
jq '.Serilog.MinimumLevel=env.LOG_LEVEL' appsettings.json | \
jq '.Serilog.WriteTo[0].Args.rollingInterval=env.LOG_ROLLING_INTERVAL' > appsettings.temp.json