How to create a Spinnaker pipeline

Previously on the Mirantis blog, we gave you a quick and dirty guide for installing Spinnaker. And that’s great, but it would help if we knew how to do things with it. Ultimately, we’re going to use Spinnaker for our whole CI/CD lifecycle management, right on to creating intelligent continuous delivery pipelines, but that involves a lot more configuration and integration, so let’s just start by creating a simple pipeline just to get our feet wet.

In this article, we’re going to create a Spinnaker application that lets us resize a cluster based on feedback from an external system.  We’ll do this in 5 steps:

  1. Create the application
  2. Create a server group
  3. Create a simple pipeline that checks the size of the server group
  4. Create a webhook for Spinnaker to call
  5. Create pipeline that resizes the cluster if the webhook says it should

Let’s start by creating the application and server group.  (If you haven’t already installed Spinnaker, go back and do that now.)

Create a Spinnaker application and server group

A Spinnaker application groups together all of the resources that you’re using, such as server groups, load balancers, and security groups. A server group is a group of instances that are managed together, with a cluster being a grouping of server groups.

Start by creating an application.

  1. In the upper-right-hand corner of the Spinnaker interface, you’ll see an Actions pulldown; choose Create Application.
  2. Specify a name and email for the application; for the moment, you can leave everything else blank.  I’m going to call mine sample. Click Create to create the application. createapp2
  3. You’ll find yourself on the Clusters page for the sample application. Click the Create Server Group button.clusterspage
  4. Next we’ll configure the server group. We’re going to create Kubernetes resources, so we’ll specify the Kubernetes account we created when we were deploying Spinnaker (my-k8s-account). We’ll specify the default namespace for the pods we’re going to deploy.
  5. We’re going to specify that this server group is part of the dev stack, and we’ll give it a detail of tutorial.  Note that these names are completely arbitrary, but they help to make up the name of the cluster the group goes into.
  6. Finally, we’ll need to specify containers to deploy into this server group; we’re not actually going to do anything with these pods in this exercise, so we’ll just specify that we want Nginx; if you start typing “nginx” in the Containers field, autocomplete will give you your available choices.
  7. We’ve got a number of other options we can set at this point, such as volumes, replicas, and the minimum and desired number of replicas to start out with, but for now just accept the default, which will give us one instance. Click Create.createservergroup2
  8. You can monitor the creation from here…createclustergroup3
  9. Or click Close and check on it from the Tasks tab at any time. createservergroup4
  10. Once the server group is created, you will see it in the Clusters tab.  Note the single green rectangle; that’s our single instance.  This page is handy because you can see the status of each of your instances.createcluster6

Now we’re ready to create the pipeline.

Create a simple pipeline

A Spinnaker pipeline is a sequence of deployment actions called stages, used for managing your deployments. You can use them to create complex sequences that involve triggers, delays, decisions, and even human intervention.

We’re not going to do that right now.

No, right now we’re just going to create a simple pipeline that checks to make sure our server group isn’t getting too big before we do anything else to it.

  1. Start by clicking the Pipelines tab. As you can see, we don’t have any pipelines already created, so go ahead and click Configure a new pipeline.createpipeline1
  2. Choose a type of Pipeline and name it basic, then click Create.createpipeline2
  3. Now we want to add a Stage, which is a unit of action within a pipeline.  So click Add Stage to create the new stage, and choose Check Precondition as the type.createpipeline4
  4. Now we need to specify the actual precondition we’re checking for. Ultimately, we’ll be scaling up the server if our webhook says to, so we want to make sure it doesn’t get too big.  To make that happen, we’ll create a condition that checks the size of the server group, and stops the pipeline if it’s too big (that is, if the check fails). Click Add Precondition.createpipeline6
  5. Now we need to create the precondition. Specify that we want to Check the Cluster Size, and then specify the Kubernetes account the cluster lives on.  (In our case, that was my-k8s-account.) You can either type in the cluster name, or click Toggle for list of existing clusters and click sample-dev-tutorial.
  6. Now we need to specify the actual condition; we want to make sure that the cluster itself — note that’s ALL server groups, not just the one we just created — is less than 10.  So specify less than or equal to (<=), and then 10 in the expected size
  7. Finally, we want the pipeline to fail if this condition is not true, so check the Fail Pipeline box.Click Update to add the Precondition.createpipeline7
  8. Now click the Save Changes button at the bottom right of the window.
  9. Once you save changes, will be on the main Pipelines page. Pipelines can be triggered in a number of ways, such as a Jenkins job or another trigger. In this case, though, we’re going to click Start Manual Execution to test the pipeline.You’ll have the option to be notified when the pipeline completes using email, SMS, or HipChat, but in our case we’ve got a very short pipeline, so we won’t bother with that.  Click Run to start the execution. createpipeline9
  10. You should see the success of the pipeline fairly quickly, and if you click the Details link on the left, you can see more information about the process.createpipeline11

