問題發生
在將 Production 環境複製到 Staging 環境後,發現 Strapi CMS 無法上傳圖片到媒體庫,畫面只顯示 Internal Server Error。
Strapi 是一個開源的 Headless CMS(無頭內容管理系統),可以讓開發者快速建立 API,並提供管理後台來管理內容。在這個專案中,我們使用 Strapi 搭配 AWS S3 來儲存上傳的圖片和檔案。
追查過程
第一步:查看 Kubernetes Logs
由於 Strapi 部署在 EKS(Elastic Kubernetes Service,AWS 的託管 Kubernetes 服務)上,我透過 kubectl 指令查看 Pod 的日誌:
kubectl logs -f deployment/strapi-stg --tail=100
kubectl 是 Kubernetes 的命令列工具,用來與 Kubernetes 叢集互動。logs 指令可以查看容器的輸出日誌。
第二步:找到錯誤訊息
在日誌中發現關鍵錯誤:
error: The bucket does not allow ACLs
AccessControlListNotSupported: The bucket does not allow ACLs
這個錯誤訊息指出 S3 Bucket(AWS 的物件儲存服務中的儲存桶)不允許使用 ACL。
什麼是 ACL?
ACL = Access Control List(存取控制清單)
ACL 是 S3 的一種權限管理機制,用來決定誰可以讀取或寫入特定的檔案。當 Strapi 上傳圖片到 S3 時,預設會設定:
ACL: "public-read"
這表示「任何人都可以讀取這張圖片」,這樣前端網頁才能透過 URL 直接顯示圖片。
為什麼會失敗?
AWS 在 2023 年 4 月的重大變更
AWS 在 2023 年 4 月之後,新建立的 S3 Bucket 預設禁用 ACL。這是一個安全性的改進,AWS 建議改用 Bucket Policy 來統一管理權限。
新 Bucket 的預設設定:
- Object Ownership = Bucket owner enforced(禁用 ACL)
- Block all public access = On(封鎖所有公開存取)
為什麼 Production 正常,Staging 失敗?
| 環境 | Bucket 建立時間 | ACL 設定 |
|---|---|---|
| Production | 2025/10/3 | ✅ 有手動啟用 ACL |
| Staging | 2026/1/6 | ❌ 使用預設值(禁用) |
問題不在於 AWS 的預設值改變,而是 建立 Staging Bucket 時忘記調整 ACL 設定,而 Production 當初有手動啟用。
解決方案
步驟一:啟用 Object Ownership 的 ACL
- 進入 AWS S3 Console,選擇目標 Bucket
- 點擊 Permissions 分頁
- 找到 Object Ownership 區塊,點擊 Edit
- 選擇 ACLs enabled,然後選擇 Bucket owner preferred
- 勾選 I acknowledge that ACLs will be restored
- 點擊 Save changes
Object Ownership 決定了誰擁有上傳到 Bucket 的物件,以及是否可以使用 ACL:
- Bucket owner enforced:禁用 ACL,所有物件都屬於 Bucket 擁有者
- Bucket owner preferred:啟用 ACL,但 Bucket 擁有者會取得完整控制權
步驟二:調整 Block Public Access 設定
- 在同一個 Permissions 分頁
- 找到 Block public access 區塊,點擊 Edit
- 取消勾選前兩個 ACL 相關選項:
- ❌ Block public access to buckets and objects granted through new access control lists (ACLs)
- ❌ Block public access to buckets and objects granted through any access control lists (ACLs)
- 保留下面兩個 Bucket Policy 相關的勾選
- 點擊 Save changes
這樣 Strapi 上傳時設定的 ACL: "public-read" 才會生效,圖片才能被前端讀取。
預防措施:使用 IaC 管理雲端資源
這次問題的根本原因是手動建立 Bucket 時遺漏設定。如果使用 IaC(Infrastructure as Code,基礎設施即程式碼) 來管理,就能確保每次建立的資源設定一致。
什麼是 IaC?
IaC = Infrastructure as Code(基礎設施即程式碼)
傳統上,我們在 AWS Console 網頁上點選按鈕來建立 S3 Bucket、EC2 主機等雲端資源。IaC 則是把這些操作寫成程式碼檔案,執行程式碼就能自動建立資源。
這就像是:
- 手動方式:每次做菜都憑記憶,可能漏放調味料
- IaC 方式:照著食譜做,每次結果都一樣
常見的 IaC 工具:
- Terraform:最流行的跨平台 IaC 工具,可管理 AWS、GCP、Azure 等多種雲端
- AWS CloudFormation:AWS 原生的 IaC 服務,只能管理 AWS 資源
- Pulumi:支援 TypeScript、Python 等程式語言撰寫 IaC
什麼是 Terraform?
Terraform 是由 HashiCorp 公司開發的開源 IaC 工具。它使用自己的設定語言 HCL(HashiCorp Configuration Language) 來描述雲端資源。
Terraform 的運作流程:
- Write:撰寫
.tf設定檔,描述你想要的資源 - Plan:執行
terraform plan,預覽將會建立或修改的資源 - Apply:執行
terraform apply,實際建立資源
Terraform 範例
以下是用 Terraform 建立 S3 Bucket 並啟用 ACL 的範例:
# 建立 S3 Bucket
resource "aws_s3_bucket" "media_bucket" {
bucket = "my-app-media-stg"
}
# 設定 Object Ownership 為 BucketOwnerPreferred(啟用 ACL)
resource "aws_s3_bucket_ownership_controls" "media_bucket" {
bucket = aws_s3_bucket.media_bucket.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
# 設定 Public Access Block(允許 ACL 公開存取)
resource "aws_s3_bucket_public_access_block" "media_bucket" {
bucket = aws_s3_bucket.media_bucket.id
block_public_acls = false # 允許設定 public ACL
ignore_public_acls = false # 不忽略 public ACL
block_public_policy = true # 封鎖 public bucket policy
restrict_public_buckets = true # 限制 public bucket
}
使用 IaC 的好處:
| 手動建立 | 使用 IaC |
|---|---|
| 每次都要記得點哪些選項 | 程式碼定義好,執行就一致 |
| 容易遺漏設定 | 不會漏,設定都在程式碼裡 |
| 無法追蹤誰改了什麼 | 可以用 Git 版本控制 |
| 各環境可能設定不同 | 同一份程式碼,環境一致 |
結論
這次問題的關鍵學習:
- AWS S3 在 2023 年 4 月後預設禁用 ACL,這是安全性改進,但可能導致舊有程式碼上傳失敗
- 錯誤訊息
The bucket does not allow ACLs表示需要啟用 Object Ownership 的 ACL 設定 - 手動建立雲端資源容易遺漏設定,建議使用 IaC 工具來確保一致性
下次遇到類似的 S3 上傳問題,記得先檢查 Bucket 的 Object Ownership 和 Block Public Access 設定。
