寫給小白的 Nginx 文章

削微寒發表於2021-02-01

原文地址:Nginx concepts I wish I knew years ago

原文作者:Aemie Jariwala(已授權)

譯者 & 校正:HelloGitHub-小魚乾 & 滷蛋

Nginx 是一個採用主從架構的 Web 伺服器,可用於反向代理、負載均衡器、郵件代理和 HTTP 快取

Emmm,上面的 Nginx 介紹看過去有些複雜而且充滿了不明覺厲的術語。Relax,在這篇文章裡,我(原作者)會先帶你理解 Nginx 的架構和專有術語,最後實踐一把安裝和配置 Nginx

簡單來說,你只要記住一點:Nginx 是個神奇的 Web 伺服器。(注:神奇之處下文會娓娓道來)

那什麼是 Web 伺服器呢?簡而言之,Web 伺服器就是一箇中間人。舉個例子,你要訪問 hellogithub.com(注:原文例子為 dev.to),在位址列輸入 https://hellogithub.com 時,你的瀏覽器會找到 https://hellogithub.com 的網路伺服器地址並將它指向後端伺服器,後端伺服器再返回響應給客戶端。

代理 vs 反向代理

Nginx 的基本特性是代理,所以你一定要明白什麼是代理反向代理

代理

看個小例子,現在我們有 N 個客戶端(N >= 1),一箇中間 Web 伺服器(在本例中,我們稱之為代理)和一個伺服器。這個例子主要的場景是,伺服器不知道哪個客戶端在請求(響應)。是不是有點難以理解?下面讓我用示意圖講解下:

如圖,client1client2 通過代理伺服器向伺服器傳送請求 request1request2,此時後端伺服器不知道 request1 是由 client1 傳送的還是 client2 傳送的,但會執行(響應)操作。

反向代理

簡單來說,反向代理與代理的功能相反。現在我們有一個客戶端、一箇中間 Web 伺服器和 N 個後端伺服器(N >= 1),同樣的來看下示意圖:

如圖,客戶端將通過 Web 伺服器傳送請求。而 Web 伺服器會通過一個演算法,當中最有意思的演算法是輪詢,直接將請求指向許多後端伺服器中的一個,並通過 Web 伺服器將響應返回給客戶端。因此,在上面的例子中,客戶端其實並不知道在與哪個後端伺服器進行互動。

負載均衡

又是枯燥的一個名詞:負載均衡,不過它很好理解,因為負載均衡本身就是反向代理的一個例項。

來看看負載均衡和反向代理的本質區別。在負載均衡中,你必須有 2 個或者更多的後端伺服器,但在反向代理中,多臺伺服器不是必需的,甚至一臺後端伺服器也能運作。我們再深入點,如果我們有很多來自客戶端的請求,負載均衡器會檢查每個後端伺服器的狀態,均勻地分配請求,更快地向客戶端傳送響應。

有狀態 vs 無狀態應用

Okay,在我們開始實踐 Nginx 之前,先搞清所有的基本知識!

有狀態應用

有狀態應用存了一個額外變數,只用來儲存伺服器中單個例項使用所需的資訊。

如圖所示,一個後端伺服器 server1 儲存了一些資訊,伺服器 server2 並不儲存此資訊,因此,客戶端 (上圖 Bob) 的互動可能會也可能不會得到想要的結果,因為它可能會與 server1server2 互動。在本例中,server1 允許 Bob 檢視資料檔案,但 server2 不允許。因此,雖然有狀態應用避免對資料庫的多次 API 呼叫,並且(響應)速度更快,但它可能會在不同的伺服器上導致這個(無法得到想要結果)問題。

無狀態應用

無狀態應用有更多的資料庫 API 呼叫,但當客戶端與不同後端伺服器的互動時,無狀態應用卻存在更少的問題。

沒明白?簡單來說,如果我通過 Web 伺服器從客戶端向後端伺服器 server1 傳送請求,它將向客戶端返回一個令牌,用於任何進一步的訪問請求。客戶端可以使用令牌並向 Web 伺服器傳送請求。此 Web 伺服器將請求連同令牌一起傳送到任意後端伺服器,而每個後端伺服器都能提供相同的所需結果。

