DEV Community

Nicholas Osi
Nicholas Osi

Posted on

Jenkins Pipeline Essentials: Deploying Applications to Kubernetes with Downtime Considerations

Deploying applications to Kubernetes using Jenkins pipelines is a robust way to automate your CI/CD processes. This guide will walk you through setting up a Jenkins pipeline to deploy an application to a Kubernetes cluster, handling scenarios that may involve downtime during deployment. Whether you’re deploying updates that require downtime or managing releases in a controlled manner, this step-by-step approach will help you achieve a smooth deployment process.

1. Prerequisites

Before you begin, ensure you have the following:

  • Jenkins Server: A running Jenkins instance. You can set this up on-premises or use a cloud-based Jenkins service.
  • Kubernetes Cluster: Access to a Kubernetes cluster where the application will be deployed. This can be a managed service like Azure Kubernetes Service (AKS), Google Kubernetes Engine (GKE), Amazon EKS, or a self-managed cluster.
  • Docker Registry: Access to a Docker registry (e.g., Docker Hub, Azure Container Registry, AWS ECR) to store your Docker images.
  • Source Code Repository: A Git repository (e.g., GitHub, GitLab, Bitbucket) containing your application code.
  • kubectl Configured: kubectl installed and configured to interact with your Kubernetes cluster.
  • Basic Knowledge: Familiarity with Jenkins, Docker, Kubernetes, and CI/CD concepts.

— -

2. Repository Setup

Organize your code repository to include your application code, Dockerfile, and Kubernetes manifests. Here’s how to structure your repository:

my-app/
├── src/
│ └── … (your application source code)
├── Dockerfile
├── k8s/
│ ├── deployment.yaml
│ └── service.yaml
├── Jenkinsfile
└── README.md
Enter fullscreen mode Exit fullscreen mode

Application Code

Ensure your application code is well-organized within the src/ directory. This example assumes a simple web application, but the steps apply to various types of applications.

Dockerfile

Create a Dockerfile at the root of your repository to containerize your application. Here’s an example for a Node.js application:

# Use an official Node.js runtime as the base image
FROM node:14-alpine

# Set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code
COPY . .

# Expose the application port
EXPOSE 3000

# Define the command to run the application
CMD [“npm”, “start”]
Enter fullscreen mode Exit fullscreen mode

Note: Adjust the Dockerfile according to your application’s requirements.

Kubernetes Manifests

Create Kubernetes manifests to define the desired state of your application in the cluster.

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
— name: my-app-container
image: your-docker-registry/my-app:latest
ports:
— containerPort: 3000
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10


#### **service.yaml**

apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
— protocol: TCP
port: 80
targetPort: 3000
Enter fullscreen mode Exit fullscreen mode

Note: Replace your-docker-registry with your actual Docker registry path.

— -

3. Jenkins Setup

Install Jenkins

If you haven’t set up Jenkins yet, follow these steps:

  1. Download Jenkins: Visit the official Jenkins website and download the appropriate installer for your operating system.

  2. Install Jenkins: Follow the installation instructions for your platform.

  3. Start Jenkins: Ensure Jenkins is running. By default, it runs on http://localhost:8080.

  4. Unlock Jenkins: On first launch, Jenkins will prompt you to unlock it using an initial admin password. Follow the instructions provided.

  5. Install Suggested Plugins: Jenkins will offer to install suggested plugins. It’s recommended to proceed with this option.

  6. Create Admin User: Set up your first admin user as prompted.

Install Necessary Plugins

To integrate Jenkins with Kubernetes and Docker, install the following plugins:

  1. Kubernetes Plugin: Allows Jenkins to interact with Kubernetes clusters.
  2. Docker Pipeline Plugin: Enables building and pushing Docker images within Jenkins pipelines.
  3. Git Plugin: Facilitates cloning Git repositories.
  4. Credentials Binding Plugin: Manages sensitive data like passwords and SSH keys.
  5. Pipeline Plugin: Provides the foundational pipeline capabilities.

To install plugins:

  1. Navigate to Manage Jenkins > Manage Plugins.
  2. Go to the Available tab.
  3. Search for each plugin by name.
  4. Select the checkbox next to the plugin and click Install without restart.

Configure Credentials

Securely store credentials required for accessing Docker registries and the Kubernetes cluster.

