About this video
What You'll Learn
- Configure a local Laravel stack with separate PHP, NGINX, and MariaDB containers for repeatable local development.
- Split Node.js asset compilation into its own container with shared volumes, so frontend changes recompile without PHP rebuilds.
- Tune Docker for Mac volume options, such as delegated and cached consistency flags, to speed file syncing.
Ciaran McNulty returns to refine the Laravel dev environment from Part I: tuning Docker for Mac volume performance, splitting Node.js asset compilation (Webpack, Laravel Mix, Tailwind) out of the PHP container, and tidying the Compose and Kubernetes manifests.
Jump to a chapter
- 0:00 Holding Screen
- 1:10 Introductions
- 1:50 Context / Background: What are we going to do?
- 11:00 Looking at the development environment we provided last time
- 12:00 Docker for Mac volume options for better performance
- 22:00 Making asset / nodejs asset compilation a better experience for local development
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
1:10 Introductions
1:12 Hello and welcome back. Hello, Kieran. How are you? I'm good. Thank you, David. How are you? Yes. I'm very well. Thank you. I am looking forward to part two of the Docker Kubernetes and Laravel edition. It's gonna be a tough one today. I feel like you have to be really high energy after your intro video. I have considered so many times making a joke about the polarity between the higher energy intro and then my completely low energy personality. I think we shared that. So, yeah, we've we've got lots of different things we could do today, haven't we?
1:50 Context / Background: What are we going to do?
1:51 Oh, we do. So let's let's build a little bit of context here. So this session is we are trying to show how to take a Laravel application from your local machine, produce a container based development environment using Docker and Docker Compose, and show you the real kind of day one deployed to Kubernetes scenario. What we tried to do in the last session was squeeze all Adam to two well, two sessions now. That's actually I guess this part three, we did non Laravel, then we did Laravel, and now we're back to continue with Laravel. Mhmm. And I think the problem with that
2:25 is we tried to do all of and all Laravel stuff in a single session. And I think what we've realized is we really just need to kinda slow down into this bit by bit. Like, there's a lot of different things that we need to get right to really produce a developer experience that's gonna work for people and not be too cumbersome. Yeah. Although, you know, not to put people off, but we've not spent that many hours on it. It's that we've done across three sessions. But if if we were at work and not talking through things, that that and I if
2:53 this was lunchtime and I spent three hours doing what we've done so far, I'd feel like it was a good day's work so far. Oh, yeah. Definitely. Yeah. Yeah. That that's that's true. And I think now that we're getting into the individual components of the Laravel application, you know, we we try to kind of build a full pipeline on the last one, and now we can go back and clean up a few of those artifacts that maybe aren't as great as we wanted. So, John, we've got what we've got to. We've got a dev environment.
3:22 We're building the front end assets. We're populating the database. It's we deployed it to your local Kubernetes. Right? We did deploy it to Kubernetes. Yep. We left a few things to the imagination, though. So I think if I just let's pop up my my code. My work code. This is pair programmed. So we put together a Docker proposed file. We have an engineized container that is delivering our front end assets. We have a PHP container that is delivering the Laravel code. And we have MariaDB, which has a basic health check that allows us to really provide a local experience for
4:06 working in containers that should be one command using those health checks to handle the orchestration component. We have a Dockerfile with multiple layers. We did our best to put together a base layer with dev tooling that we would require and production tooling. So we've a PD with mySQL extension. We then augment that with a few developer tools. Right now, we are bundling in Node. Js and NPM to do asset compilation, but one of the major themes for today. We this is not something that we would expect or this is not something we think is is an ideal
4:38 what else do we have? We have then the FPM image, which we're using to actually ship to production. We have a test layer, which we're gonna run our test against. And we have some asset compilation to produce an asset production image, which can be deployed separately, which we did show with this really primitive deployment YAML for Kubernetes, which deployed our FPM and Nginx containers site by site. Mhmm. We added in because it was late in the last session. Oh, this is how we would draw on our database migrations, but we didn't actually run that ourselves.
5:20 That's something that we will need to tackle at some point. But we are gonna use a concept code and a container. And if I remember correctly on the last conversation, I said there were really two ways to handle this. The other way would be that you could spin off a job that ran alongside of your deployment to handle the migrations asynchronously, which is a flow you may wish to adopt as your migrations are additive. They work in a way that no, like, no new queries yeah. No new key queries could potentially break with an older version of the schema. Yeah.
5:56 I think that's right. Yeah. That's a lot. To do to do it as part of an init container, the migrations have to be safe to run more than once. Right? To do it as part of an an Internet container. Yes. So your your migrations definitely have to be idempotent. They're gonna run every single pod that deploys. But more importantly, I guess, is that they have to be able to handle some sort of locking mechanism because there is the there is the chance or likelihood that two pods will try to run at roughly the same time during that deployment.
6:38 Now we did briefly talk about update strategies and how we can use that to limit that risk. But really, you just wanna be able to make sure that your migration says, hey. I'm gonna lock this table right now. No one else gets access. It will put up that latency on other pods starting up, but it definitely is the safest way to handle our migration in this kind of situation. A lot of migration libraries, there's obviously different ways to do it. They'll have one table which shows what version of the migration you're on, and that's the only one you need to lock.
7:04 Right? So you wouldn't be you wouldn't be contending necessarily with live transactions happening on the database for normal traffic. Yeah. Exactly. Spot on. Like, it's I think I think every migration library I played with in the past has had a migrations table which gets locked on a migration run to protect it, which means your application will still handle traffic pretty well. Alright. So we know our Docker file. Why were we installing NPM? Is that just left over? Because it looks like we're using an NPM image to build the assets. Oh, it was on dev, wasn't it, that
7:43 we were installing NPM? Yeah. We do augment our development. So we can figure out a way that we could run this through a d shell environment so that we could have, like, an interactive terminal. I still think that's a really good way of doing it, but what we didn't cover was automatic re compilation of those assets and then the ability to deliver them for a different container. So it may be that we break up that environment and well, we could talk about that in a minute. I I think we we will separate that. And the d shell will only be for
8:14 the PHP commands because those are the ones that I think are gonna run interactively, and we'll try and move the MPM stuff to be a more automated fashion. Which is a diff the which which is the difference that comes from the JavaScript stuff being sort of built, compiled language, whereas PHP is interactive. So there's a big difference, isn't there? There's an explicit step of, now it's ready to execute in JavaScript. Yes. Exactly. You know, those those assets are assets are being delivered. There's no like, it's not like we have to go ahead and run composer install. I
8:50 mean, we do have an NPM install, but we'll handle that separately. It's not like we need to do composer cache for you. It's not like we have to do database migrations. I mean, the only interactive component here is Laravel. Our Laravel application is what we're developing. The assets were something that enriched that application with other things, and we can push that to the site and and have that work in a slightly different way. Yeah. Yep. Okay. So what we're gonna do we I think we're gonna focus on the the asset stuff today. That's where most of the questions came
9:19 via YouTube comments or Reddit or Twitter. What I will suggest is for anyone that is watching, if you want to ask questions, you can leave them on the YouTube chat live, and we will do our best to ask them. If you have questions that you wish to ask after this video or you're not watching live, there's a link on the bottom of the screen. And if you're feeling brave and want to come and join on and ask a question, then we we're gonna try and encourage that too. So you can take a message and just follow YouTube. Either way to go.
9:48 Peter has a very loud plane overhead. I think it's a helicopter. I'll just I'll just mute Keith. We don't need to hear from him anyway. So sorry, mate. Alright. So Kieran has told me that my link is missing in HTTP. It's not. I thought it was cool. I thought that was Oh, right. That's on purpose. Yeah. I mean, everything that like, I wanted people to know it was a URL, so I put, like, colon slash slash. I didn't think that HTTP or HTTPS was really you're making me do all my things there. Anyway, I thought it was cool. That's it. That's it.
10:29 Alright. You want to you want to protocol less URL, you don't put the code long on. Slash slash? Yeah. If you you do a hyperlink like that, and it'll stick to the which is very useful if you're serving your site under HTTP and HTTPS, for instance. Okay. Anyway So today, I've learned something. Alright. So let's take a look at our current development environment for this application then. So what we said well, I don't know. We did we use Maker just? Because I always change Maker. Okay. So what we said was we wanted this d shell
11:00 Looking at the development environment we provided last time
11:12 environment to provide something that felt like I was developing locally, but with the parity of that container based deployment. Mhmm. And that means I'm inside the container right now. What do we want to do? In fact, let's just split this first. So I could do a DCPS to see what I have running when I do the make d shell command. So we do have a MariaDB database self. Good. We have an NGINX service static assets and, hopefully, proxy in any request for PHP back through to the FPM container. We have our FPM server running, and we have an interactive
11:55 shell. Yep. Which is the same image, right, as the Exact same image with the same volume notes. Yep. Just working as is. So what we need now is confirmation that this application is working. So and I'm now typing h t t p s. Sorry. Local host eighty eighty. Hey. Alright. Okay. Not too bad. We haven't actually logged in, have we? We're just trusting it works. The database is there. Looks like the assets are compiling correctly. Yep. So hello there. Welcome to Ping CRM. We can artificially create five hundreds and four hundreds. We have the ability to bridge organizations.
12:00 Docker for Mac volume options for better performance
12:55 But this is not the fastest application right now, and that is because I'm developing on a Mac. Now there is a flag to speed that up. So maybe that should just be the first quality of life thing that we're gonna do. And there's a whole new mechanism in Docker for Mac that does some sort of syncing, but I've not tried it out. Say what? There's a new file system. Something RPC. You got some link goodness there for me to check out? Guess I can just Google it over for Mac. Yep. If you're on the RPC.
13:39 Think RPC. Yeah. Not that. It's in the new edge thing. So what it does is instead of it it in the container uses a few the user land fuse file system that has some RPC thing. So it's syncing the Mac file system events. It's then pushing them to Docker. It's not actually that much faster, allegedly. So people I know have used it said it's about 10 or 20% faster. Okay. I am gonna enable the delegated feature that I am somewhat familiar with. Yeah. Let's see if we see if we get a little bit of improvement.
14:27 Do I spent sorry? Do a quick benchmark first before we change it. Let me just get the documentation so we can share that to the Docker for Mac delegated folder. There we go. Okay. So And people get very confused between delegated and cached. Yeah. We'll try and cover that too then. So how do we benchmark this? What we wanna do is time how long it takes for a curl request, I guess, to pull down that index page. So let's see. Localhost, it it it it time. 22.4Seconds. Although, that didn't give us a login page, so let's render that too.
15:23 Alright. It looks like we're run two point eight seconds. It definitely felt longer than that here. Is it not? Maybe there's some sort of cold start booking. I wonder if Laravel in Devon caches things. Do you know what I mean? Maybe that was just under three seconds. Right? Okay. So let's see if I can get a little bit of performance out of that then. So if we find delegated there we go. These are the three options that we have. These are, I believe, max specific things that change the way the fail synchronization well, they don't change the way it works.
16:05 They change the expectations on requesting fails from the container side. Mhmm. So by default, I believe we get consistent. That means whenever I modify a fail on the host and I then make a request against that container, it is going away and until both of those fail systems are in complete sync before responding to any of the requests coming in. This means that if I modify what did we get on this page? See, I want to change dashboard. Wait. That's not as easy as I'm hoping it is. Let's assume this is the template. Go away. Okay.
16:51 Have I saved this from my oh, I had log in, and I'm not logged in here. Going well. Okay. So oh, it didn't. Oh, Windows, the default behavior has not changed, or have I just changed the wrong template? Anyway, let's talk about it first. Then we've got cached. Cached just means that we are gonna trust the host container as always correct. Yep. And what that means is that we'll happily take a little bit of delay in a response. Delegated means just invert that. It means whoever's in the container, just use it. Now from a performance point of view, just always using
17:38 delegated means that when you're working with your application, you're always gonna get fast responses. It may mean that you have to refresh the page to see that new change come in just after. So it depends on how quickly your workflow is between changing something on a template site and then moving over to your browser and refreshing that. So I feel from a experience point of view that fully delegated is better because I would prefer that the page responded faster even if it is not entirely accurate for the changes I've made. I agree with that. I think it's
18:09 a no brainer for stuff that containers writing. Right? So if you're mounting a logs folder or, like, a temp a cache folder out to your host, delegated is what you want because there might be a slight delay in you looking at that file on the host, but you want the you want the Docker file system to able to write to that quickly without waiting for updates. It's also it's less of it's less obvious, but I think it's the right behavior for the code you're mounting because in your typical workflow, you're gonna hit save, and then you're
18:42 gonna take a small amount of time to switch window. And most of the time, the sync will have happened before you get there. It's really unlikely you're gonna be able to run it in the few microseconds it takes. And this is a problem for PHP specifically, right, in other interpreted languages in a way that it's not for Go Because if you're compiling Yeah. You if you've got loads of source code and you're compiling it into a binary file and the binary file is the thing that you're mounting into the container, that's one file to sync.
19:10 Whereas a typical JavaScript not JavaScript. A typical sort of non minified, non compiled JavaScript library or PHP library. There's thousands of files, lots of dependencies that all have to be synced before it works. Yeah. Exactly. I I want a lot of like, compared languages don't suffer this because, generally, the completion steps is just explicit. People are familiar with it. They have to do it anyway. And if that completion happens when a Docker image builds, then the tooling knows to do that and restart the container. For interpreted languages, yeah, this is the this is where the pain points are because we
19:46 we're working directly with the source code itself. And it's the file count, isn't it? It's not the size of the files, specifically. Yeah. And if you've got 10 to 10 to small files, like, you know, vendor directories, then that becomes a challenge. What a lot of people do is move that vendor directory into the container using some sort of container side volume. These are all things that we'll try and cover over the next however many sessions we we continue with this. Yeah. I I mean, to be honest, for me, because I work on a Mac as
20:14 well, and it's it's quite a slow Mac, I often don't for instance, I might only mount the source folder or I'll actively be mounting the thing I'm working on, but it involves a bit more sort of thinking about which files are on the host, which files are on the folder. But you can get even without many tweaks, you can get quite decent performance just by not mounting much stuff. You don't need to mount vendors unless you're actively editing your composer config and trying things out. I have a question for you, David. Is there a way to say mount this folder
20:51 except for these subfolders? Oh, that's tricky. No. There isn't No. I've tried that so many times because it would be really nice to be able to say mount dot but not vendor. Unfortunately, that is a challenge. And then you'd you ran into this weird checking and egg problem when you do want to move your composer vendor folder to be something that only exists in the container through volume, the where the local one over sits on the top of it anyway is is really frustrating, actually. It's not a nice developer experience. It's not really a way to make it nicer. It's
21:29 just yeah. It's just painful. So if you only want some folders mounted, you have to list them out in the Docker Compose explicitly. You need to list everything except Blender as I said. You would. Yeah. I'm not familiar with a way of yeah. I I don't think it's on the on the way of handling that. Yeah. Thinking about how Unix mount points work, there's probably not a nice way to do that. So I've made two changes to this view template. I'm not seeing it updated here. So this is not the developer experience I'm hoping for.
22:00 Making asset / nodejs asset compilation a better experience for local development
22:11 And it seems to be So you're already seeing them on host? Yeah. Yeah. Oh, no. This is a view. Ah, this is asset compilation. It's a building. Pardon me. Alright. So we need to fix the build pipeline in order to get this experience that we need. It's a good illustration of why people want it. Yeah. That makes sense. Mhmm. Alright. Let's jump down to our Docker Compose because what we're seeing now is that this needs to change. Okay. Now that probably means Using a target of our own? I'm trying to work out whether we still
23:07 want to go through engine x and has that consume our volume from our node container that runs in a continuous loop, compiling the assets whenever things change. Like, it's really easy for us to That makes sense. So let's start naively, I think. Sorry. Yeah. Yeah. I was assuming we would be doing this as part of Docker build, but no. We don't have to at all, do we? We just need something that's constantly doing the build into a into a volume. Right. Yeah. I think so. We wanna be able to they wanna be able we wanna
23:43 allow them to change these files whenever they want, which means let's just fall back. Like, whenever I speak to people and they're trying to adopt containers, I say just try and use the same tooling. Try and have the same workflow that you have, but use containers so that you have that party. And from my experience of working on the the node ecosystem is I would just run, like, an NPM watch. I would watch my assets. We can Yeah. Do all the completion. And we need to have that same workflow here. So what we'll do is we'll start very naively.
24:14 We're we're gonna provide a custom NGINX that has node available. No? Oh, you're looking at me funny. Okay. Then we won't start naively. We'll just go fill Then we end up with two of them anyway. One that's running this watch entry point and one that's running h t web server. Right. Right. Right. Okay. Let's add a let's add node. So And and we've already we're already using the node image in the in the live, so it's sort of bringing us Alright. So we're gonna add node. We'll use the same image we were using on our
24:47 Docker file, which is just node 10. Excellent. We're gonna add a volume, and we want to mount how is the directory structure here? Are we doing a dot? We didn't bother working out which files were important last time. We we we did we went around that we went down that route of only mounting select things, and we couldn't work out which ones are important or not. K. Well, like we said, we're gonna do things slower this time. A bit more explicit. So if we take a look at our our package of JSON. Yeah. We are using webpack.
25:27 It's got a watch. So this is what we wanna use in development. It is we're m p m run development that is doing this. This is Laravel Mix, which is a wrapper around webpack. Leo told us that last time on the chat. But there's no directories here. They may just be working against the same assets. We found out last time that the tailwind config JS was important. It was important. There's there's another one. The pack package dot JSON, obviously. Yeah. There's something else, I thought. I think this resources thing well, that's what PHP in it too.
26:23 I think resources might be enough plus the package JSON plus the tailwind plus We can find out then. Okay. Let's go back here. So we're gonna do package dot JSON, which we know we definitely need. Set a working dir of slash code. I will call slash nodes to remove any ambiguity between what we do with PHP. We also need the tailwind config dot j s, which we can just stick in there. We need webpack dot mix. Do you need that trailing slash on the node to stop it making a file? Just a question about math.
27:15 Yes. I'm hoping this is going to work. Yes. The the slash should mean don't name the file node, and then the slash should mean put it in directory. I could also just do package dot JSON. Yeah. You need package lock as well, I think, depending if they're using that. I'm gonna ignore that file forever. I don't think we should commit lock files. Oh, yeah. Yeah. That's I mean, we have containers. We already have immutability. We have repeatability. We have all of this stuff. It's like the lock fail. Like, your deployment without containers makes sense, but
27:52 I'm not going to about this last time when it made perfect sense when we were talking about production. Does that still hold for development? When It doesn't make sense for production. Having the log file and the container for auditability is fine, but not committing it yet. The only reason I think we have to make the lock fail last time was it required shrink-wrap, which looked for the lock fail. Yeah. No. No. I mean, if I'm a developer and I'm checking out a project, the earliest time I'm gonna find out I've been working with the wrong set of dependencies
28:26 will be when it runs through CI. If the developer has access to that lock file, they get an earlier discovery of what versions they should be using. I I guess we're getting into this. So the log file is it guarantee that the dependencies that I have in a log file that I have either run test against to confirm that it works, and that's Yeah. Now if I remove that log file, what I'm saying is I trust whatever is in my package dot JSON, and I have semantics there to indicate which major, minor, or patch version of the package I
28:59 want, which means I can always get security updates and the and the latest and greatest where needed for packages that I trust not to be breaking backwards compatibility regularly. Obviously, those things do happen, and we can pin those versions and the the JSON file, which serves a lock. I just I don't like committing a lock file because what I'm saying is whenever I rebuild this project, I never want to get security updates unless I manually do it. And I think that's a really big problem, especially with the amount of vulnerabilities. I don't think people that are ignorant to security
29:30 really understand how many CVEs there are. Crypto miner ends up in MPM projects fairly regularly. Yeah. So I guess what I'm thinking about is that we talked about we're in the deployment pipeline. We have this guarantee, don't we, that the the exact image that the CI ran against is the one that's being deployed. So that's giving you some of that guarantee. Whereas here, maybe the developer isn't someone who runs the tests locally. We're rebuilding the image fairly regularly. We haven't we don't really know that the you know, we're about to start recompiling the assets on the fly every time
30:10 you change a file. So we don't have that guarantee that the the code I'm working on, the versions I'm using have passed the test. So I guess you do have the backup, of course, when the CI runs. But, you know, I'm thinking about for some developers I know, that's eight days later. Yeah. So if you don't have a a really tight integration and short feedback loop with a CI server that runs within minutes of pushing code somewhere, then, yeah, you probably do wanna commit a log file. If you work at for a bank where you have to manually approve every patch update,
30:45 you probably wanna commit a log file. All our scenarios, I'd say, embrace the ability to update and get security updates. A crypto minor may still end up in there because of that, But, you know, we we have other ways to audit that code. Alright. Let's leave it there. We've run four lines of code. So let's if we'll keep making some progress here. Okay. Resources, packages on tailwind, and webpack. Let's see if we have enough to trigger and run those MPM scripts. So I'll use my other terminal here. Docker propose run, interact with the IT once it run.
31:25 We wanna run node. Let's run the batch command. Okay. Okay. So it it's going to make me do this. And that's because the folder doesn't exist. Right? If it existed, everything would be happy. When I set the directory to slash the working directory to slash nodes, so that directory does exist. Oh, does it get created? Yeah. So we comment that's it. Is that created what yeah. I don't know when that's created. Is that created at run time or is it created? When the working directory is set and the container metadata and the container is spun up,
32:12 that directory will exist. And now I can drop back out, bring these back in Mhmm. Then we have what we want. Okay. Cool. And that's the directory with all the stuff that we need. So I should we pull up the package dot JSON again. What we really wanna be able to run here is MPM run dev. We will need to do an MPM install. Wasn't it watch? The dev runs a watch. So MPN dev runs development. Development then runs oh, no. Oh, yeah. Okay. You're right. Development just does the cool way pack stuff. Watch does the
32:58 watch. Good catch. Thank you. We are what what are we gonna run-in? Does watch do the install? Do we we're gonna have to run install and then watch? So yeah. We'll need to handle that. I'm not sure. Install, but that's good. I think we have enough. Don't wanna get too cocky, but I think it looks good. What's it doing there? Up building. Alright. So let's assume this works. Right? Of course. Right. We need the output public directory. Okay. So because the complete the compiled assets are not something we need available locally, we're going to use a named volume.
34:13 So what we'll do is node public. Sorry. An anonymous volume. We don't need a name. We're just saying make the just expose this directory as a volume. Yeah. Now what may happen here is that it fails on the same step again because the CSS, JS, and other directories don't exist. I don't know if MPM will attempt to create them. The other challenge we have now is I don't know if we need anything from this public. I think only the PHP file is needed by the PHP container. Why don't we do it using the files on host?
34:58 Why do we mount the host? Why mount it? I I so I guess this is more a subjective thing from my point of view. Because it's compelled assets, like, I was working with c, if I was working with any other compelled language, all those things I don't necessarily need on my host. There there's something I built. Help me a lot. I can run my application, and when I kill the containers, I want them to disappear. I don't want them back on my host. And the other aspect would be just that if you want this to be ubiquitous across
35:30 all operating system, Docker for Mac does a really good job of mapping the ownership of the files. On Linux, those files will be owned by Root, and you have to then jump to a whole bunch of other groups Yeah. To map them back to a non root user on your local host, which means if I were locally, I'd be able I'd try and run RM on that file, and it would say, hey. You need to sit with that. And that always really frustrating from someone because you've learned that daily. The other aspect of it is
35:58 do I have another aspect of it? And Yeah. I Maybe there's any front end devs participating. They can comment, say, like, how often do you actually look at the compiled assets? Is it never? I I'm gonna assume never. And, also, I don't want people to have I I don't want it to be too easy for people to work locally and in a container at the same time. I I want a workflow that allows people to ignore Docker altogether if they really want to work locally or use a container based workflow. If you end up in a mishmash of
36:33 both because I generate assets in a container and then try to do something with them locally, they're compelling in something that like, the targets are different, and I just I think that could be rather complicated. Yeah. I don't have a big clear picture of whether that stuff's useful for debug. Because I think the the converse thing is associates in PHP world with things like VAR folders and compile you know, compiled containers and things. People go looking for that stuff on their host, and then they're confused because it's not there. So I go look in this public folder for the assets and get
37:09 have a little mental roadblock because there's an older version of the compiled assets in there from when I run it on host, and I'm trying to set breakpoints. So I don't I don't know how how important that would be. Let's do it with the volume. Alright. So the first thing we're gonna notice now is that my node modules is gone. So we're gonna process that across runs. That was a dapt mistake of me. So what we're seeing now is node modules, stick in that container side volume, public stick in that container side volume. We'll drop out of those, run it again.
37:44 We'll have access to those two volumes. I honestly thought volumes had to be named. I think I can take a few characters out of a lot of my files now. No. They they only have to be named if you need to be able to reference them through other tools. Now that will be something that we do with the public directory. We are gonna expose this to NGINX. But right now, it does not need to be there. And an image an image can declare a volume as well in the build? It can pass a hint to the container
38:17 run time that when you run this image, this should be exposed and consumable through other containers if you wish. Yep. Yes. Okay. Sure. There's some very weird semantics there, but yes. So what I'm gonna do is just drop out of this and pop back in. What we should see is I'm empty node modules. I wonder if it's not named, I'm not gonna get persistence. You just dropped out of the image, didn't you? The container. Yeah. To show to show that it would still be there. Oh, you're still logged into it? Let's name it Public. Or is it because I'm using Docker Proposal
39:04 ah, there we go. Yes. This is I because I'm using a weird syntax. So I'm not gonna be able to tell it to consume that same volume each time unless I name it. Okay. Yeah. That's just because of the way I'm running this container. Let's try that. Oh, I did declare it. K. So we need a node modules, and we can just use a Tilde which says use it to fall configuration. Now anytime I use the Docker Propose run, it should always use those named volumes, which have now been created here. And there's nothing special about them. Well, that
40:04 runs oh, I won't be able to see it on a Mac, will there? No. I'm not gonna so it's just all it does is create a directory under varlib docker volumes. And that's what that's where that fails. Let's there's nothing magic or sophisticated about that approach. And I should be able to drop out this container, pop back in, run an LS on node modules. And we won't have to run that again, though, for as long as that that named volume makes us do. If I were using Docker Compose up, I wouldn't need to name that volume. But Right.
40:39 Okay. So l s with modules, dropout, spin up, hope for the best. Don't be wrong. Okay. So then I can run n p m I. It should just be really fast. Well, as fast as n p m I can be. But okay. Next step was we wanna try we'll try running dev without watch first, see if it has a problem with that public directory and whether we actually need to create those subdirectories or not. But I'm assuming if they don't exist. I mean, maybe not creating the public directory, but I would hope that no one is happy
41:27 to create the subsequent directories. What what other files were in there? Is it just robots text? So this persistence here tells me that this is maybe a real file. I thought it should exist. It is not ephemeral or generated. Let's see. Yeah. P s good. Not very happy. Is it big? Let's take a look. Public CSS, app CSS, batch h, 1.2 meg, 74,000 lanes. Let's just close the oh, I've actually closed my whole course. Alright. Okay. It's been back up. Lost my directory now. Okay. Good. Back into my initial. So let's maybe stop in that fail and
42:42 then then Well, it's so big. It must be compiled. Right? It's not compiled. Wow. This is oh, this is the tailwind fail. No one's written that. But it may just be pasted pasted in the whole tailwind for configuration, which will then be parsed in post CSS to be minimized later. So that's meant see, now now we're in this really weird territory where public is not an ephemeral directory and is is not yeah. Damn. So what are the options? We could build it into the image. Assuming none of that stuff's actually gonna get edited. Let's mount it in first. And then Yeah.
43:45 Sorry. What I was gonna say there is let's mount it in, run the the development steps, see where that asset completion I mean Yeah. It must be out here. Right? That's why these JS files are numbered. In fact, here's what we can do. Locally, let's do an MPM. Must be, like, a cache clear. Right? Mhmm. And you can see if they're in git. Are we git to ignore? Okay. Ignore. Yeah. That's a good idea. Up a bit. Yeah. CSS is there. So they're the ephemeral ones. So where is that app CSS been generated if it's
44:35 not by the MPM? Did you run it did you run it locally in the past on host last time? Something like that. Yes? The native is in git ignore. But it's not mentioned that to the container. I'm saying you may have just generated it on host when you're messing around with the code previously. Yeah. Okay. But how did we do that then? How can we how can we generate that again? Delete it or rename it and see if it comes back again when you run the command. Alright. So we wanna go local. We want to delete public CSS, app CSS,
45:33 and then we want to run the npm run dev. And your hypothesis beam, we wanna know if that's created set again. Yeah. Could it be created by install? I don't know if NPM drops stuff out of node modules. It was generated right there. So we can use the git ignore as a list of things that we want to have as volumes. It's effectively a list of the ephemeral folders. Right? Yes. So you're let me clarify what you're saying there. Is you believe the problem that this is failing is because it's missing access something? And if we tweak our
46:34 so so that would mean there's something on the public directory that we're not mounting that is needed. Yeah. But if we look at this direction Probably something how unrelated. I just don't see what it could be. You could try running it with a mount of everything. Just mount dot and then just to debug, check that works, and take things out rather than adding things in. Excuse me. Okay. Let's try it. But we're saying we want to mount everything to node. Mhmm. Node modules go over the top. I'm hoping that works. It can be a bit weird trying to
47:26 work out the order that's gonna happen in and then MPM or dev. And assuming that the yeah. It didn't look like there were any specific files get ignored. We could check that. It looks like they have got distinction between, you know, ephemeral folders and non ephemeral folders. So we could use that list in the gitignore as a list of things that we need to mount as volumes. Okay. I think that's a solid idea. Alright. So that worked. So we still wanna be in a position where we don't have this. We want this. So what what consider missing
48:12 then? Split open, get ignore, see what's there. Because if we probably we're having was we're we think we're having was is mounting public. I I don't think it's public. I think we're missing something from the root now. Okay. I I think. Okay. So we got tailwind. We got webpack. We don't have we don't have package lock, do we? It can't be that. It could be. Maybe maybe we should rule it. Maybe we should rule that. Oh, wait. Do we need Bootstrap? No. We have the PHP Bootstrap, so it's not like a JavaScript Bootstrap. At nope. It's all very PHP centric. Opt is
49:07 my stuff. Database is not important now. We source these. We mount all of that in any way. Storage PHP test. I mean, let's add the lock, and then I'm gonna so let's roll it out. Right? Yep. So no with that package lock JSON. I want to stick it down next to this one. Alright. So we drop it at this. We spin this back up. Thought it was the name. Alright. So now we have our package or package lock. Should I blow in node modules to rule that out now that we're bringing in this lock and bail?
50:05 It's in We can just we'll just trust MPM to kinda fix that up just now. And if this works, then we're actually in a position where there is maybe a break by automatically pulling in the updates. But I am gonna refuse to believe that is the case. So you look at all these vulnerabilities and there's lock fail version. Yeah. Right. This is this is the stuff you don't want. Okay. Run dev. But, of course, you also want your application to work for a while. So That reminds me. I did work in an environment where there was some sort of corporate
50:51 vetting of which packages were allowed in. So Yeah. They had to have the lock file in case one of their vendors went rogue and stuck in some malware. Sort of tightly regulated setup. Same error. And that fail actually does exist. Scroll up to the error message. Did it have a trail leading forward slash? Yeah. Let me just again. Should've cat should've catered that fail. That 1.4 meg text file with 74,000 lines in it. Let's just drag this up to So node resources CSS up CSS. Right. Or let's let's actually read the message. Ah. Slash public.
51:46 Root fail system public CSS app CSS. Not slash node slash public. So that's our issue. Why? It's great. One one thing we're not better than is dot e n v. Don't see anything particularly useful here. So we think that somewhere there's a config that tells it what folder to use. But it must be relative because it's a it's a project that one of the files that didn't mount tells it, use the current folder for this install. Indeed. So let's take a look. Is that let's try and understand this. There's not gonna be specific path anywhere because
52:44 So this is the post CSS right here. We should use a Laravel mix. We do have a Laravel mix. Do we have a Laravel mix config? Yeah. This makes manifest. No. That's, like, the payload asset. Okay. Something in here is telling it where to find those files. So the work's fine in the PHP environment. That's worth pointing out. Like, if I come up here I don't That's my It worked fine when we mounted everything. Yeah. When we mount everything, it's fine. In a in a when we mounted everything into an NPM container that doesn't have PHP, so there isn't a
53:46 PHP file doing it. Is there anything it could be looking at that isn't right. So when we when we run it normally, it would look at, like, the current working directory, probably. The p w d or the c. That will all get sent to the working folder. Let's try app env on a web. Can you look at the mixed configuration again? Yeah. Sure. Yeah. Alright. Okay. So this is So if I'm just looking at some of the docs. That dot j s method is source and a destination. And that post CSS mess this right hand the second parameters are
54:56 destinations Yep. Which look relative. They do, which is why I'm assuming something in dot e n v is to use the current working directory, which is I was hoping with the app and local. Mean Is the current working directory not the It is. It is. If you echo c w d or whatever. Yeah. Oh, I have p w d. Sorry. Yeah. It's gonna be something so easy and simple if you know if you know. There's a method set public path. But Let's try. Oops. But in tier front failures now. Package dot JSON. There's nothing environment specific here.
56:25 Do we have the CSS directory? Is it created at the point we're running the command? Yeah. We we do have an app dot CSS fail in that directory where we run the develop command. Yeah. I'm gonna take a look at so we can actually see the web pack config. And if we take a look at that, that should expose any environment variables that are needed. So We need somebody knows how to use mix on the chat, don't we? Yeah. I was hoping for a magical comment from somebody, but not today. Right. So we've got them now. Let's take
57:06 a look at this actual config. Right away, Max is trying to load something from source index, but that's from its source. So it should be fine. I've got edit task, webpack builder. That wasn't very useful. It just must be something. Don't wanna rest the stream just to be us reading the mixed document. It is what it is. We're so we said we're gonna slow it down, and we were gonna do this properly, and that means trying to learn new APIs. Yeah. Right. So okay. So it's determined a path to the user's webpack mix dot j s. Looks for
58:22 webpack dot mix. We need to make sure that's in the right path. The route is then set to the route. Okay. This looks interesting. So either looks at yeah. It looks at process dot c w d. Yeah. On one one path. So how can we ensure it takes that path? Put in Right. Okay. We're loading it. So we do have webpack dot mix here, which kinda tells me that that should be in setup. Config. Alright. So node ENB. Okay. That just says that I have a flag test. Public path is blank resource rich slash. Is
59:52 that what we're looking for? Resource view. We have two comments. Oh. They're not applicable. I mean, this I read. Okay. So I feel like this should work. I feel like we're mounting enough. I mean, I know we're not. So what was so what was that resource route comp setting that you just looked up? It was set forward slash. Yeah. So I think this is a default configuration. So if we come into source config, there is a resource route. I guess if we do a quick grab But maybe that's for external. Then we see that as over we can overwrite it
1:00:56 via the API. If we call set resource group. So alright. So we it has to be a fail. It works when we change the amount to be a dot. Yeah. That's the bet that's really bugging me. We're we're good developers. Right? We can do a, like, a some binary search. Can you I'm good about this file. Let's mount ENV first. It's a really nice my thing because it just feels instinctively like that is the file. I mean, I guess I don't need to mind that we can use ENV file. I don't know. Because we had that syntax
1:01:44 issue last time. Okay. Dot ENV to slash node dot ENV. Let's drop out and see if that changes how this code run. And it does not. Okay. So let's mount and let's just keep adding files. We'll add half of them. You want a binary search that fails in the missing configuration? Are we are we assuming it's a file that's in the root? So just get the full list of all the files, paste it into your VIM, do a quick VIM magic, mount them all, and we'll find out if it's one of the ones that's in the root folder first
1:02:51 rather than something buried in one of those other folders. Sorry. Can you say that again? Just get the list of all the files and make a mount for all of them in whatever the quickest way is. Copy paste. Do some Vim craziness. Okay. Not the ones that are folders. So grip out the ones that start with d. Right. Hold on. S dash l. Now what we only want files. We don't want folders. Grip dash v. So we don't want anything that has dash e e v. D. D. How's it? It's d at the start. We don't want
1:03:50 Yeah. So we're doing a regex graph where the lane starts with a d and arc prints one, two, three, four, five, six, nine. There we go. And then said? Or you can do it in Rawk, actually, can't you? Just print. We could do a begin statement. So begin No. You can just do a print line space. Oh, yeah. Yeah. I'm I'm over complicating it now. So, yeah, we could do dash and copy it. Can we copy? Some shelters were Okay. Wouldn't it be amazing if it turned out to be the license? I don't think that's gonna happen.
1:04:48 Alistair Steve says I have a table and a dot EMV night. Oh, thanks, Alistair. Is there ah, of course, it didn't. I mean, we haven't confirmed that that is the problem, but that's where my hunch is. So let's comment this out. Yeah. Test that test my hunch. He always adds a bit of value as well. I can't believe you're not even catching my tapers. Quite a few of them. Neither have I, not a dev. Celebrating too early. Okay. My hutch did not work, so we're gonna bring in everything. We don't actually list the dot files,
1:05:52 but let's try anyway. We can always add the dot files. Yeah. No. So it's something in the folder. Either that or it could be one of the dots. There's an ESLint. Oh, I just lost access to my You need to plug it in. I do need to plug it in more. Let's check this ESLint. Yeah. I don't think that would be needed to run our dev step, but I will validate that. K. Let's let's run our wacky thing again. This time at the LSE, let's get all those fails including the dots. Oh, that does work.
1:06:58 Delete. Stick it back in. It'd be good if any sort of junior developers are watching to see this is what you spend a lot of time doing. Twenty years I've been doing this, and I still spend all my time debugging. In fact, I spend more time debugging those than I ever did, probably. And if this doesn't work, that means we're looking at something which is in a nested subdirectory somewhere. Really annoying. Okay. Alright. Let's try. You know what? Let's So try all the folders? Yeah. Try everything. So I'm just gonna remove the Gorep here. Well, no. Just invert it.
1:08:03 Because we want to we know everything. If we mount everything that it works. Okay. So true. See if it works, just take the v out. Oh. Oh, yeah. Oh. Well, that must still work, actually. It would also catch any files with d in. Yeah. Alright. So we want directory's only. Okay. Tripod thing is gonna get very annoying fast. Okay. Can't type. Alright. And then we'll just start ruling out the things that we're pretty confident it is not. Although, how funny would it be if this fails? I'll probably go out and face something on the window.
1:09:07 Yeah. It should work. I mean, it might not work. This should work. It failed. New hypothesis? I'm working on it. Did it work with a dot? Are we making that Hang on, David. Don't know those files need to have where are they gonna be mounted? Those mounts. Don't worry. I'm just creating a bunch of dead volume. Yeah. So we haven't actually been checking anything before. No. Okay. Let Let's go back to the theory. It's one of the files in the root for now. I make myself laugh sometimes. Other times, I make myself cry. But we'll feel really good when we figure
1:10:20 it out. Okay. I'm I'm sure there's a way to automate this, but right now, it's not popping into my head. You can automate it on the command line again. So delete delete it all. Yeah. I can do print print print. Yeah. Okay. Good idea. So we want dash dash dash colon slash node slash print name. See, that's why you're here. It's what good copilot does, isn't it? Shut up when you're doing it. Chip in with useful thing. So it wasn't an error because Docker Compose was creating lots of volumes. Okay. That's not quite right. I didn't add on
1:11:17 flash. It's just a I can't remember how to do multiple cursors on a Mac. On Linux, I've got it down, but my the key is different. I don't know Versus code. Okay. Alright. Hypothesis one, validation 14. To put it that point is mounting everything explicitly, is it? Yeah. So we can probably just remove. And then we'll do a half. We'll do a Oh, that's the that's the directions we've done. So let's get the files now. Did it work? Well, no. Because I completed the righties. We wanted to set the files first. What's the error message?
1:12:08 That was a duplicate mode because of stuff. So I'm just gonna take out all of these midpoints except for the node modules. So we're gonna mount our node modules and everything locally. That is a fail. Just for my sanity, can you check node modules is in git ignore? And there isn't some super significant It is. Yeah. Okay. That would be crazy. Okay. So now we are mounting every fail in the root directory and so the container very, very manually. David David, look in the chat. Has some has this person saved us? They're anonymous. No. I would be very disappointed
1:13:05 if Moxie's wrapper around webpack required Artisan to exist, which means you're probably right. Have a look at the Artisan file. What's in it? PHP? But maybe it uses the artisan file the presence of the artisan file for some sort of directory relativeness to to find the root. Do you know what I mean? I didn't write it. Let's Right. We know it doesn't have to run PHP because we might end up, and it worked. Right? Yeah. But I'm saying I've seen projects that find the root folder by finding where the dot git folder is, Things like that.
1:13:55 Okay. So we're gonna comment all of this. We are gonna mount an artisan. And we do need all the other stuff. Yeah. The JS. Let's try and go back to what we actually wanted though, right, which was to maintain resources. We we need public directory. And then the web pack stuff. Okay. It was just wait. It's okay. So public, we've done resources. Yep. Okay. Let's try this. And we're just about out of time as well, which is unfortunate. At least the show notes for this episode will be really easy. Like, watch Kieran struggle. What did Kieran and David struggle for forty
1:15:21 minutes? I can't believe that worked. Yeah. Why why does that need to guess you can't use mix outside of Laravel projects. But you should try it try it with an empty file called artisan. Just for a laugh. Oh, yeah. Because that's really that's so fun. Alright. Alright. So let's let's let's make it field first again. Right? And I'll touch touch artisan and see if it passes. And then someone could go and open a very strongly worded issue against this repository. Sorry. A politely worded issue. It's frustrating, you know, this stuff happens. Yeah. You gotta see it from the point
1:16:11 of view of someone who who's every every project they ever have has an artisan file, and it's a great way of locating. Yeah. Nice idea. Is it? An environment variable would have been just as good. I've written code that looks for composer JSON. Let's just put it that way. Unbelievable. No. Thank you. No. If that was not for your comment, we would have spent at least the next three weeks. So do we have enough time to add to the watch container that we were trying to Let's confirm that command work. It should no different from develop except the runs watching
1:17:01 the fail. Alright. So that means well, we can delete all of that. I mean, I think that debugging process is as painful as that may be for some people to watch. There was some good nuggets of bash there for people to to learn. Yeah. No. Okay. So we wanna automate that. So we're gonna set our entry point to be MPM. We'll set the command to be run or watch. So that will now run that on alert. Anytime those assets change, we'll see that completion step happen. We can now then expose this public directory to engine x.
1:17:53 So instead of mounting in the local public, we are going uphill or volume from here. Mhmm. That makes sense? Mhmm. Okay. So let's do Did Just spin it all up. We can just Did you make did you actually make that change? I think Which Which Look at the YAML again? Yeah. You have. Sorry. Yeah. I can run a Docker Compose PS. We're gonna see engine x is running. Hopefully, it's not been running long. It has our new changes. We've got our node watch running. Now let's confirm this works. We should be able to reload this.
1:18:39 And dashboard two is there. Now the final test is if I change that to dashboard three. Can you tell the locks of the watch container? Yeah. Sure. Note. And we wanna change that view template from earlier, which was here. If I can find out this here. It's time for dashboard three. Compilation step. Jump over here. Oh. Oh, no. Wait. It sent two places in that file, actually. That's for three. There's that's oh, yeah. There's another one here. That's the title tag, I think. That should run. Is this browser cache issue? Do a force reload. Alright. Let's let me let's confirm that what's
1:19:48 running. Yeah. Definitely. Okay. Let's check the NGINX container. Is this NGINX? No. I see PHP. D c e x d NGINX bash. Email. Public. Nope. Where did we mount that to? The w w w HTML. Oh, okay. Oh. Oh, yeah. Oh, wait. There's no view templates. Oh, okay. We don't expect the view templates to be there, do we? No. We should change the CSS. How would it be I wish I knew how view worked. They'll be in this in the logs it was generating. In the tailed logs, it generated j s 13 dot j s.
1:21:08 So one line. Yeah. So, I mean, we could we should be able to see this fail of the actual dashboard for a floor. Right? And we're hoping dashboard four, three, two. So two okay. So that's interesting. Okay. Let's confirm. 13 JS was edited at 12:17. I have a reservation, Dave. No. You so the file you edited was in resources. Correct. Yep. And it's the same volume in both containers. So is does it contain a two or a three when you look at that folder from the node running container? Okay. So from a node point of view
1:22:19 Yes. If you shell into the node one. Resources. Where was Public. It's node public. Yes. Cat 13. Correct. Dashboard four. Nope. That work two is still there. Is this us just not understanding Vue? I mean, I definitely have never used Vue before in my entire life. So let's but we do see asset compilation. We do see JavaScript files being written. Let's change the CSS. Let's let's maybe you're right. Right? Okay. So what about if we change one of these colors? Mhmm. That would be in the tailwind thing, though, I think. So it's basically, if you would change the
1:23:13 class name and the template. Well, let's change indigo to be black. I'm assuming that indigo is the state bar. Yes. I rock and roll. And that's now run again. Okay. That worked. So it's just us not knowing how to edit the view thing. Alright. Let's wrap this up and explain, hopefully, what the hell we did. So we have separate seats very painfully, node watcher out of our PHP serving image into its own container that runs in the background, continually watching the assets, recompiling the JavaScript and the CSS. Now I think you're right that we don't
1:24:11 understand the view component of this yet, which is why that dashboard change never made it on the live reloading bit. But we were able to change the tailwind configuration, and I'm assuming we could change other parts of that CSS and JavaScript to work. So we do need to work at the view component next time. What else? What have I meant? That's what we did. I got it. I mean, we haven't modified the product we didn't modify any of the production deploy stuff because that already builds the same pipeline successfully. Yeah. We just focused on trying to get
1:24:51 a shorter feedback loop in the development environment. That means that on our next session or we can definitely tackle the view template. We can work out how we we trigger the the rebuild, the redeploy, whatever has to happen there. I don't even know if Laravel delivers the view, and I'll need to do a little bit of research on that. I'll spare people that on the stream. Then we will move back over to the PHP development environment and really clean up that workflow as well with regards to how we interact with composer. How do we run tests? And yeah. We
1:25:22 definitely will tackle the tests on the next session for sure. Okay. Look forward to it. Alright. Awesome. That was challenging, but really, I I I definitely learned a lot from Yeah. Just working with the this tool. Again, Laravel is new to both of us, but I think we're making good progress. And, hopefully, people find this useful. So, you know, thank you for tuning in. Keenan, thank you for joining me. It was an absolute pleasure again. Enjoyed it. And I will see you all soon. Thanks.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments