終於來到最後一天了!這 30 天,我們把一個 Next.js 專案從 Docker 化開始,送上 EC2、接上 Domain 和 HTTPS、交給 GitHub Actions 自動部署,最後又換一條路,用 S3+CloudFront 讓它上線。今天來回顧這兩條路,再整理一份「前端要怎麼選」的判斷方法ㄅ!
| 階段 | 文章 | 做了什麼 |
|---|---|---|
| 起步 | Day1~Day5 | 認識雲服務、建立 AWS 帳號、IAM 和 AWS CLI |
| Docker | Day6~Day9 | 跑第一個 container,用多段式 Dockerfile 把 Next.js 打包成 Image |
| EC2 | Day10~Day13 | 開 EC2、用 Session Manager 連進去、把 Image 推上 ECR 跑起來 |
| 網路 | Day14~Day18 | IP、Port、VPC、路由表,以及固定 IP 的 Elastic IP |
| Domain 與 HTTPS | Day19~Day22 | DNS、Cloudflare 的 Record、TLS 憑證,用 Caddy 開 HTTPS |
| 自動化與維運 | Day23~Day26 | IaC 與 CI/CD 的觀念、OIDC、deploy.yml,以及上線之後要顧的事 |
| S3+CloudFront | Day27~Day29 | 靜態匯出、私有 bucket+OAC、ACM 憑證、Cache 規則和自動部署 |
兩條路放在一起,就是這 30 天做出來的全部:
同一份程式碼,push 到 main 一次,兩條路會各自 build、各自部署。差別在使用者連進來的時候:EC2 那條路有一台 Server 在跑 Next.js,S3 那條路只有 build 好的檔案。
| EC2 | S3+CloudFront | |
|---|---|---|
| 網站是什麼 | 一個一直在跑的 Next.js Server | 一堆 build 好的 HTML、CSS、JS |
| 能做的事 | Next.js 的功能都能用:API route、讀 cookie、每次請求才產生的頁面 | 提供 build 好的 HTML、CSS、JS,瀏覽器仍能呼叫另外部署的 API |
| 費用 | 開著就收錢,一個月大約 13 美元 | 照用量計算,小網站幾乎是 0 |
| 要顧的事 | 作業系統更新、Docker、Caddy、logs(Day26) | 幾乎沒有,S3 和 CloudFront 都是 AWS 在顧 |
| 離使用者多遠 | 一台主機在台北,使用者越遠越慢 | CloudFront 在世界各地都有據點 |
| 流量突然變大 | t3.micro 撐不住,要自己換機型或加機器 | CloudFront 自己扛 |
| 部署 | 大約 3 分鐘,換 container 那一兩秒會回 502(Day25) | 大約 3 分鐘,網站不會中斷(Day29) |
| 憑證 | Caddy 跟 Let's Encrypt 申請,Caddy 自己續期 | ACM 發的,ACM 自己續期 |
Day1 提過:這次的專案是 Next.js SSR,需要一台真的在跑 Node 的機器;如果專案是純靜態的 SPA,路線會不一樣。走完兩條路之後,可以把這句話變成一個問題來問自己:
上線之後,是把
npm run build產生的資料夾放上去就好,還是要有一個npm start一直開著?
如果是資料夾放上去就好,就走 S3+CloudFront;如果是要一直開著,就走 EC2。接下來分幾種情況來看。
| 專案 | 怎麼選 |
|---|---|
| Vite+React 或 Vue 的 SPA、Astro、VitePress、作品集、活動頁 | build 出來就是 HTML、CSS、JS,走 S3+CloudFront |
| Next.js、Nuxt 這類可以在 Server 上產生頁面的框架 | 要看用了哪些功能,Next.js 的話看下一段 |
output: 'export' build 一次最快的初步檢查,就是加上 output: 'export' 實際 build 一次,看看錯誤和警告。不過 build 成功不代表所有功能都正常,例如 rewrites、redirects、headers 不會隨靜態檔案一起生效,上線後還是要實際測試。Next.js 文件列出的不支援功能,換成前端常見的情境是這樣:
| 用到了 | 常見的情境 |
|---|---|
cookies()、會讀 request 的 Route Handler |
依照登入狀態顯示不同內容、用 API route 接表單 |
| Server Actions | 表單送出之後在 Server 上執行 |
| Proxy(Next.js 15 以前叫 Middleware) | 沒有登入就導去登入頁 |
| ISR | 每隔一段時間重新產生頁面 |
動態路由,但沒有 generateStaticParams |
文章頁、商品頁的 id 是使用者新增的,build 的時候不知道有哪些 |
next.config 裡的 rewrites、redirects、headers |
轉址、加 header |
next/image 預設的圖片壓縮 |
這個有解:Day27 用的 images.unoptimized |
一個都沒用到,就走 S3+CloudFront。有用到的話,先想想能不能換掉,例如資料改在瀏覽器裡抓;換不掉,就走 EC2。
不一定,要看前端怎麼接後端。用 Vite 做的 SPA,在瀏覽器裡直接呼叫後端的 API,前端就只是一包靜態檔案,可以放 S3。但如果在前端專案裡用 Next.js 的 API route 當中間層,例如把 API key 藏在 Server 上,或是由 Server 幫忙設定 HttpOnly 的 cookie,這一層就需要 Server。像我的 side project 就是這樣:後端是另一台 Server,但前端有登入、上傳這些要經過 API route 處理的功能,所以正式站還是只能走 EC2。
不一定要二選一。像是有些網站有登入和上傳功能,所以放在 EC2;另外把首頁用成像是展示用的,資料是寫死的,就放在 S3+CloudFront。正式站用原本的 Domain,展示版用另一個 Subdomain,各自部署,互不影響。
如果你的 SPA 有好幾個頁面,例如 /about,放上 S3 之後還要多設一個 custom error response:遇到 403 時回傳 /index.html,狀態碼改成 200。不然在 /about 按重新整理,S3 找不到 about 這個檔案,就會看到 403。設定的地方跟 Day28 的 404 頁面一樣,同一個錯誤碼只能設一種回應,SPA 就改成回傳 /index.html。
Day1 提到的 Vercel、Zeabur、GitHub Pages 這類部署平台,還是讓網站上線最快的方法。Day10 說選 EC2,是想親手把流程跑過一遍,看看平台幫我們做了什麼。現在回頭看,平台默默做掉的事,就是這 30 天的內容:build、HTTPS 憑證、CDN、push 之後自動部署。知道這些之後,遇到平台的限制,或是公司要求放在自己的 AWS 帳號時,就知道該從哪裡下手了。
練習完不打算繼續用的話,記得把資源收掉。原則是由外往內:先拿掉使用者連進來的入口(DNS),再刪後面的服務,最後才刪權限。
example.com 的 A Record、www 和 static 的 CNAME。ACM 驗證用的那筆 CNAME 先留著。us-east-1,刪掉那張憑證,再回 Cloudflare 刪掉驗證用的 CNAME。my-app 這個 repo,裡面的 Image 會一起刪除。gha-deploy-role、ec2-app-role。GitHub 的 OIDC 身分提供者如果沒有別的 repo 在用,也可以一起刪掉。順序為什麼重要?
Domain 是一年一年續約的,如果不打算繼續用,記得到 Cloudflare 關掉自動續約。
--platform linux/amd64(Day13).dev、.app 這類網域,瀏覽器一律要求 HTTPS(Day20)sub 換了新格式,照舊教學寫的 trust policy 會被擋下來(Day24)docker image prune 要加 -a 才清得掉舊版本(Day25)us-east-1(Day28)這些坑大多是「照著做卻跑不起來」的那種,錯誤訊息也不一定直接告訴你原因。Day1 說過,現在可以請 AI 帶著部署,但觀念還是要懂,不然 AI 不小心失控的時候才來得及阻止它!
謝謝每一位看到這裡的人!這個系列從帳號都還沒開,一路走到兩條路都能 push 就自動部署。也希望這系列可以對同樣想替自己的專案點一下 AWS 技能樹的前端工程師有一點幫助。我們下次見ㄅ!
資安小提醒:收掉資源的時候,先刪 DNS Record,再釋放 Elastic IP 或刪除 CloudFront,避免 Domain 指向別人的資源。用完的 access key、IAM Role 和 OIDC 身分提供者也記得一起刪掉。