教學課程: 在 Azure App Service 中建立安全的多層式架構 (N-Tier) 應用程式
許多應用程式都有多個單一元件。 例如,您可能具有可公開存取的前端,並連線到後端 API 或 Web 應用程式,進而連線到資料庫、儲存體帳戶、金鑰保存庫、另一個 VM,或以上資源的組合。 此架構即構成多層式架構 (N-Tier) 應用程式。 請務必建構這類應用程式,以盡可能保護後端資源。
在本教學課程中,您將了解如何使用連線至另一個網路隔離 Web 應用程式的前端 Web 應用程式,以部署安全的多層式架構 (N-Tier) 應用程式。 虛擬網路中的所有流量會透過虛擬網路整合和私人端點加以隔離。 如需包含其他案例的完整指導,請參閱:
案例架構
下圖顯示您將在本教學課程期間建立的結構。
- 虛擬網路 包含兩個子網,其中之一與前端 Web 應用程式整合,另一個則具有後端 Web 應用程式的私人端點。 虛擬網路會封鎖所有輸入網路流量,但與該虛擬網路整合的前端應用程式例外。
- 前端 Web 應用程式 已整合至虛擬網路中,並透過公用網際網路存取。
- 後端 Web 應用程式 只能透過虛擬網路中的私人端點存取。
- 私人端點 與後端 Web 應用程式整合,並可透過私人 IP 位址存取該 Web 應用程式。
- 私人 DNS 區域 可讓您將 DNS 名稱解析為私人端點的 IP 位址。
注意
App Service 基本層以上皆可使用虛擬網路整合和私人端點。 免費層不支援以上功能。 透過此結構:
- 前往後端應用程式的公用流量會遭到封鎖。
- 來自 App Service 的輸出流量已路由至虛擬網路,並可觸達後端應用程式。
- App Service 能夠對後端應用程式執行 DNS 解析。
此案例示範了 App Service 中其中一個可能的多層式架構 (N-Tier) 案例。 您可以使用本教學課程中涵蓋的概念來建置更為複雜的多層式架構 (N-Tier) 應用程式。
您將學到什麼:
- 建立虛擬網路和子網路,以進行 App Service 虛擬網路整合。
- 建立私人 DNS 區域。
- 建立私人端點。
- 在 App Service 中設定虛擬網路整合。
- 在 App Service 中停用基本驗證。
- 持續部署至鎖定的後端 Web 應用程式。
必要條件
本教學課程使用裝載於 GitHub 的兩個範例 Node.js 應用程式。 如果您還沒有 GitHub 帳戶,請建立免費帳戶。
如果您沒有 Azure 訂用帳戶,請在開始之前先建立 Azure 免費帳戶。
完成本教學課程:
在 Azure Cloud Shell 中使用 Bash 環境。 如需詳細資訊,請參閱 Azure Cloud Shell 中的 Bash 快速入門。
若要在本地執行 CLI 參考命令,請安裝 Azure CLI。 若您在 Windows 或 macOS 上執行,請考慮在 Docker 容器中執行 Azure CLI。 如需詳細資訊,請參閱〈如何在 Docker 容器中執行 Azure CLI〉。
如果您使用的是本機安裝,請使用 az login 命令,透過 Azure CLI 來登入。 請遵循您終端機上顯示的步驟,完成驗證程序。 如需其他登入選項,請參閱使用 Azure CLI 登入。
出現提示時,請在第一次使用時安裝 Azure CLI 延伸模組。 如需擴充功能詳細資訊,請參閱使用 Azure CLI 擴充功能。
執行 az version 以尋找已安裝的版本和相依程式庫。 若要升級至最新版本,請執行 az upgrade。
1.建立 Web 應用程式的兩個執行個體
您需要兩個 Web 應用程式執行個體,一個用於前端,另一個用於後端。 您必須採用基本以上的定價層,才能使用虛擬網路整合和私人端點。 您將在稍後設定虛擬網路整合和其他設定。
請建立資源群組來管理您在本教學課程中建立的所有資源。
# Save resource group name and region as variables for convenience groupName=myresourcegroup region=eastus az group create --name $groupName --location $region
建立 App Service 方案。 將
<app-service-plan-name>
更換為唯一名稱。 如果您需要使用不同的 SKU,請修改--sku
參數。 因為免費層 SKU 不支援必要的網路功能,所以請確定您並非採用該定價層。# Save App Service plan name as a variable for convenience aspName=<app-service-plan-name> az appservice plan create --name $aspName --resource-group $groupName --is-linux --location $region --sku P1V3
建立 Web 應用程式。 以兩個全域唯一名稱 (有效的字元為
a-z
、0-9
和-
) 取代<frontend-app-name>
和<backend-app-name>
。 本教學課程中提供了範例 Node.js 應用程式。 如果您想要使用自己的應用程式,請相應變更--runtime
參數。 執行az webapp list-runtimes
可取得可用執行階段的清單。az webapp create --name <frontend-app-name> --resource-group $groupName --plan $aspName --runtime "NODE:18-lts" az webapp create --name <backend-app-name> --resource-group $groupName --plan $aspName --runtime "NODE:18-lts"
2.建立網路基礎結構
您將建立下列網路資源:
- 虛擬網路。
- App Service 虛擬網路整合的子網路。
- 私人端點的子網路。
- 私人 DNS 區域。
- 私人端點。
建立虛擬網路。 將
<virtual-network-name>
更換為唯一名稱。# Save vnet name as variable for convenience vnetName=<virtual-network-name> az network vnet create --resource-group $groupName --location $region --name $vnetName --address-prefixes 10.0.0.0/16
建立 App Service 虛擬網路整合的子網路。
az network vnet subnet create --resource-group $groupName --vnet-name $vnetName --name vnet-integration-subnet --address-prefixes 10.0.0.0/24 --delegations Microsoft.Web/serverfarms --disable-private-endpoint-network-policies false
針對 App Service,建議虛擬網路整合子網路至少需具有
/26
個 CIDR 區塊。/24
已足夠。--delegations Microsoft.Web/serverfarms
指定將子網路委派至 App Service 虛擬網路整合。為私人端點建立另一個子網路。
az network vnet subnet create --resource-group $groupName --vnet-name $vnetName --name private-endpoint-subnet --address-prefixes 10.0.1.0/24 --disable-private-endpoint-network-policies true
針對私人端點子網路,您必須將
--disable-private-endpoint-network-policies
設為true
,以停用私人端點網路原則。建立私人 DNS 區域。
az network private-dns zone create --resource-group $groupName --name privatelink.azurewebsites.net
如需這些設定的詳細資訊,請參閱 Azure 私人端點 DNS 設定。
注意
如果您使用入口網站建立私人端點,系統會自動為您建立私人 DNS 區域,無須分別建立區域。 為與本教學課程保持一致,您會使用 Azure CLI 分別建立私人 DNS 區域和私人端點。
將私人 DNS 區域連結至虛擬網路。
az network private-dns link vnet create --resource-group $groupName --name myDnsLink --zone-name privatelink.azurewebsites.net --virtual-network $vnetName --registration-enabled False
在虛擬網路的私人端點子網路中,為您的後端 Web 應用程式建立私人端點。 以後端 Web 應用程式名稱取代
<backend-app-name>
。# Get backend web app resource ID resourceId=$(az webapp show --resource-group $groupName --name <backend-app-name> --query id --output tsv) az network private-endpoint create --resource-group $groupName --name myPrivateEndpoint --location $region --connection-name myConnection --private-connection-resource-id $resourceId --group-id sites --vnet-name $vnetName --subnet private-endpoint-subnet
使用後端 Web 應用程式私人端點的 DNS 區域群組,將私人端點連結至私人 DNS 區域。 此 DNS 區域群組可協助您在私人端點出現更新時,自動更新私人 DNS 區域。
az network private-endpoint dns-zone-group create --resource-group $groupName --endpoint-name myPrivateEndpoint --name myZoneGroup --private-dns-zone privatelink.azurewebsites.net --zone-name privatelink.azurewebsites.net
當您建立 App Service 的私人端點時,會隱含停用公開存取。 如果您嘗試使用後端 Web 應用程式預設 URL 存取該應用程式,存取將會遭到拒絕。 請使用瀏覽器中導覽至
<backend-app-name>.azurewebsites.net
以確認此行為。如需使用私人端點設定 App Service 存取限制的詳細資訊,請參閱 Azure App Service 存取限制。
3.在您的前端應用程式中設定虛擬網路整合
在您的應用程式上啟用虛擬網路整合。 以 Web 應用程式名稱取代 <frontend-app-name>
。
az webapp vnet-integration add --resource-group $groupName --name <frontend-app-name> --vnet $vnetName --subnet vnet-integration-subnet
虛擬網路整合可讓輸出流量直接流向虛擬網路。 根據預設,只有在 RFC-1918 (英文) 中定義的區域 IP 流量,才會路由至虛擬網路,這正是您私人端點所需要的。 若要將您的所有流量路由至虛擬網路,請參閱管理虛擬網路整合路由。 如果您想要透過您的虛擬網路路由網際網路流量,例如:透過 Azure 虛擬網路 NAT 或 Azure 防火牆,也可以使用路由所有流量。
4.透過網際網路實現後端 Web 應用程式部署
因為後端 Web 應用程式無法公開存取,所以必須將 SCM 網站設為可公開存取,並藉此讓持續部署工具觸達應用程式。 主要 Web 應用程式本身可以繼續拒絕所有流量。
啟用後端 Web 應用程式的公開存取。
az webapp update --resource-group $groupName --name <backend-app-name> --set publicNetworkAccess=Enabled
為主要 Web 應用程式設定不相符規則動作,以拒絕所有流量。 雖然一般應用程式存取設定會設為允許公開存取,但此設定仍會拒絕對主要 Web 應用程式的公開存取。
az resource update --resource-group $groupName --name <backend-app-name> --namespace Microsoft.Web --resource-type sites --set properties.siteConfig.ipSecurityRestrictionsDefaultAction=Deny
為 SCM 網站設定不相符規則動作,以允許所有流量。
az resource update --resource-group $groupName --name <backend-app-name> --namespace Microsoft.Web --resource-type sites --set properties.siteConfig.scmIpSecurityRestrictionsDefaultAction=Allow
5.鎖定 FTP 和 SCM 存取
現在,後端 SCM 網站已可公開存取,您需要使用更好的安全性加以鎖定。
停用前端和後端 Web 應用程式的 FTP 存取。 以您自己的應用程式名稱取代
<frontend-app-name>
和<backend-app-name>
。az resource update --resource-group $groupName --name ftp --namespace Microsoft.Web --resource-type basicPublishingCredentialsPolicies --parent sites/<frontend-app-name> --set properties.allow=false az resource update --resource-group $groupName --name ftp --namespace Microsoft.Web --resource-type basicPublishingCredentialsPolicies --parent sites/<backend-app-name> --set properties.allow=false
針對這兩個 Web 應用程式停用 WebDeploy 連接埠和 SCM/進階工具網站的基本驗證存取。 以您自己的應用程式名稱取代
<frontend-app-name>
和<backend-app-name>
。az resource update --resource-group $groupName --name scm --namespace Microsoft.Web --resource-type basicPublishingCredentialsPolicies --parent sites/<frontend-app-name> --set properties.allow=false az resource update --resource-group $groupName --name scm --namespace Microsoft.Web --resource-type basicPublishingCredentialsPolicies --parent sites/<backend-app-name> --set properties.allow=false
停用 App Service 的基本驗證會對 Microsoft Entra ID 所支援的使用者限制 FTP 和 SCM 端點存取,進一步提高應用程式的安全性。 如需停用基本驗證 (包括測試和監視登入的方法) 的詳細資訊,請參閱停用 App Service 的基本驗證 (英文)。
6.使用 GitHub Actions 設定持續部署
導覽至 Node.js 後端範例應用程式。 此應用程式是簡單的 Hello World 應用程式。
選取 GitHub 頁面右上方的 [派生] 按鈕。
選取 [擁有者],並保留預設存放庫名稱。
選取 [建立派生]。
對 Node.js 前端範例應用程式重複相同的流程。 此應用程式是可存取遠端 URL 的基本 Web 應用程式。
建立服務主體。 以您自己的值取代
<subscription-id>
、<frontend-app-name>
和<backend-app-name>
。az ad sp create-for-rbac --name "myApp" --role contributor --scopes /subscriptions/<subscription-id>/resourceGroups/$groupName/providers/Microsoft.Web/sites/<frontend-app-name> /subscriptions/<subscription-id>/resourceGroups/$groupName/providers/Microsoft.Web/sites/<backend-app-name> --sdk-auth
輸出為具有角色指派認證的 JSON 物件,該認證可讓您存取 App Service 應用程式。 複製此 JSON 物件以供後續步驟使用。 該物件包含您的用戶端密碼,並且只會在此時顯示此密碼。 最佳做法為一律只授與最低存取權。 此範例所包含的範圍僅限於應用程式,而非整個資源群組。
若要將服務主體認證儲存為 GitHub 秘密,請前往 GitHub 中的其中一個分支範例存放庫,然後依序前往 [Settings]>[Security]>[Secrets and variables]>[Actions]。
選取 [New repository secret],然後為下列每個值建立祕密。 您可以在您稍早複製的 json 輸出中找到這些值。
名稱 值 AZURE_APP_ID <application/client-id>
AZURE_PASSWORD <client-secret>
AZURE_TENANT_ID <tenant-id>
AZURE_SUBSCRIPTION_ID <subscription-id>
針對其他分支範例存放庫重複此流程。
若要使用 GitHub Actions 設定持續部署,請登入 Azure 入口網站。
導覽至前端 Web 應用程式的 [概觀] 頁面。
在左窗格中,選取 [部署中心]。 然後,選取 [設定]。
在 [來源] 方塊中,選取 [CI/CD] 選項的 [GitHub]。
如果您是第一次從 GitHub 進行部署,請選取 [授權],然後遵循授權提示。 如果您想要從不同的使用者存放庫進行部署,請選取 [變更帳戶]。
如果您使用在必要條件中分支的 Node.js 範例應用程式,請針對 [組織]、[存放庫] 和 [分支] 使用下列設定。
設定 值 組織 <your-GitHub-organization>
存放庫 nodejs-frontend 分行 main 選取 [儲存]。
針對後端 Web 應用程式重複相同的步驟。 下表將為您提供部署中心設定。
設定 值 組織 <your-GitHub-organization>
存放庫 nodejs-backend 分行 main
7.使用服務主體進行 GitHub Actions 部署
您的部署中心設定已在每個範例存放庫中建立預設工作流程檔案,但預設會使用發行設定檔,該發行設定檔會使用基本驗證。因為您已停用基本驗證,所以如果您查看部署中心的 [記錄] 索引標籤,就會看到自動觸發的部署導致錯誤。 您必須修改工作流程檔案,以使用服務主體向 App Service 進行驗證。 如需範例工作流程,請參閱將工作流程檔案新增至 GitHub 存放庫。
開啟其中一個分支 GitHub 存放庫,並前往
<repo-name>/.github/workflows/
目錄。選取自動產生的工作流程檔案,然後選取右上方的「鉛筆」按鈕以編輯檔案。 使用下列文字取代內容,該文字會假設您稍早已為認證建立 GitHub 祕密。 更新「env」區段下
<web-app-name>
的預留位置,然後直接提交至主要分支。 此提交會觸發 GitHub Action 再次執行並部署您的程式代碼,而這次會使用服務主體進行驗證。name: Build and deploy Node.js app to Azure Web App on: push: branches: - main workflow_dispatch: env: AZURE_WEBAPP_NAME: <web-app-name> # set this to your application's name NODE_VERSION: '18.x' # set this to the node version to use AZURE_WEBAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Node.js version uses: actions/setup-node@v1 with: node-version: ${{ env.NODE_VERSION }} - name: npm install, build run: | npm install npm run build --if-present - name: Upload artifact for deployment job uses: actions/upload-artifact@v2 with: name: node-app path: . deploy: runs-on: ubuntu-latest needs: build environment: url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} steps: - name: Download artifact from build job uses: actions/download-artifact@v2 with: name: node-app - uses: azure/login@v1 with: creds: | { "clientId": "${{ secrets.AZURE_APP_ID }}", "clientSecret": "${{ secrets.AZURE_PASSWORD }}", "subscriptionId": "${{ secrets.AZURE_SUBSCRIPTION_ID }}", "tenantId": "${{ secrets.AZURE_TENANT_ID }}" } - name: 'Deploy to Azure Web App' id: deploy-to-webapp uses: azure/webapps-deploy@v2 with: app-name: ${{ env.AZURE_WEBAPP_NAME }} package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - name: logout run: | az logout
針對其他分支 GitHub 存放庫中的工作流程檔案重複此流程。
新的 GitHub 提交會觸發每個應用程式的另一次部署。 這次,因為工作流程會使用服務主體向應用程式的 SCM 網站進行驗證,所以部署應該會成功。
如需如何使用 GitHub Actions 等提供者設定持續部署的詳細指導,請參閱持續部署至 Azure App Service (機器翻譯)。
8.驗證連線和應用程式存取
瀏覽至具有其 URL 的前端 Web 應用程式:
https://<frontend-app-name>.azurewebsites.net
。在文字方塊中,輸入後端 Web 應用程式的 URL:
https://<backend-app-name>.azurewebsites.net
。 如果您已正確設定連線,就應該會收到「來自後端 Web 應用程式的 Hello!」訊息,這也是後端 Web 應用程式的整個內容。 所有前端 Web 應用程式的輸出流量都會透過虛擬網路路由傳送。 您的前端 Web 應用程式會透過私人端點安全地連線至後端 Web 應用程式。 如果您的連線出現問題,前端 Web 應用程式就會當機。嘗試使用其 URL 直接瀏覽至後端 Web 應用程式:
https://<backend-app-name>.azurewebsites.net
。 您應該會看見訊息Web App - Unavailable
。 如果您可以連線到應用程式,請確定您已設定私人端點,而且應用程式的存取限制應設為拒絕主要 Web 應用程式的所有流量。若要進一步驗證前端 Web 應用程式是否透過私人連結連線至後端 Web 應用程式,請透過 SSH 連線至其中一個前端的執行個體。 若要使用 SSH,請執行下列命令以建立應用程式 Web 容器的 SSH 工作階段,並在瀏覽器中開啟遠端殼層。
az webapp ssh --resource-group $groupName --name <frontend-app-name>
請在瀏覽器中開啟殼層時執行
nslookup
,以確認使用後端 Web 應用程式的私人 IP 連線至後端 Web 應用程式。 您也可以執行curl
再次驗證網站內容。 以後端 Web 應用程式名稱取代<backend-app-name>
。nslookup <backend-app-name>.azurewebsites.net curl https://<backend-app-name>.azurewebsites.net
nslookup
應解析為後端 Web 應用程式的私人 IP 位址。 該私人 IP 位址應該是您虛擬網路內的位址。 若要確認您的私人 IP,請前往後端 Web 應用程式的 [網路] 頁面。在另一個終端機 (非前端執行個體上的 SSH 工作階段) 重複相同的
nslookup
和curl
命令。nslookup
會傳回後端 Web 應用程式的公用 IP。 因為已停用後端 Web 應用程式的公用存取,所以如果您嘗試連線至公用 IP,就會收到存取遭拒錯誤。 此錯誤表示您無法從公用網際網路存取此網站,這是預期的行為。 因為您只能透過私人 DNS 區域在虛擬網路內解析nslookup
,所以其無法解析為私人 IP。 只有前端 Web 應用程式位於虛擬網路之內。 如果您試著透過外部終端機在後端 Web 應用程式上執行curl
,傳回的 HTML 會包含Web App - Unavailable
。 當您嘗試在瀏覽器中導覽至後端 Web 應用程式時,此錯誤會顯示您稍早所見之錯誤頁面的 HTML。
9.清除資源
在上述步驟中,您已建立資源群組中的 Azure 資源。 如果您在未來不需要這些資源,請在 Cloud Shell 中執行下列命令,以刪除資源群組。
az group delete --name myresourcegroup
執行此命令可能會需要花費幾分鐘。
常見問題集
- 是否有使用服務主體進行部署的替代方案?
- 當我在 App Service 中設定 GitHub Actions 部署時,會發生什麼事?
- 讓後端 SCM 保持可公開存取是否安全?
- 是否存在完全不開放後端 SCM 網站即可進行部署的方法?
- 如何使用 ARM/Bicep 部署此結構?
是否有使用服務主體進行部署的替代方案?
因為您已在本教學課程中停用基本驗證,所以無法使用使用者名稱和密碼向後端 SCM 網站進行驗證,也無法使用發行設定檔進行驗證。 除了服務主體外,您也可以使用 OpenID Connect。
當我在 App Service 中設定 GitHub Actions 部署時,會發生什麼事?
Azure 會在您的存放庫中自動產生工作流程檔案。 所選取存放庫和分支中的新認可,現在會持續部署到 App Service 應用程式。 您可以在 [記錄] 索引標籤上追蹤認可和部署。
使用發行設定檔向 App Service 驗證的預設工作流程檔案會新增至您的 GitHub 存放庫。 您可以前往 <repo-name>/.github/workflows/
目錄來檢視此檔案。
讓後端 SCM 保持可公開存取是否安全?
當您鎖定 FTP 和 SCM 存取時,可確保只有 Microsoft Entra 支援的主體可以存取 SCM 端點 (即使端點可公開存取也一樣)。 此設定應該可保證後端 Web 應用程式依然安全無恙。
是否存在完全不開放後端 SCM 網站即可進行部署的方法?
如果您對啟用 SCM 網站的公開存取感到擔憂,或受到原則限制,請考慮其他 App Service 部署選項,例如從 ZIP 套件加以執行。
如何使用 ARM/Bicep 部署此結構?
您可以使用 ARM/Bicep 範本來部署您在本教學課程中建立的資源。 連線至後端 Web 應用程式 Bicep 範本的應用程式可讓您建立安全多層式架構 (N-Tier) 應用程式解決方案。
若要了解如何部署 ARM/Bicep 範本,請參閱如何使用 Bicep 和 Azure CLI 部署資源 (機器翻譯)。