 So my name is Chris Tankersley. Hopefully you remember me from yesterday. But today I'm going to talk actually about Docker and some things that we can do and think about as we start to design workflows and move it more into the development stage. Docker is one of those things that has really started to pick up over the last three, four years as a development tool, as opposed to just primarily a deployment system. Even though originally that's kind of how it was designed. So I stole this from the Docker website years ago. I don't even know if this page exists anymore. But doctors build as an open platform for developers and systemants to build, ship, and run distributed applications. And that was primarily its core focus. It wasn't necessarily designed as a development tool, but we make it work for us because realistically we want our setups to work all the way from development to production. And we want to get away from that it works on my machine mentality. So depending on how long you've been doing development we went through the same kind of renaissance seven, eight years ago with tools like Vagrant which made virtualization a much more attractive option for doing development because it fixed a lot of problems. Doing work directly on your machine causes inconsistencies that you don't want to have minor PHP version differences, configuration differences. And Vagrant promised kind of that same setup to get rid of the it works on my machine problem. But very few places actually followed through on it as they generally didn't use the same puppet or Ansible configs in Vagrant that they actually used in production if they used any at all. Docker still kind of has that problem but one of the big advantages is you have a same binary base across the board so it even lessens that more and more. It's kind of at the bottom here Docker enables apps to be quickly assembled from components and eliminates the friction between development, QA, and production environments. And that's really the big thing that we worry about as developers is making sure that our workflows from dev to production are as seamless as possible and we can reuse those environments as much as possible. So I'm going to spend just a small portion of time on exactly what a container is. If you are not familiar with how Docker works already I would suggest maybe going to one of the other talks I'm not really going to go over the basic fundamentals of Docker above and beyond this because how a container works is a very important topic. So if you're not sure how Docker works or exactly how you want to get like composed set up this is going to be a little bit slightly higher level. But I want to make sure that we're all on the same page on what a container is because what containers are kind of influences how we actually structure our workflows. So in the really old days of my previous job last year you would deploy code to a bare metal server and in all honesty 80% of the projects I work on probably still get deployed to a bare metal server it's really easy to set up it's very non-resource intensive but from like a large enterprise setup or like you looking at something like DigitalOcean or AWS or Rackspace it's super inefficient so we've got a fake little computer here with some processes being used in a normal setup you've got your processes up there in green and then you've got a little bit of a little bit of a talk to an operating system and the operating system talks to the hardware and this works really well if your PHP application says I want RAM it asks the operating system instead of having to understand how to talk to the actual physical hardware to get that RAM. Now there is a little bit of overhead our operating system takes up some resources so that's our graph on the side we're going to say our operating system takes up 10% CPU 20% RAM and then we're going to add that and gobbles up some more of that makes total sense we use resources but if we have a single application on a single server that's really inefficient but we really don't want to overload servers too much and there are issues especially security issues with shared hosts so a lot of places moved over to virtual machines and this worked great this is why Vagrant was so powerful resources used with all of this so generally with a virtual machine we still have our base operating system we have the hardware at the bottom but then we have this intermediary layer called a hypervisor sometimes that's built into the operating system sometimes it's a completely separate software product like a virtual box you have things like hyper V in windows which kind of blur that line but basically the hypervisor is an intermediary between the virtualized operating system so whenever you deploy an application you get a whole new operating system with all the processes on top of it so our resource usage goes up but we get the better separation of concerns between those individual machines that's great for big companies like AWS and Rackspace and stuff because they can shove more operating systems on a single machine not worry about them clobbering each other with different configuration needs or whatever and you get some benefits of being able to move them around through some of the various VMware technologies you can do cool things like hot swap VMs with no downtime but there's a lot of overhead with that because if your PHP application needs to read a file PHP talks to the virtualized operating system which then has to talk to the hypervisor which then has to talk to the real operating system which then has to ask the hard drive for the information and then it has to flow all back up how many people in here use MacOS with Docker? That's why it's so slow because we cheat and I'll talk a little bit more about that in a bit but what containers do is let you run everything bare metal but just separates out the processes themselves into little walled gardens so here we've got them walled off in the red with our VMs it comes up kind of brownish on here but our VMs are separated by the red boundary here containers we do the same thing but you'll notice there's no intermediary layer so if PHP asks for RAM it's going to ask the actual operating system and the operating system will give it RAM the separation just becomes what the processes can actually see alongside themselves so they're literally just walled processes so in a normal setup you have a kernel sitting at the top 90% of the time it's going to be some sort of Linux kernel so we're going to fake it and say it's a bunch of kernel here and that has its own worldview so everything that's in the root directory get your bin, your Etsy, your VAR-WW clients, whatever and when you run processes they talk directly to the kernel and the kernel by default looks at that view all a container does is create a new view and point a process to that new view that new view gives us some things like not being able to see other people's processes getting your own separate network topology all kinds of nice little things but a high level all it's doing is just creating another little world inside your system so Docker itself when you get right down to it is just vagrant for containers Docker doesn't actually do anything magical it's using existing technologies Docker nowadays especially is an entire ecosystem so we get this kind of muddled terminology with like when we say Docker what exactly are we talking about nine times out of ten you're probably talking about Docker engine which is the actual thing that the command line you're typing into and the API that it's talking to you have a couple other things on top of that like machine compose and swarm though I really should probably take swarm out at this point because nobody uses it, it failed in the market but you've got Docker composed for your orchestration and Docker machine for building machines that do Docker so where a lot of the pain points come in especially as you start to talk about Docker with teams is how it actually works for you individually on your machines so if you're like me and despite having a MacBook I generally actually do most of my programming on Linux so it works out of the box using a bunch of built-in things into the Linux kernel because Docker is primarily actually a Linux technology it takes advantage of things like C groups and the way it handles processes and visibility straight out of the box it can use a variety of different container technologies under the hood but primarily it's just talking to a Linux kernel because it talks to a Linux kernel the other two options Windows and Mac OS all actually have a virtual machine in there that meets up some resources and causes some additional pain points because you're probably not deploying to a Docker install on Windows or a Docker install on OS X I'll put a little asterisk next to that so with OS X most of your problems you're going to run into are because of that virtualization layer it doesn't quite handle port binding the way that it does in Linux or it will in production ignoring that you're probably running some stuff in production but just straight Docker it works a little bit differently because you can do different port bindings and Docker expects ports to work a specific way and they kind of fudge a lot of that in OS X because of the XI virtualization layer that layer actually has a really poor I.O. implementation so if you're using basically anything kind of symphony base that uses a lot of annotations reading and a lot of I.O. at the operating system level is super inefficient which is what causes all of the slowdowns it's not actually a Docker problem but it's also a problem you'll never have in production it exists solely because of that virtualization layer Windows actually has three different ways to run containers all fully supported by Microsoft and Docker the bottom two are the most common use cases for people so you have Hyper-V containers where there is a virtual machine running like OS X it sets up a small little Linux machine actually running a distribution called MobiLinux and runs everything in there the newer style way which is still technically in beta preview is the WSL2 stuff do we have anybody in here who runs Windows? Decent handful WSL2 and everything is wonderful I love it it works with native containers but still actually in a virtual machine because WSL2 has moved to a virtual machine WSL1 doesn't use a virtual machine it actually has this weird translation layer to turn POSIX stuff into native Windows calls which is why there was no overhead with WSL1 WSL2 introduces a tiny amount of overhead but they've done a ton of work to get that to work it's not actually a full-on Linux installation though so if you need things that use like SystemD it doesn't work but Docker is working with Microsoft to actually make that all work itself Windows is in a unique position as well because they actually have a native server technology that they kind of worked with Docker to do you can actually Dockerize Windows programs Windows Core perhaps so .NET can actually be distributed as a Windows container or what they call a server container you can actually deploy that on server 2016 and up and I believe Windows Pro and up through Docker desktop so if you're building Windows applications or .NET applications you can actually compile them as a Windows binary and ship them off as a container there's no VM involved because a Windows binary knows how to talk to a Windows kernel there's no need for an intermediary layer we have to use virtual machines because we tend to run Linux binaries on non-linux systems so you can't expect a Linux binary to understand how to talk to a Windows NT kernel or the mock kernel inside OSX so that's why we have those virtualization layers and they will probably never go away Apple doesn't seem to be interested in coming up with their own kernel setup but even if they did you'd have to get macOS containers which would be separate from your Linux containers which are separate from what you probably deploy into production there is the kind of like Linux-y the Pi subset of containers because those are ARM processors not generally x86 processors you have to get Pi-specific containers for a lot of those things if you are on an old version of OSX or you're on Windows less than 10 or you're not on Windows Pro sorry, pay the upgrade fee to Windows Professional there is Docker Toolbox I do not suggest using it at all many of you are laughing but it's using virtualbox which has its own performance problems especially with the file mounting system so if you think it's bad on OSX natively it gets worse with Docker Toolbox so they offer it but it is definitely a best effort kind of thing you will have much less headaches just upgrading your operating system level as much as you can or paying the license fee to go from home to Pro if you are in an environment where your company says no I do not really know how to help you install a Linux virtual machine but I really caution you to not standardize on Docker Toolbox I've been giving this talk in various forms for many many years and we have finally gotten to the point where I think it's very comfortable to say we don't use Docker for everything anymore it really is turning into a development layer tool containers are not just Docker in fact in production you're probably not actually running Docker I mean people in here are running Kubernetes a good smattering of you it's probably not using the Docker engine under the hood it actually uses a different container technology under there the cool thing is all of these different tools are talking to each other so you can use all of those things back and forth and you can reduce that friction even if you're not using the tooling from top to bottom Red Hat's been working a lot on their Podman Tool which is a rootless version of running containers it can run Docker containers and it uses the standard there's a standard for how you should build images and things like that and it will use those as well once they get all the Docker compose translation stuff handled I'm going to probably assume that's what a lot of people are going to start running in production for simpler applications Kubernetes has won the distributed multi-node system so I'm not going to get into production level stuff here because that's a whole other ball of wax but just remember that Docker is a single portion of the ecosystem not the entire ecosystem so probably one of the best ways to think about the workflows for what you want to do is this idea of a 12-factor application I believe it's 12factor.net has a list of really good ideas to think about as you're building distributed applications and even if ultimately you're not going to have an application that needs to scale to hundreds of containers because Docker was designed with distributed in mind 12-factor applications give us a lot of things to think about and implement as we start to add workflows to our things because there are a lot of anti-patterns in Docker that you can really fall into because they're easy but that will make production deployment very hard and if you follow many of these workflows you're actually going to get to a point where pushing to production should be fairly seamlessly even if currently you're deploying to a bare-metal server and you're just using Docker for development so a couple of these are going to sound really, really obvious is anybody in here not using some sort of version control even if you're still using CBS is anybody not using version control this is the first conference in a while where at least not one person has risen their hand you want to keep everything in version control for many obvious reasons but the big reason for that is you need to start keeping track of everything in time you should have a single repository for every full application that you're deploying I don't care if it's a microservice or a monolith or whatever everything for an application goes into a singular repository I've worked on some consulting contracts where you have your config stored in a specific directory your application in another for example they're building an application currently in vagrant so that has its own config repository and the Docker stuff's in another one and you marry those two things together when you do a deploy that stuff gets out of sync anything that you need for your application goes into the repository sands like private configuration things if you're not tagging your releases now begin to tag them for many of this this is probably a kind of a duh thing but there's many places that don't tag any of their releases you want to start getting in the habit of being able to say I release this thing on this day pick semver, pick date-based, pick incremental I don't care but pick something consistent that you can point back to and say that I deployed version 5.3 on this day I can go back to 5.3 and look at it as of that snapshot is anybody ever moved a tag? you are better than some Fortune 500 companies I've worked with you can move tags you can delete them and re-tag things don't ever do that because then you lose confidence in what a version number is one company I consulted for at one point we had five different versions of what was it, it was 2.3 of an application deployed to customers you could not guarantee 2.3 was the same from customer to customer doing tech support for that was a nightmare for that team because they literally had to figure out which Git commit with that 2.3 pointed to so don't ever move a tag if you tag something and you find a bug congratulations you have a new tag you don't move the other one I don't care if you found it four hours after you make a new release and run your unit tests I love this debate monolith versus microservices is anybody considering moving to microservices? ask yourself really heavily why you're moving to a microservice there is nothing wrong with a properly constructed monolith if you cannot construct a monolith now you cannot construct a microservice if you're breaking up a monolith congratulations your monolith now exists on five different servers microservices have their place but you really need to treat them as separate applications not as one big global application each part of your microservice needs to be treated as some sort of third party service I have no control over Twitter so if my application relies on Twitter I have to be completely separate from them Twitter is not going to say oh hey by the way we're going to break this in six months make sure that you're up to date and that you deploy on March 25 because we're also going to deploy if you have to have that level of granularity with your deploys you just have a monolith that you've broken up and now just have to figure out all that coding stuff microservices are not bad but you have to treat them as separate applications if you store all of your microservices in let's say a gigantic monorepo congratulations you have a monolith treat any microservice you have as a separate project completely they can all be handled by the same team but they have to be thought of as complete concrete separate applications in their own repository with their own deploy steps with their own configuration kind of like with the bare metal stuff I generally just build monolithic applications because they work they can scale just as well so if you're going to move to microservices especially if you're breaking up a monolith ask yourself what you're gaining out of it before you make that step it might look like it will help you with Docker but it probably won't this is another dull one for us as PHP developers but you should declare all of your dependencies don't do something like commit your vendor directory to your repository it's bad it gets you in the habit of editing those files with no kind of way to actually audit those if you need to update but you need to explicitly declare and isolate those dependencies if it's a package.json, a composer.json whatever else you're using for your application hot take of the day commit composer.json and your composer lock files both of them the only time you don't do that is when you're distributing a library but at that point you're not building containers so it doesn't really matter so like my day job I work on the PHP SDK at Nexmo we don't ship a lock file because that introduces some compatibility issues with people trying to build their stuff sometimes guzzle is a great collider with our stuff if we ship a composer.lock file so we just ship a composer.json if you're shipping an entire application commit your lock file that way you get the specific version that you know works with all of your tests and all of that it also helps if you need to rebuild an older version so that you make sure you get the old version of the libraries you're using and you can debug those properly I worked with we didn't commit the lock file so if we had to go back to an old version for a customer because they just hadn't upgraded I got newer versions of libraries which may introduce slight bugs despite how well people follow semvers sometimes those things slip through and maybe they were being caused by a library bug that has since been fixed in a patch update but we would never know because we didn't keep a lock file and kind of along that when you're talking with Docker you have that same repository that you keep everything else in your Docker file is a dependency it tells you what image you're going to be building from and the steps you need to build it so that needs to live alongside the rest of your codebase for configuration you're going to have to look at how you're going to split that up but ultimately you want to start storing your configuration files in the environment itself not as files or .m files or whatever you want to make sure that your application can just request whatever it needs from the system and it will make life much easier to deploy anything that's environments specific should move to these configuration variables if you want to ship a default set pagination automatically to 25 or a default time zone for a new customer or a deployer that's so bad but things like where's my database located where's my caching located where are my PHP servers at those things should start to move to environment variables we sometimes get in the habit of being stuck on like oh I shipped a .m file so we're kind of doing it but if you're shipping a file you're still kind of doing it wrong .m exists so that we can replicate environment variables in development not as a way to actually deploy them this makes it a lot easier to deploy your code because you can pull an image down give it some config environment variables and start it up you don't have to edit any files you can just point it to your dev database or whatever and move those things around it's a lot less you have to maintain in the containers themselves as you go through and you think about and you just start to say well I need a configuration to start using get-in your code cares less about what external services it's talking to and it makes you think about where you're actually hard coding things in for Docker itself depending on how you're actually invoking your application if you're just doing like a real basic apps you know prototyping something you can use the dash e command or the dash e parameter and do multiples of them to do var name equals value and that will pass them in as a normal environment variable if you have a lot of them that you use commonly you can use dash dash emv file and pass it in any file that has all of these things skeletoned out as well and you can use them in parallel so if you want to change something real quick you can throw a dash e in there and kind of override stuff if you're using Docker compose you can also specify this so there is an environment file and an emv section where you can use Docker compose if you want to hard code that kind of stuff but get in the habit of pulling those things out and tracking them that way instead of worrying about what goes into a configuration file and speaking of that anything that is not your application treat as a third party treat as something that you don't actually have any control over usually like MySQL we just say connect to MySQL on localhost well with Docker you don't have a concept of a local socket you technically they're there but you can't talk to MySQL because MySQL is not running in your container you need to get in the habit of talking to everything over a network but then also thinking about well if it's over a network it's technically external how do I handle failures how do I handle network latency I know most of the projects I work on we will probably use RDS in production but not development so being able to separate those things out through things like the configuration files but then still treating it as a network resource really come in handy there's less I have to do in my application code if I just assume it's a network request across the board you don't really have to start thinking about anything that's running locally on the box like what do I need to have your application just needs whatever it has everything else is external you can do things like scale up your database server through proper database scaling techniques because don't run your database in Docker that's not how they scale but you can take care of things specifically and then if you need to swap out to a third party service like a SAS or whatever you can run fake versions of them locally and then point to the real ones in the real world environment if you're using environment variables it should just be an environment variable config change no code changes and it makes it a lot easier to scale up especially as you start to scale up individual containers in the back end which is another thing we'll consider but you can if you start to treat everything as external you can scale your individual pieces of your application much easier there's nothing specifically inherent in Docker to do this this is something that's probably going to be a code change for you but I think that's something that you can really consider as you're starting to architect your application and build the workflow for yourself your build release and run steps should be separate concrete stages that individually can be run by themselves and re-run by themselves with the same output so you should end up with actually three steps every time you do a deploy the first one is your build step that's going to take all of your dependencies compiles any files you have like if you're probably pulling down Composer and probably running MPM install but if you're using Python if it's got to compile any files in the back end do that and you'll get some sort of artifact out of that depending on what you're using that might be a tar file for Docker it's probably going to be an image file but this will be a thing that has no configuration or deployment information whatsoever this is just a thing that I can put on another machine with some configuration and have it work your release step will pull a build artifact off the shelf and then start to prep that and put that into production and then your run step will actually do the flipping from one version to the next so for Docker itself you want to start thinking about this build step as being an image that comes with everything you need to run so the standard standard PHP app if we go really super simple and we're just using Apache and mod PHP this will be a single image with all of our dependencies and all of our code inside of an image that we store somewhere you can put it on Docker hub you can put it on Amazon don't care where but we'll have a pretty fat image with all of the data inside of it that we need to run our application we can then pull that off the shelf and shove it wherever we ultimately need to deploy it you should be able to run that build step multiple times and get the same output if you're using your composer.lock file you'll always get the same dependencies pulled down if you're using the same image versions they should all be pulled down the same every single way when you're ready to deploy you'll grab an image from the repository and shove it wherever it needs to go be it my local machine be it a QA server, like a QA cluster you've got set up so we'll like Kubernetes production but it's never going to actually build directly from the repository you want to get in the habit of pulling those images from a registry not building it and then deploying immediately tag all your builds and by that I mean come up with a nice naming convention I usually do it by date underscore version because I might deploy like dev versions or QA versions multiple times throughout the day as we tweak little different things you want to be able to say like which version am I actually running in QA in production query Kubernetes real quick and be like okay I know exactly what that is and track all of your releases somehow this is a little bit more important if you're doing like client or consultant where type of stuff but if you have like a white box app product that you're selling off to people tag who and what goes out to where different customers probably have different release schedules and are comfortable with different layers of work you want to know what actually goes out and you want to know what version you're pulling down when you start to do development if you are not building now start small your build application can be run composer run MPM do whatever MPM build stuff you need to do and then use Docker build to just make an image that can be your entire build step and then shove it to a private registry if you don't want to really worry about it Docker hub is super cheap I'm not going to tell you how but there are ways to get around some of their limits but it's really super cheap and really easy to get into I like pushing people that way because AWS can be very intimidating especially if you're not deploying to AWS if you're deploying to AWS use AWS's registry you're probably already fully invested in that stack just tack that on for your build step there's a couple of things you can look at generally depending on how your application is structured you might be using individual Docker files or you might be using a multi-stage file but I never keep my Docker file like in the root of my directory I actually keep it in a separate docker folder so you can use dash-file to actually point to a specific file separate from where you're running the command I always recommend using dash-no-cache Docker tries to be really super helpful in that if it doesn't need to rebuild an individual build step it won't but you really run the risk of getting outdated layers throughout there if you've got something where you're pulling down a couple libraries for dependencies for different things those can get out of date and if those get out of date you're not pulling in security fixes that can be really bad from a lot of standpoints so always get in the habit of not using a build cache for that it'll take 40 seconds longer but then you can fight on the chairs as you compile all your code a couple people laugh but mostly it's a security issue always make sure you're pulling down the latest dependencies that you have and then do a dash-dash pull that makes sure you're pulling the latest version of whatever your base image is again to make sure that you're getting all of the security updates three or four years ago there was a really bad outbreak where various versions of bash had a lot of security flaws turns out a ton of containers should have updated but they never did so a ton of containers went out with a bunch of bash flaws you don't want to have that happen to you so always do a pull to grab the latest version and always use no cache to make sure you're getting the latest dependencies so you can do docker build no cache my docker file is in a docker php folder because in this example nginx was in its own separate folder just makes keeping configuration files separate, cleaner so you use dash-f to pass that into and then dash-t will give you your tag name you really should be using like vendor slash whatever but I've only got so much screen space and there's an option that most people don't really notice with docker build and that's at the last parameter for it last argument is actually where the root of your project exists you don't have to run docker build next to your docker file you don't have to run it anywhere near your code you can point it to a file and then wherever your build output is and change the context where the docker file runs the docker file will run in the context of whatever that last argument is so you can have docker build run from wherever and just point to wherever your build directory is so a lot of times I'll actually have that build directory as like a variable in a shell script with whatever the last build folder is because I'll randomize file names and then the last thing you can do or one of the steps in your docker file you'll do is you'll just do a copy this is what trips a lot of people up trying to build these copy and add if you want to use add runs in the context of your build directory not in the context of where your docker file exists so if I tell it to look in opt builds 2016-10-10 copy will actually copy from that folder into my container from var www if I run this command in the root of my directory but I supply something it's not going to run in the root of my directory so that's where you can start to split things up but you have to be really careful about the context this runs in because if your docker files in a sub folder and you don't change that target it'll try to run in the context of that sub folder not the root of your project so that's something to keep in mind another hot take of the day execute apps is one or more stateless processes if you have more than one process in your container you're doing it wrong don't at me you're doing it wrong it's not designed to run multiple processes in a single container it can but just because you can doesn't mean you should if your container contains a supervisor process like supervisor D you're doing it wrong sorry you're doing it wrong if supervisor crashes it takes everything else down with it because docker will only ever watch one process at a time it is not designed to run multiple processes if you need that functionality don't use docker use something like LXC or LXD from Canonical those are designed to replicate an entire machine with a full init process and run multiple things at once docker only wants one process at a time the reason for this goes back to my very first slide if you're distributing your application across multiple servers you don't need to scale everything all at once so traditional PHP app you probably have Nginx and PHP FPM at a bare minimum for your application those are two separate processes those are two separate containers don't use supervisor D to start up both of them because generally your bottleneck will not be both of them if you're having slowness serving static files then you will need to scale up your Nginx end if you need more raw processing power scale up your PHP processes this also allows you to swap out images as you need so let's say you want to upgrade from 7.4 to 8 I don't think we're building 8 containers yet but let's say when that day comes you can just swap out that one image not touch your web server image and have a lot more fun with that but make sure that you're not getting in that anti-pattern of running multiple processes in a single container you want to get in the habit of like I said treating everything as an external resource and if it has a network port export that you want your services to live on ports not as sockets this is built in automatically every container gets an IP address in Docker so you can reference a container by its IP address and the port on it this gets people a little bit confused because if they're running multiple Nginx multiple PHP FPMs those all are listening on port 9000 I now have a bunch of things listening on port 9000 that's awesome but they're all probably running in their own IP space and they all have their own individual IPs so we can get away with that because we've properly separated everything out as external things you're already talking over a network shouldn't be a ton of extra configuration from that end this will also let you work with service locators that are port based there are some that will say oh this is port 80 we're going to assume it's a web server you will automatically dump this into a web server group I don't personally like those but there are some that makes life easier for people because we adhere to the one process for container rule we can easily adhere to the concurrency rule which is scale out via the process model scale out the things that need to scale don't scale everything 100% you're just going to waste resources you really have to think about what happens when your application scales though Docker will not make your application scalable it will help you deploy multiple copies but that doesn't mean your code is going to automatically know what to do so your code probably runs perfectly fine right now with one single application what happens when you have two of them running what happens when you have three of them running if a user uploads a file how do the other two processes get it where are those things being stored how are you handling that in your application Docker is not going to fix that for you but it allows you to scale out a little bit nicer in the case of like uploading a file you might want to consider doing something like fly system as an abstraction layer and saying well in development we're going to do fly system to a local folder but in production it's going to be an S3 bucket because you're taking advantage of just changing that configuration at that specific deployment time but again we're doing one process per container so we can scale those things just up and you can scale up just a container that's needed Docker Compose has ways to automatically scale your containers up and down in development so if you want to test how your application scales especially under load you can say give me ten instances of PHP FPM but two of Nginx does anything fall over as you start to run your tests ultimately your app should not care how many instances of itself are running just the fact that it is running it should never know how many copies of PHP FPM are actually running how many web server heads are in front of it it just needs to know if I get a request and how do I handle it Docker won't tell your application how to do that but it makes it much easier to test when you add that distribution in I still haven't come up with a good phrase to replace the normal one but your containers are cattle not pets you should be able to get them quickly and call them quickly and that's not the most vegetarian way of doing it if someone else has a much better way of phrasing that let me know but generally like if you're going to Google stuff treat them as cattle not as pets they should start very quick and they should gracefully shut down I didn't say they should quickly shut down they should gracefully shut down Docker because it starts a single instance most of our applications start pretty instantly Nginx and Apache have almost no boot time like they don't do a lot of logic to get set up PHP just kind of starts and waits for a request databases sometimes take a little bit longer but Docker will start a container pretty quickly there's almost no difference between running a native process and running a container process what you really want to start getting in the habit of handling and this is more of a coding issue than a workflow issue is how do things happen when they shut down so what Docker will actually do is it will send a SIG term to a container not a kill signal so that your application can handle that so you need to get if you have like worker processes think about how do they actually shut down you might want to do something graceful like finish the current work unit and don't accept anymore Cal Evans has a really good book called Signaling PHP if you've never worked with POSIX signals before it's like a $5 ebook I would recommend picking that up just so you can get in the habit of working with those things because what will happen is Docker will send a SIG term and if it doesn't get a proper response or it's not timed properly like you don't tell Docker to wait longer it will just send a SIG kill and just kill your application right there for most PHP applications it's probably not a big deal it really gets to when you're thinking about long running processes and daemons like if you're running React or workers or things like that we kind of get this out of the box Dev and production parity use the same image from top to bottom and that's kind of the whole point of Docker is being able to use that same image but in your development process use the exact same image that you build in production when you pull it down to your machine you can do things like host mount your volumes so that way your code that you're live editing on your computer gets injected in but you're still pulling down that same image you're using in production work with your team to make sure that your Docker compose files which should be in your repository and should be being version controlled are being properly pulled down and you're all using that same kind of static build yes the production image is probably fairly large but you're probably not you shouldn't really probably be changing that terribly often so you want to keep that as similar as possible that cuts down on a lot of those problems and then you're kind of left with where like I ran a composer update by example for example and that a new library has broken something or like weird little machine specific problems anything that you log treat as an event stream you never want to get in the habit of relying on local logs because they will not exist in production as soon as you remove a container it gets pulled out of rotation by default the logs disappear so on my machine if I do a Docker RM all those logs disappear I have no way of getting any of those back the nice thing is Docker has various logging methodologies built into it the default is JSON so if you do Docker logs it's actually just reading a JSON file and spitting it out depending on what you're doing that may work you can tail it just like running the tail command to not see everything or to see it live updates but it's not really sustainable especially if you've got multiple containers that can correlate events so these should be all the main options that you can do out of the box with no special like additional plugins what I would probably suggest especially starting out is using the Fluent D unfortunately there's no Log Stash 1 but Fluent D is Log Stash compliant because then you can get in the habit of throwing those logs out to a third party service or at least another container and get in the habit of it Journal D and Syslog are really good because if you have an ops team they're probably already remotely shoving those logs away anyway and they probably have a system for handling that so don't go to Fluent D if production is already using something else work within whatever your constraints are one you'll get the added benefit of everything is logged but two they're much more searchable because like I said if a container dies and the system calls it they're gone you don't get those logs back you also don't want to get in the habit of like what we used to do 20, 30 years ago or last year like for the most part for me going onto a server and looking at the Apache logs it's not sustainable you won't have access to those clusters now you can host this yourself or you can pay for a SaaS if you're going to go and do this to push your logs the Elkstack is one that many people are familiar with you just can't use Log Stash and replace it with Fluent D I'm going to assume most of you cannot read these examples I'm going to read through them I'll post these slides so you can get them later but suffice to say you're going to have a new section called logging in your compose file you can tell it use Fluent D and then basically point to whatever the local host address that you're going to have so in this case I'm going to point it to local host and I'm going to give it a tag called Apache.access then you can add a Fluent D service to your Docker compose file so you can put this in your dev config but you can run Fluent D inside Docker there's no reason you can't do that so you'll push your logs from one Docker container to another and you really only have to pass in a config file and open up the ports there is one thing you have to do Fluent D does not have an elastic search plugin by default you have to install it so you'll probably have a Docker file for that just to get that additional dependency and we'll see of course I closed that window I'll change this so that it actually links to it I didn't mean to close my Chrome window but there's some config files you can set up in Fluent D to push it off to elastic search and then you can run elastic search in a container so you can keep all of your elastic search in Kibana which is the visualization part you can keep all the stuff in your Docker compose file scale them up when you need them and shut them down when you don't and then this can be part of a dev Docker compose file and you never have to have it push to production production can push to a fully backed up compliant SAS setup you've never used Kibana before has a nice web interface horrible query structure but so down here below that little graph you can see actually the actual log file that was dumped out and you can search to this a lot nicer and correlate logs from multiple containers especially if you've got 5 or 6 FPM containers running you don't know which container was actually talked to so you can deal with that Docker logs command though will stop working if you move it off the default JSON driver so if you switch to using Fluent D and you run Docker logs you'll get no logs I think unless you're paying for Docker Enterprise but I honestly don't know anybody who pays for that the example can be cleaned up a bit there's some port things with that so when you grab the slides just keep note it's very much tailored for a slide format than like a true production format and you want to get in the habit of containerizing all of the scripts that you need to run as commands Docker will actually allow you to containerize your entire tooling structure to run the actual applications that you're running so in this case we can have a test runner service and if you specify a command you can actually tell it to by default run a command so I can do Docker compose run test runner and it will run my PHP unit test for me if I have database migrations that I need to run especially in a production environment you might containerize that I hate having stuff installed on my laptop especially multiple versions primary I'm a Linux developer I do not understand how homebrew works with multiple versions of PHP so I will use Docker to have multiple versions of PHP installed and I can swap those out based on the build parameter to different Docker files and then run PHP unit under different versions of PHP I can run all my npm stuff under the various versions because sometimes you get version locked and then you take all of your commands throw them in as services and then you can Docker compose run them just like a regular command now your tooling actually moves from machine to machine and developer to developer further reducing the differences between each person's machine couple other tidbits couple minutes left you can stack compose files to build up a specific configuration so Docker compose allows you to specify multiple files with the dash f flag so you can have a base Docker compose.yaml file which just kind of skeletoned out your services and then you can have a production compose which adds what your external ports are what extra configuration variables you need and then even a dev one which might have different port bindings and does all your host mounting and things like that and you can just stack those compose files on top of each other so in this case we might have a Docker compose file which by default says hey use the nginx docker file openport80 and 443 here's our php stuff and then mysql really you'll notice we're not pushing any code into here if you were to live edit through php storm or visual studio code the stuff you edit on your desktop we're not doing any host mounting or anything like that we can push that off to a dev file which does add our volumes which allow us to do our host mounting and stuff like that so we can start to actually get that uh ability to say the configuration I'm using in production I can also replicate down to my development in a true way but also have an easy way to override for my development stack one thing I'll do a lot is I will override the build to a debug build which has xdebug and stuff like that so I won't ship xdebug to production I'll switch my docker file to point to one that has xdebug installed and I'll do that through my dev docker compose and set up all my xdebug stuff as separate environment variables then to use it you just stack the dash f it does go left to right and override so first it'll take docker compose and then it will layer dev on top of it one other kind of little gotcha is that it for anything that's an array like ports or volumes it does not replace it appends so technically in this example if I'm opening port 80 and 443 if I'm opening the production one those will also be open and I don't open like port 8080 on here if I had another port in here we would actually have all three of them open not it won't replace so that's one kind of weird thing to keep in mind and then when you're doing a deployment you just do like a docker compose up or whatever you're doing use the production one it will replace that so if you put any of your values in dollar sign curly bracket it will replace that from a local environment configuration so you might do something like image is deploy version underscore PHP it's whatever I last built so then doing deployment you can say deploy version equals 2018 05 25 docker compose up dash d and then it will actually inject that into that variable this allows you to especially during a build process use a very generic compose file