以前dotnet web应用程序开发完成后,我们都是使用IIS部署在Windows Server上,如今netcore技术发展迅速,因为其跨平台的特性,将dotnet web应用程序部署在更方便部署和更廉价的Linux服务器上日益流行。这里简单介绍如何使用Nginx/Systemd/Kestrel将netcore web应用程序部署在Centos系统上。将会涉及两个概念:反向代理和负载均衡。

1.Nginx/Systemd/Kestrel托管netcore应用

1.1 准备netcore web应用

  首先创建一个netcore mvc项目,修改 Startup.cs,添加了加粗部分代码,如下:

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,如下:

    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主要是为了设置部署的端口,内容如下

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

这里只是演示部署,HomeController和Index的逻辑很简单,仅仅展示X-Forwarded-For和Host主机:

 //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 ,如果可以成功启动表示编码没问题,到这里准备工作就结束了。

1.2 Systemd托管dotnet web应用

  在Windows上部署时,如果不使用IIS进行部署的话,我们一般把netcore应用部署成window服务,因为部署成服务可以实现开机自启,也比较稳定。在Linux部署时也最好部署成服务(守护进程),这里采用Systemd将dotnet web应用部署成服务,本篇的底部有Systemd简单介绍,如果不了解的园友可以看一下。首先  cd /usr/lib/systemd/system/ 到Unit配置文件目录下,创建一个配置文件,名字为 kestrel-mysite1.service,内容如下:

