如何正確地發行 ASP.NET Core 網站到遠端 IIS 站台 | The Will Will Web

The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何正確地發行 ASP.NET Core 網站到遠端 IIS 站台

我們最近有專案需要發行一個用 ASP.NET Core 5 開發的網站,有經驗的人應該知道,當網站正在執行時,尤其是 Windows 作業系統,過程中所有 *.dll 檔案都會被鎖定,無法成功覆蓋檔案,以致於自動部署失敗。今天這篇文章我打算來分享幾個常見的作法,以及我認為最好的作法。

手動關閉 IIS 站台 (WebSite)

這種方法的基本流程是:

  1. 停止 IIS 站台
  2. 進行網站程式更新
  3. 啟動 IIS 站台

我發現很多人會用 關閉 IIS 站台 的方式讓網站停下來,但我覺得這方法有點問題!🔥

  1. 首先,你今天的目的是要「更版」,而不是要「關閉站台」,用這種方式雖然可以讓網站關閉,但是卻不一定會關閉應用程式集區 (Application Pool)。怎麼說呢?因為一台 IIS 伺服器中,一個應用程式集區可以讓多個站台應用程式目錄使用,你就算停掉一個站台,可能還有其他的站台還在使用應用程式集區

    重點是,應用程式集區負責用來管理 w3wp.exe 程序 (Process),你的 Process 不停下來,現有被鎖定的 *.dll 組件一樣還是被鎖定,你一樣無法更版!

    一般我們都不建議應用程式集區共用,一個站台使用一個獨立的應用程式集區才正常!

  2. 其次,你在關閉站台的時候,網站是被直接關閉的狀態,完全連不上,是連 HTTP 狀態碼都不會回應的狀態,我覺得這很不正常,我個人很不喜歡這種方法!

  3. 其三,我們都知道 IIS 可以支援同一個 Port 80 同時服務多個不同域名的站台。所以當你在關閉站台的時候,很有可能使用者連進網址看到的確實「另一個網站」的內容,這就更怪了。這點我是完全無法接受!

📝 小結:我覺得這是個有問題的更版方法,請大家不要再這麼做了!

手動關閉應用程式集區 (Application Pool)

這種方法的基本流程是:

  1. 找出要關閉的站台所使用的應用程式集區
  2. 停止應用程式集區
  3. 進行網站程式更新
  4. 啟動應用程式集區

這種方法還行,不過若你有多個網站站台共用同一個應用程式集區,會將所有網站也一起關閉。

關閉應用程式集區之後,站台其實不會關閉,而是改回應 HTTP 503 Service Unavailable 錯誤:

HTTP Error 503. The service is unavailable.

這種作法有幾個優點:

  1. 網站依然可以正常回應,而且 HTTP 503 可以表達非常明確的意圖,就是服務無法使用
  2. 網站不會像上面第一種方法那樣,讓網站連到其他完全不相關的站台。

如果你想用 PowerShell 自動化這個過程,可以參考以下命令:

# 停止應用程式集區
(Get-IISAppPool "DefaultAppPool").Stop()

# >>> 進行網站程式更新 <<<

# 啟動應用程式集區
(Get-IISAppPool "DefaultAppPool").Start()

📝 小結:我覺得這種方法還能接受,只是手動關閉應用程式集區程序比較麻煩一點!

建立應用程式離線檔 (App Offline file)

這種方法的基本流程是:

  1. 在站台根目錄加入 app_offline.htm 檔案 (檔案不需要內容)
  2. 進行網站程式更新
  3. 在站台根目錄刪除 app_offline.htm 檔案

你看,多簡單!這其實是微軟官方建議的部署方法!👍

.NET Runtime 有個 ASP.NET Core Module (ANCM),它是一個 IIS 模組,用來幫助 IIS 管理 ASP.NET Core 的執行程序。

事實上 ANCM 會在啟動 Kestrel 的時候,同時監控應用程式根目錄下是否有個 app_offline.htm 檔案出現,只要有出現,就會立即對應用程式進行優雅的關閉(gracefully shut down),並且停止接聽從用戶端發來的要求(Requests)。

這裡有個重點,那就是所謂 優雅的關閉(gracefully shut down),這個概念我在 當 .NET Core 執行在 Linux 或 Docker 容器中如何優雅的結束 文章中分享過這個觀念。這個意思也就是說,當 app_offline.htm 檔案出現的時候,並不代表著 ASP.NET Core 程序會立即停止,如果你的程式沒寫好,或是有個長時間執行的前景執行緒沒有完成,都會延長程序終止的時間。在 IIS 中,應用程式集區進階設定有個 ShutdownTimeLimit 設定值,用來定義當執行程序不優雅的不結束時,最長可以允許等待多久。IIS 的預設值為 90 秒。

一般來說,當你在透過 CI/CD 自動部署的階段,在建立 app_offline.htm 檔案之後通常會等待幾秒鐘,這個時間長短很難說,端看你的程式與電腦當下的效能狀況而定。所以比較正確的作法,通常是實作 重試 (Rety) 機制,當部署失敗就重新再發布一次,重複個兩三次應該就非常足夠了。

更多關於應用程式離線檔

基本上,你的網站只要有了 app_offline.htm 檔案就會立刻讓網站所有要求全部都轉入 HTTP 503 的回應,而頁面中的回應內容就是 app_offline.htm 的檔案內容。

這裡有點要特別注意,請不要將 app_offline.htm 網頁中需要載入的圖檔、JS、CSS 等檔案放在原本網站中,因為網站已經暫時無法服務。比較正確的作法是將這些靜態檔案放到 CDN 或是其他網頁儲存空間,如果你要把這些資源全部弄成 data URIs 格式也是可以的。

另外,如果你的站台主要以 Web API 為主的話,要注意除了回應狀態碼會統一變成 HTTP 503 之外,還要注意所有回應的 Content-Type 都會暫時變成 text/html 喔!

相關連結