National Chengchi University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

政治大學 AQI 54 25°C PM2.5 11
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 09

埠號、容器網路與 Entrypoint

-p 對應埠號、讓容器互相連線、ENTRYPOINT 與 CMD
Interaction 25 minute(s)

Learning Objectives

  • 用 -p 主機埠號:容器埠號 對應埠號,說明沒有對應時為什麼從外面連不到容器
  • 用 -P、綁定特定網路介面與 docker container port 管理埠號
  • 說明 Docker 預設的 bridge 網路,並用 docker network create 建立自訂網路,讓容器用名字找到彼此
  • 比較 ENTRYPOINT 與 CMD,說明執行時加上的參數如何與兩者搭配

容器有自己的網路

每個容器都有自己的網路環境:自己的 IP 位址、自己的埠號。容器裡的 nginx 聽的是容器的 80 埠,不是主機的 80 埠。

瀏覽器 ──▶ 主機 localhost:8080 ──-p 8080:80──▶ 容器 web 的 80 埠(nginx)
                                              │
                                              └── 同一個自訂網路 ──▶ 容器 db 的 3306 埠(MySQL)

這一課處理兩個方向的連線:

方向 做法
外面(主機、瀏覽器、其他電腦)連進容器 用 -p 把主機的埠號對應到容器的埠號
容器連到另一個容器 把它們放進同一個自訂網路,用容器名稱連線

最後再補上 Dockerfile 的 ENTRYPOINT:它和 CMD 一起決定容器啟動時執行什麼。

-p:把主機的埠號對應到容器

docker container run -d -p 7777:80 --name static_web ncuedu/static_web

-p 主機埠號:容器埠號,左邊是主機、右邊是容器。沒有加 -p 時,容器的埠號只在 Docker 內部的網路裡, 主機的瀏覽器連 localhost 找不到它,Dockerfile 裡寫了 EXPOSE 80 也一樣(EXPOSE 只是文件)。

寫法 意思
-p 7777:80 主機所有網路介面的 7777 埠對應到容器的 80 埠
-p 127.0.0.1:7777:80 只綁在主機的 127.0.0.1:只有主機自己連得到,其他電腦連不到
-p 8080:80 -p 8443:443 一次對應多個埠號
-P 把映像檔 EXPOSE 列出的埠號,全部對應到主機上隨機的埠號

指定埠號的好處是明確、每次都一樣;-P 寫起來簡單,但每次啟動的埠號可能不同,要查了才知道:

指令 作用 舊寫法
docker container ls PORTS 欄顯示對應,例如 0.0.0.0:7777->80/tcp docker ps
docker container port static_web 只看這個容器 docker port static_web
docker container port static_web 80 只問容器的 80 埠對到主機哪裡

主機的同一個埠號只能給一個容器用。如果 8080 已經被占用,會出現 Bind for 0.0.0.0:8080 failed: port is already allocated,換一個主機埠號即可(容器那邊仍然是 80)。

Docker 的網路:預設的 bridge 與自訂網路

安裝 Docker 時會建立一張虛擬網路卡 docker0,它像一台虛擬的交換器(bridge),把容器和主機連在一起:

  • 使用私有 IP 位址,預設子網路是 172.17.0.0/16,閘道是 172.17.0.1(在主機上用 ip addr show docker0 看得到)
  • 沒有特別指定網路的容器都接在這裡,依序拿到 172.17.0.2、172.17.0.3……
  • 如果 172.17 已經被其他網路使用,Docker 會改用 172.16~172.31 之間的其他子網路
docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
1f0c3a2b9d7e   bridge    bridge    local
6a1d0e5c3b82   host      host      local
c2e9f7a14b05   none      null      local

在預設的 bridge 上,容器之間可以用 IP 互連(投影片的 MySQL 範例就是先用 inspect 查出資料庫容器的 IP 172.17.0.2,再用 mysql -h172.17.0.2 連線)。 問題是:IP 每次啟動可能不同,而且預設的 bridge 不能用容器名稱連線。

解法是建立自訂網路。接在同一個自訂網路上的容器,Docker 內建的 DNS 會讓它們用名字找到彼此:

docker network create mynet
docker container run -d --name db --network mynet -e MYSQL_ROOT_PASSWORD=請換成你的密碼 mysql:8.4
docker container run -d --name web --network mynet -p 8080:80 nginx

web 容器裡的程式連資料庫時,主機名稱直接寫 db 就好。也可以開一個暫時的 MySQL 用戶端容器,接上同一個網路連進資料庫:

docker container run -it --rm --network mynet mysql:8.4 mysql -hdb -uroot -p
指令 作用
docker network create mynet 建立自訂網路
docker network ls 列出網路
docker network inspect mynet 看子網路與接在上面的容器
docker network connect mynet 容器 把已經在執行的容器接上這個網路
docker network rm mynet 刪除網路(上面不能還有容器)

注意:db 只接在 mynet、沒有加 -p,所以主機外面連不到資料庫,只有同一個網路裡的 web 連得到。資料庫通常就該這樣設定。

互動練習:埠號與容器網路

依右下角的四個任務操作:先啟動沒有加 -p 的 web_test,用瀏覽器開 http://localhost:80,看看連不連得到;接著 docker network create mynet,在 mynet 上啟動資料庫 db;再用 --network mynet -p 8080:80 啟動 web,從瀏覽器開 http://localhost:8080(也可以用 docker container port web 看對應);最後在 web 裡用 getent hosts db 以名字查到 db 的 IP。右上方會顯示網路與瀏覽器的狀態。

