0:00
Hello friends and welcome to this
0:29
Kubernetes fundamental series. This is session five. I'm Rodrigo. And today I'm going to talk about services
0:39
Okay. So before we do that, let me do a short recap about the last session
0:48
where we talked about deployments and replica sets. So the thing about replica sets and deployments is that you don't deploy your pods in an isolated fashion
1:06
Most of the time you want a deployment to deploy, right, your applications
1:12
And inside your deployment, you can specify a replica set. In other words, you're specifying how many pods you want in that particular deployment for a particular pod
1:25
Okay. So we talked about replica sets as a way of defining how many replicas of a specific pod we need in the cluster
1:40
However, most of the time you want to use a deployment. Okay. So let me go and talk about deployment
1:51
deployment. So those deployments enabled you to deploy and manage updates for pods. That's
1:57
the difference between replica sets and deployments. With deployments, you can do rollouts and
2:02
rollbacks of those deployment objects, and they manage the lifecycle of a pod. That's
2:11
the difference between deployments and replica sets. And of course, the best practice is to
2:17
use a deployment. Okay. So I showed you this the other day. This is a deployment.jml file
2:28
Of course, a fictitious one. And in the spec field, we can specify the spec of the deployment
2:37
inside the spec, we can define the template for the pod. So as you can see here
2:47
we can read that blue text that is defining the pod. In this case, it's using this Redis image
2:56
and is using container port 80 and is defining some labels about that
3:04
However, the most important thing, of course, which is the deployment. However, here in the replicas field
3:10
inside the spec of the deployment, we can define how many replicas we want
3:16
for that particular pod, okay? So I used some of those commands
3:23
that are here on the screen, such as describe deploy for obtaining all the metadata
3:30
about the deployment and get deploy or get deployment or get deployments
3:35
Those are aliases for obtaining all the deployments in the cluster. And of course, if you pass the deployment name
3:45
you can get the specific deployment that you're specifying. So this is one thing that I missed the other day
3:57
I didn't show you this. And it's about the rollouts. Of course, I can create a deployment just by, you know, code cuddle and then apply dash F and the name of the jammu file
4:15
And that's going to deploy or create the deployment, right? However, we can use other commands for obtaining the rollout history of a particular deployment
4:28
And we can also roll back a deployment. So actually, you know what? Let's do this. I created this mini cube cluster beforehand to expedite this process. So as you can see, I have a cluster with three running at this moment, right
4:53
So if I go to session four, I have the my deployment.jama file
5:00
Let's open up Visual Studio Code. And here, as you can see, I have the deployment object
5:09
And I'm specified 10 replicas of this pod, which is based on the Redis image name, right
5:18
So let's do this. kubectl apply dash F my deployment, and this is gonna create the deployment
5:29
and we can get pods to obtain all the different pods that are being created right in this moment
5:40
Okay, so let's do that again. Some of those are running and some others are still being created
5:49
and all of them are running right now, okay? So I can show you get deploy or get deployment
6:00
deployments, right? So those are aliases of the same thing. This is the deployment and it's saying, you know what
6:11
I have 10 pods, okay? I can actually run kubectl describe deployment
6:19
slash my deployment to understand the metadata about this particular deployment, okay
6:29
So I can see the status of the deployment and some other things such as the port
6:35
and the labels that were used for this deployment. And of course, labels and annotations and selectors
6:46
I'm gonna explain those in the following session, okay? okay so this is great now what happens if i make a change in the pods maybe i want to change the
7:00
image name maybe i want to do i don't know some particular change about those pods in this
7:08
deployment right so i can go back here and change something such as i don't know let's change the
7:17
Of course this is quite strange because most of the time you not going to change the image name and the underlying application like I doing right now okay However it fine
7:35
Let's execute kubectl apply dash f my deployment. And now those pods, if I execute get pods
7:46
some of those are being created because now I changed the image name
7:54
and some others are running right and this is very fast because i already have the
8:00
uh nginx image image container image here in my computer okay so cool what happens if i want to
8:10
see the rollout history so i can execute kubectl rollout history and the name of the deployment
8:17
So let's go back to the terminal, kubectl. What was it? Rollout history, rollout history
8:28
and deployments and the name of the deployment. Okay. So as you can see, I have two versions
8:37
The first one was the original one that I created by using kubectl apply, right
8:43
And the second one is the one that was rolled out by the change I made in the JML file
8:56
Okay. We don't have change causes because this is related with annotations
9:01
And actually I have this code right here in the slide that allows me to define this annotation
9:10
However, the annotations topic, I'm going to speak about that in the following session
9:15
Okay. So that's great. It means that all my pods are based on the Nginx image
9:23
I can actually execute get pods and describe one of them, kubectl get pod and the name
9:32
of the pod describe, not get, describe. and of course this is using the nginx image right so far so good so uh what happens if i
9:49
made a mistake what happens if i want to roll back a deployment so you can actually do that by
9:57
executing rollout undo okay rollout undo and the name of the deployment
10:03
So kubectl, rollout, undo, and deployments, my deployment. So let's see what's going on here
10:15
kubectl, get pods. Some pods are being created, and it makes sense, right
10:22
Because now I rolled back to the Redis image name for the pods, right
10:31
So let's execute this again. and now let's describe this pod and kubectl describe pod and the name of the pod
10:42
And as you can see, this is based on the image in the Redis image name, right
10:50
And if I execute rollout history, we can see that now I have this list, right
10:59
Because I rolled back and now I'm in version three. So this is how Kubernetes is managing all those versions. Okay
11:09
Of course, it's kind of tricky if you don't have change causes
11:14
That's why you can use kubectl annotate and the name of the deployment that you want to annotate
11:21
In this case kubectl annotate deployment, that's the entire command name, the name of the deployment
11:28
And then you use the kubernetes.io change cause station and a value that you want, right
11:36
So actually I want to copy this so I don't make a mistake here
11:42
and to expedite this process. So kubectl annotate deployment, the name of the deployment
11:48
the name of the annotation that you want to use. And actually this is the change cause annotation
11:54
that is right there. And I'm annotating that with this value, right
12:00
With fix. Cool. And of course, I'm using this flag, which is override for overriding the value of the change cost
12:10
And let's execute this and let's see the history again. And now I'm able to see what happened with that particular deployment
12:22
And this is very common. Of course, you can have a lot of different changes in the deployment through history
12:32
Right? So it comes in handy if you have the change costs
12:38
And we can do that by executing the annotate deployment command. So far so good about deployments
12:48
And now let's talk about services. So the thing about pods is that those are great
12:55
Of course, our containers are running inside a pod. that's the smallest object that we can deploy
13:04
in a Kubernetes cluster. However, let me execute this, oh, I'm using the wide option
13:16
so we can see the IP addresses of those pods. So pods are great
13:24
However, those IP addresses that we're seeing in the screen, of course, each pod has a unique IP address, okay
13:34
Those IP addresses are randomly created. However, they could change over time
13:41
because as you saw, you can just delete a pod and then Kubernetes is gonna fulfill your desire
13:50
to have the number of replicas in the cluster and it's going to create a new pod
13:57
So pods are dynamic, right? By nature, pods are dynamic. So it's not a good idea to reference pods directly
14:08
We cannot rely on pre-assigned IP addresses because of course those IP addresses could change
14:15
Pods are being deleted all the time and pods are being created all the time, okay
14:23
And on the other hand, pod to pod communication is unrestricted. And I'm going to talk about in a future session
14:34
However, the main thing here on the gist of this slide is that we cannot rely on pre-assigned IP addresses
14:43
Okay. That's why we have services that provide a stable IP and DNS name
14:50
Services know about pods. regardless of the instance of the pod right Because the name of the instance and the IP address that a specific pod has it doesn matter if that deleted
15:07
and then a new pod comes. The service will know about those pods
15:12
those backing pods for your application, okay? So a service can behave like a load balancer
15:25
right, for a number of pods. And so we don't have to use pods directly
15:33
And actually a pod, if you want to communicate from a pod to another pod
15:39
then you use a service as well, okay? So there are four types of services
15:48
Cloth therapy, which is the default. This is for exposing the service inside or within the cluster
15:57
And NodePort for exposing the service outside the cluster. And LoadBalancer for exposing the service through the use of a LoadBalancer
16:09
And of course, this is related with managed Kubernetes, such as, I don't know, Azure Kubernetes Service
16:17
or Google Kubernetes Engine and those cloud provider offerings. And finally, external name
16:27
that allows you to map a service to a DNS name. I guess the most important ones
16:34
are Cluster IP and NodePort. And that's actually what I'm going to explain
16:39
in this session, Cluster IP and NodePort. Okay. Another trait about services is that they send traffic only to healthy pods
16:56
What happens if a pod is behaving incorrectly? Maybe it's in the faulty state or something
17:05
So the service is going to know that it won't send. it won't send the traffic to that specific unhealthy pod
17:15
Okay. And as I mentioned, services know about those backing pods and it's using
17:27
or the way that it does that is by using selectors. And as I mentioned before
17:36
annotation selectors and labels is actually the next session in this series, okay
17:41
So what's happening with a service is the following. So let's imagine that we have three pods in a cluster, okay
17:52
And of course, each pod is running a container image. Of course, it's running an application
18:00
each pod has an IP address assigned to that pod, okay? So in this case, I don't know, 100, 101, and 102
18:13
and actually it doesn't matter. It depends on the networking configuration of your cluster
18:19
However, let's say that we have these three pods, okay? And you specify in your application or maybe, I don't know, maybe in a web service or something that you want to communicate to a pod, okay
18:36
And actually, it doesn't make sense because that specific IP address of that pod could change over time, okay
18:47
So your service or application is not going to find the pod, right
18:56
Because the IP address changed. So that's why we deploy services. We use this primitive objects in a Kubernetes cluster to communicate to pods indirectly, right
19:10
From our applications or services or even other pods. and the service will know those pods
19:20
It'll know the pool of pods that are available for communicating to
19:27
So in this case, if a pod changes its IP address, it actually doesn't matter because the service will know
19:37
that this new pod has the same label as its selector. So it'll know that it can communicate
19:47
to this newly created pod, this freshly created pod. So your application or service
19:56
it doesn't matter, right? It doesn't matter for your application because you will be able to use the service
20:02
and the service will know about those pods. As I mentioned before
20:09
services are based on a selector. And actually that's similar to deployments
20:17
in a sense that deployments use selectors as well. And of course, again
20:25
this is not the right time for speaking about selectors that comes in the next session
20:32
However, let's do this. this kubectl describe, I have a describe deployment somewhere, right
20:41
Describe deployment, blah, blah, blah. This is the one that I was looking for
20:47
And if we describe this deployment, you can see in the selector field
20:53
that all the pods that have the app label with that my deployment value
21:04
they're going to be the backing pods for this specific deployment. That's how a deployment can relate to pods
21:13
And this is the same with services. You use labels and selectors for specifying that
21:22
So let's create a new folder here. Session 5, session 5, city session 5
21:31
And now let's create a new service. Okay. So let's create a new file here, myservice.jml
21:46
And of course I can use the service snippet that I have installed
21:56
And here, as you can see, again, API version, kind, metadata, and spec
22:02
those fields that are the same for the majority of objects in a Kubernetes cluster
22:14
So in this case, I'm using API version V1, the kind of object is service
22:21
This is the name of the service. This could be just my service or something Actually you know what let me try it again And let change this to my service
22:35
Okay, my service without the dash, I think it's better. And here in line number seven
22:43
you can see that I can specify the name of the label
22:48
or the label value that I want to use for this selector
22:52
So in this case, my deployment, Because again, remember that this is the label
23:00
that I'm using for the deployment. Of course, those labels could have additional labels
23:07
In this case, I'm just going to use this, okay? My deployment
23:13
I guess there is another command, kubectl get pods and show labels, I guess
23:20
This is valid. and yes, we can see the labels for each pod
23:26
So I could use others such as pod template. It actually doesn't make sense right now
23:33
However, I'm going to use app equals my deployment. And finally you specify the port that you want to use
23:44
as the incoming port, say 80. And finally the target port, this is the port related to your application
23:54
that is running inside the pod. So let's say 80 again, okay
23:59
And this is the hello world of services. Actually I can execute my kubectl apply
24:08
Let's do that. Again here kubectl apply dash F my service. and the service was created
24:20
We can query for those services by using kubectl get service or get services
24:27
As you can see, I have two. The first one was created by Kubernetes itself
24:34
The second one is the service that I just created by using the declarative way
24:40
If I describe this service, kubectl describe my service
24:51
service forward slash my service. We can see the selector value, which is the app equals my deployment
25:02
Again, you can specify some other labels and you actually, you can use a mixture
25:10
of different labels for your selectors. and I'm going to talk about that in the next session
25:18
However, this particular service detected that 10 pods are its backing pods
25:26
So we can see those endpoints values, such as 10, 244, 0, 10, 0, 8, 0, 9, and seven more
25:36
0, 8, 0, 9, and 7 more. Those pods that the service detected
25:46
are the ones that have the app equals my deployment label. Of course, those are the only pods
25:55
that I have in this cluster. However, I could have thousands of other pods
26:01
with other kind of labels. And this service is gonna detect that only 10 pods are its backing pods, right
26:14
Again, let me execute get pods output wide so we can see those IP addresses
26:23
that are actually the same ones as the endpoints field that the kubicotl describe command returned, right
26:34
eight, nine, 10, 11, 12, blah, blah, blah. Actually Kubernetes deployed those pods in different nodes
26:44
because I have this cluster with three nodes and some of those are in node number two
26:52
and some others in node number three, and actually in the control plane node
26:59
because I didn't specify otherwise. So that's why we have those specific IP addresses
27:07
And when I created the service, it detected 10 pods for its backing pods
27:14
So this is great. This is how we can describe pods by using the declarative way
27:20
However, there are other ways such as the imperative way, okay? Before we do that, let me show you the service types again
27:32
cluster IP this is the default when we create a cluster IP service the service
27:40
is only reachable from within the cluster okay so it's not that you're gonna
27:48
create some requests from your web service or application to that cluster IP service because
27:56
it's not being exposed. The one that you're looking for is NodePort
28:02
because this service is reachable from outside the cluster and or Load Balancer
28:11
And that is the one that we use most of the time when we use a cloud provider Kubernetes offering
28:19
such as Azure Kubernetes or AWS or so, right? And there's a fourth one, which is external name
28:28
that allows you to map a service to a DNS name. However, in this session, I'm going to use Cluster IP
28:36
which I did by creating that service in NodePort, okay? So cool, we executed that
28:48
and I created the Cluster IP service by using the YAML file
28:55
However, we can use the kubectl run command for creating both pod and service
29:03
This is great for training purposes or when you're learning about Kubernetes
29:08
or maybe you want to do something very fast, very rapidly. You can use kubectl run
29:15
the name of the pod that you're creating, the image name that you want to use
29:20
and finally the port that you want to use for that particular service
29:25
And then you use the flag expose, okay? So let's do that
29:33
KubeCurl run, blah, blah, blah. Let's execute this. KubeCurl run. It's using Nginx
29:44
And then I'm using the expose flag. So both pod and service were created, right
29:55
If I execute get service, I'm gonna receive services, right? Because I just created the My Nginx service
30:06
Again, this is for, I don't know, for training purposes, or if you want to learn about Kubernetes
30:14
or if you want to do something very fast in your cluster
30:18
you want to create a new pod and then expose it somehow
30:22
you can use this command. And actually this is, if you're pursuing the Kubernetes certifications, this is actually that you should know, okay, how to expose a pod by using a service
30:38
And that's what I did. So cool. And kubectl, get pods. And of course I have 11 pods in this moment because I created this other pod
30:52
However, let's search for the labels, show labels. In this case, this pod doesn't have the app my deployment label, okay
31:07
It just has the run my NGNX. So that's why this pod is not going to be included in the backing pods for the previous service that I created, okay
31:21
What happens if you change the label of this My NGINX pod dynamically
31:29
so you can add the app equals my deployment value? In that sense, in that case, yes, that newly created pod will be included
31:42
as a backing pod for an existing service. So, yeah, you can do that. Okay
31:48
Okay, so let's go back here. And that's one way to create a service in the pod
32:01
the underlying pod. And you know what? Let me show you the selector for the newly created service
32:08
So kubectl describe service and the name was mynginx, my nginx, my nginx
32:23
So in this other case, as you can see, the selector is just run equals my nginx
32:29
That's why this service only has one backing part, one endpoint, okay
32:38
Cool. how can we create a service by using the create service command
32:48
that is included in kubectl. So you execute kubectl create service and then the type of service that you want to use
32:58
in this case, cluster IP or node port or load balancer or whatnot, okay
33:05
And then the name of the service and then some other additional flags such as TCP
33:12
that allows you to create the incoming port and the target port
33:17
In this case is doing something very similar of what we did by using the myService.jml file. Okay
33:27
So let's do this, kubectl create service. So let's go back here, kubectl
33:37
create service and actually it has a hidden character or something. Anyways, I'm gonna type that kubectl create service
33:48
cluster IP, and then the name of the service, my new service or something
33:55
And finally, the ports that you want to use by using the TCP flag, okay
34:04
So TCP equals 8080 or 3080, or it depends on the application that is running in your pods
34:17
this target port. So in this other case, I created this my new service by using create service
34:25
And of course, in this moment, I have four services. We can verify that by executing get service
34:32
and I have this my new service, right? And if I execute this with the wide output
34:41
you can see that my new service has this selector. That is app equals my new service
34:48
That's the default app equals the name of the service. And yeah in this other case what happened In this other case what happened What are the backing pods for this new my new service So let do that kubectl describe service forward slash my new service
35:12
my new service, right? And as you can see, this other service
35:17
it doesn't have any endpoint because there's no pod with app equals my new service label, right
35:28
So it didn't detect a single pod with that label. So there's no endpoint
35:36
There are no endpoints for this service, right? However, this is great for our learning purposes
35:45
you know, creating services by using the create service command. Again, if you're pursuing the Kubernetes certifications, this is something that you should know
35:55
Okay. And finally, what happens if you have an existing deployment that is not being exposed somehow and you want to expose it by using a service
36:10
so you can create the service and you can specify the selector
36:15
or you can just execute expose deployment, which is another kubectl command
36:22
that allows you to expose an existing deployment just by specifying the deployment name
36:30
and the ports, you know, the incoming port and the target port that you want to use
36:38
In this other case, a new service will be created for this
36:44
So let's execute this. Let's go back here to the terminal. So yeah, kubectl expose deployment, the name of the deployment
36:59
Remember that I have a deployment, right? kubectl get deployments. Yeah, my deployment
37:08
So I can do this. And finally, the target port. So in this other case, of course
37:16
the MyDeploymentService already exists. So it's not that I can create a new service
37:24
just by executing this command that I'm showing you. Of course, I can change the name of the service
37:30
However, it's the same result that we have already in place, okay
37:35
So many different ways of creating services. Of course, if you want to specify node port
37:45
for exposing the service outside the cluster, you need to use that type of service, the node port
37:56
Okay. So let's do that. So let's try again with this. So let me see what's going on here
38:09
kubectl get service. I know those services exist. Those are of type cluster IP, right
38:22
So I know that my deployment, I think that's the first one that we created
38:33
Let's describe that. Describe deploy, my deployment. And yes, this is the one that I was looking for
38:48
So endpoints. No, I'm sorry, I'm looking at the deployment, not looking at the service
38:57
So again, describe service, services, my deployment. Yes, this is the one that I was looking for
39:07
So I can create a new pod for communicating to those backing pods
39:17
Can I do that? Well, actually, let's see. Okay, so I know that those 10 pods are running
39:28
and those are the backing pods for this service. And I'm going to use this IP address
39:34
for communicating to that particular pods. So let's try to do this
39:41
Can I run, can I execute something inside the existing pods that I have
39:48
for instance kubectl exec my nginx nginx IET which is the interactive session and then say I don know Shell can I do this Yes I can So I running inside the container
40:15
and running these commands, right? Because I executed the kubectl exec command
40:22
So this is great. Can I execute something? For instance, curl, do I have curl
40:32
Yeah, I have curl. So can I do this? 10, 244, HTTP 10
40:44
Can I do this? 244, 0, 10, 244, 0, 10. port 80, can I do that
40:58
I should be able to do that. Of course I'm trying to use the pod directly
41:04
I want to use the service. That's a mistake that I'm doing right now
41:08
In this container, in this pod, I want to use an existing service
41:14
for communicating to those pods. So the IP address of this service is this one
41:21
So I wonder if I can do this, curl, HTTP, and then the name of that, the number of IP address that I want to use, such as 10, 106, 117, 12
41:38
Can I do this? I should be able to do that, to be honest with you. So I'm trying to use the service to communicate to those backing pods from this existing container
41:48
Somehow I'm missing something here. However, because I wanted to show you that those services are cluster IP
41:56
Those services are being used internally in the cluster. You know what
42:02
Maybe this is not the IP address that I need to use
42:07
Maybe I need another IP address that I'm not seeing right now
42:11
Anyways, that's the cluster IP. This service is only available inside the cluster
42:18
Let's close this down. and let's return to the terminal because I want to create another
42:25
get another service. I guess I have too many services right now
42:29
that are doing almost the same. And I have a lot of things going on here
42:37
Maybe if I just deleted those services, I could start from scratch
42:43
However, I don't think I will have the time today to do that
42:48
In the next session, and I'm going to start from scratch and we're going to communicate to a service
42:55
to a cluster IP service, okay? That's the one that I'm interested in
43:01
for this particular demo, because I just want to use the service
43:05
to communicate to the backing pods. Anyways, it doesn't matter right now
43:11
So what happens if I want to change the type of an existing service
43:17
Of course you can do that declaratively just by changing the type of the service
43:27
I guess we have the selector and then the app and we should have something like type and no port
43:36
no port, just like that. And let's deploy this again, kubectl apply forward slash and then the service
43:47
And now kubectl get service. And as you can see, my service is the one that is node port
43:59
I know this is, I have my deployment, my new service, my service, my Nginx
44:04
That's why I think I'm confused about those services that I created in this session
44:09
Anyways, I changed the type of this service just by specifying the type field and the type of service that you want to deploy, in this case, node port
44:21
So in this other case, I can communicate to this particular service because I can expose it externally
44:34
That's what I'm doing right now. Okay, it's being exposed with that port
44:41
that is right here. This one, 32,135. That's the number of the port
44:50
that this particular service is using. So let's try this kubectl get nodes
44:59
And so Can I Um So can I get notes
45:12
Can I describe the, I can, I think I can do that
45:16
or something is missing here. So is, okay, let me try this
45:26
Let me execute curl, curl to that particular IP address. And then the name of the port
45:34
I don't think this is going to work because something is missing here. Yeah, I wanted to connect to that service, 200 and 200
45:49
Yeah, I think I'm missing something and I don't have enough time today to fix this
45:57
Anyways, something is missing here. Maybe I could just execute another command
46:01
that I will show you in the next session, which is kubectl proxy or to create this tunnel
46:09
to create this access to that particular service. Minikube has this other command
46:16
which is, is that minikube exposed? Minikube? Let me see if I remember
46:25
Minikube help. Is that exposed? Mount. No, no, no, no. Service tunnel
46:34
Is that it? No, service. Service. That's the one that I'm looking for. Minikube service
46:40
And then the name of the service. Is that it? I think this is going to work
46:49
Yeah. it actually did the trick for me. And now we can see here that I'm returning this
46:59
but somehow my ports are not being mapped correctly. That's actually what I did before trying to execute a request
47:07
to 172.17, blah, blah, blah. And then the name of the port
47:13
Yeah, something's missing. I have something in my hand since I began this session
47:19
I think spider bite beat me. Anyways, I'm going to show you this in the next session
47:29
where we're going to see selectors because that's paramount to understand services
47:34
and deployments. Both services and deployments are based on selectors and labels
47:43
So with that topic, we're going to close the deployment and services topics down, okay
47:52
Those things are missing here to understand, to fully understand what's going on
47:58
That's why we're going to see selectors and labels and of course annotations in the following session
48:03
And by then I think I could be able to create a request
48:08
to my own cluster because something's missing here and I'm not seeing what
48:14
And yeah, I'm going to show you the load balancer option as well
48:19
Okay. I created beforehand this Kubernetes cluster in my own Azure portal
48:28
I created this and I want to create a service. Of course, I need to deploy some applications
48:34
to that cluster and then create a service that is of type load balancer
48:41
So we can see that Kubernetes is going to communicate to Azure to create a new load balancer with all the rules and all that jazz to communicate
48:52
to those pods, to those backing pods, okay? And that's one of the things that I'm going to show you in the next session
49:01
So, two types of services, Cluster IP and NodePort. There are more, of course
49:08
I just showed you or talked about two. Cluster IP, that's the one that you can use within the cluster
49:16
And node port is the one that you want to use if you want to expose the service outside the cluster
49:22
We have others such as load balancer and external name. However, yeah, I think we can see load balancer as well
49:31
in the following session and external name. I don't think we will have the time to use it in this series
49:41
And yeah, that's what I got for today. Labels, selectors and annotations
49:49
is the topic of the next session, okay? So thank you for your time and see you next week