Overview

About this video

What You'll Learn

  1. Containerize a Laravel application with docker-compose using PHP-FPM, Nginx, and MariaDB.
  2. Build a multi-layer Dockerfile workflow that handles PHP dependencies, extensions, and environment files.
  3. Deploy the resulting app image to Kubernetes while considering migration strategies and rollout behavior.

Ciaran McNulty joins David to containerize the Ping CRM Laravel app from scratch: building a docker-compose stack with PHP-FPM, Nginx and MariaDB, layering a Dockerfile with PHP extensions, then deploying to Kubernetes.

Chapters

Jump to a chapter

  1. 0:00 Holding screen
  2. 1:30 Introductions
  3. 4:30 Looking at Flarum
  4. 10:00 Switching to pingcrm (Thanks @bowersbros)
  5. 14:00 How specific should I be with container images?
  6. 17:00 Adding MariaDB to `docker-compose.yml`
  7. 19:30 Adding nginx and fpm to `docker-compose.yml`
  8. 24:00 Dependencies and health-checks for `docker-compose.yml`
  9. 33:00 Fighting with `.env`
  10. 56:00 Interactive development with a the Docker shell pattern (dshell)
  11. 1:02:00 Add a multi-layer `Dockerfile`
  12. 1:04:00 Adding PHP extensions to the container image
  13. 1:11:00 Adding a `Makefile` to codify commands
  14. 1:21:40 Fighting with nginx
  15. 1:40:00 Deploying to Kubernetes
Transcript

Full transcript

Generated from the English captions. Timestamps jump the player to that moment.

Read the full transcript

1:30 Introductions

1:52 Hello, and welcome. Hello. How's it going, mate? Good. How are you? I'm very well. Thank you. I'm feeling good. I'm excited. Yeah. It was fun last time. It was. We I feel we got quite far, but the requests were unanimous. People want to see how we do this with Laravel and all the bets that we didn't cover. The bets we left to the reader's imagination, if you will. So are we assuming people saw the previous session? Yes. Yes. We are assuming that people have seen the previous session. Although, to be honest, we are it doesn't matter if it doesn't.

2:38 We will do our best to keep things explained as we go along, and we are starting with a Laravel project that has no containerization at all. So we are gonna be doing a little bit of a repeat, but hopefully much faster pace than last time with some of the core steps. And how's your Laravel? Non existent. Wonderful. Wonderful. Like, few versions ago, I did the demo. But Right. It's all sensible stuff, isn't it? And I think a lot of the things we're gonna talk about, the general way you containerize things, how you build assets, how you do various things are gonna be

3:23 applicable across all projects, aren't they? Yeah. Hopefully. Well well, that's yeah. They will. They definitely will. I don't think there's gonna be much super Laravel specific stuff to how we how we can turn Razer. Yeah. Well, I mean, let's set the context then for the people that are watching. Okay. First and foremost, I'm gonna look away from you so I can click over here. We we are taking comments. If you wanna ask any questions, if there's anything that we don't explain very well, drop it into the comment section on YouTube or Twitter. We'll keep an eye on it. Also,

3:56 if you need a nice, easy way to find these streams in the future, you can go to lot Rawkode.live. And for anyone that wants to keep a conversation going afterwards, there's a slash chat, which is a Discord server. Feel free to come and join us there and ask questions when we're not streaming. B, I'm getting better at this. Okay. Now Now we're gonna set some more context about what we're actually gonna do. So I am just gonna pop open the share here, and we have my screen. So how did I pick the project to work

4:29 with today? Simple. I went to github.com/topics/Laravel. I filtered on PHP and most stars, and I scrolled down this list until I find a project that had no containerization whatsoever. K. So, obviously, we're not gonna do Laravel itself because we're looking for a project that has all of that say through stuff, migrations, asset completion, etcetera. I did consider using this Coel project for music streaming. It looks really cool, but I wanted something that hopefully more people are actually using as a development platform rather than maybe as a tool to consume where you would just use Docker files anyway.

4:30 Looking at Flarum

5:12 There's a few library based projects and projects, and then I got the Flatum Flatum. I'm not sure how to pronounce it, but I'm sure I'll change it randomly as I go. But this project looks cool. It's like a forum, retinal larval, and it it checked all my criteria. My criteria was no containerization. You can see there's no Docker compose here. There's no Docker file here. We get to do everything ourselves. Yeah. There's not much there, is there? No. It's got a pretty solid amount of stars. You know, it's pushing on 11 k. It's got, you

5:50 know, over a thousand forks. Feels maybe got a vibrant community contributor list. There's 20 contributors down here. So PHP, it's Laravel. I mean, I think this is gonna be a pretty cool project and a fun one to hack on today. But I have cloned it. So I just did this a few moments ago. You can see it's actually cloned here. Alright. We've got the files. We're starting from a fresh a fresh slate here. But, I mean, I think I'm hoping this is an I do a math, a very Laravel, I don't know, steady technical application that we could just go from start

6:39 to production and cover all of the steps that we kind of plucked around last time. So I guess what makes sense first is to just copy what we did last time, and that's what just get it running and understand the steps that are that we need to go through to make that happen. That sound good? Yeah. And I can see that they've got a installation guide. Where we are going, we don't need guides. Well, it's interesting to know what how they say to install stuff, don't they? Alright. You go your way. I'll just I'll have a quick look and

7:16 see if there's any gotchas in it. I'm just gonna run composer and so first. Like, let's let's see how far through this we can get. You take a look at the gate. The in fact, I'll pull up the gate too. Let's do it. Where's the installation gate then? Is it here? Oh, there we go. Docs.florum.org. Yeah. Yeah. Click that. So they give you installation on Apache or NGINX. You need something you need MySQL or MariaDB, and they're specifying particular version of PHP. That's it. Ideal. So we have our first warning comment from Alex who has said that this does not look

8:08 like a standard Laravel application and that the composer JSON doesn't even mention Laravel. Now I did notice the lack of Laravel adhere. I'm hoping that that it just encapsulated in platinum core. I know that's an assumption, but I'm gonna clarify that now. So If you decompose an info in your shell, does it have Laravel? Yeah. There's loads of Laravel stuff in core. So what was that command? How can I confirm that here? Well Composer Composer info after this. Yeah. Let it install. See, but I could still eliminate. I'm pretty sure that's a Laravel thing. I can see

8:52 Yeah. I'm not seeing Laravel Laravel. So your your concern is it might be too nonstandard? I wasn't concerned. Alex is now mainly concerned. Yeah. Within the first six minutes of the stream, choose me. So I guess the question for me is, do you do things that a typical Laravel project would do, for instance, to run migrations? Okay. So if I grab composer info for dash I Laravel Mhmm. So maybe they're just using a load of Laravel bits. That's frustrating. Yeah. Yeah. Okay. Now is there enough Laravel here for us to continue? No. Illuminate is the database package,

10:00 Switching to pingcrm (Thanks @bowersbros)

10:00 so we can show migrations. Right? Even if they're not Laravel specific, hopefully, that's not gonna be too different. Right. Okay. Right. Alex, you've been so Alex has suggested we take a look at anterior patent CRM. So we're gonna do a last minute switch. What's the worst that can happen? And carrier Peng CRM. Oh, and they actually alright. Okay. Right. No Docker set up. Alright, Alex. We'll do it. You have persuaded us. I mean, it's only got 670 stars. What's the license? The GitHub license? That just means that everyone can use it. Could it's something could

11:03 it's something that can't show on screen? Yeah. That's fine. Okay. We've got installation instructions, MPM, composer. Okay. I'm happy with that. So we've got some artisan here, which I believe is the OLM part of Laravel. So we will we'll put it. Let's go for it. So I will clone this. Artisan is like the task runner. Okay. Task runner. So let me pop that open. It means I'm gonna kill this live share, but I'll send you a new link in case we need to do any pairing. It should automatically copy to my clipboard. Copy. Copy. Copy.

11:53 And we'll get started. Alright. So I need to run composer install again. Wonderful. Let's just get a Docker Compose file started for now. We get some of the boilerplate out of the way. Yep. We're gonna go with version two. I covered this in the last session about why I prefer version two over version three. And and it's really because we want to be able to orchestrate our we wanna be able to use health checks to guarantee the order in which things are starting. I I think I I wouldn't spend five to ten minutes to get over why that's important, you know,

12:41 as much as we want parity between our development environment and our production environment. Visit the workflows with development and production are extremely different. So watch the last video for for more information on why version two is important. We will call this service PHP. I will just add a very base PHP image for now. I'm not gonna do anything else. And I'm assuming we're going to need a database. Looks like it. Yeah. Alright. We have another question from Pierce. So are we taking a Laravel app and building Docker into it? Yeah. We are. We did the session last

13:20 week where we took a PHP application. We showed how to build a development environment using Docker Docker Compose, but then take that automation and use that to build a Docker image that can be shipped to Kubernetes on production. The feedback that we got is that people wanna understand how to handle all of the the deployment steps that we cannot flock over. Database migrations, asset compilation, and a few other recipes running these tests. That's what we're gonna do today. So we're gonna use Laravel. We're gonna get development environment running with Docker and Docker Compose again. We'll do that relatively

13:54 snappy, and then we'll start to talk about the Kubernetes aspect and how we run those migrations in a production like environment. Yeah. It needs PHP 7.3 or better. Yeah. So we're just on PHP seven, which we cover as the latest anyway. So you you don't you don't nail it onto a specific one for dev? Yeah. I should do this as if I was doing this for production. I will. Okay. So what I would normally do is go to here, Docker Hub, go to the PHP image. I can set the cookies, tags, and I thought we had a 7.4, but I

14:00 How specific should I be with container images?

14:39 guess I was wrong. Yeah. There is. Seven four out of time. We were using last time. But in a general sense, do you not do you not specify a minor version? Would would you do you think it's important? No. So I like to take as many updates as possible. So I don't commit lock files, and I don't generally pen to minor versions in a Docker in a Docker container. The reason why would be that I always wanted to pull the latest and greatest, and then I wanna trust my continuous integration and deployment platforms to detect

15:21 when something goes wrong and block that pipeline so that I can go and investigate and fix it. Right. Which means if I do pin to seven and seven point five came out and there was some sort of regression, my CI or my test would pick that up, and then I would be able to go and make my code work with that latest version rather than just pan into 7.4 and then 7.7 comes along. I've got three releases of things. You know, that that surface have changed. It's just much bigger, and I'd rather deal with really small incremental continuous updates than large

15:54 ones. But you gotta have to take one in there. Yeah. They would do try and sort of lock everything to specific versions as a sort of safety net, don't they? But you're saying that the CI is it okay to break the CI every so often? Yeah. Definitely. You know, and then track your continuous integration. Track your test. You know, if you've got enough of that if you're getting so much ACG's or set one incidents or anything like that because of that approach, then your test coverage needs to improve, the automation needs to improve. You know, there's

