【茶包射手日記】.NET Core File Watcher 在 Docker 環境下失效
2 | 2,911 |
遇到一個問題,.NET Core 的 File Watcher 機制(在檔案新增修改刪除時觸發 .NET 事件,參考)在 Docker 中可能失效。
註:File Watcher 可做到 Config 檔修改讀取生效、程式檔案異動時重新編譯,或是資料檔改變清除 Cache,非常實用。
以下是我重現問題的範例:
class Program
{
private static PhysicalFileProvider _fileProvider =
new PhysicalFileProvider(Directory.GetCurrentDirectory());
static void Main(string[] args)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
Console.WriteLine("update quotes after 5 seconds");
File.WriteAllText("quotes.txt", DateTime.Now.ToString());
});
WaitFileChange().GetAwaiter().GetResult();
Console.WriteLine("done");
}
static async Task WaitFileChange()
{
IChangeToken token = _fileProvider.Watch("quotes.txt");
var tcs = new TaskCompletionSource<object>();
token.RegisterChangeCallback(state =>
((TaskCompletionSource<object>)state).TrySetResult(null), tcs);
await tcs.Task.ConfigureAwait(false);
Console.WriteLine("quotes.txt changed");
}
}
程式的運作原理為使用 PhysicalFileProvider.Watch()
觀注 quotes.txt 檔案,以 IChangeToken.RegisterChangeCallback()
註冊檔案變更事件並利用 TaskCompletionSource 同步化,在檔案變更時印出 "quotes.txt changed"。在此同時,另開一條 Thread 在 5 秒後複寫 quotes.txt 檔案以觸發事件,故正常狀況應為,程式開始後等待五秒出現 "update quotes after 5 seconds",隨即印出 "quotes.txt changed",最後顯示 "done" 測試完成。
程式在 Windows 測試 OK,部署到 Linux 執行也沒問題,包進 Docker 裡跑結果也正確,但若使用 Docker Volume 將 /app/quotes.txt 對映到實體主機的 /home/jeffrey/dockers/fwt/quotes.txt 時,本機的 quotes.txt 檔案會被覆寫,但 RegisterChangeCallback() 事件不會被觸發。
.NET Core File Providers 文件 是有提到這點:
Some file systems, such as Docker containers and network shares, may not reliably send change notifications. Set the DOTNET_USE_POLLING_FILE_WATCHER environment variable to 1 or true to poll the file system for changes every four seconds (not configurable).
意思是這個機制遇到 Docker 檔案系統可能失效,必須設定 DOTNET_USE_POLLING_FILE_WATCHER 環境變數為 1 或 true,改用 4 秒一次的輪詢取代。實務上可在啟動 Docker 時透過 docker run ... -e DOTNET_USE_POLLING_FILE_WATCHER=1 ... 參數指定,更治本的解法則是在 Dockerfile 宣告 ENV 內嵌到容器裡:
FROM microsoft/dotnet:2.1-runtime
ENV DOTNET_USE_POLLING_FILE_WATCHER=1
WORKDIR /app
COPY ./publish ./
ENTRYPOINT ["dotnet", "FileWatcherTest.dll"]
貌似一行搞定,但我卻在這裡陷入泥坑,設好 DOTNET_USE_POLLING_FILE_WATCHER 卻不見無效,原以為可以輕鬆寫完的文章,反覆試了近兩多小時,將近午夜電腦都要變回南瓜仍然無解,讓我萬念俱灰了無生趣。
爬文時由一則 Github Issue 追到另一則被合併至 2.2 版的相關 PR,浮現靈感,查詢我安裝 Microsoft.Extensions.FileProviders.Physical 的 NuGet 套件版本為 2.1 穩定版,改裝 2.2 Preview 姑且一試。
喵的,就這麼成功了~
這枚 Microsoft.Extensions.FileProviders.Physical 程式庫升級 2.2 預覽才解掉的茶包,算是我第一次踩到的 .NET Core + Docker 地雷,不怕不怕,呵。
補充今天查到的Docker Image 版本補充,你知道 2.1-runtime-deps、2.1-runtime 有什麼不同? 什麼情況該用哪一種?
- microsoft/dotnet:2.1.0-runtime-deps - use for deploying self-contained deployment apps
- microsoft/dotnet:2.1.0-runtime - use for deploying .NET Core console apps
- microsoft/dotnet:2.1.0-aspnetcore-runtime - use for deploying ASP.NET Core apps
- microsoft/dotnet:2.1.300-sdk - use for building .NET Core (or ASP.NET Core apps)
Using an example to reproduce file watcher issue in Docker with volume mapping. Setting DOTNET_USE_POLLING_FILE_WATCHER=1 doesn't works with Microsoft.Extensions.FileProviders.Physical 2.1, upgrade to 2.2 preview is the workaround.
Comments
# by Mars
2.1 為 LTS 版本竟然沒有修正這問題也頗奇怪的 @@
# by Victor
不奇怪啊,現在都改走release date先決、hotfix style了。時間到了有什麼就推什麼,塞不進去的就等下一版....