A simple, self-contained setup that demonstrates Dockerized deployment, local Kubernetes (kind) orchestration, and Jenkins CI/CD.
DevNotes is a solo, self-contained project demonstrating how to:
- Containerize a simple full-stack app,
- Deploy it to a local Kubernetes cluster (kind) with state persisted via a PVC,
- Automate builds/deploys via a Jenkins job.
This README covers the project’s purpose, architecture, and setup steps.
- Project Purpose
- System Overview
- Prerequisites
- Usage
- Jenkins-in-Docker First-Time Setup
- Kubernetes Fixed Ports
- Next Steps and Future Improvements
DevNotes aims to:
- Show how to package backend and frontend into separate Docker images
- Demonstrate how to deploy a multi-service app with Kubernetes (kind) manifests
- Illustrate persistent storage via a PVC that survives pod restarts (but not cluster deletion)
- Provide a simple Jenkins CI/CD pipeline for building images and deploying to kind
The actual “notes” app is trivial (create/read simple text entries), as the focus is on the container/cluster setup.
-
The DevNotes App
- A fullstack app for note-taking. Consists of:
- Backend, located in
backend/- A small Flask app that handles GET and POST requests for notes.
- It stores data in a local
notes.jsonfile.
- Frontend, located in
frontend/- A static HTML page served with Nginx. It lets users submit new notes and view existing ones by calling the backend API.
- Backend, located in
- A fullstack app for note-taking. Consists of:
-
Docker Compose
docker-compose.yml(root of repo)- Spins up both services and mounts a volume to preserve note data between runs.
- Unused if you're using Kubernetes, but convenient for local development.
-
Kubernetes (kind)
- Runs the app in a local cluster emulating a production-like environment.
- YAML manifests are under
k8s/:backend-deployment.yaml,backend-service.yaml– deploy and expose the backendfrontend-deployment.yaml,frontend-service.yaml– deploy and expose the frontendpersistent-volume.yaml,persistent-volume-claim.yaml– enable note persistenceingress.yaml– enables a single access point via port 30080kind-config.yaml– configures kind to expose fixed ports
-
(Optional) Jenkins CI/CD
- Automates Docker builds and deployment to the cluster
- Configuration lives in
jenkins/and the rootJenkinsfile
To run this project, make sure you have the following installed on your machine:
If you're on macOS with Homebrew installed:
brew install docker kubectl kind gitYou can install Jenkins manually or simply use the provided Docker setup:
cd jenkins
docker-compose up --build -dIf using this method, see Jenkins-in-Docker First-Time Setup.
How to start, use, and stop the project using Docker Compose, Kubernetes, or Jenkins.
Start everything:
docker-compose up --buildUse the app:
- Backend: http://localhost:5001/notes
- Frontend: http://localhost:8000
Stop everything:
docker-compose downStart everything:
To save time, run the provided bootstrap script (bootstrap-kind.sh), which creates the cluster, builds & loads images, and applies manifests in order.
./bootstrap-kind.shUse the app:
- Frontend: http://localhost:30080
- The frontend internally calls the backend API
Stop everything:
kind delete cluster --name devnotesStart Jenkins:
cd jenkins
docker-compose up --build -dAccess Jenkins UI:
- Visit http://localhost:8081
Run the pipeline:
- Create a classic job using the
Jenkinsfileat the root of the repo - Run the job to build, load, and deploy to kind
Use the app (after the job runs):
- Visit http://localhost:30080
Stop Jenkins:
cd jenkins
docker-compose downBecause Docker containers don’t share the host’s 127.0.0.1, Jenkins needs to talk to the cluster at host.docker.internal instead.
We patch your kubeconfig to reflect this.
From inside the project folder, run:
kubectl config view --raw --minify --context=kind-devnotes | sed 's|https://127.0.0.1:|https://host.docker.internal:|; s|https://0.0.0.0:|https://host.docker.internal:|' > ./jenkins/kubeconfig.patchedIf you run into TLS issues inside Jenkins, open kubeconfig.patched and replace:
certificate-authority-data: ...with:
insecure-skip-tls-verify: trueThis allows Jenkins to connect without verifying TLS certificates.
These port mappings come from k8s/kind-config.yaml.
| Purpose | Host Address | Container Port |
|---|---|---|
| Backend (Flask API) | localhost:5001 |
5001 |
| Frontend (Nginx static site) | localhost:30080 |
80 |
| Kubernetes API Server | localhost:6443 |
6443 |
⚠️ These only apply when running via Kubernetes, not Docker Compose.
- Use Helm charts: Package the manifests for easier configuration
- Add end-to-end tests: Use Jenkins to verify backend behavior via HTTP
- Persist data across cluster deletion: Use a
hostPathinpersistent-volume.yaml - Add authentication: Protect the backend API with a token or key
- Use GitHub Actions: Replace Jenkins with a cloud-based CI/CD pipeline