WEBVTT

00:00.180 --> 00:03.020
-In this lecture, we will discuss
about imperative

00:03.020 --> 00:05.230
and declarative approaches in
Kubernetes.

00:05.690 --> 00:07.110
Towards the end of this
lecture,

00:07.360 --> 00:10.570
we will talk about some tips
that you can use in the exam.

00:11.780 --> 00:15.030
So far, we have seen different
ways of creating

00:15.160 --> 00:17.580
and managing objects in
Kubernetes.

00:19.330 --> 00:22.210
We created objects directly
by running commands,

00:22.370 --> 00:24.920
as well as using object
configuration files.

00:25.790 --> 00:27.960
Now, in the infrastructure
as CodeWorld,

00:28.460 --> 00:31.880
there are different approaches
in managing the infrastructure.

00:32.300 --> 00:36.890
They are classified into imperative
and declarative approaches.

00:39.640 --> 00:42.350
Let's understand these with
an analogy.

00:42.850 --> 00:46.440
Let's say you want to visit
a friend's house located at street B.

00:47.060 --> 00:51.820
In the past, you would hire a taxi
and give step-by-step instructions

00:52.070 --> 00:54.740
to the driver on how to
reach the destination,

00:55.030 --> 00:58.990
like take right to street B,
then take left to go to street C.

00:59.280 --> 01:02.160
Then take another left and
then right to go to street D

01:02.330 --> 01:03.450
and stop at the house.

01:04.120 --> 01:08.210
Specifying what to do and
how to do more importantly

01:08.540 --> 01:10.590
is the imperative approach.

01:11.340 --> 01:14.130
On the other hand, today
when you book a cab,

01:14.130 --> 01:18.390
say through Uber, you just
specify the final destination,

01:18.510 --> 01:20.010
like drive to Tom's house.

01:20.640 --> 01:23.100
This is the declarative
approach.

01:23.100 --> 01:26.190
In this case, we're not giving
step-by-step instructions.

01:26.190 --> 01:29.690
Instead, we're just specifying
the final destination.

01:29.900 --> 01:33.530
We're declaring the final
destination and the system

01:33.530 --> 01:36.740
figures out the right path
to reach the destination.

01:37.160 --> 01:39.870
Specifying what to do,
not how to do,

01:39.870 --> 01:41.530
is the declarative approach.

01:42.910 --> 01:45.210
What's that got to do with
what we are learning?

01:45.870 --> 01:47.960
In the infrastructure
as CodeWorld,

01:47.960 --> 01:51.670
an example of an imperative
approach of provisioning

01:51.670 --> 01:55.720
infrastructure would be a set
of instructions written step-by-step,

01:55.920 --> 01:58.840
such as provisioning a VM,
named a "web-server,"

01:59.220 --> 02:01.260
installing the NGINX
software on it.

02:01.680 --> 02:04.720
Editing configuration file
to use port 8080,

02:05.140 --> 02:07.230
and setting the path to
web files.

02:07.640 --> 02:10.270
Downloading source code of
repositories from Git,

02:10.980 --> 02:13.190
and finally starting the
NGINX server.

02:13.900 --> 02:19.740
Here, we're saying what is required
and also how to get things done.

02:20.410 --> 02:24.080
In the declarative approach,
we declare our requirements.

02:24.240 --> 02:27.000
For instance, all we say is
that we need a VM

02:27.000 --> 02:30.000
by the name "web-server,"
with the NGINX software on it,

02:30.380 --> 02:34.250
with a port set to 8080 and
the path to the web files defined,

02:34.460 --> 02:38.170
and where the source code
of the application is stored.

02:38.550 --> 02:42.050
Everything that's needed to be
done to get this infrastructure

02:42.350 --> 02:45.970
in place is done by the system
or the software.

02:46.390 --> 02:49.140
You don't have to provide
step-by-step instructions.

02:49.810 --> 02:53.020
Orchestration tools like
Ansible, Puppet, or Chef,

02:53.520 --> 02:55.780
or Terraform fall into this
category.

02:56.820 --> 02:59.030
In the imperative approach,
what happens

02:59.030 --> 03:02.410
if the first time only half
of the steps were executed?

03:02.740 --> 03:05.080
What happens if you provide
the same set of instructions

03:05.080 --> 03:07.330
again to complete the
remaining steps?

03:07.910 --> 03:10.040
To handle such situations,
there will be

03:10.290 --> 03:14.420
many additional steps involved,
such as checks to see if something

03:14.420 --> 03:18.590
already exists and taking an action
based on the results of that check.

