So an image is closed once we build it,
once these instructions were executed.
That's why we have to rebuild it
if we need to update something in there,
for example, when our code changed,
and we wanna copy the new code into a new image,
that's what we covered in the previous lecture.
Building up on that,
there is another important concept related to images
which you also should be aware of.
They are layer based.
Now what do I mean by that?
With that, I mean that when you build an image,
or when you rebuild it,
only the instructions where something changed,
and all the instructions there after are re-evaluated.
Keep in mind that I changed the code,
and then I rebuilt this image.
We did this in the last lecture.
Now, I did not change the code again since then.
So if I now rebuild this image, again,
by running Docker build dot,
you see this is super fast.
It finished in like a quarter of a second.
It was super fast
because we see all these using cache messages here.
Because Docker basically recognized
that for all these instructions,
the result when the instructions are executed again,
will be the same as before.
We have the same working directory,
the code I copy has not changed at all,
there is no new file,
no file has changed,
and therefore Docker is able to infer
that it doesn't really need to go
through that instruction again.
Instead, whenever you build an image,
Docker caches every instruction result,
and when you then rebuild an image,
it will use these cached results
if there is no need to run an instruction again.
And this is called a layer based architecture.
Every instruction represents a layer in your Dockerfile.
And an image is simply built up from multiple layers
based on these different instructions.
In addition, an image is read only,
which means once an instruction has been executed
and once the image is built,
the image is locked in
and code in there can't change
unless you rebuild the image,
which technically means you create a new image.
That's what I covered before.
But let's come back to these layers.
And images layer based every instruction creates a layer
and these layers are cached.
If you then run a container based on an image,
that container basically adds a new extra layer
on top of the image,
which is that running application that running code,
basically the result of executing the command
which you specified in your Dockerfile.
This adds the final layer which only becomes active
once you run an image as a layer.
All the instructions before that final instruction
are already part of the image though as separate layers.
And when nothing changes,
all these layers can be used from cache.
Now if I do change something in code,
if I add more exclamation marks here or anything else,
no matter what you change,
if I now build this again,
by repeating Docker build dot,
you will see that now it takes longer
because it only uses some results from cache.
It used the work
directory instruction result from the cache,
but it noticed that for the copy instruction,
it needs to run it again.
Because it scans the files which it should copy in,
and Docker detects that one file changed,
and hence it copies in all files again.
Now, here's the thing.
Whenever one layer changes,
I said that all other layers are all rebuilt,
Docker is not able to tell whether npm install
would now yield the same result as before.
After all we copied in our files again,
and Docker does not do a deep analysis
of which file changed where
and if this could affect npm install.
So whenever one layer changed,
all subsequent layers are also re-executed,
which is why here npm install run again.
So I hope this layer based architecture makes sense
and is clear.
It exists to speed up the creation of images
since Docker only rebuilds and re-executes
what needs to be re-executed.
And that's of course, a very useful mechanism.
Now it also means that at the moment,
whenever we change anything in our code,
we also run npm install again,
even though we as a developer note
that this is unnecessary.
Unless we change something in package.json,
which manages the dependencies of our project,
there is no need to run npm install again, ever.
Because if we just changed something in our source code,
this has no impact on the dependencies this project needs,
and therefore in nodes world,
npm install does not need to be re-executed.
And here we have our first
tiny bit of optimization potential for this Dockerfile.
Instead of copying everything like this,
and then running npm install,
it would be better if we would copy
this after npm install,
but before we run npm install,
we also copy the package.json file,
and we copy that into the app folder.
With that, we would pick up this package.json file,
copy that into the app folder,
then run npm install,
and then copy over our other code.
With this, we would ensure that this layer,
the npm install layer comes before we copy our source code.
So in the future,
whenever we change our source code,
these layers in front of the copies source code command
will not be invalidated.
An npm install will not run again,
just because we copied in our source code again.
So now only these layers would run again
and that will be more performant
than running npm install again,
which simply takes a certain amount of time to finish.
I hope this makes sense.
So if I now build this again,
for the first time,
it will run npm install and copy in everything,
but then here we got our image name,
and if we now use that to run our container,
and we reload,
we see this change in source code of course,
which I made before.
But if we now stop this container, first of all,
with Docker stop,
and then go to server js and update the source code again
to remove all the exclamation marks again.
You will notice that if I rebuild the image
with Docker build dot,
it's now again super fast
because it was able
to use the cached result from npm install.
Because the steps prior to npm install didn't change
because Docker sees that the package.json file
did not change it's the same as before,
and therefore there was no need
to copy that again and to run npm install again.
The only change happened in this step,
but that comes after npm install.
So that's the first small optimization,
but more important than that optimization
is that you understand
why we are doing it
and that you understand this layer based approach,
this layer based architecture.
It's really important because
it's a core concept in Docker and Docker images,
and it exists for the reasons outlined in the last minutes.