iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Modern Web

前端來點 AWS 技能樹!系列 第 13 篇

Day13 - 把 Image 推上 ECR,讓 Next.js 在 AWS 上跑起來

  • 分享至 

  • xImage
  •  

昨天透過 Session Manager 連進 EC2,也成功跑出了 Hello from Docker!。今天就來把前幾天做的 Next.js Image 搬上去ㄅ!

ECR 負責存,EC2 負責跑

昨天我們從 Docker Hub 拉過 hello-world,而今天是把自己做的 image 放進 Amazon ECR(Elastic Container Registry)。它是 AWS 提供的放 Image 倉庫。
而今天會比較複雜一點,會再多個平台間進行切換:

  • 會現在 AWS Console 上建立 ECR repository、調整 EC2 role 與 Security Group
  • 再來會運用自己電腦的終端機進行 build Image、用 AWS CLI 登入 ECR、push image
  • 最後用瀏覽器透過 HTTP 連到 EC2 看結果

建立 ECR

  1. 登入 IAM 帳號進到 AWS Console,搜尋 ECR(記得地區要確認一下是否跟 EC2 的一樣)
    https://ithelp.ithome.com.tw/upload/images/20260927/20179793XuqXc2hnme.png
  2. 進到 ECR 頁面後點選右側的 **建立(Create repository)**按鈕
  3. 填寫欄位內容:
  • 儲存庫名稱(Repository name)填自己想填的
  • 映像標籤可變性(Image tag mutability)選 Mutable
  • 加密設定(Encryption)選 AES-256,如果想自行控制金鑰、管理權限或留下稽核紀錄的話可以改選 AWS KMS(Key Management Service)
    建立後會看到類似這樣的 URI:
    123456789012.dkr.ecr.ap-east-2.amazonaws.com/<repo 名稱>
    前半段主機名稱是 registry 的位址,後面 <repo 名稱> 是 repository 名稱,後面再加 :v1,才是今天要使用的 image 名稱與 tag。

建一份給 EC2 的 Image

接著來到自己電腦的終端機,我是 mac 。

  1. 確認 AWS CLI 和 Docker 都能使用
aws --version
docker version
aws sts get-caller-identity --region ap-east-2

這邊要檢查一下帳號是否和剛才建立 ECR 的 AWS 帳號相同
2. 接著進到 Day9 專案根目錄,也就是有 Dockerfile 的地方;若你的路徑不同,換成自己的:

cd ~/ironman-lab/day09/my-app
docker buildx build --platform linux/amd64 --load -t my-app:day13 .
  • Day9 是在自己的 Mac 上跑,不必特別指定平台。但今天的目標是建立的 x86_64 EC2(要注意自己是用哪個 CPU 架構),所以要 build linux/amd64。
  • mac m 系列晶片一般會建立 arm64 image,單純改 tag 不會把它轉成 amd64。
  • Docker Desktop 支援透過模擬建置其他架構的 image,但會比較慢一點。
  • 這邊指令的 --load,是把建置結果載入本機 Docker,讓 docker tag 和 docker push 能使用;my-app:day13 則是本機標籤。
  1. 完成後確認平台:
docker image inspect my-app:day13 --format '{{.Os}}/{{.Architecture}}'

https://ithelp.ithome.com.tw/upload/images/20260927/201797935HzfIcfaqR.png

在 Mac 登入 ECR,再把 image 推上去

我們把 image 做好了,但它還是在我們的電腦裡,現在要把它 push 到 ECR 上才能讓機器使用

  1. 在自己電腦的終端機上,先設定後面會重複使用的變數
ECR_REGION=ap-east-2
ECR_ACCOUNT_ID=$(aws sts get-caller-identity --region "$ECR_REGION" --query Account --output text)
ECR_REGISTRY="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com"
ECR_IMAGE="${ECR_REGISTRY}/<repo 名稱>:v1"
  • ECR_ACCOUNT_ID 從目前 CLI 身分取得帳號 ID
  • ECR_IMAGE 就是完整的 image 位址
  1. 接著登入 registry
