Join us on June 5th for a Tech Talk with Bill Doerrfeld and Kenn Hussey as we discuss the future of open source. Register now
.
Back to blog
KUBERNETES

5 Essential Kubernetes Deployment Strategies for Optimal Performance

Kay James
December 21, 2023 | 20 min read

5 Essential Kubernetes Deployment Strategies for Optimal Performance

Kubernetes is entirely designed for large-scale deployments, allowing engineers to orchestrate containerized applications across clusters of machines, automatically handling the management, scaling, and operations of these deployments.

Deployment strategies in Kubernetes encompass the methods and practices used to update applications running in a Kubernetes environment. These strategies play a crucial role in application lifecycle management, directly influencing application availability, service continuity, and the capacity to adapt to new requirements.

Deciding on the correct deployment strategy for your use case isn’t trivial. Deployment strategies need to be tailored to specific scenarios and requirements. Each strategy offers unique benefits and challenges, from ensuring zero downtime to prioritizing data and testing. Understanding these nuances aids in selecting the most appropriate approach for different deployment scenarios.

Here, we will take you through the five main Kubernetes deployment strategies you might need to use, how to make the decision, and how to deploy using each of these strategies.

The 5 Main Kubernetes Deployment Strategies

All Kubernetes deployment strategies have unique characteristics and fits for different scenarios. The decision on which strategy to employ depends on various factors, including the application's nature, updates' criticality, user tolerance for disruptions, and organizational capacity for handling potential rollback scenarios.

Here are the five core Kubernetes deployment strategies:

  1. Rolling update. A rolling update is the default deployment strategy for Kubernetes. This strategy ensures that the application remains available during the update. It allows for gradual exposure to traffic to the new version, reducing the risk of significant failures. This is a suitable strategy for general updates where immediate rollback is not a primary concern and minimal impact on availability is crucial.
  2. Blue/Green deployment. This method involves running two identical environments, a new version (green) alongside the old version (blue), and only one serves live production traffic at any time. After deploying the latest version to the idle environment, traffic is switched over if the new version is stable. This deployment strategy facilitates testing the latest version in a production-like environment and allows for immediate rollback by switching traffic back. It works well with a CI/CD pipeline and is ideal for critical applications where the risk of downtime or failure must be minimized, and rapid rollback is necessary.
  3. Canary releases. In canary deployments, the new version is rolled out to a small subset of users before being rolled out to the entire user base. Based on feedback and performance, it is either gradually rolled out to the rest or rolled back. This approach limits the impact of any new bugs and allows for real-world testing on a small scale before broad deployment. It is helpful for applications where user feedback or real-world performance data is crucial for validation.
  4. A/B testing. A/B testing involves routing a subset of user traffic to a new version of the application to test new features or changes, typically driven by user behavior or other criteria. A/B testing allows for comparative analysis between different versions based on specific user segments' responses. It is an effective strategy for testing feature variations and user experience enhancements where user behavior influences decisions.
  5. Shadow deployment. In this strategy, the new version of the application runs alongside the old version but does not serve real user traffic. Instead, real-world requests are mirrored to the new version for testing purposes. This strategy enables validation of the new version under real-world load without exposing users to changes. It is suitable for performance testing under actual load conditions and validating changes without impacting users.

What makes all of this possible easily with Kubernetes is the Kubernetes Deployment Controller. The primary objective of a deployment controller is to maintain the desired state of an application as defined by the user. It ensures that a specified number of pod replicas are running at any given time, automating the deployment and scaling of applications and their underlying infrastructure. Let’s look more closely at each of these Kubernetes deployment stratgies and how to deploy an application using them.

Rolling Update

Rolling updates are the default strategy for deployments in Kubernetes. This process allows you to update your application in Kubernetes with minimal downtime, as it ensures that some instances of your application are always running during the update process.

Setting up a rolling update in Kubernetes involves configuring your deployment to update Pods in a rolling manner, replacing existing Pods with new ones incrementally.

Let’s say we’re initially using this simple deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 3
selector:
matchLabels:
app: example
template:
metadata:
labels:
app: example
spec:
containers:
- name: nginx
image: nginx:1.16

We create the deployment using:

kubectl apply -f deployment.yaml

Then, we can check the status of this current deployment:

kubectl get deployment example-deployment

To perform a rolling update, you only need to change the container image or other specifications. For example, update the image version in your deployment YAML:

spec:
...
containers:
- name: nginx
image: nginx:1.17 # Update the image version

Then apply the update using:

kubectl apply -f deployment.yaml

Kubernetes will start the rolling update process. It will incrementally terminate existing Pods and replace them with new Pods. You can monitor the rollout status:

kubectl rollout status deployment/example-deploymen

Once the update is complete, verify the new version is running:

kubectl get pods -l app=example

If something goes wrong, you can rollback to the newer version:

kubectl rollout undo deployment/example-deployment

Given that we are just updating the image used for our application, we can also use kubectl set image to make the update

kubectl set image deployment/[DEPLOYMENT_NAME] [CONTAINER_NAME]=[NEW_IMAGE]:[TAG]

You can define the update strategy and parameters (like max surge and max unavailable) in the deployment YAML under spec.strategy. Having readiness and liveness probes in your containers is good practice to ensure smooth rolling updates.

Blue/Green Deployment

Setting up a blue/green deployment in Kubernetes involves deploying two separate but identical environments: one (Blue) is the current production environment, and the other (Green) is the new version of your application. Traffic is switched from Blue to Green once the newer version is ready. This setup ensures minimal downtime and allows easy rollback in case of issues.

First, you need to deploy the current version of your application (Blue). Create a deployment YAML file (blue-deployment.yaml) for your application:

apiVersion: apps/v1
kind: Deployment
metadata:
name: blue-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: blue
template:
metadata:
labels:
app: my-app
version: blue
spec:
containers:
- name: my-app
image: my-app:1.0 # Use your application's image

Deploy it using:

kubectl apply -f blue-deployment.yaml

Expose the Blue deployment using a Kubernetes service. Create a service YAML file (blue-service.yaml):

apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
version: blue
ports:
- protocol: TCP
port: 80
targetPort: 8080 # The port your app listens on

Apply it using:

kubectl apply -f blue-service.yaml

Next, deploy the new version of your application (Green). Create a similar deployment YAML file (green-deployment.yaml), but with the latest version of your application:

apiVersion: apps/v1
kind: Deployment
metadata:
name: green-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: green
template:
metadata:
labels:
app: my-app
version: green
spec:
containers:
- name: my-app
image: my-app:2.0 # New version of your application

Deploy it using:

kubectl apply -f green-deployment.yaml

Before exposing the Green deployment to actual users, you should access it internally for testing purposes. To do this, you can use Kubernetes port forwarding to access the Green deployment from your local machine:

kubectl port-forward deployment/green-app 8080:80

This command forwards the local port 8080 to port 80 on the Green deployment, allowing you to access it via localhost:8080. Then, create an internal service (ClusterIP) that exposes the Green deployment only within the cluster:

apiVersion: v1
kind: Service
metadata:
name: green-app-internal
spec:
selector:
app: my-app
version: green
ports:
- protocol: TCP
port: 80
targetPort: 8080

You can then run your suite of automated tests against the Green deployment by configuring your test suite to target the Green deployment's access point. These could be unit tests, integration tests, or end-to-end tests, depending on your application.

Once confident with the Green deployment, update the service to redirect traffic from Blue to Green. Modify the blue-service.yaml file to point to the Green deployment:

spec:
selector:
app: my-app
version: green

Apply the changes using:

kubectl apply -f blue-service.yam

After switching, monitor the Green deployment closely to ensure everything is running smoothly. If the Green deployment is stable, you can then move your development to the Blue deployment. You then work on Blue until that version is ready to be deployed and reverse the above process. You switch between Blue and Green, always using one for production and one for development.

Out of all Kubernetes deployment strategies, Blue/Green deployments typically involve the most manual intervention, especially in switching the service selector from Blue to Green. It's essential to have good monitoring and logging in place to quickly detect any issues after the switch. You might consider automating the switch and cleanup process as part of your CI/CD pipeline.

Canary Deployment

Setting up a canary deployment (sometimes called progressive delivery) in Kubernetes involves deploying a new version of your application to a small subset of users before rolling it out to everyone. This approach allows you to test the newer version in a production environment with minimal risk.