Nginx 是什麼?

Nginx 是網路伺服器,到目前為止,我的整個部落格一直在用這個網路伺服器。老實說,Nginx 這就像個中間人

這個圖不難理解,它是目前為止所有概念的一個組合。在這裡,我們有 3 個後端伺服器執行在 3001、3002 和 3003 埠,這些後端伺服器都能訪問同一個執行在 5432 埠的資料庫。

當一個客戶端向 https://localhost (預設埠 443)發起一個 GET /employees 請求時,Nginx 將基於演算法向任意後端伺服器傳送請求,從資料庫獲取資料並將 JSON 資料返回 Nginx Web 伺服器再傳送給客戶端。

如果我們使用一個諸如輪詢這樣的演算法,它讓 client2https://localhost 傳送一個請求,然後 Nginx 伺服器會先將請求傳到 3000 埠並將響應返回給客戶端。對另一個請求,Nginx 會把請求傳給 3002 埠,以此類推。

知識儲備完成!到這裡,你對 Nginx 是什麼以及 Nginx 所涉及的術語有了一個清晰的理解。是時候,瞭解安裝和配置技術了。

開始安裝 Nginx

時機到了,如果你瞭解了上面的概念,可以動手開始 Nginx 實踐了。

嗯,Nginx 的安裝過程對任何系統來說都很簡單。我是一個 Mac OSX 使用者,所以例子的命令是基於 macOS 的, UbuntuWindows 和其他 Linux 發行版操作和例子類似。

$ brew install Nginx

只要執行上面這步,你的系統就有 Nginx 了!是不是很神奇!

執行 Nginx 如此簡單 ?

要檢查 Nginx 是否執行也很簡單。

$ nginx 
# OR 
$ sudo nginx

執行上面指令,再開啟瀏覽器並輸入 http://localhost:8080/ 回車檢視下,你會看到以下畫面!

Nginx 基本配置 & 示例

下面,我們通過實操來感受下 Nginx 的魔力。

首先,在本地建立如下的目錄結構:

.
├── nginx-demo
│  ├── content
│  │  ├── first.txt
│  │  ├── index.html
│  │  └── index.md
│  └── main
│    └── index.html
└── temp-nginx
  └── outsider
    └── index.html

當然,.html.md 檔案中要包含基本資訊。

我們想要得到什麼呢?

這裡,我們有兩個單獨的資料夾 nginx-demotemp-nginx,每個資料夾都包含靜態 HTML 檔案。我們將著力在一個公共埠上執行這兩個資料夾,並設定我們想要的規則。

回到之前說的,如果要修改 Nginx 預設配置,得修改 usr/local/etc/nginx 目錄下的 nginx.conf檔案。我的系統中有 vim,所以在這裡用 vim 來更改 Nginx 配置,你可以用自己的編輯器來修改配置。

$ cd /usr/local/etc/nginx
$ vim nginx.conf

上面的命令會開啟一個 Nginx 預設配置檔案,我真的不想直接使用預設配置。因此,我通常的做法是複製這個配置檔案,然後對主檔案進行更改。這裡也不例外。

$ cp nginx.conf copy-nginx.conf
$ rm nginx.conf && vim nginx.conf 

