- Learn how to expose a deployment using Ingress
- Learn how to use
deployments
- Learn how to scale deployments
- Learn how to use
services
to do loadbalance between the pods of a scaled deployment
In this exercise, you'll learn how to deploy a pod using a deployment, how to
scale it, and how to expose it using an Ingress
resource with URL routing.
Deployments are a higher level abstraction than pods, and controls the lifecycle
and configuration of a "deployment" of an application. They are used to manage a
set of pods, and to ensure that a specified number of pods are always running
with the desired configuration. deployments
are a Kubernetes kind
and
defined in a manifest file.
A deployment
manifest file looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: # Deployment name
spec:
replicas: # the number of pods to run
selector: # How to select pods belonging to this deployment, must match the pod template's labels
matchLabels: # List of labels to match pods
template: # Pod template
metadata:
labels: # List of labels
spec:
containers: # List of containers belonging to the pod
- name: # Name of the container
image: # Container image
💡 An Example: Nginx deployment
An example of a deployment manifest file for nginx would look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
In order to make our application stable, we want high availability. High availability means that we replicate our applications, such that we have redundant copies, which means that when an application fails, our users are not impacted, as they will simply use one of the other copies, while the failed instance recovers.
In Kubernetes this is done in practice by scaling
a deployment, e.g. by adding or removing
replicas
. replicas
are identical copies of the the same pod.
To scale a deployment, we change the number of replicas
in the manifest file, and then apply the
changes using kubectl apply -f <manifest-file>
.
Ingress in Kubernetes that manages external access to the services in a cluster, typically HTTP and HTTPS.
Ingress can provide load balancing, SSL termination, and name-based virtual routing.
Ingress builds on top of the service
concept, and is implemented by an ingress controller
.
An example Ingress manifest to be used in AWS looks like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# Annotations are used to configure the ingress controller behavior.
alb.ingress.kubernetes.io/scheme: internet-facing
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
name: quotes-ingress
spec:
# Rules are used to configure the routing behavior.
rules:
# Each rule has a host and a list of paths. The host is used to match the
# host header of the request, normally the domain name.
- host: quotes-1.prosa.eficode.academy
http:
paths:
- pathType: Prefix
path: "/"
# Each path has a backend, which is used to route the request to a service.
backend:
service:
# This is the name of the service to route to.
name: quotes-frontend
port:
number: 80
When you apply this manifest, an A-record will be created in Route53, pointing to our ingress-controller called ALB, and the ALB will route traffic to the service.
- Add frontend Ingress and let it reconcile
- Turn the backend pod manifests into a deployment manifest
- Apply the backend deployment manifest
- Scale the deployment by adding a replicas key
- Turn frontend pod manifests into a deployment manifest
- Apply the frontend deployment manifest
- Test service promise of high availability
💡 If you get stuck somewhere along the way, you can check the solution inthe done directory.
Step by step:
- Go into the
deployments-ingress/start
directory.
In the directory we have the pod manifests for the backend and frontend that have created in the previous exercises. We also have two services, one for the backend (type ClusterIP) and one for the frontend (type NodePort) as well as an ingress manifest for the frontend.
As it might take a while for the ingress to work, we will start by adding the ingress to the frontend service, even though we have not applied the service yet.
- Open the
frontend-ingress.yaml
file in your editor. - Change the hostname to
quotes-<yourname>.<prefix>.eficode.academy
. Just as long as it is unique.- the prefix normally is what is after your workstation-X.
<prefix>
.eficode.academy. If you are unsure, ask the trainer.
- the prefix normally is what is after your workstation-X.
- Change the service name to match the name of the frontend service.
- Apply the ingress manifest.
kubectl apply -f frontend-ingress.yaml
Expected output:
ingress.networking.k8s.io/frontend-ingress created
- Check that the ingress has been created.
kubectl get ingress
Expected output:
NAME HOSTS ADDRESS PORTS AGE
frontend-ingress quotes-<yourname>.<prefix>.eficode.academy 80 1m
Congratulations, you have now added an ingress to the frontend service. It will take a while for the ingress to work, so we will continue with the backend deployment.
To show how Deployments take the place of Pod manifests we will first deploy the quotes application, and then slowly replace the Pods with Deployments.
-
Deploy the following using the
kubectl apply -f
commandfrontend-pod.yaml
frontend-svc.yaml
backend-pod.yaml
backend-svc.yaml
-
Verify that the frontend is accessible from the browser.
How do I connect to a pod through a NodePort service?
💡 In previous exercises you learned how connect to a pod exposed through a NodePort service, you need to find the nodePort using
kubectl get service
and the IP address of one of the nodes usingkubectl get nodes -o wide
Then combine the node IP address and nodePort with a colon between them, in a browser or using curl:
http://<node-ip>:<nodePort>
Now we'll replace the Pod manifest with a Deployment manifest. In addition to the regular manifest fields, we need to set couple of extra things:
- A Pod
template
. This almost a copy of most of the Pod manifest. This defines the Pods that will be created by the deployment. - A
selectorLabel
. This is used to determine which pods to manage will be managed by the ReplicaSet. - A number of
replicas
. This specifies how many simultaneous identical copies we want of the Pod
To do this, open both the backend-deployment.yaml
and the backend-pod.yaml
files in your editor,
and make the following changes to backend-deployment.yaml
:
- Set
metadata.name
tobackend
- Add the label
run: backend
under themetadata.labels
key - Set
spec.replicas
to1
Now we need to set the selectorLabel
- Add a
selector
key under thespec
key - Add a
matchLabels
key under theselector
key - Look up the the
metadata.label
section inbackend-pod.yaml
and the labels defined there undermatchLabels
💡 Hint
The matchLabels
key should look like this:
...
spec:
replicas: 1
selector:
matchLabels:
run: backend
template:
...
Finally, we'll add the Pod template
. We will take this information from the Pod manifest.
- Copy the
metadata.labels
andspec
contents of thebackend-pod.yaml
file into thebackend-deployment.yaml
file under thespec.template
key - Make sure the copied section is properly indented by two spaces more than
spec.template
- Delete the
spec.template.metadata.name
key. The Pods will be named automatically by theReplicaSet
💡 Hint (solution)
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: backend
name: backend
spec:
replicas: 1
selector:
matchLabels:
run: backend
template:
metadata:
labels:
run: backend
spec:
containers:
- image: ghcr.io/eficode-academy/quotes-flask-backend:release
name: quotes-flask-backend
Now, it is time to replace the backend
Pod with the `backend
Deployment. We've already deployed the Pod, so let us remove it again before applying the Deployment
-
Delete the
backend
podkubectl delete -f backend-pod.yaml
-
Apply the deployment manifest
kubectl apply -f backend-deployment.yaml
Expected output:
deployment.apps/backend-deployment created
-
Check that the deployment has been created.
kubectl get deployments
Expected output:
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE backend 1 1 1 1 1m
-
Check that the pod has been created.
kubectl get pods
Expected output:
NAME READY STATUS RESTARTS AGE backend-5f4b8b7b4-5x7xg 1/1 Running 0 1m
-
Access the frontend again from the browser.
Now the Ingress should work and you should be able to access the frontend from the browser using the hostname you specified in the ingress manifest.
The url should look something like this:
http://quotes-<yourname>.<prefix>.eficode.academy
-
If it still does not work, you can check it through NodePort service instead.
-
You should now see the backend.
-
If this works, please delete the
backend-pod.yaml
file, as we now have upgraded to a deployment and no longer need it!
-
Scale the deployment by changing the replicas key in the deployment manifest. Set the replicas key to 3.
-
Apply the deployment manifest again.
kubectl apply -f backend-deployment.yaml
Expected output:
deployment.apps/backend-deployment configured
- Check that the deployment has been scaled.
kubectl get deployments
Expected output:
NAME READY UP-TO-DATE AVAILABLE AGE
backend 3/3 3 3 3m29s
- Check that the pods have been scaled.
kubectl get pods
Expected output:
NAME READY STATUS RESTARTS AGE
backend-5f4b8b7b4-5x7xg 1/1 Running 0 2m
backend-5f4b8b7b4-6j6xg 1/1 Running 0 1m
backend-5f4b8b7b4-7x7xg 1/1 Running 0 1m
- Access the frontend again from the browser. It should now periodically change the
hostname
part of the website.
You will now do the exact same thing for the frontend, we will walk you through it again, but at a higher level, if get stuck you can go back and double check how you did it for the backend.
- Open both the
frontend-deployment.yaml
and thefrontend-pod.yaml
files in your editor. - add the api-version and kind keys to the
frontend-deployment.yaml
file. - Give the deployment a name of
frontend
undermetadata.name
key. - Add a label of
run: frontend
undermetadata.labels
key. - Set
spec.replicas
to3
. - Copy the
metadata
andspec
contents of thefrontend-pod.yaml
file into thefrontend-deployment.yaml
file under thespec.template
key. - Add a selector key under the
spec
key.- The selector key should have a
matchLabels
key. - The
matchLabels
key should have arun: frontend
key-value pair.
- The selector key should have a
- First, delete the frontend pod.
kubectl delete pod frontend
Expected output:
pod "frontend" deleted
- Apply the frontend deployment manifest.
kubectl apply -f frontend-deployment.yaml
Expected output:
deployment.apps/frontend-deployment created
- Check that the deployment has been created.
kubectl get deployments
Expected output:
NAME READY UP-TO-DATE AVAILABLE AGE
backend 3/3 3 3 2m41s
frontend 3/3 3 3 2m41s
- Check that the pod has been created.
kubectl get pods
Expected output:
NAME READY STATUS RESTARTS AGE
backend-5f4b8b7b4-5x7xg 1/1 Running 0 3m
backend-5f4b8b7b4-6j6xg 1/1 Running 0 2m
backend-5f4b8b7b4-7x7xg 1/1 Running 0 2m
frontend-47b45fb8b-4x7xg 1/1 Running 0 1m
frontend-47b45fb8b-4j6xg 1/1 Running 0 1m
frontend-47b45fb8b-4x7xg 1/1 Running 0 1m
-
Access the frontend again from the browser. Note that both the frontend and backend hostname parts of the website should change periodically.
-
If this works, please delete the
frontend-pod.yaml
file, as we now have upgraded to a deployment and no longer need it!
- Delete the deployments.
kubectl delete -f frontend-deployment.yaml
kubectl delete -f backend-deployment.yaml
- Delete the services
kubectl delete -f frontend-svc.yaml
kubectl delete -f backend-svc.yaml
- Delete the ingress
kubectl delete -f frontend-ingress.yaml
💡 If you ever want to delete all resources from a particular directory, you can use:
kubectl delete -f .
which will point at all files in that directory!
Test Kubernetes promise of resiliency and high availability
An example of using a LoadBalancer service to route traffic to replicated pods
We can use the ghcr.io/eficode-academy/network-multitool
image to illustrate both high
availability and load balancing of services
. The network-multitool
pod will serve a tiny
webpage that dynamically contains the pod hostname and IP address of the pod. This enables us to see
which of a group of network-multitool pods that served the request.
Create the network-multitool deployment:
kubectl create deployment customnginx --image ghcr.io/eficode-academy/network-multitool --port 80 --replicas 4
We create the network-multitool deployment with the name "customnginx" and with four replicas, so we expect to have four pods.
We also create a service of type LoadBalancer
:
kubectl expose deployment customnginx --port 80 --type LoadBalancer
💡 It might take a minute to provision the LoadBalancer, if you are using AWS, then
kubectl get services
will show you the DNS name of the provisioned LoadBalancer immediately, but it will be a moment before it is ready.
When the LoadBalancer is ready we setup a loop to keep sending requests to the pods:
while true; do curl --connect-timeout 1 -m 1 -s <loadbalancerIP> ; sleep 0.5; done
Expected output:
Eficode Academy Network MultiTool (with NGINX) - customnginx-7fcfd947cf-zbvtd - 100.96.2.36 <BR></p>
Eficode Academy Network MultiTool (with NGINX) - customnginx-7fcfd947cf-zbvtd - 100.96.1.150 <BR></p>
Eficode Academy Network MultiTool (with NGINX) - customnginx-7fcfd947cf-zbvtd - 100.96.2.37 <BR></p>
Eficode Academy Network MultiTool (with NGINX) - customnginx-7fcfd947cf-zbvtd - 100.96.2.37 <BR></p>
Eficode Academy Network MultiTool (with NGINX) - customnginx-7fcfd947cf-zbvtd - 100.96.2.36 <BR></p>
We see that when we query the LoadBalancer IP, it is giving us result/content from all four pods. None of the curl commands time out. Now, if we kill three out of four pods, the service should still respond, without timing out. We let the loop run in a separate terminal, and kill three pods of this deployment from another terminal.
kubectl delete pod customnginx-3557040084-1z489 customnginx-3557040084-3hhlt customnginx-3557040084-c6skw
Expected output:
pod "customnginx-3557040084-1z489" deleted
pod "customnginx-3557040084-3hhlt" deleted
pod "customnginx-3557040084-c6skw" deleted
Immediately check the other terminal for any failed curl commands or timeouts.
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-4w4gf - 10.244.0.19
Expected output:
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-h2dbg - 10.244.0.21
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-5xbjc - 10.244.0.22
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-h2dbg - 10.244.0.21
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-4wn9c - 10.244.0.20
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-5xbjc - 10.244.0.22
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-h2dbg - 10.244.0.21
Eficode Academy Network MultiTool (with NGINX) - customnginx-59db6cff7b-5xbjc - 10.244.0.22
We notice that no curl commands failed, and actually we have started seeing new IPs.
Why is that? It is because, as soon as the pods are deleted, the deployment sees that it's desired state is four pods, and there is only one running, so it immediately starts three more to reach the desired state of four pods. And, while the pods are in process of starting, one surviving pod serves all of the traffic, preventing our application from missing any requests.
kubectl get pods
Expected output:
NAME READY STATUS RESTARTS AGE
customnginx-3557040084-0s7l8 1/1 Running 0 15s
customnginx-3557040084-1z489 1/1 Terminating 0 16m
customnginx-3557040084-3hhlt 1/1 Terminating 0 16m
customnginx-3557040084-bvtnh 1/1 Running 0 15s
customnginx-3557040084-c6skw 1/1 Terminating 0 16m
customnginx-3557040084-fw1t3 1/1 Running 0 16m
customnginx-3557040084-xqk1n 1/1 Running 0 15s
This proves, Kubernetes enables high availability, by using multiple replicas of a pod, and loadbalancing between them.
Remember to clean up the deployment afterwards with:
kubectl delete deployment customnginx
And delete the LoadBalancer service:
kubectl delete service customnginx