16:29 there are symptoms there that could be worked on, and it doesn't mean that everyone can take this approach that I have. I generally I I think I spent and invest a lot of time in the tooling to make it work. And it's a journey, so it's a scale of zero to 10 and how close you can get to that continuous automatic update. Okay. It's not for everyone, but try and get towards that stage, I think, is really important because then your team can focus on the more fun stuff. Right. Let's let's go with I'm just gonna have a seven APM after

16:59 that. Okay. Now let's take a look at these installation instructions again. We don't need to play with them anymore. There's much there. So there's NPM stuff. So you run composer. You run NPM. You use some database stuff that I think assumes you've got a database. Okay. So we're not gonna use SQLite as the easy approach here. Let's MySQL MariaDB Postgres. Where where's your poison, Kieran? What do you want? Don't mind. MariaDB. Believe it's version 10 currently. Okay. Now we're I can start filling in the environment that we need to configure this. But let's just walk through those steps as well.

17:00 Adding MariaDB to `docker-compose.yml`

17:52 If I didn't know, then I would come to the MirrorEye DB image. I can scroll down, and, eventually, we'll get to these environment variables that allow to configure how this runs. So we're gonna do a random password. Nice. I've never used that. Yeah. It just means that we're gonna force our automation to use a very specific username and password that we also can better for environment variables. This is how it would run-in production. We wouldn't run necessarily with the root user and our production DB, but I'm not gonna do that here. Which means we can now specify these two

18:34 fields, the username, password, and the database. So we'll do that now. Mhmm. And we'll call the user and CRM, and the password is gonna be the same CRM. Not very secure, but it's dev. It's okay. What else do we need? Database. Okay. We'll get the database and name. Ping CRM? Yeah. You got it. So let me explain something else that's weird about this configuration. The MariaDB container image wants to be a drop in replacement for Oracle's MySQL, which is why these environment variables are called MySQL. So you can swap these out as you wish, and they just work. Nice night.

19:29 Now that's the database configured. So once we've run that, we'll have a an application server, an FPM server, and a database. We need a web server. Correct. Well, we will need NGINX. And we're gonna make that available locally so that we can actually browse through it. We need our code inside of this container. We wanna work in there as that code. So these two go great together. We can set the working directory, which means if I exec into this container, I'm gonna be next to the code where I'm at it, which is also here. What else does that PM require?

19:30 Adding nginx and fpm to `docker-compose.yml`

20:17 I'm assuming we're gonna have some cache and logs to deal with, but not sure what that looks like yet. Yeah. We'll find out. Alright. Well, why have I got a right. Oh, okay. Because this is on a a list. Okay. Looks okay. Yep. Alright. So Thomas is reaching out with a comment. Using Docker plus b three would allow you to use secrets and the MySQL password file. Yeah. And there are just too many disadvantages. Like, if I were to use version I really recommend watching last week's video. It's at Rawkode.live. And the steps that I would need to take

21:02 to orchestrate or or script around starting my database container and then waiting for it to be healthy to receive traffic and then spinning up in the next to that game. It's just it's just too much work. And as much as I would like to use the secrets locally, I'm happy to have met that for an environment that I can spin up with a single command. And I think for developer onboarding and quality of life and working on the project at the end to end basis, that is more important than consuming something like the secrets. I think that's a good example of what

21:34 you were talking about, the version three is more oriented towards deployment into prod as well because managing a secrets file is very much the sort of thing you do wanna do in production, but it can probably overkill for a dev environment. You can just set there's no point having a MySQL secrets file that just has ping CRM, ping CRM, ping CRM, and it's a credential. Yeah. My my goal is to so our our number one objective is never to break the parity between development and production as far as operating system, dependencies, extensions, if that's all. But when it comes

22:09 to the dev side, I want developer happiness and experience. And when it comes to production, I want security and resiliency. And Yeah. You know, those are I can't have both of those at the same time. And I'm not gonna settle for anything in the middle that satisfies dev and prod. So just separate workflows. Appreciate that and make it work. Yep. Cool. NGINX, FPM, MariaDB. Think we're got a certain point. Yeah. We need so we need an NGINX config file, so NGINX can find FPM. And we need to know how the PHP app connects to the database

22:48 so we can set some environment variables so it can find the database. Perfect. So you're right. Let's get that engine x config here. And they don't provide one, do they? No. Laravel did. Just saying, guys. Let's steal the Laravel one. It was a dot fail. It's literally complicated, though. Have you got the one from last time? I do have the one from last time. Because we've had a very cut down one, didn't we? Okay. So that was under slimopsenginex.com. I'll just copy that there for now, but we now have our v host file here. Which is very basic.

23:33 We might just need to change that. Do we need to change that route path? No. I'll just make sure that's where I'm at. We're not delivering any kind of thing yet. We'll do that if we have to. I don't think we I don't believe we have to just yet. Okay. Let's get this working there. So if we do a volumes here, we mountourvehicles.com, that's etcetera, nginx, B default.com. We also need some dependencies. So nginx uses as you can see in this vhost.com, nginx uses this host name of PHP, which means if we remember from last time, that

24:00 Dependencies and health-checks for `docker-compose.yml`

24:14 container has to be created first. Otherwise, that resolution will fail. Yeah. Because we're using version two, we can take advantage of the my editor seems to go that we are going to this syntax. PHP open editor condition service. Now we don't need it to be healthy. We just need it to be available for the DNS resolution. However, our PHP application has a dependency too, which is gonna be MariaDB. Mhmm. And the condition here, we're gonna make service healthy because we're gonna automate running our migrations. Yeah. Potentially. Potentially. Which means we need to add a help

25:09 check to this. So we can do a help check, We have a test. We use the shell syntax last time. We said run bash bash c. And this is a very crude health check, but it's fast. We did cat of dev. Oh, is it P2P? I'm gonna get this wrong. C p o six. Cat from or cat to? You you're reading from it? Yeah. I'm just trying to I I could pretty much do a cat or an echo to it. It'll fail as that port isn't opened. I'll show how that works in a second. We'll spin up my

26:06 container and actually run that command. Do need an angle bracket in there? Cut to No. It should be okay with that. Let's let's let's check it for it. So container run. You know, I'll enter active. We're going into MariaDB 10, and I will we'll just run bash. Start Docker. Why do I always forget to start Docker? It'd be nice if the command line had a do you want to start Docker? Yes. That would be nice. Alright. I'll just take a few seconds. I mean, it's just because you've got a Mac. Right? Like Yeah. I keep debating them back to using

26:50 my my Linux machine for day to day, but I just I'm trying to persevere with the Mac right now. So Alex has left another comment. Thank you for making this interactive. It's more fun when you leave comments. If you want standard NGINX stuff for Laravel Yeah. Next? I'm hoping what you're trying to say there is Laravel possibly has an NGINX virtual host. If it does, drop the comment, and I'll I'll just use their NGINX config straight up. If we have any problems with it, I'll make changes. I want this to be super Laravel specific. Send me a link, please. Okay. Let's run

27:27 this again. And the syntax we're using is batch test c cat dev p t p localhost p t o six. No such bad directory. So I probably just got that wrong again. It's a net. Oh, I think it is p t p. This is looking up from last time. One day I'll remember that syntax. It's a really cool hack though. So Yeah. Because I've seen people running curl and stuff, you know, and having to put something like that into their image, whereas this is bash built in stuff, isn't it? Like cat. Yeah. It does. It's the TCP.

28:11 Well, but I did an echo instead of the cat. Okay. Maybe that is important. So echo TCP local host. So yeah. Because that's better. So what Bash does is Bash provides a virtualized TCP stack through dev slash TCP, which means that you can put in any IP address or or domain name, like, .com/44three, and it'll test if it's open. If you don't get a response back like that, it means it's listening, and you can actually echo it as response code. I wanna see whether Google is responding on 1443, then, you know, that's gonna change that.

28:52 The local host, you won't get a timeout. You'll just get a direct error saying, hey. What's happening? Yep. So only Bash provides that. I mean, if you're using Alpine as your base, I mean, she won't have access to it unless you install Bash. But it's a really nice, great way to do sample port based health checks when that's all that you need. And as we discovered last week, MySQL and MariaDB only open the socket when the database is ready to receive queries or commands, which helps. Very helpful. Okay. So let's make that an echo there.

29:25 So now we have a help check on our MariaDB. We can depend on it here. Now what that means is that we now have a single command to spin up all of our services, which should work. PC is just an alias I have for Docker Compose. I should be able to run up. Oh, yep. So two doesn't work. Has to be 2.4. And it's not oh, it is test. Right? Command or command shell. No one ever remembered all these things. Okay. Cool. Okay. So for the environmental stuff, for PHP finding the database, Laravel, I think, uses dot env often

30:28 for config dot env files. Alright. So, yep, we have a dot env example. Nice. So those are the there's the SQL. We'll need to look at what the fuck I'm looking at. So that makes it so almost trivial to dockerize or containerize application. You know, this is classic 12 factor manifesto stuff. Been able to configure it each environment through environment configurations. It just makes everything a lot easier, but that's great. I must have been able to copy this to be not a dot example. So let's do that. Okay. So all that started. That just means my

31:12 health check is working. Engine x can can reach PHP as a DNS name and PHP is well, that won't be doing anything yet, so PHP probably exit. Well, I APM is ready to handle connections, but I don't think we've we've told it where to run around with that. So it's probably not doing anything. We should get What's the next step? Gonna say you can see if we got the hello world NGINX page. Oh, yeah. I'll just spin it up in the background. We don't need to watch the logs right now. So, I mean, I can leave it

31:46 running. Mhmm. So comment from Alex in the chat. Yeah. I actually see not not Alex, I see people get confused about dot end versus real environment variables. And, yeah, if if we're using Docker, can specify all the config with our our actual environment variables and not have that dot in file. Right? It so you can, but it makes it a little bit more difficult. So let me clarify that. Now I can't I'm here, add environment, and add every variable that we need. The first problem is let's just do this. I mean, that becomes unruly a certain number

32:43 of environment variables. And if we take a look at this dot e n b, there are a fair few. And, also, I cannot reuse this. Now what I can do is use the Docker Propose e n b file where I have a list, and I say dot EMB. And that just means that if I need to consume those environment variables in another process, whether it be imagine if we have I'm not sure what Laravel uses for job processing that has a different process that runs. But, you know, if this is real, it'd be like sidekick or something.

33:00 Fighting with `.env`

33:22 And it might need access to those environment variables too. It just means I don't have to duplicate it. But I generally prefer using the dot e n b approach and then just linking it together with it. And then I wouldn't have these. It would be better. I can't I conflict on it with I think Laravel does this and I know Symphony does this. The frameworks then try and read the dot end file directly irrespective of whether they're in a Docker context to find environment variables aimed at dev dev type environments. So I'm not aware of any