aws ecr get-login-password --region "$ECR_REGION" \
| docker login --username AWS --password-stdin "$ECR_REGISTRY"

這裡分成兩段:

  • AWS CLI 用目前的 IAM 身分取得 ECR 登入密碼
  • 再經由 | 交給 Docker。--username AWS 的 AWS 是固定寫法,不用換成 IAM 使用者名稱。
  • 成功時會看到 Login Succeeded,這次登入是進到 registry,所以後面沒有 /my-app。
  1. 把本機 image 加上 ECR 位址的標籤,再推上去:
docker tag my-app:day13 "$ECR_IMAGE"
docker push "$ECR_IMAGE"
  • docker tag 只是替同一份 image 增加名稱,真正上傳的是 docker push。
  • 看到推送成功後,回 Console 的 my-app repository 重新整理,應該會找到 tag 為 v1 的 image。
    https://ithelp.ithome.com.tw/upload/images/20260927/20179793F9KcIuu5po.png

讓 EC2 有權限拉取 image

昨天的角色(role)只有 Systems Manager 所需權限,現在要加上 ECR 權限。

  1. 回到 AWS Console ,跟昨天一樣到 IAM 的 角色(role) 頁面
  2. 點擊昨天建的角色名稱進到該角色的詳細頁面
  3. 點擊許可政策區塊中增加許可按鈕(Add permissions)中連接政策(Attach policies)
    https://ithelp.ithome.com.tw/upload/images/20260927/20179793JIUXAgX0kq.jpg
  4. 搜尋:AmazonEC2ContainerRegistryPullOnly並打勾建立
  • AmazonEC2ContainerRegistryPullOnly 提供拉取 image 所需的權限,不包含一般 image push 所需的上傳權限
  • 這份 AWS managed policy 的資源範圍是 *,並沒有只限定 my-app。之後要限制到特定 repository,可以再改成自訂 policy。

目前我們就把工作分開了:

  • Mac 使用的 IAM 身分負責上傳
  • EC2 使用的 role 負責拉取。

切到 EC2 的 Session Manager

回到 EC2 頁面,用昨天的 Connect → Session Manager 連進去

  1. 在 EC2 終端機執行這段指令:
aws --version
sudo systemctl is-active docker
aws sts get-caller-identity --region ap-east-2

看到顯示:

aws-cli/2.33.15 Python/3.9.25 Linux/6.18.48-109.150.amzn2023.x86_64 source/x86_64.amzn.2023
active
{
    "UserId": "...:...",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/role 名稱/..."
}

就表示成功了!

  • Docker 預期回傳是 active;若尚未啟動用 sudo systemctl start docker 來啟動。
  • 確認 Account 和 ECR 所在的 AWS 帳號相同,且 Arn 包含 assumed-role/role 名稱/,表示 EC2 正在使用我們設定的 role
  • AWS CLI 會使用掛在 EC2 上的 role 取得短期憑證,因此不需要在主機執行 aws configure 輸入長期 access key。
  1. EC2 是另一個終端機,要重新設定變數:
ECR_REGION=ap-east-2
ECR_ACCOUNT_ID=$(aws sts get-caller-identity --region "$ECR_REGION" --query Account --output text)
ECR_REGISTRY="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com"
ECR_IMAGE="${ECR_REGISTRY}/<repo 名稱>:v1"
  1. 接著登入並拉下 image
aws ecr get-login-password --region "$ECR_REGION" \
| sudo docker login --username AWS --password-stdin "$ECR_REGISTRY"
sudo docker pull "$ECR_IMAGE"

我們一樣習慣在 Docker 指令都加 sudo,包含 docker login。Docker 的登入設定依使用者儲存;如果用一般使用者登入,卻用 sudo docker pull 拉取,可能會讀到不同的設定,出現 no basic auth credentials。

在 EC2 啟動 Next.js

  1. 在 EC2 終端機操作:
