Docker + ReactJS tutorial: Development to Production workflow + multi-stage builds + docker compose

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

why use React inside a docker? I still can't find any reason why people do that. Can someone give me a good explanation?

👍︎︎ 4 👤︎︎ u/That_Guy_Kev 📅︎︎ Mar 10 2021 🗫︎ replies

Hey guys I made a video on how to run a react app in docker containers and how to build a development to production workflow using multi-stage builds and docker-compose.

00:00 Intro

00:12 Initialize React App

00:46 Building an Image with Dockerfile

12:56 Creating first container

14:20 Docker networking - forwarding ports

19:25 dockerignore files

24:07 Bind mounts to sync src code

35:22 Read only bind mounts

37:44 Environment variables

43:02 Docker Compose

58:15 Multi-stage Build for Production with NGINX

1:11:42 Development vs production workflow

1:22:57 docker build with --target flag

👍︎︎ 3 👤︎︎ u/sloppy_networks 📅︎︎ Mar 09 2021 🗫︎ replies

Great tutorial! What resources did you use to learn this stuff yourself? Was it mainly work experience, reading docs, or just trial/error?

👍︎︎ 2 👤︎︎ u/BrownManPro 📅︎︎ Mar 09 2021 🗫︎ replies

Question: this docker setup allows you to launch the yarn -watch inside the container console and have browser automatic hot reload?