33:59 frameworks that would read the dot e n v as part of their process. What Laravel Maybe it's just maybe Laravel does that. What most recommend is running a source prior to running your commands to pull it into the execution context. So I'm not on as solid ground on Laravel, but Symphony has a PHP component that will read the dot end file and set those environment variables in PHP using PHP set end. No. Not what you're doing. But, basically, it sets them in the framework, and that some can sometimes conflict with what you're doing in your Docker Compose setup.

34:36 Okay. So Let's let's see if we run into that. I'm not sure what will happen. So I tend to turn that stuff off, basically, is what I'm gonna get to and make it so that it's Docker that's reading the environment variables. I've pasted in. It's it's pasted in twice because this sharing isn't working, but you can see the commented out section. It's pasted wrong. That's the MySQL config. Delete the bit up below. It's put it in about eight times. Okay. This is weird. I'm working on that. There we go. Oh. Oh, you're having it? You're just making sure I have that. Right?

35:24 Yeah. Alright. So our database host is MariaDB. Our database is Ping CRM, Ping CRM. If he wants something specifically, especially something like DB host, I'd be happier having in the composed file because then, you know, that service name. Yeah. The thing I think I'd be I'd be happy to put them in the Docker Compose. Although, generally, what I would do is use the for dev, I'd use the ENV. Right. And so that I can set them once. In fact, that's a good example. Let's do that. But here, I've got this year. It's better when the configuration uses the same

36:14 names. So I can see this is MariaDB, my SQL username. Let's just copy this so we're not I don't need to guess it. Okay. So we have these now. This also means that I can just consume this in ENV here. Even though it's got loads of stuff in it, the only best I care about are another thing we need is just that one here. There's no point. No. I don't think there's much point in putting that in the dot e n b because it's not consumed anywhere else. And in fact, we could just split that

36:58 out so we're not having that we're not loading on everything. So let's just do dot e n b dot MariaDB. That there. And we'll do dot MariaDB. And then we can consume this file and the FDM one as well. Yep. And then there's a third option, isn't there, that Docker compose will pick up that dot ENV file. And you can use those variables in this config file. Oh, I wasn't aware that Docker compose would parse a dot ENV, but you may be right. It does. So if you for instance, you could have it in environment. You could

37:39 have mys MySQL username colon and then dollar what what is in there what the secret is in the dot e v file. Okay. Cool. That's good for now. So you can keep coming to chat. Alex says Yeah. That's what that's what I was supposed to do. Good good comment. So I'm gonna remove these ones there and there, and we're gonna change our configuration to use the MySQL one. So where did you see that there was config database? Awesome. And if I say There we go. So I'm okay with that one being the same, but we want to reuse.

38:30 Let's go. Or Yeah. Great. Okay. So MySQL, that's user isn't when I have to check, that's, like, 14 times. Okay. MySQL database user password. User password database. User password database. You Then do these ones because these are unique. Okay. I think we're good. Which means we got DB connection, DB port. We've got a reusable one here. Now we can confirm all of this work by spending all of that down. We can do a Docker compose down dash b. What we would expect to see when we do an up now is that MariaDB should still be happy because it's consuming the configuration

39:27 from enb. MariaDB. And our Laravel application, when we run any sort of migration, should be happy too because it's consuming the same setup. We could change the interval. What's happening here and why the MariaDB has taken five to ten seconds or however long that is is and our health check here. We don't override the interval or how many intervals we need for it to be considered healthy, etcetera. So we could speed that up by just changing that. But I'm I'm quite happy with it's it's not taking too long. Now if we run a PS, we can

40:20 see our health check now reports this as healthy. We have our NGINX healthy, and we have our FPN what we have up. Sorry. What you wanted us to do was to confirm. That's that's good. I'm happy with that. NGINX responding, but we haven't actually given it anything to serve yet. Yeah. And okay. So now we have another comment from Alex. Four more comments, Alex, and we're just gonna have you join the session. So doing this obviously makes it harder to change post queries in the future. I would take config changes, but that's fine if you know what you're going to use.

41:03 No one actually changes database engines. Okay. I'll leave that up. We can talk about that for a little bit if you want. Any thoughts on that, Kevin? I have changed database engine. I had a CTO say, good news. We've we've decided to move from Oracle to MongoDB once. So that's a that's a good sign not be a trivial change either. It's gonna save us a lot of money there, is the sale. Yeah. Licensing is a lot better. Yeah. I feel like we're we're changing things in lot of places. So I guess looks like Laravel has that

41:40 two layers of environment variables, and then that config file is mapping the how you're using the environment variables to the real config. And we've another layer of indirection going in the Docker compose. So you can probably rip it all out and make it very simple, couldn't you? You you probably have in the end file my my database password. And what pretty much actually simplify by using a DSM. So another nice way of handling this kind of thing is to make make it so there's only one config key for the database connection by having a URL like

42:18 protocol for it. I've seen that for databases, queues, etcetera. So am I correct in saying what you and Alex are both worried about is I modified this file. Right? Yeah. Okay. So let let's not change it. Which is sounds like fine if you know what you're doing. Okay. So let's not change that file. Now the only reason I changed that is because enter dot e m v dot MariaDB, we have these variables available to us. So let's put this back to what Laravel expects then, and then we'll change it on the the database side, which I guess is actually cleaner

42:55 because we're not our cases are in a single location. So what we can just do here is say this is equal to DB username, password, DB password, and we can set my SQL database. That DB database. And that should still work. We're not past that even on the Laravel side. Yes. That's I think that's actually much nicer. I think that's a good idea. Let's make sure it was not broken. I can't see what it's doing. Can't see me. What? You can't see yourself? When there's a comment up. Oh, that's a shame. Oh, no. There you go.

43:59 He's about to just try and pick up above the comment. It looks like there's a typo there, doesn't it? DB data database. Yeah. It's not the best name. Alright. So we got Sorry. Wrong in all the places. Okay. So the my the database is still happy, and we could just confirm that we can connect to that now. So let's do a DC EXE. I'm not gonna spend any more time on this because I think that's been a while now. Go inside of our DB container. Oh, yeah. You can't do dash IT on top of ComposeExec.

44:34 We do it for you. But we should be able to run MySQL host one two seven as user ping CRM password and then the database ping CRM. What did I get wrong there? You've got database spelled wrong in some of the places in the variable. As long as it's only in that place, it should work. You're supposed to tell everything. I did. I tried to, but I said it wrong. Okay. One more time. So that Python dot end stuff is from Docker Compose trying to read the end file. So yeah. Docker report there's actually a weird thing, and

45:33 I can't remember the specifics of it it because I tried to avoid the syntax anymore. But to use the Docker Compose one, I think you need the export. It's a bit weird. We'll deal with that if we have to. Let's see what happens. As long as this works, we can move on to the next Alright. So let's just check we have what we expect. So I I would expect to see a bunch of these. Okay. That's not work. But that's our challenge then. Let's see. Do we have our DB underscores? No. Okay. So that is

46:13 that really annoying thing with Docker then. So I probably let me show you what I mean. Docker is a bit annoying and that I believe it expects this syntax. Which I bet you is not gonna work with the dot ENP and the the Laravel sites. Rather than me fighting with it, I think we'll probably just stick it all in the Docker compose file for now. You haven't got you haven't got those variables defined in the dot ENV, have you? They're defined here. Right? And where is that referenced? In my Docker compose file here. That no. That won't work. So

47:13 the way it works is your n file setting go back to the compose file. Your n file setting means those environment variables will be defined in the container. So I would expect the DB one. The in in your environment stanza, you're expanding variables. Those variables would come from dot env only. Yeah. It works now. Yeah. Okay. That's all working. That won't work is what I'm trying to say. Right. Anyway, we could You can't use variables in the in the Docker Compose YAML. The end file is sourcing them into the into the running container. It's not making them available while passing the

48:03 YAML. If you want them available while the YAML is being expand being expanded, you have to put them in dot env. Gotcha. Gotcha. Gotcha. Gotcha. Okay. But then also expects the six port syntax rather than I've got to be honest, I haven't used in far much. So let's test the hypothesis. Oh, no. The problem is I had cool on. The expert probably wasn't I'm just being really dafted there. I had the b username. I had the YAML syntax. I would say it's dot net detail. Yeah. I know. Right. Okay. You're saying that I put this here. We'll

48:51 get the expansion. Let's just remove this dot one for now, and let's confirm that these weird weird things. Correct. Did you just take it out of the dot env again? Alright. Thought you said apart this one automatically. No? Yeah. Without you putting in file in. But did you delete are those variables defined in dot env? Yeah. They are. Okay. I've moved them I've them all to dot in. Let's just if it's something right. Oh, that should be Okay. Really gonna change that interval now. So interval. One sec. Interval. That's not last week. Okay. So what we just wanna confirm here then before

49:59 we actually do something a bit more interesting is those environment variables are set, and I can log in to my sql slash berated b. Okay. That works. So I can do one two seven. Ping CRM. Ping CRM. K. Alright. Next. I think it protected that password even though it's ping CRM. Okay. So something that we didn't touch on last week is what is our okay. We haven't configured FPM. So let's go inside of our FPM container. They call it Laravel. There we go. Where does that live? Is it user of our HTML or something like that?

51:08 Docker Docker file? Because we're mapping our code to slash code, and that location is not correct. So let's just Yeah. So the FPR. What's the Apache stuff you're doing? Let's just look it up from last week. Okay. Slim. Let me modify it here. For our w w h t m l. Make sure we could have guessed that. Nope. Okay. Slash prop slash one cat command line. So we use catlocaletcppipm.com. That's a really cool hack, actually. Whenever you're in a container, slash prog slash one is always the running or primary process. And if you just tap the command line

52:27 fail, you can see what the entry point was. Save my bacon a few times now. So that is our default FPM configuration, and we're just looking for What are you looking for? Just a rip, which currently is in maybe it's in this dot d directory. Are you trying to change the config to point to slash code rather than I just wanna know how that is. So, yeah, so I can get the directory. So I'll tell you so I just read the read me and put it in the place they tell me to and try not

53:17 to look at how any of it works. There's a fun in that. We've got a whole bunch of different things here. Let's do www.com. You can see some environment variables being set. You can see share route is not set. No. That doesn't mean it's the web route or the whatever they call it. Let's see if I can grab this. What I'm lost what you're trying to do. I want to know where it expects the PHP files to live. We saw that as var w w HTML. But But that directory doesn't exist, which made me think it was lying.

54:12 Isn't it just because you have to mount it in or copy it in? Hang on. Let's just go to Dockerfile. Okay. I couldn't grab an FPM one. That was unfortunate. FPM. Worked there. Yeah. It's part of the. Okay. It just doesn't exist. So we can mount our code. We'll just leverage that. So we will change the working directory. Now we need to serve public h two. So I see the same as what we did last week, but we need to modify our index configuration. Try to get lost. Yes. It's still comfy. And this will just need to match public

