Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 4개 있습니다.)
(시리즈 글이 3개 있습니다.)
개발 환경 구성: 172. IIS 7.5부터 지원되는 웹 사이트 자동 시작 모드
; https://www.sysnet.pe.kr/2/0/1367

개발 환경 구성: 701. IIS - w3wp.exe 프로세스의 ASP.NET 런타임을 항상 Warmup 모드로 유지하는 preload Enabled 설정
; https://www.sysnet.pe.kr/2/0/13512

닷넷: 2194. C# - WebActivatorEx / System.Web의 PreApplicationStartMethod 특성
; https://www.sysnet.pe.kr/2/0/13513




IIS 7.5부터 지원되는 웹 사이트 자동 시작 모드

IIS는 웹 애플리케이션을 호스팅하는 w3wp.exe 프로세스를 HTTP 요청이 없는 경우 기본적으로 20분이 지나면 자동적으로 프로세스를 종료시킵니다.

이는, 서비스를 거의 사용하지 않는 시간에 w3wp.exe를 내림으로써 개발자들이 설령 프로그램에 Memory Leak과 같은 버그를 남겨놨다고 해도 정상적으로 서비스가 될 수 있도록 만드는 장점을 갖습니다. 하지만, 단점도 또한 존재합니다. w3wp.exe가 내려간 상태에서 첫 번째 요청을 보내는 사용자는 로딩 시간만큼 서비스 이용이 지연된다는 불편함이 발생하는 것입니다.

이런 문제를 해결하기 위해 II 7.5부터는 AppPool에 "Start Automatically", "Start Mode" 2가지 옵션이 제공됩니다.

apppool_start_1.png

이 중에서 "Start Automatically" 옵션은 IIS 7부터 이미 제공되고 있었습니다. 이 옵션에 대해서는 다음의 글에서 자세하게 설명해 주고 있습니다.

Configure Automatic Startup for an Application Pool (IIS 7)
; https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/cc772112(v=ws.10)

옵션의 이름과는 달리 "Start Automatically" 옵션은 .NET 웹 애플리케이션을 자동으로 시작해 주는 것이 아니고, IIS 서비스가 시작할 때나 AppPool 자체가 생성되면서 그것이 동작할 것이냐를 지정하는 것뿐입니다. 그렇게 따지면 IIS 6.0의 경우에도 "Start Automatically" == "True"라는 설정으로 이미 동작하고 있었던 것이나 다름없습니다. 단지 IIS 7부터 이 옵션이 등장하게 된 것은 "False"라는 값을 설정하기 위해서인데, 이에 대해서는 다음과 같이 언급하고 있습니다.

You can configure the Windows Process Activation Service (WAS) to automatically start an application pool when the application pool is created or when IIS is started. If you disable automatic startup of an application pool, you must start the application pool manually. Disabling automatic startup is useful when you want to make configuration or content changes to an application in the application pool before the application pool starts.

ImportantImportant

When an application pool is not started, requests made to applications in the application pool will return an HTTP 503-Service Unavailable error.


즉, 서비스를 구동시키기 전에 이거저거 설정해야 할 것이 있다면 "Start Automatically" == "False"로 해놓은 상태에서 할 수 있다는 의미가 있는 것입니다.

어찌 보면, IIS 7까지 이 옵션은 그다지 큰 의미가 없었지만 IIS 7.5부터 새롭게 생긴 "Start Mode"에서 상황이 바뀝니다.




"Start Mode" 설정은 AppPool 설정창에서 "Start Automatically" 옵션 바로 아래에 있습니다.

apppool_start_2.png

기본값은 OnDemand이지만, "AlwaysRunning"으로 설정한 경우 IIS 7.5는 Recycle 후에 w3wp.exe를 곧바로 다시 실행을 합니다. 즉, Recycling은 그대로 되는 장점을 유지하면서 첫 번째 사용자의 초기 로딩 시간은 줄일 수 있게 됩니다. 참고로, 이 정보는 "%WINDIR%\system32\inetsrv\config\applicationHost.config" 파일의 applicationPools 노드 내에 담겨 있습니다.

...[생략]...
<system.applicationHost>
...[생략]...

        <applicationPools>
...[생략]...
            <add name="AutoSite" autoStart="true" enable32BitAppOnWin64="true" 
                    managedRuntimeVersion="v4.0" startMode="AlwaysRunning" />
        </applicationPools>
...[생략]...
</system.applicationHost>
...[생략]...


그런데, .NET 웹 애플리케이션인 경우 "AlwaysRunning"은 그다지 큰 장점이 있는 것은 아닙니다. 아래 그림을 보시면,

apppool_start_3.png

.NET 웹 애플리케이션을 운영하다가 idle 시간이 20분 지나면 위의 그림에서처럼 새로운 w3wp.exe 프로세스가 실행됩니다. 그런데 아쉽게도 Working Set이 10MB 가량 나옵니다. 즉, 최소한의 native 코드만 올라왔을 뿐 CLR 쪽은 전혀 초기화가 안된 것입니다. 대부분의 시간이 .NET 어셈블리에 대한 JIT 컴파일 시간으로 소비되기 때문에 이렇게 되면 결과적으로 첫 번째 사용자의 기다림을 줄이는 데는 별로 도움이 되지 않습니다.

부가적으로 IIS 7.5에는 .NET 초기화를 위해 이에 대한 확장으로 "Auto Start Provider"라는 것이 추가되었습니다.

Writing an IIS 7.5 Auto Start Provider
; https://azurecloudai.blog/2011/05/11/writing-an-iis-7-5-auto-start-provider/

즉, IProcessHostPreloadClient 인터페이스를 구현한 닷넷 모듈을 사용자가 제공하면 IIS 7.5는 최초 w3wp.exe가 시작되면 configuration 파일에 지정된 닷넷 모듈을 로드해서 IProcessHostPreloadClient.Preload 메소드를 호출하게 됩니다. 어디... 실제로 한번 구현해 볼까요? ^^

간단하게 Library 유형의 프로젝트를 하나 만들고 System.Web 어셈블리를 참조한 후 다음과 같이 간단하게 인터페이스를 구현합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Hosting;

namespace PreloadWarmup
{
    public class WarmUp : IProcessHostPreloadClient
    {
        public void Preload(string[] parameters)
        {
        }
    }
}

위의 코드를 빌드한 어셈블리명이 PreloadWarmup.dll인 경우, 이것을 배포하는 방법은 2가지로 1) GAC에 설치하거나 2) 대상이 되는 웹 애플리케이션 프로젝트에서 참조를 하는 것입니다. (대개의 경우, 간편하게 참조를 하겠지요. ^^)

이후, 이 모듈을 코드를 통해 config 파일에 반영하려면 "Writing an IIS 7.5 Auto Start Provider" 글의 내용을 참조하고, 수작업으로 변경하려면 "%WINDIR%\system32\inetsrv\config\applicationHost.config" 파일을 열어서 pre-load를 적용하려는 웹 사이트를 <sites> 노드 내에서 찾아내 serviceAutoStartEnabled, serviceAutoStartProvider를 다음과 같이 설정해 줍니다.

...[생략]...
    <sites>
        ...[생략]...
        <site name="AutoSite" id="5">
                <application path="/" applicationPool="AutoSite" 
                    serviceAutoStartEnabled="true" 
                    serviceAutoStartProvider="PreWarmupApp">
                    <virtualDirectory path="/" physicalPath="D:\WebApplication1\WebApplication1" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation="*:8075:" />
                </bindings>
        </site>
        ...[생략]...
    </sites>
...[생략]...

주의할 것은 반드시 serviceAutoStartProvider == "PreWarmupApp"을 지정했으면 그에 해당하는 모듈이 로드되어야 합니다. 그렇지 않으면 웹 애플리케이션 서비스가 되지 않습니다. (이 부분이 좀 아쉽습니다. 일반적으로는 StartProvider가 서비스 자체에 크게 영향이 없는 코드일 텐데 원래의 애플리케이션 동작을 방해한다는 것은 좀 심한 것 같습니다.)

어쨌든, 우리가 만든 WarmUp 클래스를 로드하려면 serviceAutoStartProvider에 지정한 PreWarmupApp라는 이름과 이전에 우리가 만들어 두었던 PreloadWarmup.dll 어셈블리를 연결해야 하는데요. <system.applicationHost /> 내에 <serviceAutoStartProviders /> 노드가 이미 정의되어 있는지 살펴보고 없다면 새로 추가하고, 있다면 그 노드의 하위에 <add />로 다음과 같이 등록해 줍니다.

    <system.applicationHost>
        <sites>
            ...[생략]...
        </sites>

        ...[생략]...
        <serviceAutoStartProviders>
            <add name="PreWarmupApp" 
                type="PreloadWarmup.WarmUp, PreloadWarmup" />
        </serviceAutoStartProviders>
        ...[생략]...
    </system.applicationHost>

위에서는 PreloadWarmup.dll을 웹 애플리케이션에 같이 배포하기 때문에 상관없지만, 만약 GAC에 배포하는 경우라면 name을 FQDN 방식으로 넣어주어야 합니다. (예: PreloadWarmup.WarmUp, PreloadWarmup, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc862a9be44088eb)

이제부터는, w3wp.exe 프로세스는 recycle 후 항상 시작되고, 우리가 만든 PreloadWarmup.WarmUp 클래스의 Preload 메소드를 호출합니다.

(Preload 메소드는 여러분들이 어떻게 구현하느냐에 달려 있습니다.)




참고로, 대개의 경우 preload 기능을 구현하는 것은 특정 URL을 미리 호출해 두도록 하는 목적일 텐데요. 그래서 이를 일반화한 "Application Initialization Module"이 제공되고 있습니다.

Application Initialization Module for IIS 7.5
; http://www.iis.net/downloads/microsoft/application-initialization

그리고 윈도우 서버 2012의 IIS 8에서는 이것이 아예 IIS에 내장되었습니다.

IIS 8.0 Application Initialization 
; http://www.iis.net/learn/get-started/whats-new-in-iis-8/iis-80-application-initialization

이에 대한 사용법은 위의 문서를 보시면 쉽게 알 수 있으니 생략합니다. (나중에 시간나면 실습 과정을 한번 써보겠습니다. ^^)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 12/22/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2022-05-23 10시49분
정성태

... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...
NoWriterDateCnt.TitleFile(s)
12111정성태1/12/202020617디버깅 기술: 155. C# - KernelMemoryIO 드라이버를 이용해 실행 프로그램을 숨기는 방법(DKOM: Direct Kernel Object Modification) [16]파일 다운로드1
12110정성태1/11/202019983디버깅 기술: 154. Patch Guard로 인해 블루 스크린(BSOD)가 발생하는 사례 [5]파일 다운로드1
12109정성태1/10/202016666오류 유형: 588. Driver 프로젝트 빌드 오류 - Inf2Cat error -2: "Inf2Cat, signability test failed."
12108정성태1/10/202017488오류 유형: 587. Kernel Driver 시작 시 127(The specified procedure could not be found.) 오류 메시지 발생
12107정성태1/10/202018681.NET Framework: 877. C# - 프로세스의 모든 핸들을 열람 - 두 번째 이야기
12106정성태1/8/202019695VC++: 136. C++ - OSR Driver Loader와 같은 Legacy 커널 드라이버 설치 프로그램 제작 [1]
12105정성태1/8/202018199디버깅 기술: 153. C# - PEB를 조작해 로드된 DLL을 숨기는 방법
12104정성태1/7/202019440DDK: 9. 커널 메모리를 읽고 쓰는 NT Legacy driver와 C# 클라이언트 프로그램 [4]
12103정성태1/7/202022559DDK: 8. Visual Studio 2019 + WDK Legacy Driver 제작- Hello World 예제 [1]파일 다운로드2
12102정성태1/6/202018842디버깅 기술: 152. User 권한(Ring 3)의 프로그램에서 _ETHREAD 주소(및 커널 메모리를 읽을 수 있다면 _EPROCESS 주소) 구하는 방법
12101정성태1/5/202019219.NET Framework: 876. C# - PEB(Process Environment Block)를 통해 로드된 모듈 목록 열람
12100정성태1/3/202016631.NET Framework: 875. .NET 3.5 이하에서 IntPtr.Add 사용
12099정성태1/3/202019483디버깅 기술: 151. Windows 10 - Process Explorer로 확인한 Handle 정보를 windbg에서 조회 [1]
12098정성태1/2/202019293.NET Framework: 874. C# - 커널 구조체의 Offset 값을 하드 코딩하지 않고 사용하는 방법 [3]
12097정성태1/2/202017397디버깅 기술: 150. windbg - Wow64, x86, x64에서의 커널 구조체(예: TEB) 구조체 확인
12096정성태12/30/201919982디버깅 기술: 149. C# - DbgEng.dll을 이용한 간단한 디버거 제작 [1]
12095정성태12/27/201921724VC++: 135. C++ - string_view의 동작 방식
12094정성태12/26/201919402.NET Framework: 873. C# - 코드를 통해 PDB 심벌 파일 다운로드 방법
12093정성태12/26/201919005.NET Framework: 872. C# - 로딩된 Native DLL의 export 함수 목록 출력파일 다운로드1
12092정성태12/25/201917699디버깅 기술: 148. cdb.exe를 이용해 (ntdll.dll 등에 정의된) 커널 구조체 출력하는 방법
12091정성태12/25/201920072디버깅 기술: 147. pdb 파일을 다운로드하기 위한 symchk.exe 실행에 필요한 최소 파일 [1]
12090정성태12/24/201920142.NET Framework: 871. .NET AnyCPU로 빌드된 PE 헤더의 로딩 전/후 차이점 [1]파일 다운로드1
12089정성태12/23/201919091디버깅 기술: 146. gflags와 _CrtIsMemoryBlock을 이용한 Heap 메모리 손상 여부 체크
12088정성태12/23/201918064Linux: 28. Linux - 윈도우의 "Run as different user" 기능을 shell에서 실행하는 방법
12087정성태12/21/201918564디버깅 기술: 145. windbg/sos - Dictionary의 entries 배열 내용을 모두 덤프하는 방법 (do_hashtable.py) [1]
12086정성태12/20/201921095디버깅 기술: 144. windbg - Marshal.FreeHGlobal에서 발생한 덤프 분석 사례
... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...