今天將會使用 Docker 啟動 HTTP server,並讓瀏覽器能看得到 HTTP server 所提供的 hello world。
常見的 HTTP server 主要有兩種:Nginx 與 Apache,今天會以 Apache HTTP server(以下簡稱 Apache)為例。
首先,先使用 docker run
跑 Apache container。
# Apache 的 image 名稱叫 httpd
docker run --name web httpd
# 停止 Apache 後,移除已停止的 container
docker ps -a
docker rm web
Apache 執行時,會停在前景等待收 request。若要中止它,可以按 Ctrl + C
快捷鍵強制中離。從訊息可以觀察到,Ctrl + C
跟 docker stop
一樣是發出 SIGTERM
信號給 process。狀態已經停止了,但 container 還存在,如果不需要的話得手動移除。
通常練習或測試的 container 都不需要保留,每次手動移除也很麻煩,這時可以使用參數 --rm
,範例如下:
# 加帶 --rm 參數
docker run --rm --name web httpd
# 檢查是否移除
docker ps -a
--rm
選項的功能是,當 container 的狀態變成 Exited
時,會自動把這個 container 移除,在練習或測試的時候非常方便。
前景執行 Apache 的時候,按下昨天提到的 Ctrl + P
與 Ctrl + Q
,會發現一點用都沒有。但像今天是啟動 HTTP server,通常會希望它是背景執行,這時可以使用另一個參數 -d
,範例如下:
# 執行完會馬上回到 host 上
docker run -d httpd
# 觀察 container 是否在運作中
docker ps
執行完 docker run
會回 CONTAINER ID
並回到原本的命令提示字元。這時再觀察 docker ps
會看到對應的 container 是運作中的狀態。
在 Hello Docker 說明 docker rm
的時候有提到,container 的狀態不是 Up
才能移除。除了先使用 docker stop
再使用 docker rm
外,也可以使用 docker rm
的 -f
參數強制移除 container:
# 執行完會馬上回到 host 上
docker run -d --name web httpd
# 確認正在執行中
docker ps
# 因 container 還在運作中,所以直接 rm 會出現錯誤訊息
docker rm web
# 注意 rm -f 是發出與 docker kill 相同的 SIGKILL 信號,而不是 docker stop 的 SIGTERM
docker rm -f web
加 --rm
選項可以讓它結束後自動移除,但得先用 docker stop
停止 container 才會觸發移除,那倒不如使用 docker rm -f
指令還比較直接一點。
雖然 Docker 並沒有特別禁止,但筆者個人習慣是不會同時啟用 --rm
和 -d
這兩個選項;另外還有一個原因是,在 Docker v1.13.0 版以前,是不能同時啟用 --rm
與 -d
的,詳細可參考 Docker issue。
我們先把 container 都移除,然後背景執行一個 Apache container。
# 背景啟動 Apache
docker run -d httpd
# 確認 PORTS
docker ps
# curl 試試 HTTP 服務,也可以用瀏覽器測試
curl http://localhost/
docker ps
裡面有個欄位是 PORTS
,上面寫著 80/tcp
,看起來很像是可以透過本地的 80 port 來存取 HTTP server。但實際上用 curl 會回應連線被拒絕,這代表從 host 連到 container 的方法有問題。
說明解法前,大家可以先試看看下面的指令:run 3 個 Apache container:
docker run -d httpd
docker run -d httpd
docker run -d httpd
# 查看 container
docker ps
docker ps
可以觀察到,所有 port 都是 80/tcp
,但奇妙的是,全部 container 都沒有遇到 port 衝突!?從這個實驗結果可以了解 container 具有隔離特性,也就是每個 container 都是獨立的個體,它們各自有屬於自己的 80 port 可以用,因此才不會相衝。
今天的目標是要從外界存取 container,因此要做點手腳才行--正是標題提到的 port forwarding。Docker 使用 -p
選項設定 port forwarding,範例如下:
# 加上 -p 參數
docker run -d -p 8080:80 httpd
# 確認 port 有被開出來了
curl http://localhost:8080/
-p
後面參數 8080:80
的意思代表:當連線到 host 的 8080 port 會轉接到 container 的 80 port。以上面的指令為例,只要輸入 http://localhost:8080/
即可接到 container 所啟動的 HTTP 服務。
下面是簡單示意圖:
Container 啟動完後,8080 是被綁定在 host 上的。只要有另一個服務綁 8080 在 host 上的話,就會出現錯誤訊息 port is already allocated.
:
docker run -d -p 8080:80 httpd
docker run -d -p 8080:80 httpd
docker run
補充說明其他參數:
--rm
當 container 主程序一結束時,立刻移除 container-d|--detach
背景執行 container-p|--publish
把 container 的 port 公開到 host 上,格式為 [[[IP:]HOST_PORT:]CONTAINER_PORT]
IP:HOST_PORT
綁定到 CONTAINER_PORT
;如果沒給 IP,則 IP 會代 0.0.0.0;如果連 HOST_PORT 也沒給,則會使用 0.0.0.0 加上隨機選一個 port,如 0.0.0.0:32768
。docker rm
補充說明其他參數:
-f|--force
如果是執行中的 container,會強制移除(使用 SIGKILL)Container 具備了隔離機制,因此有辦法把它當作「輕量的 VM 」使用。
今天說明的 port forwarding,可以應用在任何有開 port 提供服務的 server,如:MySQL、Redis、Memcached 等,有興趣可以參考官網範例自行試試。
docker run
的 --rm
參數與 -p
參數謝謝分享
不過我發現最後一步curl http://localhost:8080
,
會出現錯誤 : curl:(7)Failed to connect to localhost port 8080
因為我的環境是windows的,
必須要先查出ip,docker-machine ip default
(結果顯示是192.168.99.100)
然後要將localhost換掉,變成curl http://192.168.99.100:8080
才能成功。