Share via


教學課程: 在 Azure App Service 中建立安全的多層式架構 (N-Tier) 應用程式

許多應用程式都有多個單一元件。 例如,您可能具有可公開存取的前端,並連線到後端 API 或 Web 應用程式,進而連線到資料庫、儲存體帳戶、金鑰保存庫、另一個 VM,或以上資源的組合。 此架構即構成多層式架構 (N-Tier) 應用程式。 請務必建構這類應用程式,以盡可能保護後端資源。

在本教學課程中,您將了解如何使用連線至另一個網路隔離 Web 應用程式的前端 Web 應用程式,以部署安全的多層式架構 (N-Tier) 應用程式。 虛擬網路中的所有流量會透過虛擬網路整合私人端點加以隔離。 如需包含其他案例的完整指導,請參閱:

案例架構

下圖顯示您將在本教學課程期間建立的結構。

Architecture diagram of an n-tier App Service.

  • 虛擬網路 包含兩個子網,其中之一與前端 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 免費帳戶

完成本教學課程:

1.建立 Web 應用程式的兩個執行個體

您需要兩個 Web 應用程式執行個體,一個用於前端,另一個用於後端。 您必須採用基本以上的定價層,才能使用虛擬網路整合和私人端點。 您將在稍後設定虛擬網路整合和其他設定。

  1. 請建立資源群組來管理您在本教學課程中建立的所有資源。

    # Save resource group name and region as variables for convenience
    groupName=myresourcegroup
    region=eastus
    az group create --name $groupName --location $region
    
  2. 建立 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
    
  3. 建立 Web 應用程式。 以兩個全域唯一名稱 (有效的字元為 a-z0-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 區域。
  • 私人端點。
  1. 建立虛擬網路。 將 <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
    
  2. 建立 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 虛擬網路整合

  3. 為私人端點建立另一個子網路

    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,以停用私人端點網路原則

  4. 建立私人 DNS 區域

    az network private-dns zone create --resource-group $groupName --name privatelink.azurewebsites.net
    

    如需這些設定的詳細資訊,請參閱 Azure 私人端點 DNS 設定

    注意

    如果您使用入口網站建立私人端點,系統會自動為您建立私人 DNS 區域,無須分別建立區域。 為與本教學課程保持一致,您會使用 Azure CLI 分別建立私人 DNS 區域和私人端點。

  5. 將私人 DNS 區域連結至虛擬網路。

    az network private-dns link vnet create --resource-group $groupName --name myDnsLink --zone-name privatelink.azurewebsites.net --virtual-network $vnetName --registration-enabled False
    
  6. 在虛擬網路的私人端點子網路中,為您的後端 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
    
  7. 使用後端 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
    
  8. 當您建立 App Service 的私人端點時,會隱含停用公開存取。 如果您嘗試使用後端 Web 應用程式預設 URL 存取該應用程式,存取將會遭到拒絕。 請使用瀏覽器中導覽至 <backend-app-name>.azurewebsites.net 以確認此行為。

    Screenshot of 403 error when trying to access backend web app directly.

    如需使用私人端點設定 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 虛擬網路 NATAzure 防火牆,也可以使用路由所有流量。

4.透過網際網路實現後端 Web 應用程式部署

因為後端 Web 應用程式無法公開存取,所以必須將 SCM 網站設為可公開存取,並藉此讓持續部署工具觸達應用程式。 主要 Web 應用程式本身可以繼續拒絕所有流量。

  1. 啟用後端 Web 應用程式的公開存取。

    az webapp update --resource-group $groupName --name <backend-app-name> --set publicNetworkAccess=Enabled
    
  2. 為主要 Web 應用程式設定不相符規則動作,以拒絕所有流量。 雖然一般應用程式存取設定會設為允許公開存取,但此設定仍會拒絕對主要 Web 應用程式的公開存取。

    az resource update --resource-group $groupName --name <backend-app-name> --namespace Microsoft.Web --resource-type sites --set properties.siteConfig.ipSecurityRestrictionsDefaultAction=Deny
    
  3. 為 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 網站已可公開存取,您需要使用更好的安全性加以鎖定。

  1. 停用前端和後端 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
    
  2. 針對這兩個 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 設定持續部署

  1. 導覽至 Node.js 後端範例應用程式。 此應用程式是簡單的 Hello World 應用程式。

  2. 選取 GitHub 頁面右上方的 [派生] 按鈕。

  3. 選取 [擁有者],並保留預設存放庫名稱。

  4. 選取 [建立派生]

  5. Node.js 前端範例應用程式重複相同的流程。 此應用程式是可存取遠端 URL 的基本 Web 應用程式。

  6. 建立服務主體。 以您自己的值取代 <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 物件以供後續步驟使用。 該物件包含您的用戶端密碼,並且只會在此時顯示此密碼。 最佳做法為一律只授與最低存取權。 此範例所包含的範圍僅限於應用程式,而非整個資源群組。

  7. 若要將服務主體認證儲存為 GitHub 秘密,請前往 GitHub 中的其中一個分支範例存放庫,然後依序前往 [Settings]>[Security]>[Secrets and variables]>[Actions]

  8. 選取 [New repository secret],然後為下列每個值建立祕密。 您可以在您稍早複製的 json 輸出中找到這些值。

    名稱
    AZURE_APP_ID <application/client-id>
    AZURE_PASSWORD <client-secret>
    AZURE_TENANT_ID <tenant-id>
    AZURE_SUBSCRIPTION_ID <subscription-id>
  9. 針對其他分支範例存放庫重複此流程。

  10. 若要使用 GitHub Actions 設定持續部署,請登入 Azure 入口網站

  11. 導覽至前端 Web 應用程式的 [概觀] 頁面。

  12. 在左窗格中,選取 [部署中心]。 然後,選取 [設定]

  13. 在 [來源] 方塊中,選取 [CI/CD] 選項的 [GitHub]。

    Screenshot that shows how to choose the deployment source.

  14. 如果您是第一次從 GitHub 進行部署,請選取 [授權],然後遵循授權提示。 如果您想要從不同的使用者存放庫進行部署,請選取 [變更帳戶]

  15. 如果您使用在必要條件中分支的 Node.js 範例應用程式,請針對 [組織]、[存放庫] 和 [分支] 使用下列設定。

    設定
    組織 <your-GitHub-organization>
    存放庫 nodejs-frontend
    分行 main
  16. 選取 [儲存]。

  17. 針對後端 Web 應用程式重複相同的步驟。 下表將為您提供部署中心設定。

    設定
    組織 <your-GitHub-organization>
    存放庫 nodejs-backend
    分行 main

7.使用服務主體進行 GitHub Actions 部署

您的部署中心設定已在每個範例存放庫中建立預設工作流程檔案,但預設會使用發行設定檔,該發行設定檔會使用基本驗證。因為您已停用基本驗證,所以如果您查看部署中心的 [記錄] 索引標籤,就會看到自動觸發的部署導致錯誤。 您必須修改工作流程檔案,以使用服務主體向 App Service 進行驗證。 如需範例工作流程,請參閱將工作流程檔案新增至 GitHub 存放庫

  1. 開啟其中一個分支 GitHub 存放庫,並前往 <repo-name>/.github/workflows/ 目錄。

  2. 選取自動產生的工作流程檔案,然後選取右上方的「鉛筆」按鈕以編輯檔案。 使用下列文字取代內容,該文字會假設您稍早已為認證建立 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
    
  3. 針對其他分支 GitHub 存放庫中的工作流程檔案重複此流程。

新的 GitHub 提交會觸發每個應用程式的另一次部署。 這次,因為工作流程會使用服務主體向應用程式的 SCM 網站進行驗證,所以部署應該會成功。

如需如何使用 GitHub Actions 等提供者設定持續部署的詳細指導,請參閱持續部署至 Azure App Service (機器翻譯)。

8.驗證連線和應用程式存取

  1. 瀏覽至具有其 URL 的前端 Web 應用程式: https://<frontend-app-name>.azurewebsites.net

  2. 在文字方塊中,輸入後端 Web 應用程式的 URL: https://<backend-app-name>.azurewebsites.net。 如果您已正確設定連線,就應該會收到「來自後端 Web 應用程式的 Hello!」訊息,這也是後端 Web 應用程式的整個內容。 所有前端 Web 應用程式的輸出流量都會透過虛擬網路路由傳送。 您的前端 Web 應用程式會透過私人端點安全地連線至後端 Web 應用程式。 如果您的連線出現問題,前端 Web 應用程式就會當機。

  3. 嘗試使用其 URL 直接瀏覽至後端 Web 應用程式: https://<backend-app-name>.azurewebsites.net。 您應該會看見訊息 Web App - Unavailable。 如果您可以連線到應用程式,請確定您已設定私人端點,而且應用程式的存取限制應設為拒絕主要 Web 應用程式的所有流量。

  4. 若要進一步驗證前端 Web 應用程式是否透過私人連結連線至後端 Web 應用程式,請透過 SSH 連線至其中一個前端的執行個體。 若要使用 SSH,請執行下列命令以建立應用程式 Web 容器的 SSH 工作階段,並在瀏覽器中開啟遠端殼層。

    az webapp ssh --resource-group $groupName --name <frontend-app-name>
    
  5. 請在瀏覽器中開啟殼層時執行 nslookup,以確認使用後端 Web 應用程式的私人 IP 連線至後端 Web 應用程式。 您也可以執行 curl 再次驗證網站內容。 以後端 Web 應用程式名稱取代 <backend-app-name>

    nslookup <backend-app-name>.azurewebsites.net
    curl https://<backend-app-name>.azurewebsites.net
    

    Screenshot of SSH session showing how to validate app connections.

    nslookup 應解析為後端 Web 應用程式的私人 IP 位址。 該私人 IP 位址應該是您虛擬網路內的位址。 若要確認您的私人 IP,請前往後端 Web 應用程式的 [網路] 頁面。

    Screenshot of App Service Networking page showing the inbound IP of the app.

  6. 在另一個終端機 (非前端執行個體上的 SSH 工作階段) 重複相同的 nslookupcurl 命令。

    Screenshot of an external terminal doing a nslookup and curl of the back-end web app.

    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

執行此命令可能會需要花費幾分鐘。

常見問題集

是否有使用服務主體進行部署的替代方案?

因為您已在本教學課程中停用基本驗證,所以無法使用使用者名稱和密碼向後端 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 部署資源 (機器翻譯)。

下一步