If you were to change the server group so that it has more than 10 instances and run it again, you’ll see that the pipeline fails and any future stages won’t execute.

Now let’s go ahead and make our pipeline a little more sophisticated.

Add a webhook to a Spinnaker pipeline

In this step, we’re going to enhance our pipeline so that it calls a webhook.  (In the next section, we’ll make decisions based on what we get back from the webhook, but for now we just want to call the webhook from the pipeline.)

A webhook is a way to make a call out to an external service; normally you’ll be using webhooks for services such as Github, but in our case we’re just going to create a simple PHP script that writes to a file so that we know the call happened.  On a web server reachable from your Spinnaker instance (any public web server will likely do) create a new PHP file with the following script:

   $file = "spinnakerpipeline.txt";
   $f=fopen($file, 'a');
   fwrite($f,date('Y-m-d H:i:s') . ': ' . $_SERVER['REMOTE_ADDR']."\n");

(If you prefer some other language, go right ahead; the actual contents aren’t important in this case.)

In this case we’re simply opening a file and appending the current time and date and IP address for the server accessing the script.

So if I were to then call the script by pointing my browser to it at

There’s actually no output, but if I then looked at the file:

I’d see a single line of text:

2018-04-08 13:11:25:

OK, so that part works.  Now let’s go ahead and add it to the pipeline.

  1. First click the Configure link for the basic pipeline we created in the previous section to get back to the configuration page, then click Add Stage.
  2. This time, we want to add a stage of type Webhook, and we only want it to execute if the Check Preconditions stage passed, so make sure the new stage Depends On Check Preconditions. webhook1
  3. Now let’s configure the stage itself. For the Webhook URL, put the URL where you are hosting the PHP script.  (This will be the URL where you tested it in the previous step.) To make things simple, leave the method as GET; we’re not going to pass in any information, we just want to call the script. We’re also not going to wait for completion; as long as the script gives a 2xx response — that is, as long as it doesn’t return an error — we’re going to assume that everything’s finished, and that everything is fine.  If it does return an error, we’ll simply halt the pipeline.webhook2
  4. Save the changes. This should bring you back to the Pipelines page.
  5. Now let’s test out our changes.  Click Start Manual Execution and Run.
  6. Once again, you should see a fairly quick success, but this time you can see that there are two stages (and in fact if you watch the pipeline you can see as each is executed). webhook6
  7. You can see the status of a particular stage by clicking on that stage in the pipeline.  For example, if we click on the second stage, we can see the results of the Webhook execution.webhook7

Now if we pull up the text file again by pointing the browser at

We can see that there are now two lines: the first one, from when we tested the webhook, and a second with the IP of the Spinnaker server.  (No, that’s not the real IP of my Spinnaker server. Remember that note about being careful if you open up a hole like that?)

2018-04-08 13:11:25: 
2018-04-08 13:26:11:

Great, so we know that works.  You can execute the Spinnaker pipeline multiple times and for each time, you’ll see a new line in the file.

Now let’s add some intelligence to this process.

Creating a Spinnaker pipeline to make a decision

Now that we know how to create a Spinnaker pipeline and have it call a Webhook, we’re going to get a little fancy. What we want to do is create a webhook that tells a pipeline whether or not to increase the size of our server group.

A few words about webhooks

Now, if you’re just getting into this, you might be surprised to find that calling a webhook is more than just calling a URL and getting a response, as we did in the last step.  That’s because webhooks are used for asynchronous access to long(er) running processes. That means you kick it off and when it’s done, you get an answer.

For Spinnaker, that means that you’re “polling”, or repeatedly calling a URL until you get a signal that the job is cancelled, or it’s terminated. Somewhere in that process, you’ll also (hopefully) get a signal as to whether the webhook succeeded or failed.

In our example, we’re going to consider a response of “up” as a success, and “down” as a failure, and we’re going to return a JSON objects.

So there are four possibilities for a response:

During polling, the response will give a value that’s between 1 and 10, as in

{ "current": "3"}

Once we’ve reached 10 iterations of polling, we’ll randomly get either an “up” or “down” value:

{ "current": "up"}
{ "current": "down"}

Finally, if we’ve given an up or down, we’ll indicate that we’re done.

{ "current": "done"}

Obviously this isn’t particularly robust or useful in a multi-user situation, but we’re just trying to demonstrate Spinnaker here, so we’ll go with it.

Create a new PHP script called webhook2.php and add the following:

