CKA Full Course 2024: Day 9/40 Kubernetes Services Explained: ClusterIP vs NodePort vs Loadbalancer vs External

lloydrivers

Lloyd Rivers

Posted on October 27, 2024

CKA Full Course 2024: Day 9/40 Kubernetes Services Explained: ClusterIP vs NodePort vs Loadbalancer vs External

Kubernetes Services Overview

  • Services in Kubernetes provide a way for applications to communicate with each other or with external clients. They allow for stable endpoints that remain constant, even if the underlying pods change.

Types of Kubernetes Services

  • ClusterIP: The default service type, exposing the service within the Kubernetes cluster only. It's useful for internal communication between services or pods.
  • NodePort: Exposes the service on a static port on each node’s IP, enabling access from outside the cluster via <NodeIP>:<NodePort>. Often used in development for simple external access but can have security risks in production.
  • LoadBalancer: Integrates with cloud providers to automatically create an external load balancer, which routes traffic to your services. This type is most common for exposing services to the internet in production environments on managed Kubernetes clusters.
  • ExternalName: Maps a service to an external DNS name by returning a CNAME record. Useful when pointing to external services outside of the Kubernetes environment, without complex configurations.

Prerequisites

Before we begin, ensure you have the following configuration for your KIND cluster:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: cka-cluster  
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30001  # Change this to match the NodePort
    hostPort: 30001
    listenAddress: "0.0.0.0" 
    protocol: tcp
- role: worker  
- role: worker  
Enter fullscreen mode Exit fullscreen mode

This YAML file sets up a Kubernetes cluster named cka-cluster with one control plane node and two worker nodes. The extraPortMappings section maps port 30001 to allow external access to the NodePort service.


1. Create a Service named myapp of type ClusterIP

  • Goal: This creates an internal network endpoint accessible only within the Kubernetes cluster, allowing internal traffic routing to your application.
  • Details: The ClusterIP type will not be accessible outside the cluster, so it’s used here to facilitate communication between Pods and other resources internally. The Service will map port 80 to the target port 80 on the Pods.

Steps:

Create the KIND cluster (if you haven’t already):

   kind create cluster --name kind-cka-cluster --config <kind.yml>
Enter fullscreen mode Exit fullscreen mode

Switch context to your new KIND cluster (if required):

   kubectl config use-context kind-kind-cka-cluster
Enter fullscreen mode Exit fullscreen mode

Create the Service manifest:
Open a new YAML file using your preferred editor:

   nano service.yml
Enter fullscreen mode Exit fullscreen mode

Define the Service:
Copy the following YAML configuration into service.yml:

apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  type: ClusterIP
  selector:
    app: MyApp  # Change this to match your pod labels. I copied this of the k8s docs
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80

Enter fullscreen mode Exit fullscreen mode

Apply the Service configuration:

   kubectl apply -f service.yml
Enter fullscreen mode Exit fullscreen mode

Verify the Service is running:

   kubectl get services
Enter fullscreen mode Exit fullscreen mode

This will create the myapp Service, of type ClusterIP, mapping port 80 on the Service to port 80 on the target Pods.


2. Create a Deployment named myapp

  • Goal: The Deployment manages Pod replicas for nginx (in this case, version 1.23.4-alpine), ensuring they’re running and available.

  • Details: By setting up a Deployment, you gain automatic scalability, rolling updates, and easier management. You expose the container’s port 80, which the Service will use to route traffic.

Steps:

Create the Deployment manifest:
Open a new YAML file using your preferred editor:

nano deployment.yml
Enter fullscreen mode Exit fullscreen mode

Define the Deployment:
Copy the following YAML configuration into deployment.yml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: nginx
spec:
  replicas: 1  
  selector:
    matchLabels:
      app: MyApp
  template:
    metadata:
      labels:
        app: MyApp
    spec:
      containers:
      - name: nginx
        image: nginx:1.23.4-alpine
        ports:
        - containerPort: 80
Enter fullscreen mode Exit fullscreen mode

Apply the Deployment configuration:
Run the following command to create the Deployment:

kubectl apply -f deployment.yml
Enter fullscreen mode Exit fullscreen mode

Verify the Deployment is running:
Check the status by running:

kubectl get deployments
Enter fullscreen mode Exit fullscreen mode

3. Scale the Deployment to 2 replicas

  • Goal: This tests the load-balancing functionality of the Service, as it should distribute traffic across the two Pod instances.

  • Details: You can scale the Deployment either by updating the manifest to set replicas: 2 or by using a kubectl command.

Steps:

Update the Deployment manifest: If you want to change the number of replicas in the YAML, edit the replicas field:

replicas: 2  # Update to 2 for scaling
Enter fullscreen mode Exit fullscreen mode

Alternatively, use kubectl to scale the Deployment: Run the following command to scale the Deployment:

kubectl scale deployment myapp --replicas=2
Enter fullscreen mode Exit fullscreen mode

Verify the scaling: Run the following command to check that the new Pods are created and ready:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

4. Create a Temporary Pod Using busybox and Run wget Against the Service IP

  • Goal: This verifies that the Service is reachable from within the cluster.

  • Details: You’ll launch a temporary busybox Pod and run wget to check if it can access the myapp Service over its ClusterIP. This step assumes you’re comfortable finding the Service’s ClusterIP (accessible via kubectl get svc).

