1.3. Exposing a Service
In this lab, we are going to make the frontend from the last lab accessible externally.
Task 1.3.1: Create a ClusterIP Service
The command kubectl apply -f deployment_example-frontend.yaml
from the last lab creates a Deployment but no Service.
A Kubernetes Service is an abstract way to expose an application running on a set of Pods as a network service. For some parts of your application (like frontends), you may want to expose a Service to an external IP address outside your cluster.
Kubernetes ServiceTypes
allow you to specify the type of Service you want. The default is ClusterIP
.
Type
values and their behaviors are:
ClusterIP
: Exposes the Service on a cluster-internal IP. Only reachable from within the cluster. This is the default ServiceType.NodePort
: Exposes the Service on each Node’s IP at a static port (the NodePort). A ClusterIP Service, to which the NodePort Service routes, is automatically created. Access the NodePort Service from outside the cluster by requesting<NodeIP>:<NodePort>
.LoadBalancer
: Exposes the Service externally using a cloud provider’s load balancer. NodePort and ClusterIP Services, which the external load balancer routes to, are automatically created.ExternalName
: Maps the Service to the contents of the externalName field (e.g., foo.bar.example.com), by returning a CNAME record with its value. No proxying is set up.
Ingress can also be used to expose your Service.
Ingress is not a Service type, but it acts as the entry point for your cluster. Ingress exposes HTTP and HTTPS routes from outside the cluster to services within. Traffic routing is controlled by rules defined on the Ingress resource. An Ingress may be configured to provide Services with externally reachable URLs, load-balance traffic, terminate SSL/TLS, and offer name-based virtual hosting. An Ingress controller is responsible for fulfilling the route, typically with a load balancer or configuring your edge router or frontends to handle the traffic.
To create an Ingress, we first need a Service of type ClusterIP .
To create the Service, add a new file svc-frontend.yaml
with the following content:
---
apiVersion: v1
kind: Service
metadata:
labels:
app: example-frontend
name: example-frontend
spec:
ports:
- port: 5000
protocol: TCP
targetPort: 5000
selector:
app: example-frontend
type: ClusterIP
Then, apply the file with:
kubectl apply -f svc-frontend.yaml --namespace <namespace>
Alternatively, an imperative command can create and expose the Service without a YAML file:
kubectl expose deployment example-frontend --type=ClusterIP --name=example-frontend --port=5000 --target-port=5000 --namespace <namespace>
Let’s inspect the Service:
kubectl get services --namespace <namespace>
This should output something similar to:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-frontend ClusterIP 10.43.91.62 <none> 5000/TCP
Note
Service IP (CLUSTER-IP) addresses remain stable for the lifespan of the Service.Get more information with:
kubectl get service example-frontend -o yaml --namespace <namespace>
Example output:
apiVersion: v1
kind: Service
metadata:
...
labels:
app: example-frontend
managedFields:
...
name: example-frontend
namespace: <namespace>
...
spec:
clusterIP: 10.43.91.62
externalTrafficPolicy: Cluster
ports:
- port: 5000
protocol: TCP
targetPort: 5000
selector:
app: example-frontend
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
The Service’s selector
defines which Pods are being used as Endpoints. This happens based on labels. Look at the configuration of Service and Pod in order to find out what maps to what:
...
selector:
app: example-frontend
...
With the following command you get details from the Pod:
kubectl get pod <pod> -o yaml --namespace <namespace>
Ensure the Pod’s labels match the Service’s selector:
...
labels:
app: example-frontend
Alternatively, use:
kubectl describe service example-frontend --namespace <namespace>
The Endpoints
section lists IPs of the matching Pods.
Task 1.3.2: Expose the Service with Ingress
With the ClusterIP Service ready, create an Ingress resource. First, create ing-example-frontend.yaml
and adjust the host
entry as needed:
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-frontend
spec:
rules:
- host: example-frontend-<namespace>.<appdomain>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-frontend
port:
number: 5000
tls:
- hosts:
- example-frontend-<namespace>.<appdomain>
The Ingress resource at spec.rules[0].http.paths[0].backend.service.name
uses the example-frontend
ClusterIP Service.
Apply the Ingress with:
kubectl apply -f ing-example-frontend.yaml --namespace <namespace>
Access the Ingress at http://example-frontend-<namespace>.<appdomain>
.
Task 1.3.3: (Optional) Expose as NodePort
Note
This advanced option is optional. NodePorts are typically not used for HTTP-based applications, as Ingress provides layer 7-based routing. NodePort is useful for non-HTTP applications.To make a Service accessible from outside using NodePort
, create a new Service in svc-frontend-nodeport.yaml
:
apiVersion: v1
kind: Service
metadata:
labels:
app: example-frontend
name: example-frontend-nodeport
spec:
ports:
- port: 5000
protocol: TCP
targetPort: 5000
selector:
app: example-frontend
type: NodePort
Note the changes in type: NodePort
and ports
sections. Apply it with:
kubectl apply -f svc-frontend-nodeport.yaml --namespace <namespace>
Inspect the NodePort Service:
kubectl get services -l app=example-frontend --namespace <namespace>
The output will show:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-frontend-nodeport NodePort 10.43.91.62 <none> 5000:30692/TCP
Access http://<node-ip>:<node-port>
in your browser or with curl
. Use kubectl get nodes -o wide
to list node IPs.
kubectl get node -o wide
Output example:
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
lab-1 Ready controlplane,etcd,worker 150m v1.17.4 5.102.145.142 <none> Ubuntu 18.04.3 LTS 4.15.0-66-generic docker://19.3.8
lab-2 Ready controlplane,etcd,worker 150m v1.17.4 5.102.145.77 <none> Ubuntu 18.04.3 LTS 4.15.0-66-generic docker://19.3.8
lab-3 Ready controlplane,etcd,worker 150m v1.17.4 5.102.145.148 <none> Ubuntu 18.04.3 LTS 4.15.0-66-generic docker://19.3.8
Task 1.3.4: (Advanced) Bring it all together
Now that we’ve covered how to create a Deployment, Service, and Ingress resource, it’s your turn to try it on your own. Create a Deployment with two pods using the image go-httpbin:2 .15.0 . Expose this Deployment using a Service, and set up an Ingress that responds to:
http://example-frontend-<namespace>.<appdomain>/headers
and a second Ingress that responds tohttp://example-httpbin-<namespace>.<appdomain>
Tip: Use imperative commands with --dry-run=client -o yaml
to preview the resource definitions before applying them.
Task 1.3.5: For fast learners
Examine resources in your namespace with:
kubectl describe namespace <namespace>
kubectl get all --namespace <namespace>
kubectl describe <resource> <name> --namespace <namespace>
kubectl get <resource> <name> -o yaml --namespace <namespace>