Docker Registry Credentials

  1. Navigate to Manage Jenkins > Manage Credentials.
  2. Select the appropriate domain (e.g., Global).
  3. Click Add Credentials.
  4. Choose Username with password.
  5. Enter your Docker registry username and password.
  6. Assign an ID (e.g., docker-registry-credentials) and a description.
  7. Click OK.

Kubernetes Cluster Credentials

Depending on your Kubernetes cluster setup, you might use a kubeconfig file or a service account token.

Using kubeconfig:

  1. Navigate to Manage Jenkins > Manage Credentials.
  2. Click Add Credentials.
  3. Choose Secret file.
  4. Upload your kubeconfig file.
  5. Assign an ID (e.g., kubeconfig-file) and a description.
  6. Click OK.

Using Service Account Token:

  1. Create a Kubernetes service account with the necessary permissions.
  2. Obtain the token.
  3. Navigate to Manage Jenkins > Manage Credentials.
  4. Click Add Credentials.
  5. Choose Secret text.
  6. Paste the service account token.
  7. Assign an ID (e.g., k8s-service-account-token) and a description.
  8. Click OK.

Note: Ensure that the credentials have the necessary permissions to deploy applications to your Kubernetes cluster.

— -

4. Creating the Jenkins Pipeline

Pipeline Overview

The Jenkins pipeline will perform the following steps:

  1. Checkout Code: Clone the repository containing the application code, Dockerfile, and Kubernetes manifests.
  2. Build Docker Image: Build the Docker image of the application.
  3. Push Docker Image: Push the built image to the Docker registry.
  4. Deploy to Kubernetes: Apply the Kubernetes manifests to deploy the application.
  5. Handle Downtime: Manage application downtime during deployment, if necessary.

Jenkinsfile Configuration

Create a Jenkinsfile in the root of your repository to define the pipeline. Here’s a sample Jenkinsfile with comments explaining each section:

pipeline {
agent any

environment {
// Docker Registry Variables
DOCKER_REGISTRY = ‘your-docker-registry’ // e.g., docker.io/username
IMAGE_NAME = ‘my-app’
IMAGE_TAG = “${env.BUILD_NUMBER}”
IMAGE_FULL_NAME = “${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}”

// Kubernetes Variables
KUBECONFIG_CREDENTIAL_ID = ‘kubeconfig-file’ // ID of the kubeconfig secret
KUBE_NAMESPACE = ‘default’ // Replace with your namespace
}

stages {
stage(‘Checkout’) {
steps {
// Clone the repository
git branch: ‘main’, url: ‘https://github.com/your-repo/my-app.git’
}
}

stage(‘Build Docker Image’) {
steps {
script {
// Build the Docker image
docker.build(“${IMAGE_FULL_NAME}”)
}
}
}

stage(‘Push Docker Image’) {
steps {
script {
// Push the Docker image to the registry
docker.withRegistry(‘https://your-docker-registry’, ‘docker-registry-credentials’) {
docker.image(“${IMAGE_FULL_NAME}”).push()
}
}
}
}

stage(‘Deploy to Kubernetes’) {
steps {
script {
// Use the kubeconfig file for kubectl commands
withCredentials([file(credentialsId: “${KUBECONFIG_CREDENTIAL_ID}”, variable: ‘KUBECONFIG’)]) {
sh ‘’’

Optional: Scale down the deployment to handle downtime

kubectl scale deployment my-app-deployment — replicas=0 -n ${KUBE_NAMESPACE}

Apply the Kubernetes manifests

kubectl apply -f k8s/deployment.yaml -n ${KUBE_NAMESPACE}
kubectl apply -f k8s/service.yaml -n ${KUBE_NAMESPACE}

Optional: Scale the deployment back up

kubectl scale deployment my-app-deployment — replicas=3 -n ${KUBE_NAMESPACE}
‘’’
}
}
}
}
}

post {
success {
echo ‘Deployment succeeded!’
}
failure {
echo ‘Deployment failed.’
}
}
}


**Explanation of the Jenkinsfile:**

- **Environment Variables**: Define variables for the Docker registry, image name, tag, and Kubernetes configurations.
- **Stages**:
— **Checkout**: Clones the Git repository.
— **Build Docker Image**: Builds the Docker image using the `Dockerfile`.
— **Push Docker Image**: Pushes the built image to the specified Docker registry.
— **Deploy to Kubernetes**: Uses `kubectl` to apply the Kubernetes manifests. To handle downtime, the deployment is scaled down to zero replicas before applying the new configuration and then scaled back up.

