Octopus: Keeping Tentacles up to date

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

In Octopus, you use one central web portal to push NuGet packages out to many servers. On each server is a "Tentacle", a little Agent that receives NuGet uploads, and installs and configures packages according to conventions.

One of the annoying things about Tentacles at the moment is that as you get more than a few of them, the upgrade experience isn't very nice.

  1. First you download the new Octopus installer
  2. Remote desktop to the Octopus server and install it
  3. Now download the new Tentacle installer
  4. Remote desktop into every single staging/test/production server, and install it

Improving the upgrade process

I'd like to make this process easier, so I'm using this blog post as a way to come up with a solution. I'm going to make use of bootstrapping - that is, I'm going to use Tentacle to install Tentacle.

Currently, the Tentacle is a Windows Service EXE that works something like this:

  1. It starts
  2. It hosts a WCF service (net.tcp on port 10933)
  3. Packages are uploaded to it, and it installs them

Bootstrapping the Tentacle

To allow Tentacle to bootstrap itself, I'm going to do two things:

  1. Turn Tentacle into a Console App instead of a Windows Service
  2. Create "Suction cup" - a bootstrapper Windows Service that launches Tentacle

New installs

When you first install Tentacle to your test/staging/production server, the MSI will include a bundled NuGet package. On start-up, Suction Cup will install the package using the standard conventions. Then it will launch Tentacle.exe as a child process (much like IIS's main process launches each ASP.NET application pool child process).

Upgrades

Suppose a few weeks later, you download and install a new version of Octopus. The Octopus will include a Tentacle NuGet package matching the Octopus version. You can then navigate to the Environments page in Octopus, and click a button to deploy the new Tentacle package.

Now, here's the interesting bootstrapping part:

  1. Tentacle (v1) will deploy Tentacle (v2) to a side-by-side folder
  2. Tentacle (v1) will shut itself down (Environment.Exit(0))
  3. Suction Cup will realize that the Tentacle child process has shut down
  4. Suction Cup will find the latest installed version of Tentacle (now v2) and launch the new child process

Notes

This has some added benefits - for example, if something catastrophic happened during a deployment and Tentacle crashes, Suction Cup will automatically restart it.

One thing I'll need to detect is that if Tentacle repeatedly crashes in a short period of time, e.g., 5 times in 30 seconds, Suction Cup should wait a while before trying again - this way we don't fill up the event log.

Finally, since Suction Cup itself isn't upgraded during this process, I'm going to go with the approach that Suction Cup just needs to be 100% perfect from day 1 :)