Деплой ASP.NET додатків за допомогою символічних посилань

Всім привіт.
Всі ми трохи нервуємо при установці нового релізу на прод. Існує багато різних технологій дозволяють нам полегшити цей процес і зробити його трохи менше знервованим. Одна з таких технологій, яку вже досить давно облюбували UNIX-інженери, це використання символічних посилань, що дозволяє звести до мінімуму час накатки релізу і відкатки на попередній реліз якщо «щось пішло не так»©. Цей механізм так само присутній і в Windows, проте чомусь активно не використовується. А даремно. Дана стаття покликана виправити це непорозуміння і зробити процес накатки релізу більш приємним.


Кадр з х/ф «Джентльмени удачі»

Цю технологію в загальному можна застосовувати не тільки до ASP.NET-додатків, але і до будь-яких деплоям (наприклад, windows-сервіси). Тут ASP.NET розгорнуте на IIS використовується як приклад, тому що є декілька граблів на які довелося наступити перш ніж ця ідея злетіла. З іншими додатками має бути простіше.
Для початку опишемо в чому власне полягає проблема. Зазвичай файли web-додатки на IIS-е розташовуються в папці
c:\inetpub\wwwroot\{appName}
і кожен раз при деплое (наприклад за допомогою msdeploy) ми перезатираем старі файли новими. Це чудово працює до тих пір, поки у нас набір файлів від релізу до релізу не змінюється, а змінюється лише їх вміст. Якщо ж у наступному релізі, наприклад, одна з бібліотек замінюється на інший (з іншим ім'ям), то при деплое з'являється нова бібліотека, а стара не віддаляється, що часто призводить до епік фейлам. Знову ж, якщо раптом у вас щось не запрацювало і ви захотіли швидко відкотитися на попередній реліз вас може чекати черговий провал, так як не затрутся файли з нового релізу. Символьні посилання елегантно вирішують цю проблему.

Опишемо конкретний приклад. Створимо на диску с: папку Releases і в неї складемо релізи додатки:
C:\Releases\1.1\WebApp
C:\Releases\1.2\WebApp

А в папці
c:\inetpub\wwwroot\
створимо символічне посилання WebApp, яка буде дивитися на теку
c:\Releases\1.1\WebApp
і яку в будь-який момент можна буде перемикати на
c:\Releases\1.2\WebApp
і назад.

Варто відразу попередити, що ваш додаток має відповідати декільком параметрам (які прекрасно описані в 12 фактори
  1. Папка з релізом повинна містити весь необхідний набір бібліотек та файлів що б додаток було повністю працездатним. Ніяких явних посилань.
  2. Ваші релізи повинні бути сумісні з зовнішніми ресурсами (наприклад з базою даних). Тобто якщо ви внесли зміни у БД, то додаток повинен залишатися працездатним як в новому, так і в старому релізі.
Отже, для створення символічного посилання в Windows використовується команда mklink. У нашому випадку з параметром /D, що говорить про те, що це посилання на каталог.

mklink /D c:\inetpub\wwwroot\WebApp c:\Releases\1.1\WebApp

Власне все. Ви можете переконатися в папці wwwroot з'явилася директорія WebApp зі спеціальним значком, і якщо ви клікніть двічі на неї то опинитеся в папці релізу 1.1. Залишилося перетворити цю папку в IIS-е в Web-application, натиснувши на неї правою кнопкою і виберіть відповідний пункт. Зайдіть в браузер і переконайтеся, що все працює.
З «перемиканням» релізу виникають деякі труднощі. Справа в тому, що ASP.NET-додатки при першому запуску компілюються і записуються в кеш. Надалі IIS дивиться на зміни у файлі web.config і якщо вони відбуваються, то «пересобирает» додаток. У випадку з символьними посиланнями IIS не бачить цих змін, тому якщо ви просто перемкніть символічне посилання на нову директорію, то нічого не станеться, буде працювати старий реліз. Для того що б все запрацювало потрібно очистити кеш. Гугл рекомендує робити це командою iisreset, а це призводить до перезапуску сервісу W3SVC, що для нас занадто радикально — ми ж не хочемо що б перезапускались інші додатки на цьому сервері. Благо IIS дозволяє перезапускати не весь сервіс цілком, а тільки сам сайт або його пул. Відразу скажу — досвідченим шляхом було встановлено, що перезапуск сайту чомусь працює вкрай нестабільно. Він то пересобирает додаток, то ні, то взагалі постає в невідому розставивши ноги. А ось перезапуск пулу, дуже навіть добре працює.
І ще один нюанс. Команда mklink не дозволяє «перемикати» посилання. Тому спочатку її потрібно видаляти командою rmdir, а потім створювати заново, але вже з іншим шляхом. Отже, для того що б перемкнути посилання потрібно виконати наступний порядок дій:
  1. Зупинити пул;
  2. Видалити символічне посилання;
  3. Створити посилання з новим шляхом;
  4. Запустити пул.


C:\> C:\Windows\System32\inetsrv\appcmd stop apppool DefaultAppPool
C:\> rmdir c:\inetpub\wwwroot\WebApp
C:\> mklink /D c:\inetpub\wwwroot\WebApp c:\Releases\1.2\WebApp
C:\> C:\Windows\System32\inetsrv\appcmd start apppool DefaultAppPool


Такий підхід дає ще одну смачну булочку. Приміром, на IIS ви можете підняти ще один сайт, припустимо на порту 8080. Потім створити для нього символічне посилання з вашим новим релізом. Таким чином у вас буде старий реліз на 80, а на новий 8080. Тепер ви можете в спокійній обстановці перевірити працездатність нового релізу, а потім просто перемкнути ваш основний сайт (на 80 порту) на нову папку.

У висновку хочу привести скрипт PowerShell, який автоматизує цей процес:
Текст скрипта
param([string]$Release=$null,[string]$AppName=$null,[string]$AppPath="C:\inetpub\wwwroot\",[string]$ReleasesPath="C:\Releases\",[string]$PoolName="DefaultAppPool");
if ($Release)
{
if($AppName)
{
Write-Output "Старт!";
Write-Output "Зупиняємо пул...";
cmd /c "C:\Windows\System32\inetsrv\appcmd stop apppool $PoolName"; 
Write-Output "Оновлення додатка $AppName.";
$link = $AppPath+$AppName;
Write-Output "Link: $link";
$target=$ReleasesPath+$Release+"\"+$AppName;
Write-Output "Target: $target";
if(Test-Path $target)
{
# якщо символічна посилання існує, то видаляємо її
if (Test-Path $link)
{
cmd /c "rmdir $link"
Write-Output "Видалили директорію $link";
};
# Створюємо символічне посилання
cmd /c "mklink /D $link $target" 
}
else
{
Write-Error "Не знайдений шлях: $traget";
}
Write-Output "Запускаємо пул..."
cmd /c "C:\Windows\System32\inetsrv\appcmd start apppool $PoolName";
Write-Output "Ok!" 
}
else
{
Write-Error "Не заданий параметр AppName";
}
}
else
{
Write-Error "Не заданий параметр Release!"
}


В якості параметрів туди передаються:
  • Release — назва папки з релізом;
  • AppName — назва додатка;
  • AppPath — шлях до програми (де знаходиться символічне посилання);
  • ReleasesPath — шлях де лежать релізи;
  • PoolName — ім'я пулу.


І ще…
PowerShell 5.0 тепер підтримує роботу з символьними посиланнями. Так що якщо ви встановите п'яту версію і powershell-модуль управління IIS-ом (
PS> Import-Module WebAdministration
), то можна обійтися взагалі без команд cmd, зробивши все на чистому powershell.

Текст скрипта Powershell 5.0
param([string]$Release=$null,[string]$AppName=$null,[string]$AppPath="C:\inetpub\wwwroot\",[string]$ReleasesPath="C:\Releases\",[string]$PoolName="DefaultAppPool");
if ($Release)
{
if($AppName)
{
Write-Output "Старт!";
Write-Output "Зупиняємо пул $PoolName...";
Stop-WebAppPool $PoolName -Passthru;
Write-Output "Оновлюємо додатки $AppName.";
$link = $AppPath+$AppName;
Write-Output "Link: $link";
$target=$ReleasesPath+$Release+"\"+$AppName;
Write-Output "Target: $target";
if(Test-Path $target)
{
#створюємо посилання
New-Item -ItemType SymbolicLink -Path $AppPath -Name $AppName -Value $target -Force
}
else
{
Write-Error "Не знайдений шлях: $traget";
}
Write-Output "Запускаємо пул..."
Start-Sleep 3; #потрібно трохи почекати перед запуском :)
Start-WebAppPool $PoolName -Passthru;
Write-Output "Ok!" 
}
else
{
Write-Error "Не заданий параметр AppName";
}
}
else
{
Write-Error "Не заданий параметр Release!"
}



Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.