iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
Modern Web

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

Day15 - 搞懂 Port:80 和 3000 是怎麼接起來的?

  • 分享至 

  • xImage
  •  

昨天講了如何分清楚 IP 位址,也知道上次部署完開網站時,用的是 EC2 的公網 IP。
不過,前面在本機開 Next.js,網址明明是 http://localhost:3000,到了 Day13,卻只要輸入 http://EC2-公網-IP 就能看到頁面。
後面的 :3000 跑去哪了?今天就來拆開看ㄅ!

Port:找到主機後,要連哪個服務?

什麼是 Port?

一台主機可以同時提供網站、郵件等多個網路服務。而資料到主機後,作業系統還是需要分辨說:這些資料要交給哪個程式處理?
這時就會用到 port(通訊埠,也常翻成連接埠)。可以先把它理解成程式收送網路資料的邏輯入口,由作業系統管理,並以數字識別。
以銀行為例:IP 位址可以想成這棟銀行大樓的地址,port 就像裡面不同服務櫃檯的編號。
以這次部署的 Next.js 網站來說,伺服器程式會在指定的 IP 位址與 port 上等待連線,這個動作叫做 監聽(listen)。所以光知道入口編號還不夠,對應的程式也要有啟動並等待連線,才能提供服務。
看網址時,可以先這樣拆:
https://ithelp.ithome.com.tw/upload/images/20260929/20179793o5JxCfsxXs.png

先認識 TCP 和 UDP

昨天介紹的 IP,主要處理「封包要送到哪裡」。至於程式之間如何傳送與接收資料,就會用到 TCP、UDP 這類通訊協定。

  • TCP(傳輸控制通訊協定):先建立連線,確認對方有沒有收到資料。發現資料遺失時會嘗試補傳,並把收到的資料按順序交給程式。
  • UDP(使用者資料報通訊協定):不用先建立像 TCP 那樣的連線,直接送出資料。它本身不確認是否送達,也不會自動補傳或整理順序。

假設要傳送的資料分成三段:第一段、第二段、第三段。傳送途中,第二段資料遺失,接收端只收到第一段與第三段。這時 TCP 和 UDP 會怎麼處理?

過程 TCP UDP
傳送前 先建立連線 不用先建立像 TCP 那樣的連線
第二段資料遺失時 嘗試補傳第二段資料 UDP 本身不會自動補傳
已收到的第三段資料 先暫存,等第二段補齊後再按順序交給程式 可以交給程式,不會等待第二段
程式最後取得的資料 補傳成功後,依序取得完整資料 本例只取得第一段與第三段,缺少第二段

這是簡化的傳送情境;UDP 範例假設應用程式沒有另外設計補傳機制。
兩者都會搭配 IP 傳送資料,也都使用 port 找到對應的程式。這次部署的 Next.js 網站使用的是 TCP,所以接下來的設定會看到「TCP 80」和「TCP 3000」。

為什麼設定裡會寫「TCP 80」?

TCP 和 UDP 各自有一套 port 編號。 所以同一個 IP 位址上的 TCP 80 和 UDP 80,代表不同的通訊端點;不能只看到數字相同,就當成同一個入口。
因為這次我們部署的網站是使用 TCP,Docker 的對應與 Security Group 的規則,也都要對上 TCP。看到設定裡的 TCP / 80,可以拆成「使用 TCP,目的 port 是 80」。
所以昨天介紹的封包:來源與目標的 IP 位址寫在 IP 標頭裡,來源與目標的 port 則寫在 TCP 或 UDP 標頭裡。 IP 負責依照位址轉送封包,TCP/UDP 則透過 port,將收到的資料交給對應的程式。

為什麼網址有時候不用寫 port?

因為通訊協定有預設的 port。像是在使用 http://的情況下,又沒有另外指定 port 時,預設會使用 80 的 port。

因此,下面兩個網址都是指向同一個入口:

http://203.0.113.10
http://203.0.113.10:80

HTTPS 的預設 port 則是 443,後面設定 HTTPS 時會再遇到。這些預設值讓我們平常輸入網址時,不必每次把 port 寫出來。
如果網站對外提供 HTTP 的 port 不是 80,就要在網址中指定這個對外 port,例如 http://EC2-公網-IP:8080。