55:20 pixel. Right. We will recreate what needs to be created. Got some interesting questions in the chat about how do you maybe we'll come on to this. How do you run typical sort of development commands in a dev environment? I think that. We'll definitely We're right there. I use a pattern called deshell, so I'll show you what that looks like. Okay. Good. I mean, it's it's failing, but that's okay because we haven't followed the installation instructions, which means we do now have commands that we need to run as part of the installation, which means we need to develop an

56:00 Interactive development with a the Docker shell pattern (dshell)

56:14 environment. Now whenever you type PHP or Composer here, to me, that's a a warning sign. We wanna avoid that as much as possible because we're running those commands in an environment that does not have parity with production. But how do we change that? What I normally do is build a development workflow. It looks like so. So my entry point is now going to be bash. Okay. We'll do this to our make target. A little bit later. There's no make fail in this project. That's where projects kinda make fail. I generally add a deshell target, and the

57:04 goal being I wanna be able to say make deshell in a terminal and get interactive development environment that I can work with, which means what we're going to do is do Docker Docker compose run service ports. And I'll explain what these flags do in just a moment. I just wanna keep adding those things. It's really annoying. Run service port slash slash r m. PHP is the service that we want to run. I'm also going to override the entry point to be batch. Mhmm. And don't think I need IT. I do not. Okay. So I don't know

57:53 what service points does, service ports. Yes. So when I do a Docker Compose run, which means I want to run an individual service here. Mhmm. It does not publish those ports. Mhmm. When I tell it to when I add service ports, it will just make sure that the ports are exposed. Now, actually, I'm not exposing FPM locally, but I'm so I'm just gonna keep the flag there because that's important for most applications and may become important. I override the entry point so it doesn't run FPM. So what that means now is when I run make b shell

58:32 yeah. Make files are fun. So we need a dot phony b shell. That's basics or tabs. And there's no edit or oh, there is. We've got it already. Okay. Make file and then style tab and then size for we save the style. I hate make. It does have I really wish it doesn't have that pain point of tabs and spaces, but doesn't matter. So what happened here is we ran PHP. It started MariaDB because of this dependency. It hasn't started in Genet, which I didn't think would well, I said that it's fake encounter, but it makes sense. So we'll fix

59:21 that in a minute. We don't have anything running here, which means which means I need to add one step here up NGINX background. Just because we can't have a separate dependency, so I can't start PHP because NGINX depends on it. So we just have to make that a bit special. Let's run that again. NGINX is up. FTM is up. Now if I run here, it's gonna time out and fail. Oh, it didn't. Oh, because I still got it running. Shut that down. And they'll make these shell. So, like, in a fresh environment with nothing running, I would

1:00:12 run this command. It starts engine x, starts where you need to be, basically, an interactive terminal. I can have go here by the Starting NGINX is starting because of the dependency. Funny. Because you're doing compose Docker compose up dash d NGINX before you Yes. Alright. That's that's that's good. Okay. Doesn't matter. So now we need now we have an environment which is production like web files. I'm not gonna have composer, so we need to fix that. So this isn't the is this the have we run a new copy of that service? Yeah. There'll be two running right now. So

1:01:03 let's Two running. Jump out here, go to my directory, PHP examples. And we're relying on the fact that they share a volume. Right? But if I run v c p x But this is the actual service that was started by the Internet's dependency. Yeah. And this is my run container. They're both interacting. This is consider right now is horizontally scaled. We can still run our migration path and asset compilations. They're both using the same volume, and so I don't foresee any problems right now. And we could remove that this one here by just having an exit zero in the

1:01:50 Docker compose file, But it's it's really not that important. Yeah. It makes sense. So yeah. So we can do all the typical stuff because all the things you want to do are either creating files in the volume or probably doing stuff in the database. Oh, yeah. Definitely. So right now, we need to fix the composer thing. Like, if I'm saying I don't want to run composer install locally because maybe there's dependencies on extensions that which would fail locally, then I have to do that inside of the container. And right now, we don't even have access to

1:02:00 Add a multi-layer `Dockerfile`

1:02:24 composer, so we need to fix that. And the way that we do that is the same as last week too, where we have multi layer files. So we're gonna say PS code that is particularly right. We'll call this our base. Mhmm. We're not we're not gonna do anything with. We're just removing the duplication of base image. Then we have dev where we add the tooling that we need. So I can do APK. I don't know where I'm doing nothing. I can do an app update and app install. Let's just stick in them there. Okay. And

1:03:01 we're also going to copy composer from the official image. And if we remember correctly, last week, it is available under user. Let's just try and compose our user. May be local, but I can't remember. We can come back out here and instead of using this, we're gonna say that we wish to build an image, the context of the current directory, and we're gonna target that dev layer that has all of our wonderful tooling that we need. Makes sense? Yeah. K. So now It's also gonna affect the running FPM. Right? So we've we're making it even

1:03:44 more explicit that our our composed file is for dev. Oh, yeah. Totally. Yeah. Post files should never be used in production. Let's get to Docker Post files. I mean, there are people out there who say Docker Post can be used in production with version three. And if you're happy with that, by all means. But Uh-huh. It's not for me. So this installation of app is gonna be cached. Right? Because we're pulling we're copying stuff from the composer image, which is always gonna be cacheable. And then we do Okay. So let's go back into our d

1:04:00 Adding PHP extensions to the container image

1:04:25 shell. We now have the ability to run Docker Compose install. And now you can see the the the problem, right, is that my local environment magically seems to have some extensions that are required by this application that aren't available in my production environment, which is this container. So we we need to fix that. So we need these two extensions. And the Dockerfile provides a whole bunch of different helpers to do that. So and we can do run. And then we've got PHP what's it called? I'm just looking out. Docker PHP extension install. There we go. Does

1:05:13 this help a script where we can do EXT XF and EXT GP? You spelled XF wrong? Mhmm. That's a lot of it, but I don't know if it worked and didn't work. Stop refreshing. I I don't think you need the the access and GD. There we go. And then we can copy that command to our base image. So Docker, p t, x k, install, GD, That's a zlib or zlib c. So and does that help? It's our dev package. Let's work out this dependency. Maybe I should just quickly Google it. Zedlib g zedlib one g?

1:06:56 Yeah. That might be it. We'll try that. And if not, we'll just look it up very quickly. That's what That's what Google says. And this interactive environment is is good for this because we can test this works before we rebuild the image. Alright. Okay. So, like, we got past that. So let's add that first. So our base image now needs this app update and app install dev. Oh, what was that? One g. Yep. One g dev. We also need some sort of PNG one. So let's do the PNG. It's a dev. A PNG dev. And now we're codifying everything that we need.

1:07:45 It's it's good. More works on my machine nonsense. So in terms of wanting the latest versions of things, how do you how because this is something I've wanted to ask before, actually. When when you've got your app update at the start, that's gonna be cached. So it's every time I build the container, it's not going to do apt update? No. But this base image, you know, our continuous integration system is is gonna force a rebuild to this on a regular cadence, either every twenty four hours or maybe once a week depending on how frequently your deploys are. You know, that cadence is

1:08:44 really coming down to each team. But, yeah, the I mean, the the challenge being is that if you have a base layer like this and you never rebuild it, then your dependencies, your security patches, all of that become very dated very quickly. And then you'd have to build that. You'd have your tooling force to sort of uncached build every so often. Yeah. Use cache builds during nine till five, and then at 3AM every morning, have an uncached build run to make sure you're always up to date. Right. Okay. So That's a good now. Yeah. So the reason I say nine to five

1:09:18 use cash bills is that, you know, if you're shipping and building regularly during the day, you don't want your developers sitting waiting any amount of time that you don't have to. And it depends how slow your builds are. If your builds are under a few minutes, never use cache. Yeah. Just make sure people are productive during the day. You don't want them to set and sort of tighten because of compilation step. Yeah. But we've got that safety net that if there's a critical security vulnerability in Zedlib, we're gonna get the fix at some point. Exactly.

1:09:47 And the composer install hasn't failed with the extensions. We now have a container and production environment that has everything we need about this application. And we can cache that composer step as well as we we did last week where we say, okay. We don't modify what's our build JSON that often. So we're gonna copy that to our working directory. Mhmm. And we can put this but I'm just moving these steps up because even if this does change, we don't need to reinstall these two dependencies. So we use the cache there. And we're doing a composer install,

1:10:27 and that becomes part of our build process. That means if we do modify your composer dot JSON, you just have to rebuild this image to get the latest dependencies. Okay. But currently, we're mounting of local vendor folder over the vendor folder in the image anyway. Yeah. There's a lot of that hoops you would have to run around actually to take that up. Which I do because I'm like, you're using a Mac. And the less files mounted, the better. So for some big projects, they end up just mounting instead of mounting everything, might mount the the app folder.

1:11:00 Adding a `Makefile` to codify commands

1:11:12 We'll we'll we'll come back to that. Like, I mean, we could spend at least an hour just covering that, especially on a Mac because it is very weird. But let's not we'll come back to that maybe next week on our session. Let's try and actually get this running. We're at over ten minutes, and we haven't got it running yet. So installation steps. So let's expect us to run this generate command. So we're gonna qualify that in our make file. So let's call this setup. Why else do we need migrate? Collaboration expired. Right. And we'll be okay with that.

1:11:58 We need to see their database. Mhmm. And we can run star, but we'll actually call this run. That's the web server, isn't it? I I think so. So we've got engine x already. We don't need that. Okay. So let's do make setup. So we're still inside our d shell environment, which is our interactive dev environment. Hopefully, all these commands just work, which, of course, never gonna be the case. So if I run if I run make setup from the host, it's gonna fail? Well, I wouldn't be able to find the database. Because maybe I haven't got PHP installed.

1:12:45 Or MariaDB running. You're always you're always running make d shell and then using it inside the d inside the d shell. Right. So it says it wasn't able to find our driver. Does that make sense? We haven't installed the MySQL driver as part of our Docker file setup because it was it must be an optional dependency in the composer dot JSON because you might not be using a MySQL compatible database. So we just need to hear I think it's just called MySQL. You think that's right? Is that what the error message said? Yeah. It couldn't find the the driver for

1:13:26 connecting to the database. PDO MySQL. So we need to enable that. As a downstream product here and I know what database technology we're gonna use, I could come in here and just enforce that at the compilation step. Where's the extensions? To the top. There you go. Line 30. So we could just codify that pixel. Mhmm. Let's just turn them. Let me come back into our d shell, and we can run setup. And, hopefully, it's a little bit happier. It is happier, but it's still not working. So connection refused. So let's check our configuration. So FPM doesn't have any config. So that

