為應付龐大流量時後端可能不是只有一個伺服器,Nginx作為中間代理,能解析用戶端來的請求的路徑,做派發到特定後端伺服器處理,當中也可做些負載平衡的設定,讓流量分散到各伺服器,防止只有單一伺服器繁忙的狀況
測試環境準備
首先來寫一個簡單的python flask server
資料夾結構:
docker-compose.yaml
nginx/
├─ log/
├─ html/
│ ├─ index.html
├─ default.conf
server/
├─ app.py
├─ Dockerfile
├─ requirements.txt
Dockerfile:
from python:3.8
ADD . /app
WORKDIR /app
RUN pip install -r requirements.txt
requirements.txt:
flask==2.0.1
先利用上面的 Dockerfile build我們的app image
只要在 server 資料夾裡執行build指令就可: docker build -t test-app .
會build出一個名為test-app的image
app.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'hello'
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
docker-compose.yaml:
version: '3'
services:
nginx:
image: nginx
container_name: my-nginx
volumes:
#- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./nginx/html:/usr/share/nginx/html
- ./nginx/log:/var/log/nginx
ports:
- "80:80"
environment:
- NGINX_PORT=80
restart: always
networks:
- mylab
app:
image: test-app # 使用我們上面build的image
container_name: my-app
volumes:
- ./server:/app
ports:
- "5000:5000"
environment:
- FLASK_RUN_PORT=5000
- FLASK_ENV=development
- FLASK_APP=app.py
command: ["flask", "run", "-h", "0.0.0.0"]
networks:
- mylab
app-2:
image: test-app
container_name: my-app-2
volumes:
- ./server:/app
ports:
- "5001:5001"
environment:
- FLASK_RUN_PORT=5001
- FLASK_ENV=development
- FLASK_APP=app.py
command: ["flask", "run", "-h", "0.0.0.0"]
networks:
- mylab
networks:
mylab:
driver: bridge
docker-compose up 運行,再試著以 curl localhost:5000 連線,應該就可以收到flask server回覆的 hello 字串
如此我們就設定好我們的後端server ,接下來是要設定Nginx將Request導向後端server
Nginx 的 Load Balance 設定
上面的 docker-compose up 會跑起兩個相同功能的server,container my-app 跟 container my-app-2
那我們就試著設定 Nginx 讓流量分散到這兩個 app 吧
default.conf:
upstream app {
server my-app:5000;
server my-app-2:5001;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://app;
}
}
upstream 可以定義 server pool,upstream 做為入口,幫忙分流進宣告的 server
以上面的例子來看,我們定義了一個 upstream app,app 後接的是我們 docker-compose 跑起的 my-app 和 my-app2 這兩個 server
稍微說明一下 docker-compose 的網路,上面的 docker-compose.yaml 每個 service 都有寫 networks: mylab,另外最下面有定義networks mylab 是使用 bridge 連線,就我們跑起這個 docker-compose,會架起一個叫 mylab 的網路,而在這網路的 service 就有 my-nginx, my-app, my-app-2 這三個容器,容器名也做為 hostname,在這 mylab 網路裡 my-nginx 容器裡 ping my-app 能成功 ping 到
因此 upstream 裡的 server 才會寫成 my-app:5000
如果你想測試 ping 看看,可以這麼做:
docker exec -ti my-nginx bash # 先進入my-nginx container裡
### 在 my-nginx 容器中執行 ###
apt-get update
apt-get install iputils-ping # 要先下載ping tool
ping my-app
proxy_pass url會將請求導向後面的 url 指向的位置
prxoy_pass 設定 path 要注意:
location /name/ {
proxy_pass http://app/remote/;
}
如果傳 `http://localhost/name/1` 會導到 `http://app/remote/1`,因為當proxy_pass is specified with a URI 時,只會將 /name/ 後的值接到 app/remote/ 後
location /name/ {
proxy_pass http://app;
}
如果傳 `http://localhost/name/1` 則會導到 `http://app/name/1`,因為當proxy_pass is specified without a URI 時,則會將整個 path 值接到後面
測試
首先跑起我們的 services: 跑 docker-compose up
接著連線多次看看:curl localhost
會看到這樣的結果
my-app 和 my-app-2 輪流收到訊息,顯示我們成功做到分流
另外也可試著把一個 my-aooserver 關掉看看:docker stop my-app
會發現 Nginx 之後都會改只傳給 my-app-2
Nginx的負載平衡也會考量進這個server狀態,若server沒回應時,之後的輪詢將不會輪到此server
但Nginx只提供被動偵測(Nginx Plus才有主動偵測),當某 server 連線失敗達到 max_fails 次數,會判斷這個 server 不能用,會等待過 fail_timeout 時間後才會再去請求此 server,如果之後成功,會恢復之前的輪詢方式
預設定是 fail_timeout=10, max_fails=1
沒有留言:
張貼留言