iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 28
10
Modern Web

Half-Stack Developer 養成計畫系列 第 28

俄羅斯不愧是戰鬥民族:nginx

俄羅斯不愧是戰鬥民族:nginx

無論你寫的是前端還是後端,都需要一台伺服器來放你的檔案。尤其後端更是如此。但你可能會疑惑說,我用 Express 不是就可以啟動一個伺服器了嗎?我為什麼需要另外再找伺服器?這個問題問得很好!

還記得你之前寫過的簡單後端 API 嗎?我們讓它跑在http://localhost:5566。可是,你要怎麼讓別人連進來你的電腦,看到你寫的後端 API 呢?這個時候你就需要一個 Domain 了。

Domain 的中文翻譯叫做網域,跟網址的概念很像,例如example.com就是一個 domain,如果你想買 domain 的話,可以參考知名的網域購買平台GoDaddy。在買了之後,你需要設定你的 domain,才能讓其他人連到正確的地方。

假設你電腦的 IP 是 123.1.1.1 好了,你就要在你買網域的地方的後台設定把example.com指到 123.1.1.1,這樣子當別人輸入example.com的時候,其實就是在連到 123.1.1.1 了。網頁的預設 port 是 80,所以 example.com 等於會連到 123.1.1.1:80,如果你想連到別的 port 的話,需要自己再設定一下。

可是,你有看過網址上面出現 port 的嗎?這樣也太難記了吧!因此,大多數都是直接用 80 port,省去使用者要自己輸入 port 的麻煩。但問題就來了,如果我在一台主機上面,想要跑兩個網站怎麼辦? example.com 跟 example2.com 雖然都設定好連到 123.1.1.1,可是 80 port 只有一個啊,我該怎麼辦呢?

這時候,你的救星來了,叫做 nginx,是一個「反向代理」的伺服器。

有關於什麼是代理,什麼是反向代理,有一篇文章講得超級清楚:反向代理为何叫反向代理?。雖然他已經講得很好了,但為了造福懶得點連結進去看的人,我再稍微解釋一下。

要談反向代理,就要先談正向代理,什麼是正向代理呢?例如說我缺一大筆錢,但我沒信用不能借,我就找我朋友去跟銀行借。這時候你朋友就是你的「代理」,而銀行那邊也只會知道這個代理人,不知道是你借的錢。用網路中的術語來講,就是一台 proxy server,你先連到 proxy server,再從 proxy server 連到別的網站,所以那些網站的 IP 來源都是 proxy server,而不是你自己的電腦。

那反向代理是什麼呢?就是你今天跟一個私人機構說要借錢,而那個私人機構也把錢借給你了。可是呢,其實私人機構背後有很多金主,你不知道是哪一個金主借你的錢,你只知道這個神秘私人機構是你的對口而已。

正向代理:Server 不知道到底真的 client 是誰。
反向代理:Client 不知道到底真的 server 是誰。

那反向代理可以做什麼呢?

還記得剛剛講到的問題嗎?我們想要在一台主機上面跑兩個不同的服務,這時候就可以在這台主機上先架一個反向代理的服務,也就是 nginx。再讓 nginx 決定他要把這個 request 丟給哪一個服務,就可以達成目的了。

更白話一點講,假設你今天 A 服務跑在 5566 port,B 服務跑在 7777 port。nginx 對外監聽的都是 80 port。當你第一個 request 進來的時候,Host 的值寫著:example.com,nginx 就知道他要找的是 example.com,就可以幫你把這個 request 轉送給 5566 port。當第二個 request 進來的時候,寫著 example2.com,就會轉送給 7777 port,這樣就可以在一台主機上面跑一大堆的服務了。

我們來試試看 nginx 吧!

安裝的話,如果你是 Mac 只要 brew install nginx 就可以了,其他作業系統則有其他安裝方法。至於 nginx 的設定檔,你可以在 /usr/local/etc/nginx 裡面找到。你可以打開 nginx.conf 看看,這是預設的設定檔:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    include servers/*;
}

看不懂沒關係,最後一行有個include servers/*;,代表會把 servers 資料夾下面的設定檔全部都引入。我們在 servers 資料夾底下開一個 test.conf,馬上來嘗試看看 nginx 的威力。

server {
    listen       80;
    server_name  example.com;
    
    # 把 request 轉給 localhost 的 5566 port
    location / {
      proxy_pass http://127.0.0.1:5566;
    }
}

server {
    listen       80;
    server_name  example2.com;
    
    # 把 request 轉給 localhost 的 7777 port
    location / {
      proxy_pass http://127.0.0.1:7777;
    }
}


接著我們用 node.js 寫一個非常簡單的 index.js,並且讓它跑起來:

var http = require("http");

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World, I am 5566");
  response.end();
}).listen(5566);

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World, I am 7777");
  response.end();
}).listen(7777);

再來,因為你更改了 nginx 的設定檔,所以需要重新啟動一下 nginx,在 mac 上我最喜歡的是把 nginx 直接關掉再啟動一次:sudo pkill nginx && sudo nginx。真的重新載入設定檔的指令我記不太住...

最後只剩一個步驟了,那就是 exmaple.com 跟 example2.com 這兩個 domain,現在都不是連到你自己這台電腦上,所以用 nginx 來監聽根本就沒有用。因此,你現在要把這兩個 domain 指到 127.0.0.1。可是你又沒有買這兩個 domain,怎麼設定呢?

在 Mac 裡面,有一個檔案叫做 /etc/hosts,裡面就可以指定你要讓哪一個 domain 連去哪邊,這邊的設置優先層級會最高。記得要用 sudo 來打開,才能更改檔案。

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1   localhost
255.255.255.255 broadcasthost
::1             localhost
127.0.0.1 example.com
127.0.0.1 example2.com

這樣子設置,就可以讓這兩個 domain 都指到自己的電腦了。接著打開瀏覽器,並且瀏覽這兩個網址,你就會發現看到兩個不同的頁面了。

這篇只提到 nginx 最基本最基本也是我最常用的功能,但事實上 nginx 厲害的地方就在他可以做的事情還超級無敵多,你甚至可以搭配 plugin 就直接用剛剛那個設定檔來寫程式!狂吧,不愧是俄羅斯人做出來的東西。

如果對 nginx 想更瞭解的,就自己去 Google 找一些資源吧!


上一篇
時間停止器:git
下一篇
把你的親生兒子公諸於世:VPS
系列文
Half-Stack Developer 養成計畫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
微中子
iT邦新手 4 級 ‧ 2017-01-07 00:52:05

是蠻猛的,只是一般業界通常也不會這樣幹吧 XD

huli iT邦新手 3 級 ‧ 2017-01-07 00:56:45 檢舉

你說把 request forward 到某個 port 嗎?我之前的公司會欸,我在那邊才學到這招的XD

0
HoiDam
iT邦新手 5 級 ‧ 2021-01-05 11:45:39

強欸 推~

我要留言

立即登入留言