In this backend master class, we’re going to learn everything about how to design, develop, and deploy a complete backend system from scratch using PostgreSQL, Golang, and Docker.
TECH SCHOOL - From noob to pro / techschoolguru At Tech School, we believe that everyone deserves a good and free education. We create high-quality courses and tutorials in Information Technology. If you like the videos, please feel free to share and subscribe to support the channel or buy us a coffee: https://www.buymeacoffee.com/techschool
Content
0.089 -> Hello everyone, welcome to the backend master
class.
3.5 -> In the previous lectures, we’ve learned
how to create an EKS cluster on AWS
8.64 -> And connect to it using kubectl or k9s.
11.19 -> Today let’s learn how to deploy our simple
bank API service to this Kubernetes cluster.
17.29 -> So basically, we have built a docker image
for this service and push it to Amazon ECR,
22.92 -> And now we want to run this image as a container
in the Kubernetes cluster.
27.67 -> In order to do so, we will need to create
a deployment.
31.84 -> Deployment is simply a description of how
we want our image to be deployed.
36.37 -> You can read more about it on the official
Kubernetes documentation page.
40.219 -> And here’s an example of a typical Kubernetes
deployment.
44.76 -> So let’s copy its content,
47.149 -> Open our simple bank project.
49.34 -> Then in the eks folder, I’m gonna create
a new file called deployment.yaml
54.68 -> And paste in the content of the sample deployment.
57.32 -> On the first line is the version of the Kubernetes
API
60.77 -> we’re using to create this deployment object.
63.43 -> Then on the second line, is the kind of object
we want to create,
68.039 -> which is deployment in this case.
70.249 -> Next, there’s a metadata section, where
we can specify some metadata for the object.
75.909 -> For example, the name of the object, I’m
gonna call it simple-bank-api-deployment.
81.09 -> And the labels are basically some key-value
pairs that are attached to the object
87.469 -> Which are useful for the users to easily organize
and select subsets of objects.
93.889 -> Here I’m gonna add only 1 label for the
app, which is called simple-bank-api.
99.499 -> Now comes the main specification of the deployment
object.
102.889 -> First, we can set the number of replicas,
105.289 -> or the number of pods we want to run with
the same template.
108.829 -> For now, let’s run just 1 single pod.
112.09 -> Next, we need to specify a pod selector for
this deployment.
116.079 -> It’s basically a rule that defines how the
deployment can find which pods to manage.
121.939 -> In this case, we’re gonna use a matchLabels
rule.
126.059 -> And I’m gonna use the same label app: simple-bank-api
as we’ve used before.
130.89 -> This means that all pods that have this label
will be managed by this deployment.
135.92 -> Therefore, in the next section, pod template,
we must add the same label to its metadata.
142.129 -> Alright, now comes the spec of the pod.
144.959 -> This is where we tell the deployment how to
deploy our containers.
149.39 -> First, the name of the container is gonna
be simple-bank-api
153.879 -> Then the URL to pull the image from.
156.629 -> As our simple-bank images are stored in Amazon
ECR,
160.29 -> let’s open it in the browser to get the
URL.
163.94 -> Here we can see, there are several images
with different tags.
167.93 -> I’m gonna select the latest one,
170.68 -> And copy its image URL by clicking on this
button.
174.409 -> Then paste it to our deployment.yaml file.
177.829 -> Note that this long suffix of the URL is the
tag of the image.
182.48 -> And it’s basically the git commit hash as
we’ve set up in one of the previous lectures.
187.719 -> For now, we’re setting this tag value manually,
190.099 -> But don’t worry, in later lectures,
192.829 -> I will show you how to change it automatically
with the Github Actions CI/CD.
197.64 -> Alright, now the last thing we’re gonna
do is to specify the container port.
202.37 -> This is the port that the container will expose
to the network.
207.22 -> Although it is totally optional, it’s still
a good practice to specify this parameter
212.51 -> because it will help you or other people to
understand better the deployment configuration.
217.29 -> OK, I think that should be it.
220.769 -> The deployment file is completed.
223.7 -> But before we apply it, let’s use k9s to
check out the current state of the EKS cluster
229.349 -> That we have set up on AWS in previous lectures.
233.23 -> If we select the default namespace,
235.37 -> We can see that there are no running pods
at the moment.
239.739 -> And the deployments list is also empty.
243.319 -> Now let’s apply the deployment file that
we’ve written before
247.109 -> Using the kubectl apply command.
249.92 -> We use the -f option to specify the location
of the object we want to apply,
253.999 -> Which, in this case, is the deployment.yaml
file inside the eks folder.
260.109 -> OK, it’s successful, and the simple-bank-api
deployment has been created.
265.3 -> Let’s check it out in the k9s console.
269.139 -> Here it is, the deployment has shown up in
the list,
272.25 -> but somehow it is not ready yet.
275.86 -> To see more details, we can press d to describe
this deployment object.
280.78 -> OK so the image URL is correct.
283.65 -> And in the events list, it says scaled up
replica set simple-bank-api-deployment to
289.94 -> 1.
290.96 -> All looks pretty normal.
292.199 -> But why this deployment is not ready yet?
295.199 -> Let’s press enter to open the list of pods
that this deployment manages.
300.009 -> OK so looks like the pod is not ready yet.
304.06 -> Its status is still pending.
306.05 -> Let’s describe it to see more details.
309.139 -> If we scroll down to the bottom to see the
events list,
312.5 -> We can see that there’s a warning event:
failed scheduling.
316.789 -> And that’s because there are no nodes available
to schedule pods.
320.88 -> Alright, now we know the reason,
323.099 -> Let’s go to the AWS console page,
326.229 -> and open the EKS cluster simple-bank that
we’ve set up in previous lectures.
330.41 -> Voilà, here it says “This cluster does
not have any attached nodes”.
335.78 -> Let’s open the configuration tab, and select
compute section.
340.25 -> In the Node Groups table, we can see that
the desired size is 0,
345.419 -> So that’s why it didn’t create any nodes
(or EC2 instances).
349.849 -> To fix this, let’s open the simple-bank
node group,
353.199 -> And follow this link to open the auto-scaling
group.
356.509 -> Here we can see, its desired capacity is 0,
and so is the minimum capacity.
362.129 -> Let’s click this Edit button to change it.
365.19 -> I’m gonna increase the desired capacity
to 1.
369.12 -> Note that this number must be within the limit
range of the minimum and maximum capacity.
374.16 -> OK, let’s click Update.
377.29 -> Now the desired capacity has been changed
to 1.
380.33 -> And in the Activity tab, if we refresh the
activity history,
385.62 -> We can see a new entry saying launching a
new EC2 instance.
389.169 -> This might take a while to complete.
392.02 -> So let’s refresh the list.
395.11 -> Now its status has changed to Mid Lifecycle
Action
398.05 -> Let’s wait a bit, and refresh again.
402.72 -> This time, the status is Successful.
405.539 -> So now we have 1 instance available in the
node group.
408.26 -> Let’s go back to the EKS cluster’s node
group page.
412.28 -> Select the nodes tab, and click this refresh
button.
416.47 -> This time, there’s 1 node in the group.
417.81 -> But its status is still not ready.
420.409 -> We have to wait a bit for the EC2 instance
to be set up.
424.22 -> Alright, now the node is ready.
426.199 -> Let’s go back to the k9s console to see
what happens to the pods.
430.349 -> OK, it’s still in pending state.
433.569 -> Let’s find out why!
436.15 -> Now at the bottom, there are 2 new events.
439.11 -> The first one says 0/1 nodes are available,
1 node had taint: not ready,
444.75 -> that the pod didn’t tolerate.
447.21 -> And the second one says: Too many pods.
449.95 -> So this means that the cluster has recognized
the new node,
453.289 -> And the deployment tried to deploy a new pod
to this node,
456.439 -> But somehow the node already has too many
pods running on it.
460.22 -> Let’s dig deeper to find out why.
462.68 -> I’m gonna open the list of nodes.
465.449 -> This is the only node of the cluster.
467.75 -> Let’s describe it!
469.78 -> If we scroll down a bit to the capacity section,
472.78 -> We can see some hardware configurations of
the node, such as the CPU or memory.
478.58 -> And at the bottom is the maximum number of
pods can run on this node, which is 4 in our
484.449 -> case.
485.449 -> There’s also a section to tell you the size
of the resources that can be allocated to
489.18 -> this node.
490.78 -> And if we scroll down a little bit more,
492.819 -> We can see the number of non-terminated pods.
495.3 -> Currently, there are 4 running pods.
498.58 -> And they are listed in this table.
500.37 -> All 4 pods belong to the kube-system namespace.
504.62 -> so these 4 kubernetes system pods
506.919 -> has already taken up all 4 available pod slots
of the node.
510.759 -> That’s why the deployment cannot create
a new one for our container.
514.96 -> In case you don’t know,
516.47 -> the maximum number of pods can run on an EC2
instance
520.27 -> depends on the number of Elastic Network Interfaces
(or ENI)
524.81 -> and the number of IPs per ENI allowed on that
instance.
529.05 -> This github page of Amazon gives us a formula
531.65 -> to compute the maximum number of pods based
on those numbers.
535.31 -> It is: number of ENI multiplied by (number
of IPs per ENI - 1) then plus 2.
542.63 -> There is also a documentation page of Amazon
545.66 -> that gives us the number of ENIs and IPs per
ENI for each instance type.
550.1 -> If you still remember, we’re using a t3.micro
instance for our node group,
555.94 -> So according to this table, it has 2 ENI,
and 2 IPs per ENI.
562.19 -> Now if we put these numbers into the formula,
564.49 -> we will get 2 * (2-1) + 2 = 4,
569.91 -> which is the maximum number of pods that can
run on this type of instance.
573.48 -> If you’re lazy to do the math, you can just
search for t3.micro on this page,
579.6 -> Then here you are, 4 is the maximum number
of pods.
582.61 -> OK, so in order to run at least 1 more pod
on the node, we will need a bigger instance
587.65 -> type
588.65 -> T3.nano is also not enough resource to run
more than 4 pods,
593.17 -> But a t3.small instance can run up to 11 pods,
597.19 -> so it should be more than enough for our app.
599.84 -> Alright, now we need to go back to the Amazon
EKS cluster node group page.
604.98 -> As you can see here, the current instance
type of this node group is t3.micro
609.6 -> Let’s try to change it to t3.small
612.84 -> In this edit node group page, we can change
several things,
616.03 -> such as the scaling, the labels, taints, tags,
or update configuration.
621.05 -> But there’s no option to change the instance
type of the node group.
624.03 -> So I guess we’re gonna need to delete this
node group and create a new one.
628.34 -> Let’s do that!
630.05 -> To delete this node group, we have to enter
its name here to confirm
634.08 -> Then click this Delete button.
636.06 -> OK, now if we go back to the cluster page,
639.25 -> We can see the status of the node group has
changed to Deleting.
641.97 -> After a few minutes, we can refresh the page.
646.53 -> Now the old node group has gone.
648.41 -> Let’s click Add Node Group button to create
a new one.
651.35 -> I’m gonna use the same name: simple-bank
for this node group.
656.01 -> Select the AWS EKS Node Role that we’ve
created in the previous lectures.
660.91 -> Then scroll all the way down, and click Next.
663.96 -> For the node group configuration,
665.86 -> We will use the default values:
667.43 -> Amazon Linux 2 for the image type,
670.16 -> and On-demand for the capacity type.
673.1 -> But for the instance type, we will choose
t3.small instead of t3.micro as before.
679.61 -> Here we can see the max ENI is 3, and max
IP is 12.
684.21 -> So looks like the maximum number of pods is
equal to this max number of IPs minus 1
689.84 -> OK, next, for the disk size, let’s set it
to 10 GiB.
694.85 -> Then for the node group scaling,
696.25 -> I’m gonna set the minimum size to 0,
698.78 -> and the desired size to 1 node.
701.63 -> Then let’s move to the next step.
704.13 -> Here the default subnets are already selected,
706.85 -> so I’m just gonna use them.
708.43 -> No need to change anything.
710.32 -> In the last step, we can review all the configurations
of the node group.
713.93 -> And if they all look good, we can go ahead
to create the group.
718.12 -> OK, so the group is now being created.
721.18 -> This might take a while to complete.
723.63 -> So while waiting for it, let’s go back to
the k9s console
727.51 -> and delete the existing deployment.
729.95 -> To do that, we simply press ctrl + d.
733.42 -> Then select OK, enter
735.42 -> And that’s it, the deployment is deleted.
738.64 -> And its managed pod is deleted as well.
741.81 -> Alright, so now the cluster is back to a clean
state, ready for a new deployment.
747.12 -> Now, let’s refresh the page to see if the
node group is ready or not.
751.25 -> OK, its status is now Active, so it should
be ready.
755.46 -> Let’s open the terminal, and run the “kubectl
apply” command to deploy our app.
760.81 -> The deployment is created.
762.35 -> Let’s check it out in the k9s console.
764.71 -> Yay, I think it works, because this time the
color has just changed from red to green,
770.35 -> And it says READY 1/1 here.
773.42 -> Let’s describe it to see more details.
775.48 -> OK, everything looks good.
777.03 -> The number of replicas is 1.
780.22 -> Let’s check out the pods.
781.88 -> There’s 1 pod, and its status is Running.
785.08 -> Perfect!
786.08 -> Let’s describe this pod.
788.58 -> Scroll all the way to the bottom.
790.57 -> We can see several normal events.
792.37 -> And they’re all successful.
794.55 -> The container has been created and started
with no errors.
798.66 -> Now if we go back to the pods list, and press
enter,
802.2 -> It will bring us to the list of containers.
804.42 -> So, there’s only 1 single container running
in the pod.
808.22 -> If we want to see the logs of this container,
810.52 -> we can simply press L, as it’s clearly written
here.
814.25 -> OK, here are the logs.
816.7 -> It first ran the db migration,
819.12 -> Then the app was successfully started.
820.68 -> The server is now listening and serving HTTP
requests on port 8080.
825.8 -> That’s great!
827.52 -> But the next question is: how can we send
requests to this API?
831.36 -> If we go back to the pod list, we can see
the IP address of the pod,
835.63 -> However, it’s just an internal IP, and cannot
be accessed from outside of the cluster.
841.86 -> In order to route traffic from the outside
world to the pod,
845.48 -> we need to deploy another Kubernetes object,
which is a Service.
850.31 -> You can read more about it on the official
Kubernetes documentation page.
854.56 -> Basically, a service is an abstraction object
858.08 -> that defines a set of rules to route network
traffics
861.02 -> to the correct application running on a set
of pods.
864.44 -> Load balancing between them will be handled
automatically,
868.29 -> Since all the pods of the same deployment
will share a single internal DNS.
872.83 -> OK, here’s an example of how we can define
a service.
877.32 -> I’m gonna copy it.
879.29 -> Then go back to the code,
880.84 -> Let’s create a new file: service.yaml inside
the eks folder.
885.99 -> Then paste in the content of the example service.
889.38 -> It also starts with the api version, just
like the Deployment object.
893.9 -> But now, the kind of this object is Service.
897.09 -> We also have a metadata section to store some
information about this service.
901.05 -> Here I’m just gonna specify the name, which
is simple-bank-api-service.
904.43 -> Next, the specification of the service.
908.66 -> First, we must define a pod selector rule,
911.42 -> so that the service can find the set of pods
to route the traffic to.
915.6 -> We’re gonna use a label selector,
918.52 -> So I’m gonna copy the app label from the
pod template in deployment.yaml file
924.01 -> And paste it to the service.yaml file here,
under the selector section.
928.62 -> OK, next we have to specify the rule for ports.
932.01 -> This service will listen to HTTP API requests,
so the protocol is TCP
937.67 -> Then, 80 is the port, on which the service
will listen to incoming requests.
943.43 -> And finally, the target port is the port of
the container,
946.73 -> Where the requests will be sent to.
948.44 -> In our case, the container port is 8080, as
we’ve specified in the deployment file.
954.84 -> So I’m gonna change this target port value
to 8080 in the service file.
959.73 -> And that’s it!
960.78 -> The service.yaml file is done.
963.33 -> Now let’s open the terminal
965.17 -> and run kubectl apply -f eks/service.yaml
to deploy it.
971.56 -> OK, the service is created.
973.99 -> Let’s check it out in the k9s console.
976.63 -> I’m gonna search for services.
979.71 -> Here we go.
980.71 -> In the list of services, beside the system
service of kubernetes,
984.97 -> we can see our simple-bank api service.
986.64 -> Its type is ClusterIP, and here’s its internal
cluster IP.
993.22 -> And it’s listening on port 80 as we’ve
specified in the yaml file.
998.09 -> But look at the external IP column!
1000.44 -> It’s empty!
1001.63 -> Which means this service doesn’t have an
external IP.
1004.98 -> So how can we access it from outside?
1007.38 -> Well, in order to expose the service to the
outside world,
1011.28 -> we need to change its type.
1013.05 -> By default, if we don’t specify anything,
the service’s type will be ClusterIP.
1017.88 -> Now, let’s change its type to LoadBalancer
instead.
1021.8 -> Then save the file, and go back to the terminal
to apply it again.
1026.64 -> OK, the service is configured.
1028.909 -> This time, in the k9s console, we can see
that its type has changed to LoadBalancer,
1035.089 -> And there’s an external IP, or a domain
name has been assigned to the service.
1039.47 -> Awesome!
1040.47 -> But to make sure that it’s working well,
1042.689 -> Let’s try nslookup this domain.
1044.63 -> Oops, we’ve got an error: server can’t
find this domain name.
1049 -> Maybe it will take a bit of time for the domain
to be ready.
1054.75 -> Now let’s try nslookup again.
1056.27 -> This time, it’s successful.
1057.5 -> You can see that there are 2 IP addresses
associated with this domain.
1061.49 -> That’s because it’s a network load balancer
of AWS.
1065.059 -> OK, now let’s try sending some requests
to the server!
1068.6 -> I’m gonna open Postman, and try the login
user API.
1072.08 -> We must replace the URL localhost:8080 with
the production domain name of the service.
1078.759 -> And if I remember correctly,
1080.23 -> we’ve already created user Alice in the
production DB in one of the previous lectures.
1085.03 -> I’m gonna check it quickly with TablePlus.
1087.52 -> Yes, that’s right!
1089.889 -> User Alice already existed.
1091.419 -> So let’s go back to Postman and send the
request.
1095.179 -> Yee!
1096.179 -> It’s successful.
1097.179 -> We’ve got an access token together with
all user information.
1101.2 -> So it worked!
1102.549 -> Now let’s see the logs of the container.
1104.919 -> Here we go: a POST request to /users/login
1108.1 -> Alright, now if we go back to the service
and describe it,
1112.559 -> We can see that it’s sending the request
to only 1 single endpoint.
1116.279 -> That’s because right now we only have only
1 single replica of the app.
1120.46 -> Let’s see what will happen if we change
the number of replicas to 2 in the deployment.yaml
1125.429 -> file
1126.429 -> Save it, and run kubectl apply in the terminal
to redeploy to the cluster.
1132.149 -> OK, now, let’s go to the deployments list.
1135.95 -> This time, we can see READY 2/2,
1139.74 -> which means there are 2 replicas, or 2 pods
up and running.
1144.009 -> Here they are!
1145.009 -> Now let’s take a look at the service.
1147.1 -> If we describe the simple-bank API service,
1150.35 -> We can see that it is now forwarding requests
to 2 different endpoints,
1154.98 -> And those endpoints are actually the address
of the 2 pods where our application is running
1159.5 -> on.
1160.5 -> Alright, let’s go back to Postman and send
the request again to make sure it still works.
1166.41 -> Cool!
1167.41 -> The request is successful.
1168.41 -> Even when I send it multiple times.
1170.799 -> So the service is handling well the load balancing
of the request when there are multiple pods.
1176.46 -> Now before we finish, let’s check out the
resources of the node.
1179.72 -> I’m gonna describe this node,
1182.49 -> And scroll down to the capacity section.
1185.299 -> Here we can see that the maximum number of
pods that can run on this node is 11,
1190.129 -> Exactly as we calculated for a t3.small instance.
1194.179 -> And if we scroll down a little bit more,
1196.7 -> We can see that at the moment, there are 6
pods running on this node.
1201.21 -> 4 of them are the system pods of Kubernetes.
1204.59 -> And the other 2 are our simple-bank API deployment
pods.
1208.13 -> OK, so that’s all I wanted to share with
you in today’s lecture.
1212.379 -> We have learned how to deploy a web service
application to the Kubernetes cluster on AWS.
1217.629 -> And we were able to send requests to the service
from outside of the cluster
1221.629 -> Via the external IP or an auto-generated domain
name of the service.
1226.13 -> But of course, we don’t want to use that
kind of domain
1229.09 -> For integrating with the frontend for external
services, right?
1232.779 -> What we would like to achieve is to be able
to attach the service
1236.809 -> to a specific domain name that we have bought,
1239.44 -> such as simplebank.com or something like that,
right?
1241.649 -> That will be the topic of the next video.
1244.999 -> I hope you enjoy this video.
1247.409 -> Thanks a lot for watching!
1249.38 -> Happy learning, and see you in the next lecture!