**Note:** Replace placeholders like `your-docker-registry`, `https://github.com/your-repo/my-app.git`, and credential IDs with your actual values.

— -

## **5. Handling Downtime During Deployment**

While Kubernetes supports zero-downtime deployments through strategies like rolling updates, there are scenarios where you might need to intentionally introduce downtime (e.g., for database migrations or significant changes). This section explains how to manage such scenarios within the Jenkins pipeline.

### **Strategy Overview**

1. **Scale Down**: Reduce the number of replicas to zero to stop serving traffic.
2. **Deploy Updates**: Apply the new Kubernetes manifests (e.g., updated Deployment).
3. **Scale Up**: Increase the number of replicas to resume serving traffic.

**Pros:**

- Ensures that users are not served a partially updated application.
- Useful for critical updates that require the application to be fully stopped during deployment.

**Cons:**

- Results in application downtime, affecting user experience.
- Not suitable for high-availability applications where uptime is critical.

### **Implementing Downtime in Jenkins Pipeline**

The `Deploy to Kubernetes` stage in the Jenkinsfile handles downtime by scaling the deployment down before applying updates and scaling it back up afterward.

**Detailed Steps:**

1. **Scale Down Deployment**

kubectl scale deployment my-app-deployment — replicas=0 -n default
Enter fullscreen mode Exit fullscreen mode
  • Sets the number of replicas to zero, effectively stopping all running pods.
  1. Apply Kubernetes Manifests

kubectl apply -f k8s/deployment.yaml -n default
kubectl apply -f k8s/service.yaml -n default


- Applies the updated Deployment and Service configurations.

3. **Scale Up Deployment**

kubectl scale deployment my-app-deployment — replicas=3 -n default
Enter fullscreen mode Exit fullscreen mode
  • Restores the desired number of replicas, starting new pods with the updated configuration.

Enhanced Jenkinsfile with Downtime Handling:

stage(‘Deploy to Kubernetes’) {
steps {
script {
// Use the kubeconfig file for kubectl commands
withCredentials([file(credentialsId: “${KUBECONFIG_CREDENTIAL_ID}”, variable: ‘KUBECONFIG’)]) {
sh ‘’’
echo “Scaling down the deployment to zero replicas for downtime…”
kubectl scale deployment my-app-deployment — replicas=0 -n ${KUBE_NAMESPACE}

echo “Applying Kubernetes manifests…”
kubectl apply -f k8s/deployment.yaml -n ${KUBE_NAMESPACE}
kubectl apply -f k8s/service.yaml -n ${KUBE_NAMESPACE}

echo “Scaling up the deployment to desired replicas…”
kubectl scale deployment my-app-deployment — replicas=3 -n ${KUBE_NAMESPACE}
‘’’
}
}
}
}


**Alternative Approach: Using Kubernetes Rolling Updates**

If downtime is not acceptable, consider using Kubernetes’ rolling update strategy, which updates pods incrementally without taking the entire application offline.

**Modify `deployment.yaml` for Rolling Updates:**

spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
…
Enter fullscreen mode Exit fullscreen mode

Adjust Jenkinsfile Deployment Stage:

Remove scaling steps and allow Kubernetes to handle updates seamlessly.

stage(‘Deploy to Kubernetes’) {
steps {
script {
withCredentials([file(credentialsId: “${KUBECONFIG_CREDENTIAL_ID}”, variable: ‘KUBECONFIG’)]) {
sh ‘’’
echo “Applying Kubernetes manifests with rolling update…”
kubectl apply -f k8s/deployment.yaml -n ${KUBE_NAMESPACE}
kubectl apply -f k8s/service.yaml -n ${KUBE_NAMESPACE}
‘’’
}
}
}
}


**Recommendation:** Use rolling updates for zero-downtime deployments. Reserve downtime handling strategies for cases where they are absolutely necessary.

— -

## **6. Running and Validating the Pipeline**

Once the Jenkins pipeline is configured, follow these steps to run and validate the deployment.

### **Create a New Jenkins Pipeline Job**

1. **Navigate to Jenkins Dashboard**: Open your Jenkins instance in the browser.

2. **Create a New Item**:

- Click on `New Item`.
— Enter a name for your pipeline (e.g., `Deploy-My-App`).
— Select `Pipeline` as the project type.
— Click `OK`.