If is not clear (sorry, English isn't my first language), if I use this docker setup in local and I change a row in the code, it will be available on the browser without refreshing it?

This is my biggest problem when I build react+symfony apps using docker.

👍︎︎ 2 👤︎︎ u/VelociGattor 📅︎︎ Mar 10 2021 🗫︎ replies

Thank you, this was interesting to watch.

👍︎︎ 2 👤︎︎ u/Unforgiven-wanda 📅︎︎ Mar 10 2021 🗫︎ replies
Captions
what's going on guys so in this video i'm going to walk you through setting up a development workflow for react apps within docker containers i'm going to show you how you can tweak this workflow for both your development environment as well as your production environment so let's initialize our dummy react app so we're just going to do an npx create react app and we're going to initialize this in our current directory now that that's complete let's start our development server by doing an npm start and doing that should then go ahead and start your react app so we're not going to touch this app it's just merely there for demonstration purposes uh in the next section we're going to work on setting up our first docker container and then working our way into making sure that we can get this app running within that docker container all right guys so for this video i'm going to assume that you already have docker installed on your machine if you don't go ahead and look up the instructions the docker documentation is fantastic so it's fairly easy to set up docker on any operating system but once you have docker setup go ahead and navigate to hub.docker.com so this is a repository of public docker images so pre-built images that we can use for any of our projects we're going to do is we're going to search for node and so you know react runs on top of node so i think it makes sense to look for a pre-built node image and go ahead and select the first result so this is the official node image and this is ultimately what we are going to use for application it's just a lightweight image that already has node inside for us so we don't have to worry about installing it ourselves now this image uh isn't really going to contain anything else other than node it's a bare image just has node installed which isn't obviously going to work for us because you know the idea behind containers is it's going to contain every single thing that we need for application to run and that includes our source code that includes all of our dependencies and anything else our application may need this image doesn't have that so what we need to do is we need to take this image and customize it so we need to take this image we want to copy our source code into this image and we want to install all of our dependencies into this image so that this image can deploy pre-built containers that have everything that we need to actually run our application and to customize an image we have to create a docker file so let's go to our project folder and i'm going to create a new file and call it dockerfile with a capital d uh and so this file is going to contain all of the steps that we need to customize an image and the first command that we need to run is called from so we want to capitalize all the letters and we save from and then we specify a known image so anytime you're customizing an image we have to give it a image that we want to customize so we're going to customize that node image and i know it's called node because of the documentation so if you look at the documentation down here you can specify node and then you can also specify a specific version so right now uh if i just do node um i forget what the default version is if you read the documentation it's going to tell you it's probably like version 14. i think there's a latest tag that one of these is gonna have so maybe it's this one so this could be the latest version but you can also specify a version um right now i'm just going to leave it as a default but if you wanted to specify version 15 you can do that as well the next command is called workdir and so what this does is it's going to set the working directory of our container so i'm going to set this to the slash app directory because i know this directory exists in the container and what this ultimately does is that anytime we run any commands anytime we copy any files it's going to run those commands and copy those files into this directory so we know that anything related to our app is going to be stored in that directory so technically we don't need this command for anything but it makes working with our uh with our container a little bit easier now the next thing that i want to do is i want to take our package.json file and copy it into the image right and then that way once we copy the package.json file we can do an npm install to install all of our dependencies because the package.json has a list of all the dependencies that we need for our project and so we can use the copy command and we can say copy and then the path to the file that we want to copy or the directory we can copy an entire directory but we just want to copy our package.json and since it's uh in the same directory as a docker file i could just type in the name of the file which is package.json um but you know if it was in another directory you could always you know do like slash source slash whatever right so package.json and then we have to specify the directory that we want to copy this file to in our container uh and so what we can do is i can type in dot right so that means the current directory in the container and what this is going to do is this is going to copy it into the slash app directory because we set the working directory to be slash app so anytime we copy files and we just put the dot it's going to put in the slash app we could also just hard code the full path and just type in slash app it'll do the same thing but once again since we have that workdir command we can just specify the dot after we have our package.json copied over the next thing that we want to do is run an npm install so to run a command in our container we need to do a run npm install so that's going to install all of our dependencies uh now the final thing i want to do well it's not the final thing but there's a couple more steps so the next thing that i want to do is now copy the rest of all of our code or the rest of all of our files into our container so we can do a copy and then dot so everything in the current directory and i want to copy it to once again the slash app directory so i just do dot again and now i know you might be a little bit confused uh at what we just did right here because you'll notice that we copy the package.json file and then we do an npm install and then we copy all of our files over right and when we do a copy um with the dot dot that's going to copy all of our files including our package.json so you might be wondering well why did we copy the package.json by itself and the reason for this is a little bit of an optimization technically you didn't need to do it um but docker likes to optimize things and make things quicker the um for future builds so let me explain how docker works when we're creating an image like we are with this docker file each command that we have so we've got five commands right here is a layer of our image so docker builds these images based on these layers so we have five different layers and what docker does is when we build our image for the first time it's gonna cache the result of each layer so layer 1 says hey we need to pull the node image from docker hub and then it caches that result we then set our working directory to slash app and then it caches our result we then copy our package.json it copies the result of that we do an npm install it copies the result of that and then we copy the rest of our files and it um caches the result of that and i want you to think about exactly how our development environment is going to our de our development workflow is going to work uh when we're actually working on our code you know we're going to be in our source directory we're going to be changing our code and we rarely ever touch our package.json file right you know i mean there's going to be times when you're developing where you do install new packages but for the most part you're not installing packages regularly right only when you specifically need it which is uh occasionally and the way that and i mentioned how we have caching so uh the reason why we split this up is that since package.json doesn't change very often it's going to cache this result and then since package.json doesn't change running an npm install doesn't change anything so it's going to catch that result however since we are changing source code right it means that it will have to rerun copy every time we change our code it's going to have to rerun the copy to copy the rest of our files however if we didn't have this these two lines before then and we put let's say we just deleted this and then move the npm install underneath it that would mean that every time we changed our source code it would have no idea when i say it docker would have no idea whether we changed our source code or we changed the dependencies in our packages.json so every time we ran a copy we would have to then do a full npm install regardless of whether or not the dependencies change so we would be unable to take the cache result so by splitting this up we can ensure that uh only when we change our package.json well we have to run an npm install and that is a very time consuming task that's actually going to take the most amount of time when creating our docker images running the empty install and installing all the dependencies so a little bit of an optimization hopefully that made sense but if it didn't you'll see that when we actually go to build our images uh it'll make a little more sense all right so now after we copy all of our code the next thing we want to do is our remember our app listens on port 3000 so we want to expose port 3000 and then finally we need to do an npm start to actually start the development server so we'll type in cmd and we'll say node oops not node uh npm start all right and so this should be everything that we need so let's save that uh and then now let's stop our um our server that's running on our local machine because we're no longer going to be developing on our local machine or our host machine we're going to be developing in our docker container so let's stop that and let's go ahead and build a docker image from this docker file so the way to build a image from a docker file is to do docker build and then the current directory of the docker file so you specify the path to the directory of your docker file so um we're already in react docker yt which is the pro project directory so we can just do a dot which means our current directory and it's going to find a file called dockerfile so let's do that and i want you to pay attention to the output this output is actually very important um if you notice it says step one slash five like i said uh docker uh treats each one of these as a separate layer or as a separate step and you'll see that it caches the result of each one of these things so we pulled a node image uh we copy workdir to slash app you'll see that this says it's cached probably because i ran this before for testing purposes but it's going to run through the other ones and you'll see that the second time that we run this if we change nothing it'll run so much faster because all the results are cached from each one of those five steps but notice how the npm install is what's ultimately taking the longest amount of time all right you can see how long each step actually takes so that was it right it's already over 33 seconds whereas the other two steps took uh what 11 seconds all right so it's complete to verify that our docker image got created we can do a docker image ls uh you can see i've got a ton of images i know that the last one that got created 11 seconds ago is the one i created and you'll notice that there's no name and that's a little bit of a problem so let's go ahead and delete that image uh and then create a new one but let's give it a name this time so i'm going to copy the image id i'm going to do a docker image rm and then paste that id alright so it deleted that image and now what i'm going to do is i'm going to hit the up arrow until i get to that um until i get to the docker build command and this time i'm going to pass in a t flag so this is going to give it a name or a tag in this case and i'm going to call this uh we'll call this react dash image although it looks like i already have an image called react image so let me delete that one first actually i'm just going to overwrite it it's fine so we'll do react image right and notice how the second time we ran it ran much much faster and that's because you can see that steps one sorry steps two three four and five the results were all cached so running it again was much much quicker however if i changed anything from any of these files you'll see that we'll have to rerun them because those results aren't cached and keep in mind you know let's say we change what's in our package.json if we can no longer use the cached entry because this line changed docker has no idea if these lines change as well so anytime a line changes we're gonna have to rerun all the steps after it as well because it has no idea if those changed or not so once again that's ultimately why we moved that package.json line by itself uh to kind of optimize it a little bit more for us so if we do a docker image ls we got our react image and so from that react image let's go ahead and create our first container and so to create our first container i'm going to do a docker run and then we want to specify the container sorry the image name so we have the uh react dash image we'll do react dash image now there's a couple other flags i want to pass in so i'm going to do dash d um that's because you know whenever you do the docker run command by default it's going to attach to the container and i just want it to run in the background and then i also want to give it a name so i'm going to give it a dash dash name i'm going to call this react dash app and let's hit enter all right and let's do a docker ps this is going to show all your running containers you'll see that i have a couple of other containers i've got this react docker react app don't worry about that same with this docker dind the one we want to focus on is the one right here called react dash app okay and so now that it's running let's go ahead and go to localhost 3000 and let's hit refresh and let's see what happens all right so it's spinning and that usually means something's broken and there we go so this is expected uh so in the next section i'm going to explain uh exactly why we can't connect to our react application now that it's running within the docker container okay guys so let's try to figure out exactly what went wrong so if we go back to our source code um you'll see that you know in our docker file we did expose 3000 uh port 3000 so why exactly can we not reach it well the reason is is that this means nothing this command does nothing it's more for documentation purposes but at the end of the day there's no way to actually reach the container at the moment and so that's why going to localhost 3000 means absolutely nothing the container's just kind of sitting by itself in its own isolated network and it's kind of an important thing to understand with docker containers and that is that you know when you create a docker container by default even though the container itself can access the internet and we know it can because it ran an npm install and the only way for that to work is if it had internet access even though it can reach the internet the outside world can't talk to containers by default so when i mean the outside world i don't mean just the internet i'm talking other devices on your home network i'm talking even my host windows machine right my windows machine is considered an outside device from docker's perspective so that container even though it can it can talk to us it can talk to the internet we can't talk to it we can't initiate the connection and that's an important thing to understand so what we have to do is we actually have to poke a hole on our local machine and basically say that hey traffic that comes in on a specific port to our my local machine which once again is this windows uh with this windows machine right here i want to forward that traffic to this docker container so how do we actually do that well first of all let's kill our container because that thing's not going to do anything at this point so we'll do docker rm then we can use the container name react app now normally if you want to kill a container you have to stop it first but that's a waste of time so i usually like to pass in the dash flag that'll allow you to kill a container that's actively running so we'll do that and then once that's done we'll just do a docker ps let's just make sure it got removed and it did so perfect and now i'm going to hit the up arrow until i get to my docker run command and i'm going to pass in another flag doesn't matter where i pass it in but i'm going to pass in the dash p flag i'm going to do 3000 colon 3000 so let me explain what this is so there's two numbers separated by a colon the first let's actually let's actually take a look at uh the number to the right of the colon so this number dictates what port we're going to send traffic to our container so our container our application is listening on port 3000 so we want to forward all traffic to port 3000 so that's why uh it's set to port 3000 on the right side the number on the left side is the port that we need to send traffic to on our local machine so this is saying that any traffic sent to my windows machine on port 3000 will get forward we'll get forwarded to port 3000 on the docker container now keep in mind these numbers don't need to match even though i made it match in this case just because it's simpler why not um but if i said this to be 4000 what that whoops that's 400 4 000 oh i have insert enabled that's why okay hold on there we go 4 000 uh so what this is going to do is it's going to say anybody in the outside world any other devices on my network anyone on the internet uh anybody that sends traffic to port 4000 on my local machine uh my local machine is then going to forward it to port 3000 in my docker container uh and so that's ultimately going to fix everything i'm going to change this back to 3000. i've set up a couple of slides actually it's just one slide in powerpoint to explain this a little better um hopefully i just want to make sure that we're all on the same page when it comes to how uh this kind of port forwarding actually works with docker containers okay so we have our host machine in the blue here so this represents my windows pc and then somewhere on my machine we have a react container running and so the idea is that we want to send traffic that comes to my host machine my witness machine on port 3000 and forward that to port 3000 on the react container uh so anytime anyone in my local network or anybody outside of my host machine that sends traffic to me on port 3000 my windows machine is just going to naturally forward it to the react container on that same exact port and the same thing applies when i go to my web browser and i type in localhost colon3000 so that's basically initiating a connection from the outside world as well so i'm sending it to localhost 3000 and my machine once again is going to forward that to port 3000. so hopefully that made sense uh just keep in mind those two numbers don't need to match but i just think it makes things a little bit simpler so i'm going to hit enter and let's see if this fixed our issue yep and look at that so now we have successfully connected to our docker container on port 3000 so so now that we got our react application to work inside our docker container i actually want to connect into the file system offering our container and take a look at a few things and so if you've never actually connected into the file system of your docker container it's easy to do all you have to do is type in docker exec and you want to pass in the dash i t flag for interactive mode then you pass in the name of the container so we gave it a name of react app so i can just copy that and then now what we want is we want to override the default command of the container so the default command is an npm start our application's already running we don't want to do that instead we want to run the command bash so we can uh take a look at the um at the bash terminal of our docker container and so now we can we are actually in our docker container it's going to automatically drop us into the slash app directory just because that's the working directory and here if i do an ls this is going to print out all the files in the slash app directory of our container and you could take a look at all the files and so because we ultimately did a copy you'll see that we copied over our source folder our public folder we got our package.json we got our node modules we got our readme and then interesting we have our docker file here as well now that makes sense because we are copying everything however do we actually want our docker file in our container we only need our docker file to build the image but it doesn't need to be in the image and so that's a little bit of an issue i mean it's not going to break anything but i think you're starting to see where i'm going with this right there's going to be certain files that we don't want copied over into our container on top of that you'll see that we have our node modules folder on our local directory when we do a copy we are copying our node modules folder over and there's absolutely zero reason to do that because we're running an npm install and this node modules folder could be massive and that's going to take a lot of unnecessary time copying this folder over when we already have all the dependencies installed from npm install so clearly we can see that there needs to be a way to ensure that we don't copy certain files over not just not just the docker file not just our node modules folder right we've got git folders we've got a git ignore we might have a folder that stores all of our secrets we obviously don't want those copied over into our container so how do we actually prevent that from happening well we can use we can create a file called a docker ignore file very similar to a git ignore file in fact same exact concept so let's create that we'll call it dot docker ignore all right and so let's specify the files we don't want copied over so we don't want the node underscore modules folder being copied over because we're running an npm install we don't want our docker file copied over we don't want our git folder copied over we don't want our git ignore copied over and now that we have a docker ignore file let's make sure we don't copy that over as well and um you know we might have a env file that stores all of our environment variables we probably don't want that copied over as well and we're going to add that in the in the future or a couple of minutes down the road so let's save this i'm going to exit out of this docker container and let's go ahead and kill the current running container so docker rm react dash app dash f all right so we have our new docker ignore file and so now if we do a docker image ls we don't want to create a new container because it's still going to use the old image which automatically has all of these files baked in uh and it didn't factor in the dot docker ignore file which we just created so we're gonna have to rebuild the image so i'm just gonna keep hitting the up arrow until i get to that docker build command and i'm just gonna rerun that same exact command all right and so now we've got our new image let's run that container uh from that image so we'll do actually just keep hitting the up arrow until i get to that same command let's see where is it here we go we're gonna run this command right here and once that's run let's go into bash by doing that docker exec command and let's just verify those files aren't there and so when we do an ls we can see that the docker file and the docker ignore file are not there now we do see a node modules folder but once again that's not actually from us copying it over that's actually from running an npm install no way to prove that obviously but hopefully you guys can just take my word for it all right so let's connect back to our application and let me just hit refresh just to make sure everything's still working and it is and i want to go to our code and let's just say we are actively developing our project and we make some changes to our code so let's go to our app.js file and i'm just going to add another p tag i'm just going to say hello there we'll hit save and now let's go back to our app uh it looks like we don't see it you know normally react apps when you know when you save it you know node mod is going to automatically restart it and show you the changes uh let's try refreshing it see if that changes and it looks like nothing's changed even when i do a refresh so something is clearly broken and once again let's go ahead and investigate why uh so i'm still connected into my uh docker container if you haven't uh if you already exited out just run that docker exec command right and do an ls you'll see all the files let's go into our source folder and let's actually take a look at the app.js let's see if those changes actually got propagated into our docker container so if you want to print out the contents of a file on a linux operating system just do cat and then app.js okay so that's going to print out the contents of that file and uh if we take a look you can see that we do not have the extra p tag that was created or that we just created a few seconds ago so what exactly happened why is the code not updating in our docker container and that's a really easy answer think about the steps that we performed right you know the first thing we did was we ran a doc let me exit out here we ran a docker build which runs these commands so it copied our code right and the code it copied over was you know we ran this before we made the code changes so before that extra p tag got put into place it then created an image right and that image that we created is right here however that image does not have the changes we just made because it was made you know over four minutes ago and we made those changes you know 30 seconds ago so we have a still image all right and then we run and then we ran a docker run which then runs that code but nowhere are we telling the container or the image to update those changes that we make on our local file system because at this point we just change it on our local file system but nothing's been updated on our docker container so this is a little bit of a problem from a development perspective you know how do we get it so that the code actually syncs so that uh you know so that our code actually shows the changes that we make well the first thing that we can do is um let's go ahead and kill our container as usual let's make sure everything's saved you know with our changes what we can do is we can just build a brand new image so we can do docker build uh dash t what did i call this react image and then specify the context which is you know where our docker file is ultimately run that so now if we do a docker image ls now our image has the updated code so if we do a docker uh well let's just hit the up arrow until we find that docker run command let's run that now if i hit reload we can see the hello there all right so that's how we updated the code because now our image is no longer stale image however here's the problem with doing that if i change my code again and say um i don't want this p tag anymore hit save right once again it's not going to update automatically i'm going to have to go through that whole process of rebuilding the image and re-running the container which obviously is not a scalable or maintainable solution because for any code change if you want to just quickly test it out you'd have to kill a container you'd have to rebuild the image and then deploy a new container and that would just slow your development workflow down to the point where you can't get anything done so there has to be an easier solution and there is what we're going to do is we're going to make use of something called volumes in docker containers so docker containers have a concept of volumes which allow you to have persistent data because right now if you have a container with any kind of data in the container if you kill the container the any data it has is lost automatically and volumes just allow you to maintain that data even if your containers die um but the nice part about it is there's different types of containers and there's a specific type of sorry there's a different types of volumes and we're going to use a very specific type of volume called a bind mount and bind mounts are really cool because they're going to solve our our main problem here and bind mounts what they do is they allow you to take a folder a directory on your host machine right that's my windows machine it allows you to take a folder on your host machine and sync it to a folder in your docker container right and that's exactly what we want we want to sync our project directory with the slash app directory in our docker container so let's take a look at uh at those bind mounts and how to actually configure them so i'm going to kill my container and i'm going to hit the up arrow till i got to my docker run command and i'm going to pass in a simple little flag called v this stands for volume so you're allowed to specify as many volumes but we just want one volume and the way this command's going to work is we specify the uh the directory on our local machine so i'll just call this dur i'm just kind of this is kind of like pseudocode local directory and then you do colon and then you do container directory so that's the way you specify this command you specify the path to the local directory that you want to sync and then you specify to the directory in the container that you want to sync it to so let's type this out now so here this is where it's a little bit of a problem we have to um on this when we do this command unfortunately we can't do current directory with the dot instead we have to grab the full path in our operating system to this directory so i'm just going to do a right click and just do copy path i'm going to paste it in here and so we get this massive line that starts from my c drive and then it goes all the way to app.js but we don't need the app.js we just need to make sure we get uh actually we don't even want to go into source we want to just do my main project directory which is react docker yt actually we do want to sync the source directory because ultimately we don't care about syncing all of these other files we just want to sync our source code so we're going to sync our source our source directory we do colon and then where do we want to sync it to it's going to be slash app source and so this is going to ultimately sync those two folders up so that anytime we make changes in our code it's going to match in our docker container although what i don't like is this massive line right here it's it's just so big and disgusting and also uh you know if you're writing this out and you happen to have a directory on your file system with spaces in it you're going to have to make sure you wrap it in quotes or then you're going to run into some errors but i'm going to show you a shorthanded syntax so that you don't actually have to type out that entire line and it's going to vary from operating system to operating system but if you're on windows and you're using powershell which in this case i'm not i'm actually using the standard command line but if you're using the powershell what you can do is you can type in dollar curly braces and then within there you can type in pwd so that's going to grab the current working directory which is exactly what we want and then you can do you know src uh if you are on the uh the windows command line the main terminal uh you can do uh instead of curly braces you can do uh percent c d percent and that's going to grab the current working directory and then we want to go into the source folder from there so it's just like a variable that you can save and then finally if you're on linux you can do dollar and then parentheses and then pwd and remember on linux it's always forward slashes slashes so uh either one works if you want to do the full path but i you can do that also this works for mac as well so mac is basically just running linux so if you're running mac go ahead and do that i'm going to change this back to uh what i need to for my for my windows operating system which is going to be uh percent cd percent and then on windows it's backslash so this is gonna sync those files together uh let's run that now uh you're gonna get a warning so if you're using wsl 2 on windows you're going to get a warning uh it's it's recommended to mount the the linux file system onto your machine and then put your code on that linux file system i didn't bother doing that so it's just letting me know that you know it may not be optimized or it may lead to issues uh so just keep that in mind all right so we got that our app is running let's go back to the uh uh to the web browser hit refresh it's working uh let's go back to the code and let's make some changes i'm gonna put another p tag say hello there again let's save and let's hit refresh and once again it didn't work now for some of you it may have worked this is actually a windows issue and so to get this to work we actually have to pass in an environment variable but like i said this is strictly a windows issue so i'm going to kill my container and i'm just going to keep hitting the up arrow and i'm going to pass in one more flag so if you want to pass an environment variable into your container you just do the dash e flag and what we want to do is us this choke dar i don't even know what that means but this was just from googling so chocodar and then you want to set that equal to actually sorry it's chocodar underscore use polling set that equal to true and that should fix our issue hopefully all right so let's refresh that let's just make sure it's working okay so we got the new hello there but that's because it was already there let's make the change now so if we change this to be hello sanjeev hit save pray it works and look at that we can now see that the code is in fact updating on our local container and react is automatically restarting the application so that we can see those changes and so that's basically how we can use bind mounts to sync our code from our local directory to our docker containers app directory now with bind mounts this creates one minor issue and that is that the syncing between our local file system and the container file system is a two-way street so if we make changes to our local to our code in our project directory on our local machine it's going to sync it to our container which is exactly what we want however our container can make changes to our code or create new files or delete files and that's going to get synced to our project directory and i think you're probably thinking well why would the container ever make changes well it shouldn't but why even give it the option right so if we actually log into our container by running the docker exec command i'm going to navigate into the slash app source directory where we copied our source code and i'm going to run this command called touch so on a linux file system this creates an empty file i'm going to call this hello and i want you to notice something when i create that file look at that it syncs to our folder on our local machine and so there's really no reason why we ever want our container to be making changes to our source code or touching any of our project files we just wanted to receive the changes but we don't want it to make any changes so the way we can do that is by making that bind mount read-only so let me show you how to do that i'm going to exit out of that i'm going to kill my docker container we do docker rm react-app and then f and i'm gonna delete this file because we don't need it and i'm gonna hit the up arrow until i get to my docker run command and i'm gonna go up to my volume my bind mount and at the end i'm going to do a colon i'm going to do ro so what this is saying is that this bind mount is going to be read only so our container can read it but it cannot make any changes to it it cannot change any of our code it cannot change any files it cannot delete or create any files so let's run that and now i'm going to do a docker exec again to log into the file system go into our source directory and i'm going to do that same touch command and look at that when we run that it says this is only a read this is a read-only file system so a little bit of a protection mechanism a little bit of security mechanism just to ensure that your container doesn't mess with any of your source code however if there is a specific need for the container or your application definitely make changes to the files uh within your docker container then you obviously wouldn't want to do that okay so the next thing i want to introduce is working with environment variables when it comes to our docker container and i already kind of showed you guys a little bit when it comes to environment variables so we saw that to get to get around the the issues with windows machines when it comes to updating the code we had to pass this environment variable uh right here right and the syntax is you pass in the dash e flag and then you just say the name of the environment variable and then what value you want to set it to so this value is true for this environment variable so what i want to do is i actually want to put in an environment variable into our source code so that we can actually test and verify if um you know the environment variables that we set are actually working so where it says hello here instead of hard coding my name what i'm going to do is i'm going to change this to a template string i'm going to say actually first of all this has to be in curly braces so that react knows javascript coming we say hello and then we'll do string interpolation and then we'll say uh to grab the environment variable we're going to do process dot env and let's just pass in a environment variable called uh react underscore app underscore name so if we pass this environment variable in it's going to be passed into this string right here and remember when it comes to react applications you have to prepend the name of all of your environment variables with react underscore app okay let's save that and let's go to our docker file and let's add a default environment a default value for that environment variable so we can do env and we can say uh react underscore app underscore name and we'll just set that equal to my name all right and i'm going to kill my container now since i made changes to the docker file let's go ahead and actually rebuild our image so we'll do docker build t react image and then dot for the current directory and then now let's run our container with the same command that we've been using and if we check our code now we can see that it passed in the default environment variable all right so that works but let's override that and i'm going to do that by first of all killing my container we saw that you know we passed the other environment variable by passing the dash e flag and so if you want to pass in more environment variables you can just do dash e again and then pass in another one so this is called react app name copy that and i'm going to set that equal to sanjeev all right and now let's refresh this and we can see that look at that our environment variable got passed in to our development server and updated the code respectively so this can be a little bit annoying if you've got a lot of environment variables which you very well may and so if you've got like 10 environment variables having to type those all out into the command is a little bit obnoxious and it makes your command look really ugly so instead what we can do is we can create an env file actually i wanted to put it here dot env and we can just paste in all of our environment variables so i'm going to grab this one and i'm going to grab this one all right and so now you can store all of your environment variables into this.env file and now i'm going to kill my container and i'm going to run the same command but i'm going to remove the environment variables and i'm going to pass in that file and so to pass in a file we do dash dash env dash file and then the path to the file so it's in the current directory and it's called env and did i not kill the oh i tried to delete the image not the actual container uh so hold on i want to kill react app not react dash image and then now we can rerun that command with the dash dash env file and let's pray and hope that this works yep and look at that so it's now loaded the environment variables uh from that env file so use whichever one you prefer i like using the file just because you know when you get a lot of environment variables it's just easier to store it in that file okay so we finally got basically our main development docker container up and running and we have the command to do that we hit the up arrow you can see that we have to pass in a couple things we've got the environment file or you can pass in the environment variables one at a time we've got our bind mount which it looks like i forgot to put in the colon ro i'm not sure what happened there but that's optional uh we want the d flag then we have the the port mappings we also have the name of the container and then the image that we built it from so this line is kind of long and you'll see that you know as you keep working with docker some of your docker run commands will start to get even longer than this like this is actually nothing compared to some of them and it's also important to understand that generally when you're working with a dockerized application you're going to have multiple containers that you need to spin up to actually do all of your testing and to do your development and imagine having to remember five or six of these long commands for you to have to run just to bring up your development environment and then imagine having to kill all those containers one by one right that's a very long and slow process and i think it would be nice if there was an easy way to kind of write down all these instructions in a file and then just run one command that pulls all the configs from those files and guess what there is a tool for that it's called docker compose so to work with docker compose it's really easy we just create a new file i'm going to call it uh docker dash compose dot yama so this uses yaml syntax which i mean doesn't really matter you can look it up but the main thing that i need you guys to understand is that when you're working with a yaml file spacing matters right it's like writing python right you need to make sure that things are tabbed in the right way if you put too many spaces in one section it's going to lead it to not work now with docker compose we are going to provide the instructions for creating all of our containers which in this case is just one react container but it's going to have the instructions for that so the first thing is we have to specify the version of docker compose that we're going to use so i'm going to do a version i'm going to do colon 3. and put that in quotations too so i'm going to do version 3. now when it comes to versioning i recommend you look up this page right here so just copy that url and this shows you all the different versions and it shows you what features are supported in which version so depending on what you need when it comes to creating your docker containers you can try to figure out what version of docker compose you need to run i'm just gonna do version three uh we're not really doing anything special we don't need any of the newer features so i'm just gonna grab version three but if you do need one of the newer features just grab the the version it was first introduced or just use the latest version it doesn't really matter so now that we have our version defined the next thing we want to do is do services so services and docker compose represent a container so each container that you have is going to represent a different service so for us we have a react application so we're going to create a service called react app or whatever we want to call it uh if you have a back-end server like a node.js or express server you can create one called backend server if you have a postgres database running in a docker container you can create another service for that and call it like postgres or database or whatever you want so i'm gonna hit enter and this is important um because the configs are gonna go within the service block so i'm gonna hit tab once so that we know these lines are part of the services tab and i'm going to give this service a name you can give it any name you want i'm just going to call it react app because that's ultimately what it is and i think that makes sense now within our react app i'm going to hit enter and do one tab so make sure you go one tab over so this is going to signify that all of these configs that are going to pass in are all the configs for our react app container or serv or service so the first thing that we have to do is we have to specify the image that's going to be used for this uh and so since we're creating a custom image we're gonna pass in the build option and then we have to give it that context right so if you remember uh when you do a docker build you pass in the context which is just basically the simplest way to think of it is just the path to the dockerfile so i'm just going to do a dot but keep in mind it's not exactly that exact definition but i think it just simplifies it for now uh the next thing that we need to do is pass in the port mapping so we can do ports and hit tab and here we can specify a list of port mapping so you could technically have more than one nothing's going to stop you from doing that but we're just using one so we're just going to do the same thing all right and keep in mind i'm literally just putting in the config options for each one of the flags that we've passed in so i'm just going to go step by step so we're going to do 3000 colon 3000 so what else do we need to do uh well let's see we need to uh define that bind mount so let's do that next and so now uh create one called volumes and notice how all the configs are all lined up remember spacing matters when it comes to yaml we'll do volumes and remember you can have more than one so we have to write out a list so anytime you're working with lists and yaml you do a dash and then whatever it is so we'll do um dot slash source colon slash app source so remember here we had to use the the little percent cd percent or pass in the full path when you do docker compose you can just do current directory and then slash source so that's a little bit nicer when it comes to working with docker compose and then you can also do the read-only if you really wanted to but who cares about that for now and keep in mind you can add as many volumes as you want if you wanted another value just do the dash and then whatever kind of volume you want to add uh next thing that we want to do is we want to pass in environment variables so we'll do environment and this is going to take in a list so there's going to be two so we'll do react underscore app underscore name equals sanjeev and then you know if you're on windows you also want to pass in this one all right and there's actually an alternative uh way uh to do environment variables remember we can do a file as well so if you want to do a file you can do env underscore file and then you can pass in a list of files so you can technically have more than one and then just the path to that file so whichever one you want i'm going to just comment this out just for documentation purposes so that when you guys look through the the code you can remember how to do with the file i'm just going to pass it in directly in this case and i think that should be it so let's save this and now what we're going to do is first of all let's kill our other container if it's still up so now i'm going to do a docker dash compose this is important do docker dash compose don't do docker compose it's a different command so do docker dash compose and let's do a help just to see what are the options so we have a couple of different options but if we look down you'll see that we have a uh where is it up command so a docker compose up is going to run all of these instructions it's going to bring up all of your services all your containers so if you had 10 containers with whole bunch of configs it would spin all 10 of them up for you with one simple command and that's really the power of docker compose so let's do docker dash compose and then do up and let's do dash dash help one more time just to see the different flags that we have uh one thing you do have is the dash d flag for detached because by default just like when you run you know a docker run uh it's going to attach to the container we don't normally want to attach it so we can pass in the dash d flag so let's do that and let's see what happens and let's take a note of the output so first things first the interesting thing about docker compose is it's going to create a new docker network for you so up until now when we run the docker run command it puts the containers in the default network that gets creative when you install docker when you do docker compose it actually creates a new network for you and it's called react docker dash yt underscore default and that name actually comes from your project directory so mine's called react docker dash yt and then it just does underscore default uh and so once that's done we can actually take a look at the network it created but that's not really that important after that you can see it starts to build our image right and that comes from this build step right here and so it's going through the different steps whole bunch of warnings who really cares about that all right and so after it finished building the image which is right here it then went ahead and created our container so let's do a docker ps and you can see it created a container called react dash docker yt underscore react app underscore one so that name is just our project directory dash or sorry project directory underscore service name react app and then underscore one because i guess you can have multiple containers part that are part of that service so now let's go to our web browser and let's just check to see if it works it looks like it's working let's just change our code uh and so i'm just going to just add some stuff right whoops yep and there you go so you can see that it actually did update perfect now one thing i do want to point out actually now let's go ahead and actually uh remove our container now so we brought all of our containers up with a docker up we can do the same thing and bring everything down with a docker compose down so we can do docker dash compose down and that's going to bring down all of our containers so hopefully you can see how powerful docker compose is because you can spin up your entire environment and bring it all down with just one simple command so we'll do down and you'll see that it stops the container deletes it and then it removes that the network that it created for us also keep in mind when it builds the image it actually builds a brand new image so if you do a docker image ls you can see it created an image for us so did react docker dash yt underscore react app so it names the image in that same exact format so this is the one it created we've now killed our container if we do docker ps it's no longer there now a couple things to note uh if you remember our docker run command where is it so our docker run command for some people you may actually run into some issues if you don't pass in the dash i t flag for react uh so this was kind of broken for a certain amount of time it looks like we don't need it anymore um but if you do need it uh you'll have to pass in the dash i t flag so if you ran into any issues uh with this video and you weren't able to actually get docker working and react working in a docker container you may need to pass in the dash i t flag and i did and the reason i'm bringing that up now i probably should have done it before but i had forgotten but when it comes to our docker compose file the dash it flags can also be passed into here as well so for for the i for the the i flag you can do std uh i n underscore open set to true and uh also i think there's another one for the t and so for the t flag we can do uh dty colon true so that's going to be the equivalent of passing in the dash i t flag but looks like we don't need it anymore or i don't in my case but things may be different depending on which version of react you're using so keep that in mind let me just comment that out for now uh now there's one thing i want to point out with docker compose uh if you run it up i want you to notice what happens actually before we do that let's say we make a change to our um our docker file right let's say we add a new environment variable say env uh we'll call this chinese underscore food equals good right so we've made a change to our docker file and when we do a docker compose up you're naturally going to assume that it's going to rebuild the image but if we actually do a docker compose up let's actually see what happens you can see that i created the network and then it just started a container right so it did not rebuild the image and the reason for that ultimately and the reason we're seeing all this output is i didn't run the dash d flag but the reason why it doesn't actually rebuild the image is because docker compose is very dumb and docker composed just does one simple check we do a docker image ls all it does is it looks for a docker image with a specific name based off your project directory and the service name it has no idea if this is the latest image if this is a stale image it just looks for an image with that name and if it sees it it's not going to rebuild it so you have to tell docker compose when you want to rebuild the image because docker compose can't figure this out so we know we put in a new environment variable uh and so we have to do a docker compose up and then we let's remember the dash d flag because i hate reconnecting it and then just do a dash dash build so a build is going to trigger a build again so you can now you can see it's running through all of the build steps it's creating a brand new image with that environment variable and then it brings up a container right and so that's all i wanted to show you um when it comes to the docker up um because a lot of times people forget that you know anytime you make changes to your docker file and your image you have to tell it to rebuild by passing in the dash build flag because uh docker compose isn't intelligent enough to figure that out on its own but let's go ahead and tear everything down by doing a docker compose down all right so we've got a solid workflow now uh where we've kind of automated all of the configs that we need for um bringing up our container however this is this will only work for our development environment our production environment is ultimately going to be a little bit different actually it's going to be significantly different um because right now we're running react in in development mode and it's not actually ready for production so in the next section we're going to see how we can set up two different uh essentially two different workflows one for your development environment and one for your production environment all right guys so i set up a few slides to kind of explain the difference between uh running react in development mode versus running react in production mode uh and so right now we've been running in development mode till now so anytime you do npm start that automatically implies that you're running in development mode but when you're running in development mode what happens is that react actually spins up your own custom dev server a little web server that'll serve your react application uh and what i mean your react application all your source code so you got that index.html file you get all your css all your javascript and so when your web browser sends a request on port 3000 the web server is going to then send back the index.html back to your web server and so all of this is right now is running in a docker container however this is just how it works in development mode in production it's a little bit different because first of all the dev server that comes with react is not production grade obviously it's only meant for development purposes if you sent a couple thousand connections it would probably destroy it so when you want to move into production mode uh what happens is instead of running an npm start you run an npm build and what that does is it creates a directory in your project folder uh called build and then within the build you'll have the final html file your final css i forgot an s there and then your final javascript file so it's just going to return three static files that have all your code baked into it and that's it so how exactly do we work with these files in production right because they're just three files and we no longer have a development server when we do an npm run build we just have those three files so what exactly do we do then well when we go into production mode we have to set up something pretty similar to what we had with our development server the only difference is that we put in our own production grade web server and so we swap out the react to dev server with something like nginx and it doesn't have to be nginx you can use apache you can use any other web service or web server that you know of but you just need something that's able to serve those static files uh and so now our web browser can send traffic or send a request to our docker container on whatever specific port it's been configured to listen on in this example i just chose support 3000 again but um you can pick any port if you wanted to do the default port 80 you can do that if you for some reason you need to use a non-default port you can specify any port that you want that request is going to go to the nginx server the nginx server is going to return those three files that it got from when from when you ran npm run build and so that's ultimately how our production server is going to work so now we have to kind of understand how exactly are we going to accomplish that because we've set up a docker file that creates um our own custom docker image and then does an npm start to start our development server but this is a little bit trickier because when we do npm run build it's just going to return a couple files so how do we then create an nginx server and then have those files and then nginx server serve that in a docker container well the way we can do that is with multi-stage builds so till now we've just been using single stage builds but we can create a multi-stage build which means that you can basically run the from command multiple times right the from command determines what base image we're going to use and so this diagram is going to explain ultimately how we're going to do that so we're going to break it down to two different stages so the first stage is basically what we had before so we do the from node and then a specific version if you're interested in that we copy the package.json we install the dependencies we copy the rest of our source code and then we run an npm run build instead of an npm start and that's going to return all of those static files in a build folder then we're going to start stage 2 where we do it from nginx so we're going to take the public nginx the official nginx image from docker hub we're going to take that and then we're going to copy the result of this build folder or the result from stage 1 into the folder inside our nginx container and then we're going to start nginx and nginx will then serve those files so it's actually pretty simple uh when you think about it we just have to break it out into two different steps so let's take a look and see how we can actually accomplish that all right so what we're gonna do is we're gonna have to have two different docker files um right now we just have one we're gonna have to have one for development and one for production so what i'm gonna do is i'm gonna rename this guy i'm gonna name him dockerfile dockerfile.dev so this is the steps that we're going to use for development there's nothing that changes for development we're really just focused on what are the different steps we need for production however changing this file name does create does create a few issues so by default docker will only look for a file called dockerfile it will not automatically know to pull something called dockerfile.dev so moving forward anytime you do like a docker build and then you know you do the context we actually have to pass in one more flag we have to pass in the dash flag for file name i think uh and so now you have to pass in the full path to your docker file and the name of the docker file so it's in the current directory and then we call it dockerfile.dev and since it's in the current directory we don't actually need the dot slash and so now if we run that it's going to create an image so i just wanted to highlight the difference now that we changed that name and also in docker compose we're going to have to change this as well because right now it just says build a dot which is going to look for a file called dockerfile which no longer exists and so we actually have to split out the build stage into a few more configuration options so now we have to pass in the context which is just going to be the dot which is the current directory and then we also have to pass in the name of the docker file which is going to be docker file dot dev and so those are that's really the only change that you have to make when you change your file name to be um dot dev and dot prod um but now let's go and create our dockerfile dot prod and let's create that multi-stage build and so just like previously actually i'm going to actually just copy a lot of this and then we're just going to tweak it so once again we're gonna copy the note image set the working directory uh copy the package.json do an npm install copy all of our source code uh we may have some environment variables don't worry about those actually we don't need that anymore and actually let's remove that from here as well uh same thing expose 3000 we don't actually need the exposed 3000 because remember we're not actually going to do anything with um with this node app right here it's just going to spin up that container and then run all these commands but instead of doing an npm start we're going to do an npm run build and instead of cmd this is actually going to be a run so this is going to run at build time and actually i could just change this to be npm run build and so that's the end of the first stage that's all we need we just need to build those files and then we'll move on to stage two so we'll do from and then from here let's go to docker hub and i want to search for nginx let's find the official nginx image first result's going to be the one that you want and so this has got some instructions about you know how this image is supposed to work and so let's go back to our source code we'll do from nginx and we'll say copy dash dash from equals react actually i haven't explained that yet so let's just copy um whoops so what exactly do we need to copy right we need to copy the output of this uh this command so what we can do is we can actually name each stage so i'm going to name this as and we can call this you know react build or something and so then i can say copy dash dash from and then we just specify the name so react dash build actually i'm just going to name this build instead i don't need the react all right and from there we want to copy whatever is in the slash app slash build directory because when we do an npm run build it's going to create a folder called build in here so i'm going to just show you that real quick on our local machine not in our container and you'll see that it's going to create those files for you in a couple seconds uh just pay attention right up here you'll see a build folder pop up there you go so we've got the build folder and um it hasn't finished yet so it's just created a couple of the files but you'll see eventually uh an html file and a javascript file in a css file probably all right so now you can see we've got under static css javascript and me and media so we're going to copy everything in the slash app slash build folder and what are we going to copy it to well we got to figure out what directory we need to paste it in our nginx container so we know that nginx will automatically serve those files if you take a look at the documentation we want to send it to uh slash user slash share slash nginx slash html so you put it into that directory and nginx will automatically serve it for you you won't need to configure anything else so let's do slash user share slash engine x whoops engine x slash html alright so hopefully that makes sense we're just saying from the previous stage called build i want to copy whatever's in the build directory which is those static assets and then paste it into this folder inside the nginx container and then lastly we have to figure out um yeah i think that's about it so let's uh save this and let's try building this image by itself so do docker build uh now we have to do dash f so we want docker file dot prod and then let's give it a name so i'm going to do dash t we'll call this docker image prod and then the build context is the current directory let's try that out and so we do a docker image ls we've got our docker image pro let's try deploying a container so i'm gonna hit up until we get to that long command that we used to be running uh with the docker run uh and so a couple of things need to be changed so this is production uh so we definitely don't need the bind mount because we don't want our code to be syncing there it's production uh then what ports do we want to map uh so nginx by default is configured to listen on port 80. so we got to change that to port 80 and then what port we want to open up on our local machine it can be any port it doesn't really matter i'm just going to do port 8080 but if you wanted to do just port 80 that's fine then we have to specify the image so it's react-image prod and then we can call the container react app prod and then everything else should be the same so let's try running that i couldn't find that image what did i go oh i called it docker image prod okay that was a bad name um but we can change that it looks like port 8080 is already taken uh let me check that docker ps oh that's my other container i was running earlier today so i'm gonna stop that and then let's run that command again yep so we're getting this error just because um previously it created the container it just didn't start it because port 8080 was taken let me just delete that and then we can just start it from scratch so docker ps dash a we're gonna just do a docker rm react dash app dash prod okay so now we run this command it should start it there we go and now if we go back up to here remember we're not listening on port 3000 we're listening on port 8080 let's check it out and look at that it works our application works uh if we change our code it shouldn't change anything because remember this is our production server so i'm going to delete the asdf i don't need that anymore hit refresh nothing changes it should still be there perfect all right guys so let's quickly delete this container that i just created for production uh because we won't need that anymore so i'm going to do docker rm react dash app dash prod f and now what i want to do is we have a docker compose but right now it's just setting up the instructions for our development environment we also need to set up the instructions for our production environment and i'm trying to think of the best way to do this and i think the easiest solution is to have a separate docker compose file for your development environment and a separate docker file for your production environment so i'm going to create another file in fact i'm actually going to create two more files and i'll explain why so first of all i'm going to rename this i'm just going to call this um backup now this is just so that you know you guys can take a look at the configs for when we had um the original configuration um but let's create three new files so i'm going to create a new one i'm going to call this a docker dash compose dash dev yaml then i'm going to create a new one called docker dash compose dash prod.yaml all right so obviously the dash dev is going to have all of the instructions for our development environment the product's going to have all the instructions for our production environment and then finally i'm gonna have one i'm gonna create another one called dockercompose.yaml so you might be wondering well why did i create another just dockercompose.yeah we've already got one for dev and one for prod well here's the thing right we're only working with one container so uh splitting it up between dev and prod is not a big deal but if you've got you know 10 12 different containers all with a lot of configs you'll see that most of these containers the configs for development and production are going to be roughly the same there may be like one or two changes for each container and so what i want to do is the docker compose.yaml is going to have the configurations of containers that will be the same regardless of development or production environment and then the dev file is going to have only the config differences that are applicable to development and the production one's going to only have the configs that are relevant to prod and you'll see that the dev and the prod file they can actually overwrite configs on the docker compose so you can set up a docker compose that has some basic configurations and then override them for each specific environment and so what i'm gonna do is i'm gonna go to this docker dash compose backup i'm just gonna copy this whole thing for now i'm gonna put that into my docker compose dash yaml so the first three lines are going to be the same because we want to define our service um however um our build actually we'll leave the build here for now let's take a look at the uh the configs that are going to be the same so what configs do you think will be the same well the environment variable is going to are going to be different probably and uh we know this is only going to be applicable for development environments uh so let's remove this because we can just apply the environment variable specific configs uh inside the prod and the dev yaml files uh this one the volumes are not going to be the same for prod and dev you'll see dev just the dev will have the bind mounts the prod will not uh the ports uh so if both uh you can configure your nginx to listen on port 3000 as well uh then you can have a shared ports config but for us they're going to be different so i'm going to delete it from the shared config and then the build is also going to be different because you'll see one's going to point to dockerfile.dev and then one's going to point to dockerfile.prod so we can delete that or comment it out and you'll see that we're ultimately left with nothing so like i said um we don't technically need this doc composed yaml file but you know in real uh practical applications you're gonna have more than one container uh or yeah more than one container more than one services and uh you'll see that there will be a lot of configs that will be roughly the same across all of them so there's definitely a benefit to having this shared config file so i'm going to leave that here and actually what i'm going to show you is i'm going to uncomment this i'm going to show you how we can overwrite whatever values are stored here so in the prod we can go under build and just specify a different file and it'll overwrite this so i just want to showcase that functionality all right and then now i'm going to take the the backup yml gonna copy this and post it into the dev so this is already the exact configs that we need for our development environment um because this is what we're using when we were developing our app and so you can see we're specifying the dockerfile.dev opening port 3000 we got our bind mount and we got our environment variables and so now let's go to prod and let's set that up so things are going to be a little bit different so first of all all our build is going to be dockerfile.prod our ports remember we changed our ports to be 8080 it's going to map to port 80. we're not going to have a bind mount and for our environment variables let's just change this to sanjeev-prod so we know that it's working and for dev we're going to change this to dev and i think that should be everything that we need uh let me just double check all right so it looks like everything's good to go so let's test this out and so what i'm going to do is i'm going to do a docker dash compose so now we have to pass in two different files so we pass in the dash flag for file name and so the first one is going to be the docker dash compose.yaml which is the shared configuration and the order does matter by the way so always start with the shared config file and let me just make sure i do a save all and then we pass in the second file which is going to be the docker dash compose dash compose dash dev yaml and we're going to do up and then we're going to pass in the dash d flag and then the dash dash build just to make sure it builds the images although the first time you run it it's always going to build the image so he didn't technically need that so it's created our application our container was to docker ps and we got our one container running and so this is our development environment so we want to go to port 3000 and you can see that it's working right we got port 3000 and look it says sanjeev-dev so the environment variable worked the last thing that i want to test for our development environment is uh let's go to um our source code and let's change a few things so let's just add some gibberish let's hit save and you can see the gibberish showed up so um our changes are getting synced and react is automatically reloading so let's uh let's tear that down now so we're gonna do a docker compose down and you're gonna have to do the dash flags as well we'll do down it's going to tear everything down and let's try production now and so it's very easy same exact command except instead of dev i'm going to do pride all right and now let's test this out it's going to be port 8080 okay and so it looks like it's working however there's one issue look at the my name all right it looks like our environment variable which we pass into our docker compose dash prod uh did not take effect in fact it's actually taking the default one from our docker file which let's go to our prod and so we set that here so what exactly is happening well this actually took me a little while to figure out um but what happens is that all all the values of the environment variables when you're in production mode get set here because once we run npm run build all of our source code is set so those environment variables get populated into your code uh and then when we do docker compose dash prod and we pass the environment variables here this is passing it into our nginx container which is not very helpful for us right which is not very helpful for us because it's not in the source code and so even though it's running on this server remember our um our application is actually running on the web browser so we actually need to find a way so that we can pass it into the build uh when it's doing an npm run build and the only way i can figure out how to get this to work was by doing this we actually have to pass in arguments and so arguments allow us to pass data from a docker compose file into a docker build file so let's see how we can do that uh here under um uh under the build section uh we can pass in args so we can pass the list of args i'm going to pass in one called react underscore app underscore name and that equals sanjeev dash pride we don't even need this line anymore we don't need either of these lines anymore uh so both of those can go all right so this argument that we're passing right here will be passed into our docker file so it's going to get passed into this section right here and to accept the argument we have to um we have to pass in arg and then react underscore app name so this argument is going to be whatever value we set in our docker compose right here all right and then now one last thing that we have to do is we just say react underscore app dash name equals and then we have to pass in this value so to use that as a variable we just do dollar and then react underscore app name it's going to take that argument and it's going to pass it into this environment variable so it gets set when we're actually building the files so it's a little inconvenient i actually don't like having to do that but i couldn't find any other alternatives so for every environment variable you're going to have to paste all of those in it's a little time consuming um that's why i kind of just don't recommend doing doing react within docker i just don't see the true benefit of it but i just wanted to make sure you guys understood how to do it and why you needed to do it so let's test this out now let's save everything and i'm going to tear everything down first all right and then now let's do an up again and remember we want to rebuild this um actually let's put that space back in there we want to let me save that but we want to rebuild it so make sure you pass in the dash dashboardflex so anytime you change anything with the docker files of any of your services within your docker compose you want to make sure you do pass in the dash dash build because docker compose isn't smart enough to figure that out on its own it's going to rebuild your image all right and so now that it's running let's hit refresh and let's hope that the environment variable got set and look it's finally been set correctly to sanjeev dash pride so that's how you can pass in environment variables when you're working with react in a production environment all right so that's pretty much everything i wanted to show you there's one last thing i wanted to show you when it comes to multi-stage build and then we can kind of wrap up this video so if we actually go back down here and i'm going to tear everything down here we go down i want to show you uh what we can do when it comes to multi-stage builds so we've got our prod file and we've got the two different stages let's say you wanted to build an image but you only wanted to run some of the stages well you can do that so when you do a docker build and then the context and then let's just give it a quick tag of um a multi-stage example what we can do is we can pass in the well first of all we also have to pass in the dash f for uh the dockerfile name so we'll do dockerfile dot prod and then we can pass in the dash dash target flag so this target flag is ultimately going to tell docker what is the final stage that you want to run so in this case we only have um we only have two different stages um but let's say we added a couple more right let's say we go to the top and we just added um you know from let's say i don't know is there a postgres there's probably a postgres and then we do some you know some steps whatever steps that we wanted to what we can do is if we say dash dash target um it equals build sorry and there's no equal sign so if we just do dash dash target build it's going to run uh stage one and then stage two so by specifying the name of the specific stage that's going to be the final stage that runs so anything after it will not run so if we remove this uh in this case we just have two stages so if i just say target build it's only going to run this stage because it's the uh it's the final stage and since it's also the first stage we're only gonna have one stage that runs so if we run that you're gonna see it builds the image but you'll see that it will not do the nginx image and so you see it just stops right there so there's no engine x involved so if you've got like really complex uh docker files with like you know multiple stages you may not always want to run all those stages you may only want to run a couple of the stages and so this is how you do that by passing in the dash target flag
Info
Channel: Sanjeev Thiyagarajan
Views: 16,994
Rating: 4.983027 out of 5
Keywords: docker, react, reactjs, programming, webdev, devops, development, javascript, container, build, compose, tutorial, beginner
Id: 3xDAU5cvi5E
Channel Id: undefined
Length: 85min 37sec (5137 seconds)
Published: Tue Mar 09 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.