Continuous Delivery: Setup and run
In my previous post I tried to make it clear that continuous production is for good. This post I’ll show how to prepare environment for continuous production. The idea is that you should be able to configure and test it locally. All configurations have to be part of source code under SCM. It should not depend on machine and run any environment you like. Success criteria is: pick up clean machine, do checkout, run build.bat/deploy.bat and have installed web application.
Integration and database deployment
As I said, UppercuT and RoundhousE are really nice tools for that. As soon as you follow the instruction’s you will have a build.bat
, that would be able to build up all binaries, run tests against that and put all build artifacts to package. That is pretty good for start, but we still missing “deployment” part.
As you a little bit more familiar with UppercuT, it provides good facilities for deployment as well. Basically, there are folder deployment\templates\
where you able to define your custom deployment scripts. Typical web application requires 2 scripts:
- AppDeployment.bat - for web site deployment
- DbDeployment.bat - for database deployment
There files are templates, from which script for particular environment
is generated. The environment is defined in settings
folder and include such information as deploy folder, web site name, database name, server name as number of variables. Example,
<?xml version="1.0" encoding="utf-8" ?>
<project name="Settings">
<!-- environment settings -->
<property name="environment" value="PRODUCTION" />
<!-- servers -->
<property name="server.database" value=".\SQLEXPRESS" />
<property name="web.deploy.folder" value="c:\trackyt.net\web\" />
<property name="web.site.name" value="trackyt.net" />
<property name="database.name" value="trackytdb" />
<property name="log.level" value="DEBUG" />
<property name="app.user.name" value="alexander.beletsky" />
<!-- base settings -->
<property name="project.name" value="trackyt.net" overwrite="false" />
<property name="repository.path" value="git://github.com/alexbeletsky/trackyt.net" />
<property name="folder.app.drop" value="${project.name}" overwrite="false" />
<property name="folder.database" value="db" overwrite="false" />
<!-- database deployment -->
<property name="dirs.db" value="..\${folder.database}" />
<property name="file.version" value="_BuildInfo.xml" overwrite="false" />
<property name="restore.from.path" value="..\${database.name}.bak" overwrite="false" />
</project>
In template .bat file it is possible to refer, to some particular variable, so it is possible to make those quite generic. After build, template .bat files are post-processed and actual batch is generated. The name would be like ENVIRONMENT.AppDeployment.bat
, where ENVIRONMENT is type of environment you defined.
Web site deployment script
If you do ASP.net (MVC) website in 99.9% cases you will be happy with simple XCOPY
deployment type. Basically it means, simple copy of website to defined IIS folder.
But, as soon it is continuous production deploy it means that Web Site is already running. It would not be possible to re-write some files, since they could be used by IIS. So, we need to stop the site before update. I found very good possibility for that with %windir%\system32\inetsrv\appcmd
command. After site is stopped, we just copy full content of Web folder, remove some redundant files and run site again. In batch code it would look like,
@echo off
SET DIR=%~d0%~p0%
SET web.deploy.folder="${web.deploy.folder}"
echo stopping web site..
call %windir%\system32\inetsrv\appcmd stop site ${web.site.name}
if %ERRORLEVEL% NEQ 0 goto errors
echo copy application content
rmdir /s /q %web.deploy.folder%
xcopy /E /F /H /R ..\_PublishedWebSites\Web %web.deploy.folder%
xcopy ..\build_artifacts\_BuildInfo.xml %web.deploy.folder%
if %ERRORLEVEL% NEQ 0 goto errors
echo remove redudant files
del %web.deploy.folder%*Tests*.htm*
del %web.deploy.folder%Web.Debug.config
del %web.deploy.folder%Web.Release.config
del %web.deploy.folder%*packages*
if %ERRORLEVEL% NEQ 0 goto errors
echo starting web site
%windir%\system32\inetsrv\appcmd start site ${web.site.name}
if %ERRORLEVEL% NEQ 0 goto errors
goto finish
:errors
EXIT /B %ERRORLEVEL%
:finish
Database deployment script
RoundhousE does all infrastructure work for us. All we need to create a batch file, that would be able to run during continuous production cycle. As well as AppDeployment.bat
I defined DbDeployment.bat
in deployment\templates\
folder. But before any update of database it is always good to have a backup, to be able to restore from it if something went wrong. Actually, RoundhousE should have such ability, but unfortunately I didn’t get how to use it. I’ve created my simple SQL script that is able to backup.
USE $(Database);
GO
BACKUP DATABASE $(Database)
TO DISK = 'C:\backup\$(Database).bak'
WITH FORMAT,
MEDIANAME = 'C_SQLServerBackups',
NAME = 'Full Backup of $(Database)';
GO
And corresponding batch file, that would run backup.sql
.
@echo off
if '%1' == '' goto usage
if '%2' == '' goto usage
sqlcmd -S %1 -i .\scripts\backupdb.sql -v Database = %2 -e
if %ERRORLEVEL% NEQ 0 goto errors
goto finish
:usage
echo.
echo Usage: backup.bat [server] [database]
echo [server] - server eg. mymachine\SQLEXPRESS
echo [database] - name of database to backup
echo.
EXIT /B 1
:errors
EXIT /B %ERRORLEVEL%
:finish
Both files are placed into deployment\scripts
folder. So, the DbDeployment.bat
template, would first run database backup and if it is successfull, run RoundhousE to update database.
@echo off
SET database.name="${database.name}"
SET sql.files.directory="${dirs.db}"
SET server.database="${server.database}"
SET repository.path="${repository.path}"
SET version.file="${file.version}"
SET version.xpath="//buildInfo/version"
SET environment="${environment}"
echo backup database
call .\scripts\backupdb.bat %server.database% %database.name%
if %ERRORLEVEL% NEQ 0 goto errors
echo update database
"%DIR%rh\rh.exe" /d=%database.name% /f=%sql.files.directory% /s=%server.database% /vf=%version.file% /vx=%version.xpath% /r=%repository.path% /env=%environment% --ni --simple
if %ERRORLEVEL% NEQ 0 goto errors
goto finish
:errors
EXIT /B %ERRORLEVEL%
:finish
Putting it all together
We already have build.bat
as part of UppercuT, now we need to define deploy.bat
that would do deployment of product. It would be called immediately after build.bat finished, so binaries are ready, tests passed and code_drop
folder contains all artifacts for deployment. The script is rather simple and utilize stuff we did previously.
@echo off
if '%1' == '' goto usage
SET ENV=%1
cd .\code_drop\deployment
echo Deploy database
call .\%ENV%.DbDeployment.bat
if %ERRORLEVEL% NEQ 0 goto errors
echo Deploy application
call .\%ENV%.AppDeployment.bat
if %ERRORLEVEL% NEQ 0 goto errors
goto finish
:usage
echo.
echo tracky.net deploy script
echo Usage: deploy.bat [environment]
echo [environment] - deployment environment could be STAGING or PRODUCTION
echo.
EXIT /B 1
:errors
echo Build FAILED
EXIT /B %ERRORLEVEL%
:finish
echo Build SUCCESS
Notice, it receives the parameter ENV
. It will contain type of environment for deployment. For staging environment, you should call deploy.bar STAGING
, for production deploy.bat PRODUCTION
.
Testing it out
That’s basically it. Now, you should make sure everything works as expected. Run build.bat/deploy.bat
, make sure build went with no errors, deploy.bat correctly does back up of database, updates database and update site content.
As I said on top, it is very important that configuration is part of product, part of source code. If you follow this, it will be possible to deploy application but just getting sources from SCM. This is first step of setting up your Continuous Production server.
In my previous post I tried to make it clear that continuous production is for good. This post I’ll show how to prepare environment for continuous production. The idea is that you should be able to configure and test it locally. All configurations have to be part of source code under SCM. It should not depend on machine and run any environment you like. Success criteria is: pick up clean machine, do checkout, run build.bat/deploy.bat and have installed web application.
Integration and database deployment
As I said, UppercuT and RoundhousE are really nice tools for that. As soon as you follow the instruction’s you will have a build.bat
, that would be able to build up all binaries, run tests against that and put all build artifacts to package. That is pretty good for start, but we still missing “deployment” part.
As you a little bit more familiar with UppercuT, it provides good facilities for deployment as well. Basically, there are folder deployment\templates\
where you able to define your custom deployment scripts. Typical web application requires 2 scripts:
- AppDeployment.bat - for web site deployment
- DbDeployment.bat - for database deployment
There files are templates, from which script for particular environment
is generated. The environment is defined in settings
folder and include such information as deploy folder, web site name, database name, server name as number of variables. Example,
<?xml version="1.0" encoding="utf-8" ?> <project name="Settings"> <!-- environment settings --> <property name="environment" value="PRODUCTION" /> <!-- servers --> <property name="server.database" value=".\SQLEXPRESS" /> <property name="web.deploy.folder" value="c:\trackyt.net\web\" /> <property name="web.site.name" value="trackyt.net" /> <property name="database.name" value="trackytdb" /> <property name="log.level" value="DEBUG" /> <property name="app.user.name" value="alexander.beletsky" /> <!-- base settings --> <property name="project.name" value="trackyt.net" overwrite="false" /> <property name="repository.path" value="git://github.com/alexbeletsky/trackyt.net" /> <property name="folder.app.drop" value="${project.name}" overwrite="false" /> <property name="folder.database" value="db" overwrite="false" /> <!-- database deployment --> <property name="dirs.db" value="..\${folder.database}" /> <property name="file.version" value="_BuildInfo.xml" overwrite="false" /> <property name="restore.from.path" value="..\${database.name}.bak" overwrite="false" /> </project>
In template .bat file it is possible to refer, to some particular variable, so it is possible to make those quite generic. After build, template .bat files are post-processed and actual batch is generated. The name would be like ENVIRONMENT.AppDeployment.bat
, where ENVIRONMENT is type of environment you defined.
Web site deployment script
If you do ASP.net (MVC) website in 99.9% cases you will be happy with simple XCOPY
deployment type. Basically it means, simple copy of website to defined IIS folder.
But, as soon it is continuous production deploy it means that Web Site is already running. It would not be possible to re-write some files, since they could be used by IIS. So, we need to stop the site before update. I found very good possibility for that with %windir%\system32\inetsrv\appcmd
command. After site is stopped, we just copy full content of Web folder, remove some redundant files and run site again. In batch code it would look like,
@echo off SET DIR=%~d0%~p0% SET web.deploy.folder="${web.deploy.folder}" echo stopping web site.. call %windir%\system32\inetsrv\appcmd stop site ${web.site.name} if %ERRORLEVEL% NEQ 0 goto errors echo copy application content rmdir /s /q %web.deploy.folder% xcopy /E /F /H /R ..\_PublishedWebSites\Web %web.deploy.folder% xcopy ..\build_artifacts\_BuildInfo.xml %web.deploy.folder% if %ERRORLEVEL% NEQ 0 goto errors echo remove redudant files del %web.deploy.folder%*Tests*.htm* del %web.deploy.folder%Web.Debug.config del %web.deploy.folder%Web.Release.config del %web.deploy.folder%*packages* if %ERRORLEVEL% NEQ 0 goto errors echo starting web site %windir%\system32\inetsrv\appcmd start site ${web.site.name} if %ERRORLEVEL% NEQ 0 goto errors goto finish :errors EXIT /B %ERRORLEVEL% :finish
Database deployment script
RoundhousE does all infrastructure work for us. All we need to create a batch file, that would be able to run during continuous production cycle. As well as AppDeployment.bat
I defined DbDeployment.bat
in deployment\templates\
folder. But before any update of database it is always good to have a backup, to be able to restore from it if something went wrong. Actually, RoundhousE should have such ability, but unfortunately I didn’t get how to use it. I’ve created my simple SQL script that is able to backup.
USE $(Database); GO BACKUP DATABASE $(Database) TO DISK = 'C:\backup\$(Database).bak' WITH FORMAT, MEDIANAME = 'C_SQLServerBackups', NAME = 'Full Backup of $(Database)'; GO
And corresponding batch file, that would run backup.sql
.
@echo off if '%1' == '' goto usage if '%2' == '' goto usage sqlcmd -S %1 -i .\scripts\backupdb.sql -v Database = %2 -e if %ERRORLEVEL% NEQ 0 goto errors goto finish :usage echo. echo Usage: backup.bat [server] [database] echo [server] - server eg. mymachine\SQLEXPRESS echo [database] - name of database to backup echo. EXIT /B 1 :errors EXIT /B %ERRORLEVEL% :finish
Both files are placed into deployment\scripts
folder. So, the DbDeployment.bat
template, would first run database backup and if it is successfull, run RoundhousE to update database.
@echo off SET database.name="${database.name}" SET sql.files.directory="${dirs.db}" SET server.database="${server.database}" SET repository.path="${repository.path}" SET version.file="${file.version}" SET version.xpath="//buildInfo/version" SET environment="${environment}" echo backup database call .\scripts\backupdb.bat %server.database% %database.name% if %ERRORLEVEL% NEQ 0 goto errors echo update database "%DIR%rh\rh.exe" /d=%database.name% /f=%sql.files.directory% /s=%server.database% /vf=%version.file% /vx=%version.xpath% /r=%repository.path% /env=%environment% --ni --simple if %ERRORLEVEL% NEQ 0 goto errors goto finish :errors EXIT /B %ERRORLEVEL% :finish
Putting it all together
We already have build.bat
as part of UppercuT, now we need to define deploy.bat
that would do deployment of product. It would be called immediately after build.bat finished, so binaries are ready, tests passed and code_drop
folder contains all artifacts for deployment. The script is rather simple and utilize stuff we did previously.
@echo off if '%1' == '' goto usage SET ENV=%1 cd .\code_drop\deployment echo Deploy database call .\%ENV%.DbDeployment.bat if %ERRORLEVEL% NEQ 0 goto errors echo Deploy application call .\%ENV%.AppDeployment.bat if %ERRORLEVEL% NEQ 0 goto errors goto finish :usage echo. echo tracky.net deploy script echo Usage: deploy.bat [environment] echo [environment] - deployment environment could be STAGING or PRODUCTION echo. EXIT /B 1 :errors echo Build FAILED EXIT /B %ERRORLEVEL% :finish echo Build SUCCESS
Notice, it receives the parameter ENV
. It will contain type of environment for deployment. For staging environment, you should call deploy.bar STAGING
, for production deploy.bat PRODUCTION
.
Testing it out
That’s basically it. Now, you should make sure everything works as expected. Run build.bat/deploy.bat
, make sure build went with no errors, deploy.bat correctly does back up of database, updates database and update site content.
As I said on top, it is very important that configuration is part of product, part of source code. If you follow this, it will be possible to deploy application but just getting sources from SCM. This is first step of setting up your Continuous Production server.