py程式設計(三)課程網站
延伸單元 · 約 2 節課 · 可接在第 8 週(記帳網站上線)之後

部署到 Azure:同一份程式碼,多一個家

學會用 Vercel 上線之後,這一課把同一個 GitHub 儲存庫再部署到 Microsoft Azure 的 Static Web Apps,最後替網站綁上自己的網址。學生會看到:程式碼只有一份,卻能同時部署到兩個雲端平台;也會親手讀懂一次部署失敗的錯誤訊息並修好它。本頁所有截圖都來自實際部署派課室網站(pyclass.bangcoding.com)的過程。

GitHub一份程式碼vercel.json、staticwebapp.config.json、.github/workflows 各給各的平台讀
git push→
Vercelpyclass-site.vercel.app讀 vercel.json,自動部署
同時→
Azurepyclass.bangcoding.comGitHub Actions 依 workflow 部署

本課結束時你會

0:00–0:15觀念:代管服務 vs 虛擬機器、一份程式碼部署到多個平台
0:15–0:30步驟 1:登入 Azure、確認訂用帳戶與權限
0:30–1:00步驟 2:建立 Static Web App【跟著做】
1:00–1:10休息
1:10–1:40步驟 3:第一次部署失敗 → 讀錯誤 → 修 YAML【一起除錯】
1:40–2:10步驟 4–5:網域與 DNS、綁定自訂網域【老師示範】
2:10–2:30驗收、費用檢查、回顧

學生帳號需要能在 Azure 建立資源。學校訂用帳戶建議由老師先建好一個資源群組(例如 rg-class-2026),在「存取控制 (IAM)」把學生加為該資源群組的參與者,不要給整個訂用帳戶的權限。也可以讓學生使用個人的 Azure for Students 方案。買網域只需要老師示範一次,學生之後可以分到老師網域底下的子網域。

觀念:Azure 上「放網站」有很多種方式

Azure 的服務多達數百種,同樣是「把網站放上網」,選錯服務的代價可能是每個月好幾千元。我們的課程網站和學生專案的前端都是寫好的 HTML、CSS、JavaScript,伺服器只要把檔案送出去,Python 與 SQL 練習也都在學生的瀏覽器裡執行,所以最適合的是靜態 Web 應用程式(Static Web Apps):Azure 全權代管、免費方案就夠用,而且和 Vercel 一樣會從 GitHub 自動部署。

Static Web App(本課使用)虛擬機器
誰管主機Azure 全部代管自己安裝系統、架網頁伺服器、更新、顧資安
費用免費方案 $0只要開著就按小時計費
更新網站推上 GitHub 自動部署自己把檔案傳上主機
網址與 HTTPS自動提供,可綁自訂網域並免費核發憑證自己設定網域與憑證

在 Marketplace 搜尋時,判斷一張卡片只要看兩個地方:發行者是不是 Microsoft,以及類型是 Azure Service(代管服務)、Virtual Machine(虛擬機器)還是 SaaS(訂閱軟體)。下圖第一張「靜態 Web 應用程式」才是我們要的;其他寫著 Nginx、Next.js 的卡片都是虛擬機器,旁邊還標著每小時的價格。

Marketplace 搜尋結果,第一張是 Microsoft 的靜態 Web 應用程式,其餘多為按小時計費的虛擬機器
Marketplace:只有第一張是 Microsoft 的代管服務,其他多是會按小時計費的虛擬機器

最常見的意外支出:首頁上方常有熱門產品的推薦(例如 AI 模型服務),按下「設定並訂閱」就會開始計費;建立表單的「方案類型」預設是付費的「標準」。每一步都要看清楚再按。

建立資源頁上方的熱門 Marketplace 產品推薦
建立資源頁上方的推薦產品,和本課無關,不要點

步驟 1:登入並確認訂用帳戶

  1. 打開 portal.azure.com,用學校發的 Microsoft 帳號登入(和 Office 365 同一組)。
  2. 首頁點「訂用帳戶」,確認看得到學校的訂用帳戶。
  3. 點進訂用帳戶 →「存取控制 (IAM)」→「角色指派」,確認自己的角色是「擁有者」或「參與者」。只有「讀者」就不能建立資源,要請管理者開權限。
