上周我們遇到一個非常罕見的問題,一群人費了九牛二虎之力才徹底釐清案情,並且成功修復問題。我們在部署 Azure Functions 的時候,大部分的情境底下都是正常的,唯獨將站台重新部署到 Linux 平台的 App Service Plan 之後,詭異的狀況就此發生。由於案發過程過於冗長,我打算摘要記錄一下我們遭遇的現況與解決方法。
建立 Function App 的三種方法
-
透過 ARM Template
所有 Azure 部署的核心技術,就是 ARM Template,所有最新、最完整的部署設定,都可以從 ARM Template 找到!
-
透過 Azure CLI
最新版的 Azure CLI 通常可以跟上 ARM Template 的腳步,但還是會慢一點點
-
透過 PowerShell Az Module
最新版的 PowerShell Az Module 通常比 Azure CLI 還要強大,我不知道為什麼 Az
模組就是功能比較強、問題少,而且強型別的設計也確實好用許多,但我就是不愛用。😅
-
透過 Azure Portal
事實上 Azure Portal 的底層主要還是依賴 ARM Template 在運行,只是 Azure Portal 的 UI 變動的很慢,方便歸方便,但完全依賴 Azure Portal 的人,使用新技術是很容易鬼打牆的。
自動部署 Function App 的兩種方法
-
使用 Azure Functions Core Tools 工具手動部署
func azure functionapp publish $name
-
使用 Azure Pipelines 的 Azure Functions 工作 (Task) 自動部署
問題描述
我們的 Function App 跑在 dotnet-isolated
執行環境下,而且採用 .NET 6.0
版本。
透過 Azure Portal 建立 Function App 時,目前 Runtime 無法選取 .NET Isolated
,只能選擇 .NET
+ 6
而已。
透過 Azure Functions Core Tools 部署 Function App 時,工具會幫我自動調整 Function App 的 Runtime 設定,所以部署完成後是可以跑起來的。
如果你用 Azure Pipelines 的 Azure Functions 工作 (Task) 部署 Function App 的話,預設並不會自動調整 Function App 的 Runtime 設定,所以部署完成後 Function App 的 Runtime 設定不會變更,如果執行模式不同,就會導致 Azure Functions Runtime is unreachable 的問題!
重點是,目前 Azure Portal 無法在建立 Function App 之後手動調整 Runtime 設定,完全看不到選項可以調整,所以你不會在第一時間發現是這個設定有錯。(雷)
但是,當你使用一次 Azure Functions Core Tools 來部署 Function App,問題就會迎刃而解。
重現問題
我們的開發環境、測試環境,原本順利運行好幾週,但我想要重跑一次自動基礎環境部署流程,我原本自信滿滿,自動化部署跑了無數次,我在把資源群組砍掉後重建,就再也起不來了。這下慘了,Function 應用程式怎樣都跑不起來,真的是鬼打牆一整天,一個是我、一位來自上海的技術支援工程師、一位來自台灣的技術支援工程師、兩位負責應用程式開發的工程師,五個人花了 8 小時,才徹底釐清是上述的問題,成本之高啊!🤧
以下是全新環境的部署流程:
-
我們透過 ARM Template 部署 Function App,並沒有指定 linuxFxVersion
屬性
當時不知道有這個屬性存在,也覺得這個設定不重要,因為之前沒設定也跑得好好的,沒想到 Azure Functions Core Tools 部署 Function App 時會幫我自動調整 Function App 的 Runtime 設定!
這裡的 linuxFxVersion
屬性預設值為 DOTNET|6.0
。
-
啟動 Azure Pipelines 自動建置與部署 Function App
我是使用 Azure Function Deployment: ARM 進行部署,所以預設並不會自動調整 Function App 的 Runtime 設定,也因此我的 Function App 就爆掉了,完全無法啟動,Azure Portal 會一直出現 Azure Functions Runtime is unreachable 訊息提示,不過即便看了官方的 Troubleshoot error: Azure Functions Runtime is unreachable 文件依然完全沒有幫助。
此時我們還不知道是 linuxFxVersion
屬性屬性錯誤的問題。
-
接下來就是各種參數的排列組合,然後重建 Function App 數十次都沒有效果!
老實說,我當下有想到嘗試用 Azure Functions Core Tools 來部署,但心理就是不願意嘗試這種作法,因為我就是不想用手動部署環境。也還好我沒有這樣嘗試,否則問題的主因(Root Cause)就有可能找不到了。
我們一度以為 ARM Template 寫錯了,但一直無法讓服務恢復正常,深深覺得非常困擾!!!😡
-
最後嘗試用 Azure Functions Core Tools 來部署,想不到網站復活了!
經過將近 7 個小時的努力,終於第一次讓服務動起來,而且從 func azure functionapp publish $name
執行過程的訊息中,發現了一行不太顯眼的訊息,發現了他竟然在背景偷偷的幫我調整 App Service 的 linuxFxVersion
屬性,是真的很貼心沒錯啦,但文件要寫啊!(怒)😡
老實說,如果一開始就用 Azure Functions Core Tools 來部署,也不一定會發現這個小秘密,這 7 個小時實在是經歷太多技術細節了,真的是上帝要你學會這麼多東西,你還不能不接受呢!😅
解決方案
我最終的解決方案有兩個面向,兩種二選一都可以解決問題:
-
直接從 ARM Template 調整,建立 Function App 資源時就先指定好 linuxFxVersion
屬性為 DOTNET-ISOLATED|6.0
即可。
-
調整 Azure Pipelines 的 Azure Functions 工作 (Task),在部署時自動調整目標 App Service 的 linuxFxVersion
屬性為 DOTNET-ISOLATED|6.0
!
steps:
- task: AzureFunctionApp@1
displayName: 'Azure Function App Deploy: myfuncapp1'
inputs:
azureSubscription: 'sp-cicd-staging'
appType: functionAppLinux
appName: myfuncapp1
package: '$(System.DefaultWorkingDirectory)/_MyFuncApp1-CI/drop'
appSettings: '-FUNCTIONS_WORKER_RUNTIME dotnet-isolated'
configurationStrings: '-linuxFxVersion DOTNET-ISOLATED|6.0'
我不期望有多少人看懂我這篇文章,但我希望我以後再也不用透過 Google 查到這篇文章的存在! 😅
希望 Azure 變的更好的 murmur
- 要是 Azure Portal 不要這麼雞婆,隱藏他們認為不需要的設定,直接把所有選項都顯示在畫面上,問題可能會少很多。
- 要是 Azure Portal 可以更新的即時一點,都說未來要推
dotnet-isolated
了,為什麼到現在還不能在建立資源時選擇這個選項。
- 要是 Azure Pipelines 可以雞婆一點,把預設的
linuxFxVersion
設定上去,並顯示在 Log 中,我可以不用查問題查這麼久。
- 要是 Azure Functions Core Tools 發行時,可以把變更
linuxFxVersion
屬性的行為變成「警告」,我可能會比較容易看出問題。
- 要是 Azure ARM Template 可以要求我一定要加上
linuxFxVersion
屬性,我不會鬼打牆這麼久。
相關連結