之前dotnet web应用程序开发完成后,咱们都是使用IIS部署在Windows Server上,现在netcore技术发展迅速,由于其跨平台的特性,将dotnet web应用程序部署在更方便部署和更廉价的Linux服务器上日益流行。这里简单介绍如何使用Nginx/Systemd/Kestrel将netcore web应用程序部署在Centos系统上。将会涉及两个概念:反向代理和负载均衡。html
首先建立一个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
在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实现反向代理和负载均衡。浏览器
首先介绍一下反向代理的概念。有反向代理就有前向代理,前向代理做为客户端的代理,将从互联网上获取的资源返回给一个或多个的客户端,服务端(如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应用。
考虑一个问题:若是咱们的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实现了负载均衡,负载均衡还有其余的形式,在下一篇会介绍。
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表示禁止执行。
咱们知道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