About this video
What You'll Learn
- Set up a Slim PHP app with separate NGINX and php-fpm containers driven from a shared codebase.
- Build a Compose 2.x workflow with service health checks, dependency ordering, and cached composer installs.
- Deploy the same Dockerized stack to Kubernetes using sidecar NGINX containers and independent service scaling.
Ciaran McNulty joins to containerise a Slim Framework PHP app: NGINX and PHP-FPM as separate containers, Compose 2.x for health-check dependencies, a multi-stage Dockerfile, then Kubernetes with NGINX as a sidecar and independently scaled via a service.
Jump to a chapter
- 0:00 Holding screen
- 0:50 Introductions
- 9:30 Creating a Slim Framework application
- 14:50 Checking out the docker-compose.yml ... oh, did I write this?
- 30:15 Adding a damn .editorconfig
- 31:15 Replacing the PHP dev server with nginx and php-fpm
- 55:00 Ditching compose 3.x for 2.x: leveraging complex dependencies with health-checks
- 1:11:00 Adding a multi-layer Dockerfile for build cache goodness
- 1:29:00 Deploying our application to Kubernetes
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
0:50 Introductions
1:02 Hello. Hello. Hello. How are you, Caleb? I'm good. Thanks, mate. Raring to go. So today, you very kindly offered to join me, and we are going to take a crack at modernizing PHP development using Docker, Docker Compose, and Kubernetes. Yeah. I'm excited because I've seen a few different ways of doing it, and I want to hear your way. Well so, I mean, the confession here is the last time I wrote a line of PHP was probably in 2016. However Still the same. I'm good on the Docker stuff. Right? So Yeah. Like, between us, we we we we got this. I'm sure. So
1:55 do you just wanted to do a quick introduction? You happy to do that for thirty seconds? Hello, everyone. I'm Kieran. I'm primarily a software developer, so I'm less of an ops person. I do a lot of consulting, lot of TDD and BDD stuff, lot of agile stuff. And I've I've kind of avoided caring too much about where stuff gets deployed until the last few years. And I think I've got a decent Docker knowledge. I'm terrible at orchestration. So it may lose me towards the end, but I can I'm sort of up on containers, and someone else takes care of that other
2:32 stuff a lot of the time. I guess that's right. One of the things about Docker is that even if you're just a software person, right, you're having to understand how these things work because it's maybe how you're running it locally as well. It's it's Yeah. And I I think that's that's really important. I think that what we're gonna look at today is is how do we use these tools to make this local development process better. But then, you know, get it that, you know, the last mile as well and so it's something like Kubernetes or any container based
3:06 deployment really. But we're we're focused on Kubernetes. Yeah. And I'm not too worried about my lack of PHP knowledge because when see, when it comes to container based development, there's only two ways to go. There's dynamic and interpreted languages, and then there's compiled languages. Like, that's it. Like, there's only two workflows, and I don't I've never seen a runtime or a language or a framework where that changes beyond those two dynamics. So Yeah. I'm pretty confident. I think in a typical PHP project, you also have to understand it's likely to also be a JavaScript project.
3:40 So something that people find difficult is that they're having to manage those two separate build pipelines and and, yeah, have the interrupt. You know, I never even gave that a moment's thought. So if that comes up, we'll tackle it one step at a time. Yeah. That's just from my experience of, you know, here's a nice PHP deployment pipeline, and someone says, well, how do I get my webpack stuff into that as well? Because I've got to transpile all this TypeScript I've written, and you kind of shrug your shoulders if you're a PHP person. Yeah. We'll we'll we'll do our best to
4:16 tackle that too. Because that's more like a compile pipeline style. Right? Well, yeah. It it has a belt step to, you know, generate some assets that need to be consumed from the actual web server side of things. I think, actually, it would be really cool to cover that because one of the things we're gonna focus on today is a split between the PHP code being interpreted by PHP FBM and engine x actually running as the web server. So, you know, been able to deliver those static access into the engine x server, then just have the PHP code be delivered
4:50 from PHP. Depending on how this works, like, kinda haven't read PHP in a long time, but we could do we could easily go the other way and have PHP, FPM deliver those static assets too. So I think where your wonderful judgment is gonna have to keep me right on what is best practice at PHP lab. And so the question that comes up lots and lots in PHP in PHP land when you're doing Docker is because PHP isn't a web server. FPM isn't a web server. Typically, people are using NGINX and FPM. And so the obvious question
5:25 that every Slack and every IOC channel has is, should I put them in the one container, or should I have two I know you've got an opinion. Last time I I do. Do definitely have an opinion. So I'm gonna share my screen right away because I have tackled this question before. I give a talk in 2018. In fact, I've given this talk many, many times. I'm sure it I think you've seen it on one on one occasion, but it's my kick ass development environments with Docker talk. I gave this talk from 2016 to 2018. The one that's got the most
6:05 traction and questions is this one from LaraCon to be in such a large event. But and this talk, what I said to people was, you know, always deploy them separately. Always deploy NGINX and FPM separately. Mhmm. And it's important to know that, well, Kubernetes was definitely probably still the default container orchestration tool in production in 2018, this talk focused purely on local development, nothing else. And what I suggest that people do is to use a Docker a Docker option which allows you to consume the volumes from one container and another container, which meant that you could have engine x
6:42 consume all of the code volume from the APM container, and you didn't have to worry about the fails not exist on the engine x container. Because that's the biggest problem. Right? People say, oh, I need things on engine x because engine x has to know that fail exists. You does it need to know about the PHP files? It doesn't. No. So It's the other came out. Right? It's the So that's what I wanna show today exactly. The engine access doesn't care if those files exist or not. It can forward them without doing any local checks.
7:15 So where the volume's from option and the talk is is still very much valid and you can use that local development, it falls flat when you go to production. Very difficult to have a volume with your code being orchestrated on multiple nodes and are consumed for token damage. So we're we're just gonna promote the idea that NGINX doesn't care and doesn't need to care about that. And one of the questions I get all the time or just at the bottom of the thread are, you have an example of how I do this today? People who want to see this
7:45 they they want the quote. Right? They don't wanna have to write this themselves. And that's our plan. And we're gonna provide a quote for people to do this without having to constantly questions and comments on this review. With that in mind, I have gone ahead and created a PHP examples repository on get labs slash Rawkode slash PHP hyphen examples. I will synchronize it to GitHub later, but for now, this is where it's gonna live. Mhmm. And I haven't checked it locally, and assuming our setup is working nicely, you should have the ability to take the
8:21 take the take the somewhere in the file. It's it's loading. It's Okay. Well, it's not important. Might be saturating my bandwidth. But you can always see what I'm typing. We can try and pair program program it up, and if we really need to, we'll refresh your staff. Okay. Yeah. It does work. Okay. Let me try typing something. Oh, there we go. You're there in my file. Hello? Perfect. So I'll I kind of I've only added some readmeets right now so that I could push this repository. Mhmm. So the plan being that we'll cover using Docker Docker Compose for local development,
9:11 cover the last mile of getting into Kubernetes, tackle any pain points and pitfalls as we go. You're gonna keep me right on everything that you have inside of your head, and I will do my best to describe everything that I'm doing as we go. Now my plan sleep after that, won't worry. So it's to use Slim framework. I'm now doubting that decision already because I don't think that Slim has that node JS dependency build step, and maybe it's better to do something a bit more feature complete or at least has all those steps for us to
9:30 Creating a Slim Framework application
9:49 to deal with. What are your thoughts? I don't have a strong preference which framework. I I'm not an expert in how to build web assets. I'm regretting making it kind of, but we could do something in slim and just add a little transpile step to something. Or we could just talk about it. No. Okay. We'll continue with the plan. We'll just it's definitely easier if get a PHP web framework going that I'm familiar with. We can very easily add, like, a package dot JSON, fill in some random load dependency and have it spell an asset. Right? And I'm okay
10:28 with that. Yeah. Okay. Which means that what we have to do is create a new slim framework application. So how do I do that? Do you just install it with Composer and make an index PHP? Something along those lines? Yep. So according to their documentation, we do Composer space create type of project. We then have slim, some skeleton, dev, master, the final parameter of some app name. Yeah. I mean, I just wanna put it in a local directory. Hopefully, it still does that. But we were calling this Docker Kubernetes. Yeah. That should be fine. We
11:21 They're saying it's not empty, but that's okay. Workforce. I don't see a I'm just gonna type force. I think it won't install into an existing folder. Oh, it won't. Well You just make another copy of the README. Yeah. Yeah. Oh, oh, I just I'm sure I just put my README over the other README. Anyway, not bad. So let's try that again. Create our projects. Does it go Oh, I'm not in the right directory. That's right. Let's go and say Docker and Kubernetes, but move root meaning itself, destroying it for r dot n d. I want the composure.
12:16 Mhmm. Create project Docker. We'll get there. It's better to have these challenges and model slip ups at the start because then it means nothing goes wrong there. Are you on Composer two? I have no idea. Composer doesn't even exist on this machine until about ten minutes ago. It's a little bit faster. Maybe if we use Composer in the build step, we can use too in the doc. But it's I just did a brew install composer. I'm assuming a composer too is is released and not in an RC stage or anything like that, then I would probably
12:58 It's a beta stage, but, you know, it's not run time of code, so it's okay to use a beta. Always. I see that as I'm running on Mac OS Big beta. So what do you think? Big Sur has some sort of fancy new virtualization framework built into the OS. Do you think that's gonna make Docker run fast? So it does have APIs for, you know, tools to talk up for Mac and other stuff. Think we're APNs, vagrant. It's not something that I've I've tried yet, because I don't think anything that I've installed works with it at the moment. I'm really
13:44 hoping though that whatever they've configured to to file synchronization has to be an improvement on what we have right now. Like, for dynamic interpretive languages, the fail sync from that Docker for Mac is is the killer. Mhmm. Yeah. It's just makes developing any sizable project very, very slow. Yeah. Okay. So what's this prompt? Do I do I want to remove the existing get history? No? Okay. I've not seen that before. It's interesting. I guess it's just because I have stuff going on here. Not really sure. So let's see. What have I got here? Oh, well, it did create me a new
14:29 directory. Let's just move on to that. K. Perfect. So we have a computer a composer JSON file. With a bunch of stuff. Look at that. It gives us a Docker Compose file. That's a bit too fancy, isn't it? That's a so it's using Alpine. It is. So it's a working directory, mixing command and entry point together. It's even using the Rawkode approved environment, so it'll send text. I I I really hate it when people use the the less syntax. Do that. Like, it just why would we do this to ourselves? Have to run for another day. We it's
14:50 Checking out the docker-compose.yml ... oh, did I write this?
15:35 supposed to pour, and we have volumes. I mean, it's pretty sensible for me. I'm not we're making too many changes. There are a few things in the three point x spec or in the Compose spec that I don't like. We may drop that down to two for very good reasons. We'll touch on them if we make an up for it. So What? Let's just There you go. Just explain one thing to me. What is that a good practice to be running PHP as the entry point? So this is That's not a good web server. For local dev, I think it's it's it's
16:11 fine. I don't really have any problems with it. It does mean that our development environment doesn't have any sort of parity with our production environment. So, you know, there may be things to that, like, you know, that's had an HD access in this public folder. Like, that's not gonna do anything on that and and your next environment for Yeah. Yeah. For example. So you may find that if you're using the HD access for any sort of security concerns or redirects, anything like that, that that discrepancy between production and development, if it's just frustrating, I probably
16:45 would avoid it. But I can see why they're doing it. They're just trying to avoid any additional complexity. I'm actually surprised how much stuff there is considering slim is haven't made a slim project in a while. Slim is high grade cut down framework. They've got Travis setup and all sorts. Yeah. ADR directory style structures. It's not the slim framework I remember from 2015. That's for sure. Alright. Let's just test this works locally and get it working in Docker. Does that sound sensible to you? Would you would you be more specific about the version of PHP?
17:37 Okay. So you're talking about using seven dash Alpine instead of seven dot one dash Alpine? Or seven four maybe. Or 73. Sorry. I'm I'm trying my best here. Yeah. Yeah. Exactly. Would I would I specify a point version? I think very possibly. I mean, I don't pay a lot of attention to the PHP RFCs anymore. But I do believe they make a lot of changes. Is Versus Code saying that you wrote that line? You know, you were surprised Did I add this? Four years ago. I did. So all these criticisms are criticisms of you. No. See, I I don't think I added
18:23 this. Hold on. Oh, hold on. Hold on. Slamming this Docker Compose file, and it's your work. I wasn't slamming it. Let's see. Let's see. There must be a scan. Perfect. Well, we've got a couple of comments. So let's just see what's going on. We've got one here that says, if we go with Laravel, then Laravel next ships with some JavaScript stuff. So, definitely, we I will provide a Laravel example either in this session or just after this session. We'll see how things go. We got Glenn asking how late you are. You're not late. We're just starting. We've not
19:06 done anything of use for anyone. And we have our friend, Bastian, who's tuning in. Nice to see you too. Hi, Bastian. So let's get into you're a slim contributor. Yeah. That's me. How much am I so far? Yeah. So someone modified this version. I'll have worked with that. That's that's not fair. This is just let me cover the the version in more detail before we get started then. So when Docker promoted the Compose spec, the three point x, they really targeted people that wanted to then go from global development to production. So they added all of these extra parameters
19:53 or pragmas, whatever you wanna call them, that allowed you to then use the same Docker Compose file to generate a Docker bundle, which could be deployed on Docker Swan. Yeah. They eventually added Kubernetes stuff on top of that. I can't remember the exact version, 3.4, three point five, whatever it was, which means you can now just deploy that Docker bundle or the Compose file straight for Kubernetes. The challenge is is that where we want parity between Docker between development and production is that there are some very key changes in the way that you work from a
20:24 workflow perspective that just don't translate. Like, on a a Kubernetes environment, I don't care about the order of containers start and the container may be deployed at their own way with their own CI and CD pipeline. Local development, I very much care about the order and the way the thing was started because I may have the database migrations to run. I need the database server to be healthy. I've got web pack steps that are really important. There's all of these things that need to be coordinated. Two point x specs makes that really easy to do through very sophisticated health checks.
20:54 So we probably will drop that version down, and that's why. And we can maybe show why it's painful first, and then jump back and say, that is why you should just use two point o. And I've heard the argument that the health checks should be part of your your application, be part of your system, and not the responsibility of the orchestration tool. If you if I'm writing a worker that's gonna read for from a queue, I've got two options, haven't I? Either I can write my code assuming the queue's there and kind of outsource that readiness.
21:30 Or I can write my application so it's gonna have some sort of retry as part of my code of my worker. Where do you fall on that? So I do agree that your application has to be built with that resiliency factor in mind, and that if things do go wrong, it will retry it. Of course. That makes sense to me. But, again, spinning up something in local development is just that bootstrapping phase is so unique and so different that I just think it's really important. Like, when I start my application, I really want to build code that retries
22:05 my migration path until it works. Yeah. I mean, no. And when I go to production, I want that if that migration step fails, I want an error log, and I want something to say, hey. This is this is not working. I don't want it to just silently keep me trying without giving me any indicators that something's potentially wrong. Yeah. So And I guess on production, you're not doing a cold start very often of all your services together. Yeah. Only once, but you spin up a new cluster or something. So And on dev, if one of your services falls over,
22:39 you probably stop everything and start it all again. I am a serial destroyer and recreator of the world. I like to do that frequently. I like to do it often, and I like to make sure that our mission works. Yep. Definitely. So is that tricky so this leads us to using Docker Compose. But there's a kind of what what is what are the downsides of that? It means you don't quite know it's gonna work until you deploy to some Kubernetes environment? With most of the control, it's less you're not dog feeding it as much. I just
23:19 you get a very limited amount of the features of Kubernetes available to you when you go through the Compose file using that as the the description of your Kubernetes deployment. The department where I'd say, you're really just using the wrong tool for the job. And you should just be using a Kubernetes manifest and getting familiar with working with that because, you know, even if your application is a monolithic PHP application, there's still surrounding services that you need to deploy. And I don't think the Compose file is a good way to describe that for all of the environments. Mhmm.
23:50 Especially, again, the database I'm gonna keep hammering on about. But my production deployment of a database does not look like my development deployment of database. And, yeah, there are Docker Compose overlays of the ability to extend service definitions per environment. But then we're just gonna answer this really convoluted setup that I don't think is actually going to save you any time in the long run. That is probably just gonna slow you down and cause a little bit of chaos. Yeah. And and looking forward, I think the idea a lot of the supporting infrastructure like databases and queues and things, the trend
24:22 seems to be away from running them yourself. I don't know if that's your experience. So No. You're completely right. My my Docker Compose locally is gonna be running a MySQL instance or or whatever. But in prod, maybe I'm using Aurora serverless or some other I know MySQL is not the best database, but you know what I mean. Exactly. You know, when we're deploying things into Azure, AWS, or even GCP, all those managed services become really, really appealing and that we don't have to handle the complicated state inside of our applications anymore. So maybe only deploying the web the FPM service and Extremely
25:03 likely. Exactly. Alright. Let's let's get started then. So yes. That turns out I contributed this Compose file. I have no idea. But that's not important. Good catch. I had never noticed that. So that looks like That line has not even changed in four years. That's still just PHP seven all time. Like So can you recap what the Docker true environment variable is for? No. I'm not sure what this is for. So it's been four years, Kevin. Yeah. I'm assuming there's something in the configuration of this application. Alright. Tap here. Found it right. There we go.
25:51 So and yeah. Perfect. I'm assuming I didn't add that too. So if it's in the Docker environment, it logs to logs to standard. Yeah. Why does Compose file have a logs volume? Adverse here. It might not be being used then in Docker. Very possibly. It could be that. Yeah. I don't know. Let's find out. Let's get it working. Let's get it working. So I'm surprised I have contributed to the rest repository because there's no make fail. The big argument against that is Windows doesn't ship with Make. That's why Symphony ended up not using Make after
26:49 very nearly adopting it as the recommended way of doing things. But sure, though, I mean, that's the time to change it. I don't wanna get into the whole WSL too and when it was being a really good dev machine now, but make us available. It's not on your terminal. Anyway so how do I run this? I do PHP then no. No. That's a thing. Right? For you do docker Well, I just wanna run it locally first. PHP dash s. I'm assuming Composer has tasks. Right? Yeah. They're called scripts. Scripts. So I can just click Composer star?
27:29 That work? Yep. Alright. And if I go to here, it it it. Right. It works. It's a good site. Where did it log to? Did you see any logs? I was about to check. No. So when you were there some in the terminal? Yeah. They were. Yeah. That's the web service logs. That's not the Oh, yeah. The Well, so because it's the same thing. Right? Oh, no. The application. Okay. Definitely. Because it's got us on longer. Okay. Maybe it's not working. So assuming I haven't made this the worst Docker Compose file in the world four years
28:11 ago, I should. That takes a while. It just eats so much bath today that you've gotta close it when you're not using it right. Yeah. I kinda want a script that wraps Docker and starts it. If it's not started, if you see what I mean. I think that would be good. Starting on the I mean, I'm not gonna do it right now, but I don't think it would be How how much stuff is in the vendor folder while while that's while that's pulling? It's quite a lot of stuff, isn't it? Yeah. I'm not I'm not sure if that's a
28:55 lot. I mean, I've been working on some node projects lately, so this is this looks quite slim, Kinda intended. That's what we mapped, isn't it? Yeah. So, you know, we could do a fail account here and see how many fails we're actually dealing with. I found that anything above usually 4,000, but thanks to that. Let's see. So this is running, so we should be able to refresh. We're still good. What was the page load time? Just out of interest. Can you Can there. Or does it tell me that here? Hello. Go to network. And we're at
29:40 three hundred and fifty milliseconds for Hello World. That's pretty slow. How about that faster? It's it is slow, but it's okay. If you're if you're a developer looking at a site in a browser, it's usable, isn't it? Okay. So what we wanna do, first and foremost, is remove the PHP web server, and we wanna bring in FPN and NGINX. Yeah. That's two separate containers. Right? That's two separate containers. That is that is the question that we can ask for the example of. So that will be the first thing we tackle. So It's formatting. Oh, there's no header. I'm thinking this.
30:15 Adding a damn .editorconfig
30:24 Slime is letting me down. So Yeah. I am a tab person, just for the record, but I've got so fed up of facing it. It was just the world has gone. Space is crazy. Shouldn't you be setting the indent size just for YAML? Because everything else is full. Yeah. No. I think that's the right thing. K. We just called this podcast. How to get attracted by every trivial detail before without actually doing any rework. Sorry. No. That's not I thought that would work. It didn't let's just do yeah. I know this. We'll keep an agent. I'll do it over there. There we
31:15 Replacing the PHP dev server with nginx and php-fpm
31:15 go. We're adding NGINX. It's an image. Why do we need NGINX? It's because FPM itself isn't gonna be able to respond to web requests because it doesn't have an HTTP interface. Yeah. So to the best of my knowledge, APM is just processes the PHP and it takes requests in over a socket and sped something back over a socket, but doesn't actually deliver any web server functionality. Right? Yes. Even the fast CGI connection is how I understand it. So it doesn't have an HTTP web server built in. And that's maybe a historical thing. Right? Because because normally you'd have NGINX and FPM on
32:07 one box. Or you'd have a Mod Apache in the old days. So it didn't feel wrong having these two daemons running that are gonna just to do one thing. Maybe that's where the question comes up a lot. When you go into Docker world, you've got this ideology that you should just have one one process per container. But then you need two containers to do one thing, which feels wrong. That's that's yeah. So in a Docker environment, you do need you you should always have one process per container, and and you do need two containers to run a PHP application.
32:42 That when it moves to Kubernetes, though, it starts to leverage something called the pod, which understands that there should be multiple containers there. Right. But it's a much it's a much better fit in the Kubernetes kinda landscape. But, yeah, for Docker, Docker Compose, it can feel a little bit counterintuitive. But, again, talking about parity between development and production, we we really do want to isolate these things. And they they both have very different properties. NGINX, we just want to deliver static assets. FPM, we just want to run it through. And the challenge of running both of those
33:10 in the one container is how would you rate a health check for a container with multiple pool service? If FPM dies and engine x is still healthy, is that container good? If engine x dies and FPM is still running, is that container good? Like and you don't want to start writing health checks or checking for multiple sockets being open and checking that they're both valid and it's Yeah. Very much work. I guess health checks are kind of second line of defense, aren't they? The the first health check for a container is is is it still running?
33:38 Like and and if you can make it so that the as long as the main process is running, the container's fine. You don't have to worry as much about health checks. Yep. I think we're good that. That's because I know why I've seen people running supervisor d as their entry point Yeah. Or running lots of workers or, you know, system d. If if you're running a supervisor in your container, that is no. Actually, see, this is where this this is little tiny bit of nuance. It's like, there is a very good use case for running a
34:12 supervisor. Usually, it's tiny inside the Docker Compose. But we're gonna ignore that for now. If you run a supervisor in the container, there's too much going on. Just use a VM. Use Vagrant. Yeah. Just go down that approach and Mhmm. Yeah. If you're if you're running an entry point that you know is something designed never to die, it doesn't tell you about the health of the system. K. Alright. So you check again. Let's let's let's do this. Valuable information, I think. These are good things. So we okay. So what have I done? I've added NGINX. I have used an NGINX
34:44 image. I'm not gonna involve a tag right now. I'm not that flexible with bears, not it. I'm exposing this on port eighty eighty now, which means I need to remove eighty eighty from FPM even though it's not FPM yet. And that's fine. We don't actually need FPM to be exposed. We can remove an entire attack vector here by not having the FPM exposed to our customers or users. So we need to move that way. And I'm happy for that just to run right now. Makes no difference to me. I'm just gonna spin this back up, and what
35:16 we should see is we now have NGINX PHP still running, but not serving any traffic to it. Yeah. I can't remember what the default NGINX response is. Is it page not found or something? Probably. Yeah. I think you get no. So the NGINX container will deliver a high versus NGINX. Oh, really? And yeah. That should be relatively smallish. Yep. So we do have slums are running, and we have engine x. I refresh this page. Three milliseconds. See, good numbers. Alright. Now we still want to deliver our PHP application. So what I'm gonna do is we're gonna move this over to FPM.
36:12 Is there an FPM Alpine tag maybe? Think there is well, Alpine FPM. Let's jump over to up docker.com/_/pt. At the end. Okay. Yeah. There does seem to be an update variant. How does that run? So let's just grab the Docker file. Unless you know it when you just wanna tell me, but Has it for an entry point that if you specify a command that's a PHP command, it'll run it. Otherwise, it will start FPM. That's my understanding. Alright. So this points off to that should cost. Right. So assuming we're doing four Alpine FPN. Yeah. Runs that.
37:20 Well, it's not actually using the entry point unless that's I get it early. So f p m Fortnite thousand, and what path is this expecting? Think it's spelled w w w. So I haven't worked there. Yeah. Part of it, w h t m l. Okay. So we're just gonna change this to match. We don't need a command overwrite anymore. We're happy with whatever's going on on the default. As far as volumes go, we want our code in the right location, that has to be updated to that. That's okay? Looks good to me. Okay. So I'm gonna assume that's gonna work because if
38:18 I restart this, there's nothing for me to verify that this works. What we need to do now is tell NGINX how to proxy the request to FPF. And for this, we're gonna need a configuration files, but I'm gonna create an opt directory. NGINX inside of that, my b host starts on. Did you bring the engine x configuration with you? I've never I've copy pasted it from project to project and then got someone to look at it for me. And it kind of annoys me that I need to do that. So I think there's a gap in the
38:59 market for an image that is just the NGINX container with a sensible PHP config? Well, after the Ladakhon talk, I had to push this up. Mhmm. So we're just gonna grab this. This is what it looks like. So we have the server definition that runs on port 80. This is using app. It's just naturally pass up by any complication. Yeah. Our application is leveraged from a public. Yeah. And this is the old way. So that is the one where you would need to share the volume. So I'm gonna leave that and so that we can actually see that
39:51 there. Yep. And then yeah. Sorry. On you go? It comes from the the model where your NGINX is also gonna serve with your static assets. Right? In the because this is we have to sort of see if the file exists. Otherwise, we'll run it on PHP. Yes. So what this is doing, it's saying, if this URL doesn't exist, then pass it as a parameter, I believe, through the PHP so that we have this kind of single entry point, and then the PHP application handles the routing there. We can change the configuration of that so that it does the trial failed, but not
40:30 for PHP fails. And then for PHP fails, regardless of exist about, just send it to FPM and let it deal with it. Okay. But I think I I don't even think that's that sensible, really. Like, don't know if in production people do scale their PHP NGINX. Does the PHP NGINX have to be the same as the static assets NGINX? Maybe you'd want them to scale separately. Or possibly, when you get to some point, you're gonna use a CDN for your static stuff, and this is only ever gonna serve PHP. Yeah. I think there's definitely a few caveats
41:02 there. I probably wouldn't put down the route of having my static assets to leverage from something that wasn't delivering the PHP traffic because from a customer perspective, they're heading the same endpoint. Well, it's a static asset or PHP file, and I want that to be a one single entry point and not do any complicated routine there. Also, just because I'm reading this, this actually is set up to work. So that's location. But then we have this PHP filter which passes at 10PM. So that's actually should work. This this config is actually good. So as expecting,
41:36 something could test on a DNS name of PHP on port 9,000. So if we just change this from slim to PHP, that should immediately work without me doing any other changes. Need On the other. I to I can't even go. In the right place. Can you say that again, please? You need to map the config file. You are very correct. Need a volumes, and we need to take opsenginexbholes.com, and we need to put this in that may be right. Let's double check that. So I I think anything in conf d is okay. Yeah. But there is a default virtual, and
42:25 I want to replace it to avoid that configuration error. So if I do Docker container l s, definitely not bigger. And exec and stage, I keep trying to keep falling back in the bad practice. From bash, Let's go take a look at comp.p. So we should be able to override that. I I think well, I wanna get to confident, but that is enough. But we're gonna stop everything I'm patiently. I'm just gonna back it. Okay. So if I pop back over here, we have our PHP application. It's kinda getting longer there. Five hundred and fifty eight milliseconds.
43:27 But it is being delivered via engine x. Now we can confirm that by doing a. That looks like a slim error. Oh, no. Okay. So that failed us. Okay. So if we look at our virtual configuration, and the failed does not exist. It goes through index.PHP. So can we take that hold it out? That would mean that we would so we could, definitely. It just means that our entry point for this application would have to be that. What we can do is modify the Internet. We we can drop something into that in the next container. Right? So
44:20 just to show that it works, we don't need to do anything else. And so that would be six. Right? Of course, it would be that's the name. We can go to w w w HTML. That totally wasn't the path for the No. That's that's correct. We didn't mount any of the files into NGINX. So make it work for now. You can do a touch, even, dot HTML. And so Yeah. And in theory, we can now skip this this could be a static asset, essentially. And we can say here. Okay. Few milliseconds. Much faster. Mhmm. This is why p this
45:27 is why you don't want FPM delivering a static asset. Mhmm. Okay. Where is my code? So we're happy with that. We're happy with that. What's next? Well, do wanna go into making a Docker file? Building the stuff? Should we try and speed it up? That's part of it. Build build the stuff into the container so we're not don't have to map as much stuff. Yeah. So the hypothesis there is changing your composer dot JSON is a very rare activity. So could we use Docker build caching speed up that process? Let's tackle that when we do it. Yeah. We
46:27 so we are gonna add a Docker file, and then we're gonna try and deploy that to Kubernetes. I'm just wondering, is there any, like, from a local dev point of view, is there anything beyond this that you need? Like, I can in fact, we haven't shown one thing. Right? I can change the code and then immediately I see something happened. Right? So I have no idea where that index page is. And you left the containers running, didn't you? I did. Is it action? User action? Is that the default page? Yeah. Maybe we should've used the framework one
47:01 of us knows. Oh, because I've created that directory with permissions, haven't I? I'm not sure what I've done there. Let's fill up my logs. I tried deleting the folder. That's all you did, just touch that file. Okay. So we're not getting an index now. So we don't actually want an index. What we want is go here. Engine. Maybe try just taking the folder out of the container the running container, or kill the container and start it again. That will definitely fix it. I'm just alright. Okay. Yeah. We don't need to publish the numbers. You're right. We're all over.
48:11 Maybe we can. So that just says hello world. So let's just search for that. So it wasn't actually in any of those things. It's in a roof tile. Okay. So we should have real time development experience. And, actually, I think with a project this size, that is an acceptable performance. You know, if you don't wanna get too stuck into how Docker works and you just want to maybe run a really specific version of PHP that you don't have you don't wanna install locally, that's kind of good enough. Yeah. I'm not gonna get frustrated making changes to that and and waiting for
48:57 it to reload. Yeah. Don't think I'm gonna do it. That fast when I'm developing. I'll make do some work and then go over and check it sometimes if I don't have a test. I've seen devs on big Magento projects waiting ten seconds for a page to load anyway. Right. Okay. So local development, I think we're okay on. The only thing we could potentially modify now would be ability to do you know what? That NGINX thing is really bugging me. We're gonna make that work. Okay. Because we're we're talking about how we want NGINX to lower static assets.
49:40 And the minute I did that, I broke it. You don't think we should fix that? Sure. The other thing I could look at is how how would we use a database? Yes. I'm open to anything. Do wanna what what was it that bothered you about the NGINX? Is that when you made the folder, it no longer looked at FPM? Correct. So let's let's do let's do two things. First, if you're watching and you want us to cover any other thing that we've been talking about, leave a comment, and we will make sure that we tackle that. Right? What we'll
50:19 do for the next few minutes is we will add that database component, and we will revert Docker Compose down to version two, explaining why that's important as we go. Right? Because we we we do have a migration step that we would want to run. So what's your favorite database? Favorite or one I use the most? Actually, the one you use the most, I guess. MySQL. Alright. Yeah. I'm gonna use a Myriad DB version. Oh, I don't know. Is Docker. It's MySQL, but not one by by Oracle. So Yeah. Yeah. Oh, we're kinda weird flips there. Okay. So we're gonna run
51:00 MariaDB. I think that it's starting as 10. We've well explored this in my local machine. I don't know why. But Yeah. You want to run a MySQL client in your ID or something at some point. Right? Yep. And then we will set this down. Let's spin this up. It'll kill the image. Nothing's really changed, but it should just kinda make work. So sometimes I have two two Docker Compose files. Right? One that adds a load of exposed ports and extra volumes and things that's explicitly dev. Yes. So, yeah, I forgot for this. And we need
51:42 to tell the really be a a few environment things. So we have to say, my SQL password password. That should satisfy that. We have a question from Glenn. He's just curious what does the Docker environment variable do. I think Glenn, we're just a little bit late, we'll cover that again. This is just a slam framework thing that tells it to tweaks the logging to standard out. So when you do container based development, you always want the logs here, first and foremost. Even when you go to production, the logs have to go to standard out because that's
52:26 how they're collected by the d, the bit, any other logs dash deal set. And and instead of settings, it's just checking for that environment variable that exists. And if it does, it says, okay. We're gonna log the standard out. Otherwise, it's gonna log to the logs directory. So it's just a slam framework thing. I'm sure there's a way to configure that for something in Largo too, but do examples of that. Right? We're there tomorrow. Now we should have my sql MariaDB slash thingy working. Yep. Can you connect to And the challenge is Have you got a local client you can
53:14 connect to it from? Or command line? You you trust your Internet connection? It's pretty solid. I get about 500 meg. What? It's pretty good. Sorry. Not not megs because we have the SI standards now. Mebi bytes. Mebi bytes. Mebi bytes. 500 bytes. 500 Mebi bytes. It's better than me. I just have not gone green. It's not according to the message. Alright. It's only, which means it's not to user local because it conflicts. So you have to add it to the path. Okay. Camera's going crazy. So we can do a post. Password. Password. Password. Okay. But it's not a capital h for what's
54:29 done. I like your security consciousness. Do you you don't have MySQL installed locally, do you? No. I do not. What's lower h? Let's do one two seven zero zero zero one. There. User I know. There we go. Nice. Okay. So that works. Now the the challenge with the 3.7 syntax now is that we have very limited support or depends on All that's except and the three point x spec is that. What that tells our Docker engine is that it must start the MariaDB container first. That's That is it. And that's so you can do Docker up Docker Compose up PHP,
55:00 Ditching compose 3.x for 2.x: leveraging complex dependencies with health-checks
55:47 and it will also start the other guy. Right? Because so far, we've just been doing Docker up. Yeah. So it means when I run Docker Compose up, that everyone will ensure that the MariaDB container exists, but has not started or health it exists and started, but not healthy before it starts with PHP one. Now that's important because the way that Docker Compose handles communication is generally through injecting stuff into the host file, and that name has to exist before that container starts for it to work. Mhmm. So we could add NGINX there as well. Right?
56:21 Yes. Now the challenge is when my PHP entry point is checking to the database to see if it has to run a migration, Is that MariaDB and most of our databases do not start as quickly as FDM. Databases generally take anywhere from two or three seconds up to maybe a couple of minutes, in worst case, before they can actually start accepting queries or rates to that system. Yeah. Which means that we lose that wonderful ability of just typing Docker Compose up and it working. We then have to start adding make files or batch scripts to do loads of checks.
56:54 And things get really complicated. Some of the think when the database arrives and then runs the migrations. Yeah. When I see people time after time I used to be a consultant, and every company I would go to had this batch scripts that tells the Docker log waiting for some random arbitrary string that may or may not change between versions of the database. That somehow indicate that it is ready to receive traffic. Doing this is okay. My equivalent is in the PHP. Run the migrations, catch the MySQL connection isn't there yet, and then retry it a few times.
57:29 So just try and run with migrations into into the database until you get a success, which is equal. Yeah. Of course. I mean, you could do that too with a restart policy of always and just keep going and going and going and going. But and we then the restart become meaningless. Right? We don't we wanna restart to me is important. When a container exits, that means either something needs fax, something crashed, not that something wasn't available. And those to me are very different finish scenarios that I'd like to understand when I build an application. In production,
58:08 you have things like Istio that sort of hold your database connection until the real database is there. And there's lot of very sophisticated solutions around it. But Yeah. So using a service mesh, which is Istio in production, would allow you to have automatic retries on that connection, which means your application would just be and it just looked like a really long request. So then you're still susceptible to time outs of the application or database takes longer than that than full up. That's a good point. And it's a really easy way. Like, I mean, that's it. Like, we do that,
58:41 and then this would depend on syntax, magically changing them to something that is actually useful. And so version two is still supported. Right? It's still current. There's still Yeah. It's just they're just desperate. I don't think there's been a new two point x release in a while, but with the open sourcing of the compose let's cover a few details here. Docker Compose. Okay. It's back. The best Compose spec website only came out this year. And as Docker is saying, right, we're we're gonna give up control of the spec. I said, no no longer something that we want to we want
59:18 to own. It's not our IP. We make it available to everyone. So now I I do think that there may be a new four point x that is catered for dev and production and Right. There may be changes that make this a different situation. But, unfortunately, for now, is that Docker or Docker did still load it. And what we've got here is 2.4 has been updated since, like, 2018, not five, four. And that's three point spec is continuing to evolve, but it has all of that stuff for production in it. So if we take a look at
59:54 depends on right. It's a very simple list where we can specify the service. However, if we change how do I get to it? Version two. Then we can still have the list. Right? So there's a there's it works on both. But, however, we have this more verbose syntax. Actually, it allows us to say we're not just depending on the service starting, but we actually want to have a condition. We will not start a web application until a, our DB condition is service healthy, and Redis is condition service started. And that's what we're gonna do here. So
1:00:49 What's the distinction with started and healthy? Started just means it's been the boot started, but it might not be ready, might not be healthy. Yes. So if we do NGINX conversion service started, that's no different than doing that. Those those are a problem. So that's not gonna stop PHP in from booting until NGINX has finished booting. It's just gonna make sure NGINX starts up. Yeah. So that's their primarily feature of the post naming tests. PHP will be able to resolve that somehow. However, for MariaDB, that's not gonna hurt. What we actually want is serve it,
1:01:32 help it. Yeah. Unfortunately, the last time I checked, MariaDB does not ship with any health check instead of a stock itself. So we do excuse me. We do have to provide a health check, and it's as simple as command, and we can then thank you for that. We can then use any command within that container to establish whether we think it's healthy or not. So if we jump into our MySQL container, anything we have available here can be a help check on that. I am sure there's probably some MySQL thing we could use. You could just try and connect.
1:02:21 Right? Connect to the local host. We could. Don't wanna do it with the MyoScale command because it will put me into some sort of shell. So what we can do is because this is batch, batch does some really funky tricks around TCP where we can actually do echo Let's just say, hi, dev. Net. I'm gonna get this wrong. 3306. Yeah. It's gonna get it wrong. Figure it. That's good. That's good. Actually had a problem once where we the health check was trying to connect internally in the MySQL container, and it was connecting, but it wasn't yet
1:03:18 accepting network connections. So it's it's accepting socket connections locally, but it wasn't accepting connections around through the through the ports. Ports weren't exposed yet. So it there we go. So that's only works with bash. It doesn't work with other shells, which is okay if I'm really deep because we have access to bash. But you can see that if we try and echo anything, they have TCP localhost and then the port number as batch will do a check to see if that port is accepting any sort of traffic. When we do three c o seven, a
1:03:59 port that we know doesn't exist, we get this failure. Because it's batch, we have an exit code which is non veto, and we can use that as a really rudimentary object. Now we're doing that for quickness and kinda speak right now. But if we wanted to, we could easily use something more sophisticated. We probably could do a MySQL port one to seven. That's root that password, and then passing a query, like, databases. Yeah. That would all be get on the calendar that it was healthy. This is what we're doing, but there's a problem with it.
1:04:37 It's like it'll still use the local socket rather than the network if it's present. Something crazy like that. So we found it unreliable, basically. Yeah. As my MariaDB and mySQL knowledge was just a little bit better, I would go with that approach because it gets me a more holistic view of whether it's healthy or not and not just a port being open. But it doesn't have to be perfect. So What should happen with Meridian Meridian b should ship ship with a health check. Right? That would be the ideal solution. The main thing I would want MariaDB to
1:05:15 ship with something that allowed me to do my SQL test connection like that. I know I I'm not a big fan of health tech and system within the Dockerfile. I'm sorry. I do I do think we should live as part of the Kubernetes manifest or something else. Or even in the Docker post file for development because, again, my health checks in dev and production are gonna be very different. I don't think they're always gonna be ubiquitous. So we're MySQL just having some sort of check, help, test command that ensures the database is ready is ideally what I'd want to
1:05:47 see here. Yep. Alright. For now, we're gonna do echo f p two p, little so forth, p p. And we're gonna make this fail to show that it works. But right now, this will never ever get healthy. Makes sense? Mhmm. Right. Let's set an interval. Run this every one second. Boom. Internal. We're gonna spend all this time. Actually, I'm just gonna destroy it. Everything oh, no. It's not command. It is test. Okay. So we're gonna spin this up. Now what should happen I'm gonna spin this up in the background, and then we're gonna run a Docker PS
1:06:41 and try and see if we can understand what's going on. So you can see it starts from really the first, then into next, and our PHP container is not healthy. I was just looking at the MariaDB documentation. It says it will not accept incoming connections until the initialization completes. This may cause issues when using tools like Docker Compose. Great. So they they're they're not shipping a health check. But that's good. The fact that they won't accept that connection is how the database is healthy means that our rudimentary track on the port will actually be really
1:07:22 good. So Yeah. Yeah. Good point. Now you can see I failed to bring up the project. That's because MariaDB is still unhealthy, and it cuts out. If we change this, Put everything back up. It's waiting on MariaDB. Oh, maybe my one second is maybe too quick. That's actually not getting help in thing now. Okay? Oh, so it waits it waits initially for that period before checking? It will wait three of those, I believe. Three. And so if we look at the specs So we have access to oh, no. Here we go. So we got test, we got interval,
1:08:21 and oh, yeah. We try. Okay. I still can't bring up my projects. Does MariaDB take more than fifteen seconds? Do you need to give a parameter to echo? We shouldn't have to. No. So it's still unhealthy. So that is actually failing. Now I think the reason ah, yeah. This will be Shell. We can't specify that here. Okay. We can copy this. Oh, that's close. Actually, what we want is test and we'll do bash dot c and then do echo dev piece of p and report. We'll do fix. We're actually saying use batch because we have
1:09:19 to have a batch shell for for this to work. I run a command, and then that should give us the export that we need. Let's try that again. Hey. So we did see. I think it's a question. Our engine access failing. We'll fix that. I don't know I don't know why that is. So we didn't have the health check. They'd have started in parallel. Right? They would have started in parallel. Instead, we were able to defer the start of the p to p container and tell MariaDB was actually out there. Mhmm. So NGINX failed because
1:10:13 that was working. Ah, so now so engine actually there we go. There's the reason that we need the the service starting. Engine x cannot resolve upstream FPM connection PHP because we got our dependencies a long way around. Yes. What happens if you put them both no. It's like, like, dependency would fail. But actually, what we're saying is PHP container has to be started, which means the DNS can be resolved before it interacts starts. Now we'll get a completely healthy system. Alright. Happy with that. Alright. So I ship this to production. Okay. So So it's like Heroku
1:11:00 Adding a multi-layer Dockerfile for build cache goodness
1:11:08 Go or something. No. We're gonna add a Docker file. And we're gonna leverage the build cache to make sure that we can we only ever do the composer install once until we change composer dot JSON. We also we're gonna use multi layers. So there's a really frustrating thing at the PHP images first. If we do seven, I'll find FPM. We copy our composer dot JSON. And when it's that working there, it's slash app. It copies this to slash app, and then I say run, compiler install. This will not work because the PHP images do not ship for
1:11:59 Composer. I find that just ridiculous. Like, people do not develop PHP beside Composer anymore. Right? People do. People don't. I can give you names. No. I don't want names. Just agree with me. So It's also a different organization. Right? Compose is a separate group of people. I do understand that. It's it's it's not part of the core PHP group and all that. And also, to be honest, I don't want Compose on my production image. So there's that that has a a good thing to it too. But it makes you know, from a new person that adopting containers and that's gonna work for
1:12:36 not having that available, it's probably annoying. So what we're gonna do is we're gonna say, let's have a development or build image. Mhmm. And I'm gonna copy from the composer image. You mean copy. Right? Not from Got it. I'm gonna do that. Then I'm gonna do my seven Alpine. We want our build image. So from build as we'll call this okay. I'm super cute. Call the top one, like, base. Base. Good call. From base as build. You you knew what I was trying to do? That is why you're here. And you want the copy to be after build in
1:13:30 the in the build? So this base image is just something that I can use as a base for the tools that I need. So I'm just adding Composer to PHP. Then in the build, I'm gonna copy and compose the JSON. We'll do the installation. Okay. Can you I just probably thought that's kind of agile. How tight would you be about the versions of everything? I know we'd in real life, would you be specifying a point release of PHP? Yeah. I probably would. I would. So do that. Point. And what what I like about this approach
1:14:08 is I can now use this here. But I've only got this defined once. You know, there's there could very easily be a version of PIVENCYR, and then I wouldn't know what I'm doing. So I could say build Docker file. We don't need to specify because it's in a default location. We need a context. And because this is multilayer, we need a target, and I could say use the base image to run this in dev. If I really want to add extra stuff to the dev version, I could then extend base here with dev, and I could add my dev
1:14:43 tooling. So and it's really common to say, let's have a k add update and say I need. XD book. XD book. Okay. I don't think that's an app. But I don't think it's an alpha and repository. Yeah. That that would work. So And often in different environment variables and thing I know you do that in the Compose. So I would put the compose a copy inside just the build image because I don't want that in my base. If I'm gonna use base for development, I will. I don't want compose with my dev container necessarily. You're be running Composer in your image?
1:15:21 In your container? For a local developer? Yes. You're gonna, like well, that's not the way I do it, but you're gonna be doing exec Composer install on the dev on the running dev container? Okay. So this is this is where the way I work is a bit different to most people. So some people will run composer install on their local machine. I'm very much against that because what happens there is you have to do no prereq check something. There's a flag that you can pass it to say, don't check for prerequisites on the whole, like, PHP extensions. Get a
1:15:59 really good example. What I generally do is provide a make file with a target called b shell, and that puts me inside of the container, and I work locally that way. Right. Because it's quite rare like you said, it's quite rare to edit Compose JSON at at at a certain point in the project. You're not you're not really editing it very often. I would rebuild when I edited it. I I just sort of remember to rebuild or do a make file that rebuilds it. Okay. I think let's finish off this Dockerfile, and then we'll show how that that step works
1:16:33 then. So Do you wanna build it now? Check it works? Yeah. So we have we had just have a couple of comments based on that conversation there. So we've got one saying someone at Mark, who says, I would run Composer and the container. You may have multiple projects for multiple Compose. Mean, I would definitely run that container too, and we get power team CI. I think that's so important. Like, me running make bells locally should be the same as my my CI. There should be no distinction between those two. Yeah. Okay. So we got front bell as prod.
1:17:10 And now we want to do our production image is gonna be Base? We didn't finish the build. Copy everything to that. That's the And then have so we want our base. F is gonna have a composure. We're gonna use a dev image as our base for building, and then we use a base image for production. And what we do here is we'd say copy all of our build artifacts from build like that. Okay. We can Yeah. That work towards here, so it only happens once. So we no longer we no longer have the Composer installer
1:18:14 on prod, is where I thought we were going. Got it. We've taken the Composer install out of our production image. And as far as command goes, I can't remember the FPM syntax. Why why are you specifying it? Why are you overriding the base images? Is that a best practice you think? K. You're right. I can just take that here. That's what I expect by default. Yeah. Yeah. Okay. Okay. So what does this mean? On the on line 13, you want one of them to say do you want to copy Compose a lock as well? No.
1:18:57 I don't commit my lock fails. I I would consider that a full path. Because, like, people can the only reason people the only reason you should commit your lock fail is if you need item for the builds. And I'm shipping as a Docker container. I don't need that. I have the cache. I have the container. I can reuse that cache. So if I need to have a specific build where I change something, I can then reference that. Commit in a log file means that I'm not gonna get automatic security updates. It just removes a lot of things that
1:19:29 we've made slightly uncomfortable. So it's because you're gonna be running your CLN smoke tests after the image is built anyway, you don't need the guarantee that the image is being built with a specific set of dependencies. I think committing the lock file comes from that workflow where you're you're running CI in just in PHP, and you want that lock file to reflect the set of dependencies that are gonna be deployed. You get that with Docker. Right? If if you Yeah. I have I have an immutable image that cannot change. So I I I wouldn't commit my log
1:20:01 file. I I don't commit my log file for any any code, any language. I I would commit so the the rule is I would commit my log file if I'm gonna be testing based on the log file and then separately deploying based on the same log file. So then the lock file's giving me that sync, but the thing I tested is the thing I'm deploying. With doc, you're not doing that separation. You're testing one thing, and then you're moving it into into prod. So you don't need that extra synchronization point. Yep. Good point. You. Let me regain my train of thought. Okay.
1:20:38 So we we we wanna build this now, and we wanna show how we're leveraging the build cache so that we don't Compose install wasn't particularly slow in this project, and it can take many minutes. Right? So we we don't wanna do that unless that fail changes, which is why we're being very specific about the order that we copy all of this time. So if I do a build and I target build, and I call this p I'll call it Rawkode build beside of a class with the PHP images that I have. I pass on the build front. It's what I'm saying.
1:21:10 Tag not target. Target. Tag. So I'm targeting my build layer, tagging it Rawkode build, and I've had go. That's 7.4. Okay. Let's take out the point, guys, and just find that. I don't wanna have to look that up, and that should be based. Anyway alright. So we run this. I got that wrong. PHP seven dash the FP and R plane. Yeah. Maybe. There we go. No. That's totally not take too long. Edge running the compose and resolve because this is the first time we a brand new step. Is this using build kit? This is using build kit. Yes.
1:22:12 That's why I have a template. Potentially, they're building parallel. If I had multiple layers where steps could be run-in parallel, yes, they would run-in parallel. So, you know, we we could we could show that working. We could add another layer that ran our unit tests, and those would run-in parallel with, say, something that was doing our completion of our JavaScript asset. But if those wouldn't conflict. That was what I was thinking. We this compose a step build step could be running and the MPM build step be running, and then dev depend on both of them.
1:22:45 K. Good. Mhmm. Now if we run this again, it takes no time at all. Nothing has changed. Nothing has nothing has to happen. Now if we change the target to production, the only thing that should run is this. We've got cached, cached, cached, cached, the production step runs. No. Why don't you make an edit to index PHP? Great idea. In public. Yeah. So I see million dollar change, and we want to rebuild our production package. And what the important thing that I I believe you're trying to have me demonstrate here is let me build our Docker file.
1:23:44 Is it the only so the way the cache of validation works in Docker is it starts at the top of this file and it says, has this has anything in this layer changed that would cause a rebuild? No. Has anything in this line changed that would cause a rebuild? No. Done. Done. Done. Has composed of updates unchanged? No. Which means we don't have to run Compose install. This copy has changed. We've just modified the index dot PHP where we're calling. So our cache is invalidated here, which means everything below that's in the file has to be run again.
1:24:16 If you change that fail, which means when I run this, the compose of JSON and the compose are installed do not need to run. Cool. Perfect. You can see our composer install and our cash composer JSON did not work. It's very fast. It just changed our code, produced the image, and that was it. Now if we change the composer dot JSON, the composer install would run again, and then every step will load out. Don't think we need to cover that. I think, hopefully, we'll explain that well enough that people understand the build cache and why that's really
1:24:55 important for both of these. Mean, that was ridiculously fast. That's what I want. And that's a good reason to have separate build targets for for the PHP compose we install and the NPM is that you'll only be invalidating one of them if if something changes. So having a kind of diamond shape to your targets where two extend this one and then something depends on both of them can really help with builds because you the cache, like you say, is sequential Yep. In terms of layers. So splitting things out and then copying the finished assets into
1:25:30 the final image can be really helpful. Oh, yeah. Definitely. And I guess it's also worth pointing out. This is not unique to Composer or PHP. Any any language with a package manager will have a file like Composer. For Rust, it would be cargo dot tunnel. For NPM, node based stuff is package dot JSON. This exact pattern works for all of those languages frameworks and run time. And is it based on the hash of the files? It is. Yes. Yeah. So it's the collective hash of the file system. So because the only thing changing at this layer
1:26:09 is that one file, they only have to check the hash of that one file, where it's a whole directory like that. It would be a cache of everything together as a song. Yeah. And that and so it's important to also include things in Docker ignore because here where we're copying everything into the image, it's important anything that's gonna be a transient file or change over time should be Docker ignored. Right? Very true. Which maybe it is, one. Yeah. So there are a lot of things here that we would not expect to trigger a rebuild, And and the best way to handle that
1:26:47 Yeah. So if if you're refreshing the cache locally, you don't want that invalidating your builds. Yeah. Or even just a readme. Like, do I want a readme to cause a rebuild with this application? Yeah. And it also stops it being in the image as well. Right? It does. It will so the way this works is it won't if we look at this command, on the end, we have this doc. That's called the build context, and, like, Docker, as sophisticated as it is, this is not sophisticated. That dot means that the Docker engine will actually charge that that entire context, that directory,
1:27:24 and send that to the engine. And that context is what ends up in your image if you copy these things in. So that's what if we have a dot Docker ignore, those files are stripped at the context stage and never actually touch the Docker engine ever. Does that make sense? Yeah. Really. Alright. So it all gets zipped up or something. Right? And then transferred or tarred up? Yep. Yeah. There's there's I mean, we we we work mostly on local machine for the Docker engine and the context are are available locally, but that's not always a case.
1:27:56 In fact, Docker expects that Docker engine to be remote. In fact, Docker for Mac is at the engine is actually on a VM. So it's it's it's all tied up together and sent over the pay under the VM where it's then analyzed, extracted, and Is there we've not got any more questions. So how are you for time? I need to go in about ten minutes. But you I mean, you could continue without me. No. Let's finish up in ten then. So if you are watching and you've got any questions on anything we've covered or anything we've
1:28:32 not covered that you want to know or want us to explain, there is your chat. This is it. You got ten minutes. So that gives me ten minutes to then shift this image to Kubernetes. Perfect. So would you would you do a similar build step for the NGINX just to get the config file in there? Or would you leave that to later? No. I I wouldn't. I wouldn't use a proprietary image for engine x and Kubernetes. I would use the official upstream ones, but provide a volume down of the config for config map. So if it's just the config,
1:29:00 Deploying our application to Kubernetes
1:29:12 you'd always use a volume for that? Yeah. I would I wouldn't build an image for that. I just don't think there would be any value add on on doing so. Because remember, I I then got to store those images on my own registry somewhere Mhmm. Which means I'll I don't have to handle regular maintenance. I have to rebuild it regularly so I'm getting security updates, patches. I have to I just create analysis of it. All that stuff that I really don't wanna have to worry about, but all I'm doing is modifying one piece of configuration.
1:29:43 Yeah. Okay. So I use my opt directory for stuff like this. I'm gonna create a Kubernetes, and I'm going to have our PHP, FPN deployment. Yeah. No. And I have a nice extension, which just kind of templates it all for me. Oh, no. Oh, that's it. Oh, it's on the back. Come back to the same. Yeah. K. Right. The image which I built was called Rawkode build. I'm not gonna change that, so we could do this. I'm not gonna use any resource constraints for the time being. I am gonna keep this lame here because if I remove it, I
1:30:40 get a little bit squiggles. And it's just telling me, hey. You probably don't seem to deploy this into Kubernetes without having resource constraints. And we're gonna say that our FTM runs on port 9,000. And that's it. It's good. Right? Next, we need our PPM. The next private So we'd so you'd be building that image as part of your CI pipeline. The PHP PHP, FPM, Apple, and the FPM. Yeah. So you wouldn't so is is that the best practice? You don't build those things as part of your Kubernetes deployment process. You you develop things locally. Something like CI then pushes an official image
1:31:32 somewhere after tests have passed, and you're deploying off of that. Yeah. I'd have something that then runs inside of my Kubernetes cluster that would detect them as a new image and do an automatic roll out or deployment on it. Right. So it's not pushing out to Kubernetes. It's pushing through a registry and then Kubernetes Yes. I would never have my CI pushed to a Kubernetes cluster because then my CI has to know that the cluster exists. Yeah. It makes it really difficult to do a femoral cluster. So if I wanna spin up, like, a pre prod or a temporary
1:32:02 environment to do some testing Gotcha. I have to build that into the CI step, and I I always try to keep those separate. So instead, what I'll have is you push the image with a certain tagging format that may say this is good for production deployment, and then have many environments that can pull that through. And and it's in the test environment, which have a slightly different tagging part. Right. Okay. So we're gonna call this engine x. We get default engine x image. There we go. This this is available on port 80. Excuse me. Okay. Now we need to config that
1:32:50 For quickness, I'm just gonna copy this from here. Normally, I wouldn't do that. And I would have custom so there's a tool called customized. It's a built in the acute CPL. And I can actually just create the conflict map for you from a local file. So that means I don't have a duplication. And if you wanna read about that, it's on customized.io. You would go to docs and There's something there. You can tell to fill in a file as a config. It's definitely there, promise. So I think I have to know one more. Recall that's thedefault.com
1:33:57 because that's what the file is called. Now that's changes. So we can either have a service for PHP or I could do something else. Oh, in fact, I've got these as two deployments, which I also wouldn't do. I guess it doesn't matter for now. We could run NGINX as a sidecar container in our APM. What what do you wanna see? What's more important to you? Can you define sidecar container? So in a pod in Kubernetes, a sidecar container is generally what we would call any container beyond the main container that provides new functionality. So we could have
1:34:31 a deployment that runs at the end, and then we could deploy a safeguard container. So Nginx container next to it where they share the same namespaces, which means that they can communicate over local. But let's let's do that because yeah. Let let's just do that. I think that makes more sense. It's the that's how I would do it in production. There's no point of me changing from what I would normally do. But Do I have a So we just add a new container. We say we're gonna augment our PHP application with engine x Yeah. As a port.
1:35:05 And then in my configuration map here, I'm actually just going to use Rawkode. It's on port end side. They share the same networking namespace. That means I I don't even have a network hop to go to get to it either. But performing cool. The only thing I need to do now is map this config map. Now this is where I'm gonna forget the syntax, but let's see what happens. We need to find a volume called m g n a config, which is that config map ref, the name of and I think I saw that I
1:35:37 did that. Yep. Yeah. So now it makes us available to me and it can be consumed here. That's volume mounts, and we're gonna mount this to end of x conflict d, and we're going to mount our end of x conflict. Now I only want to mount a single file. Default. Yeah. I do that. Okay. I think this may work. It might work. It may work. So what I'm gonna do is that we're gonna shut down all the Docker Compose stuff. My Docker Hub app is already running Kubernetes. Which is a tick box in there, isn't
1:36:26 it? You just turn it on and it works. Yeah. You can see it's running, and it's just under preferences. Not the fastest application. Yeah. Tick box. So I can now do a quick control apply dash off Kubernetes. And let's do the this one thing up first. Running. So you can see we have two containers. It's running. It's hopefully still running. I'm gonna do it as port forward. So this part well, not import eighty eighty. But before I do that, this will be broken. And then k. We now have our two container pod with NGINX speaking to FPM
1:37:46 over local hosts and look at the response time. Drastically better than a PHP building web server. And even better than the two container setup locally as well. Drastically better. Because of the lack of volume mapping. Right? So all the files are inside the baked container that you built? Yeah. This is our node detection image. There's no network traffic between NGINX and FPM, which makes it a little bit faster. Even with the two containers locally, they'd still be going over a TCP connection. There would still be a little bit of latency added there. Right. But that's just really fast.
1:38:18 Awesome. We had We've I have to leave at this point. It's been really good, David. For I've I've learned a lot of things, so thanks for inviting me. No. You're thank you for joining me in. Know, pleasure. Enjoy the rest of your day. I'll do another five minutes, and I'll finish up. And I'll be I'll watch the end when I get back. Bye bye. See you later. So we just had a a question that I'll tackle there quickly is does this mean I cannot scale engine x and FBM independently? So that is correct. When you're using multiple containers in a pod,
1:38:58 yeah, you scale them together. If for your own reasons, you don't want to take that approach, you would deploy them separately. And and that would just mean, you know, taking that out, creating our FBM engine x deployment instead. We call engine x. We update the I mean, to that, I can just but it does mean that my contact map has to change because we are going over the Kubernetes TCP stack now. So instead, I'd be looking for something that resolves to PHP, which just means I have to expose a service for our APM. So I wanna call it PHP, and if
1:39:47 we look at the selector, I can see that we have a label. It's all at PHP FPN. So I can do at PHP FPN, and the service is going to map port 18,000. And now our engine x, we can scale that independently. So we can do a delete deployment PHP SPN. I'm going to reapply all of our manifest. Got something invalid in engine x. Oh, yep. The volume, and I didn't stop you across. We apply all of those manifests, and what we have now is that we are PHP FPM is already running, engine access to those can be scaled independently.
1:40:49 Fact, I could come into our FPM and modify the spec replicas. I'll say two. And and, in fact, let's say, five. I don't know if these are all scheduled based on my laptop constraints, but we're maybe two NGINX and five FPM. We run apply again. And there we go. We've got five FPMs to NZMX. Now what's different from the last setup is that we now have a service of PHP. We can describe that service, and we can see all the endpoints from all of the bugs. And if I port forward, forward engine x, grab the first one,
1:41:43 Then that works. In fact, then going over the TCP stack hasn't really modified the time too much. It's still really bad. And that's it. So now you can choose whatever fits your model. Because I mean, the overhead of running NGINX is relatively small. Running as a safeguard at APM, I generally think it's okay. I'm not throwing away too much resources. I'm gonna be consumed. But if you do really want to deploy and scale them separately, then that would be how you do it. Alright. I'm going to delete those resources. They're all going away. Next steps. So
1:42:37 I am going to push this repository up. I've just forgotten where that was or it just joined. It is on getlab.com/rawkode/phpexamples. The codes are from today will exist in Kubernetes and Docker. I do plan to do another session either next week or the following week, where we talk about using Kubernetes for local development as well. We're just, you know, removing Docker completely. Because it was kind of a couple of questions early on, I will also push a Laravel version of this some point today or tomorrow morning. The only difference is gonna be between Laravel and the slim framework stuff that we did
1:43:19 today. It's just that it will have a little bit of extra automation around handling that build step with the Mode. Js stuff. The only thing that changes is just the Docker file has another layer that does the the asset compilation, and that's it. There's not really a lot extra to add there. But I I wanna make sure that I provide everything that's required. So let's do that. Thank you very much for joining me. Hope that was useful, and feel free to leave more comments. You can grab me on Twitter if you want to ask any
1:43:52 questions afterwards. I'm always happy to help people out. Feel free to get in touch, and thank you for joining me. It's been good fun. Have a nice day.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments