oc new-project workshop-abtest-YourName
13 A/B Testing
Introduction
With AB Testing you can introduce a new version of application and split traffic to the new version gradually instead of suddenly switching from one version to another.
In this lab we will deploy two versions of the same application (app-a and app-b). Then we will configure the route in such a way that the traffic initially would be going to first version (app-a) and then we will gradually shift traffic from app-a to app-b.
Deploy app-a
-
Create a new project. Remember to substitute your YourName.
-
Deploy an application
oc new-app --image-stream=php --code=https://github.com/RedHatWorkshops/ab-deploy --name=app-a
-
Fix the buildConfig
$ oc get builds NAME TYPE FROM STATUS STARTED DURATION app-a-1 Source Git@e2f2dd9 Running 16 seconds ago ## If the build is still running, cancel it first: $ oc cancel-build app-a-1 ## Afterwards, we need to patch the BuildConfig, which is the name of the build without the "-1" $ oc patch bc/app-a --patch '{"spec":{"resources":{"limits":{"memory":"1Gi","cpu":"1000m"}}}}' ## Now, start a new build $ oc start-build app-a ## You can check it's status again by running oc get builds $ oc get builds NAME TYPE FROM STATUS STARTED DURATION app-a-1 Source Git@e2f2dd9 Cancelled (CancelledBuild) About a minute ago 22s app-a-2 Source Git@e2f2dd9 Complete About a minute ago 51s
-
Create a route and name it
abtest
oc expose service app-a --name=abtest
-
Verify the route created and note the URL
oc get route
-
Wait until build completes by watching build logs
oc logs builds/app-a-1
-
Ensure app is deployed
$ oc get po NAME READY STATUS RESTARTS AGE app-a-2-build 0/1 Completed 0 2m2s app-a-6cfbbfbb8d-qzbrb 1/1 Running 0 71s
-
Test the app
$ oc get route abtest # Replace <route> with the hostname found with oc get route. $ curl <route> I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39
This shows I am VERSION 1
as the output.
Let us run curl 20 times with for i in {1..20}; do curl -w "\n" <route>; done
# Replace <route> with the hostname found with oc get route. $ for i in {1..20}; do curl -w "\n" <route>); done I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39
It always hits version 1.
Deploy app-b
-
We have a separate branch
v2
. Same exact app with a small change. This will be namedapp-b
oc new-app --image-stream=php --code=https://github.com/RedHatWorkshops/ab-deploy#v2 --name=app-b
-
Fix the buildConfig
$ oc get builds NAME TYPE FROM STATUS STARTED DURATION app-a-1 Source Git@e2f2dd9 Running 16 seconds ago app-b-1 Source Git@v2 Running 16 seconds ago ## If the build is still running, cancel it first: $ oc cancel-build app-b-1 ## Afterwards, we need to patch the BuildConfig, which is the name of the build without the "-1" $ oc patch bc/app-b --patch '{"spec":{"resources":{"limits":{"memory":"1Gi","cpu":"1000m"}}}}' ## Now, start a new build $ oc start-build app-b ## You can check it's status again by running oc get builds $ oc get builds NAME TYPE FROM STATUS STARTED DURATION app-a-1 Source Git@e2f2dd9 Cancelled (CancelledBuild) About a minute ago 22s app-a-2 Source Git@e2f2dd9 Complete About a minute ago 51s app-b-1 Source Git@v2 Cancelled (CancelledBuild) About a minute ago 7s app-b-2 Source Git@v2 Running 6 seconds ago
-
Watch and Wait until build completes
oc logs builds/app-b-1 -f
-
Note the service created is also called
app-b
oc get svc
Introducing app-b as Canary
Now we will do AB testing by splitting traffic between services app-a
and app-b
. We want to send a small amount of traffic to app-b
.
-
Look at the backends for our route
abtest
$ oc set route-backends abtest NAME KIND TO WEIGHT routes/abtest Service app-a 100
You can see that all the traffic going to service`app-a`
-
Let us send 10% of traffic to service
app-b
, so that it acts as a canary receiving 1 out of 10 requests
$ oc set route-backends abtest app-a=9 app-b=1 route.route.openshift.io/abtest backends updated
Verify the setting now
$ oc set route-backends abtest NAME KIND TO WEIGHT routes/abtest Service app-a 9 (90%) routes/abtest Service app-b 1 (10%)
It shows that the traffic is now split between services app-a
and app-b
in the ratio of 90% and 10%.
-
Test the app now
Let us again run curl 20 times with for i in {1..20}; do curl -w "\n" <route>; done
You’ll see out of every 10 requests 9 go to service app-a
and 1 goes to service app-b
$ for i in {1..20}; do curl -w "\n" <route>; done I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39
This is the behavior of a Canary deployment.
It is used to test your new application and see how it responds in the real world. If the application works as intended, you can increase the amount of traffic to the new version. If there were to be any problems, you can easily shift the traffic back to the old version.
Adjust the traffic split percentages
-
Let us make it 50-50 split this time
$ oc set route-backends abtest --adjust app-b=50% route.route.openshift.io/abtest backends updated
and verify the change to note 50-50 split
$ oc set route-backends abtest NAME KIND TO WEIGHT routes/abtest Service app-a 50 (50%) routes/abtest Service app-b 50 (50%)
-
Test again and note the traffic is evenly distributed between the two versions
$ for i in {1..20}; do curl -w "\n" <route>>; done I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 1 <br><br>My Pod IP is : 10.129.2.39 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48
Shift to new version
-
Let us completely shift to the new version
$ oc set route-backends abtest --adjust app-b=100% route.route.openshift.io/abtest backends updated $ oc set route-backends abtest NAME KIND TO WEIGHT routes/abtest Service app-a 0 (0%) routes/abtest Service app-b 100 (100%)
-
Test again
$ for i in {1..20}; do curl -w "\n" <route>; done I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48 I am VERSION 2 <br><br>My Pod IP is : 10.128.2.48
Notice that all the traffic is now hitting the new version.
Clean up
-
Delete application
oc delete all --all
-
Delete the project; substituting the YourName in the command below
oc delete project workshop-abtest-YourName
Summary
In this lab we have learnt Canary and AB Testing to gradually shift the traffic from one version to another.