Linux container中修改json檔案設定值的方法

現在許多的程式設計都使用了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檔案中要做下面這幾件事:

  1. 判斷container是不是第一次執行,以避免每次都進行修改appsettings.json的動作。
  2. 透過jq套件將appsettings.json的內容讀入記憶體。
  3. 找到MinimumLevel屬性,將值修改為LOG_LEVEL這個ENV所設定的值。
  4. 將jq套件修改後的內容儲存為appsettings.temp.json。
  5. 將原本的appsettings.json改名為appsettings.ori.json。
  6. 將前面的appsettings.temp.json改名為appsettings.json。
  7. 產生一個用來判斷是否執行過app-init.sh的檔案,例如:inited。
  8. 啟動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

發佈留言