03:19.090 --> 03:21.340
For instance, while provisioning
a VM,

03:21.630 --> 03:23.800
what would happen if
a VM by the name

03:23.800 --> 03:25.510
"web-server" already exists?

03:26.140 --> 03:29.270
The same goes with creating
a database or important data

03:29.810 --> 03:33.560
should it fail or should it continue
since the VM is already there.

03:34.150 --> 03:37.190
What if we decide to upgrade
the version of software

03:37.440 --> 03:39.780
to say, NGINX 1.18 in the
future?

03:40.450 --> 03:44.160
It should be as simple as
updating the version of NGINX

03:44.160 --> 03:45.490
in the configuration file,

03:45.740 --> 03:48.660
and the system should
take care of the rest.

03:49.580 --> 03:53.500
Ideally, the system should be
intelligent enough to know what has

03:53.500 --> 03:57.050
already been done and apply
the necessary changes only.

03:57.500 --> 04:00.260
That's the declarative
way of doing things.

04:01.930 --> 04:05.220
In the Kubernetes world,
the imperative way of managing

04:05.220 --> 04:08.430
infrastructure is using
commands like the "kubectl run"

04:08.430 --> 04:11.730
command to create a pod,
the "kubectl create deployment"

04:11.730 --> 04:15.190
command to create a deployment,
the "kubectl expose" command

04:15.190 --> 04:17.400
to create a service to expose
a deployment,

04:17.900 --> 04:19.820
and the "kubectl edit" command

04:19.980 --> 04:22.150
may be used to edit
an existing object.

04:22.150 --> 04:24.110
For scaling a deployment
or replica set,

04:24.110 --> 04:25.780
use the "kubectl scale" command.

04:26.240 --> 04:28.700
Updating the image on
a deployment,

04:28.700 --> 04:31.250
we use the "kubectl set image"
command.

04:32.040 --> 04:35.710
Now, we have also used object
configuration files to manage objects

04:35.830 --> 04:41.210
such as creating an object using
the "kubectl create -f" command,

04:41.510 --> 04:44.680
with the f option to specify
the object configuration file,

04:44.930 --> 04:48.100
and editing an object using the
"kubectl replace" command,

04:48.180 --> 04:52.020
and deleting an object using the
"kubectl delete" command.

04:52.560 --> 04:55.100
All of these are imperative
approaches

04:55.400 --> 04:57.610
to managing objects in
Kubernetes.

04:58.190 --> 05:02.490
We're saying exactly how to bring
the infrastructure to our needs

05:02.490 --> 05:05.490
by creating, updating,
or deleting objects.

05:06.200 --> 05:09.660
The declarative approach
would be to create a set of files

05:09.660 --> 05:12.950
that defines the expected
state of the applications

05:12.950 --> 05:14.910
and services on
a Kubernetes cluster.

05:15.420 --> 05:18.080
With a single "kubectl apply"
command,

05:18.420 --> 05:22.460
Kubernetes should be able
to read the configuration files

05:22.460 --> 05:25.260
and decide by itself what
needs to be done

05:25.590 --> 05:28.390
to bring the infrastructure
to the expected state.

05:28.850 --> 05:32.520
In the declarative approach,
you will run the "kubectl apply"

05:32.520 --> 05:36.140
command for creating, updating,
or deleting an object.

05:36.690 --> 05:39.810
The apply command will
look at the existing configuration

05:40.110 --> 05:44.030
and figure out what changes
need to be made to the system.

05:45.400 --> 05:47.450
Let's look at these in
a bit more detail.

05:48.200 --> 05:50.990
Within the imperative
approach, there are two ways.

05:51.450 --> 05:55.620
The first is using imperative
commands such as the run,

05:55.620 --> 05:59.210
create or expose commands
to create new objects and the edit,

05:59.210 --> 06:02.380
scale, and set commands to
update existing objects.

06:02.800 --> 06:06.510
These commands help in quickly
creating or modifying objects,

06:06.510 --> 06:09.140
as we don't have to deal with
YAML files.

06:09.470 --> 06:12.390
These are helpful during the
certification exams.

06:12.810 --> 06:15.980
However, they're limited in
functionality and will require

06:15.980 --> 06:19.230
forming long and complex
commands for advanced use cases,

06:19.600 --> 06:22.270
such as creating a multi-container
pod or deployment.

06:22.900 --> 06:26.070
Secondly, these commands
are run once and forgotten.

06:26.190 --> 06:28.990
They're only available in the
session history of the user

