Application Onboarding and canary release
This document describes a basic workflow for onboarding an application into TSB to perform a basic canary release of a service directing traffic progressively to a new version of it.
Initial application onboarding
An application in TSB maps to a set of services deployed across one or many Kubernetes namespaces. The first step then is to get your application deployed into a Kubernetes namespace which we won't be covering here as that is just following your regular deployment strategy in Kubernetes.
Istio injection
Application services need Istio sidecar to be present. You can label the
namespace with the istio-injection=enabled
label to get sidecars injected
automatically.
Assuming you have the above in place, the next step is to create an application in TSB mapping to your application namespace.
Via API
The API call that will help here is the create application call.
curl --request POST \
-u admin \
--url https://${TCCIP}/v1/tenants/tenant/environments/dev/applications \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data -<<EOF
{
"id": "bookinfo",
"namespaces": [
"bookinfo-ns"
],
"forceMtlsBetweenServices": true
}
EOF
The above creates an application in TSB named bookinfo
which is composed of
the services deployed in the bookinfo-ns
namespace. Additionally, setting the
forceMtlsBetweenServices
parameter to true causes mTLS to be enforced in
communications across all services in the namespace.
Via UI
Log into TSB UI, navigate to Applications
in the navigation bar on the left
side. You will see the list of applications. Click in the +
sign next to the
Applications
title.
You will see the create application form where you will need to input the
application name as well as the list of namespaces conforming it. After you are
done, click the Create application
button.
Should you want to enable mTLS in the namespace via the UI, you will need to
navigate Applications
, enter your application by clicking in its name, then
navigate to the Reliability settings
tab and toggle the Disallow non-mTLS connections between services
switch to be activated.
That is it. After creating the application, TSB will sync all services in the namespace automatically.
Canary release
The bookinfo
application includes a service called reviews
which has multiple
versions. When deploying this application without further customization, the
Kubernetes service will load balance between all instances (pods).
We will start updating the reviews
service making all traffic to reach the
pods with label version=v1
. We will also set a route that will direct traffic
to the pods with label version=v2
if the request has a given header.
Via API
First, we will need to retrieve the service:
curl --request GET \
--url https://${TCCIP}/v1/tenants/tenant/environments/dev/applications/bookinfo/services/reviews \
--header 'accept: application/json' > /tmp/reviews.json
Now we need to update the JSON object to reflect the route changes.
We are going to update the service JSON response and add a first level subsets
key in the JSON object so we can route traffic based on these subsets. Also, we
will need to update the routing rules in the service so traffic goes to v1
only except if the request includes a header named X-Release-Version
with
value v2
, then we will route the request to the v2
subset.
jq '.subsets = [
{
"labels": {
"version": "v1"
},
"name": "v1"
},
{
"labels": {
"version": "v2"
},
"name": "v2"
}
] | .internalRoutes.httpSettings.routeRules = [
{
"route": {
"destinations": [
{
"local": {
"application": "bookinfo",
"service": "reviews",
"subset": "v1"
},
"weight": 100,
"port": 9080
}
]
}
},
{
"match": [
{
"headers": {
"X-Release-Version": {
"exact": "v2"
}
}
}
],
"route": {
"destinations": [
{
"local": {
"application": "bookinfo",
"service": "reviews",
"subset": "v2"
},
"weight": 100,
"port": 9080
}
]
}
}
]' /tmp/reviews.json | tee /tmp/reviews-updated.json
Now we just need to update the service via API:
curl --request PUT \
-u admin \
--url https://${TSBIP}/v1/tenants/tenant/environments/dev/applications/bookinfo/services/reviews \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--d @/tmp/reviews-updated.json
Via UI
Navigate to the bookinfo
application, Services
tab, click in the blue link
button next to the reviews
service, then navigate to Route settings
.
- Click in the
+
sign next to theSubsets
title to add a subset. By default, the UI will create subsets based on theversion
label. Click again the+
sign to create a secondv2
subset. - Save the changes so the subsets are created.
- Click the
+
sign next to theRouting rules
title. - For the first rule, create a new match condition by clicking the
+
button next to theMatch Conditions
title. Set attribute toHeader
, header name toX-Release-Version
, condition toexact
and value tov2
. In theRoute destinations
set thereviews
service and select thev2
subset. - Create a new routing rule, this time do not set any match condition, select
as route destination the
reviews
service and thev1
subset.
Traffic shifting to v2
Based on the previous state you'll need to, steering traffic to the v2
subset
is possible by once again updating the routing rules in the service. So we will
repeat what we did in the previous step, just adding a destination in the
default route (the one that does not specify a match condition) to the v2
subset and setting the percentage of traffic directed to each subset as desired.
Via API
First, we will need to retrieve the service as we did before:
curl --request GET \
--url https://${TCCIP}/v1/tenants/tenant/environments/dev/applications/bookinfo/services/reviews \
--header 'accept: application/json' > /tmp/reviews.json
Now we need to update the JSON object to reflect the route changes.
In this case the subsets
already exist so we need to set the route
destinations as follows:
jq '.internalRoutes.httpSettings.routeRules = [
{
"route": {
"destinations": [
{
"local": {
"application": "bookinfo",
"service": "reviews",
"subset": "v1"
},
"weight": 80,
"port": 9080
},
{
"local": {
"application": "bookinfo",
"service": "reviews",
"subset": "v2"
},
"weight": 20,
"port": 9080
}
]
}
},
{
"match": [
{
"headers": {
"X-Release-Version": {
"exact": "v2"
}
}
}
],
"route": {
"destinations": [
{
"local": {
"application": "bookinfo",
"service": "reviews",
"subset": "v2"
},
"weight": 100,
"port": 9080
}
]
}
}
]' /tmp/reviews.json | tee /tmp/reviews-updated.json
We added a new destination in the default rule and changed the weight
attribute in each destination to direct 80% of traffic to the v1
subset and
20% to the v2
subset. Now we just need to update the service via API:
curl --request PUT \
-u admin \
--url https://${TSBIP}/v1/tenants/tenant/environments/dev/applications/bookinfo/services/reviews \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--d @/tmp/reviews-updated.json
Via UI
Navigate to the bookinfo
application, Services
tab, click in the blue link
button next to the reviews
service, then navigate to Route settings
.
- Expand the routing rule that has no match condition (should be rule 2 if you followed the steps above).
- Change the
% Traffic
of the existing destination rule to 80. Don't save yet. - Add a new destination rule, selecting the
reviews
service and thev2
subset. Set the% Traffic
to 20. Save the changes.
You can now repeat the steps above to progressively steer traffic to the new
v2
version in gradual steps by changing the weight in each destination (% of
traffic) until all traffic is directed to v2
.
End-of-life for v1
At this point, with all traffic directed to the v2
subset you may want to
clean up the v1
subset and the routes that direct traffic to it.
Via API
Again, we will need to retrieve the service:
curl --request GET \
--url https://${TCCIP}/v1/tenants/tenant/environments/dev/applications/bookinfo/services/reviews \
--header 'accept: application/json' > /tmp/reviews.json
Now we need to update the JSON object to remove the v1
subset and the route
destination with weight 0 for it. We are also going to remove the route that
routed traffic to the v2
service based on request header as it will have the
same effect as the default route now.
jq '.subsets = [
{
"labels": {
"version": "v2"
},
"name": "v2"
}
] | .internalRoutes.httpSettings.routeRules = [
{
"route": {
"destinations": [
{
"local": {
"application": "bookinfo",
"service": "reviews",
"subset": "v2"
},
"weight": 100,
"port": 9080
}
]
}
}
]' /tmp/reviews.json | tee /tmp/reviews-updated.json
Now we just need to update the service via API:
curl --request PUT \
-u admin \
--url https://${TSBIP}/v1/tenants/tenant/environments/dev/applications/bookinfo/services/reviews \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--d @/tmp/reviews-updated.json
Via UI
Navigate to the bookinfo
application, Services
tab, click in the blue link
button next to the reviews
service, then navigate to Route settings
.
- Click in the
+
sign next to theSubsets
title to add a subset. By default, the UI will create subsets based on theversion
label. Click again the+
sign to create a secondv2
subset. - Click the red
x
button for the subsetv1
. Do not save yet. - Click the red
x
button for the first destination rule, the one that routes based on the request header. Do not save yet. - In the remaining destination rule, delete the one that targets subset
v1
which has a% Traffic
of 0. - Save changes