1:14:36 is a full part on our part. Oh, yeah. We haven't set it. We only set it inside the MySQL. Let's make one more. I got Phoebe. That's the one. Okay. Lucky. Okay. What have we got wrong here? Our database where's our post? I think Alex said that in the chat about an hour ago. 12:12 fifty. Alex said, I think we're missing DB host. Okay. Host. Not host name. You saw nothing. Okay. Okay. So the migration ran. Database was CD'd. So asset compilation. Is that what that means? Is that what makes sense? Right? Yeah. I think so.

1:16:06 So the in the instructions, there was a couple of NPM commands ahead above what we did. Alright. Okay. So we need an NPM CI to install the dependencies and an NPM run dev to build our assets. Okay. So there there's half a dozen ways to do this. So a lot of these things, we're we're treating them as ad hoc one off things you do in your dev environment. So, you know, seeding the database, running there's actually two let's split them into two. There's things you only do in dev. Seeding seeding the database isn't something you're doing in

1:16:53 prod. Right? Running migrations is maybe something we're gonna do in prod. Yep. And building the pipeline at that kind of build step, that feels like something we should be doing as we build the image almost. Yeah. There so I think the two ways we'll try and cover that is the one you just which is whereas maybe as a PHP developer, I'm not gonna touch the the front end assets or like that. And I can make that into the image, but I'm not gonna change. But I think there's the other end of that where maybe we are changing the assets as part

1:17:27 of our development workflow. We we still want that to work too. So let's do let's go with the naive approach in that. We're not gonna change these assets. And if I'm not gonna change the assets, that means I can ship them in the NGINX image if I want. I mean, if anyone's getting any strong opinions and, like, again, we're both not Hirevel developers. That's best. Maybe maybe our is not the one to listen to. What what I suspect is gonna happen is the mix like Symphony webpack encore. There there will be end up being some

1:18:09 interaction between the PHP framework and the static assets. Like, it'll check for there or do you know what I mean? It'll it'll it'll want to have access to them in the PHP image. That's my prediction. So okay. So let's make this the easiest way. As as part of setup, we run an MPM CI, and we run an MPM run dev. Mhmm. That this is the naive. Let's get it going, and then we'll see how we can optimize it. Now the first challenge is we don't have access to MPM inside of our dev base layer image, but we can fix

1:18:42 that. I believe. Install Node. Js and NPM. Obviously, I'm not entirely comfortable with leveraging dev images super repository or at least a super image with loads of stuff in it. So we'll maybe try and see if we can clean that up. But it does mean that I can run MPM CI. It should just work. And I think what we'll do is we'll just prove this works. We'll take it away, and we'll bake this into the engine x data things. I really I think I do feel quite strongly that NGINX should be serving the static assets.

1:19:52 The build process should be part of that image build. And if they change, I'm just gonna force a rebuild. I mean, the development workflow for working on PHP versus working on the static assets would probably again, maybe I'm just not the person with the experience to indicate how the workflow should work. Yeah. The front end developer's gonna want, you know, what wanting to be watching the file system and rebuilding if things change and all that stuff, which might lead them towards running that stuff locally. So we can we can we can make it work that way. Do some do some

1:20:23 do some some me, I just want all all the stuff built so I can work on the PHP. So would that be a an m a node build step? I'd probably add another container to handle that. It'll this MPM run dev seems like it's actually running a web server it's gonna start doing something in a moment. Oh, no. It's building. Okay. These things take time. It's a lot of files, so it's probably quite slow with volume amounts. That's true. Wouldn't have that problem on Linux. What do call them? Host mounts? Host volumes. Yeah. A bank mount or a host mount.

1:21:04 A what? A bank mount or a host mount. Bank mount. Okay. Because there's been some chat about slow slow max. It's that. Right? It's having lots of files mounted. Cool. So let's see what happens. That'd It's doing something. It was doing something. Alright. Well, again, some sort of four zero four, so let's just see what's failing. CSS. Okay. Yes. So let's check our engine x config. So where does that JS file come from? App dot CSS. Well, I can't see that file. It could be in public It's there. Okay. So I'm assuming there is some sort of redirect magic

1:21:40 Fighting with nginx

1:22:41 on the APM site. They should be looking for that stuff in that directory. Let's go to index on PHP. I wonder if Laravel should be handling this. You'd you'd not want it to, would you? No. That just outputs everything under resources. Or not because the CS is under app CSS, but the path is just here. Let me check that. No. No. CSS, app CSS. Yeah. So we wanna serve that resources to retro as a root fail system from NGINX, probably. That's kind of the vibe I'm getting. Have a look in the public folder and check

1:23:45 there isn't a sort of sneaky SIM link or Ah, there's an h t access. It's a CSS folder there, isn't it? But is it It just pushes over her through index dot PHP, which makes me think Laravel is trying to handle that, and then it's failing. No. That that's in that in that h t access, it's only applying it's only requests that don't hit a file or a directory going to PHP. Yeah. But a request for slash CSS slash app CSS is gonna go be routed through index of PHP because it doesn't exist. That is that CSS folder in public empty?

1:24:34 Oh, record. Oh, okay. Well, so what's going on here then if I do public? Is there any So development development and resources. So maybe that's just with 11, and it gets compiled into public CSS. Alex says that Laravel does not handle the CSS checks. So let's try. But we're serving everything from oh, no. Right. Okay. So I think the problem is is that request is coming in for app dot CSS. Our engine x configuration at the moment is configured through everything through the index dot g s, and then that's failing. Now, let's just see if there is

1:25:51 engine x for Laravel apps. We thought we'd have done that. Rather, I was trying to So you think all requests are going through to PHP? It is definitely. Yeah. Right. So we just want someone we just want our production engine x config. Yeah. Looks good. Or we could just make that those assets available in in the NGINX site, and we set them over NGINX. I still feel that that's the right way. That would make more sense, wouldn't it? Let's have NGINX server. Okay. So how do we do that? Lead volume? Well, yeah. Okay. That was naive approach. Why

1:26:54 not? Thanks. And that in a nice possible way, of course. I know. So that should just take everything from here. The the challenge would be the index. Php would exist. Very naive approach. Let's see if that fixes engine x. Let's just use a second terminal. Oh, I wanna restart the engine next. What that should mean is if I go inside the engine container did I not mention it? Or w w HTML. Oh, r m engine x. Help. Still there? Down? There we go. Stop it. Stop it and force it. Right? Okay. And then start and

1:28:13 run up. I should have the note. K. So now it should Mhmm. Be able to deliver those asset. Well, then now we have the 403 problem, which we got last week as well. And then I believe it's just because that public directory. The permissions on it? I don't think it's permissions. I think it's that it doesn't exist. See what happens if we do that. Is it gonna be trial and error? Stop. R m s f. Engine eight. I'm not back. Definitely a four zero three. And the problem is that is for bettering. So it's try because the public folder does

1:29:38 exist, does that mean it's not passing things on to FBM? Is that our config? So this should be running as Alex has some pertinent comments in the chat. Did I manage it to the wrong location? No. What does our vhost.com look like? Can we remind ourselves? Mhmm. Oh, wait. No. That's right. Because that's still the. Yeah. So I was trying to access the file. I can't get a directory listed in that, which makes me you know, the seems like it's permissions. I can access that. And so I'm assuming I could probably access that. So the challenge is just the PHP fail.

1:31:13 So what I need to do It's close. What's the what's the problem? The problem is let's add pick it up. Okay. So adding the index dot HTML at least goes with the the error. The problem is now that fail exists. So it's when when you go into slash, it's because the public folder because the folder exists in the NGINX volume now Yep. It's trying to serve the directory index of that folder rather than asking FPM for it. Correct. And then something else has turned off directory indexing. Is that right? Yes. But it's trying to deliver

1:32:09 it's trying to find the root fail, which doesn't exist. And which should be gone through the PHP, but I think we need to add something specific. So what was that? Root file does exist. It's the folder. And then the root had public. He's saying if we do that, it should work. It's a good thing people watch this thing. Alright. Let's see if he sees our end. So we're gonna restart with that. Now let's confirm. Yeah. That doesn't work. So we've broken that. It's not that we wanna add a public here. It's that Stan just used the official engine x thing.

1:33:15 Well, let let's do that. And look at why it works and ours doesn't. Okay. Wonder if it's that, the little cheeky head. No. I was like, ah, that's what it is. That one thing failed. Right? Then I'll copy the failed, but, you know, I do I I need to confirm it from my own. The problem is it's looking for an index, HTML. It can't get a direct to the list of them, but we can just save the index as a PHP file, which forces it through this route. Mhmm. Thanks. K. That works, and this will work.

1:34:10 Alright. I'll copy the file. Copy the whole file. Copy the whole file. So it was pretty We're getting bugged down in NGINX. If this works, let's never Yeah. Talk about it again. That's just not important. You're right. It's not an NGINX stream. Not using a UNX socket. We're using I should've kept that lane. I think that should be alright. Okay. CSS is working. Fail not found here. Oh, no. Oh, come on. It's good that we've got that confidence each time. Oh, see, that seems like I didn't wanna copy this nonsense. Okay. W w w dot

1:35:57 It's wrong. Primary script name unknown. And because it should be okay. Does the server name have to be there? I feel like I was closer with my own fail. For better now, specs, the CSS works. For better now, it's because they're trying to access the root file. So that should be enough to tell it not to look for an index by HTML. And if that doesn't work, we'll fix it. K. So now we're getting a fail not found. Let's try written errors through PHP, which we had an example up here. Public. We getting we are getting a four

1:37:53 zero four. Yeah. They're not there. We shouldn't use a public. We just wanna force it to go through PHP so it goes to the FPM endpoint. Probably that's not working. Just make sure I'm getting the right update error message. It's over the past CPI error now is saying yeah. Okay. So the path is important here. There's a log. Alright. Let's try and do something interesting. So okay. So now we've got engine x serving as static assets. We got PHP serving another PHP application. We've got our database working. For development environment, we have an interactive D

1:39:07 shell, which is over here. So as I change my PHP code, I can run composer command if I need to to cache clears. Do we have a development environment that'll work? Yes? Yeah. I think so. If you were a front end developer, you might want to have some clever watch magic going on, but it it works. Should we pivot towards production? Just thinking about the time available. Because this is very devy, isn't it? Yes. This is a development environment. So And be interesting to talk about how we'd build the NPM stuff for prod. Okay. I just let's just make sure it was

1:40:00 Deploying to Kubernetes

1:40:09 semantic a few times. So, like, what I don't mind. This development environment would be let's say I wanna do database migrations. Maybe make just wanna make sure that people understand that that is the way that I would work. If I were to change my database where do migrations work? Yeah. Cool. If I were to change one of these files, my development workflow would be that I know from this environment, just run these commands, and it worked. Wow. That seats is complaining. We know that already have seats, but that's not idempotent. So maybe it's only run the migration.

