DevOps is a relatively new space in the software engineering world. There are a smattering of tools to aid in the automation of application deployments, but precious little guidance with respect to patterns and practices for using the new tools. As a guy who loves leaning on principles this lack of attention to best practices leaves me feeling a bit uncomfortable. Since I’m leading a migration to Octopus Deploy, I thought I would share some of the decisions we’ve made.
This series of posts is an attempt to start a conversation about best practices. I want to be clear: We have not been applying these ideas long enough to know what all of the ramifications are. Your mileage may vary.
Posts in this series
1. Environments
2. Roles
3. Variables & Variable Sets
Variables & Variable Sets
Octopus Deploy allows you to modify your application’s configuration through the use of variables. You can define variables at the project level, or share variable values between projects through variable sets. If you have relatively little sharing of variables between projects you will likely prefer to create variables at the project level. My team manages over 50 different applications. Many of them are web services designed to support SOA. The net impact is that we have a lot of shared variables and for this reason we define variables exclusively through variable sets. This saves us time hunting for where a given variable is defined.
We use 2 kinds of variable sets
1. Global
2. Role based
Global variable sets define values that might be required across the company irrespective of any particular application, or that are more easily managed together. For example, we wish to capture metadata about environments. Octopus itself does not have a facility for tagging environments with arbitrary metadata. To satisfy this goal we created a variable set called “environment” in which we create variables to indicate values such as “owner” and “abbr”. We use these values to compose the values of other variables such as dns addresses or email addresses.
We also have some environments for which we do not create dns addresses for the sites. In these environments we need to install web applications with alternate ports. We keep a variable sets to define the port number we use for web applications in these environments since they must be unique across the web server.
The number of global variable sets should be as small as possible.
Role based variable sets are variables defined for the specific roles they target. If we have a role called heroes-iis
we will also have a variable set called heroes-iis
. Since we create roles on a per-deployed-application basis, this helps us keep roles, projects, and variable sets linked. If heroes-iis
as web service end points, this variable set may be included in some other project that depends on those end points.
Naming Conventions
It is important to have naming conventions for your variables. I highly recommend prefixing all variables in a variable set with the name of the variable set to avoid potential naming collisions. For example, If I have a variable set called heroes-iis
it will have variables with names like:
- heroes-iis.application-pool.name
- heroes-iis.application-pool.password
Define a Standard Structure for Similar Variable Sets
Once you get the rhythm of installing applications with Octopus, you will discover that similar kinds of applications have similar variable definition needs. You can save yourself a lot of time and Chrome tabs by establishing a variable set template that you use when creating a variable set for each kind of application you deploy. Here is our variable set template for web applications being deployed to iis:
Variable Set Name | Segment | Field | Variable Name | Notes |
---|---|---|---|---|
name-iis | application-pool | name | name-iis.application-pool.name | The name of the application pool |
username | name-iis.application-pool.username | the username the application pool runs under | ||
password | name-iis.application-pool.password | the password the application pool runs under | ||
host | name.host | This corresponds to the site name as registered in IIS. It does not include the protocol (http://, https://). It should be blank if the site is being deployed into an environment without a dns entry. | ||
site-name | name.site-name | This will be just the name of the web application in environments that do not have a dns entry. If the environment has a dns entry, it should resolve the host property. | ||
site-root | name.site-root | This is the url root for the site. It should include the protocol (http://, https://) as well as the port, and any additional routing information. | ||
endpoints | endpoint-name | name.endpoints.endpoint-name | A web service may expose one or more endpoints. These should have unique names. Their values should be defined with reference to the host and port variables. | connection-strings | cs-name | name.connection-strings.cs-name | The name of the connection string in the config file. |
Scope
Octopus Deploy allows you to scope variables by environment, role, or channel (as of 3.3). The scoping rules are as follows:
(environment1 OR environment2 OR ...) AND (role1 OR role2 OR ...) AND (channel1 OR channel2 OR ...)
I recommend that you scope variable values as broadly as possible. Use composed variable values where you can to minimize the number of variable values you have to maintain. For example:
<br />heroes-iis.connection-strings.heroes-db => "Server=#{environment.sql-server.url}; Database=#{heroes-db.database-name};"
heroes-db.database-name => #{HEROES_}#{environment.name}
environment.sql-server.url => http://sql-server.#{environment.name}.com
By using a composed variable value I don’t need to scope the connection string variable itself. Instead, I can confine scoping to environment.name
and satisfy the resolution of all of the descendant variables. This minimizes the number of variables I have to actively maintain as new environments are created.
DevOps is a relatively new space in the software engineering world. There are a smattering of tools to aid in the automation of application deployments, but precious little guidance with respect to patterns and practices for using the new tools. As a guy who loves leaning on principles this lack of attention to best practices leaves me feeling a bit uncomfortable. Since I’m leading a migration to Octopus Deploy, I thought I would share some of the decisions we’ve made.
This series of posts is an attempt to start a conversation about best practices. I want to be clear: We have not been applying these ideas long enough to know what all of the ramifications are. Your mileage may vary.
Posts in this series
1. Environments
2. Roles
3. Variables & Variable Sets
Roles
When you add machines into Octopus, you must specify environments and roles for that machine. For our purposes, environments were pretty easy to define. Roles however took some work. Here are the kinds of roles we defined.
Operating Systems
Example: windows, linux
This is pretty easy. We started with Linux
and Windows
for this type of role. I can see a day when we may need to additionally specify ubuntu-14
or 2k8-r2
. In the meantime, YAGNI.
Environment Types
Example: dev, uat, integration, staging, prod, support
Our environment naming convention for developer environments is dev-{first initial}{last name}
. For uat environments it’s uat-{team}
. There is only one of each integration, staging, production, and support environments. There are certain variables that are defined consistently across all dev
environments but may differ in uat
environments. For this reason we are applying the environment type as a role across all machines in the relevant environments.
Commands
Example: hero-db.migrator
This is a standalone role. There will only be one machine in each environment that will have this role. It’s purpose is to execute commands on some resource in the enviornment that should not be run multiple times or concurrently. A good example of this is an Entity Framework database migration. We choose one machine in an environment that database migrations can be run from.
Applications
Example: webapp-iis, topshelf-service
Each deployable application has its own role. Not every application gets installed on every machine in an environment. We use the -iis
affix for applications installed into IIS regardless of whether they’re sites or web services. We use the -service
affix for Windows Services. We do this because we sometimes have a family of applications that have the same name but target a different kind of application.
DevOps is a relatively new space in the software engineering world. There are a smattering of tools to aid in the automation of application deployments, but precious little guidance with respect to patterns and practices for using the new tools. As a guy who loves leaning on principles this lack of attention to best practices leaves me feeling a bit uncomfortable. Since I’m leading a migration to Octopus Deploy, I thought I would share some of the decisions we’ve made.
This series of posts is an attempt to start a conversation about best practices. I want to be clear: We have not been applying these ideas long enough to know what all of the ramifications are. Your mileage may vary.
Posts in this series
1. Environments
2. Roles
3. Variables & Variable Sets
Our Default Lifecycle
Before I begin, I should give you some background on our development ecosystem. Our Octopus Lifecycle looks like this:
dev => uat => integration => staging => prod => support
The Environments
Name | Convention | Purpose | Notes |
---|---|---|---|
dev | dev-{first initial}{last name} | The primary purpose of these environments is to test the deployment tooling itself. | We have 15 or so individual developer environments. Each developer gets their own environment with 2 servers (1 Linux, 1 Windows) and all of our 60 or so proprietary applications installed to it. |
uat | uat-{team} | These environments are used by teams to test their work. | We have 10 or so User Acceptance Testing environments. These are a little bit more fleshed out in terms of hardware. There are multiple web servers behind load balancers. The machines are beefier. These enviornments are usually owned by a single team, though they may sometimes be shared. |
integration | N/A | Dress rehearsal by Development for releases | The integration environment is much closer to production. When multiple teams are releasing their software during the same release window, integration gives us a rehearsal environment to make sure all of the work done by the various teams will work well together. |
staging | N/A | Dress rehearsal by Support for releases | Staging is exactly like integration except that it is not owned by the Development department. We have a team of people who are responsible for executing releases. This is their environment to verify that the steps development gave them will work. |
prod | N/A | Business Use | Prod is not managed by the deployment engineering team. We build the button that pushes to prod, but we do not push it. |
support | N/A | Rehearsal environment for support solutions | Support is a post-production environment that mirrors production. It allows support personnel to test and verify support tasks in a non-prod environment prior to running them in production. |