sudo docker run -d \
--name my-app \
--restart unless-stopped \
-p 80:3000 \
"$ECR_IMAGE"

前面用過 -d,表示讓容器在背景執行。今天多注意兩個設定:

  • --restart unless-stopped:設定容器重新啟動的策略。搭配昨天 Docker 的開機啟動設定,讓原本執行中的容器能在 Docker 重啟後恢復;如果自己手動停止容器,就需要手動啟動。
  • -p 80:3000:把 EC2 的 TCP 80 對應到 容器的 TCP 3000。對外連線用 80,Next.js 在容器裡仍使用 3000。
  1. 之前我們在 Dockerfile 已設定 HOSTNAME=0.0.0.0,讓 Next.js 接受容器網路介面的連線
    先確認容器狀態和日誌:
sudo docker ps --filter name=my-app
sudo docker logs --tail 50 my-app
  1. 等 Next.js 顯示已啟動,再從 EC2 自己 發出 HTTP 請求:
curl --fail --silent --show-error --output /dev/null --write-out '%{http_code}\n' http://127.0.0.1

回傳 200表示 Next.js 首頁有成功起來了!但它還不能證明外面的瀏覽器連得進來。
這裡的 127.0.0.1 指的是 EC2 本機,可以先確認容器和 port mapping 有正常工作。

開放 HTTP,從瀏覽器看成果

之前我們把 Security Group 的 inbound rules 留空,現在終於有網站可以開放了!

  1. 回 EC2 Console ,點擊 Instance 名稱,下方選擇安全性(Security)tab ,直接點畫面右下角"安全群組"欄位下的藍色連結
  2. 進到使用的安全群組頁面,在"傳入規則(inbound rules)"區塊中點擊"編輯傳入規則(Edit inbound rules)"
  3. 進到編輯傳入規則(Edit inbound rules)頁面後,點新增規則(Add rule):
  • 類型(Type)選 HTTP
  • 通訊協定(Protocol):TCP(自動填入)
  • 連接埠範圍(Port):80(自動填入)
  • 來源(Source):把「自訂」改成 「我的 IP(My IP)」
    https://ithelp.ithome.com.tw/upload/images/20260927/20179793sK2yixUqzD.png
  1. 按下"儲存規則"
  • IP 會填入目前電腦對外的公網 IPv4,通常是 /32
  • 這次先讓自己的網路能連進來。如果換 Wi-Fi、VPN 或改用手機行動網路,來源 IP 可能不同,需要更新規則。
  • 這次外部進入的是 80,所以 Security Group 開放 80 就好,container 的 3000 不用另外對外開放。
  • 前面的 ECR login、push、pull 使用的是對 AWS 服務的連線,也不需要為它們增加 EC2 inbound 規則。
  1. 回到 instance 詳細資訊,複製目前的 Public IPv4 address。在自己的瀏覽器輸入:
    http://你的-EC2-公網-IPv4
  • 要明確使用 http://,這台主機還沒設定 HTTPS。
  • 現在的 port 是 80,因此網址後面不用加 :3000
    看到跟之前一樣的 Next.js 網頁,今天就完成了!
    https://ithelp.ithome.com.tw/upload/images/20260927/20179793ww4PKbmpEQ.png
    這次頁面的 container 已經部署到 EC2 上,即使關掉 Mac 上的開發伺服器,也不影響 EC2 提供頁面了。

今天終於把 Next.js 搬上 EC2 了!也把之前幾篇文章終於接在一起了。
目前我們是透過公網 IP 和 HTTP 存取,也只開放自己的來源 IP。接下來,就來處理固定 IP、網域和 HTTPS,讓這個網站一步步變成可以正式分享的樣子ㄅ!


上一篇
Day12 - 用 Session Manager 連進 EC2,裝好 Docker
下一篇
Day14 - 搞懂 IP、子網路與路由:80 和 3000 port 差在哪?
系列文
前端來點 AWS 技能樹! 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言