Run a canary release

In the previous tutorial we deployed our app sava 1.0. If you haven’t walked through that part already, please do so before continuing.

Now let’s say we have a new version of this great application that we want to canary release into production. We have it containerised as magneticio/sava:1.1.0 and are ready to go. In this tutorial we will:

  • Prepare our blueprint
  • Deploy the new version of our application next to the old one
  • Canary release the new application
  • Use conditions to target specific groups
  • Learn a bit more about conditions

Requirements: Docker machine should have access to at least 3GB memory

Prepare our blueprint

Vamp allows you to canary release application updates by merging new blueprints to a running deployment. Take a look at the YAML blueprint example below. It is almost identical to the blueprint we initially used to deploy sava 1.0.0, with one difference - this time the breed describes the sava:1.1.0 service.

name: sava:1.1
clusters:
  sava:
    services:
      breed:
        name: sava:1.1.0
        deployable: magneticio/sava:1.1.0
        ports:
          webport: 8080/http
      scale:
        cpu: 0.2       
        memory: 64MB
        instances: 1
      health_checks:
        initial_delay: 10s
        port: webport
        timeout: 5s
        interval: 10s
        failures: 10          

Deploy the new version of our application next to the old one

Let’s introduce sava:1.1.0 to the running sava deployment.
We can merge the above blueprint to deploy sava:1.1.0 alongside the existing sava:1.0.0. The merge will not affect the running service and initially no traffic will be routed to the new sava:1.1.0.

Merge using the UI

  1. Go to the Blueprints page and click Add (top right)
  2. Paste in the above blueprint and click Save. Vamp will store the blueprint and make it available for deployment
  3. Open the action menu on the sava:1.1 blueprint and select Merge to
  4. You’ll be prompted to select the deployment you wish to merge the blueprint with - select sava
  5. Click Merge to deploy the sava:1.1.0 service to the running sava deployment.
    Vamp will work out the differences and update the deployment accordingly.

Merge using the API

You can complete the same merge action with the Vamp API - remember to set the Content-Type: application/x-yaml for your requests:

  1. To create the blueprint, POST the above blueprint YAML to /api/v1/blueprints
  2. To merge the blueprint, PUT the below YAML to /api/v1/deployments/sava
  name: sava:1.1

Canary release

When Vamp has finished deploying, open the sava/sava/webport gateway. You will see two routes listed - one for the sava:1.0.0 service and one for the new sava:1.1.0 service. The weight of our newly merged service is set to 0%, this means that no traffic is currently being routed here. Whenever Vamp merges a new service to an existing cluster it applies the default weight of 0%.
Let’s adjust the weight and start to send traffic to our new sava:1.1.0

  1. Click the edit icon next to WEIGHT
  2. Adjust the weight slider to distribute traffic 50% / 50% between the two services
  3. Click Save and Vamp will update the load balancing rules accordingly
  4. Click the HOST - PORT/TYPE to open the gateway.
    Each time you do this the application will switch between a 1.0 page and a 1.1 page.

You can also use Vamp as a reverse proxy to access the exposed sava gateways (note that this works best with the “Incognito” or “Anonymous” mode of your browser because of the caching of static assets):

  • http://localhost:8080/proxy/gateways/sava%2Fsava%2Fwebport/
  • http://localhost:8080/proxy/gateways/sava%2F9050/

Use conditions to target specific groups

Using percentages to divide traffic between versions is already quite powerful, but also very simplistic. What if, for instance, you want to specifically target a group of users? Or channel specific requests from an internal service? Vamp allows you to do this right from the blueprint DSL.

Let’s start simple: We can use the Vamp UI to allow only Chrome users to access v1.1.0 of our application.

  1. Go to the Gateways page and open the sava/sava/webport gateway
  2. Click the edit condition icon for the sava/sava/sava:1.1.0/webport route and enter the condition User-Agent = Chrome Now we need to set a strength for the condition.
    As we want all Chrome users to be sent to this route, we will set the condition strength to 100%.
  3. Click the edit condition strength icon for the sava/sava/sava:1.1.0/webport route and move the slider to 100%. Finally, we need to account for routing of traffic that does not match the condition (that is, all non-Chrome users). We do this using the route weight.
    As we want only Chrome users to be sent to the sava/sava/sava:1.1.0/webport route, we need to set its route weight to 0%. That might sound confusing, but remember that the route weight is used for distributing traffic that didn’t match the applied condition - and we want 0% of all non-Chrome users to be sent to the sava/sava/sava:1.1.0/webport route
    Read more about route weight and condition strength
  4. Click the edit icon next to WEIGHT
  5. Adjust the weight slider to 0% for the sava/sava/sava:1.1.0/webport route
  6. Click Save

As we are not actually deploying anything, just reconfiguring routes, the update should be almost instantaneous. You can fire up a Chrome browser and a Safari browser and go to http://localhost:8080/proxy/gateways/sava%2Fsava%2Fwebport/ to check the results. A hard refresh might be necessary because of your browser’s caching routine.

A bit more about conditions

Our browser example is easily testable on a laptop, but of course a bit contrived. Luckily you can create much more powerful conditions quite easily. Checking Headers, Cookies, Hosts etc. is all possible. Under the hood, Vamp uses Haproxy’s ACL’s (cbonte.github.io/haproxy-dconv/configuration-1.5 - ACL basics) and you can use the exact ACL definition right in the blueprint in the ‘condition` field.

Vamp short codes

ACLs can be somewhat opaque and cryptic. That’s why Vamp has a set of convenient “short codes” to address common use cases. Currently, we support the following, but we will be expanding on this in the future:

User-Agent = *string*
Host = *string*
Cookie *cookie name* Contains *string*
Has Cookie *cookie name*
Misses Cookie *cookie name*
Header *header name* Contains *string*
Has Header *header name*
Misses Header *header name*

Vamp is also quite flexible when it comes to the exact syntax. This means the following are all equivalent:

hdr_sub(user-agent) Android   # straight ACL
user-agent=Android            # lower case, no white space
User-Agent=Android            # upper case, no white space
user-agent = Android          # lower case, white space

Add multiple conditions

Multiple conditions can be included using boolean expressions. For example, the following condition would first check whether the string “Chrome” exists in the User-Agent header of a request and then it would check whether the request has the header “X-VAMP-TUTORIAL”. So any request matching both conditions would go to this service.

gateways:
  weight: 100%
  condition: "User-Agent = Chrome AND Has Header X-VAMP-TUTORIAL"

Using a tool like httpie (github.com/jkbrzt/httpie) makes testing this a breeze.

http GET http://10.26.184.254:9050/ X-VAMP-TUTORIAL:stuff

What next?