3. **Configure the Pipeline**:

- **Description**: Optionally, add a description.
— **Pipeline**:
— **Definition**: Choose `Pipeline script from SCM`.
— **SCM**: Select `Git`.
— **Repository URL**: Enter your Git repository URL (e.g., `https://github.com/your-repo/my-app.git`).
— **Credentials**: Select or add credentials if your repository is private.
— **Branches to Build**: Specify the branch (e.g., `main`).
— **Script Path**: Enter `Jenkinsfile` if it’s in the root directory.

4. **Save**: Click `Save` to create the pipeline job.

### **Trigger the Pipeline**

1. **Manual Trigger**:

- Navigate to the pipeline job.
— Click `Build Now` to start the pipeline manually.

2. **Automatic Trigger**:

- Ensure that your repository is set to trigger Jenkins builds on commits (using webhooks).
— Push changes to the repository to trigger the pipeline automatically.

### **Monitor the Pipeline**

1. **View Build Progress**:

- Click on the running build to see real-time logs.
— Monitor each stage (`Checkout`, `Build Docker Image`, `Push Docker Image`, `Deploy to Kubernetes`).

2. **Handle Failures**:

- If any stage fails, Jenkins will mark the build as `FAILED`.
— Review the console output to identify and fix issues.

### **Validate Deployment in Kubernetes**

1. **Check Pods**:

kubectl get pods -n default
Enter fullscreen mode Exit fullscreen mode
  • Ensure that the pods are running and match the desired replica count.
  1. Verify Service:
  • Obtain the service’s external IP:

kubectl get services -n default


- Access the application via the external IP to verify it’s working as expected.

3. **Check Application Logs**:

kubectl logs <pod-name> -n default
Enter fullscreen mode Exit fullscreen mode
  • Review application logs for any runtime issues.

— -

7. Best Practices

To ensure a reliable and maintainable CI/CD pipeline, consider the following best practices:

1. Use Version Control for Manifests

  • Store all Kubernetes manifests in version control alongside your application code.
  • This ensures that infrastructure changes are tracked and can be audited.

2. Implement Infrastructure as Code (IaC)

  • Use tools like Terraform or Helm to manage Kubernetes resources.
  • This promotes consistency and reusability across environments.

3. Secure Credentials

  • Avoid hardcoding sensitive information.
  • Use Jenkins credentials and Kubernetes secrets to manage sensitive data securely.

4. Enable Pipeline as Code

  • Define your pipeline using a Jenkinsfile stored in the repository.
  • This allows versioning and easier collaboration.

5. Implement Rollback Mechanisms

  • Configure your pipeline to handle failures gracefully.
  • Use Kubernetes deployment strategies to rollback in case of deployment failures.

6. Monitor and Log Deployments

  • Integrate monitoring tools (e.g., Prometheus, Grafana) to observe application performance.
  • Centralize logs using tools like ELK Stack or Fluentd for easier troubleshooting.

7. Optimize Docker Images

  • Use multi-stage builds to minimize image size.
  • Regularly scan images for vulnerabilities.

8. Test Before Deployment

  • Incorporate automated testing (unit, integration, end-to-end) in your pipeline.
  • Validate that the application works as expected before deploying to production.

9. Handle Downtime Carefully

  • Prefer zero-downtime deployment strategies like rolling updates.
  • If downtime is necessary, communicate it clearly to stakeholders and plan deployments during maintenance windows.

10. Document the Pipeline

  • Maintain clear documentation of your pipeline stages, configurations, and dependencies.
  • This aids in onboarding new team members and troubleshooting issues.

— -

8. Conclusion

Automating application deployments to Kubernetes using Jenkins pipelines streamlines your CI/CD processes, enhances consistency, and reduces the potential for human error. By following this step-by-step guide, you can set up a robust pipeline that builds, tests, and deploys your applications efficiently. Handling downtime during deployments is crucial for maintaining application availability and user satisfaction. While Kubernetes offers advanced deployment strategies to minimize or eliminate downtime, understanding when and how to manage downtime ensures that your deployments align with your application’s operational requirements.

Adhering to best practices around security, version control, testing, and monitoring further strengthens your deployment pipeline, enabling you to deliver high-quality applications reliably and consistently.

Feel free to customize and expand upon this foundation to suit your specific project needs and organizational standards.

Top comments (0)