All right, so before anything, what I want to do is delete everything from my Docker cache.
So Docker system prune a.
You can also go into your Docker dashboard and press the X button on whatever you need to delete.
Anyways I will remove all stopped containers, all images, all builds that we did so far.
I guess I didn't have anything on my system.
All right, so um, here is the next app that we need to containerize.
Here we have the developer, captain DevOps, telling us there are a couple of ways to get this flask
app up and running.
First we install the dependencies with pip install requirements.txt.
So there's one thing I want to make note of.
Make note of is that this is the first app that we're running that requires dependencies in order to
run.
Uh, the apps before it did not need dependencies.
They were very simple.
Hello World apps.
So this is going to change our Docker workflow just a little bit.
Because what I want to do is create an image that already contains all the dependencies.
So we're going to run this command during the image building phase.
And then after we have an image that includes the source code, the dependencies that the code relies
on in order to compile, and the pre-configured Python environment.
In this case this is a Python application.
Then we're going to provide the container with the commands that it needs to execute the Python application.
So we can provide the container with two separate commands um with two different commands.
One way of running this Python app, according to our dear developer myself in this case.
But let's just pretend I'm captain DevOps or captain DevOps and we're on this DevOps journey.
There are two ways to run this Python app after you've installed the dependencies.
Python app.py or another way of running a flask app is to set a flask app shell environment variable
equal to the entry point of your web application so that when Flask Run is invoked, it knows to start
running the flask app from this file.
Okay, so let's just go ahead and start setting up the Docker file.
It's going to make a lot of sense once we do that.
So from base image is typically the first thing that we run.
Because we need to pull a Python environment that our app relies on in order to compile.
So Python 3.8 slim is the image that I want to pull from Docker Hub.
And then we want to set the directory where we want this application to run once we create a container.
So workdir the application is going to run inside the directory slash app.
All right.
Then we need to copy the project files into the working directory.
What I want to do at first is just say copy the source where it's going to set equal to dot the destination
equal to dot as well.
So dot in the first argument means current directory.
So the directory that the docker file is in is oh five starter code.
So it's just going to copy everything inside of the current directory into the destination which is
another dot the dot.
And the second argument means working directory.
So copy everything in the current directory into the working directory of the container.
All right then we're going to install the dependencies.
So the run command executes a command during the image building phase.
I want to distinguish between the run command and the CMD command.
So the run command executes a command while we're building the image, and any dependencies that we
install in this step will be inside of the image itself.
CMD provides the container with the commands that it needs to run the application.
All right.
So this, uh, this command is executed after we create a container.
This command is executed as we're building the image.
So the dependencies that we install here, using whatever command is given to us by the developer,
they're telling us how they normally install the dependencies on bare metal.
We can just copy it over here.
Okay.
So all the dependencies listed in requirements.txt are going to be installed inside of the image itself.
The image will have the source code.
It's going to have all the dependencies that the code needs in order to run, and it's going to have
the preconfigured Python environment.
All the container has to do is run the application with whatever command we give it.
All right.
Now there's one thing I want to make note of.
We're copying everything in the current directory into the working directory.
So the working directory is going to have the folder flask demo.
And inside of it requirements.txt which means the run command is not going to be able to recognize the
requirements.txt.
Because what it's going to see is flask demo and inside of it is requirements.txt.
So now at this point we need to document and inform the developer that the application will use port
5000 of the container.
So the application itself is going to be serving requests on port 5000.
But because we're going to be running it inside of a container, we need to make sure to tell the developer
that so that they can map the container port to some port on the machine so that it can be accessed
from the outside world.
So don't be confused by this command.
It doesn't actually expose port 5000, it simply documents and informs whoever is creating a container
out of this.
Um, whoever is running this application inside of a container to map the correct port to a host machine
port.
All right.
And now here we can define the command to run when the container starts.
So, um, the command that we're going to give the container for now is Python app.py.
All right.
I want to make note of the fact that we kind of have a command that runs during the image building phase,
and a command that is given to a container after that container is created.
We should be.
Oh, no.
I made a mistake.
So remember that I said that after step two, everything happens inside the working directory.
So when the container runs Python app.py, it's not going to see App.py.
It's going to see flask demo because that's what we copied over into the working directory.
And inside a flask demo is app.py.
Okay.
Now we're going to say docker build dash t I'm going to call the image flask demo.
Oh sorry guys we need to CD into the appropriate directory.
So CD oh five starter code clear.
Then I'm going to docker build t Tea flask demo.
That's what we're that's what we're going to name our image.
And we're going to build an image out of the Docker file inside the current directory.
Oh five starter code.
Okay.
Fingers crossed everything works smoothly.
All right.
So here we have the first layer of our application pulling.
Let me just wait till it's done.
All right.
So here we have the first layer of of our application pulling the Python environment from Docker Hub.
So our overall image includes the base image Python 3.8 slim.
Which means we now have access to a Python environment that we can use to run the app, while the container
will have access to a Python environment.
Now then we have work dir app.
Here we're just setting up the working directory, the working directory where all the magic is going
to happen.
Then we copy literally everything inside the current directory that the Docker file is in.
Into the working directory.
So the working directory is going to actually include the docker file and the flask demo project.
I know what you're thinking.
We should have a docker ignore so that the docker file doesn't go into it, but whatever.
Then the fourth step.
Remember, everything here is happening during the image building phase.
We haven't actually created any containers yet.
The fourth step happens while we're building the image.
So that is going to install all the dependencies inside of requirements.txt into the image itself.
So when the container runs the application, it's going to have access to all the dependencies that
the application needs in order to compile.
It's going to have access to the preconfigured Python environment, and it's going to be able to successfully
run the app using the command that we give it.
So now if I do docker build, not docker build docker run to create a Docker container out of the image
that we just created, the image that includes the source code, the environment and the dependencies.
Everything should work smoothly, but as always, we're going to name the container flask demo container.
And what we're going to do is set it to automatically remove when we're done with it.
And there's one more thing.
So looking at the expose command, this tells me that the application, without looking at the code
itself, me, the person running the container, I can tell the app is using port 5000 of the container
to serve requests, while the containers network is completely isolated from my host machine's network.
So if I say localhost 5000, it's not going to work.
So what I need to do is map.
We're going to do a port mapping.
So I usually like to do this right after a docker run we're going to say dash P for port mapping.
We're going to map the container port that the application is serving requests on to a host machine
port.
In the interest of, I don't know, messing around.
We'll make it port 8080.
So when I say localhost 8080, it's going to map to the container port 5000 where the where the application
is going to be serving requests.
All right.
Let's do it.
Now let me go to localhost 8080.
And I might have been playing around with this before the demo started, but everything works beautifully.
Okay.
Now, um, before we continue, let's start up an interactive shell.
So over here.
Sorry.
We'll.
After the port mapping, we'll say dash it And then we're going to start up an interactive shell.
So slash bin slash shell.
Beautiful LZ.
Here we see the Docker file and flask demo.
Now I know what you're thinking.
We could have a docker ignore that removes the docker file, or it ignores the docker file when copying
the project files into the working directory.
But I want to do something else.
What I want to do is instead of copying everything into the working directory, I want to copy what's
inside a flask demo into the working directory.
So, um, let's exit.
So I want to copy what's inside of flask demo.
I don't want to include the flask demo folder inside of the work.
Dir.
Just what's inside of the flask demo folder into the working directory, which means the working directory
is only going to see app routes, dot pi, app dot pi and requirements.txt.
So inside the working directory the run command is not going to see flask demo, it's going to see requirements.txt,
which means when the container is running the application inside the working directory it's going to
straight up see app dot pi.
There is no flask demo folder anymore.
Before I let the container run the app on its own, let me start up an oh well, we need to rebuild
the image because we changed the docker file.
Now we have a whole new image.
Okay.
And then I'm going to say then we're going to start up an interactive shell LZ.
Now in the working directory we just straight up have the files themselves.
Um, if the container were to run Python app.py.
It would work beautifully.
We're going to let the container run the command on its own.
So I used control C to exit from this runtime.
And then I can press exit to exit the interactive shell.
And now uh do do do do.
I'm just going to let the container run the app on its own.
Without us having to do it for it.
All right.
Beautiful.
Um, let's just test it out.
You can never be too safe.
All right.
What else did I want to teach you?
Let me just check my clipboard real quick.
Do do do do are really important.
So, um, I want to change up our workflow a little bit just to get you more comfortable.
So here's what I want to do first.
Okay.
Here's the diagram we're going to use.
First.
The only thing I want to copy into the working directory is just the requirements.txt.
Okay.
Then we're going to use the run command to install all the dependencies inside of requirements.txt.
After the dependencies have been installed, they're going to be inside of the Docker image itself.
Then we're going to copy over the remaining source code into the working directory and and do the same
thing.
Document that it's using port 5000.
That's the port that needs to be exposed by the container.
And then provide the container with the commands that it needs to run the app.
So so it's actually good practice to be more purposeful with what you copy over step by step.
That way you have more fine grained control over the image building process and can therefore modify
it as you see fit.
Um, and so once we have that all set up, we're going to create a container.
A container is going to use the command that it was provided to run the application itself, located
inside the Docker image.
So the Docker image has got the application.
It's got the dependencies.
It's got the Python environment.
All the container has to do is say Python app.py.
All right.
It doesn't actually need to say Python app app dot pi, but whatever.
We'll just keep it there for reference.
Okay.
So let's change it up a little bit.
Um, we're just going to copy the requirements.txt file first into the working directory.
Okay.
So flask demo slash requirements.txt.
Then we install The dependencies inside requirements.txt inside the image itself.
Then we're going to copy the remaining project files into the working directory.
Now if you prefer the the the approach of just copying everything over all at once, uh, instead of
splitting it up into different steps, that's fine.
But it's good to be familiar with more than one way of doing something.
So copy.
Uh, then we're just going to copy over everything else into the working directory, and that's it.
We shouldn't see much of a change except from how the image itself is being constructed.
But when you're able to do things like this, it's a testament to how comfortable you are with building
images.
Right.
So now we're pulling the Python image.
We have it local, so it takes zero seconds.
We don't have to pull it from Docker Hub anymore.
Uh, we're setting up the working directory slash app copying over first.
We're only copying over the requirements.txt file at this point.
Um, the image does not know anything about app.py or app routes.
Dot py.
It only has the requirements.txt file.
It installs all the dependencies inside the requirements.txt file, and then it copies over the remaining
files into the working directory only after the dependencies have been installed.
So after the dependencies that the application relies on have been installed, then it copies over the
application.
It's much better practice to do it that way, in my honest opinion.
But some people don't really care.
They just dump everything in there all at once.
And it's totally valid to to be honest.
And then we can just say Docker run.
And, uh, let me just use the container from before.
And everything works beautifully.
Is there anything else I wanted to.
Yes there is.
So our dear developer myself, uh, told us there are two ways to run the app after dependencies have
been installed.
So we can provide the container with this command or with this command flask run.
But this command relies on a an environment shell variable being set first the environment the flask
run command.
When it sees the environment shell variable, it understands what the entry point of the application
is and therefore knows where to start when to run the app.
So different applications that you dockerize quote unquote will have different environment shell variables
that you can actually set directly in your Docker file.
So if I want to set an environment shell variable.
So this was one way to do things.
Let me fix up the numbering 1234567.
So this was one way of doing things.
Another way of running the application is to set an environment shell variable.
So on bare metal you would just say for your terminal you would have just said export flask app equals
app dot pi.
And then when you say flask run it's going to use that shell variable to know how to run the application.
Uh, in this case we can provide the environment shell variable over here.
So we say env.
The key is going to be flask app.
And the value app dot Pi.
So anything that you do bare metal Docker typically provides the flexibility to do inside of its ecosystem
as well.
So we can set the environment variable, the environment shell variable as.
So then we can provide the container with the commands that it needs to run the application.
So now the command is going to be flask run.
The container is going to successfully execute this command upon us giving it the environment shell
variable as well.
All right.
So um, you know how you and I, we would run export flask app within our terminal itself?
Well, now we're providing the container with the means of it doing that itself and then calling flask
run.
Pretty cool if you ask me.
Let me go ahead and rebuild the image.
All right.
Docker run.
Beautiful.
Never hurts to test.
That's super weird.
Um, let me try something out.
All right.
Rebuild the image.
All right.
I suspected as much.
This has more to do with how flask runs its applications.
When you run an application using Flask Run, it automatically binds the server to local host.
If you think about the flask application running inside of a Docker container.
Local host in this case is what it's local to the container itself, which means it's unable to access
anything outside the container even if we do the port mapping.
If we say dash dash host 0.0.0.0, it basically allows the application to be able to accept requests
from any available network interface, in this case from any IP address.
We're trying to access it from the IP address of our local machine, which is why adding this allows
us to bypass the restriction imposed upon us by flask itself.
So under this caveat, I would have preferred to just run it using Python app.py.
But um, just know that this is a typical command that is used to run flask applications.
In this case, we need it to provide the container with a command that allows us to run the application
with unrestricted access.
Now, these little caveats can vary from one application to another.
With a little research and troubleshooting, you should be able to understand what's going on and move
from there.
And it's only through bugs and errors that we are able to grow as developers.
So it's good to explore many different ways of doing something.
In the interest of keeping things simple, I'm going to resort to Python App.py doesn't need an environment
shell variable after we have the dependencies in our image.
After we have the Python environment.
Simply running app.py is a lot simpler in my opinion.
All right, that's it for this lesson.
Uh, now we're going to look into Dockerizing, a spring boot application.
This is another application, but this time running in a maven Java environment.