[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 查看服务运行情况,效果如下:

  到这里我们已经将netcore项目部署成一个服务了,dotnet web应用运行在Kestrel服务器上(我们安装netcore环境时会自动安装kestrel服务器),接下来使用Nginx实现反向代理和负载均衡。

1.3 Nginx实现反向代理

  首先介绍一下反向代理的概念。有反向代理就有前向代理,前向代理作为客户端的代理,将从互联网上获取的资源返回给一个或多个的客户端,服务端(如Web服务器)只知道代理的IP地址而不知道客户端的IP地址;而反向代理是作为服务器端(如Web服务器)的代理使用,而不是客户端,客户端知道代理服务器IP地址而不知道具体后台服务器的IP地址。简单的讲,我们在公司通过代理服务器访问外网,这里代理服务器属于前向代理服务器,而客户通过代理服务器从外网访问我们公司的服务器,这里的代理服务器就是反向代理服务器。Nginx实现反向代理很简单,使用一个proxy_pass指令将请求转发给指定的后台服务器即可。

  通过 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

快速掌握Nginx(三) —— Nginx+Systemd托管netcore应用的更多相关文章

  1. 使用 Nginx 在 Linux 上托管 ASP.NET Core 应用程序

    本文于2019年04月10日将标题「CentOS7 部署 ASP.NET Core应用程序」修改为「使用 Nginx 在 Linux 上托管 ASP.NET Core 应用程序」. 环境准备 VMwa ...

  2. 使用Docker快速部署ELK分析Nginx日志实践(二)

    Kibana汉化使用中文界面实践 一.背景 笔者在上一篇文章使用Docker快速部署ELK分析Nginx日志实践当中有提到如何快速搭建ELK分析Nginx日志,但是这只是第一步,后面还有很多仪表盘需要 ...

  3. 使用Docker快速部署ELK分析Nginx日志实践

    原文:使用Docker快速部署ELK分析Nginx日志实践 一.背景 笔者所在项目组的项目由多个子项目所组成,每一个子项目都存在一定的日志,有时候想排查一些问题,需要到各个地方去查看,极为不方便,此前 ...

  4. 使用Linux、Nginx和Github Actions托管部署ASP.NET Core 6.0应用

    使用Linux.Nginx和Github Actions托管部署ASP.NET Core 6.0应用 前言 本文主要参考微软这篇文档而来 Host ASP.NET Core on Linux with ...

  5. Nginx教程(三) Nginx日志管理

    Nginx教程(三) Nginx日志管理 1 日志管理 1.1 Nginx日志描述 通过访问日志,你可以得到用户地域来源.跳转来源.使用终端.某个URL访问量等相关信息:通过错误日志,你可以得到系统某 ...

  6. nginx(三)反向代理和负载均衡

    nginx(三)反向代理和负载均衡 正向代理概念:比如在学校要上网,在学校内网是一个内网ip,需要连上公网就需要一个正向代理服务器. 反向代理概念: 看下图(Nginx只做请求的转发,后台有多个htt ...

  7. Nginx(三):日志文件管理

    一.Nginx日志描述 通过访问日志,你可以得到用户地域来源.跳转来源.使用终端.某个URL访问量等相关信息: 通过错误日志,你可以得到系统某个服务或server的性能瓶颈等.因此,将日志好好利用,你 ...

  8. 《nginx 三》实现nginx的动态负载均衡——实战

    Http动态负载均衡 什么是动态负载均衡 传统的负载均衡,如果Upstream参数发生变化,每次都需要重新加载nginx.conf文件, 因此扩展性不是很高,所以我们可以采用动态负载均衡,实现Upst ...

  9. Nginx三种模式的虚拟主机(附Apache基于域名的虚拟主机)

    1.安装nginx # pcre中文"perl兼容正则表达式",安装pcre库是为了让nginx支持具备URL重写功能 # 的Rewrite模块,rewrite可以实现动态页面转成 ...

随机推荐

  1. 谈下git的基本操作

    在工作之后,我比较经常地接触git,关于git的用法,网上有很多的教程,而且git的指令是非常多的,强如阮一峰这样的高手也直言无法记住git的很多指令.实际上我也看了不少关于git指令的文章,个人觉得 ...

  2. vue(4)—— vue的过滤器,监听属性,生命周期函数,获取DOM元素

    过滤器 vue允许自定义过滤器,我个人认为,过滤器有两种,一种是对数据的清洗过滤,一种是对数据切换的动画过滤 数据切换的动画过滤 这里还是利用前面的动态组件的例子: 这里由于没办法展示动画效果,代码如 ...

  3. 使用Server Trigger保护重要的数据库对象

    一 .Server Trigger的简单介绍 在SQL Server数据库中,Server Trigger 是一种特殊类型的存储过程,它可以对特定表.视图或存储中的必然事件自动响应,不由用户调用.创建 ...

  4. Postgres中文分词

    环境 CentOS Linux release 7.2.1511 (Core) 安装Postgres 安装postgres很简单 yum安装 sudo yum install postgresql-s ...

  5. mysql下载安装及常见问题

    1.下载MySql 官网下载地址:https://dev.mysql.com/downloads/mysql/ 2.安装 如果下载的是zip的,直接解压目录即可,我的解压目录时:C:\mysql\my ...

  6. python+selenium运行时,提示元素不可见

    python+selenium运行多次新增项目脚本(出错的元素通过by_id的方式定位),当第三次新增时报Message: element not visible的错误,加入等待时间,等页面加载完成, ...

  7. Centos7安装搜狗输入法.

    系统默认安装输入法管理器的是 ibus. 而搜狗使用 fcitx 1.以我们先要安装 fcitx和必要的软件包 yum -y install fcitx* yum -y install libQtWe ...

  8. python中使用redis发布订阅者模型

    redis发布订阅者模型: Redis提供了发布订阅功能,可以用于消息的传输,Redis的发布订阅机制包括三个部分,发布者,订阅者和Channel.发布者和订阅者都是Redis客户端,Channel则 ...

  9. iis设置默认文档,提示web.config配置xml格式不正确

    网站上传后,配置默认文档,提示web.config配置xml格式不正确,几经尝试,发现是sqlserver密码中的“&”符号惹的祸,web.config文件中不能使用该字符.分享出来,大家遇到 ...

  10. Graph图总结

    将COMP20003中关于Graph的内容进行总结,内容来自COMP20003,中文术语并不准确,以英文为准. Graph G = {V, E} 顶Vertices V: can contain in ...