First, you need your primary or stable deployment, which serves the majority of the traffic. Create a deployment YAML file (primary-deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: primary-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
track: stable
template:
metadata:
labels:
app: my-app
track: stable
spec:
containers:
- name: my-app
image: my-app:1.0 # Replace with your current application version

Deploy it using:

kubectl apply -f primary-deployment.yaml

Create a service that targets the stable deployment. Here's an example service definition (primary-service.yaml):

apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
track: stable
ports:
- protocol: TCP
port: 80
targetPort: 8080 # Replace with the port your app listens on

Apply the service using:

kubectl apply -f primary-service.yaml

Now, deploy the canary version of your application. Create a similar deployment (canary-deployment.yaml), but with the new version of your application and fewer replicas:

apiVersion: apps/v1
kind: Deployment
metadata:
name: canary-app
spec:
replicas: 1 # Less than the primary to limit exposure
selector:
matchLabels:
app: my-app
track: canary
template:
metadata:
labels:
app: my-app
track: canary
spec:
containers:
- name: my-app
image: my-app:2.0 # Replace with your new application version

Deploy the canary version using:

kubectl apply -f canary-deployment.yaml

To split traffic between your primary and canary deployments, you can use a service mesh like Istio or Kubernetes Ingress if it supports advanced traffic routing rules. Here's a simplified example using Kubernetes Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
spec:
rules:
- http:
paths:
- path: /my-app
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
# Additional rules to route a percentage of traffic to the canary service

Canary deployments in Kubernetes might require additional tools or service mesh capabilities to manage and route traffic effectively. Edge Stack allows you to easily deploy canary releases, using a weighted round-robin scheme to route traffic between multiple services. Here’s an example:

---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
name: quote-backend
spec:
prefix: /backend/
service: quote
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
name: quote-backend2
spec:
prefix: /backend/
service: quotev2
weight: 10

This would push 10% of the traffic to the quotev2 service.


Closely monitor the canary deployment for any issues. Look at logs, metrics, and user feedback.

Based on the monitoring feedback, either gradually increase the traffic to the canary deployment or roll back if issues are detected. To rollback, simply reduce or remove the replicas in the canary deployment and route the traffic back to the primary deployment.

A/B Testing

Setting up an A/B testing deployment in Kubernetes requires complex routing rules, often achieved with a service mesh like Istio or advanced Ingress capabilities. The goal is to serve different versions of your application state to different user segments based on specific criteria (e.g., user type, location, etc.).

First, you must deploy both versions (A and B) of your application. Create separate deployment YAML files for each:


Version A (deployment-a.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: app-version-a
spec:
replicas: 2
selector:
matchLabels:
app: my-app
version: A
template:
metadata:
labels:
app: my-app
version: A
spec:
containers:
- name: my-app
image: my-app:A # Replace with your Version A image

Version B (deployment-b.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: app-version-b
spec:
replicas: 2
selector:
matchLabels:
app: my-app
version: B
template:
metadata:
labels:
app: my-app
version: B
spec:
containers:
- name: my-app
image: my-app:B # Replace with your Version B image

Deploy both versions using:

kubectl apply -f deployment-a.yaml
kubectl apply -f deployment-b.yaml

Then create Kubernetes services for each deployment. Service for Version A (service-a.yaml):

apiVersion: v1
kind: Service
metadata:
name: service-a
spec:
selector:
app: my-app
version: A
ports:
- protocol: TCP
port: 80
targetPort: 8080 # Your app's port

Service for Version B (service-b.yaml):

apiVersion: v1
kind: Service
metadata:
name: service-b
spec:
selector:
app: my-app
version: B
ports:
- protocol: TCP
port: 80
targetPort: 8080 # Your app's port

Deploy the services using:

kubectl apply -f service-a.yaml

kubectl apply -f service-b.yaml

Using Istio or a capable Ingress controller, configure routing rules to distribute traffic between the two versions based on your A/B testing criteria. Here’s a simplified example using Istio:


Istio Virtual Service (virtual-service.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-app
spec:
hosts:
- my-app
http:
- match:
- headers:
user-type:
exact: "A"
route:
- destination:
host: service-a
- route:
- destination:
host: service-b

This allows for routing traffic for users with a user-type header of "A" to Version A, and all others to Version B. Apply the virtual service using:

kubectl apply -f virtual-service.yaml

Monitor both versions of your application. Analyze metrics, user feedback, and other relevant data to determine the performance of each version. After your A/B test concludes, clean up by scaling down or removing the less successful version and updating the routing rules accordingly.

The specifics of how you configure A/B testing may vary based on your service mesh or Ingress controller's capabilities. Proper monitoring and analytics are crucial to gather meaningful insights from your A/B test. Ensure that your application versions are instrumented to track the necessary metrics for your test.

Shadow Deployment

The final Kubernetes deployment strategy we’re highlighting is shadow deployment. Setting up a shadow deployment in Kubernetes involves deploying a new version of your application (the "shadow") alongside the existing version. The shadow version receives a copy of the live traffic but does not impact the actual user experience. This setup allows you to test traffic to the new version under real-world conditions without exposing users to potential issues.

Shadow deployments can be complex but achieved easily in Edge Stack by defining appropriate mappings in your YAML configuration files. Here's how you can put together these mapping YAML files for shadow deployments

First, define the mapping for your primary service. This is the service currently handling your production traffic (primary-service.yaml):

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
name: primary-service-mapping
spec:
prefix: /service/
service: primary-service

In this example, traffic going to /service/ is routed to a service named primary-service. Next, define the mapping for your shadow service. This service will receive a copy of the live traffic (shadow-service.yaml):

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
name: shadow-service-mapping
spec:
prefix: /service/
service: shadow-service
shadow: true

Here, we specify the same prefix as the primary service, but set shadow: true to indicate that this is a shadow mapping. Traffic to /service/ will also be mirrored to shadow-service. Apply these mappings to your Kubernetes cluster using kubectl:

kubectl apply -f primary-service.yaml

kubectl apply -f shadow-service.yaml

Ensure that Edge Stack API Gateway is running in your cluster and is configured to handle traffic for your services. Since the shadow service receives a copy of the live traffic, monitoring it closely for testing is crucial. Ensure it doesn't impact your live environment, especially in terms of data integrity and system performance.

Shadow deployments are useful for testing in production-like environments without affecting the actual user experience. Be cautious with shadow services that interact with databases or external systems, as they might cause unintended side effects. Shadow deployments can generate significant load since they process real traffic in parallel with the primary application. Ensure your infrastructure can handle this additional load.

Using shadow deployments with Edge Stack API Gateway, you can test new features or changes under real-world conditions.

Choosing the Right Strategy

Selecting an appropriate Kubernetes deployment strategy involves several key considerations. This decision impacts not only the immediate deployment process but also the ongoing maintenance and scalability of the application.

  1. Application criticality. Evaluate the importance of the application in the business context. High-impact applications may require strategies like blue/green deployments for minimal downtime. Consider the tolerance for downtime. Some applications can afford brief outages, making strategies like rolling updates feasible.
  2. Update frequency. For applications requiring frequent updates, choose a strategy that offers speed and efficiency, such as rolling updates or canary releases. In cases where updates are less frequent but significant, strategies like blue/green deployments might be more suitable.
  3. Risk tolerance: Assess the acceptable level of risk. Canary releases and A/B testing allow for controlled exposure and are suitable for applications where new features might carry higher risks. Blue/green deployments provide a safer option with easy rollback capabilities for applications where stability is paramount.
  4. Resource availability. Consider the available infrastructure and resources. Some strategies, like blue/green deployments, require double the resources as both versions run simultaneously. Ensure sufficient capacity and budget to support the chosen strategy without affecting other operations.
  5. Team expertise. The team's familiarity with Kubernetes and its deployment strategies plays a critical role. Complex strategies require skilled personnel for effective implementation. Training and experience levels should align with the complexity of the chosen deployment strategy.
  6. User impact. Consider the end-user experience. Strategies like A/B testing and canary releases allow for user feedback incorporation and minimal disruption. Ensure the strategy aligns with user expectations and service level agreements (SLAs).
  7. Compliance and security. Review compliance requirements and security implications. Specific strategies may better align with regulatory and security standards. Assess the impact of each strategy on data integrity and confidentiality.
  8. Rollback plans. A robust rollback plan is essential. Ensure the chosen strategy allows for swift and effective rollback in case of deployment issues. Test rollback procedures regularly as part of the deployment strategy.

No single strategy fits all scenarios. The decision should stem from thoroughly analyzing the application requirements, organizational context, and operational capabilities. The chosen strategy should align with the broader goals of reliability, scalability, and continuous improvement.

Learn more about Edge Stack API Gateway to understand how it can simplify your Kubernetes deployments. You can also read more about the specific Kubernetes deployment strategies such as canary releases, shadowing, and the use of ingress controllers in our blog and documentation.

Edge Stack API Gateway