# Assignment The Nautilus DevOps team has already deployed a ReplicaSet to host an application that requires a highly available infrastructure. Your task is to expose the application running in the existing ReplicaSet by creating a Kubernetes NodePort Service. Follow the specifications below to create the Service and ensure the application pods are accessible: A ReplicaSet named httpd-replicaset is already running in the cluster. The pods managed by the ReplicaSet use the following labels: Assign labels app as httpd_app, and type as front-end. Create a NodePort Service named httpd-service to expose the application. Set the NodePort to 30080. Expose port 80 of the application. Note: Do not delete or modify the configuration of the deployed ReplicaSet application. # Solution # Kubernetes NodePort Service — `httpd-service` A NodePort Service that exposes the pods already managed by the `httpd-replicaset`, matched by their labels. Applied inline via a heredoc — no manifest file on disk, and the ReplicaSet is left untouched. ## Apply (heredoc → kubectl) ```bash kubectl apply -f - <<'EOF' apiVersion: v1 kind: Service metadata: name: httpd-service spec: type: NodePort selector: app: httpd_app type: front-end ports: - port: 80 targetPort: 80 nodePort: 30080 EOF ``` ## How it works ### The heredoc apply pattern - **`kubectl apply -f -`** reads from **stdin**; nothing written to disk. - **`<<'EOF'` (delimiter quoted)** disables shell expansion — the right default for k8s YAML. ### A Service finds pods by label selector A Service has no direct link to the ReplicaSet — it targets **pods** whose labels match its `selector`. This is the crux of the task: - **`selector: {app: httpd_app, type: front-end}`** — must exactly match the labels on the pods the ReplicaSet manages. Any pod carrying **both** labels becomes an endpoint of this Service, regardless of what created it. Since the ReplicaSet stamps its pods with these labels, the Service automatically picks them up. A mismatched selector would yield a Service with **zero endpoints** — it'd exist but route nowhere. This is also why we don't touch the ReplicaSet: the Service attaches by label, so exposing the app needs no change to the existing workload at all. ### NodePort and the port fields `type: NodePort` opens a port on **every node** that forwards to the Service, which in turn load-balances across the matching pods. The three port fields each mean something distinct: | Field | Meaning | |-------|---------| | `port: 80` | The port the **Service** itself listens on (its ClusterIP:80). | | `targetPort: 80` | The port on the **pod/container** traffic is forwarded to — Apache's port 80. | | `nodePort: 30080` | The port opened on **each node's** IP for external access. | So the path is: `:30080` → Service `:80` → pod `:80`. `30080` is inside the valid NodePort range (`30000–32767`), so the API accepts it. ### Why all three ports are set explicitly `targetPort` defaults to `port` if omitted (both 80 here, so it'd work either way), and `nodePort` would be auto-assigned from the range if omitted — but the task pins it to `30080`, so it's set explicitly. Being explicit also makes the manifest self-documenting. ## Verify ```bash # Service created as NodePort with the right ports kubectl get service httpd-service # Endpoints populated = selector matched the ReplicaSet's pods (this is the key check) kubectl get endpoints httpd-service # Confirm it reaches the app (from a node or the jump-host) curl -s http://:30080 | head -n 5 ``` Expected — `httpd-service` of type `NodePort` showing `80:30080/TCP`, and `kubectl get endpoints httpd-service` listing one IP per matching pod. If endpoints is empty, the selector doesn't match the pods' labels — recheck them with `kubectl get pods --show-labels`.