Transcript from the "Docker with Angular" Lesson
>> I want to talk about Docker, so in the previous lesson, I showed how to essentially do a production build that generates kind of this optimized set of static files that you can put into like an S3 bucket. Or that being AWS, there are essentially, variations of this for every single major cloud hosting platform.
[00:00:25] But another very viable option, and one that I use very often is docker, and there's a ton of great material front end masters. And so we're not going to do a deep dive into docker. But what I wanna talk about is how I use docker in my own development workflow, to not only develop locally, but to develop within a team, and then ultimately, where that Dockerfile kind of ends up going in terms of production.
[00:01:00] And so within the application, specifically on the 08.1 docker branch is where you're gonna find this. This will all go back into master when we're done. But, there are some interesting additions to this particular project that I want to point out. Now before we get into the files themselves, that if you go to the readme to the very bottom, there is this very handy Section that we've added to this readme, that kind of explains how to get up and running.
[00:01:44] And so, what we've done here and I can just go ahead and do a preview of this. So, open preview, you scroll down to the bottom and these are the commands that we use. So one thing that I do want to call out is that if you're going to do this locally, then you need to install if you can believe it, docker, and so this is a super great tool.
[00:02:13] It's super easy. And this allows you to run docker containers locally. And so you can see here that I have two applications running. So I went ahead and did the Julia Child's cake in the oven, and we're not gonna waste 15 minutes or whatever watching this build. But we have two containers.
[00:02:35] We have the app and the API. And so this leads to really the discussion that I want to get to about how do I use this in my local development environment. And so, what I've done is if you look into the file structure here is that I have a Dockerfile for the app, and then I have a Dockerfile for the API.
[00:03:03] And what we're doing is we're essentially using these Dockerfiles to build out or to really, essentially build out a container and put the application. Inside of that docker container so that you can spin it up. And so I'll just quickly kind of walk through these pieces. So we can see kinda what's happening in what they're doing.
[00:03:29] So we're using Alpine. And in this case, we are putting because this is the API, we're doing it on port 333. And then from here, we're just packaging or essentially copying some additional files over the package dot Jason, the yarn lock and this decorate angular CLI. So what you have to do is you kind of have to play around with this a little bit to figure out what do I need to put in this to work, and more importantly is that we are essentially copying everything within this project over.
[00:04:08] Now what you would do to optimize this, is that we're just saying pick everything up, and move it over. Now this would fall very much into the make-it-work. As we start to progress and optimize on this, what you would do is, you would say I want to copy just the node files over.
[00:04:26] And so, this certainly right here for the sake of getting up and running, this is fine. But eventually what you wanna do is, you want to get specific about what you're copying into your application. Now with that, one of the things that I found is problematic or challenging when working with docker within a mono repo is that inside of our application, we only have one package dot JSON.
[00:04:59] And the problem with this is, that we have all this angular stuff. And then we have all this nest stuff. And our node modules is essentially a combination of all our angular dependencies and all of our nest dependencies. Now what you do not want to do, is you start to put this into production is to essentially burden your nest application with angular dependencies and vice versa.
[00:05:29] And there's not necessarily a great answer to this. The answer that or the technique that I use for this is that I simply create a new package dot JSON that has just the dependencies for that particular application. And I place it inside the app. So what I would do here, is, I would go into the API and I would create a specific package dot JSON for this application.
[00:06:01] And so this is kind of one of these last steps that you do as you start to integrate into a CI CD pipeline. I've spent a number of time or I've had a number of conversations with the NX team and other developers of like, what is the solution?
[00:06:16] For when you actually want to separate dependencies from applications before you deploy them and you build them. And this is one option is that you just create a separate manifest if you will, per application. There are some other solutions that exist that will kind of allow you to do this on the fly.
[00:06:35] I don't even remember what they're called. But it seemed like a lot more work than simply just manually creating a standalone manifest for the production build. So, this is one of these things if somebody is watching this, and they know of a way to segment dependencies for like application, that would be amazing.
[00:06:55] One thing I would possibly propose is being able to tag your dependencies or like sub segment them so that when you're building it, you would say, I wanna put just these inside of the docker image. So back to this Dockerfile, we're just basically picking everything up and we're moving it over, and then we're exposing it on the port that we set, which is 333.
[00:07:20] Now if we go to the angular side, we're doing essentially the same exact thing, except we're exposing port 4200. And then from here, we're essentially just picking it up and moving it over. Now, we can run this individually, one at a time. So, if you wanted to build these and run these completely standalone one or the other, then you can build the image and then you can run it.
[00:07:54] And so these are the commands here. But, what I do, because I know that I want to run this in parallel, is that I create a docker-compose file that handles essentially spinning up both of these Dockerfiles at once. And so, what I'm able to do is I'm able to define by API image.
[00:08:21] And I'm also able to define my app image. And so not only am I writing, I'm saying here's my port, but then as well is a mapping the ports. So I'm just saying 3333 is mapped to 3333 on the outside. So what you can do is you can actually have an internal port, and an external port in your docker image.
[00:08:51] And then if we look here You'll notice that we have this command. And so what it does is, when it copies everything into the image, then it issues this command. And so what I really like about this particular approach is this Dockerfile both of these, are less than 25 lines long, and they are fairly consistent.
[00:09:20] And so I would say as you start to go through this, and look at this, that you'll start to see kind of some very consistent things about this like the big one being what version are you using in your image? What is the PORT? A lot of this is pretty consistent, and now this is the command right here we're saying, serve the API.
[00:09:46] We're doing the same exact thing here, serve web. But, within the docker compose, what we're doing is that we're able to do this in one consolidated way so that when you're ready to run this, all you have to do is run this docker compose up, and what I've done and I do have this running in this other tab here is so everything that we've seen is it's building.
[00:10:20] It's just taking all of that and it's running inside of the container but you can to do this. If you pull this down, you can do docker compose up and it's just going to essentially spin up these images. The one thing that I'm doing, is that I use this remove orphans.
[00:10:47] Remove orphans and build tags to give me some greater control about that. So it's going to remove any kind of orphan images that we have, and it's going to then build it. And so the full command here, I recommend pulling this down and running this from the command line is that when this runs, that it will take a little bit of time.
[00:11:10] But what it will do when you're when you're done, so if you just run this command and you have docker running, the end result is that you will have two docker images running on your local computer. And you can also step into the docker console, and inspect a lot of this.
[00:11:34] But at the end of the day, you'll notice that I'm still at localhost 4200, because we just mapped them, but now, these are being served via the docker image. With that said, we now are running, both of our applications inside of this docker image or both. We have actually two docker images and, the next logical extension of this what I typically do, is I do not consider myself to be a database admin.
[00:12:11] But I will typically spin up postgres, and then run all of my data layer inside of yet another image. And so it's very common for me at a local like in my local development flow, to have typically like an API image, and a front end web application image and then a database image.
[00:12:30] And the beauty of this is, that this alleviates a lot of I think the developer environment and consistencies. And so there's a lot of times where it's like if this doesn't work on my computer, it does work on my computer. Well, what you can do is that when you develop against docker images is that you're able to pick these up, and essentially create some insulation around that environment, especially around the things that you don't really want to know about or you don't care about.
[00:13:01] So, for instance, a very stack that I've had to work with is the front end is applique is in angular, but the server is in Tomcat using like Java spring or something like some like Java back end. And I was using, it might have been Postgres or something well I'm not super great at ostgres, and I'm not definitely not good at Java framework like spring or whatever it was.
[00:13:28] The way to solve that, is that you wrap those particular pieces into docker images. And so I'm able to run them without having to know how they work. And then from here, really all that you do is you can now pass in these Docker files into your CICD pipeline.
[00:13:50] And they will generate those docker images and deploy those into wherever it is you're targeting. So, for me if I'm putting docker images into the cloud typically is going to go into fargate. So I think fargate is a really, really good option for putting docker images because you just essentially say, here's my image, and it will handle all of the underlying infrastructure for you.
[00:14:15] It'll scale contract everything that you need, kind of on command. So this is where I think it's kind of a good taste of really Docker in an angular application or in a mono repo. But how this kind of works, and then now it's a matter of taking these images and then kind of putting them wherever you would like, even typically on the backside of a CI CD pipeline.
[00:14:42] Those go into like fargate would be a good example. So, with that said, I realized this has been a whirlwind sampling of Docker inside of an NX mono repo, but just to summarize that It's putting an angular application in a Docker image or desktop application in a Docker image is fairly simple.
[00:15:06] In the sense of you just copy the files over, you run the command. You can streamline that using your Docker compose. And then ultimately you decide where those need to go and then you can but those images wherever. More importantly is that you can actually pass this around, to your development team and accelerate the development workflow