About this video
What You'll Learn
- Repair missing Laravel asset builds by wiring the Node watcher as a dependency in Docker Compose.
- Connect database setup to the PHP environment with automated Artisan migrations and seeding to complete app startup.
- Enable Laravel hot module reload by fixing HMR port collisions and container bind addresses during local development.
Ciaran McNulty and Alex Bowers join David to wire up a Laravel dev loop in Docker Compose: fixing the npm watcher, running Artisan migrations, chasing HMR port bindings, and refactoring the production Dockerfile for Composer caching.
Jump to a chapter
- 0:00 Holding screen
- 1:34 Introduction & Series Context
- 1:35 Introductions
- 3:00 Context - What have we done thus far?
- 3:14 Recap of Current Docker Development Setup
- 4:00 Identifying Initial Problems (Missing Assets, NPM Not Running)
- 5:00 Debugging Node/Asset Workflow from Previous Episode
- 5:50 Automating the npm watcher
- 5:51 Fixing Node Container Dependency in Docker Compose
- 7:48 Troubleshooting NPM Run (Cross-env not found)
- 8:22 Realizing NPM Install is Required
- 8:46 Automating NPM Install & Watch via Make Target
- 10:08 Successful Basic Development Environment (Assets Compiling)
- 12:00 Automating database migrations
- 12:08 Application Loads, Identifying Missing Migrations
- 12:23 Running Database Migrations & Seeding with Artisan
- 12:46 Discussing Development Workflow and D Shell Usage
- 17:57 Application Fully Functional After Migrations
- 18:00 Confirming live reload works
- 18:10 Testing Live Reloading of Assets
- 20:28 Live Reloading Appears to Work (Initial Check)
- 20:45 Discussion: Rolling Up Migrations (Laravel 8)
- 21:11 Discussion: Using SQL Dumps for DB Init
- 22:10 Getting hot module reload working
- 22:11 Addressing Hot Module Reloading (HMR)
- 23:17 Switching to NPM Run Hot for HMR
- 24:53 Debugging HMR - Not Working as Expected
- 26:20 Identifying HMR Server Port Conflict
- 27:44 Adjusting Docker Compose Ports for HMR
- 28:15 Restarting Services & Re-running NPM Install
- 31:28 Further HMR Debugging (Console Errors)
- 31:39 Reverting to NPM Run Watch for Comparison
- 33:07 NPM Run Watch Works - Examining Mix Manifest and Hot File
- 41:58 Realizing Hot File Needs to be Accessible to PHP Container
- 43:34 Manually Creating Hot File in PHP Container
- 44:38 Debugging HMR Requests (Port 8080)
- 45:54 Fixing NPM Run Hot Binding Address (Localhost vs 0.0.0.0)
- 47:17 Re-testing HMR After Binding Fix
- 58:00 Adding database persistence
- 59:20 Successful Hot Module Reloading
- 59:40 Viewer Q&A: DB Persistence & Series Navigation
- 1:02:01 Assessing "Completeness" of Local Development Setup (Missing Scheduler, Queues, Advanced Tests)
- 1:03:00 Running the tests
- 1:03:05 Deciding on Future Episodes with a More Complex Application
- 1:03:19 Current Goals for Ping CRM: Run Tests, Deploy to Kubernetes
- 1:03:27 Running Unit Tests (Make Test)
- 1:05:09 Reviewing Production Dockerfile (Multi-stage Build)
- 1:05:10 Optimising our Dockerfile layers and caching
- 1:06:01 Running Tests Within the Dockerfile Build
- 1:06:48 Fixing Production Dockerfile Composer Steps
- 1:08:31 Viewer Q&A: Docker BuildKit Parallelism
- 1:10:47 Refactoring Dockerfile Composer Steps for Caching
- 1:14:26 Testing Refactored Dockerfile Build & Caching
- 1:16:51 Discussion: Composer Lock Files (.lock) in Container Workflows
- 1:19:01 Troubleshooting Dockerfile Composer Scripts Issue
- 1:20:01 Using --no-scripts for Composer Install
- 1:21:26 Using Composer 2 Image
- 1:24:12 Production Docker Image Build Successful (with Caching)
- 1:29:26 Discussion: Docker Build Secrets (SSH/Private Repos)
- 1:31:01 Wrap-up: Ping CRM Setup Complete (for basics), Next Steps with New App & Kubernetes
- 1:31:38 Conclusion
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
1:34 Introduction & Series Context
1:34 Hello. And welcome to today's episode. Today, we are continuing the series with Laravel working on a local development environment with Docker. And we will also cover deploying our Laravel application with Kubernetes. Hopefully, the production like uncover all the good bits in between. As you'll seen in previous episodes, if you've seen them, this is not always plain sailing, but I will do my best to make this work as best as we can. I'm very fortunate today to be joined by again, Kieran McNulty. Always a pleasure to have you back. And a new guest today, we have Alex Bowers who is our resident Laravel
1:35 Introductions
2:09 expert who is gonna make sure that we don't mess up too much again. Hey. How are you both then? I'm good. Thank you. No pressure, Alex. But now everything lies on your shoulders as Kieran and I have proved ourselves useless a few times now. So Well, the odds and issue last week definitely wouldn't have been something I caught because I did not know that was a thing. So yeah. We'll see how it goes. Yeah. If I don't sorry. I only got kitchen. I like this new idea of drafting people in from the comments. Yeah. You comment a lot. Come and join us.
2:40 I think what was in fact, we got really lucky I think last time and we'll cover what we we went over. But the artisan file being used to detect the root directory was actually a random comment as well from someone that was watching, which really saves us probably hours if not days. And then on that week or We'll see how we get on today. I'm not sure how many more of these we're gonna have. I think we're close. We have a pretty good setup. There's lot of stuff in Laravel that's still not being covered yet
3:00 Context - What have we done thus far?
3:08 though. Maybe this is a bad idea. Alright. Let's go for where we are. That's a bit of a a recap. I'll pop up my screen. And I think what we'll do is we'll start with the how do I expect this to work? Right? So what we have and I'll go over the files in a moment. But what we should have is an environment where I can run shell and everything should just be working. This is the plan. Right? That should spin up. In fact, we can see it's got our MariaDB running. We have a PHPFPM
3:14 Recap of Current Docker Development Setup
3:46 container running, which is serving our PHP assets. We have NGINX running, which is serving our static assets, and we have no no nodes. So we maybe didn't automate that last time. We did. The engine x doesn't depend on it. Alright. So should definitely fix that. Now if I take a look at our PHP configuration, I don't know. NGINX is serving. So browsing to eighty eighty should get me something. We've got some work to do. If if we never use to log in, which is something. So PHP is working. It looks like it's not it's a few stuff isn't working there.
4:00 Identifying Initial Problems (Missing Assets, NPM Not Running)
4:39 Yes. So the chances are that in our last episode, working with the the node NPM style workflow, we just did our position right now where we don't actually have those assets being delivered. Is it worth just before you do anything, be compiling all of those assets just to make sure that they're all present? Hello, it says? Oh, no. Yeah. Got letter 4. 4 0 4. Okay. So let's go over that then. So our main focus last time was this node workflow here. Mhmm. What we wanted to do was provide something that would run an MPM run watch
5:00 Debugging Node/Asset Workflow from Previous Episode
5:12 in the background continually working on the asset completion for us. Now this actually looks okay, but the problem we had last time was we needed Artisan to exist in order for our assets to be put in the right directory. So let's confirm that. And, David, that that volume, the assets were being put into, is it likely that's been removed from your machine in the last week? Yeah. Definitely. For sure. I I delete liberally. So So we have to have never been compiled yet. It might be all works if we start there. Okay. So first problem. There's no MPM container
5:51 Fixing Node Container Dependency in Docker Compose
5:55 running, and that's because our d shell environment is very specific with the dependencies. And we haven't actually made anything dependent or node dependency because if I recall correctly, we had a recursive dependency. Don't remember that. Right? We do. So let's take a look at the make fail. And We'll just spend a few minutes. I think it's important to remember where we are exactly rather than weighing in it. So d shell environment spends up NGINX in the background, and then it spends up PHP and our dependencies of PHP will be spun up for us. So if we come back
6:31 to our Docker Compose file, we can see that it depends on MariaDB and that is created for us, but that is it. There's nothing currently that starts node. Now if I remember correctly, the challenge with automating that was we have a shared volume between NGINX and node. They're both consuming this public volume. Mhmm. And could we get away with that? And we're just saying that our PHP no. Okay. So we can't add it to PHP because PHP is a dependency on engine x. And engine x wouldn't be able to satisfy the dependency, but also depending on node.
7:11 So we can let's see what happens. Yeah. I see that. Right? Alright. So we're just gonna add that dependency. I am gonna add the artist on file because we did cover that. We know that the assets go in the wrong direction if we do not have that fail existing. Mhmm. Yeah. And in theory, I should be able to run this again. We have a node container, so our watch should be running. And we got an exit one. Okay. Let's put out the logs on our node application. Cross end of not found. Interesting. So anyone know what cross env is?
7:48 Troubleshooting NPM Run (Cross-env not found)
8:16 It's a node package that's the pain of my life whenever you try and do stuff with NPM. Okay. Perfect. So the problem here is NPM run watch as in doing an NPM install first. Yes. Okay. So the way that we wanna tackle that is probably by bringing on a custom build step where it runs a Docker image, which is run NPM install as part of the build process and then no. Because we went to over we've got the volume there. So we need we actually need to do this and say, we're gonna need to make
8:46 Automating NPM Install & Watch via Make Target
8:49 target that this runs that ensures the MPM install happens before the run watch install happens. Yeah. We don't need to build it into the image because this is a and sort of a femoral Exactly. Then Plus we have the shared volume, which would just blow away over the top of it, so it wouldn't work. So okay. Step one. Let's let's get this sorted then. So we're gonna do node assets, which is going to ensure we have an NPM install followed by an NPM run watch. It's in central. If we update our Docker compose file here,
9:29 where you can just say make. And I'm assuming that make is gonna exist inside this container. I was gonna ask that. How many people try and make their containers pretty lean, don't they? How many actually include Make? Anything that's based on a Ubuntu Debian will come with Make. Pretty sure. The Alpine variants generally don't there we go. We're good. You need to mount the Makefile as well then. Gotcha. Hey, Aaron, on your keep already. Oh, we might add our Makefile. And by magic, we're running our one single command that we want to be our entry
10:08 Successful Basic Development Environment (Assets Compiling)
10:17 point to this application. That looks pretty good. Can you see the logs? Logs. Hold on. Okay. MPM blah blah blah off the codes. What we're assuming here then is when this completes, I should be able to browse to the application. Should should. So while that's working, let's encourage people to ask oh, well, we have some comments already. So yeah. I was gonna say we should encourage people to leave comments. We will act we'll answer as many questions as we can handle. And if you want us to cover that's not been covered yet, feel free to let
11:12 us know in the comments too. Okay. What do we got so far? Oh, we got a comment from Robert. This episode will be useful. Yep. I really hope so. I'm I'm really hoping this is the first episode where we're not a problem driven development and we actually, you know, can do the things that we wanna do. But I'm not gonna get I'm not gonna get make too many assumptions. Yeah. Else have we got? So we have a comment from Mackey's. You're brand new to this project. You're calling the repo. How would I make sure my
11:49 note requirements were installed? Oh, we just answered that by absolute sure luck. So yep. We are using a meet target as part of the node entry point there. And We're just tackling that problem. Good. So, yeah, keep those comments coming. And look at that. Easy. Easy. Alright. So as pre populated the username and password, which I'm gonna assume work. Good. It's leading us on nicely to something we haven't covered yet. We got to run the migrations. And we got to do the migrations. How did you manage to log in last time then? Had you run them manually in
12:23 Running Database Migrations & Seeding with Artisan
12:28 DHL? Yes. That we yeah. Because that's I might have been episode one. I should just check it all works. Okay. So we're we're in pretty good shape then. I'm happy with that. Mhmm. Because that was a relatively easy fix. We got our make fail. We added a target. We got ours in. Now we wanna handle common development tasks, which is where our d shell environment comes in. This here is where we run all of the commands that we would normally do if we were building natively on the host. And in fact, the setup that we have here works regardless
12:46 Discussing Development Workflow and D Shell Usage
13:01 of a d shell. Right? If you can use all of these make targets without ever using the Docker setup. It's just there as convenience and and for the parity thing, which I think I rambled on a lot about the first episode. So I would suggest if we if okay. So resident Laravel expert, that's his where you shine. If I were if you were building a Laravel application locally, what would be your workflow for running the migrations here? You just run PHP arts and migrate. Does that set up the table scheme as well? Yes. So the migrations
13:39 you have a database folder in the I think the root folder that will contain all of the migrations with, a date prefix. It just runs on list top down. In the database itself, there's a migrations table that keeps track of what migrations were run, rent, when, and handles it all for you. In in Laravel, creating a table is normally a a mig it's always a migration. There isn't a separate sort of create schema and then Not typically. Although, you can make use of Doctrine's ORM, which I believe is then driven based on models or some I've never done that, but
14:15 you can make use of that with extra packages. Okay. So you're saying the workflow would just be our design migrate? Space migrate. I'm assuming that creates the migration. Artisan is about wrong as well. Artisan migrate. Mhmm. And now that's all your migrations done. You should now be that's that's also in the so you can run that again all the time. And would there be CTA that I need to load? There can be. I assume this project probably does. On the end of migrate, you can do double dash seed. I would do that locally, but never in
14:55 production. Or you can do d b colon seed, which is a separate command which does the same thing. So do we want that do you want the migrations to run automatically when we enter the d shell or when we start the project? I mean, it definitely could be we could have some sort of make target called make and it the handle then any bootstrapping commands. And then, you know, again, we wanna replicate native local development. I don't well, I I have a lot of me every native command I can with a make target. This is just what people are used to
15:31 doing. I don't wanna remove that muscle memory, that habit, all that stuff that they normally do. Again, this is a I want them to feel like this is just a local machine only happens to be in a container. Yeah. That's my goal here. One one thing to note on that. Quite often when I do development, I have probably 10 tabs open. Oh, well, not necessarily 10, but a lot of tabs open of, like, the same folder. What we'll have migrations won't be tailing logs, all that sort of stuff. If you just go into the d
16:00 shell, is that the same instance or are they new instances each time you go into it? You get a a new container every single time. But this it's gonna be pretty lightweight. I would I I personally wouldn't be concerned about that. But if we did think it was gonna be a hassle, we could add a t shell enter target which allowed you to pop open more than one. You get, like, a sudo t two I inside the same container on a on a loop. Yeah. The the overhead really of having loads of containers running, especially on Linux, should be
16:29 the same as having loads of tabs open Pretty close. Yeah. Yep. This is right. Because there's no I think what's really important I think Kieran's just kind of alluded to anyway, but I'll I'll make it a little bit more explicit. Is the opening a container without a process running in it doesn't have any overhead. It's when I run the process, so that process runs within the namespaces that we configure in the host. So, yeah, open it up. You're right. Running running these containers is no different from really opening up a new shell process on your your
16:58 local machine. Yeah. Because they enter your cache. Although, might be an overhead on non Linux platforms. Normally, would say I don't support those, but I know I found myself confined to a Mac. So I'm having to support this use case, but we'll see. We'll see how it goes. But but let me show that working then. Right? So if I just jump down here and split this. Oh, how do I do the other split? I see. Okay. Because it is mixed up. Make the shell. Just ensure no dependencies are running. Have I oh, there's no p s command
17:32 here. I'm gonna show you that there's nothing else running. Sure. It's gonna be the same regardless, but this is you just need to trust me. This is this container. In fact, the IDs are different. There we go. EFB. So these are different containers, But I can still open as many as I might want. It's pretty lightweight. The seats the seats worked. I have a login. I've now got some sort of a way to view the $500 page, the photo for error page. I've got my menus. This application well, besides is that You left with the CSS, didn't
18:10 Testing Live Reloading of Assets
18:11 you, to test and watch? What do you mean by If you're to change back the default tailwind config, you'd be able to see stuff, I guess. Alright. Let's see. Get this. At least I get status. I don't see that. Oh, because I've pushed it since so the code was available for everyone. Alright. Let's see. Where is the CSS again? It is This is tailwind config file. Oh, tailwind config. Nice. Oh, yeah. Yeah. Those templates. I can get the defaults if you want and I can just replace them. So if you need to rewrite those. I was hoping it would give him a
18:54 hover with the diff, which normally quite nicely does for me, but not today. Yeah. I'll let you drop in some value. I otherwise, I would have made them all white or black or red or whatever just to fix the menu. So we just have one other problem last time as well. Right? And the challenge was I modified the view fail. This is why I changed the tailwind fail. Right? Because we modified the view thing, reloaded the page, and I never actually seen the update. But to confirm that it was the live reloading was working, I modified the tail when
19:30 the configuration. So I guess we should once we revert these values back to the proper colors, which Alex is doing right now, perfect. We should make another change to the view and see if our new node runner has fixed that configuration. There. That's better. Yeah. So you were trying to check that text dashboard dashboard file right now. Yeah. So where was that? Was that let's go to app. Nope. Was in resources. Resources, JavaScript, pages, dashboard. Dashboard. Index. Yeah. Alright. Okay. So my five is here. I changed that in more yeah. Then the bottom too. And do we have the five? Yeah. We
20:26 do have the five there. So in theory, I've I should It just works. Yep. Well, how this is gonna be the first episode where everything just goes plain sailing. Now that I've said that, it probably just jinxed everything. But With the question about the migrations, Alex. Do people sort of roll up the migrations on a big project? So something came out new in Laravel eight, which was released about a month ago Mhmm. Which is where you can combine previous migrations into a schema file, and it will handle all that stuff for you now. That but that is a a recent feature. It's
20:45 Discussion: Rolling Up Migrations (Laravel 8)
21:08 not something that's been around for long. Because I know those I know that MariaDB container has a feature where you can pass it a special file location that will run dot SQL files in that location. Okay. Well, I think you still need to make use of the arson migrate command. It sort of like it figures this out how to run that alongside your current migrations and stuff. I'm not sure whether it does anything clever other than just, like, run that file first and migrate afterwards. I don't know. What what you can do I I don't I haven't
21:11 Discussion: Using SQL Dumps for DB Init
21:39 done it with Laravel, but you can sort of dump your entire database into a dot SQL file. Run that, and that would include the migrations table. So then Ah. And when you run the does prefer. It's all the ones that it's it's kind of done already that that snapshot has. I'm not sure because I don't think it takes all of your data unless your migrations themselves put data in. I'm not sure on that, but I think it's to some extent intelligent. I'm not sure. Right. Okay. We have a a question then. So Robert is saying, does hot module reload
22:11 Addressing Hot Module Reloading (HMR)
22:14 work or is this not part of the Laravel basic package? So I'm not really sure what the hot module reload we're talking about here. Is that something you're familiar with, Alex? Yeah. So when you're making changes in JavaScript or CSS or whatever, if you're changing, for example, a value and it requires you to reload a page, that's not necessarily ideal depending on the type of application you're dealing with and where what is you're doing. If it takes, like, 12, like, 12 pages to get to the same point of, like, filling in out a form or something, it's,
22:44 like, a single page application. You don't have to do that. So hot module reloading reloads just that one section of CSS or that one section of JavaScript or whatever. Reloads the page, like, behind the scenes, fetching in the new values so it's updated without you having to, like, refresh the page. Okay. So that change I made there should have automatically just showing up. Yeah. That would have just when you by time you got back, it would have just, like, been there as if you'd done a reload, but you hadn't. Okay. So that automatic I'm assuming that the
23:12 way that we work with NPM run watch is handling that? I believe that Laravel Mix has a different mode. Not because it has it requires like a separate server running in the background Yeah. To handle the reloads. So this could this could get interesting, actually. Similar, doesn't it? You have some extra JavaScript gets injected into the page for the Yeah. To get then polls the back end server for updates. Yeah. So if you do you do MPM run hot. Yeah. MPM run hot. Alright. Okay. So I there are some things required here. We'll see if we need to
23:17 Switching to NPM Run Hot for HMR
23:53 tweak the host in the port to work in the Docker setup, but let's get the run hot. Now does the run hot work alongside run watch or does it replace run watch? Instead of okay. Might be Right. Let's see if we get this working then. So that means I wanna change or make target. It should be the simple alright. So we'll drop out this. We don't need to log anymore. We know that works. Pop open our detail again. And then I look cute oh, I've got key. See, I'm I'm still getting this whole command and alt being in the
24:37 wrong way problem. But, I mean, that's okay. So this is our hot version of JavaScript running. I'm assuming if I change a fail, I'll see something here. I'm also gonna pop this open. I'll get my two back. So let's refresh that. And let's try And that probably loaded extra JavaScript in the page. So it's good to have refreshed there. I'll do a hard refresh just in case. Right. So now let's change that dashboard file again. 21. Why not? I'm not sure which one of these are meant to be changing. I didn't see anything particularly Oh, the metal is the title tags, the
24:53 Debugging HMR - Not Working as Expected
25:20 ones that the the guy says in the comments. Alright. I I I didn't see anything happen here, but let's just and that's what that's still visible. But it's okay. So that the JavaScript bit is definitely getting a change, but we're getting a problem here. So it looks like we are gonna be diving into this configuration. A quick look in dev tools to see what connections it's making. That's a good idea. See if it's getting a four zero four to somewhere. Let's try. Yep. Let's go for the network one. See if we got a WebSocket or something.
26:06 WebSocket. Yeah. I don't see anything obvious here. Do we have any console letters? No. This is the kind of problem where you have to understand how it actually works to to figure out what to fix. So the hot the hot compiles a version of the assets that we have served through NGINX. Right? We're confident that's working. It's the hot version. Believe so. Otherwise, the compiled version the compiled files, like well, you could test that by removing the public folder, like CSS or something like that and run hard and just see if that continues to work, I guess.
26:20 Identifying HMR Server Port Conflict
26:53 It should just So that explains why it should just work, though. Yeah. But there's things like on. P r t answer. Have we meant to be doing that? No. That's just instead of doing just like, funding PHP and FPM and stuff. I'll send serve just the same thing. But does it do some, like, stuff like Okay. So I think that the challenge here is MPM run halt opens a server. So this actually runs something on port eighty eighty for those connections. Which we aren't exposing. Which we're not exposing. So let's expose that and see if that triggers something.
27:41 Although that port's the same as what we are actually binding to us. So let's let's change that first. Do want any error messages like combine support? Let's go with 8,000 here. We'll expose note on and also, I'm gonna assume that the assets aren't compiled or transferred or whatever is happening with the hot reload and it's maybe actually serving them over that port that may trigger injection of some code. So we need to make sure we handle that just in case. Mhmm. Let's spin this all back up then. But I'm just going put it away. Spin it back up
28:15 Restarting Services & Re-running NPM Install
28:23 by any chance. Okay. Now I'm gonna have to run these MPM install is gonna take longer now. I shouldn't have done the volumes. If you if you control c on Docker Compose, is that the same as Docker Compose one? No. The actual c inside of a t shell only shuts down that container. No. Sorry. In the when you run it, not in the background without the dash d option. If you can see, is that a stop or a down? A stop. It doesn't destroy everything as much. Alright. So we just need to give yeah. See, there's this WDS,
29:19 which I'm assuming is the the thing. So we're it it may be that we actually need to remove NGINX if we want the live reload to work. Well, you can you can change the port in the mix config if that's the issue. No. So it's not the port. The problem is is that our as as we have it set up right now, our NPM run hot is generating assets, which will then be delivered over NGINX and not via this. Although it could be that this is an additional port. So maybe that's webpack as an actual web
29:56 server server and assets, but it's just that web socket connection. I'm not really sure. I don't I think we're gonna have to kinda poke it a little bit and see what happens. Like, if I just browse to this, what will I get? Do I get the assets directory? Do I get You won't be able to browse to that because that's internal to the container, isn't it? So local host is referring to a different IP address. Oh, no. I just exposed it with a port forward. This connection was reset tells me I wanted an upgrade to a WebSocket and it failed. So
30:24 I'm gonna assume it's not serving the assets and all it's doing is serving WebSocket for the reload. So this the existence of that port as a WebSocket connection may mean that something happens here. Maybe. And so if we go to 8,000 It's crazy. Trying to filter by web socket. Migrations again? Yep. Let's get the migrations up and running. So PHP artisan migrate. Let's see. If this happens a third time, we're gonna add the migrations to the d shell command. Pop this back up. And p m install? That's still running. Yeah. You're you're right. No. No. It's finished. Can
31:16 you look at the console then? Because that might indicate that it's not found any of the assets. Yeah. Alright. So what's going on here? Maybe a change between NPM run hot and NPM run watch. If we switch it back to a watch, does it work? Maybe they're not something that you could directly swap out. It's kind of my my thought process. I wish thought you could. That's why I make a file. What was that? Sorry. Missed something. Why was that looking at port 8,000? Well, I moved engine x support 8,000 so that I could expose the WebSocket from node.
31:39 Reverting to NPM Run Watch for Comparison
32:11 Okay. Yeah. Okay. So let's Oh, wait. Are the assets they're looking for $80.80 or 80 80,000? Sorry. They were looking for 8,000, which is where they are. They should but they should not be looking for eighty eighty, should they know for the library loading stuff to work? Or if that's just a WebSocket connection? My assumption right now. It it depends. So if yeah. If they get if they get them initially from NGINX and then get updates from webpack, then they should load them initially from NGINX. But it should work anyway. Right? My understanding on the documentation is that it's
32:46 that they should always now refer to eighty eighty. Okay. Excuse me. Alright. Everything oh, because I haven't restarted the note container with a new command. Once it let's just check the watch works and and that all goes back to normal. And then we've we'll change it back to hot and we'll start modifying the mix config to do what I think we maybe need to make it do. What one third one thing worth looking at is what changed to the mix manifest file happens when you wouldn't get in the hot? Because that tells it where all the assets
33:07 NPM Run Watch Works - Examining Mix Manifest and Hot File
33:32 are held, like, where the assets are living. And maybe those files aren't exposed. Okay. Let's tackle this one thing of it. Oh, time. So there was a lot more output here from the run watch than there was from the run hot. The run hot did not do any of this. Yeah. It's doing stuff like versioning. Alright. So we're we're back. As far as web sockets go, we didn't get any trying to open. So let's copy this. I'll comment it out. We're not gonna put it in directly. So actually, this might completely simplify the way we're doing the assets because
34:14 in dev, we maybe don't need to have a shared volume at all. If everything comes in on server. How do you mean? Sorry. If the if the operating mode is that in in dev, we use hot module reload and all of the assets are served from that webpack server. It means we don't have to serve anything through any of the static assets through NGINX and a lot of the stuff we did last time we can chuck. I wouldn't necessarily say that hot module reloading is the way that everybody does it. A lot of people don't bother. It depends on the
34:53 type of application that you're dealing with because some people who are doing single page applications will find it more useful than people who are doing a standard service side rendered application. Yeah. I guess if your page is reloading every time you click something. Yeah. It's not it. Yeah. It's it's not an issue. Whereas if you're doing service side a single site single page applications, then it becomes more of an issue because where you are on the page matters more in front end rendering as well. Mhmm. Okay. So also what I've read here is that if
35:26 your application isn't using this syntax for the assets, then it's not gonna be able to do hot reloading. And we haven't really confirmed that ping does that. I'm just checking it now. Alright. It does make use of that stuff so it should be fine. Alright. So we're gonna bring back and run hot. We're gonna open up our mix file. Webpack dot mix. Yep. And this lane here, which they're falling off my buffer. See what that's with local host. Right? Yeah. So this turns on hot model reload. It's kinda what I'm getting from it. HMR options,
36:14 blah blah blah. So let's what did I change? Restart node. So that should now switch to running an MPM run hot again. And then we jump back into a d shell and see what happens. K. Run like it said failure then. Yeah. That was just because I killed and restarted the container. Okay. Okay. So we now got serving on eighty eighty, which we're exposing locally. Let's just see what happens. I mean, things working. So I guess it's click into, like, organizations. Okay. That seems okay. So if you make a change to the something now, I guess it should change.
37:25 Yeah. But I can't see a WebSocket open. I changed the dashboard. But that But well, none of this is server side rendered. So that if you change that to 23, that must be fine. But I guess change it to 24 now 25. No. It's not doing that, is it? Actually That's you refreshing right there. Yeah. So that's the hot not even yeah. That's so that's just what I was worried about. That's the MPM run hot isn't actually doing any compilation of assets. Isn't it? Well, no. I just changed that template refreshing the page manually, and I'm still not
38:03 seeing that update. Although, we do see a compilation step here. Okay. So what's I think it's doing that. It's compiling it, and then it's pushing the new compiled asset across the WebSocket rather than and not probably not saving it to disk. Yeah. I think you're you're right, which means NGINX is never gonna be able to serve this. So we probably if we want hot module reloading, we'd need to pull out NGINX and serve the JavaScript over. But it should be doing that automatically already. The the file what do you call them? Like, the file URL should be referencing port eighty eighty now
38:44 and not the other one. Do we need to control the assets once the volume using make assets so that the compiled JavaScript knows it's using mix? Because I think since we edited the mix config, we haven't actually compiled the assets. I'm happy to try it. Just yeah. Try just running it once and then I'm I'm just because I'm gonna request one of these assets over that socket. And I just curiosity is getting the better of me right now. So and then Yeah. Because what I think might have happened is Yeah. See, this is coming over 8,000.
39:29 This isn't eighty eighty. I'm pretty sure that should be eighty eighty. Yeah. But there's no way for the browser to know that. Right? Because the version we're serving on Port 8000 is the one we compiled fifteen minutes ago before we changed anything. So nothing to tell the web browser. You should be looking at WebSocket. That doesn't work. I think we need to compile it with yeah. Run the NPM whatever command we had, the the non hot one. Run that once and see if it changes. Alright. So I will cheat and jump inside of notes and do NPM run dev.
40:15 I wonder if artisan serve has something baked in to make this work. I don't think so because I've done it I'd never used artisan seven. I've had hot hot module reloading work before. So then so now I do a false reload in your browser and see what see what's what. Everything's still served over 8,000. If you do run run hot again and then have a look at the mix manifest file. K. We'll restart. Frogs. So you want me that's just the old one failing, so we'll give that a second. Which manifest file do you want me to
41:05 look at? I think it's in public slash mixed dot manifest. I think it's called a mixed manifest dot JSON, something like that. Indeed. Right. After you do hot, does that change? Or if you just run hot there? Yeah. We're still giving output, is it? Go away doc. There we go. That's what we're looking at. Isn't the one that's in the volume? Yeah. The one in the volume is the one that might matter. Well, won't matter. Alright. So what we're seeing is we have a public directory here with a mixed manifest. Never mind. I was expecting that to say
41:46 maybe have the port numbers in it or something. And it has been modified right now, and there's a hot directory. Okay. Okay. Hot file. Right. And that's got the so that needs to be exposed to the PHP one because Laravel mix has a PHP mix function that's exposed to the Laravel, and that will be checking if there's a hotfail to get the server from that rather than for it to be Right. It makes sense in my head. Don't know how to explain it, but I think that file needs to be exposed. I should be able to have a port
41:58 Realizing Hot File Needs to be Accessible to PHP Container
42:30 8,000 hot. Right? That's why we we need that, which it works, actually. But is that exposed to Yeah. We had this we had this on the engine export. But is that exposed to PHP was the question. So it would need to be able to see that volume would be mapped into the PHP container. Why would the PHP container need that when it's FPM that's running? It doesn't actually know about hot reloading or this file. And we're not using artisan serve. If we were using artisan serve, I would understand. But I'm assuming this hot I mean, we don't
43:09 even have a request to hot on the PHP side here on the network tab. No. Because it's looked it'll be like a floating the file with PHP. You don't see PHP request. They happen inside of the app. So Yeah. What if you like? Yeah. If you go into the PHP Docker container and paste that file there now or well, then the file or something there, does it start working? So it's server side using hot and then changes what it's sending to the browser. K. So we don't have the hot file there. And what we're saying is if
43:34 Manually Creating Hot File in PHP Container
43:44 I echo HTTP one two seven zero zero one hot to this. I forget the port. It wasn't hot on there. It was colon eighty eighty. That's all I had, I think. Okay. So it's just the base of URL. And I think maybe a trailing slash. Yeah. Yeah. But we can we can check. Right? So Yeah. It's probably worth checking. Okay. So This is exactly what we have there. It's a bit like the artisan file thing. You've had you know, they're they're quite tightly integrated with PHP and the JavaScript. And and the way we're trying to way we've been
44:28 trying to approach it is this quite separate pipelines. Maybe they might. Yeah. So that has broken out, which is good. I think it's now yeah. We can see asset request from port eighty eighty. But that's So that that's actually doing what we wanted to do. It's just a shame the request is failing. You have got an extra slash in here which probably isn't helping although So it doesn't have a trading slash where we added it. It shouldn't have a trading slash. As though that's not serving the asset anyway. So what's going on with run hot?
44:38 Debugging HMR Requests (Port 8080)
45:10 Let's go back into the node container. I think we're close. So Can we can we fix the hot file in the PHP container? Yes. I will do that. Just a moment. I wanna be able to curl eighty eighty j s zero dot j s. And I can from a node container. From here Is 8080 definitely exposed properly? I'm not gonna say definitely, but I'm pretty confident. Yeah. But it's not working there. It needs to be exposed from the node container. Right? Hold on. I bet you there's a problem. So MPM run hot as an actually binding
45:54 Fixing NPM Run Hot Binding Address (Localhost vs 0.0.0.0)
45:58 on all the interfaces. We need to fix that. Okay. Got it. I can fix this. So that's good. Listening on local host not on zero zero zero zero. Yeah. So this is the port that we wanna bind, not actually where we wanna be delivered. We're gonna bind on all interfaces on port eighty eighty. If we do this and let's restart node there, And our logs down here, we should see this is running on 000 80 80, which means I can have that from my browser. That should work. It should work. Let's get the logs running again.
46:39 I'll just tackle a question right there. So Robert's asked what's good practice for preserving the DB in the dev environment. So we're already configured MariaDB to use a volume. So you can count the containers and the database as preserved. The only reason it died for me and I have to migrate again is because through don't know if it's good or bad habit, but this dash v on the Docker Compose down will remove all the volumes as well. So I actually explicitly told it not to preserve the database state, which was the which was the problem.
47:09 And then for prod, your database will be handled completely differently. We can talk about that a little bit later. Okay. This is better. Which means if I hit refresh, we've got assets on that port. Now we can refresh this page. What's not gonna work right now? Oh, no. It's working. Oh, yeah. That PHP fails still exist. So we have to automate that. We can't forget. But we should have reloading there. Do we agree? Yeah. But it won't work because you're loading on pause you're loading IP 0000 from the front end, is wrong. If you look in your requests
47:17 Re-testing HMR After Binding Fix
47:51 So there must must be something in this configuration option then where I can say bind on this IP, but really we are delivering it over something else. So let's go to is it Laravel Laravel? What are looking for the mix stuff? Yeah. I'm looking for the code. No. It's Jeffrey Way slash Laravel dash mix. Jeffrey Way. Laravel mix. The search. Okay. Okay. I'll just search Laravel Laravel. Let's see if I can find that option. So it's It's not part it's so it's only part of Laravel mix. Okay. That's our goal. Maybe just lying to me.
48:42 I sent you an yeah. Cool. So we wanna understand this configuration. It only looks like you get the two config options. Yeah. Then that's not gonna work. Yeah. Okay. It's it's just I was gonna say it's it's purely designed. It's not intended for container sales. That's never gonna work. I I can't listen on all interfaces and rewrite. No. Yes. What's it you You can't you could you not make it use a specific IP and have the IP be, like, a Docker network? Or is that not I I wouldn't go down that route. It's too cumbersome, too painful.
49:39 Instead, what I want is to just put see, the the disconnect here is when we run a server, we have the binding address and then listen. The binding address and where we wanna route traffic is different in a container setup. So all I really wanna be able to do is tell this webpack configuration to bind on all ports, but still rewrite things to one two seven. There's a relevant question in the chat. The service name, it's a good idea, but it won't work locally. So, you know, browsing to the node DNS name on my local machine wouldn't work.
50:17 I've had I mean, we could force that to work and that I could set something on my host files that satisfied that. But again, I don't want that. I want everything to be contained in in this configuration. Could you make it inside of the actual container itself where local host wasn't one two seven, but instead zero zero zero zero? Because that's only based on the host file, I believe, is it? I mean, it's not exactly clean, but it would do it, maybe. Yes. A Rawkode engine host file. Sounds like a dreadful idea. Is there some host names in Docker Compose?
51:01 We can do some stuff with host names, but, again, it's a territory usually doing bad things. Okay. Do we know what is actually running this web pack server that's serving the assets? Is it yeah. Okay. So it's just webpack dev server. Right? This is what we got here. Yeah. So let's go take a look at webpack dev server. Yeah. I'm thinking the extra host setting in Docker Compose to do what Alex was suggesting. Okay. So dev server has an allowed hosts, which tells me that it's not it's not always gonna run on a one two seven
51:59 binding. That's something strange. Yeah. It's tricky. I mean, this the 000 is working because I can have it from my local machine. The problem is just to rewrite inside now. Well, an alternate option then is that you after the compile stuff happens, instead of letting Laravel mix actually change your hot file, you instead just force write it yourself. There's a good point, actually. Because we don't actually care about We haven't modified the hot file. It still says one two seven. No. No. That would have been modified by It can. No. The volume isn't shared between
52:57 those containers. HP containers only got the file because they've SSH didn't write it. So the existence of this file is what happens. It's not actually using the value to rewrite the pass. Is that something we can leverage? I think it is using the value for the initial page load, and then the rest of it is done with the hot server. So if you do a hard relash reload on this page, is it using eighty eighty to begin with? Oh, look. Yeah. And then it's the and then it's the server itself, the node web server, which is then returning
53:29 the new assets, like endpoints. That's annoying. Which means that if you were to go into the hot hot module server and look at the hot file in that one, that will have zero zero zero. And then if you were to change that to be eighty eighty or whatever at the moment. Now if you change that one and then reload the browser, it should work. But then as soon as you do a hot recompile, it will break again. So that's loaded everything. But now if if you change an asset, that file will break. Oh, it it it failed.
54:13 Oh. Oh, no. That's that zero dot zero dot zero dot zero is actually working. That's a red heading. Yeah. Okay. So we don't care. How is that working? I'm I'm not gonna argue. I'm assuming 0000 is just resolving in the browser to to local host. 000080800. I had no idea. That was a thing. So we're trying to fix something that's not a problem. Okay. Cool. So What what what goes on to this? Because I've seen those little red lines through it and got worried. Okay. Right. Right. And I assumed it was failing, but there's a 200 here. So
54:55 and we have a WebSocket connection. So okay. We got completely sidetracked there for something that wasn't a problem. So let's So the bit we yeah. Check it works. Amazing. So that made yeah. So that works without a PHP refresh. Okay. But we had to manually write that file in the PHP container to make it work. K. So let so let's fix that then. Right? So what we need for the hot reload scenario is it failed to exist. So if we take a look at how our PHP is configured, we mount all of this. So I mean, it's probably as simple as just
55:38 saying echo. Only if you're running hot, though. If you're not running hot, you don't want that file there. Because that file will make everything break because it will be expecting it to run through hot one when it isn't. But I guess we're deciding But but we are deciding. We but right now, we've got our node assets. It's always gonna run hot. So I'm comfortable. And this being the node Come on. Well, how else would we do it? Yeah. Shouldn't this be in the node assets target? Which runs automatically anyway. I mean, I yeah. I mean, I can do that. So at
56:16 least the code is script together, it makes a bit more logical sense. But implementation wise, it's the exact same. Mhmm. Alright. So I'm not gonna destroy my volumes, but I am gonna shut everything down. And that backup, then we should get live roll live hot reloading by default and everything work. If you don't destroy the volumes, how will you know necessarily that the hot file got written to the right place? Because it's They're not in a volume. Yeah. There's no volume. Okay. So I'm just gonna run make b shell. Our Rawkode is gonna handle everything for me.
56:52 We will need to just keep an eye on our logs on the node site. We can see the echo run. That's great. Can see it's a cross end node. No. We don't want it on the node server. We want it on the PHPFPM one, don't we? It is. It's just Kieran wanted to bundle it with that automation, but it does happen on the PHP side. So this happens with oh, no. That does run-in the That one's on the note. Hold on. Don't your key on. You're wrong. Oh my god. D shell here is the that's the PHP container. Yeah. That's our interactive
57:29 development environment and say oh, no. Yeah. And saved. And actually, it's right in that file locally on the disk, so it's gonna be available here for the container. But I'm I'm I'm okay with this step. It's assuming that fails and they get ignore. Yep. It is. It's not it's not, you know, wait, which means we can now monitor node again. Once that's happy, we should be able to oh, where's my Third time. Why did that? Awkward. We kept in containers, We're not using the volume. We're not using the volume. Okay. Let's just let's process the database. So
58:00 Adding database persistence
58:16 I'm sorry. I lied to everybody. We will make sure that we do persist this volume. We're using the named volume and bar lab my s q l. I think that's right. The way that we would confirm that is to do a docker image spec MariaDB. What version? 10. And you should see the volumes listed by the MySQL. So now we've got persistence there, which means I do need to run migrations one more time, but that will be it. I promise. Okay. So we should get home module reloading by default. There's our WebSocket. That's a really
59:20 Successful Hot Module Reloading
59:20 good start. I'm gonna modify our template one more time. Resources, JavaScript, pages, dashboard, view. I am live. Reload. Awesome. That's pretty nice. Alright. We got a couple of comments. Let's see what we have here. Tony says first time viewer, where should I start? If you're interested in Laravel and PHP, start with part one of this. This is part three. If you're really, really curious, there was a non Laravel specific one where we covered a lot of the groundwork with Docker. And if you go to let me do this live. Let's see how fancy I can get.
59:40 Viewer Q&A: DB Persistence & Series Navigation
1:00:06 With the rockcode.live/PHP, you will see all of my PHP videos. Thank you for watching. Now Is it worth having a Laravel save playlist on on YouTube. I am gonna add a Laravel tag. In fact, does Laravel work? Was I was I smart enough to go ahead and do that? It was. There you go. Rawkode.live/Laravel. You'll see we're live right now with this one. There's the part ones and two Laravel edition and Kieran and I also is that only three weeks ago? Wow. There's a deeper dive into the the general PHP server Docker Compose here before I had
1:00:51 my my brand in setup. Anyway, thank you for your question. Let's see. We've got well, it's in. Hello. Nice to see this live. Yeah. Very much live and very much when things go wrong, know about it just as well as we do. And I like covered up Keaton's face. Sorry, mate. And we got Robert's in oh, yeah. He's answered my question for me. Thank you very much, Robert's. Okay. I can't believe things are working. We almost we almost went down a rabbit hole that we didn't have to go down, but we fixed it. So. We know you got caught up by another
1:01:19 rogue file that exists somewhere. It's supposed to work. So Nice. Yeah. And who knows? We've learned to do that. Browser to zero dot zero dot zero dot zero results to local host, which is really convenient. Otherwise, this reloading would probably have been a lot problematic, and we'd have to specify the extra configuration. We'd have to contribute something to mix to say, I wanna bang on this, but lessing on this. Might be worth doing that anyway because I don't know how reliable it would be to rely on zero zero zero zero actually going to one two
1:01:46 seven. That's that's nice. It might be worth improving open source just in general. Whoever gets the opportunity to do that might be worth doing that just in general. Yeah. It probably depends on what audio network adapters are in or something. Okay. So how is this is this is this a fully working Laravel local development environment? No. No. So there's two there's two sections which I can think of immediately off the top of my head, would be useful. That is the there's something called the scheduler, which is just cron, and there's the queue system as well.
1:02:01 Assessing "Completeness" of Local Development Setup (Missing Scheduler, Queues, Advanced Tests)
1:02:24 Yeah. And then alongside that, there's a load of separate things which are useful, like search and things like that, which are sort of work based on things like classic search running or and there's also caching, which runs based on Redis or m m caching as well. And does Peng CRM have any of those components? Probably not because it's only a demonstration of the ping JS, like, library, which is it's not a fully flesh level application. That it is just a a demonstration of how to use, like, ping how to use inertia. Okay. So I think
1:03:05 Deciding on Future Episodes with a More Complex Application
1:03:05 the best protocol here would be that we find a more feature complete Laravel application for another episode where we tackle scheduling cron and stuff and focus on what we can do with this example so that we can kinda call it as complete as we're gonna get. Mhmm. The only thing I think we're missing right now is how do I run the tests and how do I deploy it to Kubernetes? Let's do tests. Test test should be easy. Right? Yes. Makes tests. Doesn't it? We we don't have to deploy it without running the test. So Test test.
1:03:27 Running Unit Tests (Make Test)
1:03:37 So test test is working. Yeah. Oh, that that does remind me that there is a I I don't know again whether or not PingCRM covers this, but there's a browser based testing in Laravel, a package called Laravel Dusk, which makes use of Chrome WebDriver to, you know, go through click buttons, all that sort of stuff. Doing that in Docker could be a fun topic. Yeah. Definitely. Because when things go wrong, it gives you screenshot outputs. It it does all that sort of stuff. So you'll need to have, like, yeah, probably some heavy debugging in that one. So I have
1:04:16 run Puppeteer and containers before. That will be fun. But not part of the Ping CRM project. So I think the vibe I'm getting right here is we need another application to continue the CDs on. But let's still continue with what we have for Ping CRM. We still got, what's this, another twenty minutes. Are you gonna contribute this back at the end, though? Sorry? Are you gonna contribute this to Ping at the end? If they want it, they can have it for sure. Okay. Woah. What's going on here? We had this last time. This is because
1:04:53 one of us, maybe me, has the file open. It was a sharing issue last time. So Yeah. I think it's just a weird quirk. Okay. Now let's talk about this. We we we don't have a Docker file right now for building this and distributing it. Oh, we do. Okay. So let's take a look at our Docker file. How far do we get with this? Could be four. Alright. We have a base layer where we add our dependencies. We've got the dev layer, which is the one we target for actually working. And then we build an FPM container to ship
1:05:10 Optimising our Dockerfile layers and caching
1:05:30 to prod, which we kinda started on. Oh, we have tests. So we have, you know, that but that's really important as well that we put this inside the Docker file. It means when we do the Docker image build, we're running the test that can run happily inside the build process. And actually, we don't build an image of those test fail. If that step fails, there's no artifact. There's no Make test target. And we have a make test target. Ta da. Because it was only at one point. So that's good. Yeah. So after test fail, I mean, we could always
1:06:01 Running Tests Within the Dockerfile Build
1:06:04 artificially make a test fail if we want. But when I do a Docker image build, we we're never gonna get a container image out of that because this fails, which is which is good. What we the guarantee of the the process we're trying to encourage or enforce here is that if a Docker image build passes, that is an image I can ship. Assuming or I can also run other types of tests against that which are also equally important like the puppeteer test. When I run integration or end to end test, the chances are I want to use the artifact that
1:06:34 I'm gonna ship to prod and run the test against the and then tear it back down. No. We're not gonna be able to do that. I think we definitely need to try and find a more feature could be application that has all of these additional components. But we can ship this to we can ship this to our fake prod of Kubernetes. So the test that passed by the time you build the image will be sort of internal tests, the self tests? Yeah. And if anything that doesn't have dependencies, I would run-in the part of the Docker
1:06:48 Fixing Production Dockerfile Composer Steps
1:07:00 image build process. Like, I don't want my CI system to be complicated. My CI system should be Docker image build and that's it. All I want it to be. And I target build f p m, which is our production PHP image that we're building here. But we never got the also order working, I don't think. Why did we comment that? That's just production optimization. You don't want that in development. But that's just the production image we're building with that one. Yeah. So it's spelled FPM. I mean, it's purely named, but it's spelled FPM for production.
1:07:38 That's what we're supposed to take away from that. Yeah. I don't because the I think we were hitting some more than we fix it. The problem was we then we we actually decided we didn't want composer available in the production image. So we actually wanted to extract the composer installed to dump out a loader to its own step and then have a production image that pulls down all of the vendor directory and stuff when we just never got around to it. This is annoying thing with Composer is that you have to have all of the files there to optimize
1:08:06 the auto loader because it scans all the files. So whereas you can do the vendor directory generation just with the composer JSON. So so then so you could move the composer install out, but the dump auto loader has to happen after you've copied everything. Yep. Okay. So we also have a question here. Sorry. Sorry, Kieran. The way we've got it there, we're copying the composer JSON, and then immediately, we're copying the everything on sixteen and eighteen, which is a bit messy. Yeah. I think we were just kinda faffing around with it at that point. We
1:08:31 Viewer Q&A: Docker BuildKit Parallelism
1:08:44 haven't really decided what our what our kinda goal was. So Leo has popped in one day sorry, Kieran. I'll just keep covering you up. One day say to put in the test and so Docker fails, you can't paralyze steps in CI. That's half true. There's some changes that do allow you to do that. So I'll talk about that in a second. You followed that up with unit test. That's not a problem. Integration test can be slow. Okay. So if you're using build kit, you do get parallelism on the Docker image build. So you can do Docker build kit
1:09:18 equals one Docker image build dash t. Let's call it PHP prods and I need a target of build m dot. And I run this. You'll see the output is different than what you would normally expect. And that's as parallel. So what that means is it creates a a bag, a graph of all these steps. And if there are steps that do not have any dependents on each other, they all run on their own core and parallel as quite fast. Once you build the image, this is why the unit tests are okay here because we're just running them to confirm that the image
1:09:56 that we're building, it's almost gonna work. You know, we've done some testing on it. And then you could parallelize integration tests however you want. Whether that be through multiple containers or given a container access to multiple cores and then that we have the internal workings of it. Like, whatever that is, it's fine. It depends what kind of integration tests we're talking about as well. Because if you're talking about integration tests that are testing a module inside your PHP application, you can still run them pretty quickly as part of this build. The ones that are normally slow are the
1:10:27 ones that are depending on IO, and they're the ones that will need other containers involved to to to run the test. So they're the ones you'd be testing afterwards after the build. You know, running any tests that need a database because you have to have your image and your and your database. Exactly. Alright. Let's fix this build step, deploy it to Kubernetes, and we'll call Pang CRM finished. We are done with ping. David, is there any sensible way of running build kit with Docker compose yet? Yes. You just have to expose that environment variable, which is actually what I've got. So
1:10:47 Refactoring Dockerfile Composer Steps for Caching
1:11:05 if I do a ENV grab build kit. If you just set this in your shell environment, when you do a Docker compose build, you actually do use build kit. No? It doesn't look like build kit. I don't think you do. Oh, no. That was the old step. So I think in your CR, you might not be using Docker Compose, which is fine. And you'll get that password. I we got it. That's a nice way of running build kit. Feel very disappointed. You can manually do oh, woah. Ah, there we go. So oh, because Docker Compose doesn't depend on
1:11:51 Docker CLI, I don't think. There you go. Docker Compose needs the daemon. But I don't think it needs the Docker CLI, and this fails out and uses the Docker CLI. Either that or docker post has a build kit baked into it. But, I mean, build kit is just a library that docker uses when you do a docker image builds. Compose is probably using the same library and it it may not actually be using The name of the docker daemon. I think it's running the CI command. Okay. So yeah. There you go. Compose. I don't know that. That's cool. Compose Docker CLI.
1:12:30 I'm just gonna add that to my shell. I always wanna use BuildKit now. In fact, Leo already popped in. Hey. Excuse me. Alright. Let's separate this out. What we're gonna do is I know the naming here is terrible, but I mean, we we we've already worked out that we need everything, right, to do the compose or auto loader stuff. So gonna do this in its own step. Yeah. Sorry. And oh, yep. Sorry. I made the wrong one. And then down here, we saw what that worked there. We can do from and from build FPM composer, and we're gonna copy everything
1:13:33 to the same location. The only reason we're adding on this extra step is just so that composer doesn't make its way into this image when we ship it to production. So just it removes an attack vector, essentially. Then our tests run. In fact, our APM does this here. We already have that step. We've done here, but then we've got our test here. Getting so confused. We don't need this step, so that can go away. And we ship our build FBM. So we can build this and deploy that to Kubernetes now. So let's just do that.
1:14:14 The slight annoyance is that it means that it's gonna read the composer step's gonna rebuild every time we touch a file. Like, it's nice to do it'd be nice to do that two step thing if you depend on I guess as this is for prod, we don't care as much. Is that right? Maybe it's I I guess it would it's still be nice to use the build cache. I mean, there are you know, you can do a cache from on a Docker CLI to reuse a cache from a previous build. So let's try and make that work. So
1:14:26 Testing Refactored Dockerfile Build & Caching
1:14:46 we can just say copy composer dot JSON. It's closer dot JSON. We'll do the initial composer install. Then we'll copy everything. And then we'll dump the auto loader. Let's see if that works. So and the thing that could go wrong here is that this copies over our vendor directory. But we might be okay because it should do a merge in a Dockerfile copy rather than a replace. So docker image build, we call this PHP prod target or build FPM angle. Kinda where we are right now. What we should see now the first time is the docker compose install will take a
1:15:27 little bit of time. If we rerun that again, it should work. Yep. And the with a very good comment there. Because I didn't change I didn't change ownerships of the fail, and I do have this all running as root. So we'll we'll bring that in once we confirm it works. I should have maybe pulled in composer too. Get the speed improvements. There's a pull request on there at the moment that offers another massive speed improvement. Oh, really? On addition to what they already optimized for composer two? Yeah. It's not merged yet, but it's somehow in the solver eliminates
1:16:12 loads of the nonworking root combinations to check. So they're seeing, like, ten seconds going to four seconds. I think Leo was trying to push my buttons today. Make sure you copy the composer dot lock as well. Mhmm. No. I mean, I think I've ranted about this in every episode so far in the series. I we don't need a lock file. Docker is our idempotency. Docker is our auditability. Everything that we need to know is is there. Lock files, I would argue, should only be used on a non container based deployment, which means you also don't commit a lock
1:16:50 file into git. I'm not gonna rant about it again, but maybe we should maybe I'll organize a bigger conversation around lock files and container workloads in the future. I think what we said was if you know that that exact version that's been tested is going to production, you don't need to connect the lock. Well, the lock fail is giving you a guarantee that if I do a composer install, again, I get the same dependencies. When I build on a container, I don't need that semantic anymore. So all you're really doing is restructuring your you're making that very explicit operation to update
1:16:51 Discussion: Composer Lock Files (.lock) in Container Workflows
1:17:24 your dependencies. Whereas in a container based workflow with good continuous integration and tests, I always wanna opt in to those security updates and updates to my packages in general. So I would never, I personally never commit a log file of container based development. That's an argument for not putting the log files the log files in your container. And in git. That's not an argument for omitting them in git. Why? Because putting them in git guarantees other things that aren't to do with production. Things like all the developers are working off the same set of dependency.
1:17:58 But I want a developer to get those updates just as quickly as my production image so they can actually notice the errors locally before it gets to production. I mean, you should always be using the latest and greatest versions within semantic reasoning, of course. Like, you're not gonna go and upgrade from three to four. But I always wanna get patch updates and minor updates in my in my code as fast as possible. That way, it's not this horrible ceremony. It's all we're gonna update the packages. That's the first of the quarter or whatever. And you have this whole thing
1:18:25 where we do it and then check for run our tests and check for bugs and, like, really, you just wanna do this often as frequently as possible and get that feedback loop as tight as you can. So does that mean in your compose JSON and your packages JSON, you've got just star as the constraint? No. I still use semantic version. I still say get me two dot x. I just I'm never gonna do a backwards compatibility major update. I will do minors and patches automatically though. So our composer auto rules are failed because there's nothing in the database seats.
1:19:01 Troubleshooting Dockerfile Composer Scripts Issue
1:19:01 Oh, okay. Now I remember. The the problem was this is doing an auto loader by default, wasn't it? It was actually configured. You're looking at one place. Close it, Jason. Yeah. And we changed it to false. It was true. That was failing for when we wanted we were trying to build just the vendor folder first. Then then oh, it's just annoying, isn't it? This is when we were sort of using the container for dev as well, was it? Hold on. I can't remember what command. So this is just running the install without the dump bottle loader.
1:19:51 Mhmm. And install needs other things. I I'm not gonna fight that because we're almost at the end of our session. So we will go back to copy the world. And we should we can always do no scripts. Right? Let's do no scripts. Let's make this work properly. It's because, like, some post install hooks, but then expect us to be there. I can't remember the flag. Is it just that? Yeah. Yeah. K. But we want all we wanna do is take the capabilities on, download our dependencies, and don't do anything else. Everything else will happen in the dump hot loader. Or do
1:20:01 Using --no-scripts for Composer Install
1:20:29 I need to do another install to get those scripts to run? Oh, it's like for Well, let's rebuild that again. The no scripts should stop the the database seed stuff from triggering. We will want that to happen slightly. We want that to happen when we bring in the rest of the code so we can leverage to build cache. So again, unfortunately, this will take a few minutes. But I should be able to run this again when it is complete, and we should see the cache used for the dependencies. The nice thing about that is when I
1:21:02 ship a production image to a container registry somewhere, I can use the previous build as a cache from that if the composite dot json hasn't changed, that wouldn't rerun that step. Even if the local build agent is not aware of that cache, which is really nice. I like it. Cool. Now we just need to kill another minute and a half while that runs. What would speed up your Sorry, Kevin? What do you add the step that makes it Composer two? Is there a an image for Composer two? It composes self update. Oh, there might be
1:21:26 Using Composer 2 Image
1:21:36 an image by now. They want some sort of alpha. There's a self update command you can do that pushes it. Oh, there is. Okay. Yeah. We'll we'll use Composer two then once I've shown that the cache works. So cache form lets you cache from a another remote image? How does it work? Yes. So I will answer that. I will also point out Leo's question. You are right. We discovered that in the last session that the no loader is on by default, but we actually did disable it in the composer dot JSON. Tada. Because it did it did annoy me. I
1:22:19 did get frustrated. That that's to say it was optimization. It might still dump auto loads dot PHP. So it might still do some work to make the auto load, and then we'll we're gonna have interview and optimize the We're upload PHP files. Right? Okay. I guess we're gonna find out in a moment. And if that is gonna fail, we're definitely gonna want composer too. So spring that in. Yeah. It's generating a load fail. Damn you. Okay. So what we're actually saying here is we we want no auto load. We'll do it explicitly on our own step. There's no
1:23:11 auto loader. Never. Whatever. Last time. I'm not running this command again. This should be quick. Is it not colon two? Yes. It is. Thank you. Oh, well, it's That's the last time I'm running it. I'm not running it again again. Okay. Right. Cool. No worries. That's the cool comparison. But we did it quick on the install. Oh, and the output's different too. The composer too. I know we got some parallelism going on there. That is do you actually built into plugins for composer to make it work parallel? Is that built into composer too now then, I
1:23:54 guess? It's got faster so solver in parallel downloads. It's it's much faster. I think. Yeah. Yeah. You're right. Just this one, whichever one has in their Docker files. Okay. So what's important here is we got past the composer install. We're now in step six of seven, which failed because of a flag. The good news is I should be able to run that, and it should resume from step seven of seven. That's cached. Perfect. See? I love it when stuff works. I mean, I have a production Docker image. And now if you were to change a
1:24:12 Production Docker Image Build Successful (with Caching)
1:24:30 PHP but not a composite dependency, does it use the same cache? Yes. It will. So if I change where's the where's the PHP code app? Right. Let's change kernel dot PHP. I'll just say hello. When we run this, our composer dot JSON and composer installer should be cached. Oh. It's because you oh, why is that copied? Oh, no. It it worked. Yeah. Yeah. That that was right. But we want to do the composer install stuff because nothing changed on composer's requirements. Yeah. But it was cached. See? Composer composer install. This is the second composer install that ran.
1:25:14 But do we want to Well, we got a second one. That's for the To trigger the To trigger the scripts. It was It did do its job. It did use the cache. But it did something. It did seven seconds of stuff. Oh, yeah. It still run the scripts. It didn't it didn't fetch the dependencies. Do do that again. There's a progress bar. Hello too. Right? Well, the output format. Oh, it's downloading stuff. Alright. So how can I tell a composer to run oh, that's the dev dependencies? Why is it download in PHP unit? Because you have a dash node dev. You
1:25:57 want double dash node dev on there. I thought I did. On both? Oh, yeah. Can I just run composer scripts? Is there a way that I can just check out the scripts without the install? You can close the help without dash dash. Excuse me. You've got that one so many times. Oh, because it That is Yeah. Okay. This. Why does it not list subcommands? To compose a list. It says in there somewhere. Compose a list. Okay. Run script. Run what? Run run. Okay. Composer run is what we want. So let's remove that. I don't know why it's fetching those
1:26:56 dependencies. I know. It's because we're doing copies it's because we're doing copies copy everything, and we're maybe can copying the compose a lot from the host. Well, you have to specify a script. It doesn't run all scripts. That that's fine. Let's leave that as That's what you care about. So, David, in Docker ignore, is Composer locked there? If it's not, then what's happening is composer lock's been copied from your host machine. In those steps, and that's why it's in a different state when we do the second install. Gotcha. So if we run this once, we should get complete cache. Yep. And then
1:27:46 if I modify PHP again There we go. Okay. Cool. So we got a cache even even though we modified PHP, we did not have to actually technically do a composer install again Except for the one where we're running the scripts. JSON file, I assume this will then start downloading stuff. If we modify composer dot JSON, it will invalidate the cache, and it will refresh all of the dependencies. Even if it's just How do you compose a cache for dependencies that were already downloaded? It uses the image cache if it is available. So yes. Now, of course, I've I I'm
1:28:30 it depends on my CI system, but there may be a I may wish to run this without a cache. It's really common for production builds at least. The okay. And actually four, so I complete rebuild of everything. But composer itself comes with its own cache as well. So if you've got 10 packages, nine of them are the exact same version down to the patch version and one was changed. It would only load it would load them in from cache and then download the new one. Not in the Docker image build, that cache won't exist. Right.
1:28:59 And it'll be there. But if you're caching from a previous image, that might happen. If you cache from a previous image, then yes. It will have access to some compulsory cache. And if you had a a named volume with it, then potentially use volumes with the Docker image build. So that's it's not it's not an option. Yeah. Running this run time after the images. So Right. Okay. Okay. And to make build secrets because someone mentioned it in the chat. Was there a question on secrets? Someone just mentioned the dash dash SSH option in build. Okay. Do we have time to convince the
1:29:26 Discussion: Docker Build Secrets (SSH/Private Repos)
1:29:47 secrets today? The short version is if you want to we can open it up from a private repo, you have to do some extra stuff using, I think, a build secret. So it's got your SSH keys. Relatively new. What? About twelve months old? Doesn't work with Docker Compose yet. Sorry. Let's not talk about it. Here we go. Yeah. So if if you do need access to stuff like that, there is a way to do it. I guess, when we tackle the the non ping CRM Laravel application and we are doing a bet more with the images,
1:30:27 then we can we can handle that. I guess, the main use case is, you know, if you've got private composer. Like, for your composer install, it won't work without that secret and you don't want to do you don't want that. You don't want your token or your secret inside the image which is available for people to kind of hack or inspect and steal it. So you would choose a build secret which is then a a way to inject the femoral things into the build process. But no requirement for that right now. We won't tackle it.
1:31:00 And just with it being half two, we're not even gonna be able to do the Kubernetes deployment but now, but we're in a pretty good shape. And I think we did do a Kubernetes deployment on the first episode, didn't we? Yeah. Okay. Cool. So that that's still available to people if they wanna check it out. I will push this code. We will call ping CRM complete. We got everything working that we wanted to work. I will talk to Alex and Kiran afterwards. We will find a more real world application that has all these extra components so we
1:31:01 Wrap-up: Ping CRM Setup Complete (for basics), Next Steps with New App & Kubernetes
1:31:26 can tackle scheduling queues, integration tests. I think it's really important. And we will have an episode dedicated to actual Kubernetes production stuff. Yep. Awesome. Great. Thank you. Sorry? Great. Alright. Awesome. Cool. Well, thank you both for joining me. This was another pleasure again. I hope it was useful for the people at home. We will continue this. I don't know when, but we will tweet out very soon. Thanks again. Have a nice day everyone. Bye. Bye.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments