Using route-based deployment strategies¶
To roll out application changes to selected traffic in OpenShift Container Platform, you can use route-based deployment strategies with the router and Deployment objects.
These advanced strategies, including blue-green, A/B, and canary, affect specific routes rather than every route that resolves to the application.
The most common route-based strategy is to use a blue-green deployment. The new version (the green version) is brought up for testing and evaluation, while the users still use the stable version (the blue version). When ready, the users are switched to the green version. If a problem arises, you can switch back to the blue version.
Alternatively, you can use an A/B versions strategy in which both versions are active at the same time. With this strategy, some users can use version A, and other users can use version B. You can use this strategy to experiment with user interface changes or other features in order to get user feedback. You can also use it to verify proper operation in a production context where problems impact a limited number of users.
A canary deployment tests the new version but when a problem is detected it quickly falls back to the previous version. This can be done with both of the above strategies.
The route-based deployment strategies do not scale the number of pods in the services. To maintain desired performance characteristics the deployment configurations might have to be scaled.
Proxy shards and traffic splitting¶
To precisely control how traffic reaches application shards in OpenShift Container Platform, you can use relative scale and proxy shards. A proxy shard forwards or splits incoming requests to other services so you can implement percentage-based traffic, comparison testing, or related patterns.
In the simplest configuration, the proxy forwards requests unchanged. In more complex setups, you can duplicate the incoming requests and send to both a separate cluster as well as to a local instance of the application, and compare the result. Other patterns include keeping the caches of a DR installation warm, or sampling incoming traffic for analysis purposes.
Any TCP (or UDP) proxy could be run under the desired shard. Use the oc scale command to alter the relative number of instances serving requests under the proxy shard. For more complex traffic management, consider customizing the OpenShift Container Platform router with proportional balancing capabilities.
N-1 compatibility¶
To run old and new application code side by side during a rollout in OpenShift Container Platform, you need N-1 compatibility. Design your data and schemas so that values written by the new version can be read or safely ignored by the old version.
Applications that have new code and old code running at the same time must be careful to ensure that data written by the new code can be read and handled (or gracefully ignored) by the old version of the code. This is sometimes called schema evolution and is a complex problem.
This can take many forms: data stored on disk, in a database, in a temporary cache, or that is part of a user’s browser session. While most web applications can support rolling deployments, it is important to test and design your application to handle it.
For some applications, the period of time that old code and new code is running side by side is short, so bugs or some failed user transactions are acceptable. For others, the failure pattern may result in the entire application becoming non-functional.
One way to validate N-1 compatibility is to use an A/B deployment: run the old code and new code at the same time in a controlled way in a test environment, and verify that traffic that flows to the new deployment does not cause failures in the old deployment.
Graceful termination¶
To avoid dropping user connections when pods shut down in OpenShift Container Platform, you can rely on graceful termination. The platform sends a TERM signal so your application can stop accepting new traffic, close open connections, and exit before the grace period ends.
On shutdown, OpenShift Container Platform sends a TERM signal to the processes in the container. Application code, on receiving SIGTERM, stop accepting new connections. This ensures that load balancers route traffic to other active instances. The application code then waits until all open connections are closed, or gracefully terminate individual connections at the next opportunity, before exiting.
After the graceful termination period expires, a process that has not exited is sent the KILL signal, which immediately ends the process. The terminationGracePeriodSeconds attribute of a pod or pod template controls the graceful termination period (default 30 seconds) and can be customized per application as necessary.
Setting up a blue-green deployment¶
To switch users from a stable application version to a new one in OpenShift Container Platform, you can set up a blue-green deployment.
Run both versions at once, then point the production route at the new (green) service when you are ready, with the option to switch back to the blue version if needed.
Because many applications depend on persistent data, you must have an application that supports N-1 compatibility, which means it shares data and implements live migration between the database, store, or disk by creating two copies of the data layer.
Consider the data used in testing the new version. If it is the production data, a bug in the new version can break the production version.
Blue-green deployments use two Deployment objects. Both are running, and the one in production depends on the service the route specifies, with each Deployment object exposed to a different service.
Note
Routes are intended for web (HTTP and HTTPS) traffic, so this technique is best suited for web applications.
You can create a new route to the new version and test it. When ready, change the service in the production route to point to the new service and the new (green) version is live.
If necessary, you can roll back to the older (blue) version by switching the service back to the previous version.
Procedure
-
Create two independent application components.
-
Create a copy of the example application running the
v1image under theexample-blueservice: -
Create a second copy that uses the
v2image under theexample-greenservice:
-
-
Create a route that points to the old service:
-
Browse to the application at
bluegreen-example-<project>.<router_domain>to verify you see thev1image. -
Edit the route and change the service name to
example-green: -
To verify that the route has changed, refresh the browser until you see the
v2image.
A/B deployments¶
To test a new application version with a limited share of production traffic in OpenShift Container Platform, you can use an A/B deployment. You send most requests to the stable version, route a fraction to the new version, and increase that fraction as testing progresses.
Because you control the portion of requests to each version, as testing progresses you can increase the fraction of requests to the new version and ultimately stop using the previous version. As you adjust the request load on each version, the number of pods in each service might have to be scaled as well to provide the expected performance.
In addition to upgrading software, you can use this feature to experiment with versions of the user interface. Since some users get the old version and some the new, you can evaluate the user’s reaction to the different versions to inform design decisions.
For this to be effective, both the old and new versions must be similar enough that both can run at the same time. This is common with bug fix releases and when new features do not interfere with the old. The versions require N-1 compatibility to properly work together.
OpenShift Container Platform supports N-1 compatibility through the web console as well as the CLI.
Load balancing for A/B testing¶
To split production traffic between application versions for A/B testing in OpenShift Container Platform, you can configure a route with weighted services. Use the oc set route-backends command or edit the route to assign weights so the router sends a proportional share of requests to each version.
You set up a route with multiple services. Each service handles a version of the application.
Each service is assigned a weight and the portion of requests to each service is the service_weight divided by the sum_of_weights. The weight for each service is distributed to the service’s endpoints so that the sum of the endpoint weights is the service weight.
The route can have up to four services. The weight for the service can be between 0 and 256. When the weight is 0, the service does not participate in load balancing but continues to serve existing persistent connections. When the service weight is not 0, each endpoint has a minimum weight of 1. Because of this, a service with a lot of endpoints can end up with higher weight than intended. In this case, reduce the number of pods to get the expected load balance weight.
Procedure
-
Create the two applications and give them different names. Each creates a
Deploymentobject. The applications are versions of the same program; one is usually the current production version and the other the proposed new version.-
Create the first application. The following example creates an application called
ab-example-a: -
Create the second application:
Both applications are deployed and services are created.
-
-
Make the application available externally via a route. At this point, you can expose either. It can be convenient to expose the current production version first and later modify the route to add the new version.
Browse to the application at
ab-example-a.<project>.<router_domain>to verify that you see the expected version. -
When you deploy the route, the router balances the traffic according to the
weightsspecified for the services. At this point, there is a single service with defaultweight=1so all requests go to it. Adding the other service as analternateBackendsand adjusting theweightsbrings the A/B setup to life. This can be done by theoc set route-backendscommand or by editing the route.Note
When using
alternateBackends, also use theroundrobinload balancing strategy to ensure requests are distributed as expected to the services based on weight.roundrobincan be set for a route by using a route annotation. See the Additional resources section for more information about route annotations.Setting the
oc set route-backendto0means the service does not participate in load balancing, but continues to serve existing persistent connections.Note
Changes to the route just change the portion of traffic to the various services. You might have to scale the deployment to adjust the number of pods to handle the anticipated loads.
To edit the route, run:
Example outputapiVersion: route.openshift.io/v1 kind: Route metadata: name: route-alternate-service annotations: haproxy.router.openshift.io/balance: roundrobin # ... spec: host: ab-example.my-project.my-domain to: kind: Service name: ab-example-a weight: 10 alternateBackends: - kind: Service name: ab-example-b weight: 15 # ...
Managing weights of an existing route by using the web console¶
To split production traffic between application versions for A/B testing in OpenShift Container Platform, you can configure a route with weighted services. Use the oc set route-backends command or edit the route to assign weights so the router sends a proportional share of requests to each version.
Procedure
- Navigate to the Networking → Routes page.
- Click the Options menu
next to the route you want to edit and select Edit Route. - Edit the YAML file. Update the
weightto be an integer between0and256that specifies the relative weight of the target against other target reference objects. The value0suppresses requests to this back end. The default is100. Runoc explain routes.spec.alternateBackendsfor more information about the options. - Click Save.
Managing weights of a new route by using the web console¶
To set traffic weights when you create a new route for A/B testing in OpenShift Container Platform, you can use the web console. Create the route, add an alternate service, and assign relative weights so the router distributes requests between application versions.
Procedure
- Navigate to the Networking → Routes page.
- Click Create Route.
- Enter the route Name.
- Select the Service.
- Click Add Alternate Service.
- Enter a value for Weight and Alternate Service Weight. Enter a number between
0and255that depicts relative weight compared with other targets. The default is100. - Select the Target Port.
- Click Create.
Managing weights using the CLI¶
To manage service traffic weights for A/B testing in OpenShift Container Platform, you can use the oc set route-backends command. Set or adjust weights on a route so the router sends a proportional share of requests to each service.
Procedure
-
To manage the services and corresponding weights load balanced by the route, use the
oc set route-backendscommand:For example, the following sets
ab-example-aas the primary service withweight=198andab-example-bas the first alternate service with aweight=2:This means 99% of traffic is sent to service
ab-example-aand 1% to serviceab-example-b.This command does not scale the deployment. You might be required to do so to have enough pods to handle the request load.
-
Run the command with no flags to verify the current configuration:
-
To override the default values for the load balancing algorithm, adjust the annotation on the route by setting the algorithm to
roundrobin. For a route on OpenShift Container Platform, the default load balancing algorithm is set torandomorsourcevalues.To set the algorithm to
roundrobin, run the command:For Transport Layer Security (TLS) passthrough routes, the default value is
source. For all other routes, the default israndom. -
To alter the weight of an individual service relative to itself or to the primary service, use the
--adjustflag. Specifying a percentage adjusts the service relative to either the primary or the first alternate (if you specify the primary). If there are other backends, their weights are kept proportional to the changed.The following example alters the weight of
ab-example-aandab-example-bservices:Alternatively, alter the weight of a service by specifying a percentage:
By specifying
+before the percentage declaration, you can adjust a weighting relative to the current setting. For example:The
--equalflag sets theweightof all services to100:The
--zeroflag sets theweightof all services to0. All requests then return with a 503 error.Note
Not all routers support multiple or weighted backends.
Create multiple Deployment objects that use one service¶
To serve multiple application versions through a single service in OpenShift Container Platform, you can create multiple Deployment objects that share a common label selector.
Expose one service for those pods so you can scale or update each shard independently while users reach them through the same route.
Procedure
-
Create a new application, adding a label
ab-example=truethat will be common to all shards:$ oc new-app openshift/deployment-example --name=ab-example-a --as-deployment-config=true --labels=ab-example=true --env=SUBTITLE\=shardAThe application is deployed and a service is created. This is the first shard.
-
Make the application available via a route, or use the service IP directly:
-
Browse to the application at
ab-example-<project_name>.<router_domain>to verify you see thev1image. -
Create a second shard based on the same source image and label as the first shard, but with a different tagged version and unique environment variables:
-
At this point, both sets of pods are being served under the route. However, because both browsers (by leaving a connection open) and the router (by default, through a cookie) attempt to preserve your connection to a back-end server, you might not see both shards being returned to you.
To force your browser to one or the other shard:
-
Use the
oc scalecommand to reduce replicas ofab-example-ato0.Refresh your browser to show
v2andshard B(in red). -
Scale
ab-example-ato1replica andab-example-bto0:Refresh your browser to show
v1andshard A(in blue).
-
-
If you trigger a deployment on either shard, only the pods in that shard are affected. You can trigger a deployment by changing the
SUBTITLEenvironment variable in eitherDeploymentobject:or
Additional resources