ASP.NET Core의 Kestrel 웹 서버 구현 살펴보기
Kestrel은 크로스 플랫폼 비동기 I/O 라이브러리인 libuv 기반의 ASP.NET Core 용 크로스 플랫폼 웹 서버입니다. 또한 Kestrel은 ASP.NET Core 프로젝트 템플릿에 기본으로 포함되는 웹 서버입니다.
Kestrel은 다음과 같은 기능들을 지원합니다:
- HTTPS
- WebSockets의 활성화에 사용되는 불투명 업그레이드 (Opaque Upgrade)
- 내부적으로 Nginx의 고성능을 지원하기 위한 유닉스 소켓
Kestrel은 .NET Core가 지원하는 모든 플랫폼 및 버전에서 지원됩니다.
Kestrel을 역방향 프록시와 함께 사용하는 경우
단독으로 Kestrel만 사용할 수도 있고, IIS, Nginx 또는 Apache 같은 역방향 프록시 서버(Reverse Proxy Server)와 함께 사용할 수도 있습니다. 역방향 프록시 서버는 인터넷에서 HTTP 요청을 수신해서 사전 처리를 수행한 다음 Kestrel에 전달합니다.
내부 네트워크에서만 Kestrel이 노출될 경우, 역방향 프록시 서버를 사용하는 구성과 사용하지 않는 구성을 모두 적용할 수 있습니다.
역방향 프록시가 필요한 시나리오로의 한 가지 예로, 단일 서버에서 실행되는 동일한 IP 및 포트를 공유하는 다수의 응용 프로그램이 존재하는 경우를 들 수 있습니다. Kestrel은 복수의 프로세스가 동일한 IP 및 포트를 공유하는 기능을 지원하지 않기 때문에, Kestrel 자체만으로는 이런 시나리오를 구성할 수 없습니다. 만약 특정 포트를 수신하도록 Kestrel을 구성한다면, 호스트 헤더와 상관 없이 해당 포트의 모든 트래픽을 처리하게 됩니다. 따라서 포트를 공유할 수 있는 역방향 프록시가 고유한 IP 및 포트를 사용하는 Kestrel에 트래픽을 전달해줘야만 합니다.
굳이 역방향 프록시 서버가 필요하지 않은 경우에도 다음과 같은 이유로 역방향 프록시를 사용하는 것이 좋습니다:
- 외부로 노출되는 영역을 제한할 수 있습니다.
- 구성 및 방어를 위한 선택적인 추가 계층을 제공합니다.
- 기존 인프라와 보다 잘 통합시킬 수 있습니다.
- 로드 밸런싱 및 SSL 설정을 단순화시킵니다. 역방향 프록시 서버에만 SSL 인증서가 필요하며, 역방향 프록시 서버 자체는 일반적인 HTTP를 사용해서 내부 네트워크의 응용 프로그램 서버와 통신할 수 있습니다.
ASP.NET Core 응용 프로그램에서 Kestrel을 사용하는 방법
Microsoft.AspNetCore.Server.Kestrel 패키지는 Microsoft.AspNetCore.All 메타패키지에 포함되어 있습니다.
기본적으로 ASP.NET Core 프로젝트 템플릿은 Kestrel을 사용합니다.
Program.cs 파일에 작성된 템플릿 코드는 CreateDefaultBuilder
메서드를 호출하는데, 이때 내부적으로 UseKestrel 메서드가 호출됩니다.
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
})
.Build();
만약 Kestrel의 옵션을 구성하고 싶다면, 다음 예제와 같이 Program.cs 파일에서 UseKestrel
메서드를 호출하면 됩니다:
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
})
.Build();
Kestrel 옵션
Kestrel 웹 서버는 인터넷 노출 배포 시 특히 유용한 제약 구성 옵션이 갖고 있습니다. 다음은 설정 가능한 몇 가지 제한 사항입니다:
- 최대 클라이언트 연결 수
- 최대 요청 본문 크기
- 최소 요청 본문 데이터 통신 속도
이런 제약 조건과 기타 제약 조건들은 KestrelServerOptions 클래스의 Limits
속성을 이용해서 설정합니다.
이 Limits
속성은 KestrelServerLimits 클래스의 인스턴스를 담고 있습니다.
최대 클라이언트 연결 수
다음 코드를 이용해서 전체 응용 프로그램이 동시에 열 수 있는 TCP 연결의 최대 수를 설정할 수 있습니다:
.UseKestrel(options =>
{
options.Limits.MaxConcurrentConnections = 100;
options.Limits.MaxConcurrentUpgradedConnections = 100;
options.Limits.MaxRequestBodySize = 10 * 1024;
options.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
options.Limits.MinResponseDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
})
HTTP나 HTTPS에서 다른 프로토콜로 업그레이드 된 연결에는 (예, WebSockets 요청) 별도의 제한이 존재합니다.
연결이 업그레이드 되고 난 이후에는 MaxConcurrentConnections
제한 갯수에 포함되지 않습니다.
최대 연결 수의 기본값은 무제한 (null) 입니다.
최대 요청 본문 크기
기본 최대 요청 본문 크기는 30,000,000 바이트로, 이는 약 28.6MB 입니다.
ASP.NET Core MVC 응용 프로그램에서 이 제한을 재정의 할 때 권장하는 방법은 액션 메서드에 RequestSizeLimit 어트리뷰트를 지정하는 것입니다:
[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()
반면 다음 예제는 전체 응용 프로그램, 모든 요청에 대한 제약 조건을 구성하는 방법을 보여줍니다:
.UseKestrel(options =>
{
options.Limits.MaxConcurrentConnections = 100;
options.Limits.MaxConcurrentUpgradedConnections = 100;
options.Limits.MaxRequestBodySize = 10 * 1024;
options.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
options.Limits.MinResponseDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
})
미들웨어에서 특정 요청에 대한 설정을 재정의 할 수도 있습니다:
app.Run(async (context) =>
{
context.Features.Get<IHttpMaxRequestBodySizeFeature>()
.MaxRequestBodySize = 10 * 1024;
context.Features.Get<IHttpMinRequestBodyDataRateFeature>()
.MinDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
context.Features.Get<IHttpMinResponseDataRateFeature>()
.MinDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
응용 프로그램이 이미 요청을 읽기 시작한 다음에 요청의 제한 사항을 구성하려고 시도하면 예외가 발생합니다.
참고로 MaxRequestBodySize
속성이 읽기 전용 상태인지, 즉, 제한을 구성하기에는 너무 늦었는지 여부를 알려주는 IsReadOnly
속성도 지원됩니다.
최소 요청 본문 데이터 통신 속도
Kestrel은 데이터가 지정된 속도(바이트/초)로 들어오는지 매 초마다 확인합니다. 그리고 속도가 최소값 이하로 떨어지면 연결이 시간 만료됩니다. 유예 기간(Grace Period)은 Kestrel이 클라이언트가 전송 속도를 최소 속도까지 올릴 수 있도록 허용하는 시간을 말하며, 그 동안에는 속도가 확인되지 않습니다. 유예 기간은 TCP의 느린 시작(TCP Slow-Start)으로 인해서 초기에 느린 속도로 데이터를 전송하는 연결이 끊어지지 않도록 도와줍니다.
기본 최소 속도는 초당 240 바이트이고 5 초의 유예 기간이 주어집니다.
최소 속도는 응답에도 적용됩니다.
요청 제한이나 응답 제한을 설정하는 코드는 속성과 인터페이스 이름에 RequestBody
나 Response
가 포함되어 있다는 점만 제외하면 동일합니다.
다음 예제는 Program.cs 파일에서 최소 데이터 전송 속도를 구성하는 방법을 보여줍니다:
.UseKestrel(options =>
{
options.Limits.MaxConcurrentConnections = 100;
options.Limits.MaxConcurrentUpgradedConnections = 100;
options.Limits.MaxRequestBodySize = 10 * 1024;
options.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
options.Limits.MinResponseDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
})
미들웨어에서 요청 별 속도를 구성할 수도 있습니다:
app.Run(async (context) =>
{
context.Features.Get<IHttpMaxRequestBodySizeFeature>()
.MaxRequestBodySize = 10 * 1024;
context.Features.Get<IHttpMinRequestBodyDataRateFeature>()
.MinDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
context.Features.Get<IHttpMinResponseDataRateFeature>()
.MinDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
다른 Kestrel 옵션들에 대한 정보는 다음 클래스들을 참고하시기 바랍니다:
끝점 구성
기본적으로 ASP.NET Core는 http://localhost:5000
에 바인딩됩니다.
KestrelServerOptions
의 Listen
메서드나 ListenUnixSocket
메서드를 호출해서 Kestrel이 수신 대기할 URL 접두사와 포트를 구성할 수 있습니다.
(UseUrls
메서드, urls
명령줄 인수 또는 ASPNETCORE_URLS 환경 변수를 사용할 수도 있지만, 잠시 후에 설명할 제약 사항이 존재합니다.)
TCP 소켓 바인딩
Listen
메서드는 TCP 소켓에 바인딩하며, 옵션 람다를 전달해서 SSL 인증서를 구성할 수 있습니다:
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
})
.Build();
이 예제에서 ListenOptions를 이용해서 특정 끝점에 SSL을 구성하고 있는 방식에 유의하시기 바랍니다. 동일한 API를 사용해서 특정 끝점에 대한 Kestrel의 다른 설정들을 구성할 수 있습니다.
Windows에서는 자체 서명 SSL 인증서를 생성하기 위해서 PowerShell의 New-SelfSignedCertificate 커맨드릿(cmdlet)을 사용할 수 있습니다. 또는 자체 서명 인증서를 손쉽게 생성할 수 있게 도와주는 서드 파티 도구들도 존재합니다:
macOS 및 Linux에서는 OpenSSL을 사용해서 자체 서명 인증서를 만들 수 있습니다.
보다 자세한 내용은 Setting up HTTPS for development 문서를 참고하시기 바랍니다.
Unix 소켓 바인딩
다음 예제에서 볼 수 있는 것처럼, Unix 소켓을 사용해서 Nginx의 성능을 개선할 수도 있습니다:
.UseKestrel(options =>
{
options.ListenUnixSocket("/tmp/kestrel-test.sock");
options.ListenUnixSocket("/tmp/kestrel-test.sock", listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testpassword");
});
})
포트 0
포트 번호를 0 으로 지정하면, Kestrel이 사용 가능한 포트에 동적으로 바인딩됩니다. 다음 예제는 런타임에 실제로 Kestrel이 바인딩 한 포트를 확인하는 방법을 보여줍니다:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
var serverAddressesFeature = app.ServerFeatures.Get<IServerAddressesFeature>();
app.UseStaticFiles();
app.Run(async (context) =>
{
context.Response.ContentType = "text/html";
await context.Response
.WriteAsync("<p>Hosted by Kestrel</p>");
if (serverAddressesFeature != null)
{
await context.Response
.WriteAsync("<p>Listening on the following addresses: " +
string.Join(", ", serverAddressesFeature.Addresses) +
"</p>");
}
await context.Response.WriteAsync($"<p>Request URL: {context.Request.GetDisplayUrl()}<p>");
});
}
UseUrls 제한 사항
UseUrls
메서드를 호출하거나 urls
명령줄 인수 또는 ASPNETCORE_URLS 환경 변수를 이용해서 끝점을 구성할 수도 있습니다.
이 방식은 Kestrel 대신 다른 서버를 사용하고자 하는 경우에 유용합니다.
그러나 다음과 같은 제한 사항들을 감안해야 합니다:
- 이 방식들로는 SSL을 사용할 수 없습니다.
-
Listen
메서드와UseUrls
메서드를 둘 다 사용할 경우,Listen
메서드의 끝점이UseUrls
메서드의 끝점을 재정의 합니다.
IIS의 끝점 구성
IIS를 사용할 경우, Listen
메서드나 UseUrls
메서드 호출로 설정한 바인딩을 IIS의 URL 바인딩이 모두 덮어씁니다.
보다 자세한 정보는 ASP.NET Core 모듈 살펴보기 문서를 참고하시기 바랍니다.
URL 접두사
UseUrls
메서드를 호출하거나 urls
명령줄 인수 또는 ASPNETCORE_URLS 환경 변수를 사용할 경우, 다음 중 한 가지 형식으로 URL 접두사를 지정할 수 있습니다.
오직 HTTP URL 접두사만 유효하며, UseUrls
메서드를 이용해서 URL 바인딩을 구성할 경우 Kestrel은 SSL을 지원하지 않습니다.
-
포트 번호가 지정된 IPv4 주소
http://65.55.39.10:80/
0.0.0.0은 모든 IPv4 주소에 바인딩되는 특수한 주소입니다.
-
포트 번호가 지정된 IPv6
http://[0:0:0:0:0:ffff:4137:270a]:80/
IPv6에서 [::]는 IPv4의 0.0.0.0와 같습니다.
-
포트 번호가 지정된 호스트 이름
http://contoso.com:80/ http://*:80/
호스트 이름, * 및 + 는 특별하게 취급되지 않습니다. IP 주소로 인식되지 않은 모든 항목과 "localhost"는 모든 IPv4 및 IPv6 IP에 바인딩 됩니다. 동일한 포트에서 서로 다른 ASP.NET Core 응용 프로그램에 서로 다른 호스트 이름을 바인딩해야 한다면, HTTP.sys를 사용하거나 IIS, Nginx 또는 Apache 같은 역방향 프록시 서버를 사용해야 합니다.
-
포트 번호가 지정된 "localhost" 이름 또는 포트 번호가 지정된 루프백 IP
http://localhost:5000/ http://127.0.0.1:5000/ http://[::1]:5000/
Kestrel은
localhost
가 지정되면 IPv4 및 IPv6 루프백 인터페이스 모두에 바인딩을 시도합니다. 만약 요청된 포트가 두 루프백 인터페이스 모두 다른 서비스에서 사용 중이면 Kestrel의 시작이 실패합니다. 어떤 이유로든 둘 중 한 루프백 인터페이스라도 사용할 수 없는 경우 (가장 일반적으로 IPv6가 지원되지 않는 경우), Kestrel은 경고 로그를 남깁니다.
다음 단계
보다 자세한 내용은 다음 문서를 참고하시기 바랍니다:
- ASP.NET Core 기본 개요 2017-05-30 08:00
- ASP.NET Core와 응용 프로그램 Startup 클래스 2017-05-31 08:00
- ASP.NET Core 미들웨어의 기초 2017-06-01 08:00
- ASP.NET Core에서 정적 파일 서비스하기 2017-06-02 08:00
- ASP.NET Core의 오류 처리 살펴보기 2017-06-14 08:00
- 다양한 환경에서 작업하기 2017-08-21 08:00
- ASP.NET Core 구성하기 2017-08-28 08:00
- ASP.NET Core 파일 공급자 2017-09-04 08:00
- ASP.NET Core의 세션 및 응용 프로그램 상태 2017-09-11 08:00
- ASP.NET Core의 호스팅 살펴보기 2017-09-18 08:00
- ASP.NET Core의 웹 서버 구현 2017-09-25 08:00
- ASP.NET Core의 Kestrel 웹 서버 구현 살펴보기 2017-10-02 08:00
- ASP.NET Core 모듈 살펴보기 2017-10-09 08:00
- ASP.NET Core의 WebListener 웹 서버 구현 2017-10-16 08:00
- ASP.NET Core의 HTTP.sys 웹 서버 구현 2017-10-23 08:00
- ASP.NET Core의 요청 기능 2017-10-30 08:00
- ASP.NET Core와 URL 재작성 미들웨어 2018-05-07 08:00