Skip to content

Automated CI/CD pipeline using GitHub Actions to build, deploy, and test Node.js application on Minikube. Demonstrates containerization with Docker, Kubernetes deployment automation, service validation, and troubleshooting CI/CD workflows. Complete workflow from code push to deployed application.

Notifications You must be signed in to change notification settings

MDavidHernandezP/GithubActionsAndMinikubeCD

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CI/CD Lab 1 - Continuous Deployment with GitHub Actions

GitHub Actions Kubernetes Node.js Docker Express

This repository demonstrates continuous deployment using GitHub Actions to automatically build, test, and deploy a Node.js application to a Minikube cluster. The lab showcases CI/CD pipeline automation, containerization, and Kubernetes orchestration integrated with GitHub's native workflow system.

Overview

This lab implements a complete CI/CD pipeline that triggers on every code push, building a Docker image from a simple Node.js Express application, deploying it to a local Minikube Kubernetes cluster, and validating the deployment—all automated through GitHub Actions workflows.

What This Project Demonstrates

  • Automated CI/CD Pipeline: GitHub Actions workflow triggered on push events
  • Container Building: Docker image creation within the GitHub Actions runner
  • Kubernetes Deployment: Automated deployment to Minikube cluster
  • Service Validation: Post-deployment testing and service URL verification
  • Infrastructure as Code: Kubernetes manifests for declarative deployment
  • Development Workflow: Testing Kubernetes deployments locally before production

Table of Contents

Lab Objectives

Primary Goals

  • Automate deployment using GitHub Actions workflows
  • Integrate Minikube with CI/CD pipeline for local Kubernetes testing
  • Build and deploy containerized Node.js application
  • Validate deployments automatically through workflow steps
  • Implement best practices for CI/CD pipeline design

Learning Outcomes

  • GitHub Actions workflow syntax and job configuration
  • Docker image building in CI/CD environments
  • Minikube integration for testing Kubernetes deployments
  • Automated testing and validation strategies
  • Debugging CI/CD pipeline failures
  • YAML syntax and Kubernetes manifest structure

Architecture

CI/CD Pipeline Flow

Git Push → GitHub Repository
    ↓
GitHub Actions Triggered
    ↓
1. Checkout Code
    ↓
2. Start Minikube Cluster
    ↓
3. Verify Cluster (kubectl get pods)
    ↓
4. Build Docker Image
    ↓
5. Deploy to Minikube (kubectl apply)
    ↓
6. Wait for Service Initialization
    ↓
7. Validate Deployment
    ↓
8. Test Service URLs
    ↓
Pipeline Success ✓

Application Architecture

GitHub Actions Runner
    ↓
[Minikube Cluster]
    ↓
[NodePort Service:80]
    ↓
[Deployment: nodejs-app]
    ↓
[Pod: Node.js Express Container:3000]
    ↓
Response: "Hello World"

Application Components

Node.js Express Application

server.js - Simple Express web server:

  • Listens on port 3000
  • Serves "Hello World" response on root path (/)
  • Designed for easy testing and validation
  • Lightweight and fast startup time

package.json - Application metadata and dependencies:

  • Express framework v4.16.1
  • Start script for application execution
  • Minimal dependencies for quick builds

Containerization

Dockerfile - Multi-step container build:

  1. Base Image: Node.js 14 official image
  2. Working Directory: /usr/src/app
  3. Dependency Installation: npm install with package.json
  4. Application Copy: Source code copied to container
  5. Port Exposure: Port 3000 exposed for HTTP traffic
  6. Startup Command: Executes node server.js

Key Dockerfile Features:

  • Layer caching optimization (package.json copied before source)
  • Explicit Express installation for reliability
  • Non-root user execution (inherited from node image)
  • Clear port documentation

CI/CD Pipeline

GitHub Actions Workflow

File: .github/workflows/deploy-to-minikube-github-actions.yaml

Trigger: Push to any branch

Job Configuration:

  • Runner: ubuntu-latest (GitHub-hosted)
  • Name: Build Node.js Docker Image and deploy to minikube

Workflow Steps

Step 1: Checkout Code

- uses: actions/checkout@v2

Clones repository code into the runner workspace.

Step 2: Start Minikube

- name: Start minikube
  uses: medyagh/setup-minikube@master

Initializes a local Kubernetes cluster using the setup-minikube action.

Why This Works:

  • Installs kubectl, minikube, and required dependencies
  • Configures Docker as container runtime
  • Starts single-node cluster suitable for testing

Step 3: Verify Cluster

- name: Try the cluster!
  run: kubectl get pods -A

Validates Kubernetes cluster is operational by listing all pods across namespaces.

Step 4: Build Docker Image

- name: Build image
  run: |
    export SHELL=/bin/bash
    eval $(minikube -p minikube docker-env)
    docker build -f ./Dockerfile -t devopshint/node-app:latest .
    echo -n "verifying images:"
    docker images

