Terms to know:
- Continuous Integration is just the act of running a build script to do things like run unit tests and increment a build number. Perhaps you are making a package but not rolling it out. A must is to have a build that breaks when someone checks in code that breaks a test. Anyways, this is not the act of deploying to an environment. When you are furthermore deploying to an environment you are now undertaking
- Continuous Delivery. In the TeamCity interface it is easy to initially deploy to a build server, and then to eventually promote that deployment to a test server, and then to ultimately promote what is at the test server to production. There is likely a manual step where someone (a manager) is pressing a button to do a promotion. If the promotions happen all by themselves and are kicked off by the commits of code which do not break a build, such as in the case of Netflix and Spotify's model, then an enterprise is at a third level dubbed
- Continuous Deployment. At last night's talk we saw TeamCity call out to Octopus Deploy to make the building blocks for the delivery/deployment and honestly the fine line between delivery and deployment is just a configuration in this space. This GitHub project shows an example of it all working and to get it working you will need an environment prepped with TeamCity, Octopus Deploy, Visual Studio 2015, and a SQL Server Express Instance named SQLEXPRESS2014. The free needs such as AliaSQL and NUnit are going to be part of what you download from GitHub herein. Psake and PowerShell will get used heavily too.
What else is interesting?
- Version numbers may be pulled from source control and stamped inside of deployed .dlls.
- TeamCity can wait on Octopus Deploy to finish or it can call out to Octopus in a fire and forget fashion. In terms of how it is triggered itself, its projects have a setting called Trigger which defines this behavior. You may set the trigger to run TeamCity upon the commit of source code. Another trigger might take a "wait for successful build integration" shape and this, I suppose would be used, if the test server were listening for a successful rollout to the initial build server to do an automatic promotion in a continuous deployment act.
- You need to keep lots of space available for artifacts at deployment environments. You will want to have a versioned history you may consult and TeamCity may be set to keep the versioned history around as long as it can and then delete from the "bottom of the list" so to speak to clean up disk space when an environment starts getting pinched.
- Octopus wraps different components into NuGet packages. There will stereotypically be a sequential list of steps in a build process. Perhaps the first step just runs the unit tests. Maybe the second builds the database. Let's say that the third deploys the UI. Each of these steps could be given roles. Let's give "databaseserver" to our second step and "webserver" to our third step in this example. A different NuGet package would be speced for each of these and these are our components. They will define where each package gets deployed to. We don't need to keep the app and the database on the same server, we can break these out in Octopus deployments. Octopus lets you define "tentacles" and a tentacle has a "Display Name" which the name of the server or VM to push to. There would be two different tentacles for app and database in our example.
- I asked something like "How does one tentacle know about another?" and while the tentacles themselves don't cross-talk, obviously, somehow, the connection string in the rolled out app has to be doctored up to point to the rolled out database. How is that possible? In every step (component) there will be a screen for "Configure Features" in Octopus' interface and herein lies an option for "Configure Variables" where one may specify variables like
#{DatabaseName} and Octopus will, upon deployment, doctor up all .config files to replace
#{DatabaseName} with the ideal string. This pound sign and curly braces markup may also be used to call out to Octopus specifics like
#{Octopus.Environment.Name} and Jeffrey even showed off wacky examples of putting things like CSS's
display:none; or an HTML comment in variables which might in another environment (test instead of production perhaps) include nothing. This would allow something like
#{Octopus.Environment.Name} to be displayed on a web page in a test environment as a convenience yet hidden in a production rollout although realistically you might not want such metadata hidden in production HTML.
#{Octopus.Environment.Name} is not that egregious but you can probably imagine other things that you wouldn't want bubbling up to just-under-the-surface in production. - NUnit will be used for the tests and while TeamCity will run all tests by default, NUnit categories may be specified at TeamCity so that one may only run the tests that are decorated in C# with an attribute which says "AcceptanceTest" for example. This allows the quick unit tests and maybe integration tests to be run upon a push to build to keep the build time down to ten minutes and lets that more complicated test where you are reaching out to a web service be deferred to the push to the testing server. Test different things at different promotions.
- Jeffrey showed in depth his NuGet package for rolling out a database change and it, beyond compiling code, would pull in flat files with .sql extensions (really just glorified .txt files holding SQL) for use in AliaSQL. OctoPack is something that you may get from NuGet which is not a NuGet package but is instead a special MSBuild target which tells Visual Studio how to use Octopus Deploy. We need this. A .nuspec file in the OctoPack paradigm will specify which not compliable content files (SQL scripts) to pull in. This file is just kept in the Visual Studio class library project. Octopus may be instructed to look for a post deployment PowerShell script to run and Jeffrey's was called PostDeploy.psl and it called out to AliaSQL. In many ways the NuGet package seemed to do next to nothing other than just set the stage for the post deployment script to be woken up when it was finished, and indeed the post deployment script is doing disproportionately more work than the NuGet package which at a glance seemed almost ceremonial. There is a way to make a "Web Smoke Test" in Octopus which will do nothing but run a PowerShell script and with as much one may do simple things like spin up IE, open the web site, and make sure the web site is there by crawling it a little bit... and thus that begs the question: Why not do everything with a "Web Smoke Test" and forgo the NuGet theatrics outright? The reasons why not are:
- You might want to do something more in C# in another example.
- We need to loop in flat files and while you could just do this standalone in PowerShell...
- You really only want to use a "Web Smoke Test" for something that won't change. Anything using versioned content out of source control (which should be where just about everything is kept) should use the NuGet glorification and not the more ghetto "Web Smoke Test" PowerShell hackery.
- You can use RoundhousE instead of AliaSQL. Use what you wish to build your database.
- Using a newrelic.config file in Visual Studio (at the NuGet packages you'll make) will allow you to use New Relic to test for CPU spikes and the like. Don't wait until a push to production to get the bad news if there is gonna be bad news.
Somewhat off topic:
- I learned that there is a way to do try/catch logic in PowerShell.
- It was rewarding to hear Jeffrey decry the nondeterministic practice of going to NuGet for dependencies and he felt that your .dlls should just be kept in source control, old school style. I really agree. I have in the JavaScript space hit so much heartache in trying to get Grunt or Gulp to pull dependencies to no avail. There have been so many open source projects which I would have loved to play with which I've never been able to get to spin up at my laptop. I've not yet been burned by NuGet but Jeffrey has and it's the same thing. He spoke of pulling RavenDB and being unable to hydrate the dependencies because while the wireup worked at the time it was authored it now pulled newer versions of dependencies which caused breaking changes. You can specify specific version numbers for NuGet packages to deploy but Jeffrey suggested that no one does that in practice.
No comments:
Post a Comment