1:41:02 I don't understand how the seats work in Laravel. I'm just gonna remove it. There must be a a clean command. Right? After then. P d y. Let's see. That work. Yeah. Okay. That's better. What about tests? How do you run tests in PHP? There's an Test? It has some test targets, I think. It's basically PHP unit stuff with some extensions for Laravel. Test agreeing. Lots of tests. Okay. So the I think the development environment is fine. We could definitely clean up the asset stuff. But, yeah, let's talk about the production aspects of this and then

1:42:14 push Alex to comment on the screen so you can do a migrate fresh seat. Seems like it's a bit nicer. Okay. So how do you want to deploy this to production in QN? Well, you tell me. So what's different? Right? What's different is that the the composing store would take place in we're not gonna have as many volume mounts. Right? We're we're gonna do the composer install inside the PHP side of things. We're gonna copy the files into the images instead of mounting them in. And we would need we'd want to build those assets without having to as part of

1:43:00 the image build. So that's a question. It depends how we want to serve the static assets. Like, do you want an engine NGINX image with them baked into the NGINX? Maybe in real life, you'd be pushing them to some sort of CDN instead. Yeah. Both are very both are good workflows. So the the the challenge is just everyone should build an engine x image with the assets. We would ship them together as a deployment with two containers and a pods and probably share the same tag so that they're always deployed together. But we can talk about that, and then

1:43:43 we can also talk about the CDN approach where as part of your CI pipeline, you would just push them to a CDN and then ship your PHP image with, you know, the correct URLs, whatever. So Well, actually, I think I think there's two NGINXs. There's two roles for NGINX in this in this app. Right? One role is serving static images, which doesn't necessarily have to be static files. Doesn't necessarily have to be in the same pod as the PHP application. Right? The the other NGINX role is So I I I would just because they have to be deployed together

1:44:28 in a sense there because the static assets that are deployed to NGINX would be very unique to that build and wouldn't install like you would run older version of the assets with a newer version of your PHP and FPM stuff. So for that, I would deploy them as a deployment with two containers. Yeah. I think I think that's very much then depends on the application and the teams involved. Right? If you've got yeah. It's that Conway's Law thing where the structure of your teams determines the structure of your application. If you've got different people in

1:45:01 a if you've got a detached front end and back end workflows where maybe there's a single page application is the is the app and the PHP back end is like an API that it hits. They may well be separately deployable because the fact those two groups are working separately would mean the API would have to stay super stable, whereas the front end team could iterate quickly and make lots of changes. Right? On the other hand, if you're talking like a traditional PHP lamp stack team where there's a couple of front endies and a few back

1:45:34 endies and they're all working together on the same stream of features, then yes, a % you'd want to be deploying them together because they wouldn't have had to bake into their way of working the fact that the API has to be forwards compatible, backward compatible. They'll break stuff all the time in a coordinated way. So which one should we which version should we pick? So so in that separated out team, right, you might have the front end assets being completely deployed to either a CDN, different set of static image static servers static asset servers to when the deploys to the PHP back

1:46:13 end are going. And in that case, you might not even need NGINX for the PHP. I mentioned it to you that that I didn't know about this before. Someone called mentioned that Kubernetes can have, like, an in NGINX ingress point. So you ship your PHP you ship your FPM and sort of trust Kubernetes to expose that to the world, which wouldn't work for static static assets. But if you have this if it's a microservice and doesn't have any static assets, maybe that's a good option. Okay. So we we let's pick a pick a pick an approach for this app.

1:46:58 Alright. So what we're gonna do is we're gonna create an op directory, and we're gonna start creating our Kubernetes manifest. We'll try and tackle a few of those different scenarios that you mentioned. So I see their deployment. We'll call this a ping CRM. And we're gonna and what that will go to you. So I think because the CDN bet is not something that could show up right now, we can definitely talk about that. Although, there's only one thing that changes in the deployment pipeline. So let's go with the approach in that. We have two containers in the deployment.

1:47:37 One is running NGINX, one is running FDM. For our demo, NGINX will have the assets and we'll build an image specifically for that, and we'll see how we deploy them together. But at the same time, in your CI system, you can just not deploy the engine x one and just have a make CDN target that does whatever your CDN target is. Yeah. So I think this is the best way for now, which means that we're gonna have an image called ping CRM, and we'll call this engine engine x, and we'll give it a version of latest,

1:48:07 which I would not recommend you do. I just wanna Yeah. I don't understand what latest is. I thought I did at one point, and then all my illusions got busted. We'll have that FPM. And now we need to build these. We also need to make the ports available. So ports. Internet to less than 80. Less than 9,000. Now we need to build the images. So let's assume this make fail is also our CI, which means that we want to build NGINX stuff and build FPM stuff. Let's start with let's get NGINX delivering assets first. So if

1:49:16 we come into our deployment, let's remove let's just comment out at the end, and let's just get this working first. Yep. That may fail. We can just say that we want to build an image, and we're gonna call this ping CRM engine x latest. We're gonna need to target here because we're building engine x, and that's gonna be an echo one for now so that it doesn't break when I run it. I mean, if it's satisfied, it's build set. So we're gonna continue to use our multilayer build. And what we're gonna see here is that from

1:49:54 Engine note well, we just need Wow. We need to what we need to compile the assets first. Yeah. Yeah. I think 10 maybe an image. Yeah. Assets, those we need to be able to distribute them. So you're right with the engine expert. We'll just leave that as a tag, and this is gonna be our engine expert that we actually target. What we want to distribute here comes from our tool capture line, Asset build, and we need to copy something to file w w HTML. Like that? Yeah. This is totally how we do it. We What we need to do is make

1:50:46 our code available in the build let build set, and we wanna run n p m c I. And the dependencies. Yeah. What what I found is that front end build stuff is much less likely to have any sort of system dependency. So you can you can just use the use the node container straight off Mhmm. You know, without having to like, we have the PHP of how to install a bunch of extensions and system stuff. You don't really get that with it. Yeah. I would expect that to work. The only thing I wanna check is does MPM run dev. I don't

1:51:21 know if they have a specific production task that maybe does minimizing and, yeah, prod. So let's call that the production one. I may make it slightly difficult to validate, but we'll see what happens. And then we're gonna copy this from slash code. Yes. It's gonna be in public. Yep. Yeah. So we could be a bit more sophisticated if we wanted to dig in about which files we're copying in. Mostly from a cache invalidation point of view. So that that node build target is ephemeral. Right? It's gonna it's gonna get built. It's gonna do all the compilation, and then we're

1:52:12 kind of throwing it away. We're not gonna use it for anything. We're gonna just copy the final assets out. But so in terms of image size, you're not too worried about copying everything in, but the cache will be invalidated anytime you change any file at the moment. So you'd probably only copy the files in that you knew had to be there for the NPM build. Oh, yeah. That's Yeah. We we don't we're not gonna do that now because we we're not gonna dig in and figure out which ones. Is it just a resources directory? We can

1:52:45 we can we'll give that a shot. And if it isn't, we'll just Yeah. You're definitely right. Like, we'd probably only need And then package JSON in the root. I think you can do this syntax. Right? You can just you don't need to do that. You just you list them out. You have multiple items, and the last one is the type the place to put them. Right. Oh, maybe do multiple lines. Are you not not pressure network. Let's test it. Docker no. We're gonna use our CI. Build assets for Internet. I'm slightly concerned it will lose the directories

1:53:36 things we're in under code. It'll lose the some of the structure. Actually, it makes sense to have multiple lines sometimes if things are gonna change at different cadences. Yeah. The package dot JSON probably better be copied on first. Yeah. So there you are. That's project is using some sort of lock validation, which I generally discourage, but I'm not gonna stick with it for now. So you wouldn't use that if you're using Docker. Right? As we I I would. For sure. It just gives you consistency guarantees that you don't particularly require. So you do require, but Docker gives you.

1:54:42 Right. Right. Right. So we're using build kit when you're in a Docker, but Compose doesn't do that yet. No. It doesn't. Webpack dot mix. We need that. I'll add that and run it again. And then if doesn't work, I will add everything for now. Yeah. What the heck? What's that code called? Dot mix dot j s or something. It's .mix.js. Tailwind. Yeah. I think we let's just copy everything. Last one. Views Last one. Views you've got the idea of what we're doing. Right? It happier? No. Okay. Give me something. Alright. I'll copy everything. Yeah. Please. Something something that annoys me about

1:56:24 NPM is it shows things as errors that are just sort of more like warnings. It always seems to when I'm building stuff. I thought you're not enough to really to really know. Still has to run n p m update. That's interesting. Okay. Now what we have here CRM, NGINX, latest. Let's just go ahead and save to this container. I w l l h t m l. And there is all of ourminified.com. Cool. Should there be directories? No. Yeah. Yeah. It's it's like my star here is is probably not what we want. What we wanna do is copy.

1:57:58 Let's try that one more time. I think it's gonna start flatten the directory, which we don't want. Yeah. And that's invalidated the copy because we edited the Docker file. We shouldn't have so we need Docker ignore. Right? Well, no. Because I changed oh, yeah. Oh, yeah. If I had done a Docker ignore to ignore the Docker file, but yeah. It should be okay. Alright. Let's see what we have in this directory now. That's better. Perfect. Okay. Looks good. Alright. Let's deploy that. I know you may have the Kubernetes disabled, I think. And we could have excluded the index PHP

1:59:02 as well. Right? If we're just gonna have just the stuff that's. Well, that wouldn't be that. Like, if we if we were gonna spend the time to work out the only time we need the copies, we'd have been better. But because we're just trying to get this working and not stream for three hours to happens Oh, I'm we should put this. Okay. Right now, let's let's get that quickly enabled. I disabled it for yesterday's stream. We were looking at the latest version of Kubernetes, and this is an older version, so I have to let's see. So

1:59:43 Kubernetes apply. It's We're just gonna apply it with the engine x container to make sure that we can pull those assets. You still there? Yeah. That was good. Okay. So we have a Kubernetes. One Kubernetes. Or one Kubernetes. Who knows? Everyone get pods. Appointments. It's just a bit slow. There we go. Image pull back off. That's okay. Let's fix that. Image pull policy at not present. Right. So we'll only pull it if it doesn't have it already. Yeah. So when you locally build images, it can be a bit frustrating, but the default policy is always, which means it tries to

2:00:57 go and pull the image. So if it's working on the official registry for this, but at this local label, we're gonna set them up to do that, which means that that runs. So I'm gonna put forward to it on port eighty eighty. And if I hit here, you get engine x. But really what we wanna see is what was it? CSS. CSS. CSS. It won't be called app CSS anymore because I bet you it's minimized. So let's just go and check that out again. What is that? I think that's the debugging again. Mhmm. Let's go ahead and save it to

2:01:58 pod. Alright. I know it's around. Yeah. Did we copy in the config file? Or have we mounted in the config file? That is a good point. Let's fix that. We did not. Please Please in the default one. Etch. I would have spent ages working on that. Build it from here, so I've got my old complete. Where is my build? Image build target engine x. We're calling it n c r m engine x later. I really need to stop abandoning that cache. Hopefully, it's the last time we have to build it. And then we'll delete the pod, force it to restart. Hopefully,

