Azure Role 배포시 .NET Framework 3.5 설치하기

Azure Web/Worker Role은 기본적으로 .Net Framework 3.5(이하 net35)가 disable 되어있습니다.

그러므로 3.5에 의존성이 있는 라이브러리를 실행하기 위해서는 쉽게는 원격데스크톱 접속 후 제어판>윈도우 기능 추가/제거>.Net Framework 3.5를 해야 합니다.

하지만 Role은 VM에 문제가 생기거나 Guest OS 버전이 maintenance로 임의로 교체되는 경우가 있기 때문에 그 때마다 원격데스크톱에 접속하여 닷넷프레임웍 설치를 하는 것은 맞는 방법이 아닙니다.

이번 글에서는 Azure Role 배포시 net35 이미지를 다운받고 인스톨하는 과정과 스크립트를 소개합니다. 참고로 이 글은 Anu Thomas Chandy의 글을 기반으로 하고 있으며, 구현시 문제가 있거나 설명이 불충분한 부분을 수정하였습니다.

Azure Role 배포에 .NET Framework 3.5를 설치하려면 다음의 과정을 거칩니다.

  1. net35 이미지 확보
  2. Storage에 업로드
  3. Role 셋팅 수정
  4. 설치 스크립트 작성
  5. 배포 및 문제 해결

1. net35 이미지 확보

원래 net35를 설치하기 위해서는 다음의 PowerShell 스크립트 한 줄이면 됩니다.

PowerShell -Command "Install-WindowsFeature Net-Framework-Core" >> "%TEMP%StartupLog.txt"  2>&1

사실 이 한 줄만 추가할거면 글이 이렇게 길어지지도 않습니다. 하지만 이 방법은 동작은 해도 문제가 하나 있는데, 인터넷에서 패키지를 다운받아서 설치하기 때문에 네트웍 상태가 불안정할 경우 Role Startup Timeout 에러로 설치에 실패하는 경우가 있습니다. (그렇다고 합니다) 그러므로 패키지를 로컬(또는 내부네트웍) 경로 어딘가에 놓고 설치하는 방법을 사용하는 것이 안전한데, 이 경우 스크립트는 다음과 같습니다.

Install-WindowsFeature Net-Framework-Core –Source "<Path-To-NET35-Install-Files>"

닷넷 3.5 관련 파일은 윈도우 설치 이미지안에 있는데, 경로는 D:\sources\sxs를 통째로 zip으로 만드는 것입니다 (D는 제 경우 USB 이미지 드라이브입니다). 제 기준으로 윈도우8 이미지의 sxs 폴더 정보는 아래와 같습니다.

c2

이것을 net35.zip으로 압축합시다. sxs 폴더를 압축하지 말고 그 안에 디렉토리들을 모두선택한 후 압축합니다. 대략 107메가 정도가 되며, 반디집으로 보는 모습은 아래와 같습니다.

c1

2. Storage에 업로드

이 zip파일을 Azure Blob Storage에 컨테이너(폴더)를 하나 만들고 Public blob access로 업로드합니다. 업로드는 Azure 관리 프로그램을 쓰면 탐색기처럼 편하게 업로드할 수 있습니다. 저는 Azure Management Studio (유료)를 쓰는데 Azure Explorer (무료)도 있고 다른 회사 제품들도 몇 개 있습니다.

제 경우 http://portalvhd~~~433yc.blob.core.windows.net/windows-source-for-webrole/net35.zip 라고 올렸습니다. 올라간 것은 브라우저를 통해 다운이 잘 되는지도 확인하세요.

3. Role 셋팅 수정

이 zip파일을 다운받고 압축푸는데 temp 폴더를 이용할 수도 있지만, temp 폴더의 용량은 250메가로 제한되어 있습니다. 그러므로 우리는 107 메가+압축풀면 307메가 = 약 420메가는 이를 초과하므로 ‘Not enough space on disk’에러가 발생합니다. 이를 해결하기 위해서는 Role의 Local Storage를 지정하여 이곳에서 처리되도록 해야 합니다.

비주얼스튜디오의 Cloud 프로젝트에 Role을 우클릭하여 Properties를 열고, 아래 그림과 같이 설정합니다. 넉넉하게 500메가를 줍니다.

c3

4. 설치 스크립트 작성

설치 스크립트가 실행되기 위해서는 3단계가 필요한데, 하나는 PowerShell 스크립트, 이를 실행하는 cmd 스크립트, 끝으로 이 cmd를 실행하도록 지정하는 StartUp 설정입니다. StartUp 과정에서 바로 PowerShell 스크립트를 실행할 수 없기 때문에 이런 과정을 거칩니다.

먼저, PowerShell 스크립트는 아래와 같습니다. 제 Gist에서 받을 수도 있으며 installnet35.ps1 이라고 저장합니다.

# original: http://anuchandy.blogspot.kr/2014/06/automating-net-35-installation-on-azure.html
# Method that returns path to the directory holding 'installnet35.ps1' script. 
function Get-ScriptDirectory
{
  $Invocation = (Get-Variable MyInvocation -Scope 1).Value
  Split-Path $Invocation.MyCommand.Path
}

# Gets path to the local resource we reserved for manipulating the zip file.
[void]([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.WindowsAzure.ServiceRuntime"))
$localStoreRoot = ([Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::GetLocalResource("net35resource")).RootPath.TrimEnd('')

# .NET 3.5 source (in blob storage)
# Note that you can also include the .NET 3.5 source zip file in the package
$net35Source = "http://<your_blob_storage_address>.blob.core.windows.net/windows-source-for-webrole/net35.zip"

# Destination path for the zip file
$net35ZipDestination = Join-Path $localStoreRoot "net35.zip"

# Use WebClient to download the the zip file
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($net35Source, $net35ZipDestination)

# Destination path to hold the extracted files
$net35ExtractDestination = Join-Path $localStoreRoot "net35"
$pathExists = Test-Path $net35ExtractDestination
if (!$pathExists)
{
  new-item $net35ExtractDestination -itemtype directory
}

# Build command to unzip
$zipTool = (Join-Path (Get-ScriptDirectory) "7za.exe")
$unzipCommandArgs = "x " + $net35ZipDestination + " -o$net35ExtractDestination -y"

# Unzip the file
Start-Process $zipTool $unzipCommandArgs -NoNewWindow -Wait
$net35ExtractDestination = Join-Path $net35ExtractDestination "net35"

# Install .NET 3.5 using -Source option
Install-WindowsFeature NET-Framework-Core –Source $net35ExtractDestination

위 스크립트 중 your_blob_storage_address는 자신의 blob storage 주소를 적으세요.

두번째로, 이를 실행할 cmd 스크립트는 아래와 같은데, net35가 있는지 레지스트리를 검사 (관련글)한 후 없을 때만 설치를 진행하도록 했습니다. 제 Gist에서도 볼 수 있으며 intallnet35.cmd 라고 저장합시다.

@echo off

reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" /V SP

IF "%ERRORLEVEL%" EQU "0" (
    ECHO dotnet framework 3.5 is already installed >> "%TEMP%\StartupLog.txt" 2>&1
    EXIT 0
) ELSE (
    PowerShell -ExecutionPolicy Unrestricted "%~dp0..\..\Startup\installnet35.ps1" >> "%TEMP%\StartupLog.txt" 2>&1
)

주의: installnet35.cmd 파일은 절대 UTF-8로 저장하면 안됩니다. UTF-8 without BOM으로 하세요.

우리는 이 두 파일 외에 압축푸는 프로그램도 필요합니다. 왜냐하면 blob에서 다운받는게 zip 파일이라서 압축을 풀어줘야 하니까요. 압축푸는건 범용적인 7zip을 이용합시다. 7za.exe 라고 지금 보시는 컴퓨터에서 검색하면 잘하면 나올거고 안나오면 7zip 공식사이트에서 7-Zip Command Line Version을 받으면 7za.exe 라는 파일이 있습니다.

이렇게 파일 3개(installnet35.cmd, installnet35.ps1, 7za.exe)를 Role에 엮이는 프로젝트(클라우드프로젝트 아님. 제 경우 ASP.NET 프로젝트)의 Startup이라는 폴더에 넣습니다. 이 세 파일의 비주얼스튜디오 속성은 반드시 Build Action: Content, Copy to Output Directory: Copy Always로 해주세요.

최종적으로 아래의 그림과 같아집니다.

c4

마지막으로 Role의 Startup 프로세스가 실행될 때 cmd파일을 실행해주기 위한 설정은 클라우드 프로젝트의 ServiceDefinition.csdef 파일을 열고 아래 그림처럼 Startup 태그 아래 Task를 추가합니다.

제 블로그편집기가 XML 처리가 잘 안보여서 이미지로 첨부했는데 Gist에 별도로 올려놨습니다.

c6

이제 다 끝나갑니다.

5. 배포 및 문제 해결

준비는 다 끝났으니 Azure Role의 프로젝트에 우클릭한 후 Publish를 해봅니다.

성공하든 실패하든 인스턴스에 원격접속으로 들어가서 C:\Resources\temp\a3e778b39bb42.BapulServer\RoleTemp 폴더(경로는 조금씩 다를겁니다)를 보면 StartupLog.txt 파일이 있습니다.

더블클릭으로 열어보면 대략 다음과 같은 형식으로 기록됩니다.

c5

Success: True 위까지 7zip이 기록한 것이며, 그 후는 net35 인스톨 결과입니다. 에러가 있을 경우에도 로그가 기록되므로 이를 통해서 문제를 해결할 수 있을 것입니다.

만일 net35가 이미 깔려있는 Role은 installnet35.cmd의 내용에 따라 “dotnet framework 3.5 is already installed”라고만 기록될 것입니다.

사족: 제가 안해본거지만 사실 각 Role의 Windows 디렉토리에 WinSxS 폴더가 있습니다. 그런데 이게 .Net Framework 3.5가 포함되어 있는건지는 모르겠네요. 여유가 있으신 분들은 이 폴더로 먼저 시도하시면 어쩌면 될지도 모르겠습니다.

휴~끝입니다~~

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중