Key Operations:

  • Sets shell environment for consistency
  • Configures Docker to use Minikube's Docker daemon (crucial!)
  • Builds image with latest tag
  • Verifies image creation

Why eval $(minikube docker-env)? This connects the Docker client to Minikube's internal Docker daemon, meaning images built are directly available to the Kubernetes cluster without pushing to a registry.

Step 5: Deploy to Minikube

- name: Deploy to minikube
  run: kubectl apply -f k8s-node-app.yaml

Applies Kubernetes manifests, creating Deployment and Service resources.

Step 6: Wait for Initialization (Added Fix)

- name: Wait for deployment
  run: kubectl wait --for=condition=available --timeout=60s deployment/nodejs-app

This step was added during troubleshooting to ensure the service is ready before testing.

Step 7: Test Service URLs

- name: Test service URLs
  run: |
    minikube service list
    minikube service nodejs-app --url

Lists all services and retrieves the NodePort URL for the deployed application.

Implementation Details

Kubernetes Manifests

k8s-node-app.yaml contains two resources:

1. Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-app
  namespace: default
  labels:
    app: nodejs-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nodejs-app
  template:
    metadata:
      labels:
        app: nodejs-app
    spec:
      containers:
        - name: nodejs-app
          image: "devopshint/node-app:latest"
          ports:
            - containerPort: 3000

Configuration Details:

  • Single replica for testing purposes
  • Uses locally built Docker image
  • Container listens on port 3000
  • Label selector enables Service discovery

2. Service

apiVersion: v1
kind: Service
metadata:
  name: nodejs-app
  namespace: default
spec:
  selector:
    app: nodejs-app
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 3000

Service Configuration:

  • Type: NodePort (exposes service outside cluster)
  • Port: 80 (external service port)
  • TargetPort: 3000 (container port)
  • Selector: Routes traffic to Pods with app: nodejs-app label

Why NodePort? NodePort makes the service accessible via <NodeIP>:<NodePort>, perfect for testing with Minikube's service command.

Challenges and Solutions

Challenge 1: Service Not Immediately Accessible

Problem: After deployment, attempting to access the service immediately resulted in connection errors. The workflow failed at the testing step because the service hadn't fully initialized.

Root Cause: Kubernetes resources take time to become available:

  • Deployment needs to create Pods
  • Pods need to pull image (if not cached)
  • Container needs to start
  • Service endpoint needs to register

Solution: Added explicit wait step to the workflow:

- name: Wait for deployment
  run: kubectl wait --for=condition=available --timeout=60s deployment/nodejs-app

Result: Ensured deployment was fully ready before running tests, eliminating race conditions.

Challenge 2: YAML Parsing Errors

Problem: GitHub Actions workflow failed with YAML parsing error. The manifest wasn't being accepted by kubectl.

Error Message:

error: error parsing k8s-node-app.yaml: error converting YAML to JSON

Root Cause: Incorrect indentation in the Kubernetes manifest file. YAML is whitespace-sensitive, and even minor spacing issues cause parsing failures.

Solution:

  1. Validated YAML syntax using online YAML validator
  2. Corrected indentation to use consistent 2-space spacing
  3. Ensured proper alignment of nested elements
  4. Verified manifest structure against Kubernetes documentation

Best Practice: Always validate YAML files before committing:

kubectl apply -f k8s-node-app.yaml --dry-run=client

Challenge 3: Docker Image Not Found

Problem: Kubernetes deployment failed with ImagePullBackOff error, unable to find the specified Docker image.

Error Message:

Failed to pull image "devopshint/node-app:latest": rpc error: code = Unknown

Root Cause: Multiple possible causes identified:

  • Image wasn't built before deployment attempted to use it
  • Docker image not available in Minikube's Docker daemon
  • Incorrect image tag or registry path

Solution:

  1. Verified build step completed successfully with docker images command
  2. Ensured eval $(minikube docker-env) was executed before building
  3. Confirmed image name matched exactly in manifest and build command
  4. Added image verification in the build step output

Key Insight: Using Minikube's Docker daemon (minikube docker-env) is crucial. Without this, images are built in the host Docker daemon and unavailable to Kubernetes.

Challenge 4: GitHub Actions Environment Variables

Problem: Environment variables not persisting across workflow steps, causing Docker environment configuration to fail.

Solution: Used proper shell configuration:

run: |
  export SHELL=/bin/bash
  eval $(minikube -p minikube docker-env)

Setting SHELL explicitly ensures consistent behavior across different runner environments.

Key Learnings

CI/CD Best Practices

  • Explicit waiting prevents race conditions in deployment pipelines
  • Validation steps after each major operation catch issues early
  • Docker daemon configuration is critical when using Minikube in CI/CD
  • Idempotent operations using kubectl apply enable safe re-runs

GitHub Actions Insights

  • Actions marketplace provides pre-built integrations (setup-minikube)
  • Multi-line commands require pipe (|) syntax
  • Each step runs in fresh shell unless configured otherwise
  • Output visibility helps debugging with echo statements