2:03:21 that is enough for us to hit app dot CSS. Then we build Laravel and deploy. It'll be good. We'll get there. When I'm debugging like this, I often make sure I put my stuff at the bottom of the Dockerfile even though even if it would logically be better higher up. Yeah. I should have done that. And when it works, resync on it. Resync it and hit build and more coin and get a cup of tea. Why have we got crash it back off? That upstream PHP doesn't exist. And that's because in production, we're not using

2:04:08 an upstream PHP. So just create a v host prod Mhmm. Where we're gonna use Rawkode. I'll come into our Dockerfile. We've had many change of how to rebuild this image. This is my first stream that's when post two hours. Yeah. Is that good or bad? As far as people find it valuable, it's good. Right? I don't mind debugging things, I think. So I think it depends on your viewpoint when you're watching something like this. You either learn from seeing people debug or you get frustrated. Yeah. I'm sure we'll get both kind of audio. Both our audience will have both. This will make

2:05:27 it different from a slick presentation, isn't You get to see the frustration. Yeah. Frustrated. Definitely frustrated. We get to see that even Rawkode has to debug things and makes typo. I'm sure I make more typos in debugging than most. I break things a lot. Alright. So we're doing this part to force the new image to come into play. Hopefully, that removes our failing. Correct? We're gonna do the port forward once before And see at m eighty eighty eighty eighty. We have our CSS being delivered. Yes. And we'll just ignore that. Yeah. That's better. That's just cash. Okay. So

2:06:13 it's now trying to forward this request to FPM and it's failing. Mhmm. We need to add our second container to the deployment manifest. We want this, which means we need to add a CI for ping CRM FPM. So let's copy this FPM. And we're gonna target FPM, which means we now need a new layer. We'll call us Put it up near the other PHP ones. Alright? Put it up near the other PHP ones. Right. It's always a bit I always find it a bit weird having the notes left, you know, a lot of different images in one Dockerfile, at least put the

2:07:10 ones that are PHP, FPM together. Alright. Okay. So we wanna from we're gonna do a copy dot code partner code. We could probably We wanna do the composure install? Yeah. Yeah. Before you copy code. So it's build FPM. Mhmm. And then from build FPM, we'll do a test layer, which we're just gonna have to run the echo test for that. And then we'll do it from build f p m f p m. So this is our production image. And what we want is copy. Yeah. Well, the vendor I think we could just copy everything from

2:08:16 slash code to slash code, and then just shut that image. Oh, okay. So the build FPM, you should swap you should move the copy of everything till after composer install. Yep. And so we can leverage our cache to go. And we probably want to do composer install dash o. Does that say? Optimizes the auto loader. The dash o what? Sorry. Optimizes the auto loader. Okay. Slightly faster. And Okay. There's there's no dev or dash dash prod, I think it is. Yeah. Prod. So it won't install the dev dependencies like PHP unit. Okay. That's right, isn't it? Not having the test.

2:09:19 Our test run is just make we'll just stick it in anyway. Let's see what happens. So let's go back to our make fail, and we're gonna build FPM. The parts will run our tests, which just means using a different target. So Yeah. Can you please check the location we're copying the code into on the FPM container is the same as the volume we were using in Docker Compose. Yep. And in the FPM. Okay. Okay. Let's we're happy with the next. We're gonna run make, which is our fake CI, spells at PM. This would be our production

2:10:29 PHP CI step. And we got our extensions going, composer install. I mean, this is probably gonna take, what, one and a half minutes. You got a joke for me, Kita? What's really loud and sounds like a dog? Go on. A dog. It's a dad joke. Any compulsory. And another thing I would maybe do here is just I know I go a bit crazy adding the layers and this file will become a little bit difficult to to read. Because it'd be nice if this are text highlight and recognize the from and make these, like, rainbow highlighting in some fashion. Well, like

2:11:45 collapsible things. Yeah. Or collapsible. But as I I would probably encapsulate some of this, like, requiring composer and and provide, like, a CI layer or something like that. But for now, I am okay with that. Prod doesn't exist in layer. So It's with the FPM. Oh, I said no at first. K. The outline is that our CI would run the tests, then ship the image off to a registry. So there's there's a couple of ways I I do this. There's a different types of tests, and you can talk about that if you if you want. What I normally do is for unit

2:12:39 tests is make them part of the image build process. Mhmm. And so that the build just stops if you give a test field. There's no point shipping an image anywhere. Yep. And and then once I've got once the unit test pass and I have an image that works, I then use that as a target for my acceptance tests or end to end tests or anything like that. It's a bit more complicated, isn't it? Because I don't I don't want to have the unit testing tools in my image. No. They wouldn't be in the image. You

2:13:10 would do that as part of the orchestration. So you would run the image with an overwrite with an entry point that you inject into it that installs the mess and dependencies that you need and so forth. Right. Right. Yeah. That makes sense. So I've I've done something similar where we we build what did we do? We built the prod image, and then we built another target off it that added the unit tests, and its entry point was the tests, which is different to what you're saying. You're saying run the test as part of the build.

2:13:44 Maybe the last step of the build. Yeah. Just because it's big enough. And then there's all your acceptance tests, which aren't, you know, the non unit tests that you would then run on the after you've built the image, you then run a set of acceptance tests on it before you push it out to the registry, which should be things like AI level tests or browser automation if you wanna do a little bit of that. Or Why is this failing? I didn't see the error message. What was it? It's saying I got a PDO. But I do

2:14:26 In base. Then you're extending from base. Let's see the error message again. So that's something you added to compose the JSON. So it's quite possible we've typoed it, but it might be x dash p d o underscore minus Oh, yeah. F s o underscore. Yeah. No. Right. Show the camera. Any more dad jokes? No. There's a comment in the chat from Robert's Rawkode. My wife always says, what's the prob what's the problem of upping the project and serving it to the public? I should show this stream to her. The part of actually seeing buttons and forms

2:15:43 running is crumbs. Debugging and getting things to work takes most of the time. I I agree with that. Yeah. Definitely. Development is 90% debugging. And, actually, I think in it, it's quite an issue that a lot that a lot of people don't like doing it because it leads people to only ever write new code on nice greenfield projects. And the real valuable stuff is sometimes, like, bashing your head against the wall till you figure it out and being comfortable in that mindset. Yep. Well, that build just failed there because we don't have git. So I've added git.

2:16:24 Oh, you need git for composer, if that makes sense. Yeah. We did. I have excited the composer installed locally, which means we never had to see see why you shouldn't run any of the commands on your local machine because you need to confirm that automation works. Yeah. We didn't do composer install in your d shell, did we? We did. But because all these dependencies were there, it didn't ever have to shell exec out to the git command. Whereas in this environment, does. That's an interesting one. Okay. That's a good point. The Docker the Docker ignore file should have the vendor

2:17:09 directory. So it's never shipped with the context for clean builds and node modules. Docker Speed it all up. Right? Node modules. It's called vendor. Yeah. Vendor. Yeah. Docker file. Everything we keep editing. Everything. There's probably some cash stuff. What else do we not need? Storage. Database? Yeah. We won't put it up for now. But once this builds, hopefully, the test pass in the image, we can add this we can deploy the Kubernetes deployment again, have those containers running side by side. Oh, it's complaining. We don't have the ZIP extension. Hopefully, it still passes, but we'll see.

2:18:21 I'll start that just now anyway. So and I believe that actually needs the oh, no. It's just a command. If you do prefer dist, it won't need that. So it needs to zip it because for some dependencies, it'll download download a tarball of the release. Oh. You force it to always do a Git clone, which might isn't what you want on prod. Alright. Because that would also there's a git attribute that excludes things. Right? Mhmm. And that's used to exclude things from the tarball version. So some dependencies will end up shipping you their unit tests and their documentation and stuff

2:19:21 if you force this. If you prefer So the dev command was available in the extension extension before I started this build. It wouldn't have pulled with the get anyway. So Yes. I think it might be falling back to doing a clone in all those instances and it'll be fine. Yeah. I think it is. But may but I don't know. I don't know. Maybe you don't care about whether that's in your image or not. I know some people who have their own scripts post you know, compose a post install scripts that goes through and deletes loads of stuff from

2:19:53 the vendor folder to keep the package size down, tests and that sort of thing. Oh, this is slow. Should have used Composer two. Hang on. Yeah. Don't know if it's in the Docker image yet, but it does parallel downloads. Hopefully, it won't be long. We're close. We're almost there. I mean, once we get this once we get this deployed, you know, that this code is all available. So get that that's Rawkode, PHP examples. So Docker Kubernetes is the well, I haven't pushed it. This is the slim stuff from last week, which is now on a slim directory.

2:20:52 And I will push all of this code with the Laravel directory or the Peng CRM directory. And I think maybe another good session for another time would just be maybe cleaning this up and and optimizing it for production. I don't think we're gonna have to to change to to do that today, but we will get it working. There's a lot involved in containerizing and deploying something to Kubernetes. It's it's nontrivial. We've only been doing it for half an hour. Might be interesting to deploy it to some cloud somewhere in a future session, work through some issues,

2:21:33 pick a cloud. Well, I mean, I work for a cloud, so we'd have to use them, obviously. So we use different. We'd use a diff but that's the promise of Kubernetes. Right? Is that you can deploy to whichever cloud you fancy. Oh, yeah. Totally. I mean, Kubernetes is an API. And if you deploy to that API, the cloud becomes irrelevant unless you're consuming some managed services. In which case, of course, you're gonna get a a level of vendor lockout with each cloud. Yeah. But the the Kubernetes API being your deployment API gives you that freedom to move clouds

2:22:16 if you wish and take out managed services where needed. Or as you said last week, just do a Heroku deploy and skip all that. I know you're saying that sarcastically, but there's a lot of small teams don't need to be doing this stuff. No. No. Just yeah. If you're looking at Kubernetes, I'd hope that you're deploying more than a Laravel application. Yeah. More than we're deploying today. Because you can I mean, this repository's got a profile in it, so you can just push it to a cloud provider? This is really slow. That's over five minutes

2:22:57 now. So should we think about why max is slow? Well, that's not why this is slow. This in this instance, though. Right? Because Oh, yeah. This is just This is just the build context, which is a tarzip of the directory excluding the Docker ignore files sent to the Docker engine and the virtual machine. So this is just slow because composers I mean, we are using the get option because that wasn't available, which it probably has thrown it down. Failed. No. So why is it failed? So Doing the database. So that's the class map optimizer that runs at the end.

2:23:53 Having a problem. Why did I have a problem, Kino? Why? Hey. That isn't a file or a c. It isn't a folder or Fold the raw file. Weird. It should be we copied every oh, no. We haven't copied the code in yet. So the optimizer requires the code to be there. Yeah. Just take out the dash o then. What we can do take out the dash o there. But we have to rerun all anyway because this step failed. But take out the o anyway because this is the way you should do it. Take out the o at that