06:28.990 --> 06:32.330
who ran these commands,
so it's hard for another person

06:32.330 --> 06:35.200
to figure out how these
objects were created.

06:35.410 --> 06:37.040
It is hard to keep track of.

06:37.540 --> 06:39.790
It's difficult to work with
these commands

06:39.790 --> 06:41.710
in large or complex
environments.

06:42.340 --> 06:44.300
That's where managing
objects

06:44.300 --> 06:47.380
with the object configuration
files can help.

06:48.470 --> 06:52.140
Creating object definition files,
or configuration files

06:52.140 --> 06:54.810
or manifest files as
it's also called,

06:55.140 --> 06:59.100
can help us write down exactly
what we need the object

06:59.100 --> 07:00.810
to look like in a YAML
format,

07:01.020 --> 07:04.190
and use the "kubectl create"
command to create the object.

07:04.820 --> 07:07.740
We now have the YAML file
with us always

07:07.940 --> 07:11.110
and it can be saved in
a code repository like Git.

07:11.660 --> 07:14.410
We can put together a change
review and approval process

07:14.410 --> 07:18.290
around these files so that
a change made is reviewed

07:18.290 --> 07:22.210
and approved before it is applied
to a production environment.

07:22.790 --> 07:25.670
In the future, if a change is to be
made, for instance,

07:25.670 --> 07:28.210
editing the image name
to another version,

07:28.670 --> 07:30.630
there are different ways
to go about it.

07:31.050 --> 07:34.300
One way is to use the
"kubectl edit" command

07:34.300 --> 07:36.260
and specify the object name.

07:36.890 --> 07:41.020
When this command is run,
it opens a YAML definition

07:41.020 --> 07:44.900
file similar to the one you
used to create the object,

07:45.190 --> 07:48.650
but with some additional
fields such as the status fields

07:48.650 --> 07:52.110
that you see here, which are
used to store the status of the pod.

07:52.860 --> 07:56.780
This is not the file you used
to create the object.

07:57.200 --> 08:01.920
This is a similar pod definition file
within the Kubernetes memory.

08:02.040 --> 08:05.170
You can make changes to this file
and save and quit,

08:05.170 --> 08:08.960
and those changes will be
applied to the live object.

08:09.420 --> 08:14.140
However, note that there is
a difference between the live object

08:14.220 --> 08:16.350
and the definition file that
you have locally.

08:16.760 --> 08:19.470
The change you made using
the "kubectl edit" command

08:19.470 --> 08:21.480
is not really recorded
anywhere.

08:21.980 --> 08:25.520
After the change is applied,
you're only left with your local

08:25.520 --> 08:30.740
definition file which, in fact,
has the old image name in it.

08:31.860 --> 08:35.820
In the future, say, you or
a teammate decide to make a change

08:35.820 --> 08:38.370
to this object unaware
that a change

08:38.370 --> 08:40.790
was made using the
"kubectl edit" command.

08:41.370 --> 08:42.790
When the new change
is applied,

08:43.080 --> 08:45.120
the previous change to
the image is lost.

08:45.630 --> 08:49.000
You can use the "kubectl edit"
command if you are making

08:49.000 --> 08:52.090
a change and you're sure
that you're not going to rely on

08:52.090 --> 08:54.590
the object configuration
file in the future.

08:55.390 --> 08:59.260
A better approach to that
is to first edit the local version

08:59.260 --> 09:02.640
of the object configuration
file with the required changes,

09:03.060 --> 09:06.100
that is by updating the image
name here,

09:06.600 --> 09:09.070
and then running
the "kubectl replace"

09:09.070 --> 09:11.230
command to update
the object.

09:12.360 --> 09:16.320
This way, going forward,
the changes made are recorded

09:16.780 --> 09:19.660
and can be tracked as part of
the change review process.

09:20.620 --> 09:23.750
At times, you may want
to completely delete

09:23.750 --> 09:24.960
and recreate objects.

09:25.080 --> 09:27.170
In such cases, you may run
the same command,

09:27.580 --> 09:30.550
but with the force option
like this.

09:32.550 --> 09:36.630
This is still the imperative approach
because you're still instructing

09:36.630 --> 09:39.890
Kubernetes how to create
or update these objects.

09:40.180 --> 09:43.140
First, you run the "kubectl create"
command to create the object,

09:43.220 --> 09:45.980
and then you run the replace
command to replace the object,

09:45.980 --> 09:47.980
or delete command to delete
the object.

09:48.690 --> 09:52.440
What if you run the create
command if the object already exists?