Azure 入口網站首頁,最近的資源中有學校的訂用帳戶
入口網站首頁:「最近」清單會列出訂用帳戶與既有資源
訂用帳戶的存取控制,角色指派清單有兩位擁有者
存取控制 (IAM):看誰有權限、是什麼角色(個資已遮蔽)

名詞:看得到訂用帳戶不等於能動它,能做什麼由「角色」決定,分配權限要依「最小權限原則」。清單中的 Foreign Principal(外部群組)通常是經銷商保留的管理權限,用來處理帳務與支援,不要自己刪除。

可以順便帶學生看既有的虛擬機器:狀態要是「已停止(已解除配置)」才不收運算費;在 VM 的作業系統裡按關機只會變成「已停止」,運算費照算。停止後的磁碟與固定公用 IP 仍會收少量費用。

虛擬機器概觀頁,狀態為已停止(已解除配置)
虛擬機器「已停止(已解除配置)」:不收運算費,但磁碟與公用 IP 仍計費

步驟 2:建立 Static Web App【跟著做】

  1. 「建立資源」→ 搜尋 Static Web App → 選發行者為 Microsoft 的「靜態 Web 應用程式」→「建立」。
  2. 依下表填寫「基本」頁籤:
欄位填寫為什麼
資源群組按「新建」→ rg-pyclass(學生用老師開好的群組)同一個網站的東西放在一起,日後一次刪除也不會誤刪別人的資源
名稱pyclass-siteAzure 內的資源名稱
方案類型免費⚠️ 預設是付費的「標準」,一定要改
來源GitHub → 以 GitHub 登入並授權讓 Azure 能在儲存庫加入部署設定
組織/存放庫/分支自己的帳號 / 專案儲存庫 / main
組建預設Custom純 HTML 網站不需要框架組建;React、Vite 等專案要選對應框架
應用程式位置 / API 位置 / 輸出位置/ / 留空 / .網站檔案就在儲存庫最外層,不需另外產生輸出資料夾
建立靜態 Web 應用程式表單,方案類型預設為標準
剛打開的表單:方案類型預設是付費的「標準」
填好的表單:資源群組 rg-pyclass、方案免費、來源 GitHub
填好後:rg-pyclass、免費、GitHub 存放庫與分支
組建詳細資料:Custom、應用程式位置 /、輸出位置 .
組建設定:Custom、/、API 留空、輸出 .
  1. 按「檢閱 + 建立」,確認 SKU 為 Free 後按「建立」,約 1 分鐘完成。
檢閱頁:SKU Free、存放庫與分支、部署授權原則為部署權杖
檢閱頁:確認 SKU 是 Free。「部署權杖」會自動存進 GitHub 的 Secrets
您的部署已完成
「部署已完成」只代表資源建好了,網站內容還要等 GitHub Actions

地區顯示 centralus 沒關係:靜態檔案會自動發佈到全球節點,台灣的使用者會從最近的節點取得;地區只影響 API 與管理資料。部署權杖是一組密碼,Azure 把它存在 GitHub 儲存庫的 Secrets,不會出現在程式碼裡,這和「金鑰只放環境變數」是同一個觀念。

步驟 3:第一次部署失敗了——讀錯誤、修設定【一起除錯】

按下建立後,Azure 會在 GitHub 儲存庫加入 .github/workflows/azure-static-web-apps-….yml,並立刻觸發第一次部署。到 GitHub 儲存庫上方的「Actions」分頁,會看到第一次是紅色叉叉。點進去讀錯誤紀錄:

Detected following platforms:
  nodejs: 22.23.2
Error: Could not find either 'build' or 'build:azure' node under 'scripts' in package.json.