那 Next.js 為什麼用 3000?

3000 是 Next.js 的預設監聽 port,next dev 和 next start 預設都會使用它,這個 port 也是前端開發網站預覽時常用的。
而我們這次在做的 image 使用 standalone 的 server.js,Dockerfile 裡也明確寫了:

ENV PORT=3000

這會讓 Next.js 的 standalone server 會讀取這個環境變數,決定啟動時使用哪個 port。
所以程式一樣可以在 3000 提供給 HTTP ,也可以使用在正式模式執行。使用哪個 port,和程式是開發模式還是正式模式,是兩件分開的事。

-p 80:3000:把主機入口接到容器

我們在啟動 container 時,有這段:
https://ithelp.ithome.com.tw/upload/images/20260929/20179793PbMBwDsXbM.png
這是 Docker 的 -p 用來發布 container 的 port,寫法的順序是 主機 port:Container port。如果沒有另外指定協定時,預設是 TCP。
因此 -p 80:3000 的意思是:把送到 EC2 主機 TCP 80 的連線,轉送到 容器的 TCP 3000。
因為主機和 container 有各自的網路環境,兩邊的 port 可以不同。而 Next.js 在 container 裡仍然監聽 3000,外面的瀏覽器是連 EC2 的 80。
https://ithelp.ithome.com.tw/upload/images/20260929/20179793n6FIj1afI4.png
這就是網址後面不用加 :3000 的原因!瀏覽器連的是主機的入口,再由 Docker 接到 container 裡的網站。
PS. 假設改成 -p 8080:3000,而且外部連線規則也配合放行 8080,網址就要寫成 http://EC2-公網-IP:8080。Container 裡的 Next.js 仍然使用 3000。

EXPOSE 和 Security Group 又在做什麼?

我們在 Dockerfile 寫了 EXPOSE 3000,啟動時又用了 -p 80:3000,AWS 還設定了 HTTP 80,看起來好像一直在重複「開 port」。
但把它們放在一起,就比較好分清楚:

設定 負責的事情
ENV PORT=3000 讓這次的 Next.js server 啟動時使用 3000
EXPOSE 3000 宣告 image 預期使用的 container port
-p 80:3000 建立主機 80 到 container 3000 的對應
Security Group 的 TCP 80/My IP 允許指定來源連入 EC2 的 TCP 80

所以 EXPOSE 本身不會發布 port,也不會啟動伺服器。 可以把它看成 image 留給使用者的說明;而真正建立主機與容器之間對應的是 -p。
Security Group 則依來源、協定、port 等條件允許流量。我們設定的 My IP 限制了可以連進來的來源;而外部連線要的目標的 port 是 主機的 80,所以這次放行 TCP 80 就好,不需要因為 container 使用 3000,再額外放行主機的 3000。
同樣地,只把 Security Group 改成允許 3000,也不會自動產生主機 3000 到 container 的對應。監聽、Docker 對應與網路規則,這樣都要接得起來了。

對照一下原本的設定

如果 EC2 和 my-app container 有正在執行的話,可以在 EC2 的 Session Manager 終端機輸入:

sudo docker port my-app

這個指令會列出 container 的 port 對應。用我們之前設定與 Docker 預設綁定位址時,IPv4 那行預期像這樣:

3000/tcp -> 0.0.0.0:80

這裡是把「container 的 3000」列在左邊,右邊顯示它發布到「主機的 80」。0.0.0.0 表示綁定主機所有 IPv4 介面的位址;從瀏覽器連線時,仍然使用 EC2 的公網 IP。
看到這個對應就代表 Docker 有這項設定;但網站是否正常回應,還是要搭配服務狀態與實際連線確認。

今天先把 80 和 3000 接起來。下一篇再回到開 EC2 時看到的 VPC 與 Subnet,看看這台主機究竟被放在哪個網路裡。

參考資料


上一篇
Day14 - 搞懂 IP、子網路與路由:80 和 3000 port 差在哪?
下一篇
Day16 - VPC 與 Subnet:EC2 到底放在哪個網路裡?
系列文
前端來點 AWS 技能樹! 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言