This repository demonstrates a complete Kubernetes deployment of a 3-microservice application backed by PostgreSQL. The solution includes:
nodeservice: Node.js/Express frontend that serves a simple UI and acts as an API gateway.pythonservice: Flask-based writer service that inserts items into PostgreSQL.springservice: Spring Boot reader service that reads items from PostgreSQL.postgres: Kubernetes StatefulSet backing the data store.
It also includes cluster bootstrap automation for worker nodes and Kubernetes resource manifests for service deployment, networking, secrets, and persistence.
+-------------------+
| nginx ingress |
| controller |
+---------+---------+
|
v
+--------------+
| nodeservice |
| (frontend) |
+------+-------+
|
v
+---------------+----------------+
| |
v v
+------------+ +--------------+
| pythonservice| | springservice|
| (writer) | | (reader) |
+------------+ +--------------+
| |
+---------------+----------------+
|
v
+------------------+
| PostgreSQL |
| (StatefulSet) |
+------------------+
-
nodeservice- Serves static UI from
nodeservice/public/index.html - Exposes
POST /api/itemsto save new items viapythonservice - Exposes
GET /api/itemsto fetch saved items viaspringservice - Configured by environment variables:
WRITER_URL→http://pythonservice:5000READER_URL→http://springservice:8081
- Serves static UI from
-
pythonservice- Flask API running at port
5000 - Receives write requests and persists them into PostgreSQL
- Uses
DB_PASSWORDfrom Kubernetes Secret
- Flask API running at port
-
springservice- Spring Boot API running at port
8081 - Reads rows from PostgreSQL using
JdbcTemplate - Exposes
GET /healthandGET /items - Uses
DB_PASSWORDfrom Kubernetes Secret
- Spring Boot API running at port
-
postgres- PostgreSQL 15 StatefulSet
- Uses
postgres-secretfor database password - Uses persistence via either built-in
volumeClaimTemplatesor explicit PVC/PV
-
nodeservice/Dockerfileserver.jspackage.jsonpublic/index.html
-
pythonservice/Dockerfilerequirements.txtsrc/app.py
-
springservice/Dockerfilepom.xmlsrc/main/java/com/example/demo/DemoApplication.javasrc/main/java/com/example/demo/ItemController.javasrc/main/java/com/example/demo/ItemRepository.javasrc/main/resources/application.properties
-
K8s_Yaml_Files/configmap.yamlsecrets.yamlnodeservice.yamlpythonservice.yamlspringservice.yamlpostgres.yamlpostgres2.yamlpersistentvolume.yamlpersistentvolumeclaim.yamlstorageclass.yamlingress.yaml
-
K8s_Cluster_Files/create_cluster_worker.sh
-
db/init.sql- Database initialization script for the
itemstable
- Database initialization script for the
- Ubuntu Linux worker nodes (or Linux hosts) with
sudoaccess dockerorcontainerdinstalled on cluster nodeskubectlinstalled on your workstation or master nodekubeadm,kubelet, andkubectlfor cluster bootstrapmavenor the included./mvnwwrapper for the Spring Boot build- Docker Hub account (or another registry) if pushing images externally
cd /home/shawn/Projects/3-Microservices-Deployment/springservice
./mvnw clean package -DskipTestsFrom project root:
cd /home/shawn/Projects/3-Microservices-Deployment
docker build -t shawnprac/nodeservice:latest ./nodeservice
docker build -t shawnprac/pythonservice:latest ./pythonservice
docker build -t shawnprac/springservice:latest ./springserviceNote: The Kubernetes manifests reference images named
shawnprac/nodeservice:latest,shawnprac/pythonservice:latest, andshawnprac/springservice:latest. Replace these tags with your own registry or image names if needed.
docker push shawnprac/nodeservice:latest
docker push shawnprac/pythonservice:latest
docker push shawnprac/springservice:latestIf you are using a local cluster like kind, you can also load the images directly into the cluster instead of pushing.
This repository includes the master initialization script at K8s_Cluster_Files/k8s_Cluster_master.sh.
The script configures the master node, installs containerd and Kubernetes components, initializes the cluster, and deploys the network plugin.
sudo apt update -y
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables =k8s_Cluster_Files 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sudo sysctl --system
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /
EOF
sudo apt update -y
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo kubeadm init --pod-network-cidr=10.244.0.0/16Then configure kubectl on the master:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/configThe script K8s_Cluster_Files/create_cluster_worker.sh installs required packages and joins a worker node to an existing Kubernetes cluster.
Use it like this on each worker node:
chmod +x K8s_Cluster_Files/create_cluster_worker.sh
sudo ./K8s_Cluster_Files/create_cluster_worker.sh "<kubeadm join command>"The join command is generated by the master node after kubeadm init, for example:
kubeadm token create --print-join-commandkubectl apply -f K8s_Yaml_Files/configmap.yaml
kubectl apply -f K8s_Yaml_Files/secrets.yaml- Use
postgres.yamlfor a stateful PostgreSQL deployment with its own PVC template. - Use
postgres2.yaml+persistentvolume.yaml+persistentvolumeclaim.yamlwhen you want an explicit PVC/PV pairing. storageclass.yamlis a sample AWS EBS StorageClass for cloud environments.
Example dynamic statefulset deployment:
kubectl apply -f K8s_Yaml_Files/postgres.yamlExample explicit PV/PVC deployment:
kubectl apply -f K8s_Yaml_Files/persistentvolume.yaml
kubectl apply -f K8s_Yaml_Files/persistentvolumeclaim.yaml
kubectl apply -f K8s_Yaml_Files/postgres2.yamlkubectl apply -f K8s_Yaml_Files/nodeservice.yaml
kubectl apply -f K8s_Yaml_Files/pythonservice.yaml
kubectl apply -f K8s_Yaml_Files/springservice.yamlIf you are using the NGINX ingress controller, make sure it is installed and ready before applying the ingress resource.
kubectl apply -f K8s_Yaml_Files/ingress.yamlNote: The ingress manifest forwards
/to thenodeserviceKubernetes Service on port80.
kubectl get nodes
kubectl get pods --all-namespaces
kubectl get svc
kubectl get ingressInspect logs if any pod is not ready:
kubectl logs deployment/nodeservice
kubectl logs deployment/pythonservice
kubectl logs deployment/springservice
kubectl logs statefulset/postgres- If using the ingress resource, access the cluster via the ingress host or IP.
- If using NodePort, send traffic to any cluster node on port
30007.
The frontend UI is served by nodeservice and provides:
- A form to submit new items
- A refresh button to load saved items from
springservice
The schema for the items table is defined in db/init.sql:
CREATE TABLE IF NOT EXISTS items (
id SERIAL PRIMARY KEY,
LogName VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);If PostgreSQL does not automatically initialize the table, run this SQL against the database.
nodeserviceuses environment variables to discoverpythonserviceandspringserviceinside Kubernetes.pythonserviceandspringserviceboth consumeDB_PASSWORDfrompostgres-secret.configmap.yamlcontains the DB connection settings:DB_HOSTDB_PORTDB_NAMEDB_USER
- Replace Docker image names if you publish images under a different registry.
- For local Kubernetes environments like
kind, you may need to usekind load docker-imagefor each built image.
# Watch pod rollout
kubectl rollout status deployment/nodeservice
kubectl rollout status deployment/pythonservice
kubectl rollout status deployment/springservice
# Describe resources
kubectl describe svc nodeservice
kubectl describe ingress app-ingress
kubectl describe statefulset postgresThis project is a full end-to-end microservices deployment example that includes:
- Kubernetes cluster worker bootstrap automation
- Node.js UI/API gateway service
- Python writer service
- Java Spring Boot reader service
- PostgreSQL stateful storage
- Kubernetes ConfigMap, Secret, StatefulSet, Deployment, Service, Ingress, and storage manifests
Use this README to build, deploy, and validate the entire stack in a real Kubernetes environment.
nodeserviceuses environment variables to discoverpythonserviceandspringserviceinside Kubernetes.pythonserviceandspringserviceboth consumeDB_PASSWORDfrompostgres-secret.configmap.yamlcontains the DB connection settings:DB_HOSTDB_PORTDB_NAMEDB_USER
- Replace Docker image names if you publish images under a different registry.
- For local Kubernetes environments like
kind, you may need to usekind load docker-imagefor each built image.
# Watch pod rollout
kubectl rollout status deployment/nodeservice
kubectl rollout status deployment/pythonservice
kubectl rollout status deployment/springservice
# Describe resources
kubectl describe svc nodeservice
kubectl describe ingress app-ingress
kubectl describe statefulset postgresThis project is a full end-to-end microservices deployment example that includes:
- Kubernetes cluster worker bootstrap automation
- Node.js UI/API gateway service
- Python writer service
- Java Spring Boot reader service
- PostgreSQL stateful storage
- Kubernetes ConfigMap, Secret, StatefulSet, Deployment, Service, Ingress, and storage manifests
Use this README to build, deploy, and validate the entire stack in a real Kubernetes environment.