Docker Run Port Binding
在之前這篇文章最後: Docker Certified Associate(DCA)認證考試學習-Docker Context
有跑起來一個nginx容器並且開放了一個端口8080,並且用瀏覽器打開網址就能在畫面上看到nginx的歡迎頁面
docker run --name nginx28 -p 8080:80 nginx
那麼背後的原理是什麼呢?
首先先看一下 -p
參數是什麼意思
--publish , -p Publish a container’s port(s) to the host
--publish-all , -P Publish all exposed ports to random ports
簡單來說就是把container內部的port與host的port做連結
首先host代表的是我們的實體機器也可以是一台VM
以Play with Docker為例,當我們按下ADD NEW INSTANCE按鈕時,下方會跑出一台新的機器,我們可以把這台機器當成HOST,這台HOST有自己完整的作業系統
同時在HOST內運行起多個container時,每個container內部也有一套完整的作業系統
所以當需求訪問其中一個container時,就需要有一個轉送的機制將HOST的port的流量傳送到container內部的port
現在在看一下以下範例
這邊使用 docker ps -a
列出所以運行中的image,其中有一個欄位叫做 PORTS
值為 0.0.0.0:8080->80/tcp
[node1] (local) root@192.168.0.8 ~
$ docker run -dit -p 8080:80 nginx
[node1] (local) root@192.168.0.8 ~
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a24ee600104 nginx "/docker-entrypoint.…" 13 seconds ago Up 12 seconds 0.0.0.0:8080->80/tcp happy_archimedes
那麼在試試看 -p
參數帶入其他的值會發生什麼事
[node1] (local) root@192.168.0.8 ~
$ docker run -dit --name nginx28 -p 8080:81 nginx
b538e3d3eec1814aa7f0b71359a28fd1263f15968b77aaf0c108be1ff47af574
[node1] (local) root@192.168.0.8 ~
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b538e3d3eec1 nginx "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 80/tcp, 0.0.0.0:8080->81/tcp clever_einstein
那麼在試試看如果不帶 -p
參數會發生什麼事
[node1] (local) root@192.168.0.8 ~
$ docker run -dit --name nginx28 nginx
[node1] (local) root@192.168.0.8 ~
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b5311d14d1ae nginx "/docker-entrypoint.…" 13 seconds ago Up 12 seconds 80/tcp quirky_nightingale
該怎麼解讀這些結果呢?
首先第一個測試有帶 -p 8080:80
參數,結果可以看到PORTS欄位有一個值 0.0.0.0:8080->80/tcp
PORTS欄位的值中間有一個箭頭->
,代表這個操作是有方向行的,也就是從 A
往 B
方向傳送
也就是說Docker會監聽Host的8080 port,並且把流量轉送到container的tcp 80 port
第二個測試帶入 -p 8080:80
參數,PORTS欄位有兩個值 80/tcp, 0.0.0.0:8080->81/tcp
第三個測試沒有帶入 -p
參數,PORTS欄位只有一個值 80/tcp
那麼為什麼沒有帶 -p
參數的結果也會輸出一個tcp 80 port呢?
而且為什麼是tcp 80 port可以選擇其它的值嘛?
接下來就要到Dockerfile中找答案了
Docker EXPOSE
首先先看一下Docker EXPOSE的文檔
只要開頭使用EXPOSE並且輸入指定的port即可,也可以只定TCP或者UDP,預設情況下為TCP
EXPOSE <port> [<port>/<protocol>...]
例如以下範例,會將container內部的tcp與udp 80 port對外開放
EXPOSE 80/tcp
EXPOSE 80/udp
學會了怎麼將container的port對外開放之後
可以到Nginx的Github看看Dockerfile是怎麼寫的
在這一行可以看到EXPOSE的語法,所以這就是為什麼將Nginx運行起來時Container總會有個80 port的原因
這邊需要注意關鍵是container內部的程式
以nginx為例,有使用過nginx的人一定知道nginx關鍵的設定檔nginx.conf
這個設定檔告知nginx將會監聽80 port並且顯示預設的首頁index.html
所以在將nginx包裝成image的過程也會有同樣的目錄結構
並且在image的最後使用Expose對外暴露一個80 port
來將host的流量轉發到container內部,最後讓nginx監聽到
根據上一段的說明,我們可以來製作一個專屬客製化的nginx image 首先建立以下目錄及檔案
mkdir customnginx && cd $_
touch Dockerfile
touch 8081.conf
touch custom.html
修改8081.conf內容,此規則會監聽8081 port並轉到custom.html頁面
#8081.conf
server {
listen 8081;
listen [::]:8081;
server_name localhost;
location / {
root /usr/share/nginx/html;
index custom.html custom.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
修改custom.html內容,會顯示出客製化html
#custom.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx! From Custom Port</title>
</head>
<body>
<h1>Welcome to nginx! From Custom Port</h1>
</body>
</html>
我們這邊底層使用nginx,並將兩個檔案放到各自的資料夾內
要注意這邊的目錄可能會根據不同廠商發布的nginx image而有所不同
並且最後對將container的8081 port暴露出去
#Dockerfile
FROM nginx
COPY 8081.conf /etc/nginx/conf.d/
COPY custom.html /usr/share/nginx/html/
EXPOSE 8081
在這邊看到原本的80 port也還存在,並不會被我們自訂的port所取代,並且還額外綁定了一個8081 port
這兩個port也都綁定到HOST環境底下了,所以照理來說我們現在訪問機器的地址就能看到客制頁面了
docker build -t customnginx .
docker run -dit -p 80:80 -p 8081:8081 customnginx
[node1] (local) root@192.168.0.28 ~/customnginx
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
50f379fcb0e1 customnginx "/docker-entrypoint.…" 9 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:8081->8081/tcp vibrant_goodall
Summary
今天了解到Dockerfile的EXPOSE與Docker Run的 -p
參數,兩個是要互相搭配使用的缺一不可
並且最後還將nginx官方的image作為底層客制出我們專用的Nginx版本,能顯示出我們自訂的頁面
當我將image打包完成後,你只需要下載我的image並且只要在有Docker的環境就可以馬上運行我的成果
而且也不需要知道我到底是怎麼實做的,我只要告訴你我的image要使用80和8081 port
你只要執行docker run命令即可,這就是Docker最方便的地方
有興趣的朋友可以直接運行以下命令,就以運行今天的image了
docker run -dit -p 80:80 -p 8081:8081 allengaodev/customnginx:latest