Azure 的組建工具看到儲存庫裡有 package.json(這是給 Vercel 研究紀錄 API 用的),就以為這是需要先執行 npm run build 的 Node.js 專案,結果找不到組建指令而失敗。但我們的網站是寫好的 HTML,直接上傳就可以,所以只要告訴它跳過組建:

  1. 在 GitHub 打開 .github/workflows/azure-static-web-apps-….yml,按鉛筆圖示編輯。
  2. 在 output_location: "." 的下一行加入(前面的空白要和上一行對齊):
          output_location: "." # Built app content directory - optional
          skip_app_build: true
  1. 「Commit changes」,訊息寫「跳過組建,直接上傳靜態網站」。Actions 會自動重跑,約 1 分鐘出現綠色勾勾。
在 GitHub 網頁編輯 workflow,第 34 行加入 skip_app_build: true
第 34 行加入 skip_app_build: true,縮排與上一行對齊
Actions 頁面:第一次失敗、第二次成功
Actions:#1 失敗(紅叉),修改後的 #2 成功(綠勾)

除錯的固定流程:Actions 分頁 → 點失敗的那一次 → 找紅字 → 依訊息修改 → 推送後再看一次。YAML 的縮排有意義,空白對不齊設定就會失效,這是學生最常犯的錯。修完之後如果要在自己電腦繼續工作,記得先 git pull 把網頁上的修改拉下來。

這時網站已經可以用 Azure 給的網址打開,例如 https://white-sand-02103a410.3.azurestaticapps.net。同一個 commit 也讓 Vercel 重新部署了一次,兩邊內容永遠一樣:每個平台只讀自己的設定檔(Vercel 讀 vercel.json,Azure 讀 workflow 與 staticwebapp.config.json),互不干擾。

pyclass-site 概觀頁:狀態就緒、SKU Free、URL
Static Web App 概觀頁:狀態「就緒」、方案 Free、Azure 提供的網址

這個失敗是真實發生的,建議不要事先修好,讓學生自己在 Actions 讀到紅字。可以問:「錯誤訊息說它偵測到什麼平台?為什麼它會這樣以為?」Azure 會把 xxx.html 轉址成不含副檔名的 xxx,相對連結仍然正常,可以讓學生觀察兩個平台網址的差別。

步驟 4:網域與 DNS【老師示範】

Azure 給的網址又長又難記。要換成自己的網址,需要三樣東西互相配合:網域像門牌,向註冊商「租」來,每年續約;DNS 像查號台,記錄每個門牌要指向哪裡;網站主機則是真正的房子。一個網域可以分出很多子網域,各自指向不同的網站:

bangcoding.com(網域,一年 11.99 美元)
  ├── pyclass.bangcoding.com   → Azure Static Web App(本課)
  ├── foodie.bangcoding.com    → 吃貨評分站示範
  └── 學號.bangcoding.com      → 學生期末專題

Azure 也有賣網域,服務名稱是「App Service 網域」。在 Azure 買的好處是會自動建立 Azure DNS 區域並完成連結;限制是只有 .com、.net、.org 等國際網域(沒有 .tw),而且點數型或部分經銷商訂閱可能不符合購買資格,送出時才會檢查。

  1. 「建立資源」→ 搜尋「App Service 網域」→ 選 Microsoft 的那一張 →「建立」。
  2. 資源群組選 rg-pyclass,在「網域」欄位搜尋想要的名稱。出現「此網域無法使用」代表已被註冊,下方會列出可買的類似名稱。
  3. 「連絡資訊」要填真實資料(國際網域管理機構 ICANN 的規定),並確認隱私權保護為啟用,個資就不會出現在公開的 WHOIS 查詢裡。
  4. 在「進階」頁籤把自動續約打開,檢閱後按「建立」。網域註冊是最晚完成的一項,狀態會先顯示 Accepted。
建立 App Service 網域,可用的頂層網域與每年 11.99 美元
可買的網域結尾與價格(每年 11.99 美元)
搜尋網域名稱,顯示此網域無法使用與類似建議
名稱被註冊時,Azure 會建議類似名稱
網域檢閱頁,連絡資訊已遮蔽,隱私權保護啟用
檢閱頁:價格、法律條款、隱私權保護(教材中的個資已遮蔽)
部署中:DNS 區域已建立,網域 Accepted
部署中:DNS 區域與刪除鎖定先完成,網域註冊顯示 Accepted
最近資源:App Service 網域、DNS 區域、靜態 Web 應用程式
完成後的三個資源:網域(門牌)、DNS 區域(查號台)、網站(房子)
網域續約頁,自動續約開啟,到期日 2027 年 10 月 16 日
網域續約:自動續約開啟。網域是用租的,忘了續約網址會被收回