{ "current": "<?php
   $file = "spinnakerpipeline2.txt";
   $current = file_get_contents($file);
   if ($current == "done"){ $current = 0; } 
   elseif ($current == "10"){
       if (rand(0, 5) < 2){$current = "up";} 
       else {$current = "down";}
   } elseif ($current == "up" || $current == "down"){
       $current = "done";
   } else { $current = $current + 1;}
   $f=fopen($file, 'w');
   fwrite($f, $current);

It’s a simple script that reads the contents of spinnakerpipeline2.txt and outputs them as part of the JSON object, then either starts over, gives a random up or down, outputs the “done” signal, or increments the current value, then writes the new value to the file.

Unless you create the spinnakerpipeline2.txt file, the first time you run the script it’ll return an error, but after that you can refresh the file and see how it cycles through the values.

Now let’s add this to the pipeline.

Adding the webhook to the pipeline

Now that we have the second webhook we can add it to the pipeline.

  1. Open the configuration page for the pipeline and click the Check Preconditions stage; we want to make sure that the new stage comes after that.webhookb1
  2. Click Add Stage and choose Webhook as the type. To distinguish it from the other webhook stage, name it Webhook-Check.webhookb2
  3. Add the URL for the new webhook, just as you added it the first time. In this case, however, we do want to wait for the routine to finish so we know what to do, so click the Wait for completion checkbox.
  4. Now we’re faced with three choices for the Status URL, which is the URL Spinnaker will poll until it’s told the routine is complete. The first, GET method against webhook URL, means that Spinnaker will use the original URL.  This is what we’ll use. (The other two options are to provide a secondary URL as part of the initial response, and have Spinnaker poll against that.)
  5. Next we need to tell Spinnaker where in the response to look for the actual status information.  Remember, we have a JSON response of
    { "current": "up"}

    So we’re looking for the value of the object’s current property.  To indicate that, we’ll use $.current as the value, where $ represents the returned object, and current is a property of that object.

  6. Finally, we’ll specify the success value of up, the cancelled value of down, and the completed, or terminated, value of done.webhookb3
  7. Before we move on to save the pipeline, there’s one more thing we need to do. If you look at the top of the page, you’ll see that we have a branch after Check Preconditions, where it is executing both webhook stages. There are plenty of situations where that’s fine, but in our case, we only want Webhook to execute and write to the file if Webhook-Check is successful.  To make that happen, click on the Webhook stage at the top of the page and remove Check Preconditions as a dependency by clicking the trash can next to it, then add Webhook-Check.webhookb4
  8. Now go ahead and save and execute the pipeline. Statistically, we made it more likely that the webhook would return down than up, but either response is possible. In my case, the first time I ran it it outputted down, so the pipeline cancelled at that stage, as you can see here.  Because of that, the third stage, the Webhook to write to my file, never got executed. I can verify that by checking the contents of spinnakerpipeline.txt.webhookb6
  9. However, the second time I ran it, it did, in fact, succeed, as you can see here:

webhookb7So if I check the contents of spinnakerpipeline.txt, I can see an additional access:

2018-04-08 13:11:25:
2018-04-08 13:26:11:
2018-04-08 13:43:03:

OK!  Now that we know that we can do some conditional execution, let’s do something useful.

Resizing the cluster based on a previous Spinnaker stage

Writing to a file is great because we can see what’s going on, but let’s try to do something more operations-related. Specifically, in addition to writing to the file, we want to resize our server group.

  1. Start by adding a new stage after the Webhook-Check stage. Set the type to Resize Server Group.resize1
  2. Now we need to set the server group information and what we want to do with it. First specify our kubernetes account, just as we did when we created the server group, and the sample-dev-tutorial cluster where it lives.
  3. Next we have to specify the server group itself. This is a little tricky, in that you can’t specify the actual server group. Instead, you have three choices.  The newest erver group, the “previous”, or second newest, or the oldest server group. Since we only have one, it fits all three of those criteria, but keep in mind that if you start to include too many server groups in a single cluster, you’re going to have problems.
  4. Finally, choose an Action of ScaleUp and specify that you want to do an Incremental increase of 3.resize2
  5. Save your changes.
  6. Now let’s run the pipeline again. You may have to run it a couple of times before it succeeds (remember that Webhook-Check is skewed towards failure) but once it does, you’ll see it on that page.resize6Once it does succeed, the last stage will take a few seconds as Spinnaker does the actual ScaleUp operation.resize7
  7. Once the pipeline’s finished, click the Clusters tab to look at the server group. Instead of 1 green rectangle, you should now see 4. You can run this pipeline multiple times, but remember, once the cluster has more than 10 nodes, you’ll fail the Precondition.resize9

Moving on

In this article, we took a look at simple pipelines and using them to make decisions. Coming up, we’ll look at more complicated situations, such as triggering pipelines from a CI/CD pipeline.

Subscribe to Our Newsletter

Latest Tweets

Suggested Content

Nick Chase (@NickChase on Twitter)

Nick Chase is Head of Technical and Marketing Content for Mirantis and Editor in Chief of Open Cloud Digest. He has also authored more than a dozen technical books, including Understanding OPNFV and Machine Learning for Mere Mortals.

Mirantis Application Platform with Spinnaker
What's New in Kubernetes 1.10