在 Nginx 的 ProxyPass Upstream 設定 CORS (跨來源資源共享)

設定 Docker

不久前嘗試 Docker 化整套 PredictionIO(PIO) 設置。當中包括 Apache HBaseSpark 的設定,令人非常頭痛。最後花了一個多星期才做到一個可以勉強運行的原型。除了這些 PredictionIO 組件外,我還添加了一個 Nginx 容器作為運行 PredictionIO Event(事件)和 Prediction(預測)服務器的兩個容器的 Reverse Proxy(反向代理)。事件服務器有一個 REST Event(事件) API,可以把實時的用戶事件通過 HTTP POST 請求發送到事件服務器。由於我們想從前端 Javascript 收集事件並發送到事件服務器,我們必須在 Nginx 中啟用 CORS(跨來源資源共享)。數據流如下圖所示。

概覽

概覽

我們希望打開來自同一個域的 CORS 請求。(即本例中的* .pio.com)

Nginx 容器端口80被映射到 Docker 伺服器,它是連接兩國個 PIO 服務的唯一通路。然後根據主機名將請求路由到不同的上游。以下是兩個 Nginx 的配置文件。

upstream.conf

1
2
3
4
5
6
7
upstream pio-event-server {
  server pio-event-server:7070;
}

upstream pio-prediction-server {
  server pio-prediction-server:8000;
}

pio-proxy.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# PIO Event Server reverse proxy
server {
  listen 80;
  server_name event.pio.com;

  location / {
    # Check if the origin of th request
    set $cors '';

    if ($http_origin ~* (https?://.*\.pio\.com?(:[0-9]+)?$)) {
      set $cors 'on';
    }

    if ($request_method = OPTIONS) {
      set $cors "${cors}_options";
    }

    # Allow CORS on preflight request
    if ($cors = 'on_options') {
      add_header 'Content-Length' 0;
      add_header 'Content-Type' 'text/plain; charset=utf-8';
      add_header 'Access-Control-Allow-Origin' "$http_origin";
      add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';
      return 204;
    }

    # Proxy pass to upstream
    proxy_pass http://pio-event-server;
    proxy_redirect     off;
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;

    # Allow CORS on other requests after returning from the upstreams
    if ($cors = 'on') {
      add_header 'Access-Control-Allow-Origin' "$http_origin";
      add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';
    }
  }
}

# PIO Prediction Server reverse proxy
server {
  listen 80;
  server_name query.pio.com;

  location / {
    # Loadbalance
    proxy_pass http://pio-prediction-server;
    proxy_redirect     off;
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;
  }
}

請注意,如 proxy-pio.conf 中強調的那樣,add_header 指令必須寫在 proxy_pass 語句之後

最後,我們可以利用 Chrome Inspector 來檢查是否在 Response header(回應標頭)中添加了 CORS 的頭欄位。