Представляем первую из пяти статей, посвященных работе с ASP.NET Core: руководство по развертыванию приложения ASP.NET Core на Nano Server со службами IIS.
Nano Server — это вариант установки Windows Server 2016, который имеет более компактный размер и более широкие возможности обслуживания и обеспечения безопасности по сравнению с Server Core и полным вариантом установки. Более подробная информация приведена в официальной документации по варианту установки Nano Server. Существует три способа начать с ним работу:
1. Загрузить ISO-файл Windows Server 2016 Technical Preview 5 и создать образ Nano Server.
2. Загрузить Nano Server VHD для разработчиков.
3. Создать виртуальную машину в Azure, используя образ Nano Server из Azure Gallery. Если у вас нет учетной записи Azure, вы можете создать бесплатную учетную запись с 30-дневным пробным периодом.
В данном материале используется заранее созданный Nano Server VHD для разработчиков из Windows Server Technical Preview 5.
Далее вам понадобится созданное и опубликованное 64-битное приложение ASP.NET Core.
Создайте новую виртуальную машину Hyper-V, используя предварительно загруженный файл VHD. Прежде чем войти в систему, необходимо задать пароль администратора. Для этого нужно нажать клавишу F11 в консоли виртуальной машины.
После создания пароля Nano Server может управляться удаленно при помощи PowerShell.
Откройте окно PowerShell с повышенными привилегиями, чтобы добавить удаленный экземпляр Nano Server в список
Примечание: замените переменную
После добавления экземпляра Nano Server в список
В случае успешного подключения появится командная строка следующего вида:
Создайте каталог общего доступа в экземпляре Nano Server, чтобы скопировать в него опубликованное приложение. В удаленном сеансе выполните следующие команды:
После выполнения указанных команд вы сможете открыть каталог общего доступа, введя адрес
Выполните следующие команды в удаленном сеансе, чтобы открыть порт в брандмауэре:
Добавьте поставщика
Выполните следующие команды в PowerShell:
После установки компонента
Чтобы быстро проверить, корректно ли установлены службы IIS, перейдите по адресу
Модуль ASP.NET Core — это модуль IIS 7.5+, который отвечает за управление работой прослушивателей (listeners) ASP.NET Core HTTP и передачу запросов к процессам, которыми он управляет. На данный момент установка модуля ASP.NET Core для IIS осуществляется вручную. Потребуется установить пакет .NET Core Windows Server Hosting на устройство с обычной операционной системой (не Nano Server). После установки пакета на компьютер с обычной операционной системой необходимо скопировать следующие файлы в каталог общего доступа, который был создан ранее.
На компьютере с обычной операционной системой (не Nano Server) выполните следующие команды копирования:
На виртуальной машине Nano Server необходимо скопировать следующие файлы из каталога общего доступа, который был создан ранее, в соответствующее расположение. Выполните следующие команды копирования:
Выполните следующий скрипт в удаленном сеансе:
Примечание: удалите файлы
Если вы опубликовали переносимое приложение, платформа .NET Core должна быть установлена на целевом компьютере. Выполните следующие скрипты в удаленном сеансе Powershell, чтобы установить .NET Framework на виртуальной машине Nano Server:
Скопируйте опубликованное приложение в каталог общего доступа. Возможно, потребуется внести изменения в файл
Пример файла
Выполните следующие команды в удаленной сессии, чтобы создать новый веб-сайт в IIS для опубликованного приложения. В этом скрипте для упрощения используется
Если используется Nano Server Technical Preview 5 c .NET Core CLI, необходимо скопировать файлы DLL из каталога
Если используется команда
Если система Nano Server Technical Preview 5 была обновлена или изменена, повторите данную процедуру, так как файлы DLL также могли быть обновлены.
Опубликованное веб-приложение должно быть доступно в браузере по адресу
В данном материале используется предварительный выпуск варианта установки Nano Server, который доступен в Windows Server Technical Preview 5. Программное обеспечение на виртуальном образе жесткого диска может использоваться только для целей внутренней демонстрации и тестирования. Данное программное обеспечение не предназначено для использования в производственной среде. Дату окончания действия ознакомительной версии можно узнать здесь.
Введение
Nano Server — это вариант установки Windows Server 2016, который имеет более компактный размер и более широкие возможности обслуживания и обеспечения безопасности по сравнению с Server Core и полным вариантом установки. Более подробная информация приведена в официальной документации по варианту установки Nano Server. Существует три способа начать с ним работу:
1. Загрузить ISO-файл Windows Server 2016 Technical Preview 5 и создать образ Nano Server.
2. Загрузить Nano Server VHD для разработчиков.
3. Создать виртуальную машину в Azure, используя образ Nano Server из Azure Gallery. Если у вас нет учетной записи Azure, вы можете создать бесплатную учетную запись с 30-дневным пробным периодом.
В данном материале используется заранее созданный Nano Server VHD для разработчиков из Windows Server Technical Preview 5.
Далее вам понадобится созданное и опубликованное 64-битное приложение ASP.NET Core.
Подготовка экземпляра Nano Server
Создайте новую виртуальную машину Hyper-V, используя предварительно загруженный файл VHD. Прежде чем войти в систему, необходимо задать пароль администратора. Для этого нужно нажать клавишу F11 в консоли виртуальной машины.
После создания пароля Nano Server может управляться удаленно при помощи PowerShell.
Удаленное подключение к экземпляру Nano Server при помощи PowerShell
Откройте окно PowerShell с повышенными привилегиями, чтобы добавить удаленный экземпляр Nano Server в список
TrustedHosts
.$nanoServerIpAddress = "10.83.181.14"
Set-Item WSMan:\localhost\Client\TrustedHosts "$nanoServerIpAddress" -Concatenate -Force
Примечание: замените переменную
$nanoServerIpAddress
на используемый IP-адрес.После добавления экземпляра Nano Server в список
TrustedHosts
выполните удаленное подключение к нему при помощи PowerShell.$nanoServerSession = New-PSSession -ComputerName $nanoServerIpAddress -Credential ~\Administrator
Enter-PSSession $nanoServerSession
В случае успешного подключения появится командная строка следующего вида:
[10.83.181.14]: PS C:\Users\Administrator\Documents>
Создание каталога общего доступа
Создайте каталог общего доступа в экземпляре Nano Server, чтобы скопировать в него опубликованное приложение. В удаленном сеансе выполните следующие команды:
mkdir C:\PublishedApps\AspNetCoreSampleForNano
netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=yes
net share AspNetCoreSampleForNano=c:\PublishedApps\AspNetCoreSampleForNano /GRANT:EVERYONE`,FULL
После выполнения указанных команд вы сможете открыть каталог общего доступа, введя адрес
\\<nanoserver-ip-address>\AspNetCoreSampleForNano
в проводнике на хост-компьютере.Открытие порта в брандмауэре
Выполните следующие команды в удаленном сеансе, чтобы открыть порт в брандмауэре:
New-NetFirewallRule -Name "AspNet5 IIS" -DisplayName "Allow HTTP on TCP/8000" -Protocol TCP -LocalPort 8000 -Action Allow -Enabled True
Установка IIS
Добавьте поставщика
NanoServerPackage
, выбрав его в коллекции PowerShell. После установки и импорта поставщика появится возможность устанавливать пакеты Windows.Выполните следующие команды в PowerShell:
Install-PackageProvider NanoServerPackage
Import-PackageProvider NanoServerPackage
Install-NanoServerPackage -Name Microsoft-NanoServer-Storage-Package
Install-NanoServerPackage -Name Microsoft-NanoServer-IIS-Package
После установки компонента
>Microsoft-NanoServer-Storage-Package
требуется перезагрузка. Это временная процедура и она не будет нужна в будущем.Чтобы быстро проверить, корректно ли установлены службы IIS, перейдите по адресу
http://<nanoserver-ip-address>/
— должна отобразиться стартовая страница. При установке служб IIS создается веб-сайт по умолчанию под названием Default Web Site
, для которого используется порт 80.Установка модуля ASP.NET Core (ANCM)
Модуль ASP.NET Core — это модуль IIS 7.5+, который отвечает за управление работой прослушивателей (listeners) ASP.NET Core HTTP и передачу запросов к процессам, которыми он управляет. На данный момент установка модуля ASP.NET Core для IIS осуществляется вручную. Потребуется установить пакет .NET Core Windows Server Hosting на устройство с обычной операционной системой (не Nano Server). После установки пакета на компьютер с обычной операционной системой необходимо скопировать следующие файлы в каталог общего доступа, который был создан ранее.
На компьютере с обычной операционной системой (не Nano Server) выполните следующие команды копирования:
copy C:\windows\system32\inetsrv\aspnetcore.dll ``\\<nanoserver-ip-address>\AspNetCoreSampleForNano``
copy C:\windows\system32\inetsrv\config\schema\aspnetcore_schema.xml ``\\<nanoserver-ip-address>\AspNetCoreSampleForNano``
На виртуальной машине Nano Server необходимо скопировать следующие файлы из каталога общего доступа, который был создан ранее, в соответствующее расположение. Выполните следующие команды копирования:
copy C:\PublishedApps\AspNetCoreSampleForNano\aspnetcore.dll C:\windows\system32\inetsrvcopy C:\PublishedApps\AspNetCoreSampleForNano\aspnetcore_schema.xml C:\windows\system32\inetsrv\config\schema
Выполните следующий скрипт в удаленном сеансе:
# Backup existing applicationHost.config
copy C:\Windows\System32\inetsrv\config\applicationHost.config C:\Windows\System32\inetsrv\config\applicationHost_BeforeInstallingANCM.config
Import-Module IISAdministration
# Initialize variables
$aspNetCoreHandlerFilePath="C:\windows\system32\inetsrv\aspnetcore.dll"
Reset-IISServerManager -confirm:$false
$sm = Get-IISServerManager
# Add AppSettings section
$sm.GetApplicationHostConfiguration().RootSectionGroup.Sections.Add("appSettings")
# Set Allow for handlers section
$appHostconfig = $sm.GetApplicationHostConfiguration()
$section = $appHostconfig.GetSection("system.webServer/handlers")
$section.OverrideMode="Allow"
# Add aspNetCore section to system.webServer
$sectionaspNetCore = $appHostConfig.RootSectionGroup.SectionGroups["system.webServer"].Sections.Add("aspNetCore")
$sectionaspNetCore.OverrideModeDefault = "Allow"
$sm.CommitChanges()
# Configure globalModule
Reset-IISServerManager -confirm:$false
$globalModules = Get-IISConfigSection "system.webServer/globalModules" | Get-IISConfigCollection
New-IISConfigCollectionElement $globalModules -ConfigAttribute @{"name"="AspNetCoreModule";"image"=$aspNetCoreHandlerFilePath}
# Configure module
$modules = Get-IISConfigSection "system.webServer/modules" | Get-IISConfigCollection
New-IISConfigCollectionElement $modules -ConfigAttribute @{"name"="AspNetCoreModule"}
# Backup existing applicationHost.config
copy C:\Windows\System32\inetsrv\config\applicationHost.config C:\Windows\System32\inetsrv\config\applicationHost_AfterInstallingANCM.config
Примечание: удалите файлы
aspnetcore.dll
и aspnetcore_schema.xml
из каталога общего доступа после завершения предыдущего шага.Установка .NET Core Framework
Если вы опубликовали переносимое приложение, платформа .NET Core должна быть установлена на целевом компьютере. Выполните следующие скрипты в удаленном сеансе Powershell, чтобы установить .NET Framework на виртуальной машине Nano Server:
$SourcePath = "https://go.microsoft.com/fwlink/?LinkID=809115"
$DestinationPath = "C:\dotnet"
$EditionId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name 'EditionID').EditionId
if (($EditionId -eq "ServerStandardNano") -or
($EditionId -eq "ServerDataCenterNano") -or
($EditionId -eq "NanoServer") -or
($EditionId -eq "ServerTuva")) {
$TempPath = [System.IO.Path]::GetTempFileName()
if (($SourcePath -as [System.URI]).AbsoluteURI -ne $null)
{
$handler = New-Object System.Net.Http.HttpClientHandler
$client = New-Object System.Net.Http.HttpClient($handler)
$client.Timeout = New-Object System.TimeSpan(0, 30, 0)
$cancelTokenSource = [System.Threading.CancellationTokenSource]::new()
$responseMsg = $client.GetAsync([System.Uri]::new($SourcePath), $cancelTokenSource.Token)
$responseMsg.Wait()
if (!$responseMsg.IsCanceled)
{
$response = $responseMsg.Result
if ($response.IsSuccessStatusCode)
{
$downloadedFileStream = [System.IO.FileStream]::new($TempPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
$copyStreamOp = $response.Content.CopyToAsync($downloadedFileStream)
$copyStreamOp.Wait()
$downloadedFileStream.Close()
if ($copyStreamOp.Exception -ne $null)
{
throw $copyStreamOp.Exception
}
}
}
}
else
{
throw "Cannot copy from $SourcePath"
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($TempPath, $DestinationPath)
Remove-Item $Temp</code>
Path
}
Публикация приложения
Скопируйте опубликованное приложение в каталог общего доступа. Возможно, потребуется внести изменения в файл
web.config
, чтобы указать каталог, в который извлечен файл dotnet.exe
. Другой способ — скопировать файл dotnet.exe
в тот же каталог.Пример файла
web.config
в ситуации, когда файл dotnet.exe
не скопирован в тот же каталог:<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="C:\dotnet\dotnet.exe" arguments=".\AspNetCoreSampleForNano.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true" />
</system.webServer>
</configuration>
Выполните следующие команды в удаленной сессии, чтобы создать новый веб-сайт в IIS для опубликованного приложения. В этом скрипте для упрощения используется
DefaultAppPool
. Более подробная информация о работе с пулом приложений приведена в статье Application Pools.Import-module IISAdministration
New-IISSite -Name "AspNetCore" -PhysicalPath c:\PublishedApps\AspNetCoreSampleForNano -BindingInformation "*:8000:"
Известная проблема в работе .NET Core CLI в Nano Server и способ ее обхода
Если используется Nano Server Technical Preview 5 c .NET Core CLI, необходимо скопировать файлы DLL из каталога
c:\windows\system32\forwarders
в каталог c:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\
и в каталог двоичных файлов .NET Core c:\dotnet
(в данном примере). Это вызвано ошибкой, которая устранена в более новых версиях.Если используется команда
dotnet publish
, скопируйте также файлы DLL из каталога c:\windows\system32\forwarders
в каталог публикации.Если система Nano Server Technical Preview 5 была обновлена или изменена, повторите данную процедуру, так как файлы DLL также могли быть обновлены.
Запуск приложения
Опубликованное веб-приложение должно быть доступно в браузере по адресу
http://<nanoserver-ip-address>:8000
. Если ведение журнала сконфигурировано так, как указано в разделе Создание и перенаправление логов, все журналы доступны в каталоге C:\PublishedApps\AspNetCoreSampleForNano\logs
.В данном материале используется предварительный выпуск варианта установки Nano Server, который доступен в Windows Server Technical Preview 5. Программное обеспечение на виртуальном образе жесткого диска может использоваться только для целей внутренней демонстрации и тестирования. Данное программное обеспечение не предназначено для использования в производственной среде. Дату окончания действия ознакомительной версии можно узнать здесь.
Поделиться с друзьями
commanderxo
Не могли бы вы рассказать поподробнее для каких сценариев предназначается Nano Server?
В ссылке на официальную документацию в начале статьи говорится об использовании в качестве хоста для Hyper-V, однако в примерах наоборот, сам Nano Server это виртуальная машина.
В чём по вашему мнению преимущества .NET Core + Nano перед классическим ASP.NET MVC (OWIN) на полновесной ОС, тем более что вы всё равно используете IIS? Какие реальные проблемы решает связка c Nano?
Некоторые недостатки у Core есть, например пока отсутствует System.Drawing, которой мы используем для масштабирования JPEG-ов. Что оправдывает цену перехода на новую технологию? Может Deployment? Но, как я понимаю, даже облегчённая версия Windows слишком велика чтоб сделать весь образ артефактом который поставляют разработчики — копирование образа займёт слишком много времен.
Одним словом, чем по вашему мнению интересен Nano Server? Грубо говоря, чем он лучше полновесной Windows с одной стороны и Docker-а с другой?
/* Сухой маркетинговый язык страниц MSDN уже читал, хочется знать мнение реально работающих с этой технологией */
usarskyy
Я не автор заметки, но попробую ответить. Основное преимущество Nano Server это его требования к ресурсам. MS вырезал из этой версии ОС практически всё. На конференции показывали, что в «голом» виде там крутится в памяти что-то около 12-15 процессов :) Процесс установки и управления сервером тоже существенно упростился. Также сменили политику установки обновлений.
Применять Nano Server стоит ИМХО в связке с Docker или с Win Server Containers (тот же Docker, только в профиль). Применение в качестве хоста Hyper-V имеет смысл, потому что ОС не отъедает полтора ГБ оперативки за «красивые глазки»
kekekeks
Надо только учитывать, что при применении ASP.NET Core в докер-контейнере IIS в этом самом контейнере нафиг не нужен.
commanderxo
То-есть область применения можно сформулировать следующим образом:
Если нужна кросс-платформенность, то надо брать Core. Главная цель — докер, который нам люб за то, что когда программист говорит «готово», то результатом является контейнер, который однозначно описывает систему во всех видах установки, от DEV до Production. Тем самым на корню устраняются проблемы вида «а у меня на компьютере всё работает». Ценой является отказ от бесплатных плюшек IIS вроде gzip-упаковки или Windows-аутентификации.
Если контейнеры нам пока не светят, например по организационным причинам, то полезность Core в нынешнем его состоянии не очевидна. Да, сегодня разработчики выдают при релизе 20 Мб DLL-ек, которые отдельная Infrastructure-команда устанавливает на заботливо оберегаемом ими Windows Server 2012, а завтра программисты будут выдавать лишь 2 Мб, а остальные 18 Мб сервер сам скачает из NuGet, но правил игры это не меняет. Сервер остаётся «pet, not cattle».
Замена тяжеловесного сервера на Nano порадует сисадминов, которым мучительно больно смотреть на расходуемые зря гигабайты, но мало что изменит для программистов.
Или я где-то ошибаюсь?
navion
Для админов оно тоже не всегда лучше.
commanderxo
Спасибо за ссылку, очень интересно.
Получается, что с точки зрения программиста (которому всё равно нельзя лазить на Production серверы) Nano Server не отличается от полновесного Windows Server — в обоих есть полноценный IIS.
А у админов теперь выбор — выучить новые cmdlet-ы и радоваться легковесным виртуальным машинам, или и дальше пользоваться Windows Server, платя, тем самым, «налог на незнание PowerShell».
Учитывая тенденцию нарезать бизнес-логику маленькими независимыми кусочками микросервисов, перспектива хостить на одном Hyper-V несколько десятков лёгких VM с Nano Server весьма заманчива.
usarskyy
Да, в общем верно. ASP.NET Core для того и создавали, чтобы быть кросплатформенными.
Ну а Docker не панацея от «а у меня на компьютере всё работает», просто проще это все задеплоить (хотя тут тоже спорный момент).
Подозреваю, это будут те же 20 Мб, только заботливо упакованы в контейнер :D
kekekeks
Так же надо понимать, что переезд со Standard на Nano живёт своей жизнью и туда отлично деплоится обычный ASP.NET. Надо только ввести одно короткое, простое и легко гуглящееся заклинание в консольку:
usarskyy
На самом деле не совсем так. Да, в 80% случаев надо смотреть на связку ASP.NET Core + Kestrel, но IIS все еще далеко впереди по «фичам» (вот тут небольшое сравнение).
IIS прожорлив, но потолок быстродействия у него довольно высокий для большинства проектов. Половина сайтов умирают сами по себе еще на половине пути к этому «потолку» :)
kekekeks
По вашей ссылке сравнивают Kestrel и WebListener. Первый работает на libuv и имеет свою реализацию HTTP протокола, второй использует драйвер http.sys. В обоих случаях IIS выступает в качестве проксирующей запросы прокладки, то есть, не нужен. Во втором он вдвойне не нужен, так как WebListener может жить с IIS на одном порту за счёт этого самого http.sys.
usarskyy
Сорри за невнимательность, не тот линк вставил. Вот тут полное сравнение.
Кроме того в IIS еще есть куча фич как dynamic compression, logging, access management, балансировка запросов между процессами и т.д. Поэтому, «проксирующая прокладка» это уж слишком сильное приуменьшение ;)
Да, все эти фичи сильно снижают мифический показатель RPS, но, как я уже писал, большинство проектов никогда не увидят этот «потолок».
UPDATE: похоже, это одна и та же таблица, просто на docs.asp.net убрали колонку IIS, так как она почти полностью дублирует WebListener.
Razaz
Тут тогда еще бы добавить Nginx и Apache ;) И я бы посмотрел, например, на wildcard subdomains… ;)
Плюс по фичам — часть из них реализована в виде Middleware:
Buffering — https://github.com/aspnet/BasicMiddleware/tree/dev/src/Microsoft.AspNetCore.Buffering
Compression — https://github.com/aspnet/BasicMiddleware/tree/dev/src/Microsoft.AspNetCore.ResponseCompression
Последний линк кстати чутка устарел:) Часть фич уже сделали. Например Connection, RequestLifetime.
usarskyy
Скоро они допилят все фичи и назовут это IIS 10 :D
KirillFormado
Но как бы Kestrel не предназначен для работы в одиночку. Подразумевается его использовать(в продакшене) только за Nginx-ом или IIS-ом. О чем и пишется в документации.
navion
Забавно, MS сами [пока] не используют Docker, а установщик даже не включен в дистрибутив Windows Server.
usarskyy
Так мне на докладе о Nano Server & Containers рассказал докладчик. Конкретно: пилят свои контейнеры, но поддержке докера «из коробки» быть (но только в новом сервере, о старых никто не говорил). Рассказывали мне в апреле, с того времени планы могли немного измениться.
Я лично не совсем понимаю зачем сейчас пилить свой контейнер, лучше бы хорошо интегрировали Docker. Но может там какие-то супер-пупер инновации, о которых нам пока ничего не рассказали :)
dobriykot
Свой совместимый с докером или несовместимый?
usarskyy
Вот этого, к сожалению, не помню
Razaz
Ну MVC(OWIN) vs Core отдельная тема. Drawing, DirectoryServices отсутствуют пока что. Но если нет таких либ в зависимостях, то MVC(OWIN) в принципе теряет смысл сейчас для новых проектов.
PS. Ну и можно зареференсить их если таргет нужный поставить. И использовать Nginx + Core на WinServer.
kekekeks
ASP.NET Core умеет работать поверх обычного .NET Framework/Mono. Соответственно, с зависимостями проблем нет.
Razaz
В PS написал ;) Надо только нужный халай махалай сделать в project.json.
commanderxo
Как раз OWIN мы уж 2 года используем со старым-добрым .NET 4.5-4.6, вместе с классическим IIS.
Началось всё с тестирования микросервисов на WEB API 2, приятно когда в две строчки можно поднять в памяти весь сервис и проверять правильность на уровне HTTP Request/Response, в том числе и правильную обработку HTTP-Headers и кодов возврата. А потом как-то незаметно переползло и в обычные веб-приложения. OWIN там не даёт особых преимуществ, но просто приятно убрать всё лишнее из global.asax, в котором отовсюду торчат уши HttpHandler (каковым и являлся ASP.NET в 2002 году).
Razaz
OWIN позволяет нормально обрабатывать веб реквест выстраивая цепочки Middlware с прогнозируемым порядком выполнения. До сих пор вспомниаю ахтунг с SessionHttpModule + WSFederationAuthenticationModule.
Новые проекты уже на Core. ИМХО это большой шаг вперед относительно OWIN(как минимум нет переключений в ASP.NET для того же MVC) и его логическое развитие. Как минимум кастомизируемый роутинг на уровне Middleware и механизм IOptions/IConfiguration. Ну и производительность совершенно другого порядка. Старые проекты уже активно готовятся к переезду.
dimaaan
Возможно это вам поможет:
https://github.com/antiufo/Shaman.System.Drawing
fedorro
Вот ещё обёртка для ImageMagick. Отлично работает под Asp.Net Core — проверено.