Exposing apps
Earlier, we learned how to run software in Kubernetes as Deployments and how to access it within the cluster.
When the software users are in the cluster, they can directly contact it through the Service resource.
A lot of times the software user is outside the cluster (e.g. a person with a web browser) so we need a way to make our app available outside the cluster.
For exposing our application, we can use Ingress.
Ingress allows mapping HTTP/HTTPS traffic to Kubernetes hosted applications based on rules provided to Kubernetes.
In this section, we'll learn how to expose our application to users outside the Kubernetes cluster.
Load balancing for our app
Now that we have a working multi-pod deployment for our app, we'd naturally like to have a way to balance load between all of the Pods.
Let's create a YAML manifest named demo-service.yaml
with the following contents:
apiVersion: v1
kind: Service
metadata:
name: demo
labels:
app: demo
spec:
selector:
app: demo-pod
ports:
- port: 80
targetPort: 8080
protocol: TCP
In this YAML:
- We use the
spec.selector
to find Pod targets for our Service. The traffic sent to the service will be forwarded to the Pods have the matching labels. - We use the
ports
section to define which ports in the Service should be forwarded to which ports in the Pods.
Let's create the Service and see what information it gives us.
$ kubectl apply -f demo-service.yaml
service/demo created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo ClusterIP 172.20.255.79 <none> 80/TCP 10s
Our service has the IP address 172.20.255.79
.
The IP assigned to your Service in your cluster might be different, remember to replace the curl
command with it.
We can try the one-off pod to communicate with it:
$ kubectl run --rm -it --restart=Never --image=curlimages/curl call-demo -- http://100.66.240.247
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
Kubernetes also creates two DNS names for our service in pattern:
- One using the name of the Service:
demo
- One using the name of the Service and the namespace:
demo.tutorial.svc.cluster.local
.
Both of these names can be used from the namespace where we created our Service in, and the second one can be used everywhere in the cluster. Let's try using them!
$ kubectl run --rm -it --restart=Never --image=curlimages/curl call-demo -- demo
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
$ kubectl run --rm -it --restart=Never --image=curlimages/curl call-demo -- demo.tutorial.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
Perfect! We can also check which Pods the service uses as targets by looking into the endpoints.
$ kubectl get endpoints
NAME ENDPOINTS AGE
demo 10.0.2.105:8080,10.0.2.46:8080 8m59s
The IP addresses listed above are the IP addresses of the Pods.
Preparation
Before we can make an app available outside the cluster, we need to have it running.
Note
If you've followed the earlier sections and haven't reset your Kubernetes environment or deleted the resources, you should have a demo app already running.
In case you don't have the app running, make sure you have the following contents in the following files.
File: demo-content.yaml
:
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-content
data:
hello.txt: Hello world!
filler.txt: |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
File demo-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
labels:
app: demo
spec:
replicas: 3
progressDeadlineSeconds: 20
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: demo-pod
template:
metadata:
labels:
app: demo-pod
spec:
containers:
- image: nginxinc/nginx-unprivileged
name: nginx
ports:
- containerPort: 8080
readinessProbe:
periodSeconds: 5
successThreshold: 1
httpGet:
path: /
port: 8080
volumeMounts:
- name: content
mountPath: /usr/share/nginx/html/content
volumes:
- name: content
configMap:
name: demo-content
File demo-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: demo
labels:
app: demo
spec:
selector:
app: demo-pod
ports:
- port: 80
targetPort: 8080
protocol: TCP
Let's make sure these are deployed to the training cluster:
$ kubectl apply -f demo-content.yaml -f demo-deployment.yaml -f demo-service.yaml
To ensure that the demo application is working, run the following command:
$ kubectl run --restart=Never --rm -it --image=curlimages/curl call-demo -- http://demo/content/hello.txt
Hello world!
We're now ready to play with Ingress!
Ingress resource
Like everything else in Kubernetes, Ingress is also configured using Kubernetes resources.
Let's create a new YAML manifest named demo-ingress.yaml
with the following contents.
Note
If you're participating in one of Polar Squad's workshops, the instructors will provide you a host value to use below.
Otherwise, you can leave it as null like below.
Note
Depending on the cluster setup, you may need to set the Ingress class either with an annotation, or a specific field in the spec.
The instructors will help you with this.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
labels:
app: demo
spec:
rules:
- host: <namespace>.k8s.polarsquad.training
http:
paths:
- path: /content
pathType: Prefix
backend:
service:
name: demo
port:
number: 80
In this YAML file, the apiVersion
is now networking.k8s.io/v1
.
This is distinct to Ingress
kind.
We can specify one or more routing rules in the spec.rules
section.
Here we specify a single routing rule from all incoming HTTP/HTTPS traffic to path /content
to be routed to the demo service port 80.
The destination should match the Service details we created earlier.
The host value can be used for routing HTTP/HTTPS traffic based on domain names.
For example, if we used host foobar.example.org
, then only requests send to that host would be forwarded to the the demo application.
In the Polar Squad workshops, the cluster is pre-configured with domain names that you can use, which is k8s.polarsquad.training.
Let's create the resource using kubectl
.
$ kubectl apply -f demo-ingress.yaml
ingress.networking.k8s.io/demo-ingress created
Accessing our application
If we monitor the resource, we should eventually see that it gets an IP address.
$ kubectl get ingress -w
NAME CLASS HOSTS ADDRESS PORTS AGE
demo-ingress <none> foo.k8s.polarsquad.training 172.20.86.239 80 2m19s
The address there is the public IP address of a Load Balancer that points to the Kubernetes training cluster. Note that the address is different for your training cluster.
Open the IP address / hostname in your web browser (or using curl
) in the /content/hello.txt
path (e.g. http://foo.k8s.polarsquad.training/content/hello.txt).
Note
If you're participating in one of Polar Squad's training sessions, you should use the address you specified in the host:
field.
Open the address in your web browser (or using curl
) in the /content/hello.txt
path.
You should get Hello world!
as the response.
If we attempt to navigate to the root path of the Ingress, we should get a 404 Not Found response.
This is because we only specified /content
path to be routed to the demo app.
We can also create other routing rules to other apps in the cluster using the same Ingress resource or a new one.
As mentioned earlier, we can route the requests based on paths and domain names.
Freestyle: Another application
Based on the manifests shown above, create another application deployment next to the demo application created earlier and make it available outside the training cluster in another HTTP path using Ingress.
Make sure you use different resource names and labels in your YAML.
Next
In the next section, we'll learn how to debug our applications in Kubernetes.