快速掌握Nginx(三) —— Nginx+Systemd託管netcore應用

  之前dotnet web應用程序開發完成後,咱們都是使用IIS部署在Windows Server上,現在netcore技術發展迅速,由於其跨平臺的特性,將dotnet web應用程序部署在更方便部署和更廉價的Linux服務器上日益流行。這裏簡單介紹如何使用Nginx/Systemd/Kestrel將netcore web應用程序部署在Centos系統上。將會涉及兩個概念:反向代理和負載均衡。html

1.Nginx/Systemd/Kestrel託管netcore應用

1.1 準備netcore web應用

  首先建立一個netcore mvc項目,修改 Startup.cs,添加了加粗部分代碼,以下:python

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
          
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            //轉接頭中間件
            app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto });

            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

修改Program.cs,以下:linux

    public class Program
    {
        public static void Main(string[] args)
        {

            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            //添加額外的配置
            var cfg = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .AddJsonFile("hosting.json")
                .Build();

            return WebHost.CreateDefaultBuilder(args)
                .UseConfiguration(cfg)
                .UseStartup<Startup>();
        }
    }

hosting.json主要是爲了設置部署的端口,內容以下nginx

{ "server.urls": "http://0.0.0.0:1111"}

這裏只是演示部署,HomeController和Index的邏輯很簡單,僅僅展現X-Forwarded-For和Host主機:web

 //HomeController 
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
       //X-Forwarded-For可用於獲取真實的客戶端IP,格式爲IP1,IP2...,其中第一個IP就是客戶端IP,中間的爲代理服務器IP ViewBag.xfor = Request.Headers["X-Forwarded-For"]; ViewBag.host = Request.Headers["Host"]; return View(); } } //Index View
<div class="text-center"> <h1 style="background-color:aquamarine" class="display-4">歡迎來到 Site1 </h1> <h3> X-Forwarded-For: @ViewBag.xfor</h3><br /> <h3> Host: @ViewBag.host</h3> <br /> </div>

  發佈項目,將發佈文件傳輸到Centos的/var/www/MySite1目錄下(也能夠放在其餘地方),到/var/www/MySit1目錄下,執行cli命令 dotnet MySite.dll ,若是能夠成功啓動表示編碼沒問題,到這裏準備工做就結束了。json

1.2 Systemd託管dotnet web應用

  在Windows上部署時,若是不使用IIS進行部署的話,咱們通常把netcore應用部署成window服務,由於部署成服務能夠實現開機自啓,也比較穩定。在Linux部署時也最好部署成服務(守護進程),這裏採用Systemd將dotnet web應用部署成服務,本篇的底部有Systemd簡單介紹,若是不瞭解的園友能夠看一下。首先  cd /usr/lib/systemd/system/ 到Unit配置文件目錄下,建立一個配置文件,名字爲 kestrel-mysite1.service,內容以下:vim

[Unit]
#簡單描述
Description=run MySite on Centos

[Service]
#工做目錄
WorkingDirectory=/var/www/MySite1
#開啓時執行的命令
ExecStart=/usr/bin/dotnet /var/www/MySite1/MySite.dll
#只有出錯時重啓,Restart=always表示不管什麼緣由形成服務中止都會重啓
Restart=on-failure#服務崩潰時,十秒鐘重啓一次
RestartSec=10
#用戶
User=wyy

[Install]
#該服務所在的target
#這裏符號連接放在/usr/lib/systemd/system/multi-user.target.wants目錄下
WantedBy=multi-user.target

  而後經過命令 systemctl daemon-reload 從新加載配置文件,使用 systemctl start kestrel-mysite1 啓動服務。經過命令啓動服務不必定成功,使用 systemctl status kestrel-mysite1 查看服務運行狀況,效果以下:windows

  到這裏咱們已經將netcore項目部署成一個服務了,dotnet web應用運行在Kestrel服務器上(咱們安裝netcore環境時會自動安裝kestrel服務器),接下來使用Nginx實現反向代理和負載均衡。瀏覽器