上面命令將開啟一個空檔案,我們將為它新增配置。

  1. 新增配置的基本設定。一定要新增 events {},因為在 Nginx 架構中,它通常用來表示 worker 的數量。在這裡我們用 http 告訴 Nginx 我們將在 OSI 模型 的第 7 層作業。

    這裡,我們告訴 Nginx 監聽 5000 埠,並指向 main 資料夾中的靜態檔案。

    http {
    
      server {
        listen 5000;
        root /path/to/nginx-demo/main/; 
        }
    
    }
    
    events {}
    
  2. 接下來我們將為 /content/outsider URL 新增其他的規則,其中 outsider 將指向第一步中提到的根目錄之外的目錄。

    這裡的 location /content 表示無論我在葉(leaf)目錄中定義了什麼根(root),content 子 URL 都會被新增到定義的根 URL 的末尾。因此,當我指定 root 為 root /path/to/nginx-demo/時,這僅僅意味著我告訴 Nginx 在 http://localhost:5000/path/to/nginx-demo/content/ 資料夾中顯示靜態檔案的內容。

    http {
    
      server {
          listen 5000;
          root /path/to/nginx-demo/main/; 
    
          location /content {
              root /path/to/nginx-demo/;
          }   
    
          location /outsider {
              root /path/temp-nginx/;
          }
      }
    
    }
    
    events {}
    

    酷斃了!現在 Nginx 不僅能定義 URL 根路徑,還可以設定規則,這樣我們就能阻止客戶端訪問某個檔案了。

  3. 接下來,我們在主伺服器上編寫一個規則來防止任意 .md 檔案被訪問。我們可以在 Nginx 中使用正規表示式,因此我們將這樣定義規則:

      location ~ .md {
            return 403;
      }
    
  4. 最後,讓我們學習下 proxy_pass 命令來結束這個章節。我們已經瞭解了什麼是代理和反向代理,在這裡我們從定義另一個執行在 8888 埠上的後端伺服器開始。現在,我們在 5000 和 8888 埠上執行了 2 個後端伺服器。

    我們要做的是,當客戶端通過 Nginx 訪問 8888 埠時,將這個請求傳到 5000 埠,並將響應返回給客戶端!

    server {
        listen 8888;
    
        location / {
            proxy_pass http://localhost:5000/;
        }
    
        location /new {
            proxy_pass http://localhost:5000/outsider/;
        }
    }
    

看下,這是所有的配置資訊 ?

http {

    server {
        listen 5000;
        root /path/to/nginx-demo/main/; 

        location /content {
            root /path/to/nginx-demo/;
        }   

        location /outsider {
            root /path/temp-nginx/;
        }

                location ~ .md {
          return 403;
        }
    }

      server {
        listen 8888;

        location / {
            proxy_pass http://localhost:5000/;
        }

        location /new {
            proxy_pass http://localhost:5000/outsider/;
        }
  }

}

events {}

使用 sudo nginx 來執行此配置。

其他 Nginx 命令

  1. 首次啟動 Nginx Web 伺服器。

    $ nginx 
    #OR 
    $ sudo nginx
    
  2. 重新載入正在執行的 Nginx Web 伺服器。

    $ nginx -s reload
    #OR 
    $ sudo nginx -s reload
    
  3. 停止正在執行中的 Nginx Web 伺服器。

    $ nginx -s stop
    #OR 
    $ sudo nginx -s stop
    
  4. 檢視系統上執行的 Nginx 程式。

    $ ps -ef | grep Nginx
    

第 4 條命令很重要,如果前 3 條命令產生了一些問題,通常你可以用第 4 條命令找到所有正在執行的 Nginx 程式並殺死程式,然後重新啟動它們。

要殺死一個程式,你需要 PID,再用以下命令殺死它:

$ kill -9 <PID>
#OR 
$ sudo kill -9 <PID>

結束本文之前,宣告下,文中我用了些來自 Google 的圖片和 Hussein Nasser 釋出在油管的視訊教程。

下面盡情享受 Coding、探索 Nginx 的魔力吧!?

最後,歡迎優秀的你加入 HelloGitHub 的「譯文亦舞」系列,讓你的才華舞動起來!把優秀的文章分享給更多的人。要求:

  • 平時瀏覽 GitHub、開源、程式設計、程式設計師等英文資訊和文章
  • 想把自己閱讀到優秀的英文文章分享給更多的人
  • 翻譯準確但不是直翻或機翻
  • 保證每月至少翻譯或校正 1 篇高質量文章
  • 瞭解 Markdown 和排版規則
  • 聯絡微信:xueweihan (備註:翻譯)

相關文章