Important Labeling Warning: Ensure that the labels in the Service selector match the Deployment's matchLabels exactly. Misalignment will prevent the Service from routing traffic to the correct Pods.

Steps:

Find the Service IP:
Run the following command to retrieve the ClusterIP of myapp:

   kubectl get services
Enter fullscreen mode Exit fullscreen mode

Example output:

   NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
   myapp      ClusterIP   10.96.87.231   <none>        80/TCP    10h
Enter fullscreen mode Exit fullscreen mode

Create the Temporary Pod:
Use the following command to create a busybox Pod and run wget to test connectivity:

   kubectl run busybox --image=busybox -it --rm --restart=Never -- wget myapp:80
Enter fullscreen mode Exit fullscreen mode

Command Breakdown:

  • --image=busybox: Specifies busybox as the image to pull.
  • -it: Interactive terminal, which allows you to access the command line inside the Pod.
  • --rm: Cleans up and removes the Pod after the command completes.
  • --restart=Never: Ensures Kubernetes does not attempt to restart the Pod after it finishes.
  • wget myapp:80: Sends a request to the myapp Service at port 80, confirming connectivity.

Expected Output:
You should see output similar to the following upon a successful connection:

   Connecting to myapp:80 (10.96.87.231:80)
   saving to 'index.html'
   index.html           100% |********************************|   615  0:00:00 ETA
   'index.html' saved
   pod "busybox" deleted
Enter fullscreen mode Exit fullscreen mode

This output confirms that the Service is accessible within the cluster and can successfully handle requests from other Pods.


5. Run a wget Command Against the Service from Outside the Cluster

  • Goal: This tests if external traffic can reach the Service (spoiler: it won’t, as it’s still a ClusterIP type).
  • Steps: From an external machine or terminal, try reaching the Service IP. You won’t get a response because ClusterIP restricts access to within the cluster.

Find the Service IP:
Run the following command to retrieve the ClusterIP of myapp:

kubectl get services
Enter fullscreen mode Exit fullscreen mode

Example output:

NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
myapp      ClusterIP   10.96.87.231   <none>        80/TCP    10h
Enter fullscreen mode Exit fullscreen mode

Next, attempt to use wget to access the Service:

wget myapp:80
Enter fullscreen mode Exit fullscreen mode

Output:

--2024-10-27 06:49:46--  http://myapp/
Resolving myapp (myapp)... failed: nodename nor servname provided, or not known.
wget: unable to resolve host address ‘myapp’
Enter fullscreen mode Exit fullscreen mode

6. Change the Service Type for External Access

  • Goal: Now, we want to change the Service type so it’s accessible externally.
  • Details: Changing the Service type to NodePort or LoadBalancer enables external traffic. Use NodePort if you want to access it through a specific node’s IP and port, or LoadBalancer if your cloud provider’s load balancer will handle it.

Rationale: Instead of executing commands directly in the terminal, we should leverage YAML files for defining our configurations. This approach not only facilitates source control but also ensures that our infrastructure as code (IaC) practices are consistent and reproducible.

Steps:
Remove the existing service configuration:

   rm service.yml
Enter fullscreen mode Exit fullscreen mode

Create a new service definition using your preferred text editor:

   nano service.yml
Enter fullscreen mode Exit fullscreen mode

Copy the following YAML configuration (adapted from the Kubernetes documentation) into service.yml:

apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30001  # Optional: Define the node port if desired
Enter fullscreen mode Exit fullscreen mode

Deploy the new service configuration:

   kubectl apply -f service.yml
Enter fullscreen mode Exit fullscreen mode

For more information, you can refer to the Kubernetes documentation.


7. Access the Service in Your Browser or Using curl

  • Goal: This confirms external access to the Service once exposed.
  • Details: You can visit the nginx homepage in your browser or use curl to see the response from the service.
  • Command:

     curl http://localhost:30001
    

Discussion Points

  1. Exposing Pods without a Deployment:

    • Can it be done? Yes, it’s possible to create a Service that directly exposes individual Pods by using spec.selector to match the Pods' labels.
    • Why (or why not)? However, Deployments provide benefits like auto-scaling, rolling updates, and ensuring a specified number of Pods are always running, which wouldn’t be handled automatically if only using a Service.
  2. Choosing Service Types:

    • ClusterIP: Use it for internal traffic only, where the Service should only be accessible within the cluster. Great for microservices that need to communicate with each other.
    • NodePort: This makes the Service accessible on each Node's IP at a specific port, allowing limited external access. Often used for development or testing but not ideal for production as it doesn’t offer load balancing.
    • LoadBalancer: Ideal for production in cloud environments, as it distributes incoming traffic across multiple nodes or Pods and abstracts the complexity of handling traffic.
    • ExternalName: This type creates an alias for an external hostname, allowing Pods to access external services using Kubernetes DNS. Suitable for situations where in-cluster services need access to an external service, but it’s limited to simple DNS resolution.

Tags and Mentions

@piyushsachdeva
Day 8 video

💖 💪 🙅 🚩
lloydrivers
Lloyd Rivers

Posted on October 27, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related