在真的環境裡測試名字連線

模擬器裡用的 getent hosts db 在真的 nginx 容器裡也能用,它會請容器查詢 db 這個名字的 IP。 nginx 映像檔為了精簡沒有安裝 ping;想用 ping 測試連線,可以另外開一個暫時的容器:

docker container exec web getent hosts db
docker container run --rm --network mynet alpine ping -c 1 db

第二行開一個 alpine 容器接上 mynet,ping db 一次,結束就自動刪除(--rm)。

ENTRYPOINT 與 CMD

第 07 課的 CMD 是「預設要執行的程式」,執行時在映像檔名稱後面加任何東西,都會整個取代 CMD。 ENTRYPOINT 則是「這個容器就是這支程式」:執行時加上的東西會接在 ENTRYPOINT 後面,當成它的參數。

ENTRYPOINT ["/bin/ls"]
docker container run myls -l

容器執行的是 /bin/ls -l,而不是單純的 -l。這讓容器用起來像一個可執行檔(run as an executable)。

兩者搭配的慣用法:ENTRYPOINT 放固定的程式,CMD 放預設參數。

ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

相當於執行 nginx -g "daemon off;";執行時另外給參數,就只取代 CMD 的部分。

Dockerfile docker container run 映像檔 docker container run 映像檔 參數
只有 CMD ["python", "app.py"] python app.py 參數(整個取代 CMD)
只有 ENTRYPOINT ["python", "app.py"] python app.py python app.py 參數
ENTRYPOINT ["python", "app.py"] + CMD ["--port", "5000"] python app.py --port 5000 python app.py 參數(取代 CMD 部分)
  • 要換掉 ENTRYPOINT 本身,執行時用 --entrypoint,例如 docker container run -it --entrypoint /bin/bash 映像檔,常用來進容器除錯。
  • ENTRYPOINT 也有 exec 形式 ["程式", "參數"](建議)與 shell 形式 ENTRYPOINT 程式 參數。shell 形式會忽略 CMD 與執行時的參數,所以不建議。

互動練習:ENTRYPOINT 與 CMD 的搭配

上方可以切換兩份 Dockerfile:「只用 CMD」與「ENTRYPOINT+CMD」,兩份都是執行 Python 程式 app.py、預設用 5000 埠。每一份都用右邊三種方式執行一次:不加參數、加上 --port 8000、加上 bash,比較終端機裡「容器實際執行」的指令。想一想:哪一種寫法能讓使用者只改埠號?哪一種寫法下,bash 會取代你的程式?

下一步:用 Docker Compose 一次管理多個容器

web 加 db 的組合,每次都要打好幾行長長的指令。Docker Compose 讓你把這些設定寫在一個 YAML 檔裡:

# compose.yaml
services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
  db:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: 請換成你的密碼
    volumes:
      - db-data:/var/lib/mysql
volumes:
  db-data:

在這個檔案所在的目錄執行 docker compose up -d 就會全部啟動,docker compose down 全部停止並移除容器(volume 保留)。 Compose 會自動建立一個自訂網路,所以 web 一樣可以用 db 這個名字連到資料庫;volumes 則是第 06 課的 named volume。 這門課先到這裡,Compose 是你學完這九課之後很自然的下一步。

密碼直接寫在檔案裡只適合練習;正式使用時應該放在不進版本控制的環境變數檔或秘密管理機制裡。

想一想:埠號與網路

1.

docker container run -d --name web_test nginx(沒有 -p)啟動後,主機的瀏覽器開 http://localhost:80 會?

2.

-p 127.0.0.1:7777:80 和 -p 7777:80 有什麼差別?

3.

web 與 db 兩個容器要用名字互相連線,應該怎麼做?

第 1 次作答

想一想:ENTRYPOINT 與 CMD

1.

Dockerfile 寫了 ENTRYPOINT ["/bin/ls"],執行 docker container run myls -l,容器實際執行的是?

2.

Dockerfile 是 ENTRYPOINT ["python", "/app/app.py"] 加 CMD ["--port", "5000"],執行 docker container run myapp --port 8000 會?

第 1 次作答

重點精華回顧

  • -p 主機埠號:容器埠號 讓外面連得進容器;沒有對應時從主機連不到,EXPOSE 只是文件。
  • -p 127.0.0.1:7777:80 只綁本機,-P 對應到隨機埠號;用 docker container ls 或 docker container port 查對應;主機的同一個埠號只能給一個容器。
  • 預設的 bridge 網路(docker0,172.17.0.0/16)只能用 IP 互連;docker network create 建立自訂網路後,容器可以用名字找到彼此。
  • 資料庫這類只給其他容器用的服務,接在自訂網路上、不加 -p,外面就連不到。
  • CMD 是預設指令,執行時的參數會整個取代它;ENTRYPOINT 是固定的程式,參數接在後面;常見搭配是 ENTRYPOINT 放程式、CMD 放預設參數,要換 ENTRYPOINT 用 --entrypoint。
  • 多個容器的組合可以寫成 Compose 的 YAML 檔,用 docker compose up -d 一次啟動。
這一課完成了!
看我的學習進度
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。