Hello.
We are all a little nervous when installing a new release for the prod. There are many different technologies that allow us to facilitate this process and make it a little less nervous. One of these technologies, which UNIX engineers have chosen for quite some time, is the use of
symbolic links , which allows minimizing the time for release rollback and rollback to the previous release if “something went wrong” (c). This mechanism is also present in Windows, but for some reason is not actively used. And in vain. This article is designed to correct this misunderstanding and make the process of rolling release more enjoyable.
Frame from c / f "Gentlemen of Fortune"In general, this technology can be applied not only to ASP.NET-applications, but also to any deployments (for example, windows-services). Here, ASP.NET deployed on IIS is used as an example, because there are several rakes that had to be stepped on before this idea took off. With other applications should be easier.
To begin with we will describe in what actually the problem consists. Usually, web application files on IIS are located in the
c:\inetpub\wwwroot\{appName}
and each time it is fully loaded (for example, using
msdeploy ), we will overwrite old files with new ones. This works fine as long as our set of files does not change from release to release, but only their contents change. If in the next release, for example, one of the libraries is replaced by another (with a different name), then a new library appears when the layer is added, and the old one is not deleted, which often leads to epic feils. Again, if suddenly something did not work for you and you wanted to quickly roll back to the previous release, another failure might be waiting for you, since the files from the new release will not get lost. Symbolic links elegantly solve this problem.
We describe a specific example. Let's create on the C: drive the Releases folder and add the releases of our application into it:
C:\Releases\1.1\WebApp C:\Releases\1.2\WebApp
And in the
c:\inetpub\wwwroot\
create a symbolic WebApp link that will look at the
c:\Releases\1.1\WebApp
and which can be switched to
c:\Releases\1.2\WebApp
at any time and back.
')
It should immediately warn that your application must meet several parameters (which are perfectly described in
12 factors )
- The release folder should contain all the necessary set of libraries and files for the application to be fully functional. No implicit links.
- Your releases must be compatible with external resources (for example, a database). Those. if you made changes to the database, the application must remain efficient in both the new and the old release.
So, to create a symbolic link in Windows, use the mklink command. In our case with the / D parameter, it means that this is a link to a directory.
mklink /D c:\inetpub\wwwroot\WebApp c:\Releases\1.1\WebApp
Actually everything. You can make sure that the WebApp folder with a special icon appeared in the wwwroot folder, and if you double-click it, you will find yourself in the release folder 1.1. It remains to turn this folder in IIS into a Web application by right-clicking on it and selecting the corresponding item. Go to the browser and make sure that everything works.
There are some difficulties with the “switching” release. The fact is that ASP.NET applications are first compiled and written to the cache when they are first run. In the future, IIS looks at changes in the web.config file and, if they occur, it “reassembles” the application. In the case of symbolic links, IIS does not see these changes, so if you simply switch the symbolic link to the new directory, nothing will happen, the old release will work. In order for everything to work, you need to clear the cache. Google recommends doing this with the iisreset command, and this leads to a restart of the W3SVC service, which is too radical for us - we don’t want other applications to restart on this server. The benefit of IIS allows you to restart not the entire service but only the site itself or its pool. At once I will say - by experience it was found that the restart of the site for some reason works extremely unstable. He then reassembles the application, then no, then generally gets into an unknown raskoryaku. But restarting the pool works very well.
And one more nuance. The mklink command does not allow to “switch” the link. Therefore, at first it must be deleted with the rmdir command, and then re-created but with a different path. So, in order to switch the link, you need to perform the following procedure:
- Stop pool;
- Remove symbolic link;
- Create a link with a new path;
- Run the pool.
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
This approach provides another tasty bun. For example, on IIS, you can pick up another site, say, on port 8080. Then create a symbolic link for it with your new release. Thus, you will have the old release at 80, and the new one at 8080. Now you can check the operability of the new release in a relaxed atmosphere, and then simply switch your main site (which is on port 80) to a new folder.
In conclusion, I want to give the PowerShell script that automates this process:
Script text 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!" }
As parameters, there are transmitted:
- Release - the name of the folder with the release;
- AppName - the name of the application;
- AppPath - the path to the application (where the symbolic link is located);
- ReleasesPath - the path where the releases are;
- PoolName is the name of the pool.
And further…
PowerShell 5.0 now supports
symbolic links . So if you install the fifth version and powershell IIS control module (
PS> Import-Module WebAdministration
), then you can do without the cmd commands at all by doing everything in pure powershell.
Powershell 5.0 script text 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!" }