Kubernetes Deployment Patterns

  • Declarative manifests enable version control and repeatability
  • NodePort services suitable for development and testing
  • Label selectors create loose coupling between resources
  • Health checks should be added for production-ready deployments

Docker Best Practices

  • Layer caching improves build performance
  • Multi-stage builds reduce final image size (future enhancement)
  • Specific version tags ensure reproducible builds
  • Image verification after building catches build failures

Prerequisites

Required Tools

  • Git: Version control
  • GitHub Account: Repository hosting and Actions access
  • Basic knowledge: Node.js, Docker, Kubernetes, YAML

For Local Development (Optional)

  • Docker: Container runtime
  • Minikube: Local Kubernetes cluster
  • kubectl: Kubernetes CLI
  • Node.js 14: Application runtime

Getting Started

Quick Start

  1. Fork/Clone Repository:

    git clone <repository-url>
    cd <repository-name>
  2. Review Application Code:

    # Check server.js
    cat server.js
    
    # Review Dockerfile
    cat Dockerfile
    
    # Examine Kubernetes manifests
    cat k8s-node-app.yaml
  3. Examine GitHub Actions Workflow:

    cat .github/workflows/deploy-to-minikube-github-actions.yaml
  4. Push Changes to Trigger Workflow:

    git add .
    git commit -m "Trigger CI/CD pipeline"
    git push origin main
  5. Monitor Workflow:

    • Navigate to repository on GitHub
    • Click "Actions" tab
    • View running workflow
    • Check each step's logs

Local Testing (Optional)

# Build Docker image locally
docker build -t devopshint/node-app:latest .

# Start Minikube
minikube start

# Configure Docker to use Minikube's daemon
eval $(minikube docker-env)

# Rebuild image in Minikube's Docker
docker build -t devopshint/node-app:latest .

# Deploy to Minikube
kubectl apply -f k8s-node-app.yaml

# Wait for deployment
kubectl wait --for=condition=available --timeout=60s deployment/nodejs-app

# Get service URL
minikube service nodejs-app --url

# Test application
curl $(minikube service nodejs-app --url)

Repository Structure

.
├── .github/
│   └── workflows/
│       └── deploy-to-minikube-github-actions.yaml   # CI/CD workflow
│
├── Dockerfile                                        # Container image definition
├── package.json                                      # Node.js dependencies
├── server.js                                         # Express application
├── k8s-node-app.yaml                                # Kubernetes manifests
└── README.md                                        # This file

Workflow Execution

Successful Pipeline Run

When you push code, GitHub Actions executes the workflow:

Expected Output:

✓ Checkout code
✓ Start Minikube
✓ Try the cluster
✓ Build image
✓ Deploy to Minikube
✓ Wait for deployment
✓ Test service URLs

Viewing Results

GitHub Actions Interface:

  1. Navigate to repository → Actions tab
  2. Click on latest workflow run
  3. Expand each step to view logs
  4. Verify "Test service URLs" shows Minikube service URL

Key Indicators of Success:

  • All steps show green checkmarks
  • Docker image appears in build step output
  • Deployment becomes available within timeout
  • Service URL is successfully retrieved

Testing

Automated Testing (via Workflow)

The workflow automatically tests deployment by:

  1. Verifying cluster connectivity
  2. Confirming image build success
  3. Checking deployment availability
  4. Retrieving service URL

Manual Testing (Local)

If running locally with Minikube:

# Get service URL
SERVICE_URL=$(minikube service nodejs-app --url)

# Test with curl
curl $SERVICE_URL
# Expected output: Hello World

# Or open in browser
minikube service nodejs-app

Verification Commands

# Check deployment status
kubectl get deployments

# Check pod status
kubectl get pods

# Check service
kubectl get services

# View pod logs
kubectl logs -l app=nodejs-app

# Describe deployment for details
kubectl describe deployment nodejs-app

Contributing

This repository documents personal learning progress through a DevOps bootcamp. While it's primarily for educational purposes, suggestions and improvements are welcome!

How to Contribute

  1. Report Issues: Found a bug or error in scripts?

    • Open an issue describing the problem
    • Include script name and error message
    • Provide steps to reproduce
  2. Suggest Improvements: Have ideas for better implementations?

    • Fork the repository
    • Create a feature branch
    • Submit pull request with clear description
  3. Share Knowledge: Learned something new?

    • Add comments or documentation
    • Create additional practice exercises
    • Write tutorials or guides

License

This project is created for educational purposes as part of a DevOps bootcamp internship.

Educational Use: Feel free to use these scripts and documentation for learning purposes.

Attribution: If you use or reference this work, please provide attribution to the original author.

No Warranty: These scripts are provided "as is" without warranty of any kind. Use at your own risk, especially in production environments.

About

Automated CI/CD pipeline using GitHub Actions to build, deploy, and test Node.js application on Minikube. Demonstrates containerization with Docker, Kubernetes deployment automation, service validation, and troubleshooting CI/CD workflows. Complete workflow from code push to deployed application.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published