Multi-Stage builds allow you
to have one Dockerfile,
that define multiple build steps
or setup steps, so called "stages" inside of that file.
Stages can copy results from each other,
so we can have one stage to create the optimized files
and another stage to serve them.
We can either build the entire Dockerfile
going through all stages, step by step from top to bottom
or we select individual stages up to which we wanna build,
skipping all stages that would come after them
that are multi-stage builds and multi-stage Dockerfiles.
That's exactly what we can use here.
You can think of this build step here as our first stage.
We grab and install our dependencies,
we grab our source code
and then we build that source code
with the code and the dependencies.
After this step here, we have our finished source code.
Now, with multistage builds,
we just have to use "RUN" instead of command
and therefore just "NPM run build" like this.
Then we can continue with more steps thereafter.
Now, we could do it as in a regular Dockerfile as well.
But now here's the special thing,
I wanna switch to a different base image after this command,
Once we have these optimized files.
Because we only need node to build the optimized files,
because we need the NPM command,
which ships together with node
and because the build script actually also will use node
under the hood.
But once we get these optimized files,
we don't need node anymore,
unless we wanna bring our own node server
to serve these files.
But we don't need to do that,
we could use nginx for that,
which is a very lightweight and slim web server,
which would totally serve our purposes here.
I wanna switch to a different base image
after this run command here.
We can do this.
We can have a second FROM statement here,
effectively turning this
into a multi-stage build Dockerfile.
Because typically, you must only have one base image
but you can indeed switch.
In that case, this previous step you could say,
will be discarded and you will switch to a new base image.
Here, I wanna switch to nginx
and there used the latest stable-alpine version,
which again, is a very slim nginx version.
You can look up these available tags
on the nginx Docker Hub page, of course.
Now here, we wanna do something special though,
we don't want to have our previous changes be discarded.
Instead, we wanna use the optimized files and serve them.
Therefore, you can add a special instruction you could say,
after this FROM instruction here,
after every FROM instruction deed.
You can add the AS keyword
and give this any name of your choice,
like for example build.
But again, this name is up to you.
Then here in our second stage,
so on the second base image in this Dockerfile,
we can copy from this first stage
by using the special "dash dash from" option here
on the copy instruction.
Then you add an equal sign,
and then the stage name from what you wanna copy,
In this case, build.
That means we'll copy the final content
from that build stage.
From all the instructions up to the next FROM instruction
into this new stage here.
Now of course, I don't wanna copy everything
which is in this file system.
After FROM build,
you still need to specify the source path,
just as you did it on copy before as well.
Here we also copied from the local file system
to the working directory inside of the container.
Now here with the FROM option,
we're telling Docker that this copy
will not refer to our local host project folder,
but instead queue the file system from this build stage.
Now here, we can specify a path
in that first build stage file system.
I wanna use "app/build" because...
That's something you need to know.
The built command here in a React project
will create a build folder
which holds these final servable files.
Therefore here I copied from the build stage,
and data from the app/build folder.
Now I wanna copy into the user.
So usr/share/nginx/html folder
here in my nginx image.
That's a special folder for this nginx image.
It is the default folder
from where nginx will try to serve files.
These are always things you can look up
on your official image pages.
They are you, for example,
find that if you wanna host some simple static content,
which is exactly what we have here,
we just have an HTML file and a bunch of JavaScript files,
which is static content.
We just have to host the content
of this user share nginx HTML folder.
Therefore, that's the folder to which we copy here.
Now we're using this multi-stage feature
to copy the result from the first stage
into this new stage.
Now, the final container
will only include that second stage,
but it will first of all build this first stage
to derive this final stage.
You could also have more than one stage
in one at the same Dockerfile.
If you need that you can have as many stages
as you want.
For example, an extra stage for running your tests.
Here however,
I'm happy with copying this.
But now I wanna expose Port 80
because that is the default port nginx exposes internally
so I wanna expose it here as well in my own image,
which is based on nginx.
Then the final command here at the end
is that I wanna start ngnix.
I want to start this nginx server
and ask the official documentation states
if we run it ourselves,
we should add this "dash g, daemon off;" option here.
Therefore, let's do that.
Let's add "dash g, daemon off;"
and don't forget this semicolon at the end.
This is how we should start nginx if we started ourselves,
which we wanna do here,
since we wanna start it after we copied in our custom code.
This is now a brand new feature of course.
We haven't seen multi-stage Dockerfiles before,
but it's a powerful feature,
because it's great for situations like this,
where we have an application which we can serve
the way it is,
but which we need to build first.
Then a multi-stage build is of course awesome.
Now, that we've learned about that
and now that we've build our multi-stage Dockerfile,
let's see how we would deploy that.