1.3 Nginx實現反向代理

  首先介紹一下反向代理的概念。有反向代理就有前向代理,前向代理做爲客戶端的代理,將從互聯網上獲取的資源返回給一個或多個的客戶端,服務端(如Web服務器)只知道代理的IP地址而不知道客戶端的IP地址;而反向代理是做爲服務器端(如Web服務器)的代理使用,而不是客戶端,客戶端知道代理服務器IP地址而不知道具體後臺服務器的IP地址。簡單的講,咱們在公司經過代理服務器訪問外網,這裏代理服務器屬於前向代理服務器,而客戶經過代理服務器從外網訪問咱們公司的服務器,這裏的代理服務器就是反向代理服務器。Nginx實現反向代理很簡單,使用一個proxy_pass指令將請求轉發給指定的後臺服務器便可。bash

  經過 vim /usr/local/nginx/conf/nginx.conf 編輯nginx配置,修改nginx配置以下:

worker_processes 2;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    sendfile      on;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    server{
        listen     80  ;
        server_name default_server;
        location /{
                root html;
                index index.html;
                }


        }

#加載vhosts目錄下全部配置
include vhosts/* ;
}

經過 vim /usr/local/nginx/conf/vhosts/www.mysite.com 編輯www.mysite.com配置文件以下, proxy_pass http://192.168.70.99:1111 表示將請求交給192.168.70.99:1111處理,就是將請求交給上邊部署的kestrel-mysite1服務處理:

server {
    listen        80;
    server_name   mysite.com *.mysite.com;
    access_log  logs/mysite_access.log  main;
    location / {
        proxy_pass         http://192.168.70.99:1111;
        proxy_http_version 1.1;
        proxy_set_header   Host $host;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

  而後使用命令 nginx -s reload 從新加載nginx配置 。爲了演示方便咱們在本身的電腦上添加一個本機DNS,到C:\Windows\System32\drivers\etc目錄下,在host文件下添加DNS: 192.168.70.99 www.mysite.com 。到這裏反向代理就配置完成了,在瀏覽器上輸入www.mysite.com顯示以下:

  反向代理的基本流程:咱們在瀏覽器輸入www.mysite.com,首先經過DNS解析找到訪問的主機IP爲192.168.70.99,請求發送後被IP爲192.168.70.99虛擬機的80端口監聽到,location匹配後經過proxy指令將請求交給192.168.70.99:1111服務去處理,處理完成後將結果返回Nginx,再由Nginx返回給客戶端。192.168.70.99:1111部署的就是運行在kestrel上的netcore web應用。

1.4 Nginx實現負載均衡

  考慮一個問題:若是咱們的web應用併發量比較大,一臺服務器不知足需求,咱們多部署了幾臺服務器,那麼怎麼讓Nginx將請求轉發給多臺服務器呢?簡單的說,將請求轉發給多臺服務器能夠叫作負載均衡,同一個location下不能寫多個proxy指令,upstream指令能夠實現將請求轉發給多個不一樣的服務器。實現負載均衡的步驟也十分簡單。

  咱們修改示例項目的hosting.json配置文件,內容爲 { "server.urls": "http://0.0.0.0:2222"} ,即部署端口設置爲2222,將發佈文件放在/var/www/MySite2目錄下(爲了便於區分,部署在2222端口的界面顯示【歡迎來到Site2】);而後經過Systemd添加一個kestrel-mysite2服務,添加過程和kestrel-mysite1服務同樣,配置文件以下:

[Unit]
#簡單描述
Description=run MySite on Centos

[Service]
#工做目錄
WorkingDirectory=/var/www/MySite2
#開啓時執行的命令
ExecStart=/usr/bin/dotnet /var/www/MySite2/MySite.dll
#出錯形成服務中止時重啓,Restart=always表示不管什麼緣由形成服務未運行都重啓
Restart=on-failure
#服務崩潰時,十秒鐘重啓一次
RestartSec=10
#用戶
User=wyy

[Install]
#該服務所在的target
#這裏符號連接放在/usr/lib/systemd/system/multi-user.target.wants目錄下
WantedBy=multi-user.target

接下來修改/usr/local/nginx/vhosts/www.mysite.com配置文件,修改以下:

upstream mysite_hosts{
        server 192.168.70.99:1111 weight=1 max_fails=2 fail_timeout=30s;
        server 192.168.70.99:2222 weight=3 max_fails=2 fail_timeout=30s;
}


server {
    listen        80;
    server_name   mysite.com *.mysite.com;
    access_log  logs/mysite_access.log  main;
    location / {
        proxy_pass         http://mysite_hosts;
        proxy_http_version 1.1;
        proxy_set_header   Host $host;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}
  upstrem節點下  server 192.168.70.99:1111 weight=1 max_fails=2 fail_timeout=30s 中weight表示權重,權重越大轉發的概率就越高;max_fails=2,最多失敗兩次,失敗兩次後就再也不向這個服務器轉發請求;fail_timeout=30s表示超時時間爲30s。這裏爲了方便,部署在同一虛擬機的兩個端口上,也能夠部署在不一樣的設備上,
部署在不一樣的設備上時,修改一下IP便可。
  最後咱們打開瀏覽器,輸入www.mysite.com,不停的刷新,會出現兩種界面以下,且出現第二種界面的機率更大,若是咱們故意關掉其中的一個服務器,另外一臺服務器並不會受到影響,網站也能夠正常運行:

到這裏咱們已經經過Nginx實現了負載均衡,負載均衡還有其餘的形式,在下一篇會介紹。

補充2 Systemd

2.1 Systemd簡單認識

  Systemd即爲system daemon,是linux的一種init軟件,用來啓動和管理守護進程(相似於windows下的NSSM),它替代initd成爲系統的第一個進程(PID=1)。管理後臺服務的軟件還有python開發的supervisor等。

  Systemd管理系統資源時,全部的資源統稱爲Unit(單元)。每個Unit都有一個配置文件,用於告訴Systemd怎麼啓動這個Unit。開機時Systemd 從目錄/etc/systemd/system/讀取配置文件,該目錄裏面存放的大部分文件都是符號連接,指向目錄/usr/lib/systemd/system/,真正的配置文件存放在/usr/lib/systemd/system/目錄下。

  看一下Systemd管理防火牆的命令: systemctl enable firewalld  這條命令的做用是讓防火牆開機自啓,執行這條命令時會在/etc/systemd/system/添加一個符號連接,指向/usr/lib/systemd/system下的firewalld.service。開機時只會執行/etc/systemd/system/目錄下的配置文件,經過防火牆配置文件的符號連接,執行/usr/lib/systemd/system下的firewalld.service。相對的 systemctl disable firewalld 用於讓防火牆開機不自啓,實質上就是撤銷/etc/systemd/system/目錄下的符號連接。

  咱們能夠經過 systemctl list-unit-files 查看配置文件,以下所示:

  綠色的enabled表示開機啓動,紅色的disabled表示開機不啓動,static表示不能執行,只能做爲其餘配置文件的依賴,masked表示禁止執行。

2.2 Unit配置文件

  咱們知道Unit配置文件存放在 /usr/lib/systemd/system/目錄中,因此當添加後臺進程來託管netcore應用時,首先要在該目錄下添加一個配置文件。

  看一下上邊的Unit配置文件:

[Unit]
#簡單描述
Description=run MySite on Centos

[Service]
#工做目錄
WorkingDirectory=/var/www/MySite
#開啓時執行的命令
ExecStart=/usr/bin/dotnet /var/www/MySite1/MySite1.dll
#出錯形成服務中止時重啓
Restart=on-failure#服務崩潰時,十秒鐘重啓一次
RestartSec=10
#用戶
User=wyy

[Install]
#該服務所在的target
#這裏符號連接放在/usr/lib/systemd/system/multi-user.target.wants目錄下
WantedBy=multi-user.target

[Unit]區塊一般是配置文件的第一個區塊,用來定義 Unit 的元數據,以及配置與其餘 Unit 的關係。它的主要字段以下:

Description:簡短描述
Documentation:文檔地址
Requires:當前 Unit 依賴的其餘 Unit,若是它們沒有運行,當前 Unit 會啓動失敗
Wants:與當前 Unit 配合的其餘 Unit,若是它們沒有運行,當前 Unit 不會啓動失敗
BindsTo:與Requires相似,它指定的 Unit 若是退出,會致使當前 Unit 中止運行
Before:若是該字段指定的 Unit 也要啓動,那麼必須在當前 Unit 以後啓動
After:若是該字段指定的 Unit 也要啓動,那麼必須在當前 Unit 以前啓動
Conflicts:這裏指定的 Unit 不能與當前 Unit 同時運行
Condition...:當前 Unit 運行必須知足的條件,不然不會運行
Assert...:當前 Unit 運行必須知足的條件,不然會報啓動失敗

[Install]一般是配置文件的最後一個區塊,用來定義如何啓動,以及是否開機啓動。它的主要字段以下:

WantedBy:它的值是一個或多個 Target,當前 Unit 激活時(enable)符號連接會放入/etc/systemd/system目錄下面以 Target 名 + .wants後綴構成的子目錄中
RequiredBy:它的值是一個或多個 Target,當前 Unit 激活時,符號連接會放入/etc/systemd/system目錄下面以 Target 名 + .required後綴構成的子目錄中
Alias:當前 Unit 可用於啓動的別名
Also:當前 Unit 激活(enable)時,會被同時激活的其餘 Unit

補充:Target 就是一個 Unit 組,包含許多相關的 Unit 。啓動某個 Target 的時候,Systemd 就會啓動裏面全部的 Unit。從這個意義上說,Target 這個概念相似於"狀態點",啓動某個 Target 就比如啓動到某種狀態。

[Service]區塊用來 Service 的配置,只有 Service 類型的 Unit 纔有這個區塊。它的主要字段以下。

Type:定義啓動時的進程行爲。它有如下幾種值。
Type=simple:默認值,執行ExecStart指定的命令,啓動主進程
Type=forking:以 fork 方式從父進程建立子進程,建立後父進程會當即退出
Type=oneshot:一次性進程,Systemd 會等當前服務退出,再繼續往下執行
Type=dbus:當前服務經過D-Bus啓動
Type=notify:當前服務啓動完畢,會通知Systemd,再繼續往下執行
Type=idle:如有其餘任務執行完畢,當前服務纔會運行
ExecStart:啓動當前服務的命令
ExecStartPre:啓動當前服務以前執行的命令
ExecStartPost:啓動當前服務以後執行的命令
ExecReload:重啓當前服務時執行的命令
ExecStop:中止當前服務時執行的命令
ExecStopPost:中止當其服務以後執行的命令
RestartSec:自動重啓當前服務間隔的秒數
Restart:定義何種狀況 Systemd 會自動重啓當前服務,可能的值包括always(老是重啓)、on-success、on-failure、on-abnormal、on-abort、on-watchdog
TimeoutSec:定義 Systemd 中止當前服務以前等待的秒數
Environment:指定環境變量
注意:一旦修改配置文件,就要讓 Systemd從新加載配置文件,而後從新啓動,不然修改不會生效,命令以下:
sudo systemctl daemon-reload  
sudo systemctl restart xxx.service
Systemctl是Systemd的主命令,咱們常常用到的命令並很少,簡單總結以下:
# 當即啓動一個服務
$ sudo systemctl start firewalld.service

# 當即中止一個服務
$ sudo systemctl stop firewalld.service

# 重啓一個服務
$ sudo systemctl restart firewalld.service

# 殺死一個服務的全部子進程
$ sudo systemctl kill firewalld.service

# 從新加載一個服務的配置文件
$ sudo systemctl reload firewalld.service

# 重載全部修改過的配置文件
$ sudo systemctl daemon-reload

# 顯示某個 Unit 的全部底層參數
$ systemctl show firewalld.service

 參考文章:

【1】http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html

【2】https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-2.2

相關文章
相關標籤/搜索