費用提醒:網域費每年約 11.99 美元,通常不能用 Azure 點數抵扣;Azure DNS 每個區域每月約 0.5 美元,區域內加幾筆紀錄都一樣。Static Web App 與 HTTPS 憑證免費。學生不需要自己買網域,可以由老師分配子網域。

不在 Azure 買網域也可以:向 Cloudflare(.com 以成本價販售,美元計價、開英文 Invoice)或台灣的註冊商(可買 .tw、開統一發票,學校報帳較方便)購買,再把名稱伺服器改成 Azure DNS 提供的 4 組位址。學校子網域則可請資訊組把整個子網域「委派」給你的 Azure DNS,一次設定後就能自己新增網址。

步驟 5:把子網域綁到網站

網域和網站都在 Azure 裡,所以 Azure 會自動新增 DNS 紀錄、驗證網域擁有權、核發 HTTPS 憑證,不需要手動設定 CNAME。

  1. 進入 Static Web App →「自訂網域」→「+ 新增」→「Azure DNS 上的自訂網域」。
  2. DNS 區域選 bangcoding.com,第三層網域填 pyclass,下方會顯示完整網域 pyclass.bangcoding.com。
  3. 按「新增」,停留在頁面上等候約 5 分鐘,出現「已成功將 DNS 記錄和自訂網域新增」後關閉。
  4. 清單中狀態變成「已驗證」,到期日欄位是 HTTPS 憑證的期限,Azure 會自動續期。
新增選單:Azure DNS 上的自訂網域、其他 DNS 上的自訂網域
網域在 Azure 選第一項;在外部買的選「其他 DNS 上的自訂網域」
新增自訂網域:DNS 區域 bangcoding.com、第三層網域 pyclass
DNS 區域 + 第三層網域 = 完整網域
正在將 DNS 記錄新增至 DNS 區域
處理中:Azure 正在新增 DNS 紀錄並驗證
自訂網域清單,pyclass.bangcoding.com 已驗證
完成:pyclass.bangcoding.com「已驗證」
以 pyclass.bangcoding.com 開啟的派課室首頁
用新網址打開派課室:HTTPS、旁白音檔、SQL 練習都正常

如果網域在外面買:選「其他 DNS 上的自訂網域」,Azure 會給你一筆 CNAME(子網域指向 xxx.azurestaticapps.net),你到註冊商的 DNS 設定頁手動新增,再回 Azure 按驗證。DNS 生效可能要幾分鐘到幾小時。學校網路有時會擋對外的 DNS 查詢,在校內用 nslookup 查不到紀錄不一定是設定錯誤。

常見問題與錯誤

Actions 失敗:Could not find either 'build' or 'build:azure'

儲存庫有 package.json 但其實不需要組建。在 workflow 加 skip_app_build: true(步驟 3)。

建立後才發現選了「標準」方案

Static Web App →「主控方案」可以改回免費。改之前先確認沒有用到標準方案才有的功能(例如超過 2 個自訂網域)。

在 GitHub 網頁改了 workflow,回到電腦 git push 被拒絕

網頁上的修改產生了新的 commit,本機沒有。先 git pull 再推送。

買網域時出現「此訂用帳戶不符合建立 App Service 網域的資格」

點數型、試用或部分經銷商訂閱不能在 Azure 買網域。改向外部註冊商購買,再用「其他 DNS 上的自訂網域」綁定。

自訂網域一直停在「驗證中」

網域在 Azure:確認 DNS 區域就是網域買來時自動建立的那一個。網域在外部:確認 CNAME 拼字正確、名稱伺服器已改好,並等待 DNS 生效。

本課檢核

帶走的觀念:程式碼只維護一份,可以部署到很多地方;每個平台只讀自己的設定檔。雲端操作最重要的習慣是:看清楚發行者、類型與方案再按建立,資源放進同一個資源群組,用完知道怎麼刪除。