09:52.730 --> 09:56.780
Then it would fail with an error
that says the pod already exists.

09:57.320 --> 09:59.620
When you update an object,
you should always make sure

09:59.620 --> 10:03.080
that the object exists first before
running the replace command.

10:03.620 --> 10:06.040
If an object does not exist,
the replace command

10:06.290 --> 10:07.710
fails with an error message.

10:08.460 --> 10:13.250
The imperative approach is very
taxing for you as an administrator

10:13.250 --> 10:17.180
as you must always be aware
of the current configurations

10:17.180 --> 10:19.300
and perform checks
to make sure that

10:19.300 --> 10:22.640
things are in place
before making a change.

10:24.220 --> 10:27.850
The declarative approach is
where you use the same object

10:27.850 --> 10:30.480
configuration files that
we've been working on,

10:30.900 --> 10:33.780
but instead of the create
or replace commands,

10:33.780 --> 10:36.950
we use the "kubectl apply"
command to manage objects.

10:37.610 --> 10:40.370
The "kubectl apply" command
is intelligent enough

10:40.570 --> 10:43.990
to create an object if it
doesn't already exist.

10:44.490 --> 10:46.830
If there are multiple object
configuration files,

10:46.960 --> 10:49.750
as you would usually,
then you may specify

10:49.750 --> 10:52.880
a directory as the path
instead of a single file.

10:53.250 --> 10:56.420
That way, all the objects
are created at once.

10:57.010 --> 11:01.090
When changes are to be made,
we simply update the object

11:01.090 --> 11:04.640
configuration file and run the
"kubectl apply" command again.

11:04.930 --> 11:07.850
This time, it knows that
the object exists

11:08.020 --> 11:11.650
and so it only updates the
object with the new changes.

11:12.270 --> 11:15.530
It never really throws
an error that says the object

11:15.530 --> 11:18.860
already exists or the updates
cannot be applied.

11:19.320 --> 11:22.910
It will always figure out the right
approach to updating the object.

11:23.490 --> 11:26.120
Going forward, any changes made
on the application,

11:26.120 --> 11:28.960
whether they are updating
images

11:28.960 --> 11:31.250
or fields of existing
configuration files,

11:31.250 --> 11:35.000
or adding new configuration
files all together for new objects,

11:35.630 --> 11:39.550
all we do is simply update our
local directory with the changes

11:39.840 --> 11:42.550
and then the "kubectl apply"
command takes care of the rest.

11:43.340 --> 11:47.270
We will discuss more about how
the "kubectl apply" command

11:47.520 --> 11:51.020
works exactly in the back end
in the next lecture.

11:51.730 --> 11:56.270
For now, let me give you some
tips as part of the exam.

11:56.770 --> 12:00.150
From an exam perspective,
you could use the imperative

12:00.360 --> 12:02.990
approach to save time
as much as possible.

12:04.240 --> 12:06.580
For example, if the question
is to just create a pod

12:06.580 --> 12:08.240
or a deployment with
a given image,

12:08.620 --> 12:11.160
then one of these imperative
commands can help you

12:11.160 --> 12:13.080
achieve that quickly,
so it's important

12:13.080 --> 12:14.830
to practice the imperative commands.

12:15.250 --> 12:17.840
If you need to edit
a property of an existing object,

12:17.840 --> 12:20.300
then using the "kubectl edit"
command

12:21.170 --> 12:22.340
may be the quickest way.

12:23.680 --> 12:27.260
If you have a complex requirement,
say for example,

12:27.260 --> 12:30.390
that requires multiple containers,
environment variables,

12:30.390 --> 12:32.440
commands,
index containers et cetera,

12:32.770 --> 12:34.850
then using an object
configuration file

12:35.150 --> 12:36.980
to create the object
would be preferred.

12:37.360 --> 12:40.490
This way if you see that
you made a mistake,

12:40.990 --> 12:43.740
you can easily update the file
and apply it again

12:43.990 --> 12:46.200
and using the "kubectl apply"
command,

12:46.200 --> 12:49.240
in that case,
would be a better option.

12:49.240 --> 12:52.080
For more details on the different
approaches to managing

12:52.080 --> 12:55.210
a Kubernetes cluster,
get yourself familiarized

12:55.210 --> 12:57.540
with the Kubernetes
documentation pages

12:57.960 --> 13:00.300
and in the upcoming
lab exercises,

13:00.300 --> 13:02.880
try to use imperative
approach in solving questions.

13:03.840 --> 13:05.760
I will see you in the
next lecture.

