Ok, in my previous post I went over why we chose to use Octopus Deploy and TFS as our build and deployment pipeline. Now I'm going to go over some things I've learned while implementing that solution. Those things and the reasons behind them are bolded. The rest of it is more detail and examples. It may seem like a lot, but it's really not and it's mostly good with some gotcha's.
- Don't be afraid to fail. It'll take a while to figure out exactly what works well for your particular environment. We have hundreds of projects, nuget packages and solutions that we maintain. If you have only a few projects that don't change very often, some of these may not apply. If you have thousands of projects, you may need to take things further. This has taken months for us to figure out what works for us, and we're still learning and finding things that work better.
- Do builds and releases manually before automating. Once you know how it's actuall done and fully understand that process you can start automating and improving that process. Some of our guys here tried to automate first and didn't understand the process of deploying their application. It took them about 3 months and then they gave up trying to automate it. I then had them do it all by hand, from checking out a fresh branch, to building, reconfiguring and pushing out the new site and code. It took about about a week or 2 to iron out that process. It then took them about a another week to automate the process and refine it. Now, deployments have gone from a full day to around 10 minutes.
- Consistency is good, but not always possible. You'll find repeatedly that one process that works for one project but doesn't work for another. Don't expect that you can do a one size fits all approach. Some projects you can copy most of the steps, others you dont. I can usually copy about 75% of the steps from one project to another. But after that there's alot of customization.
For Octopus deploy:
- First and foremost, parameterization is key. If it's hard coded in the step you'll be hating it when it comes time to re-configure your process when you need to deploy through multiple environments before re-deploying to another one.
For example, your environments require going through Development, Staging and then Production. There's workarounds, like messing with your lifecycles and re-ordering the environments in there, but that's kind of messy.
- Namespace" your variables. First starting out I did everything seperately without grouping. This ended up being a mess because they all ended up being intermingled with application specific settings. App settings generally aren't namespaced (unfortunately) so they are all over the place. I now start every deployment variable name with
deploy.. Then grouping them further based on where they are used. This helped greatly when trying to find the variables I needed to edit. With this in mind, in my code, I now namespace all of my setting names as well and the rest of the development staff have followed suit. It's been really nice to be able to see exactly what a setting does and where it is used.
An example of my final deployment variables looked like this
deploy.logdrive deploy.logfolder deploy.site.<sitename>.hostname deploy.site.<sitename>.logfolder deploy.site.<sitename>.bindings.http.port deploy.site.<sitename>.bindings.http.hostname deploy.site.<sitename>.bindings.http.enabled deploy.site.<sitename>.apppool.name
- Next up is role names. This is a bit more complicated as I've yet to really figure out what seems to work best here. I started down the path of a seperate role per site/virtual directory. However, it gets a bit crazy with over 100 sites/virtual directories (and needing to double up with blue/green another post later) it gets to be a bit much and the UI doesn't really handle it very well. I've opted to group them up as much as possible. We have an external server farm, an internal farm, and a services layer farm. If we have services that need to be out on their own servers I've started creating multiple environments. I then create roles based on the project if needed, but usually I'm able to re-use the same roles (External Site Active, Internal Site Active, Admin Site Active, Services Active) or if really needed, I would create project specific roles. An example of my current environment layout.
For standard - non specific
Production - Blue
Production - Green
For non-standard or project specific server
Development - <project>
Production - <project> - Blue
Production - <project> - Green
This gave the flexibility I needed without hundreds of roles. However, as specific servers are created for specific projects increases I suspect it will also get a bit out of hand. This may very well be something that the UI will need to address.
- Don't auto start deploy on release creation. More and more I want to just create a release and not have it auto deploy out to my 1st environment. In my release steps it's just as easy to have a "deploy to development" octo step.
- Keep builds and release steps seperate Building is for building. Period. Don't release or push nuget packages or send artifacts to Octopus or anything like that. Push the artifacts to the TFS artifact repository and create a release pipeline to send those artifacts where they need to go. I can't stress this one enough. I would say about all of our projects will either push up to Octopus or push a NuGet package. But usually not both and sometimes neither. Everytime I have one of our dev's set up the build process for the first time they say lets just combine the two into one. After hours of fighting both builds and releases they seperate them back out and make it easy to manage both things. Think of the acronym...it's CI/CD and CID. They are 2 seperate processes related to one another.
- Don't use build scripts unless your project needs it. They may work well when you are using disposable build servers, like VSTS (now Azure DevOps), but when you have long-lived build servers, like you do with TFS, build scripts have a tendency to leave behind garbage. They are also generally more complicated than using the built-in build steps. The more recent updates to TFS included some very important steps that removed our need for the build script. We can now say which version of NuGet to use, and which version of DotNetCore. When those steps were added, we no longer needed them. Debugging the build process is also a little bit easier.