An Intro to Docker for Djangonauts
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Henschel, Lacey Williams
DjangoCon US 2018 - An Intro to Docker for Djangonauts by Lacey Williams Henschel If you’ve never used it before, Docker can be a little overwhelming. There is new vocabulary to learn, new commands to memorize, and new files to add to each project. Most resources fall into one of two camps: they teach you the bare bones of Docker but still leave you with too many questions to comfortably try it on your own, or they throw you into the deep end by assuming you’re more familiar with Docker than you are. In this talk, you will find that middle ground: a talk that doesn’t assume you’re familiar with Docker and so keeps everything simple, but leaves you with enough information that you can get started as soon as you leave the room. Together, we will step through the parts of a Dockerfile and learn what they do. Then, I’ll introduce you to Docker Compose and explain why using it to run multiple containers is helpful. Finally, you will learn to run commands and execute scripts from the command line using Docker, how to enter a container and why you might need to, and what to do before you go home for the day. Audience: Djangonauts who are curious about Docker, need to learn to use it for work or personal projects, or who seek a better understanding of containers in general. Audience members might be beginners to Python/Django or not, but they are definitely beginners to containerizing all the things. The audience should be familiar with basic Python concepts such as variables and running things from the command line. Some basic familiarity with Django would be helpful, but beginner Djangonauts will be able to follow along.
 Hi, everyone. Is the mic positioned okay? Awesome. Thank you. All right. Thank you so much for coming to my talk. My name is Lacey, and we are here to talk about Docker. And we're going to do so with some Harry Potter metaphors. I do promise that if you are not a huge Harry Potter fan, the metaphors are not such that you won't be able to follow along with the talk. If you are a Harry Potter fan, hopefully these metaphors will help elucidate things for you and delight you in the process. I work for a company called RevSys, and that's kind of why I'm giving this talk. Whenever I started with RevSys a year ago, I'd never used Docker before, and I found it very, very confusing. It wasn't like anything else I'd ever used. So that's kind of why I'm here. I wanted to help demystify this for you because it was very mysterious for me. So one of the things that was confusing for me is what really is Docker? We're kind of used to working in virtual environments. Some of us work in virtual machines, but Docker asks us to understand these concepts in a very, very different way, and that's a really large learning curve for a lot of people. In short, it feels kind of like magic, and most of us don't really like things that are too magical. So then we can't understand them, they're difficult to debug, and it's harder for us to do our jobs. So some of the things that I really love about Docker is that it helps you separate your dependencies, which is very handy. It shares your operating system, so it's pretty lightweight. It puts all of your team members on the same page when it comes to your development environment. So I've been in situations several times before where a co-worker was helping me debug something, but my environment had a weird setting that was the problem. If any of you have ever coached at a Django Girls workshop on installation night, we see a lot of these issues as well. With Docker, you don't have those, everyone's doing the exact same thing. And you don't need to install any Python at all. You can skip having a system Python entirely. So if you have a job where you work on multiple projects or you're an open source maintainer and you have multiple projects, keeping track of all these different Python installations can be a little bit challenging, and with Docker, you can just skip that step. However, as said before, learning all of this is kind of a learning curve. So this talk really is designed for people who have had very little to no experience with Docker. It's going to be pretty basic, pretty low level, but the hope is that whenever you walk away from this talk, you'll be able to fork a project that I'll give you, play around with it, get Docker running on your own machine, and learn to love it as much as I do. But I do want to talk first about the place of virtual environments in a Docker setup. I'm not telling you to totally ditch virtual environments in favor of Docker. I still use virtual environments a lot, mostly because I'm really lazy and I like the work on command so I can switch between projects more efficiently. But with a virtual environment, you still do have to have a system Python installed, which can get a little hairy depending on the number of projects that you work on. So we're going to start out by talking about Polyjuice Potion. Polyjuice Potion in the Harry Potter books is this potion that you use to turn into someone else. You put a piece of that person's hair into this potion and then you turn into that person. This is a lot how Docker images and containers work. So we're going to use that analogy. In this analogy, Docker is the potion itself. It's the thing that you've brewed. It's the vessel through which everything else happens. When you start a project in Docker, you first start out by defining an image. The image is like that person's hair that you want to turn into. It defines all of the basic software for your project. A more official explanation is that it's the executable that contains all the packages that your project needs. The container is you. Docker also has this concept called a container. A container is a copy of your image that's running around actually running your project. So whenever you drink the Polyjuice Potion, you've had this potion that had someone's hair in it, you turn into that person and you're a copy of them running around and causing mischief and investigating. So just to review this vocabulary, we've learned about the concept of an image and the concept of a container. The image is the executable and the container is the runtime instance of that image. When you're using Docker, you'll also become familiar with a new file. There's a file that you'll use called a Docker file and it lives in the same place in your code as your manage.py file. And that defines all of the specifics about the image that you want to use. So in that way, it's the DNA of your project. It is the hair. It tells you or it lets you tell Docker what version of Python that you want to use, what environment variables you'll need, it installs things for you. So now we have this new vocabulary word that I'll just keep on the slide for a moment. So restated, you use this hair to turn into someone else and then you drink that potion to transform the way that you look. You use the Docker file to tell Docker how to build your image and then you run your project in the container. So that's the analogy more explicitly if that helps you. So this is the basic layout of a Docker file and we're going to go through this line by line because I don't like magic and a lot of tutorials will tell you just to copy and paste and that keeps you from understanding things more fully. So the first line specifies the image that you want your image to be based on. You don't have to start your image from scratch. You can give your image a parent. In this case, we're going to base our image off of the Python image. In the Docker hub, there are all kinds of images published by all kinds of different organizations and people and Python has put their images up there tagged with the specific version so you can use the one you need. ENV Python Unbuffered 1 creates a variable called Python Unbuffered and setting it to one makes it truthy because one is truthy, zero is falsey and this means that you'll be able to see your output in the console whenever you're running your project. Python don't write byte code, keeps Docker from writing PYC files which helps keep your Docker image nice and tidy. Now your container will have its own directory structure so you'll have to copy your requirements file from your machine into your container and this line will do that for you but it puts it in a special directory called code. Since your container has its own file system, you don't want to just dump all of your files directly in there. You want to put them in their own place so that they're easier for you to find if you need to hop into your container and look around. Then you install your requirements with a command that you're pretty used to, you just have to prefix it with run and you copy the rest of your files into that code directory. This handy line sets the working directory of your container as code so whenever you're in the container you can run the commands like manage.py create super user without having to prefix it with code to make sure that you're in the right directory. Then you have to expose your containers port 8000 so that you can use your browser locally. And then you have to run your server so you type out this command to tell Docker to go ahead and run manage.py run server the way that you're used to. So Docker will do this for you. So now we're actually ready to start using our potion. We're going to learn some things here. So you run Docker build to build your image so that you have this thing that you can base your container off of. And when you run Docker build you will see a whole lot of output and so Docker is executing all of the steps that you defined in your Docker file and at the bottom it will say successfully built and then this kind of nonsense string. That nonsense string is the name of your image in this case ADFBC, et cetera. But that's not super intuitive for most people. So Docker allows us to give our images a specific name so we can name this image and get to it more easily which is really helpful. But then you see that Docker will also go ahead and tag our image once we name it. So we've seen those tags before. We referred to the 3.6 tag of the Python image. If you don't provide a specific tag Docker will just tag it latest. But tagging things is the way that you can manage specific releases of your image the way that Python does with their versions. Now I mentioned that Docker builds your image with particular steps. Docker is layered and that's a really important concept to understand. Every line in your Docker file is its own layer in your image. And you can see this whenever Docker is building your image. You've got step one which will build step two and it will go through these one at a time so it's very explicit about what it's doing. What this means is that the very first time that you build your image Docker will build everything by hand. It will make all of those layers from scratch. The second time through whenever you're building your image if you haven't changed anything Docker doesn't care about building it from scratch for you. It will go ahead and use a cache so that it doesn't have to do all of that work over again. But if you change a line in your Docker file Docker will rebuild that particular layer and all of the layers that come after that. So what that means is that say we add something new to our requirements file whenever Docker is building before it gets to that step it will use a cache and it won't try to execute those steps from scratch. But once it gets to installing the requirements it knows that something has changed and it will download all of those requirements again fresh and then it will build steps five, six, seven, eight from scratch as well. So now we're finished talking about Polyjuice Potion and we're going to talk about some charms some spells. So this is all about how you interact with your containers whenever you're actually running your project and development. You run the Docker images command to see the images that you have so we can see our Hogwarts image and its original image ID. We can also reveal our containers by running Docker container LS. This will show us any containers that we currently have running and we can see right now that we don't have any running because we've built our image but we haven't done anything with it. So in order to do something with that image we run Docker run. Docker run dash P8000 colon 8000 Hogwarts will execute everything that we had in that image. It will actually start making a copy of the container that will exist for us. So if we run that show us our containers command again then we will see all of these or not all of these there's only one right now. We will see this container that we've created so we can see the images based on the command that it's running and some other metadata about this particular container. We can also hop into our container specifically using the Docker exec command. So exacting into your container is Docker exact dash IT and the ID of your container and you can get the ID of your container from Docker container LS and then shell. And once you're there you can see that you're at the root and you can also see that you're right there in your code directory because you specified that with the work order command in your Docker file. So whenever you reveal the contents of this directory you'll see your Docker file, your manage.py file so you can see that your code was copied over the way that you told it to in your Docker file. If you go up a level then you'll be able to see that you have that code directory, it's the second one there in the third line but the container also has its own set of directories. It's doing its own thing. Most of the time you won't really need to worry about that but I want you to know about it because sometimes you will need to hop into your container to poke around and debug some things. Hop out of your container like with control D. And then if we run Docker container LS again even though we've hopped into our container to look around and now we've hopped out we can see that nothing changed with our container. We didn't stop it, we didn't start it, we didn't touch it, we just kind of poked at it and nothing changed. If we wanted to stop our container that's a pretty intuitive command. Docker stop and the ID of our container. And then Docker start to restart the container. Whenever you stop a container the container still exists. It just sort of pauses for a moment and then Docker start will restart that particular container so you're not creating a fresh copy. Docker kill however will trash your container entirely and kill it with fire and you will never see it again. Something to keep in mind about Docker kill versus Docker stop is that with Docker stop you still have all of your data. So if you've created test data, you've created a super user running Docker stop and then Docker start you'll still have access to all of that. Docker kill also kills your data but I'll tell you a little bit later how you can get around that but that's a pretty important thing to know. So one of my favorite features of Docker that makes it so pleasant for Django development is Docker compose. So this particular command, this Docker run dash p8000 colon 8000 Hogwarts is kind of a lot to type. It's not super flexible, it's not very intuitive and you might be thinking like why would I type this whole command whenever I could just type manage.py run server and my life would continue to be simple. And this is where Docker compose comes in to help you out with that. So Docker compose is this sort of add on to Docker, it's free. If you're on a Mac then it's included with Docker, if you're on another system it's a separate download. And the magical thing that it lets you do is run more than one container at once. So you can have separate containers for your database, for your web server, for other things that your code might be doing and you can also relate those containers to each other which can be very handy and I'll go through that in a moment too. So just to put a Harry Potter metaphor in here because I have to, Docker compose is kind of like Hermione's magical bag. The character in Harry Potter has this bag that expands to fit anything that she possibly needs. She puts a tent in there and everything. Docker compose is like that. It lets you create just a laundry list of all of the things that your project is going to need and have separate containers for them. So this is an example of a Docker compose file. Whenever you're using compose you'll have a Docker file the way that we talked about earlier but you'll also have a separate file called Docker compose.yaml. You start out with the version number three is the most current version of compose so that's the one that we're using. And then you define your services. Your services are the containers that you want to start whenever you start your project with Docker compose. For the purposes of this example we are using a database container and also a web container to separate our data from our code. Now one of the other things I really love about Docker is that just like I don't have to have a local Python installation I don't have to have a local Postgres installation either. I can tell Docker to go out grab the most recent Postgres image or a specific tag if I want to use that and then it will all just work. I don't have to configure anything locally other than changing my settings.py file. So that's a really, really huge plus for me. I do have a sample project that I'll link to at the end that shows the changes that you need to make with your settings.py file but just know that you are changing your database back end whenever you do that in compose so you have to do the related things that you have to do. Now we jump into our web service and this allows us to do a lot of things. We have a lot of options here. For example we can run more than one command so if we want to we can run migrations every time we start our project and also run our server so we can skip a step there. And compose also introduces this concept of a volume. So I told you earlier that whenever you kill your container you kill all of the data that's associated with your project like your super user or any test records that you created. If you use a volume then that's not true anymore. You can stick your data in a special volume that will persist between containers so you kill your container and then you restart a new one but you still have all of that test data that you created. You can map your containers port 8000 to your own local machines port 8000 so you skip that step as well from your Docker run command that we were going through earlier. And then we can relate the web container to the database container so we can say that our web service depends on our database service. We really need those to work together in order for the project to work so Docker will start the database container first and then start the web container second. Now what this means is that the last line in our Docker file that was running one server we don't need that anymore so we can kill it with fire and we can move on. The way that you use compose instead of running Docker run you just run Docker compose up and it will take care of both building your image and also starting your project. If you've already built that image once before just like Docker build will do it won't rebuild it if nothing has changed it will use that cache but it will also go ahead and rebuild things if something changes as well. If you want to make absolutely sure that your image gets rebuilt just pass the build flag. Once you run Docker compose up you'll see a lot of output and you'll see some things that confirm what I've talked about already. So you see that the database service starts first, you see the web service and then you see that they get attached. Docker relates them to one another. We can also see that our migrations were performed and that our server started so we're ready to go. And we can see that we do now have two containers instead of run whenever we run Docker container LS. So we see that we have our Postgres image and our web image and we can also see that they have much better names now. They still have those container IDs but they also automatically get names that are based on the name of our project and also the name of the service which is a lot easier to handle. And we can run regular commands like we're used to from the command line so if you wanted to run make migrations you have to prefix things with Docker compose run and the name of the service that you want to touch but then you can run everything the way that you're used to. This is a lot to type but you can also map these as shortcuts so you can create a shortcut phrase that will do all of this for you and then you can just run the manage.py and make migrations after that. And then that RM flag means trash this container whenever I'm done with it. So whenever you run something like this and you've already run Docker compose up Docker is going to start a second web container for you to run these migrations. All it's doing is running those migrations and you already have your container that's running your server so you don't need the migrations container anymore. So at passing the RM flag we'll go ahead and remove that container so that you don't clutter up your system with that. You can hop into the shell the way that you're used to in the same way which is pretty handy. So basically anything that you're used to doing with a Django project from the command line you can still do with Docker as long as you prefix it with Docker compose run the name of your service and then you just proceed as normal. You can stop and start your Docker containers at will so you can stop specifically the web container, restart it specifically. This is very handy if you're using something like Celery you can have a specific Celery container and if you're not working on the Celery portion of your project you can stop only the Celery container so that you can move on with your life and you don't get all of the output from you know, thinking something every five minutes or reaching out to an API every two minutes or something like that. At the end of the day when you're ready to go home you run Docker compose down and Docker will shut down your containers and trash them for you. And then if you want to check out a project and kind of play with this yourself this will link you to a GitHub repository. I highly encourage you to fork that repository and play around with it and get to know it a little bit. If you have any questions please feel free to ask me about those or file those as issues. And I want to leave you with this quote from Albus Dumbledore. It is the unknown we fear when we look upon Docker nothing more. I do genuinely hope that this talk helped demystify Docker for you a little bit. I really love it but that was a journey for me to get to loving it so I understand if you've been a little bit frustrated with Docker in the past. If that's been you I would really love to talk with you about that and help make this make a little bit more sense to you. I will tweet all of these out and I'll also post my slides but there are a lot of resources that are pretty friendly to learn about using Docker. Some of these go specifically into the Docker parts of things. Some of these are more the kind of Django side of things but hopefully they'll help coalesce things for you. And again I'll tweet these out. I'll also post my slides so you'll be able to link directly to these. That's all I have for today. So thank you very much. Thank you. Thank you for the talk. It is demystifying. Yay. Do you have any advice on how to use the debugger while doing Django development in Docker? Freak points and that sort of thing. So I usually use like PyTest set trace whenever I'm debugging. I'm really bad about using the built-in Python debugger. But my experience is whenever I run Docker compose, run, RM, web, PyTest for example, it will go ahead and stop at the set trace and allow me to poke around the way that I'm used to. So it doesn't really change things. I would assume that using the regular Python debugger would be similar. But I would like to sit down with you and kind of step through that and see if that's true. Hi. Thank you very much for that talk. So container data, files that you generated in the container, how would you get that out to your system? And vice versa, if you have files in your system that you need for the container, how would you get that in? So if you have like a CSV file, for example, that you need, then you just put that in your project directory the way that you normally would. And then you have access to it that way. If your system generates files, then my experience is that they show up in your project directory and that still exists locally. Like they won't be just in the container. They'll also be in your local file system. Thank you. Quick question about pulling dependencies that maybe aren't in PyPy or like in a local mirror, you need SSH authentication. How do you do that in your Docker build without hard coding secrets in the Docker file? So there is a way to use Docker secrets to kind of manage that. It's a little bit, I can't really like sum it up in 30 seconds, but there is a way to do that. And I see Steven here. I will also be able to talk to you about that. But yeah, so it involves setting up some Docker secrets, which is sort of its own talk, but there is a way to do that. And then if you're comfortable kind of hard coding things, then we have a, RevSys has a generator where you can put in the repository and it'll sort of spit out like for GitLab versus GitHub, et cetera, what you need. But if you obviously don't really want to hard code those tokens, so Docker secrets would be the way to go with that. All right. Any more? Would you like to take, I think we're probably out of time. All right. Well, thank you so much. Give Lacey a hand. Come on.
 Hi, everyone.  Is the mic positioned okay?  Awesome.  Thank you.  All right.  Thank you so much for coming to my talk.  My name is Lacey, and we are here to talk about Docker.  And we're going to do so with some Harry Potter metaphors.  I do promise that if you are not a huge Harry Potter fan, the metaphors are not such that  you won't be able to follow along with the talk.  If you are a Harry Potter fan, hopefully these metaphors will help elucidate things for you  and delight you in the process.  I work for a company called RevSys, and that's kind of why I'm giving this talk.  Whenever I started with RevSys a year ago, I'd never used Docker before, and I found it  very, very confusing.  It wasn't like anything else I'd ever used.  So that's kind of why I'm here.  I wanted to help demystify this for you because it was very mysterious for me.  So one of the things that was confusing for me is what really is Docker?  We're kind of used to working in virtual environments.  Some of us work in virtual machines, but Docker asks us to understand these concepts in a very,  very different way, and that's a really large learning curve for a lot of people.  In short, it feels kind of like magic, and most of us don't really like things that are  too magical.  So then we can't understand them, they're difficult to debug, and it's harder for us  to do our jobs.  So some of the things that I really love about Docker is that it helps you separate your dependencies,  which is very handy.  It shares your operating system, so it's pretty lightweight.  It puts all of your team members on the same page when it comes to your development environment.  So I've been in situations several times before where a co-worker was helping me debug something,  but my environment had a weird setting that was the problem.  If any of you have ever coached at a Django Girls workshop on installation night, we see  a lot of these issues as well.  With Docker, you don't have those, everyone's doing the exact same thing.  And you don't need to install any Python at all.  You can skip having a system Python entirely.  So if you have a job where you work on multiple projects or you're an open source maintainer  and you have multiple projects, keeping track of all these different Python installations  can be a little bit challenging, and with Docker, you can just skip that step.  However, as said before, learning all of this is kind of a learning curve.  So this talk really is designed for people who have had very little to no experience  with Docker.  It's going to be pretty basic, pretty low level, but the hope is that whenever you walk  away from this talk, you'll be able to fork a project that I'll give you, play around  with it, get Docker running on your own machine, and learn to love it as much as I do.  But I do want to talk first about the place of virtual environments in a Docker setup.  I'm not telling you to totally ditch virtual environments in favor of Docker.  I still use virtual environments a lot, mostly because I'm really lazy and I like the work  on command so I can switch between projects more efficiently.  But with a virtual environment, you still do have to have a system Python installed,  which can get a little hairy depending on the number of projects that you work on.  So we're going to start out by talking about Polyjuice Potion.  Polyjuice Potion in the Harry Potter books is this potion that you use to turn into someone  else.  You put a piece of that person's hair into this potion and then you turn into that person.  This is a lot how Docker images and containers work.  So we're going to use that analogy.  In this analogy, Docker is the potion itself.  It's the thing that you've brewed.  It's the vessel through which everything else happens.  When you start a project in Docker, you first start out by defining an image.  The image is like that person's hair that you want to turn into.  It defines all of the basic software for your project.  A more official explanation is that it's the executable that contains all the packages  that your project needs.  The container is you.  Docker also has this concept called a container.  A container is a copy of your image that's running around actually running your project.  So whenever you drink the Polyjuice Potion, you've had this potion that had someone's  hair in it, you turn into that person and you're a copy of them running around and causing  mischief and investigating.  So just to review this vocabulary, we've learned about the concept of an image and the concept  of a container.  The image is the executable and the container is the runtime instance of that image.  When you're using Docker, you'll also become familiar with a new file.  There's a file that you'll use called a Docker file and it lives in the same place in your  code as your manage.py file.  And that defines all of the specifics about the image that you want to use.  So in that way, it's the DNA of your project.  It is the hair.  It tells you or it lets you tell Docker what version of Python that you want to use, what  environment variables you'll need, it installs things for you.  So now we have this new vocabulary word that I'll just keep on the slide for a moment.  So restated, you use this hair to turn into someone else and then you drink that potion  to transform the way that you look.  You use the Docker file to tell Docker how to build your image and then you run your  project in the container.  So that's the analogy more explicitly if that helps you.  So this is the basic layout of a Docker file and we're going to go through this line by  line because I don't like magic and a lot of tutorials will tell you just to copy and  paste and that keeps you from understanding things more fully.  So the first line specifies the image that you want your image to be based on.  You don't have to start your image from scratch.  You can give your image a parent.  In this case, we're going to base our image off of the Python image.  In the Docker hub, there are all kinds of images published by all kinds of different  organizations and people and Python has put their images up there tagged with the specific  version so you can use the one you need.  ENV Python Unbuffered 1 creates a variable called Python Unbuffered and setting it to  one makes it truthy because one is truthy, zero is falsey and this means that you'll  be able to see your output in the console whenever you're running your project.  Python don't write byte code, keeps Docker from writing PYC files which helps keep your  Docker image nice and tidy.  Now your container will have its own directory structure so you'll have to copy your requirements  file from your machine into your container and this line will do that for you but it  puts it in a special directory called code.  Since your container has its own file system, you don't want to just dump all of your files  directly in there.  You want to put them in their own place so that they're easier for you to find if you  need to hop into your container and look around.  Then you install your requirements with a command that you're pretty used to, you just  have to prefix it with run and you copy the rest of your files into that code directory.  This handy line sets the working directory of your container as code so whenever you're  in the container you can run the commands like manage.py create super user without having  to prefix it with code to make sure that you're in the right directory.  Then you have to expose your containers port 8000 so that you can use your browser locally.  And then you have to run your server so you type out this command to tell Docker to go  ahead and run manage.py run server the way that you're used to.  So Docker will do this for you.  So now we're actually ready to start using our potion.  We're going to learn some things here.  So you run Docker build to build your image so that you have this thing that you can base  your container off of.  And when you run Docker build you will see a whole lot of output and so Docker is executing  all of the steps that you defined in your Docker file and at the bottom it will say successfully  built and then this kind of nonsense string.  That nonsense string is the name of your image in this case ADFBC, et cetera.  But that's not super intuitive for most people.  So Docker allows us to give our images a specific name so we can name this image and get to  it more easily which is really helpful.  But then you see that Docker will also go ahead and tag our image once we name it.  So we've seen those tags before.  We referred to the 3.6 tag of the Python image.  If you don't provide a specific tag Docker will just tag it latest.  But tagging things is the way that you can manage specific releases of your image the  way that Python does with their versions.  Now I mentioned that Docker builds your image with particular steps.  Docker is layered and that's a really important concept to understand.  Every line in your Docker file is its own layer in your image.  And you can see this whenever Docker is building your image.  You've got step one which will build step two and it will go through these one at a time  so it's very explicit about what it's doing.  What this means is that the very first time that you build your image Docker will build  everything by hand.  It will make all of those layers from scratch.  The second time through whenever you're building your image if you haven't changed anything  Docker doesn't care about building it from scratch for you.  It will go ahead and use a cache so that it doesn't have to do all of that work over again.  But if you change a line in your Docker file Docker will rebuild that particular layer and  all of the layers that come after that.  So what that means is that say we add something new to our requirements file whenever Docker  is building before it gets to that step it will use a cache and it won't try to execute  those steps from scratch.  But once it gets to installing the requirements it knows that something has changed and it  will download all of those requirements again fresh and then it will build steps five, six,  seven, eight from scratch as well.  So now we're finished talking about Polyjuice Potion and we're going to talk about some  charms some spells.  So this is all about how you interact with your containers whenever you're actually running  your project and development.  You run the Docker images command to see the images that you have so we can see our Hogwarts  image and its original image ID.  We can also reveal our containers by running Docker container LS.  This will show us any containers that we currently have running and we can see right now that  we don't have any running because we've built our image but we haven't done anything with  it.  So in order to do something with that image we run Docker run.  Docker run dash P8000 colon 8000 Hogwarts will execute everything that we had in that image.  It will actually start making a copy of the container that will exist for us.  So if we run that show us our containers command again then we will see all of these or not  all of these there's only one right now.  We will see this container that we've created so we can see the images based on the command  that it's running and some other metadata about this particular container.  We can also hop into our container specifically using the Docker exec command.  So exacting into your container is Docker exact dash IT and the ID of your container  and you can get the ID of your container from Docker container LS and then shell.  And once you're there you can see that you're at the root and you can also see that you're  right there in your code directory because you specified that with the work order command  in your Docker file.  So whenever you reveal the contents of this directory you'll see your Docker file, your  manage.py file so you can see that your code was copied over the way that you told it to  in your Docker file.  If you go up a level then you'll be able to see that you have that code directory, it's  the second one there in the third line but the container also has its own set of directories.  It's doing its own thing.  Most of the time you won't really need to worry about that but I want you to know about  it because sometimes you will need to hop into your container to poke around and debug  some things.  Hop out of your container like with control D. And then if we run Docker container LS  again even though we've hopped into our container to look around and now we've hopped out we  can see that nothing changed with our container.  We didn't stop it, we didn't start it, we didn't touch it, we just kind of poked at  it and nothing changed.  If we wanted to stop our container that's a pretty intuitive command.  Docker stop and the ID of our container.  And then Docker start to restart the container.  Whenever you stop a container the container still exists.  It just sort of pauses for a moment and then Docker start will restart that particular  container so you're not creating a fresh copy.  Docker kill however will trash your container entirely and kill it with fire and you will  never see it again.  Something to keep in mind about Docker kill versus Docker stop is that with Docker stop  you still have all of your data.  So if you've created test data, you've created a super user running Docker stop and then  Docker start you'll still have access to all of that.  Docker kill also kills your data but I'll tell you a little bit later how you can get around  that but that's a pretty important thing to know.  So one of my favorite features of Docker that makes it so pleasant for Django development  is Docker compose.  So this particular command, this Docker run dash p8000 colon 8000 Hogwarts is kind of  a lot to type.  It's not super flexible, it's not very intuitive and you might be thinking like why would I  type this whole command whenever I could just type manage.py run server and my life would  continue to be simple.  And this is where Docker compose comes in to help you out with that.  So Docker compose is this sort of add on to Docker, it's free.  If you're on a Mac then it's included with Docker, if you're on another system it's a  separate download.  And the magical thing that it lets you do is run more than one container at once.  So you can have separate containers for your database, for your web server, for other things  that your code might be doing and you can also relate those containers to each other  which can be very handy and I'll go through that in a moment too.  So just to put a Harry Potter metaphor in here because I have to, Docker compose is  kind of like Hermione's magical bag.  The character in Harry Potter has this bag that expands to fit anything that she possibly  needs.  She puts a tent in there and everything.  Docker compose is like that.  It lets you create just a laundry list of all of the things that your project is going  to need and have separate containers for them.  So this is an example of a Docker compose file.  Whenever you're using compose you'll have a Docker file the way that we talked about  earlier but you'll also have a separate file called Docker compose.yaml.  You start out with the version number three is the most current version of compose so  that's the one that we're using.  And then you define your services.  Your services are the containers that you want to start whenever you start your project  with Docker compose.  For the purposes of this example we are using a database container and also a web container  to separate our data from our code.  Now one of the other things I really love about Docker is that just like I don't have  to have a local Python installation I don't have to have a local Postgres installation  either.  I can tell Docker to go out grab the most recent Postgres image or a specific tag if I want  to use that and then it will all just work.  I don't have to configure anything locally other than changing my settings.py file.  So that's a really, really huge plus for me.  I do have a sample project that I'll link to at the end that shows the changes that  you need to make with your settings.py file but just know that you are changing your database  back end whenever you do that in compose so you have to do the related things that you  have to do.  Now we jump into our web service and this allows us to do a lot of things.  We have a lot of options here.  For example we can run more than one command so if we want to we can run migrations every  time we start our project and also run our server so we can skip a step there.  And compose also introduces this concept of a volume.  So I told you earlier that whenever you kill your container you kill all of the data that's  associated with your project like your super user or any test records that you created.  If you use a volume then that's not true anymore.  You can stick your data in a special volume that will persist between containers so you  kill your container and then you restart a new one but you still have all of that test  data that you created.  You can map your containers port 8000 to your own local machines port 8000 so you skip that  step as well from your Docker run command that we were going through earlier.  And then we can relate the web container to the database container so we can say that  our web service depends on our database service.  We really need those to work together in order for the project to work so Docker will start  the database container first and then start the web container second.  Now what this means is that the last line in our Docker file that was running one server  we don't need that anymore so we can kill it with fire and we can move on.  The way that you use compose instead of running Docker run you just run Docker compose up and  it will take care of both building your image and also starting your project.  If you've already built that image once before just like Docker build will do it won't rebuild  it if nothing has changed it will use that cache but it will also go ahead and rebuild  things if something changes as well.  If you want to make absolutely sure that your image gets rebuilt just pass the build flag.  Once you run Docker compose up you'll see a lot of output and you'll see some things  that confirm what I've talked about already.  So you see that the database service starts first, you see the web service and then you  see that they get attached.  Docker relates them to one another.  We can also see that our migrations were performed and that our server started so we're  ready to go.  And we can see that we do now have two containers instead of run whenever we run Docker container  LS.  So we see that we have our Postgres image and our web image and we can also see that they  have much better names now.  They still have those container IDs but they also automatically get names that are based  on the name of our project and also the name of the service which is a lot easier to handle.  And we can run regular commands like we're used to from the command line so if you wanted  to run make migrations you have to prefix things with Docker compose run and the name  of the service that you want to touch but then you can run everything the way that you're  used to.  This is a lot to type but you can also map these as shortcuts so you can create a shortcut  phrase that will do all of this for you and then you can just run the manage.py and make  migrations after that.  And then that RM flag means trash this container whenever I'm done with it.  So whenever you run something like this and you've already run Docker compose up Docker  is going to start a second web container for you to run these migrations.  All it's doing is running those migrations and you already have your container that's  running your server so you don't need the migrations container anymore.  So at passing the RM flag we'll go ahead and remove that container so that you don't clutter  up your system with that.  You can hop into the shell the way that you're used to in the same way which is pretty handy.  So basically anything that you're used to doing with a Django project from the command  line you can still do with Docker as long as you prefix it with Docker compose run the  name of your service and then you just proceed as normal.  You can stop and start your Docker containers at will so you can stop specifically the web  container, restart it specifically.  This is very handy if you're using something like Celery you can have a specific Celery  container and if you're not working on the Celery portion of your project you can stop  only the Celery container so that you can move on with your life and you don't get all  of the output from you know, thinking something every five minutes or reaching out to an API  every two minutes or something like that.  At the end of the day when you're ready to go home you run Docker compose down and Docker  will shut down your containers and trash them for you.  And then if you want to check out a project and kind of play with this yourself this will  link you to a GitHub repository.  I highly encourage you to fork that repository and play around with it and get to know it  a little bit.  If you have any questions please feel free to ask me about those or file those as issues.  And I want to leave you with this quote from Albus Dumbledore.  It is the unknown we fear when we look upon Docker nothing more.  I do genuinely hope that this talk helped demystify Docker for you a little bit.  I really love it but that was a journey for me to get to loving it so I understand if  you've been a little bit frustrated with Docker in the past.  If that's been you I would really love to talk with you about that and help make this  make a little bit more sense to you.  I will tweet all of these out and I'll also post my slides but there are a lot of resources  that are pretty friendly to learn about using Docker.  Some of these go specifically into the Docker parts of things.  Some of these are more the kind of Django side of things but hopefully they'll help  coalesce things for you.  And again I'll tweet these out.  I'll also post my slides so you'll be able to link directly to these.  That's all I have for today.  So thank you very much.  Thank you.  Thank you for the talk.  It is demystifying.  Yay.  Do you have any advice on how to use the debugger while doing Django development in Docker?  Freak points and that sort of thing.  So I usually use like PyTest set trace whenever I'm debugging.  I'm really bad about using the built-in Python debugger.  But my experience is whenever I run Docker compose, run, RM, web, PyTest for example,  it will go ahead and stop at the set trace and allow me to poke around the way that I'm  used to.  So it doesn't really change things.  I would assume that using the regular Python debugger would be similar.  But I would like to sit down with you and kind of step through that and see if that's  true.  Hi.  Thank you very much for that talk.  So container data, files that you generated in the container, how would you get that out  to your system?  And vice versa, if you have files in your system that you need for the container, how would  you get that in?  So if you have like a CSV file, for example, that you need, then you just put that in your  project directory the way that you normally would.  And then you have access to it that way.  If your system generates files, then my experience is that they show up in your project directory  and that still exists locally.  Like they won't be just in the container.  They'll also be in your local file system.  Thank you.  Quick question about pulling dependencies that maybe aren't in PyPy or like in a local mirror,  you need SSH authentication.  How do you do that in your Docker build without hard coding secrets in the Docker file?  So there is a way to use Docker secrets to kind of manage that.  It's a little bit, I can't really like sum it up in 30 seconds, but there is a way to  do that.  And I see Steven here.  I will also be able to talk to you about that.  But yeah, so it involves setting up some Docker secrets, which is sort of its own talk, but  there is a way to do that.  And then if you're comfortable kind of hard coding things, then we have a, RevSys has a  generator where you can put in the repository and it'll sort of spit out like for GitLab  versus GitHub, et cetera, what you need.  But if you obviously don't really want to hard code those tokens, so Docker secrets would  be the way to go with that.  All right.  Any more?  Would you like to take, I think we're probably out of time.  All right.  Well, thank you so much.  Give Lacey a hand.  Come on.
DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_0_335.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_336_1019.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_1020_5171.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_5172_5339.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_5340_5819.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_5820_6587.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_6588_6947.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_6948_11147.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_11148_11471.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_11472_12431.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_12432_12719.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_12720_14279.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_14280_14435.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_14436_15515.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_15516_17423.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_17424_19187.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_19188_29735.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_29736_30335.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_30336_33551.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_33552_34787.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_34788_34955.png DjangoCon_US_2018_-_An_Intro_to_Docker_for_Djangonauts_by_Lacey_Williams_Henschel_qsEfVSTZO9Q_34956_35056.png