Deploying an application to a Kubernetes cluster requires two primary tasks: running the application containers and exposing those containers to handle traffic.
graph TD
User[Client / External traffic] -->|Accesses NodeIP:30011| Node[Kubernetes Node]
Node -->|kube-proxy routes to Service| Service[Service: nginx-service <br> ClusterIP Port: 80]
Service -->|Load balances traffic| Pod1[Pod: nginx-deployment-1 <br> Container Port: 80]
Service -->|Load balances traffic| Pod2[Pod: nginx-deployment-2 <br> Container Port: 80]
Service -->|Load balances traffic| Pod3[Pod: nginx-deployment-3 <br> Container Port: 80]
A Kubernetes Service allocates a stable IP address and DNS name. It selects Pods using label selectors (e.g., app: nginx) and routes incoming traffic to the selected Pods’ targetPort.
Kubernetes supports four main types of Services:
| Service Type | Scope | Description |
|---|---|---|
ClusterIP |
Internal | Exposes the Service on a cluster-internal IP. This makes the Service only reachable from within the cluster. This is the default service type. |
NodePort |
External | Exposes the Service on each Node’s IP at a static port (the NodePort). You can contact the NodePort Service from outside the cluster by requesting <NodeIP>:<NodePort>. |
LoadBalancer |
External | Exposes the Service externally using a cloud provider’s external load balancer. It automatically provisions a public IP address and forwards traffic to NodePorts created under the hood. |
ExternalName |
Internal/External | Maps a Service to the contents of the externalName field (e.g. my.database.example.com), by returning a CNAME record with its value. |
A NodePort service is specifically designed to allow external access directly through any host node’s IP address.
[ External Client ]
|
v
<Node_IP>:<NodePort> (e.g., 30011)
|
v (kube-proxy IPTables/IPVS Rules)
Service ClusterIP:Port (e.g., Cluster_IP:80)
|
v (Load Balanced Round-Robin)
Pod_IP:TargetPort (e.g., Pod_IP:80)
When defining a NodePort service, you configure three distinct ports:
nodePort (External Port): The port exposed on all cluster host nodes. This must fall in the default range 30000-32767. If left blank, Kubernetes will assign a random port in that range. (e.g., 30011).port (Internal Service Port): The port exposed inside the cluster on the Service’s ClusterIP. Other pods inside the cluster can access the service on this port. (e.g., 80).targetPort (Container Port): The port that the application container is listening on inside the Pod. Traffic is forwarded here. (e.g., 80).thordefaultnginx-deploymentnginx-containernginx:latest380nginx-serviceNodePort300118080Establish connection to the jump host:
ssh thor@jump_host_ip
Create a file named nginx-deployment.yaml defining the replica size and container details:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest
ports:
- containerPort: 80
Create a file named nginx-service.yaml declaring the NodePort configuration:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
nodePort: 30011
Apply both manifest files to initialize the resources in the cluster:
kubectl apply -f nginx-deployment.yaml
kubectl apply -f nginx-service.yaml
Expected Output:
deployment.apps/nginx-deployment created
service/nginx-service created
Verify that all 3 Pod replicas are healthy and in Running state:
kubectl get deployments nginx-deployment
Expected Output:
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 20s
Check individual Pod statuses:
kubectl get pods -l app=nginx
Expected Output:
NAME READY STATUS RESTARTS AGE
nginx-deployment-7f8a9b0c-abcde 1/1 Running 0 25s
nginx-deployment-7f8a9b0c-fghij 1/1 Running 0 25s
nginx-deployment-7f8a9b0c-klmno 1/1 Running 0 25s
Confirm the service has allocated a ClusterIP and bound the host nodePort 30011:
kubectl get service nginx-service
Expected Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service NodePort 10.96.184.22 <none> 80:30011/TCP 30s
Send an HTTP request from outside the cluster network (e.g. from the jump host) targeting one of the worker nodes’ IP addresses on port 30011:
curl -I http://<NODE_IP>:30011
Expected Output:
HTTP/1.1 200 OK
Server: nginx/1.25.x
Date: Sat, 18 Jul 2026 ...
Content-Type: text/html
...
The Nginx web server deployment is successfully exposed externally via the static NodePort!