Skip to content

Exercise 1a — Services and networking

This section will focus on creating an nginx deployment, looking at scaling pods, connecting services and testing cluster networks.

Part 1 — NGINX deployment

We’ll begin by creating a deployment running a single nginx pod:

kubectl create namespace nginx-demo
kubectl apply -f $RES_HOME/nginx-deployment.yaml
Note nginx-deployment.yaml specifies a deployment called nginx in a namespace nginx-demo, which must be created first. For this exercise, it will be convenient to set kubectl to target this namespace:
kubectl config set-context --current --namespace=nginx-demo

You can now inspect the pod that started running with the deployment using simply:

kubectl get pods -o wide

You should see a pod running with its own internal cluster IP (it may take a few seconds to be assigned). Even simple containers produce logs that can be inspected with kubectl:

kubectl logs deployment/nginx

Scaling the Deployment

One of Kubernetes’ core strengths is scaling workloads horizontally.

We can easily scale the deployment from 1 replica to 3:

kubectl scale deployment nginx --replicas=3

Watch the new pods appear:

kubectl get pods -o wide -w

Notice:

  • each pod has a unique IP address,
  • pods may run on different nodes,
  • all replicas are managed automatically by the Deployment.

If a pod fails, Kubernetes will attempt to replace it automatically.

Part 2 — Creating a Cluster Service

Right now, the pods are completely isolated. To make them reachable from within the cluster virtual network, we create a Kubernetes Service.

kubectl apply -f $RES_HOME/nginx-service.yaml 
kubectl get svc

You should see something similar to:

NAME            TYPE        CLUSTER-IP      PORT(S)
nginx-service   ClusterIP   10.43.x.xxx    80/TCP

The Service provides a stable virtual IP, internal DNS resolution and load balancing across the nginx pods.

Importantly, the Service remains stable even if pods are recreated.

Inspecting Service Endpoints

We can see which pods the Service is forwarding traffic to:

kubectl get endpoints nginx-service

or:

kubectl describe service nginx-service

You should see the IP addresses of all nginx pods currently backing the Service.

Part 3 — Testing Connectivity Inside the Cluster

To debug networking inside Kubernetes, it is often useful to launch a temporary utility container.

We’ll use BusyBox:

kubectl apply -f $RES_HOME/busybox.yaml

This launches an interactive shell inside the cluster.

Info

BusyBox packages a large number of common 'NIX utilities into a single binary. You can find out about the project at busybox.net.

DNS Resolution

Kubernetes automatically creates DNS records for Services using CoreDNS. Inside BusyBox, we can test Kubernetes DNS:

kubectl exec -it toolbox -- sh
/ nslookup nginx-service

Using CoreDNS, nslookup will resolve to the ClusterIP assigned to the Service, but you should see some warnings too. This is because we have not given the nslookup tool namespace information. Using nslookup nginx-service.nginx-demo.svc.cluster.local will remove the warnings.

Accessing the Service

Still inside BusyBox:

/ wget -qO- http://nginx-service

You should receive the default NGINX welcome page HTML.

The step by step process at this point is:

  • BusyBox queried Kubernetes DNS,
  • resolved the Service name,
  • connected to the Service virtual IP,
  • and Kubernetes forwarded traffic to one of the nginx pods.

All of this happened transparently.

Observing Load Balancing

It is interesting to see the results if you run the request several times:

/ wget -qO- http://nginx-service

Although the webpage looks identical, Kubernetes may route each request to a different nginx pod behind the Service. The last line on the webpage output gives you the hostname.

To figure out which pod is related to which hostname, inspect the pod information:

kubectl get pods -o wide

Part 4 — Exposing the Service Externally

So far our Service is only reachable from within the cluster. To expose it on the router's network, the primitive approach is to setup a port forwarding rule for each node. This can be done editing nginx-service.yaml, changing type: ClusterIP to type: NodePort and adding a nodePort field:

ports:
- port: 80
  targetPort: 80
  nodePort: 30080
type: NodePort
You can now reach the service using any node's address:
curl kworker1:30080
Note that you must target a specific node—if that node goes down, the endpoint becomes unreachable. For a more robust solution, a dedicated Ingress controller is created that sits in front of the nodes and provides a single entry point.

Clean-up

Let's remove the deployment and reset our kubectl context:

kubectl delete deployment nginx
kubectl config set-context --curent --namespace=default

Summary

In this exercise you:

  • deployed an application with Kubernetes,
  • scaled it across multiple pods,
  • exposed it using a Service,
  • explored Kubernetes DNS,
  • tested networking from inside the cluster using BusyBox, and outside via a NodePort

These ideas form the foundation for more advanced topics including ingress controllers, service meshes, observability and multi-service applications.