Deploying microservice-based architectures requires running multi-tier workloads that isolate different application layers. A classic example is a Two-Tier Web Application:
Deploying this architecture in Kubernetes involves using Namespaces for logical resource isolation, setting Resource Limits on containers to prevent memory or CPU starvation, and exposing different Service Types (NodePort vs ClusterIP) depending on visibility requirements.
graph TD
subgraph Namespace: iron-namespace-datacenter
subgraph Front-End [Frontend Web Tier]
ServiceFG[Service NodePort: <br> iron-gallery-service-datacenter <br> nodePort: 32678] -->|Port 80| DeployFG[Deployment: <br> iron-gallery-deployment-datacenter]
DeployFG -->|Mounts config / images| VolFG[(emptyDir Volumes)]
end
subgraph Back-End [Database Tier]
ServiceDB[Service ClusterIP: <br> iron-db-service-datacenter <br> Port 3306] -->|Port 3306| DeployDB[Deployment: <br> iron-db-deployment-datacenter]
DeployDB -->|Mounts db directory| VolDB[(emptyDir Volume)]
end
end
User[Client / Browser] -->|Requests NodeIP:32678| ServiceFG
A Namespace provides a virtual cluster environment within a physical Kubernetes cluster.
<service-name>.<namespace-name>.svc.cluster.local.Kubernetes allows you to manage the computing resources consumed by containers using requests and limits:
| Resource | Unit | Over-Limit Behavior | Mechanism |
|---|---|---|---|
| CPU | millicores (m) (1000m = 1 vCPU) |
Throttled: The container is restricted from consuming more CPU cycles than its limit, resulting in slower execution but no termination. | CFS (Completely Fair Scheduler) shares |
| Memory | Bytes (Mi / Gi) |
Killed (OOMKilled): If a container exceeds its memory limit, the kernel Out-Of-Memory (OOM) killer immediately terminates the process with Exit Code 137. | Linux cgroups memory controller |
iron-namespace-datacenterthoriron-gallery-deployment-datacenterrun: iron-gallery1iron-gallery-container-datacenterkodekloud/irongallery:2.0100Mi50mconfig (type emptyDir) mounted at /usr/share/nginx/html/dataimages (type emptyDir) mounted at /usr/share/nginx/html/uploadsiron-gallery-service-datacenter
NodePort8032678iron-db-deployment-datacenterdb: mariadb1iron-db-container-datacenterkodekloud/irondb:2.0MYSQL_DATABASE: database_webMYSQL_ROOT_PASSWORD: <complex_password>MYSQL_PASSWORD: <complex_password>MYSQL_USER: <custom_user> (not root)db (type emptyDir) mounted at /var/lib/mysqliron-db-service-datacenter
ClusterIP3306Establish connection to the jump host:
ssh thor@jump_host_ip
Create the dedicated namespace for resource isolation:
kubectl create namespace iron-namespace-datacenter
Expected Output:
namespace/iron-namespace-datacenter created
Create a file named iron-db.yaml defining the MariaDB database Deployment and its internal ClusterIP Service. The configurations explicitly specify the custom namespace:
apiVersion: apps/v1
kind: Deployment
metadata:
name: iron-db-deployment-datacenter
namespace: iron-namespace-datacenter
labels:
db: mariadb
spec:
replicas: 1
selector:
matchLabels:
db: mariadb
template:
metadata:
labels:
db: mariadb
spec:
volumes:
- name: db
emptyDir: {}
containers:
- name: iron-db-container-datacenter
image: kodekloud/irondb:2.0
env:
- name: MYSQL_DATABASE
value: "database_web"
- name: MYSQL_ROOT_PASSWORD
value: "SecretRootPass123!"
- name: MYSQL_PASSWORD
value: "SecretUserPass123!"
- name: MYSQL_USER
value: "iron_db_user"
volumeMounts:
- name: db
mountPath: /var/lib/mysql
---
apiVersion: v1
kind: Service
metadata:
name: iron-db-service-datacenter
namespace: iron-namespace-datacenter
spec:
type: ClusterIP
selector:
db: mariadb
ports:
- port: 3306
targetPort: 3306
protocol: TCP
Deploy the database tier:
kubectl apply -f iron-db.yaml
Create a file named iron-gallery.yaml defining the web frontend Deployment with CPU/Memory limits, shared emptyDir scratch directories, and its externally accessible NodePort Service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: iron-gallery-deployment-datacenter
namespace: iron-namespace-datacenter
labels:
run: iron-gallery
spec:
replicas: 1
selector:
matchLabels:
run: iron-gallery
template:
metadata:
labels:
run: iron-gallery
spec:
volumes:
- name: config
emptyDir: {}
- name: images
emptyDir: {}
containers:
- name: iron-gallery-container-datacenter
image: kodekloud/irongallery:2.0
resources:
limits:
memory: 100Mi
cpu: 50m
volumeMounts:
- name: config
mountPath: /usr/share/nginx/html/data
- name: images
mountPath: /usr/share/nginx/html/uploads
---
apiVersion: v1
kind: Service
metadata:
name: iron-gallery-service-datacenter
namespace: iron-namespace-datacenter
spec:
type: NodePort
selector:
run: iron-gallery
ports:
- port: 80
targetPort: 80
nodePort: 32678
protocol: TCP
Deploy the web tier:
kubectl apply -f iron-gallery.yaml
Verify both deployments successfully scale and reach Running state:
kubectl get deployments -n iron-namespace-datacenter
Expected Output:
NAME READY UP-TO-DATE AVAILABLE AGE
iron-db-deployment-datacenter 1/1 1 1 1m
iron-gallery-deployment-datacenter 1/1 1 1 45s
Check the active Pod status:
kubectl get pods -n iron-namespace-datacenter
Expected Output:
NAME READY STATUS RESTARTS AGE
iron-db-deployment-datacenter-5f8a9b0c-abcde 1/1 Running 0 1m
iron-gallery-deployment-datacenter-6d5c4b3a-fghij 1/1 Running 0 45s
Check that both services are created in the namespace with the correct ports:
kubectl get svc -n iron-namespace-datacenter
Expected Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
iron-db-service-datacenter ClusterIP 10.96.140.231 <none> 3306/TCP 1m
iron-gallery-service-datacenter NodePort 10.96.182.11 <none> 80:32678/TCP 45s
Check accessibility of the web interface from outside the cluster:
curl -I http://<NODE_IP>:32678
Expected Output showing successful access/redirect to installer page:
HTTP/1.1 200 OK
Server: nginx/...
Content-Type: text/html
...
The multi-tier web application stack is successfully deployed, isolated, and externally exposed!