Scenario: A/B testing and progressive traffic shift
A/B testing enables you to compare two versions of an ML model, and select a winner based on a (business) reward metric. In this tutorial, you will:
Perform A/B testing.
Specify user-engagement as the reward metric. This metric will be mocked by Iter8 in this tutorial.
Combine A/B testing with progressive traffic shifting. Iter8 will progressively shift traffic towards the winner and promote it at the end as depicted below.
Iter8 defines a custom K8s resource called Metric that makes it easy to use metrics from RESTful metric providers like Prometheus, New Relic, Sysdig and Elastic during experiments.
For the purpose of this tutorial, you will mock a number of metrics as follows.
apiVersion:iter8.tools/v2alpha2kind:Metricmetadata:labels:creator:iter8name:user-engagementspec:description:Number of error responsestype:Gaugemock:-name:weblevel:5-name:web2level:10---apiVersion:iter8.tools/v2alpha2kind:Metricmetadata:labels:creator:iter8name:error-countspec:description:Number of error responsesjqExpression:.data.result[0].value[1] | tonumberparams:-name:queryvalue:|sum(increase(response_total{status_code=~'5..',deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s])) or on() vector(0)provider:prometheustype:CounterurlTemplate:http://prometheus.linkerd-viz:9090/api/v1/query---apiVersion:iter8.tools/v2alpha2kind:Metricmetadata:labels:creator:iter8name:error-ratespec:description:Fraction of requests with error responsesjqExpression:.data.result[0].value[1] | tonumberparams:-name:queryvalue:|(sum(increase(response_total{status_code=~'5..',deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s])) or on() vector(0)) / sum(increase(request_total{deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s]))provider:prometheussampleSize:request-counttype:GaugeurlTemplate:http://prometheus.linkerd-viz:9090/api/v1/query---apiVersion:iter8.tools/v2alpha2kind:Metricmetadata:labels:creator:iter8name:le5ms-latency-percentilespec:description:Less than 5 ms latencyjqExpression:.data.result[0].value[1] | tonumberparams:-name:queryvalue:|(sum(increase(response_latency_ms_bucket{le='5',deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s])) or on() vector(0)) / sum(increase(response_latency_ms_bucket{le='+Inf',deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s])) or on() vector(0)provider:prometheussampleSize:iter8-linkerd/request-counttype:GaugeurlTemplate:http://prometheus.linkerd-viz:9090/api/v1/query---apiVersion:iter8.tools/v2alpha2kind:Metricmetadata:labels:creator:iter8name:mean-latencyspec:description:Mean latencyjqExpression:.data.result[0].value[1] | tonumberparams:-name:queryvalue:|(sum(increase(response_latency_ms_sum{deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s])) or on() vector(0)) / (sum(increase(request_total{deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s])) or on() vector(0))provider:prometheussampleSize:request-counttype:Gaugeunits:millisecondsurlTemplate:http://prometheus.linkerd-viz:9090/api/v1/query---apiVersion:iter8.tools/v2alpha2kind:Metricmetadata:labels:creator:iter8name:request-countspec:description:Number of requestsjqExpression:.data.result[0].value[1] | tonumberparams:-name:queryvalue:|sum(increase(request_total{deployment='$name',namespace='$namespace',direction='inbound',tls='true'}[${elapsedTime}s]))provider:prometheustype:CounterurlTemplate:http://prometheus.linkerd-viz:9090/api/v1/query
apiVersion:iter8.tools/v2alpha2kind:Experimentmetadata:name:quickstart-expspec:# target identifies the service under experimentation using its fully qualified nametarget:test/web-traffic-splitstrategy:# this experiment will perform an A/B testtestingPattern:A/B# this experiment will progressively shift traffic to the winning versiondeploymentPattern:Progressiveactions:# when the experiment completes, promote the winning version using kubectl applyfinish:-if:CandidateWon()run:kubectl -n test apply -f https://raw.githubusercontent.com/iter8-tools/iter8/master/samples/linkerd/quickstart/vs-for-v2.yaml-if:not CandidateWon()run:kubectl -n test apply -f https://raw.githubusercontent.com/iter8-tools/iter8/master/samples/linkerd/quickstart/vs-for-v1.yamlcriteria:rewards:# (business) reward metric to optimize in this experiment-metric:default/user-engagementpreferredDirection:Highobjectives:# used for validating versions-metric:default/mean-latencyupperLimit:300-metric:default/error-rateupperLimit:"0.01"requestCount:default/request-countduration:# product of fields determines length of the experimentintervalSeconds:10iterationsPerLoop:5versionInfo:# information about the app versions used in this experimentbaseline:name:webvariables:-name:namespace# used by final action if this version is the winnervalue:testweightObjRef:apiVersion:split.smi-spec.io/v1alpha2kind:TrafficSplitname:web-traffic-splitfieldPath:.spec.backends[0].weightcandidates:-name:web2variables:-name:namespace# used by final action if this version is the winnervalue:testweightObjRef:apiVersion:split.smi-spec.io/v1alpha2kind:TrafficSplitname:web-traffic-splitfieldPath:.spec.backends[1].weight