2:24:39 step. Then optimize it later. And then after the dump auto loaded dash o as a second step, which I think is better in terms of cache and validation. Well, let's just hope this is much faster with that zip binary available. Otherwise, you've got five minutes of dad jokes to tell. I have to go soon. I can leave you to finish the stream again. Oh, you should've you should've put the ZIP on a separate line. Just like the population of the yeah. Those are quick, though. I mean, that's gonna be done in ten seconds. It's been running for twenty six seconds.

2:25:34 See. See. Pretty quick. Hopefully, this is much faster. That it doesn't have to go with the GitHub API. Yeah. And I've noticed some people who are making this a lot faster even in the uncached state by doing things like sharing the composer cache as a volume and using cache cache from to point to in it the previous image, which has its composer cache populated and that sort of magic. So there are ways of speeding up composer install. Yeah. Definitely. It's a good way to do it. Using, like like, the cache from this very handy for stuff like that.

2:26:22 We're committed we're committed now. So Like, why is it still updating dependency? I mean, it's not even at the fetch stage yet, is it? Oh, there we go. Yeah. So these installs are probably the zips. Yeah. These are much faster. You can see it blazing through this time. And one of the improvements in Composer too is that resolution step is faster much faster. And I guess even if it was using concurrent downloads in Composer two, I don't even think I expose many cores to my Docker daemon on Mac. So maybe I'd be able to take advantage

2:27:17 of them. I'm assuming it does the concurrency by spinning up multiple threads and having them each download one. So it'd be parallel downloads would be how many cores I have, I guess. It depends if it's CPU bound or network bound. If it's IO bound if it if it's sort of network IO bound, it might not matter how many calls you're using, having multiple downloads at the same time. I mean, at the other end of the network. Well, it was faster, but unfortunately, the seed thing was still doing that step. Oh. Why? In composer JSON, is there a post install

2:28:15 hook? Yep. I guess that's been true as the problem. Right? Yeah. It's not default flagged to one, so us leaving the flag out hasn't helped. It it doesn't really matter at this stage, does it? Let's Let's just take the flag out of the thing. Viewers will get the point. Oh. But then we can't leverage the build cache when we don't modify the composer dot JSON. And I still think that's important. So I'm just gonna disable the optimize and we'll do it explicitly. Because it's a deployment concern. I don't think the compose of the JSON should be setting

2:29:05 that anyway. I got root. I will decide when to optimize my auto loader. Yeah. Which means this is gonna take thirty seconds to do update dependencies. I don't if that was the extension thing. So this is gonna take sixty seconds based on the last run, but not too long. Hopefully, it wanted to optimize on the data loader, and it'll do it on our manual step. You know, just so it's really clear why we're kinda fighting with this. Mhmm. If we had moved this line here, we wouldn't have this problem. But because composer install, all they need is

2:29:47 to run when the composer dot JSON changes. We're trying to make sure that we leverage the build cache. Because our composer dot JSON probably won't change often. Which means that we only need to do this step forward. Yeah. And that done per auto loaded does the reason it's not default is it scans all the files for PHP classes. And then builds a big class map, I think. There's a bunch of other stuff we could do to optimize PHP, optimize FPM. You can prewarm the build. You can prewarm the op cache. You can now actually, I don't think you can do anything

2:30:37 to prewarm the new preloader cache, but other things we could do to make the container more ready to serve stuff. Yeah. You know, the op cache, the in memory cache, you can give it a file based backup for So then if if that file based back file based sort of second layer cache is present in the image, it'll start faster on the first stop. Good for workers. I'm Right. Let's just move it so we copy everything in. We're not gonna get to the bottom of this. One of us can figure it out later. It's almost therapeutic.

2:31:33 Right? Maybe not quite. Alright. I need to go in about eight minutes. What should what should we achieve between now and then? We'll get this working. Let's assume that works. So next, we need to handle the migration step of this deployment. Yeah. So it's just how this works. Now there are two different approaches to doing database migrations depending on what your your team does. So the first one, which is probably the easiest one to get started, is to use something called an edit container within Kubernetes. It works like this. We give it a name. We tell it the image that we

2:32:11 want to run. Now, it's just gonna be our FTM one in this case. And we can tell it to run a command. So I'm just gonna do echo because we have not deployed the database to Kubernetes, We're running very low at time. And you can imagine that what this was actually doing would be a it just go and make it won't be there. PHP, artisan, DB migrate. Now this runs in the container. You're you've got a guarantee with the Kubernetes API that and no containers for lane 24 down will run until this executes successfully. So does that mean

2:33:01 in in terms of deployment, what does that mean for downtime? How how does that coordinate with an existing running system? So with our deployment in Kubernetes, we also have access to the update strategy. Mhmm. Strategy. Where I can then say rolling update, and then I could tell the max search. Let me just while I play this for that. So when we specify the strategy, we can tell it that we're only gonna accept, you know, one to be unavailable at any given time. Right? Also takes percentages, so you could say that we're allowed to scale above

2:33:43 if we've got a record of score, if we can scale above by a %, we'll we'll watch four new months and do the rolling upgrade that way, or we can specify the maximum unavailable and say that we want to do an upgrade this way where we can we'll take one out, deploy it, and it'll work. This also requires this approach requires that your that your migrations either do database level locking so that you don't have multiple containers trying to run at the same time, or that there's gonna be it also has to be idempotent, which is

2:34:17 very important. Most database migration systems are. If you're if you've already made the premeditated effort that your database migrations are always gonna be additive, you know, you're always adding columns, which is a really good way to handle database migrations, Which means that your older version of the code will still work even if the migration hasn't run yet. This is a really nice way of handling this. Then you can remove I'm just gonna comment these out so they're available when I push the code. Then you can remove the other container and allow this just to deploy as

2:34:53 as but before you do the upgrade in this, you can use a job. There's a special Kubernetes type where we would use a job here, and then that job would also have a spec where I'm not gonna be able to I don't think I can all complete job stuff, but and that let me pull up an example from one of the helm charts that I've intend. So I use a job in this to configure the authentication in in FluxDP. So you set the API version and the kind to be job, and then we just specify a container with

2:35:38 a command that we want it to run. And that could just be a job that spends a lot asynchronously to you or before your deployment and runs the migration that way. So let me try and clarify the two different approaches and why you need where how to pick or how to decide which one to pick. So the only reason I would ever use a job approach would be my migrations do not do any database locking to make sure that two don't run at the same time. In this case, you have to, as part of your CI, run the job

2:36:09 object first, then do a deployment update. If you're in a position where you do have the database locking, which I'm assuming probably Laravel has already got sorted with the ORM, then go with the edit container approach and tweak your max unavailable or search depending on the failure condition that you're going to accept for that. And whenever possible, regardless of approach, always try and use additive changes in your database migration. Yeah. I think there's a point where you know that It's okay. Like, we can take that. Anyway, like and the the cache is there, so it should be okay. There's a there's a

2:36:49 point where if you've been adding columns consistently, some of them are now redundant and you know there'll be a point where you know there's no running code using that column. So you can you can then go out and take dump some old tables and you can even look at even in a massive system, you can look at some of the access logs. This table's never been queried in the last year. Let's delete it. Does it have you read the refactoring databases? I have not. I've got it to hand. It's pretty good. Martin Fowler imprint. Lots of patterns for

2:37:28 restructuring databases with forward and backward compatibility. Oh, nice. Mostly involving lots of triggers. So I was just trying to work out why my build cache wasn't used there. And it's because as I was talking, I was modifying the whole directory. Yeah. So there you go. Oh, we're not even but all locked. It'd probably be ignored for the Docker. Correct. And then that would have been cached. And my test failed. I'm gonna remove the test because I just wanna get this deployed. Can you make that? Yeah. We'll come back to that. My mic fail, isn't it?

2:38:23 Oh, it's just an absolute calamity of errors. It's good to show, you know, humanity. Oh, yeah. That's exactly what it is. So and I'm gonna assume that image is deployable anyway because that did build in that last one, the tests were a different step. Right? Yeah. No. I haven't purchased the production image because it was targeting test when it failed, not thing. Alright. We just need to give it another thirty seconds, and then we'll get the deployment done, and we'll both be over here. Yeah. I think we're definitely gonna have to have another session on this.

2:39:05 Yeah. Much to cover. So much to cover. We will build on this one. Right? We'll make it better. Oh, yeah. Yeah. We'll we'll stick with this. Okay. So now we've got our image built. So I can just do a cube apply opt Kubernetes. Let's see. Why is it only got one container? Duplicating the name? Is that okay? I'm not sure. I've never actually I've never tried that. There you go. That's better. Yeah. The name oh, because it must merge all that. Yeah. It must flatten it. Okay. So pill damage, and that's because I forgot the policy on our APM one.

2:40:00 That's where we again, let's just run a watcher on it. Right. Okay. Both are running. So in theory, we're now at the port forwarding state again. So two containers, one pod. I don't know if I'm used to being there. We're gonna get that off. Okay. So now the logs director needs special permission. So Why didn't that happen previous oh, because we Slim is logging to standard out. So the the problem here is that we've not configured Laravel to log the standard out. Why can't it find why can't it write to that file? I think I'm assuming this directory doesn't exist. Let's confirm.

2:40:58 Is it gonna be file permissions or something like that? The exec into this container, we need to specify it's the f e m one, and we wanna run batch. What do I call the container? Peng c o m s. Forage. It is draping through it. Does the container run its route? It does. Yeah. We know. I don't know. That's a log from that's been mounted then because there's not a Docker a Docker ignore fail that's given that. As last time, I have to go now. I've reached the last possible point where I can get to my Let's just yeah. Well, let's

2:42:08 just call it here. Let's pick it up again next week from here and see how far we get. Because like I said, we've got a lot to cover, and there's no point in me trying to rush it. Yeah. We're quite close. We've got some working images, working containers. They work fine for dev. We just we've only spent an hour trying to deploy it to production, I think That's a reasonable time scale to fail. Let's do it again. Alright. Yeah. I will push this code just now. It's available on gitlab.com/rawcode/php-examples. Kieran and I will be back again next

2:42:44 week to continue our efforts to deploy a production Laravel application on Kubernetes. Kieran, thank you for joining me again. It was a pleasure, and I'll see you again next week. Bye. See you next week. Bye.

Technologies featured

Meet the Cast

Weekly Cloud Native insights

Stay ahead in cloud native

Tutorials, deep dives, and curated events. No fluff.

Comments, transcript, and resources

More from Rawkode Live

View all 173 episodes

More about Docker

View all 36 videos
Kubernetes

More about Kubernetes

View all 172 videos

More about Laravel

View all 5 videos
PHP

More about PHP

View all 7 videos