DevOps for The Lazy
Hammerly, Aja
Like most programmers I am lazy. I don't want to do something by hand if I can automate it. I also think DevOps can be dreadfully dull. Luckily there are now tools that support lazy DevOps. I'll demonstrate how using Docker containers and Kubernetes allows you to be lazy and get back to building cool features (or watching cat videos). I'll go over some of the pros and cons to the "lazy" way and I'll show how these tools can be used by both simple and complex apps.
 So, I'm Aja Hammerly. I'm a developer advocate at Google. I tweet at ThagamizerRB, and all the code that I'm going to show in this talk is on GitHub. I'm Thagamizer on GitHub, and it's in my examples repository in the RailsConf 15 folder. And since I'm showing you code, I have to say that all the code in this talk is copyright Google and licensed under patchy v2. So, first things first. I'm not an expert at ops. I'm actually not an expert at anything. I tend toward more drac of all trades, master of none. But I've spent some time racking servers, and I have bloody knuckles. I've had bloody knuckles to prove it. And I've built things that look like this. But my skills have not gotten to the level of this yet. And when I try to do something that's all professional ops fancy, I end up building things that look a little bit like this. And why is that? Because I'm fundamentally lazy. I'm passionate about some things, solving problems, building sites, algorithms. But I'm lazy about deployment and operations and maintenance, and I find a lot of that stuff to be fiddly and not a lot of fun. And I'm here to tell you right now that that's okay for most people. And what do I mean specifically by lazy? I mean, doing the minimum that you absolutely have to to have something be successful and stable. I want to make systems that are lazy so that they'll make, I can be lazy so that they'll maintain themselves. And one way I've found to do that is with containers. So what the heck's a container? So according to several sources, a container is a way for multiple applications to securely share hardware with a minimum footprint. And frequently this means less of a footprint than using virtual machines. To me, containers can also be an environment package. It's a way of wrapping up your operating system, your build dependencies, any libraries you need, possibly your application code, all into a nice little bundle. Or wrapping up your application with its execution environment. And this takes me back to the old days when I was making little applications that I was giving to my friends on floppy disks and I had to make sure I included all the DLLs that they needed to run them. So how do you use containers? Well, I use Docker. And there are lots of container frameworks. There's lots of ways to do containers. Wikipedia will give you a list and a nice little chart of all of them. But I like Docker because there's a great community and ecosystem that go along with the container format. And many common web frameworks, including Rails, while we're why we're all here today, have containers that are freely available on Docker Hub, which is like RubyGems for Docker containers, container images. So Docker sounds awesome. How do you use it? So I'm going to do a quick demo using a basic Rails app. And by basic Rails app, I mean very, very basic. Built solely with the scaffold. This is me. And this is a to-do list app. Standard Rails stuff. It's got a title. Very, very long title. This is me telekinetically typing. Got some notes. You have a due date. And then there's a completion integer where you can put in the percent complete. And I'm sure that most of you guys in the audience could rip something that looked a lot like this up in a couple minutes. But let's actually walk through all the steps. So to make this application, Rails need to do. And there's a lot of tutorials out there for using Docker in Rails, but almost all of them stop here. They don't actually have any sort of models. And a Rails app without models is kind of boring. So let's make a simple model for a task. It has a title, some notes, a due date, and a completion parameter, which is just an integer. And to make it a little more exciting, I'm going to use Postgres as my production database. But I'm going to use SQLite in Devintest. This is probably familiar to a lot of you. It's something that you've done multiple times before. And if you're going to use Postgres as your production database, you need to change the default Rails gem file. Here's the gem file I ended up with, which should be incomprehensible. But in red right now are the two pieces I had to change, and you still can't see them, so let's zoom in a bit. So create a new group for production, put the PG gem in it. And then I need to move SQLite, which is in all groups in the default gem file, into the development of production group. So that's great. And since I changed my database setup, I also need to change my database YAML file. And those are the lines I need to change. Set the adapter to Postgres for production, and then I'm going to set my username, password, and host from some environment variables that Docker sets for me, and I will come back to this, I promise. So now I need a Docker file. And Docker file is just the list of things that need to happen to set up your environment. And I'm lazy, so I'm going to write a minimal Docker file. And I can do a Docker file for this application in three lines. Really, just three. The first line is telling me what image I want to start from. All Docker images, most Docker images are built from other images. So the Rails image is built from a Ruby image. The Ruby image is built from an operating system image, and turtles all the way down. I'm using the Rails image because it's really close to exactly what I want, and therefore I don't have to know anything about ops or how to install packages on an Ubuntu machine or anything like that if I don't want to. And then I'm also going to specify that my RailsMF is production because this isn't in the Rails image that I pulled. And then the CMD, this command line, is the entry point for my application. This is what happens once my container is built and my app needs to start up. And you'll see that I'm calling a shell script here. So let's see what's in that shell script, my init.sh shell script. And it's, again, three very simple lines. I'm doing an export of secret key base because Rails 4, bundle exec, dbcreate, and then I'm starting up Rails server. Yes, I'm using the default server. You could use Unicorn. You could use something else. Lazy. But there's nothing really complicated going on here. I hacked all this stuff together one night at Hack Night and not very long at all. So now let's build it. We need to build up our image with Docker. And an image, I've used that word a couple of times, is just a template for a container. So I'm going to build a Docker build, the dot on the end means use a Docker file in this directory. And this builds it. So let's see what that looks like. So that's done. And that went really, really fast. And I'm going to hand wave this a bit. One of the reasons it went really fast is the Docker caches all the intermediate steps in creating the image. So if I only change, each of those steps is a layer. And if I only change something at the very end in those last layers, I can use the cached versions of all the ones before that. Which means I don't have to repeat all of the steps, like pulling down packages from your package, from your distro's package management system. Or building gems from scratch if they have C extensions. I can skip all that if I'm not changing any of that stuff. And that means it builds really quickly. So now it's built. Let's run it. I'm going to run through these commands. I don't know what people's experience with Docker is in the audience, but I'm assuming that you have as about as much experience as I had three months ago, which is this much. So Docker run. First we're going to do the database component, because we need the database up and running before we start the web. So this command creates a new container named DB, and it uses the Postgres image to do that. These two lines are setting some environment variables for that Postgres container. And these set the password and username for the Postgres database. And all of this is documented in lovely detail on the GitHub page for the Postgres image. I'm following the instructions. It's lovely, especially if you're lazy like me. And then this dash D means we're going to run it demonized. So I'm not going to get the standard out. I'm going to let it run in the background and do its thing, and I'm not going to interact with the container in any way. And now I'm going to start the web server up. Again, we're going to run a container named web. We're using the Thagamizer to do image that I just showed how it built. Again, we're going to run it demonized. This line is mapping ports. That's what the P stands for. And what I'm doing here is I'm mapping port 3000 on my container to 3000 on the host machine. In that case, this case, it could be local host on a Linux box on my Mac. It's a Linux VM. And then this line is magic and lovely. So link makes it so that my containers can talk to each other. In this case, I'm linking the DB container that I just made to the container I'm starting up now, the web container. And I'm linking it with the alias PG for Postgres. What link does is it creates a secure tunnel between my web container and my database container. And it also puts a bunch of handy environment variables in my web container so that I can connect to the database container. And that Postgres username, Postgres password, and Postgres host address, all of that that was in the database.yaml file, those are created because I used this link flag on my Docker startup. So let's watch it. That's the database starting up. That's the web starting up. They're up. There's lots of ways to set up container. There's lots of ways to set up virtual machines for testing out your app. This is really, really fast. I'm going to grab the IP, throw it in a browser, be indecisive about which profile I want to use in Chrome, go to port 3000, the tasks collection, there it is. That's my app running. And in case it wasn't obvious, I got all of this stuff set up in less than 20 lines of code beyond the standard boilerplate that Rails New gives you. It's about five lines of code to change the gem file, five lines of code in the database yaml, three lines in the Docker file, and three lines in init. That's it. I don't know any ways that I can set up a Rails app to run on Ubuntu that are quite this easy. And there's a couple shell commands, but it still all comes in under 20 lines of code. So why would you use something like Docker? Why would you use containers? Some pros. For me, the biggest one is consistency. I hate hearing, but it works on my machine. I started my career in QA. I've heard that so many times. And I don't care if it works on your machine. I care if it works in production. And if you're using containers, your staging environment and your production environment are the same. They are the same image. They are the same OS with the same libraries, all the same versions. You don't have to worry about messing up your gem file slightly and getting the wrong, one minor version off in a way that messes up your app. The next talk, the one after mine, is going to talk about how you can also use Docker in development and possibly have exactly the same OSes and libraries and stuff in development. So there's none of this, but it works on my machine stuff. It either works or it does, everyone has the same experience. Speed. Containers start up really, really quickly. There was a video that came out from the Kubernetes team last week, two weeks ago, about them racing a Kubernetes cluster versus making a latte. It ended up being a neck and neck tie. But we're talking minutes. And I've worked on apps where we had Chef Build and Chef's Very Cool, but rebuilding all of our VMs from scratch took upwards of 15 minutes because of all the stuff we were having to build from source. And being able to start up a container very, very quickly is nice, especially when you want to do things like auto-scaling. And the caching makes it fast if you're only changing the last layers. Flexibility. If you've got a microservices setup or we're in the distributed computing track, containers let you change how the different processes sit on different hardware. So you have a VM you can say in development you want all the stuff to run on the same VM. And you want it to be split up in one way. In your staging environment, you want one of every machine on a separate VM. And then in production, you want five web servers and two database backends. And all of that can be set up pretty easily with a variety of container management tools. Portability. I talked to folks about why they do or do not use the cloud or why they roll their own or why they have their own hardware. And everyone's like, I need flexibility. I need to be able to move back and forward. And containers are great because you can run them on Linux. You can run them on a Mac. You can run them on Windows. You can run them on your personal machine. You can run them on data center hardware that your company owns. You can run them on a cloud provider. Most cloud providers support containers right now. You're never locked into specific hardware. You can move it anywhere. And it will behave the same. And then repeatability. Last year about this time was particularly bad if you had a DevOps as part of one of your hats you were wearing. You're patching rail security bugs. You're patching open SSL security bugs. And I was on a team that was doing DevOps at the time. And we had our client was a little bit nervous about us making OS level changes. Making changes to the OS libraries. Because we pushed code a couple of times a week. And that was all automated. And we had that down. But every time we had to do something lower level than that, we didn't have a process for that. And the nice thing about using containers is the process for, if you set it up this way, containers are flexible, you can make it so the process for updating your code is the exact same process for updating your OS is the exact same process for updating the C libraries you have. And so it's only one process. Everyone knows it and there's much less likely that you're going to mess it up. So there might be some cons. The biggest one is Yagney. You aren't going to need it. For some apps, and I'm not going to say this is most apps, but if you're doing a small proof of concept thing or your personal blog, maybe you don't need containers. Maybe the overhead of setting that up is too much for you. And you'd rather just use a platform as a service provider. That's fine. This is a very cool tool for people who can get benefit out of it. But I'm never going to say that any one tool is perfect for every application. And the other thing is that while containers have been around for a long time, I work at Google and Google has been running all of their internal stuff on containers for years. Docker is still only a few years old and some people are reticent about adopting stuff that's on the first half of the adoption curve. I am not one of those people. Y'all are at Ruby RailsConf. I assume y'all aren't those people. But if you work for some of those people who are a little bit reticent, you might have a harder sell on using a technology that's still new. But I think it's really cool. Otherwise, I wouldn't be doing this talk. So I've showed you some pretty cool stuff about running these containers on my Mac. But I don't think any of you actually run through production site off of MacBook Air. If you do, I have a dinosaur for you. We should come chat afterwards. So how do you manage containers in a cloud environment? How do you manage containers in a data center environment when you want multiple versions of something or you want five front-end web servers? And I'm going to talk about Kubernetes. There's lots of ways of managing containers. This is the one that I happen to know. And it's a Google project for managing clusters of containers. Basically the idea is that you build up a list of your desired state in a format that Kubernetes understands and then you start up Kubernetes on a group of VMs or bare metal machines and say, make it look like my desired state. And I want to point out specifically that Kubernetes is open source. There are more contributors outside Google than inside Google at this point. And all this is on GitHub. Very, very visible what's going on. So Kubernetes has a giant pile of vocabulary. I'm going to hit it up. I'm going to talk about it specifically up front because I'm going to use it and I'm going to forget what I have and have not defined. Master, this is the machine that manages everything. Masters have a set of minions. Usually there's several of them. They're running on different VMs. The underlying base unit is a pod and this is the smallest deployable unit. You can think of it as part of an application that intrinsically works together, not independently. And I asked some folks who work on Kubernetes, I'm like, so if you have a web application, is the web application plus the DB a pod or is the web app a pod and the database is a pod? And the answer is it depends. I'm going with web app and the database are going to be separate pods because I want to scale the two pieces independently. For the demo, I'm only going to have one database, but I'm going to have two front end web servers. But if you want to put them together, you can do that too. So a service is an abstraction for a logical set of pods, which is a lot of very fancy words to basically mean a named load balancer. And if I have multiple pods, I have a service that takes care of splitting the work up between them. And these are also how different pods talk to each other. Like we had links so that the database could talk to the web container on my local machine. We build services so that my database pods can talk to my web pods in the Kubernetes cluster. And then we have a replication controller. And this is responsible for ensuring that the correct number of pods are running at all times. We set up a replication controller, you say, this is my desired state. I'd like two replicas. And the replication controller is responsible for saying, are there two replicas? If one of those machines fails, it starts up another. If you somehow end up with extras, it'll shut one down. I want to also put some notes. This is currently in beta. The API may change. But people are using it. So warning, but it's still awesome. And one of the things I want to be very explicit about is that Kubernetes gives you options. This is open source. You can run this on your own hardware. You can run this on a cloud provider's VMs. Google Cloud Platform supports it. But there are others who are working with Kubernetes. There are other companies that work with Kubernetes. And this technology does not lock you into a specific provider. So that was a bunch of words. And I like giving technical talks with lots of code. So let's look at some code. This is a hand-wavy example of a Kubernetes configuration file. And this is not a specific one. But the basic format is similar for a lot of them. So you have an ID. You have a kind, which could be things like pod or service or replication controller. The API version, desired state. And that desired state is where you set up, this is what I want Kubernetes to maintain for me. And that has maybe a set of containers or something else. And then there's some labels. And these are just used so that you can gather together things that are related and refer to groups more easily. So for this example. Let's do the database first. So I'm going to have one database pod. And there's a bunch of ellipses on here where I pulled out a lot of extra braces because JSON on slides doesn't work so hot. But this is the pod configuration file for the database. And I'm specifically showing that it has a set of containers. And that's an array with one unit in it. The container is named DB. It uses the Postgres image. I can pass in my environment variables just like I did to Docker run. And then I can also do some ports. And I'm saying that I want to map my container port 5, 4, 3, 2 to the host port 5, 4, 3, 2. Standard Postgres port. And if I want other pods to be able to talk to this pod, I need to set up a service. And here's the service definition. IDDB, kind service. And again, I want the service port 5, 4, 3, 2 to be mapped to a container port 5, 4, 3, 2. And then the selector says what pods should be in this service. So any pod that's named database will be in this service. Really simple stuff. So now I've got my database set up. Let's do the web. I'm going to have a replication controller because I want two replicas. Again, pulled out a bunch of friends and curly braces and such. And this is actually a lot of stuff. So let's look at it in two pieces. The top part is the desired state for my replication controller. I want two replicas. The replicas that count toward this are the ones that are named web. And then I give a template for the replication controller to create pods. And that's what this template looks like. I've got a single container named web using the image thagamizer2do prod. And I'm passing in some environment variables. And I'm doing some port mapping. If I want to be able to access this set of pods with a replication controller, I need to have a service. Here's the service definition. I've got a selector saying anything that's named web is in this service. And I'm doing some port mapping again, specifically putting it on port 3000. So this is all great, but I haven't showed you anything running. So you can run all this, and the Google Kubernetes environment is Google Container Engine. It's in public alpha. And I'm going to show you how to get it running. So this is just something you run at the command line. The first three things, G Cloud Alpha Container is using the Google Cloud Platform command line interface. And I'm saying that I want alpha feature set, because this is alpha, and I want the container engine subset. It's the Kubernetes command line interface, and I'm going to say create from file, the DB pod.json. That's the DB pod file I showed you. And I'm going to make that. And then I can say, hey, Kubernetes, give me the list of pods. And I get back a list of pods in their state. And you can see that this one's running. You can see what image it is and what node it's been assigned to currently. Great. That's up. So now I can do the service. Again, create dash f. I can get from file for the DB service. And I can say, Qubectl, give me the services. And it gives me a list of the services and what ports they've exposed. Great. And I'll just create the web controller. Again, create from a file. And I can get the replication controllers and see that I have this web controller, the image that's being used, and how many replicas there are. And then I can go get the pods. And I can see the list of pods that's been started up and what their state is. And now I need to create my web service. And you'll notice that everything is create dash f. I'm just creating all of these from the standard configuration files. There's really not a lot that's complicated here. The first half of the line looks a little scary, but it's the same every single time. So I create my web service. I start it up and you can see that it's at port 3000. And then I'm going to do something crazy. I'm going to have Kubernetes resize my web replication controller. And I'm going to say I want five replicas now. Wait a couple seconds to a minute. And I have five running pods now. All I had to do was say how many I wanted it to happen. Because Kubernetes job is to say, what state do you want the cluster in? And I say, this is the state I want. And it's like, I will figure out how to make that happen. It pulls down images as it needs to. It creates new containers as it needs to. It divides them among the number of nodes. And you'll see that I've only got three hosts listed because the cluster I created only had three VMs in it. I didn't need to provision new VMs to have additional workers running. So at Mountain West RubyConf, Star Horan gave a great talk. And in his talk, he had a slide that said the lies I've told during this talk. So I would like to think that I haven't lied to you, but there are some large sections of this that I have hand-waved vigorously. And I'm going to call them out right now so that you guys don't have to ask questions about them when I open up the floor to questions. The first one is disk. I didn't show you how to set up the database to hit a persistent disk. So if you restart that database container right now, all your data goes away. You can do that. There's tutorials on how to do it. I also didn't show you how to set a shared disk between multiple web clients. There are tutorials on how to do that, too. This is a 101-level talk. That is not a 101-level concept. Security. I'm not going to say that what I showed you is good for production-level security. Your app has unique security concerns. Please do a security audit. Replication. I didn't show you how to set up a cluster of database machines that replicate across themselves. There are lots of tutorials for this. One of the standard Google Cloud tutorials uses Redis and has Redis Master and Redis Workers that shows how to do something like that. I kind of talked about this, but Docker runs on Linux machines only. To run it on a Mac, you need a tool called Boot to Docker that uses a virtual box to make a little Linux machine, a little Linux slice where you can run your Docker containers. It does it well, so I forget the fact that I'm not actually using Docker natively. There's tools for Windows that are the same. You can also just run this on Ubuntu. When I'm at work, I've got an Ubuntu desktop underneath my desk, a big tower, and I do most of my work as a station to that machine. And size. So, one of the things I found talking to folks who do containers and work with Docker a lot is that they're concerned about VM image size. And the VM, the images that I chose, the standard Rails image is huge. If you care about size, you can make your own by taking out the pieces you don't need. For example, I believe the standard Rails image has both Postgres database client libraries and my SQL client libraries. I was only using Postgres, so if I built my own, I could choose not to include the my SQL ones and save some space. But make the trade-off of your time versus how big an image is and figure out where your laziness actually lies on that. I was going for, you know, minimal effort here, which is why I chose those images. So, as I said, this is a 101 level talk. A lot of you in the audience have been nodding along, which means I probably didn't show you anything new. But if you want to learn more, first place to start is shipping RubyOps with Docker by Brian Helmkamp at red.rubyconf 18 months ago, a year ago. This is an awesome hour-long talk on exactly this topic, includes live code demos, because he is more daring than I. I don't do live code demos at a conference, because you never know about the Wi-Fi. If you're going to get into this, I recommend watching this talk first, because it will help you internalize the vocabulary. One of the things I struggled with learning this stuff from preparing this talk was that I would pull in a bunch of information and think I understood it and then get stuck. And I would unstick myself three days later once all the pieces sort of themselves out in my head. And I had the answer the whole time, but so much new vocabulary and so much dense documentation was making it hard for me to help myself. And once I watched this talk, I kind of got a big picture sense of how things were, and things made more sense. If you want to find out about Container Engine, cloud.google.com, container engine, doc, there are two demos, the, there's a simple hello world type demo, and then there's a guest book demo, and I use the guest book demo extensively when preparing this talk. If you want to learn about Kubernetes, that's the Kubernetes website. And if you want to talk to people who work on Container Engine and on Kubernetes, on FreeNode, go to the Google Container's room. The devs actively monitor this. No, really, they actively monitor this. You can talk to the people who are working on this and ask them very technical questions, and they will help you find answers. Google Cloud encourages folks to hit up Stack Overflow, make sure that your, it helps a lot if your question is tagged, so that the appropriate people who are monitoring those tags can answer your question for you. If you run into trouble, let me know, and I will make sure someone answers your question. And now time for the sales pitch part. I work on Google Cloud Platform. We have storage, we have VMs, we have App Engine, we have logging and monitoring tools, we have data analysis tools that are awesome, and I gave a talk about them on Western Ruby Conf. Data centers all over the world, Internet endpoints all over the world. All the stuff you'd expect for a cloud provider, and at least some of you probably didn't know that you could get a VM from Google before I said these words. And I want to point this out, because this is the really cool part for me, is I've been to a data center, and our cloud customers, their code runs on the same racks and the same physical hardware as YouTube, as Search. I can't tell in the data center what's running on any machine, so any work we do to improve the infrastructure for things like YouTube and Search benefits our cloud customers as well. We have a free trial, because you have a free trial. It does ask for a credit card, but you will not be billed unless you okay billing. The credit card is there for fraud protection and a bunch of other legal reasons. And if you have any trouble getting the free trial, or you've used your free trial and want to try something else, come chat with me. I have ways around that, but I encourage you to use the free trial, because that gives you $200 in credit for 60 days, and that's better than I can give you easily at this conference. So thank you. I want to thank the conference organizers for having me here, and for organizing this giant conference. I want to thank my coworkers who reviewed these slides for me, even though they're on the West Coast, and so I was sending them slides inopportune time zone times. When I gave conference talks, I have plastic dinosaurs. Someone told me this looks like just a random baggy stuff. There's actually a bunch of plastic dinosaurs in here that I give away to people who ask questions or come chat with me. I also have stickers up on the podium, right below the podium up here. Lots and lots and lots of stickers. Come get stickers from me. We've got about 10 minutes left, so I'm going to open the floor to questions.
 So, I'm Aja Hammerly.  I'm a developer advocate at Google.  I tweet at ThagamizerRB, and all the code that I'm going to show in this talk is on  GitHub.  I'm Thagamizer on GitHub, and it's in my examples repository in the RailsConf 15 folder.  And since I'm showing you code, I have to say that all the code in this talk is copyright  Google and licensed under patchy v2.  So, first things first.  I'm not an expert at ops.  I'm actually not an expert at anything.  I tend toward more drac of all trades, master of none.  But I've spent some time racking servers, and I have bloody knuckles.  I've had bloody knuckles to prove it.  And I've built things that look like this.  But my skills have not gotten to the level of this yet.  And when I try to do something that's all professional ops fancy, I end up building  things that look a little bit like this.  And why is that?  Because I'm fundamentally lazy.  I'm passionate about some things, solving problems, building sites, algorithms.  But I'm lazy about deployment and operations and maintenance, and I find a lot of that  stuff to be fiddly and not a lot of fun.  And I'm here to tell you right now that that's okay for most people.  And what do I mean specifically by lazy?  I mean, doing the minimum that you absolutely have to to have something be successful and  stable.  I want to make systems that are lazy so that they'll make, I can be lazy so that they'll  maintain themselves.  And one way I've found to do that is with containers.  So what the heck's a container?  So according to several sources, a container is a way for multiple applications to securely  share hardware with a minimum footprint.  And frequently this means less of a footprint than using virtual machines.  To me, containers can also be an environment package.  It's a way of wrapping up your operating system, your build dependencies, any libraries you  need, possibly your application code, all into a nice little bundle.  Or wrapping up your application with its execution environment.  And this takes me back to the old days when I was making little applications that I was  giving to my friends on floppy disks and I had to make sure I included all the DLLs that  they needed to run them.  So how do you use containers?  Well, I use Docker.  And there are lots of container frameworks.  There's lots of ways to do containers.  Wikipedia will give you a list and a nice little chart of all of them.  But I like Docker because there's a great community and ecosystem that go along with the container  format.  And many common web frameworks, including Rails, while we're why we're all here today,  have containers that are freely available on Docker Hub, which is like RubyGems for  Docker containers, container images.  So Docker sounds awesome.  How do you use it?  So I'm going to do a quick demo using a basic Rails app.  And by basic Rails app, I mean very, very basic.  Built solely with the scaffold.  This is me.  And this is a to-do list app.  Standard Rails stuff.  It's got a title.  Very, very long title.  This is me telekinetically typing.  Got some notes.  You have a due date.  And then there's a completion integer where you can put in the percent complete.  And I'm sure that most of you guys in the audience could rip something that looked a  lot like this up in a couple minutes.  But let's actually walk through all the steps.  So to make this application, Rails need to do.  And there's a lot of tutorials out there for using Docker in Rails, but almost all of them  stop here.  They don't actually have any sort of models.  And a Rails app without models is kind of boring.  So let's make a simple model for a task.  It has a title, some notes, a due date, and a completion parameter, which is just an integer.  And to make it a little more exciting, I'm going to use Postgres as my production database.  But I'm going to use SQLite in Devintest.  This is probably familiar to a lot of you.  It's something that you've done multiple times before.  And if you're going to use Postgres as your production database, you need to change the  default Rails gem file.  Here's the gem file I ended up with, which should be incomprehensible.  But in red right now are the two pieces I had to change, and you still can't see them,  so let's zoom in a bit.  So create a new group for production, put the PG gem in it.  And then I need to move SQLite, which is in all groups in the default gem file, into the  development of production group.  So that's great.  And since I changed my database setup, I also need to change my database YAML file.  And those are the lines I need to change.  Set the adapter to Postgres for production, and then I'm going to set my username, password,  and host from some environment variables that Docker sets for me, and I will come back to  this, I promise.  So now I need a Docker file.  And Docker file is just the list of things that need to happen to set up your environment.  And I'm lazy, so I'm going to write a minimal Docker file.  And I can do a Docker file for this application in three lines.  Really, just three.  The first line is telling me what image I want to start from.  All Docker images, most Docker images are built from other images.  So the Rails image is built from a Ruby image.  The Ruby image is built from an operating system image, and turtles all the way down.  I'm using the Rails image because it's really close to exactly what I want, and therefore  I don't have to know anything about ops or how to install packages on an Ubuntu machine  or anything like that if I don't want to.  And then I'm also going to specify that my RailsMF is production because this isn't in  the Rails image that I pulled.  And then the CMD, this command line, is the entry point for my application.  This is what happens once my container is built and my app needs to start up.  And you'll see that I'm calling a shell script here.  So let's see what's in that shell script, my init.sh shell script.  And it's, again, three very simple lines.  I'm doing an export of secret key base because Rails 4, bundle exec, dbcreate, and then I'm  starting up Rails server.  Yes, I'm using the default server.  You could use Unicorn.  You could use something else.  Lazy.  But there's nothing really complicated going on here.  I hacked all this stuff together one night at Hack Night and not very long at all.  So now let's build it.  We need to build up our image with Docker.  And an image, I've used that word a couple of times, is just a template for a container.  So I'm going to build a Docker build, the dot on the end means use a Docker file in this  directory.  And this builds it.  So let's see what that looks like.  So that's done.  And that went really, really fast.  And I'm going to hand wave this a bit.  One of the reasons it went really fast is the Docker caches all the intermediate steps in  creating the image.  So if I only change, each of those steps is a layer.  And if I only change something at the very end in those last layers, I can use the cached  versions of all the ones before that.  Which means I don't have to repeat all of the steps, like pulling down packages from  your package, from your distro's package management system.  Or building gems from scratch if they have C extensions.  I can skip all that if I'm not changing any of that stuff.  And that means it builds really quickly.  So now it's built.  Let's run it.  I'm going to run through these commands.  I don't know what people's experience with Docker is in the audience, but I'm assuming  that you have as about as much experience as I had three months ago, which is this much.  So Docker run.  First we're going to do the database component, because we need the database up and running  before we start the web.  So this command creates a new container named DB, and it uses the Postgres image to do that.  These two lines are setting some environment variables for that Postgres container.  And these set the password and username for the Postgres database.  And all of this is documented in lovely detail on the GitHub page for the Postgres image.  I'm following the instructions.  It's lovely, especially if you're lazy like me.  And then this dash D means we're going to run it demonized.  So I'm not going to get the standard out.  I'm going to let it run in the background and do its thing, and I'm not going to interact  with the container in any way.  And now I'm going to start the web server up.  Again, we're going to run a container named web.  We're using the Thagamizer to do image that I just showed how it built.  Again, we're going to run it demonized.  This line is mapping ports.  That's what the P stands for.  And what I'm doing here is I'm mapping port 3000 on my container to 3000 on the host machine.  In that case, this case, it could be local host on a Linux box on my Mac.  It's a Linux VM.  And then this line is magic and lovely.  So link makes it so that my containers can talk to each other.  In this case, I'm linking the DB container that I just made to the container I'm starting  up now, the web container.  And I'm linking it with the alias PG for Postgres.  What link does is it creates a secure tunnel between my web container and my database container.  And it also puts a bunch of handy environment variables in my web container so that I can  connect to the database container.  And that Postgres username, Postgres password, and Postgres host address, all of that that  was in the database.yaml file, those are created because I used this link flag on my Docker  startup.  So let's watch it.  That's the database starting up.  That's the web starting up.  They're up.  There's lots of ways to set up container.  There's lots of ways to set up virtual machines for testing out your app.  This is really, really fast.  I'm going to grab the IP, throw it in a browser, be indecisive about which profile I want to  use in Chrome, go to port 3000, the tasks collection, there it is.  That's my app running.  And in case it wasn't obvious, I got all of this stuff set up in less than 20 lines of  code beyond the standard boilerplate that Rails New gives you.  It's about five lines of code to change the gem file, five lines of code in the database  yaml, three lines in the Docker file, and three lines in init.  That's it.  I don't know any ways that I can set up a Rails app to run on Ubuntu that are quite  this easy.  And there's a couple shell commands, but it still all comes in under 20 lines of code.  So why would you use something like Docker?  Why would you use containers?  Some pros.  For me, the biggest one is consistency.  I hate hearing, but it works on my machine.  I started my career in QA.  I've heard that so many times.  And I don't care if it works on your machine.  I care if it works in production.  And if you're using containers, your staging environment and your production environment  are the same.  They are the same image.  They are the same OS with the same libraries, all the same versions.  You don't have to worry about messing up your gem file slightly and getting the wrong,  one minor version off in a way that messes up your app.  The next talk, the one after mine, is going to talk about how you can also use Docker in  development and possibly have exactly the same OSes and libraries and stuff in development.  So there's none of this, but it works on my machine stuff.  It either works or it does, everyone has the same experience.  Speed.  Containers start up really, really quickly.  There was a video that came out from the Kubernetes team last week, two weeks ago, about them  racing a Kubernetes cluster versus making a latte.  It ended up being a neck and neck tie.  But we're talking minutes.  And I've worked on apps where we had Chef Build and Chef's Very Cool, but rebuilding  all of our VMs from scratch took upwards of 15 minutes because of all the stuff we were  having to build from source.  And being able to start up a container very, very quickly is nice, especially when you  want to do things like auto-scaling.  And the caching makes it fast if you're only changing the last layers.  Flexibility.  If you've got a microservices setup or we're in the distributed computing track, containers  let you change how the different processes sit on different hardware.  So you have a VM you can say in development you want all the stuff to run on the same  VM.  And you want it to be split up in one way.  In your staging environment, you want one of every machine on a separate VM.  And then in production, you want five web servers and two database backends.  And all of that can be set up pretty easily with a variety of container management tools.  Portability.  I talked to folks about why they do or do not use the cloud or why they roll their own  or why they have their own hardware.  And everyone's like, I need flexibility.  I need to be able to move back and forward.  And containers are great because you can run them on Linux.  You can run them on a Mac.  You can run them on Windows.  You can run them on your personal machine.  You can run them on data center hardware that your company owns.  You can run them on a cloud provider.  Most cloud providers support containers right now.  You're never locked into specific hardware.  You can move it anywhere.  And it will behave the same.  And then repeatability.  Last year about this time was particularly bad if you had a DevOps as part of one of  your hats you were wearing.  You're patching rail security bugs.  You're patching open SSL security bugs.  And I was on a team that was doing DevOps at the time.  And we had our client was a little bit nervous about us making OS level changes.  Making changes to the OS libraries.  Because we pushed code a couple of times a week.  And that was all automated.  And we had that down.  But every time we had to do something lower level than that, we didn't have a process for  that.  And the nice thing about using containers is the process for, if you set it up this  way, containers are flexible, you can make it so the process for updating your code is  the exact same process for updating your OS is the exact same process for updating the  C libraries you have.  And so it's only one process.  Everyone knows it and there's much less likely that you're going to mess it up.  So there might be some cons.  The biggest one is Yagney.  You aren't going to need it.  For some apps, and I'm not going to say this is most apps, but if you're doing a small  proof of concept thing or your personal blog, maybe you don't need containers.  Maybe the overhead of setting that up is too much for you.  And you'd rather just use a platform as a service provider.  That's fine.  This is a very cool tool for people who can get benefit out of it.  But I'm never going to say that any one tool is perfect for every application.  And the other thing is that while containers have been around for a long time, I work at  Google and Google has been running all of their internal stuff on containers for years.  Docker is still only a few years old and some people are reticent about adopting stuff that's  on the first half of the adoption curve.  I am not one of those people.  Y'all are at Ruby RailsConf.  I assume y'all aren't those people.  But if you work for some of those people who are a little bit reticent, you might have  a harder sell on using a technology that's still new.  But I think it's really cool.  Otherwise, I wouldn't be doing this talk.  So I've showed you some pretty cool stuff about running these containers on my Mac.  But I don't think any of you actually run through production site off of MacBook Air.  If you do, I have a dinosaur for you.  We should come chat afterwards.  So how do you manage containers in a cloud environment?  How do you manage containers in a data center environment when you want multiple versions  of something or you want five front-end web servers?  And I'm going to talk about Kubernetes.  There's lots of ways of managing containers.  This is the one that I happen to know.  And it's a Google project for managing clusters of containers.  Basically the idea is that you build up a list of your desired state in a format that  Kubernetes understands and then you start up Kubernetes on a group of VMs or bare metal  machines and say, make it look like my desired state.  And I want to point out specifically that Kubernetes is open source.  There are more contributors outside Google than inside Google at this point.  And all this is on GitHub.  Very, very visible what's going on.  So Kubernetes has a giant pile of vocabulary.  I'm going to hit it up.  I'm going to talk about it specifically up front because I'm going to use it and I'm  going to forget what I have and have not defined.  Master, this is the machine that manages everything.  Masters have a set of minions.  Usually there's several of them.  They're running on different VMs.  The underlying base unit is a pod and this is the smallest deployable unit.  You can think of it as part of an application that intrinsically works together, not independently.  And I asked some folks who work on Kubernetes, I'm like, so if you have a web application,  is the web application plus the DB a pod or is the web app a pod and the database is a  pod?  And the answer is it depends.  I'm going with web app and the database are going to be separate pods because I want to  scale the two pieces independently.  For the demo, I'm only going to have one database, but I'm going to have two front end web servers.  But if you want to put them together, you can do that too.  So a service is an abstraction for a logical set of pods, which is a lot of very fancy  words to basically mean a named load balancer.  And if I have multiple pods, I have a service that takes care of splitting the work up between  them.  And these are also how different pods talk to each other.  Like we had links so that the database could talk to the web container on my local machine.  We build services so that my database pods can talk to my web pods in the Kubernetes  cluster.  And then we have a replication controller.  And this is responsible for ensuring that the correct number of pods are running at  all times.  We set up a replication controller, you say, this is my desired state.  I'd like two replicas.  And the replication controller is responsible for saying, are there two replicas?  If one of those machines fails, it starts up another.  If you somehow end up with extras, it'll shut one down.  I want to also put some notes.  This is currently in beta.  The API may change.  But people are using it.  So warning, but it's still awesome.  And one of the things I want to be very explicit about is that Kubernetes gives you options.  This is open source.  You can run this on your own hardware.  You can run this on a cloud provider's VMs.  Google Cloud Platform supports it.  But there are others who are working with Kubernetes.  There are other companies that work with Kubernetes.  And this technology does not lock you into a specific provider.  So that was a bunch of words.  And I like giving technical talks with lots of code.  So let's look at some code.  This is a hand-wavy example of a Kubernetes configuration file.  And this is not a specific one.  But the basic format is similar for a lot of them.  So you have an ID.  You have a kind, which could be things like pod or service or replication controller.  The API version, desired state.  And that desired state is where you set up, this is what I want Kubernetes to maintain  for me.  And that has maybe a set of containers or something else.  And then there's some labels.  And these are just used so that you can gather together things that are related and refer  to groups more easily.  So for this example.  Let's do the database first.  So I'm going to have one database pod.  And there's a bunch of ellipses on here where I pulled out a lot of extra braces because  JSON on slides doesn't work so hot.  But this is the pod configuration file for the database.  And I'm specifically showing that it has a set of containers.  And that's an array with one unit in it.  The container is named DB.  It uses the Postgres image.  I can pass in my environment variables just like I did to Docker run.  And then I can also do some ports.  And I'm saying that I want to map my container port 5, 4, 3, 2 to the host port 5, 4, 3,  2.  Standard Postgres port.  And if I want other pods to be able to talk to this pod, I need to set up a service.  And here's the service definition.  IDDB, kind service.  And again, I want the service port 5, 4, 3, 2 to be mapped to a container port 5, 4,  3, 2.  And then the selector says what pods should be in this service.  So any pod that's named database will be in this service.  Really simple stuff.  So now I've got my database set up.  Let's do the web.  I'm going to have a replication controller because I want two replicas.  Again, pulled out a bunch of friends and curly braces and such.  And this is actually a lot of stuff.  So let's look at it in two pieces.  The top part is the desired state for my replication controller.  I want two replicas.  The replicas that count toward this are the ones that are named web.  And then I give a template for the replication controller to create pods.  And that's what this template looks like.  I've got a single container named web using the image thagamizer2do prod.  And I'm passing in some environment variables.  And I'm doing some port mapping.  If I want to be able to access this set of pods with a replication controller, I need  to have a service.  Here's the service definition.  I've got a selector saying anything that's named web is in this service.  And I'm doing some port mapping again, specifically putting it on port 3000.  So this is all great, but I haven't showed you anything running.  So you can run all this, and the Google Kubernetes environment is Google Container Engine.  It's in public alpha.  And I'm going to show you how to get it running.  So this is just something you run at the command line.  The first three things, G Cloud Alpha Container is using the Google Cloud Platform command  line interface.  And I'm saying that I want alpha feature set, because this is alpha, and I want the container  engine subset.  It's the Kubernetes command line interface, and I'm going to say create from file, the  DB pod.json.  That's the DB pod file I showed you.  And I'm going to make that.  And then I can say, hey, Kubernetes, give me the list of pods.  And I get back a list of pods in their state.  And you can see that this one's running.  You can see what image it is and what node it's been assigned to currently.  Great.  That's up.  So now I can do the service.  Again, create dash f.  I can get from file for the DB service.  And I can say, Qubectl, give me the services.  And it gives me a list of the services and what ports they've exposed.  Great.  And I'll just create the web controller.  Again, create from a file.  And I can get the replication controllers and see that I have this web controller, the image  that's being used, and how many replicas there are.  And then I can go get the pods.  And I can see the list of pods that's been started up and what their state is.  And now I need to create my web service.  And you'll notice that everything is create dash f.  I'm just creating all of these from the standard configuration files.  There's really not a lot that's complicated here.  The first half of the line looks a little scary, but it's the same every single time.  So I create my web service.  I start it up and you can see that it's at port 3000.  And then I'm going to do something crazy.  I'm going to have Kubernetes resize my web replication controller.  And I'm going to say I want five replicas now.  Wait a couple seconds to a minute.  And I have five running pods now.  All I had to do was say how many I wanted it to happen.  Because Kubernetes job is to say, what state do you want the cluster in?  And I say, this is the state I want.  And it's like, I will figure out how to make that happen.  It pulls down images as it needs to.  It creates new containers as it needs to.  It divides them among the number of nodes.  And you'll see that I've only got three hosts listed because the cluster I created only  had three VMs in it.  I didn't need to provision new VMs to have additional workers running.  So at Mountain West RubyConf, Star Horan gave a great talk.  And in his talk, he had a slide that said the lies I've told during this talk.  So I would like to think that I haven't lied to you, but there are some large sections  of this that I have hand-waved vigorously.  And I'm going to call them out right now so that you guys don't have to ask questions  about them when I open up the floor to questions.  The first one is disk.  I didn't show you how to set up the database to hit a persistent disk.  So if you restart that database container right now, all your data goes away.  You can do that.  There's tutorials on how to do it.  I also didn't show you how to set a shared disk between multiple web clients.  There are tutorials on how to do that, too.  This is a 101-level talk.  That is not a 101-level concept.  Security.  I'm not going to say that what I showed you is good for production-level security.  Your app has unique security concerns.  Please do a security audit.  Replication.  I didn't show you how to set up a cluster of database machines that replicate across  themselves.  There are lots of tutorials for this.  One of the standard Google Cloud tutorials uses Redis and has Redis Master and Redis  Workers that shows how to do something like that.  I kind of talked about this, but Docker runs on Linux machines only.  To run it on a Mac, you need a tool called Boot to Docker that uses a virtual box to  make a little Linux machine, a little Linux slice where you can run your Docker containers.  It does it well, so I forget the fact that I'm not actually using Docker natively.  There's tools for Windows that are the same.  You can also just run this on Ubuntu.  When I'm at work, I've got an Ubuntu desktop underneath my desk, a big tower, and I do  most of my work as a station to that machine.  And size.  So, one of the things I found talking to folks who do containers and work with Docker a lot  is that they're concerned about VM image size.  And the VM, the images that I chose, the standard Rails image is huge.  If you care about size, you can make your own by taking out the pieces you don't need.  For example, I believe the standard Rails image has both Postgres database client libraries  and my SQL client libraries.  I was only using Postgres, so if I built my own, I could choose not to include the my  SQL ones and save some space.  But make the trade-off of your time versus how big an image is and figure out where your  laziness actually lies on that.  I was going for, you know, minimal effort here, which is why I chose those images.  So, as I said, this is a 101 level talk.  A lot of you in the audience have been nodding along, which means I probably didn't show  you anything new.  But if you want to learn more, first place to start is shipping RubyOps with Docker by  Brian Helmkamp at red.rubyconf 18 months ago, a year ago.  This is an awesome hour-long talk on exactly this topic, includes live code demos, because  he is more daring than I.  I don't do live code demos at a conference, because you never know about the Wi-Fi.  If you're going to get into this, I recommend watching this talk first, because it will  help you internalize the vocabulary.  One of the things I struggled with learning this stuff from preparing this talk was that  I would pull in a bunch of information and think I understood it and then get stuck.  And I would unstick myself three days later once all the pieces sort of themselves out  in my head.  And I had the answer the whole time, but so much new vocabulary and so much dense documentation  was making it hard for me to help myself.  And once I watched this talk, I kind of got a big picture sense of how things were, and  things made more sense.  If you want to find out about Container Engine, cloud.google.com, container engine, doc, there  are two demos, the, there's a simple hello world type demo, and then there's a guest  book demo, and I use the guest book demo extensively when preparing this talk.  If you want to learn about Kubernetes, that's the Kubernetes website.  And if you want to talk to people who work on Container Engine and on Kubernetes, on  FreeNode, go to the Google Container's room.  The devs actively monitor this.  No, really, they actively monitor this.  You can talk to the people who are working on this and ask them very technical questions,  and they will help you find answers.  Google Cloud encourages folks to hit up Stack Overflow, make sure that your, it helps a lot  if your question is tagged, so that the appropriate people who are monitoring those tags can answer  your question for you.  If you run into trouble, let me know, and I will make sure someone answers your question.  And now time for the sales pitch part.  I work on Google Cloud Platform.  We have storage, we have VMs, we have App Engine, we have logging and monitoring tools,  we have data analysis tools that are awesome, and I gave a talk about them on Western Ruby  Conf.  Data centers all over the world, Internet endpoints all over the world.  All the stuff you'd expect for a cloud provider, and at least some of you probably didn't know  that you could get a VM from Google before I said these words.  And I want to point this out, because this is the really cool part for me, is I've been  to a data center, and our cloud customers, their code runs on the same racks and the  same physical hardware as YouTube, as Search.  I can't tell in the data center what's running on any machine, so any work we do to improve  the infrastructure for things like YouTube and Search benefits our cloud customers as  well.  We have a free trial, because you have a free trial.  It does ask for a credit card, but you will not be billed unless you okay billing.  The credit card is there for fraud protection and a bunch of other legal reasons.  And if you have any trouble getting the free trial, or you've used your free trial and  want to try something else, come chat with me.  I have ways around that, but I encourage you to use the free trial, because that gives  you $200 in credit for 60 days, and that's better than I can give you easily at this  conference.  So thank you.  I want to thank the conference organizers for having me here, and for organizing this  giant conference.  I want to thank my coworkers who reviewed these slides for me, even though they're on the  West Coast, and so I was sending them slides inopportune time zone times.  When I gave conference talks, I have plastic dinosaurs.  Someone told me this looks like just a random baggy stuff.  There's actually a bunch of plastic dinosaurs in here that I give away to people who ask  questions or come chat with me.  I also have stickers up on the podium, right below the podium up here.  Lots and lots and lots of stickers.  Come get stickers from me.  We've got about 10 minutes left, so I'm going to open the floor to questions.
DevOps_for_The_Lazy_0_47.png DevOps_for_The_Lazy_48_155.png DevOps_for_The_Lazy_156_299.png DevOps_for_The_Lazy_300_851.png DevOps_for_The_Lazy_852_1283.png DevOps_for_The_Lazy_1284_1511.png DevOps_for_The_Lazy_1512_1643.png DevOps_for_The_Lazy_1644_2627.png DevOps_for_The_Lazy_2628_2699.png DevOps_for_The_Lazy_2700_4571.png DevOps_for_The_Lazy_4572_4679.png DevOps_for_The_Lazy_4680_5363.png DevOps_for_The_Lazy_5364_6815.png DevOps_for_The_Lazy_6816_7031.png DevOps_for_The_Lazy_7032_10151.png DevOps_for_The_Lazy_10152_10235.png DevOps_for_The_Lazy_10236_11219.png DevOps_for_The_Lazy_11220_14495.png DevOps_for_The_Lazy_14496_14543.png DevOps_for_The_Lazy_14544_14651.png DevOps_for_The_Lazy_14652_15155.png DevOps_for_The_Lazy_15156_15371.png DevOps_for_The_Lazy_15372_22883.png DevOps_for_The_Lazy_22884_23519.png DevOps_for_The_Lazy_23520_27731.png DevOps_for_The_Lazy_27732_28607.png DevOps_for_The_Lazy_28608_28739.png DevOps_for_The_Lazy_28740_29747.png DevOps_for_The_Lazy_29748_30263.png DevOps_for_The_Lazy_30264_30443.png DevOps_for_The_Lazy_30444_31007.png DevOps_for_The_Lazy_31008_31343.png DevOps_for_The_Lazy_31344_31595.png DevOps_for_The_Lazy_31596_31907.png DevOps_for_The_Lazy_31908_44159.png DevOps_for_The_Lazy_44160_44495.png DevOps_for_The_Lazy_44496_44675.png DevOps_for_The_Lazy_44676_44783.png DevOps_for_The_Lazy_44784_44855.png DevOps_for_The_Lazy_44856_44903.png DevOps_for_The_Lazy_44904_44975.png DevOps_for_The_Lazy_44976_45083.png DevOps_for_The_Lazy_45084_45094.png
So, I'm Aja Hammerly. I'm a developer advocate at Google. I tweet at ThagamizerRB, and all the code that I'm going to show in this talk is on GitHub. I'm Thagamizer on GitHub, and it's in my examples repository in the RailsConf 15 folder. And since I'm showing you code, I have to say that all the code in this talk is copyright Google and licensed under patchy v2. So, first things first. I'm not an expert at ops. I'm actually not an expert at anything. I tend toward more drac of all trades, master of none. But I've spent some time racking servers, and I have bloody knuckles. I've had bloody knuckles to prove it. And I've built things that look like this. But my skills have not gotten to the level of this yet. And when I try to do something that's all professional ops fancy, I end up building things that look a little bit like this. And why is that? Because I'm fundamentally lazy. I'm passionate about some things, solving problems, building sites, algorithms. But I'm lazy about deployment and operations and maintenance, and I find a lot of that stuff to be fiddly and not a lot of fun. And I'm here to tell you right now that that's okay for most people. And what do I mean specifically by lazy? I mean, doing the minimum that you absolutely have to to have something be successful and stable. I want to make systems that are lazy so that they'll make, I can be lazy so that they'll maintain themselves. And one way I've found to do that is with containers. So what the heck's a container? So according to several sources, a container is a way for multiple applications to securely share hardware with a minimum footprint. And frequently this means less of a footprint than using virtual machines. To me, containers can also be an environment package. It's a way of wrapping up your operating system, your build dependencies, any libraries you need, possibly your application code, all into a nice little bundle. Or wrapping up your application with its execution environment. And this takes me back to the old days when I was making little applications that I was giving to my friends on floppy disks and I had to make sure I included all the DLLs that they needed to run them. So how do you use containers? Well, I use Docker. And there are lots of container frameworks. There's lots of ways to do containers. Wikipedia will give you a list and a nice little chart of all of them. But I like Docker because there's a great community and ecosystem that go along with the container format. And many common web frameworks, including Rails, while we're why we're all here today, have containers that are freely available on Docker Hub, which is like RubyGems for Docker containers, container images. So Docker sounds awesome. How do you use it? So I'm going to do a quick demo using a basic Rails app. And by basic Rails app, I mean very, very basic. Built solely with the scaffold. This is me. And this is a to-do list app. Standard Rails stuff. It's got a title. Very, very long title. This is me telekinetically typing. Got some notes. You have a due date. And then there's a completion integer where you can put in the percent complete. And I'm sure that most of you guys in the audience could rip something that looked a lot like this up in a couple minutes. But let's actually walk through all the steps. So to make this application, Rails need to do. And there's a lot of tutorials out there for using Docker in Rails, but almost all of them stop here. They don't actually have any sort of models. And a Rails app without models is kind of boring. So let's make a simple model for a task. It has a title, some notes, a due date, and a completion parameter, which is just an integer. And to make it a little more exciting, I'm going to use Postgres as my production database. But I'm going to use SQLite in Devintest. This is probably familiar to a lot of you. It's something that you've done multiple times before. And if you're going to use Postgres as your production database, you need to change the default Rails gem file. Here's the gem file I ended up with, which should be incomprehensible. But in red right now are the two pieces I had to change, and you still can't see them, so let's zoom in a bit. So create a new group for production, put the PG gem in it. And then I need to move SQLite, which is in all groups in the default gem file, into the development of production group. So that's great. And since I changed my database setup, I also need to change my database YAML file. And those are the lines I need to change. Set the adapter to Postgres for production, and then I'm going to set my username, password, and host from some environment variables that Docker sets for me, and I will come back to this, I promise. So now I need a Docker file. And Docker file is just the list of things that need to happen to set up your environment. And I'm lazy, so I'm going to write a minimal Docker file. And I can do a Docker file for this application in three lines. Really, just three. The first line is telling me what image I want to start from. All Docker images, most Docker images are built from other images. So the Rails image is built from a Ruby image. The Ruby image is built from an operating system image, and turtles all the way down. I'm using the Rails image because it's really close to exactly what I want, and therefore I don't have to know anything about ops or how to install packages on an Ubuntu machine or anything like that if I don't want to. And then I'm also going to specify that my RailsMF is production because this isn't in the Rails image that I pulled. And then the CMD, this command line, is the entry point for my application. This is what happens once my container is built and my app needs to start up. And you'll see that I'm calling a shell script here. So let's see what's in that shell script, my init.sh shell script. And it's, again, three very simple lines. I'm doing an export of secret key base because Rails 4, bundle exec, dbcreate, and then I'm starting up Rails server. Yes, I'm using the default server. You could use Unicorn. You could use something else. Lazy. But there's nothing really complicated going on here. I hacked all this stuff together one night at Hack Night and not very long at all. So now let's build it. We need to build up our image with Docker. And an image, I've used that word a couple of times, is just a template for a container. So I'm going to build a Docker build, the dot on the end means use a Docker file in this directory. And this builds it. So let's see what that looks like. So that's done. And that went really, really fast. And I'm going to hand wave this a bit. One of the reasons it went really fast is the Docker caches all the intermediate steps in creating the image. So if I only change, each of those steps is a layer. And if I only change something at the very end in those last layers, I can use the cached versions of all the ones before that. Which means I don't have to repeat all of the steps, like pulling down packages from your package, from your distro's package management system. Or building gems from scratch if they have C extensions. I can skip all that if I'm not changing any of that stuff. And that means it builds really quickly. So now it's built. Let's run it. I'm going to run through these commands. I don't know what people's experience with Docker is in the audience, but I'm assuming that you have as about as much experience as I had three months ago, which is this much. So Docker run. First we're going to do the database component, because we need the database up and running before we start the web. So this command creates a new container named DB, and it uses the Postgres image to do that. These two lines are setting some environment variables for that Postgres container. And these set the password and username for the Postgres database. And all of this is documented in lovely detail on the GitHub page for the Postgres image. I'm following the instructions. It's lovely, especially if you're lazy like me. And then this dash D means we're going to run it demonized. So I'm not going to get the standard out. I'm going to let it run in the background and do its thing, and I'm not going to interact with the container in any way. And now I'm going to start the web server up. Again, we're going to run a container named web. We're using the Thagamizer to do image that I just showed how it built. Again, we're going to run it demonized. This line is mapping ports. That's what the P stands for. And what I'm doing here is I'm mapping port 3000 on my container to 3000 on the host machine. In that case, this case, it could be local host on a Linux box on my Mac. It's a Linux VM. And then this line is magic and lovely. So link makes it so that my containers can talk to each other. In this case, I'm linking the DB container that I just made to the container I'm starting up now, the web container. And I'm linking it with the alias PG for Postgres. What link does is it creates a secure tunnel between my web container and my database container. And it also puts a bunch of handy environment variables in my web container so that I can connect to the database container. And that Postgres username, Postgres password, and Postgres host address, all of that that was in the database.yaml file, those are created because I used this link flag on my Docker startup. So let's watch it. That's the database starting up. That's the web starting up. They're up. There's lots of ways to set up container. There's lots of ways to set up virtual machines for testing out your app. This is really, really fast. I'm going to grab the IP, throw it in a browser, be indecisive about which profile I want to use in Chrome, go to port 3000, the tasks collection, there it is. That's my app running. And in case it wasn't obvious, I got all of this stuff set up in less than 20 lines of code beyond the standard boilerplate that Rails New gives you. It's about five lines of code to change the gem file, five lines of code in the database yaml, three lines in the Docker file, and three lines in init. That's it. I don't know any ways that I can set up a Rails app to run on Ubuntu that are quite this easy. And there's a couple shell commands, but it still all comes in under 20 lines of code. So why would you use something like Docker? Why would you use containers? Some pros. For me, the biggest one is consistency. I hate hearing, but it works on my machine. I started my career in QA. I've heard that so many times. And I don't care if it works on your machine. I care if it works in production. And if you're using containers, your staging environment and your production environment are the same. They are the same image. They are the same OS with the same libraries, all the same versions. You don't have to worry about messing up your gem file slightly and getting the wrong, one minor version off in a way that messes up your app. The next talk, the one after mine, is going to talk about how you can also use Docker in development and possibly have exactly the same OSes and libraries and stuff in development. So there's none of this, but it works on my machine stuff. It either works or it does, everyone has the same experience. Speed. Containers start up really, really quickly. There was a video that came out from the Kubernetes team last week, two weeks ago, about them racing a Kubernetes cluster versus making a latte. It ended up being a neck and neck tie. But we're talking minutes. And I've worked on apps where we had Chef Build and Chef's Very Cool, but rebuilding all of our VMs from scratch took upwards of 15 minutes because of all the stuff we were having to build from source. And being able to start up a container very, very quickly is nice, especially when you want to do things like auto-scaling. And the caching makes it fast if you're only changing the last layers. Flexibility. If you've got a microservices setup or we're in the distributed computing track, containers let you change how the different processes sit on different hardware. So you have a VM you can say in development you want all the stuff to run on the same VM. And you want it to be split up in one way. In your staging environment, you want one of every machine on a separate VM. And then in production, you want five web servers and two database backends. And all of that can be set up pretty easily with a variety of container management tools. Portability. I talked to folks about why they do or do not use the cloud or why they roll their own or why they have their own hardware. And everyone's like, I need flexibility. I need to be able to move back and forward. And containers are great because you can run them on Linux. You can run them on a Mac. You can run them on Windows. You can run them on your personal machine. You can run them on data center hardware that your company owns. You can run them on a cloud provider. Most cloud providers support containers right now. You're never locked into specific hardware. You can move it anywhere. And it will behave the same. And then repeatability. Last year about this time was particularly bad if you had a DevOps as part of one of your hats you were wearing. You're patching rail security bugs. You're patching open SSL security bugs. And I was on a team that was doing DevOps at the time. And we had our client was a little bit nervous about us making OS level changes. Making changes to the OS libraries. Because we pushed code a couple of times a week. And that was all automated. And we had that down. But every time we had to do something lower level than that, we didn't have a process for that. And the nice thing about using containers is the process for, if you set it up this way, containers are flexible, you can make it so the process for updating your code is the exact same process for updating your OS is the exact same process for updating the C libraries you have. And so it's only one process. Everyone knows it and there's much less likely that you're going to mess it up. So there might be some cons. The biggest one is Yagney. You aren't going to need it. For some apps, and I'm not going to say this is most apps, but if you're doing a small proof of concept thing or your personal blog, maybe you don't need containers. Maybe the overhead of setting that up is too much for you. And you'd rather just use a platform as a service provider. That's fine. This is a very cool tool for people who can get benefit out of it. But I'm never going to say that any one tool is perfect for every application. And the other thing is that while containers have been around for a long time, I work at Google and Google has been running all of their internal stuff on containers for years. Docker is still only a few years old and some people are reticent about adopting stuff that's on the first half of the adoption curve. I am not one of those people. Y'all are at Ruby RailsConf. I assume y'all aren't those people. But if you work for some of those people who are a little bit reticent, you might have a harder sell on using a technology that's still new. But I think it's really cool. Otherwise, I wouldn't be doing this talk. So I've showed you some pretty cool stuff about running these containers on my Mac. But I don't think any of you actually run through production site off of MacBook Air. If you do, I have a dinosaur for you. We should come chat afterwards. So how do you manage containers in a cloud environment? How do you manage containers in a data center environment when you want multiple versions of something or you want five front-end web servers? And I'm going to talk about Kubernetes. There's lots of ways of managing containers. This is the one that I happen to know. And it's a Google project for managing clusters of containers. Basically the idea is that you build up a list of your desired state in a format that Kubernetes understands and then you start up Kubernetes on a group of VMs or bare metal machines and say, make it look like my desired state. And I want to point out specifically that Kubernetes is open source. There are more contributors outside Google than inside Google at this point. And all this is on GitHub. Very, very visible what's going on. So Kubernetes has a giant pile of vocabulary. I'm going to hit it up. I'm going to talk about it specifically up front because I'm going to use it and I'm going to forget what I have and have not defined. Master, this is the machine that manages everything. Masters have a set of minions. Usually there's several of them. They're running on different VMs. The underlying base unit is a pod and this is the smallest deployable unit. You can think of it as part of an application that intrinsically works together, not independently. And I asked some folks who work on Kubernetes, I'm like, so if you have a web application, is the web application plus the DB a pod or is the web app a pod and the database is a pod? And the answer is it depends. I'm going with web app and the database are going to be separate pods because I want to scale the two pieces independently. For the demo, I'm only going to have one database, but I'm going to have two front end web servers. But if you want to put them together, you can do that too. So a service is an abstraction for a logical set of pods, which is a lot of very fancy words to basically mean a named load balancer. And if I have multiple pods, I have a service that takes care of splitting the work up between them. And these are also how different pods talk to each other. Like we had links so that the database could talk to the web container on my local machine. We build services so that my database pods can talk to my web pods in the Kubernetes cluster. And then we have a replication controller. And this is responsible for ensuring that the correct number of pods are running at all times. We set up a replication controller, you say, this is my desired state. I'd like two replicas. And the replication controller is responsible for saying, are there two replicas? If one of those machines fails, it starts up another. If you somehow end up with extras, it'll shut one down. I want to also put some notes. This is currently in beta. The API may change. But people are using it. So warning, but it's still awesome. And one of the things I want to be very explicit about is that Kubernetes gives you options. This is open source. You can run this on your own hardware. You can run this on a cloud provider's VMs. Google Cloud Platform supports it. But there are others who are working with Kubernetes. There are other companies that work with Kubernetes. And this technology does not lock you into a specific provider. So that was a bunch of words. And I like giving technical talks with lots of code. So let's look at some code. This is a hand-wavy example of a Kubernetes configuration file. And this is not a specific one. But the basic format is similar for a lot of them. So you have an ID. You have a kind, which could be things like pod or service or replication controller. The API version, desired state. And that desired state is where you set up, this is what I want Kubernetes to maintain for me. And that has maybe a set of containers or something else. And then there's some labels. And these are just used so that you can gather together things that are related and refer to groups more easily. So for this example. Let's do the database first. So I'm going to have one database pod. And there's a bunch of ellipses on here where I pulled out a lot of extra braces because JSON on slides doesn't work so hot. But this is the pod configuration file for the database. And I'm specifically showing that it has a set of containers. And that's an array with one unit in it. The container is named DB. It uses the Postgres image. I can pass in my environment variables just like I did to Docker run. And then I can also do some ports. And I'm saying that I want to map my container port 5, 4, 3, 2 to the host port 5, 4, 3, 2. Standard Postgres port. And if I want other pods to be able to talk to this pod, I need to set up a service. And here's the service definition. IDDB, kind service. And again, I want the service port 5, 4, 3, 2 to be mapped to a container port 5, 4, 3, 2. And then the selector says what pods should be in this service. So any pod that's named database will be in this service. Really simple stuff. So now I've got my database set up. Let's do the web. I'm going to have a replication controller because I want two replicas. Again, pulled out a bunch of friends and curly braces and such. And this is actually a lot of stuff. So let's look at it in two pieces. The top part is the desired state for my replication controller. I want two replicas. The replicas that count toward this are the ones that are named web. And then I give a template for the replication controller to create pods. And that's what this template looks like. I've got a single container named web using the image thagamizer2do prod. And I'm passing in some environment variables. And I'm doing some port mapping. If I want to be able to access this set of pods with a replication controller, I need to have a service. Here's the service definition. I've got a selector saying anything that's named web is in this service. And I'm doing some port mapping again, specifically putting it on port 3000. So this is all great, but I haven't showed you anything running. So you can run all this, and the Google Kubernetes environment is Google Container Engine. It's in public alpha. And I'm going to show you how to get it running. So this is just something you run at the command line. The first three things, G Cloud Alpha Container is using the Google Cloud Platform command line interface. And I'm saying that I want alpha feature set, because this is alpha, and I want the container engine subset. It's the Kubernetes command line interface, and I'm going to say create from file, the DB pod.json. That's the DB pod file I showed you. And I'm going to make that. And then I can say, hey, Kubernetes, give me the list of pods. And I get back a list of pods in their state. And you can see that this one's running. You can see what image it is and what node it's been assigned to currently. Great. That's up. So now I can do the service. Again, create dash f. I can get from file for the DB service. And I can say, Qubectl, give me the services. And it gives me a list of the services and what ports they've exposed. Great. And I'll just create the web controller. Again, create from a file. And I can get the replication controllers and see that I have this web controller, the image that's being used, and how many replicas there are. And then I can go get the pods. And I can see the list of pods that's been started up and what their state is. And now I need to create my web service. And you'll notice that everything is create dash f. I'm just creating all of these from the standard configuration files. There's really not a lot that's complicated here. The first half of the line looks a little scary, but it's the same every single time. So I create my web service. I start it up and you can see that it's at port 3000. And then I'm going to do something crazy. I'm going to have Kubernetes resize my web replication controller. And I'm going to say I want five replicas now. Wait a couple seconds to a minute. And I have five running pods now. All I had to do was say how many I wanted it to happen. Because Kubernetes job is to say, what state do you want the cluster in? And I say, this is the state I want. And it's like, I will figure out how to make that happen. It pulls down images as it needs to. It creates new containers as it needs to. It divides them among the number of nodes. And you'll see that I've only got three hosts listed because the cluster I created only had three VMs in it. I didn't need to provision new VMs to have additional workers running. So at Mountain West RubyConf, Star Horan gave a great talk. And in his talk, he had a slide that said the lies I've told during this talk. So I would like to think that I haven't lied to you, but there are some large sections of this that I have hand-waved vigorously. And I'm going to call them out right now so that you guys don't have to ask questions about them when I open up the floor to questions. The first one is disk. I didn't show you how to set up the database to hit a persistent disk. So if you restart that database container right now, all your data goes away. You can do that. There's tutorials on how to do it. I also didn't show you how to set a shared disk between multiple web clients. There are tutorials on how to do that, too. This is a 101-level talk. That is not a 101-level concept. Security. I'm not going to say that what I showed you is good for production-level security. Your app has unique security concerns. Please do a security audit. Replication. I didn't show you how to set up a cluster of database machines that replicate across themselves. There are lots of tutorials for this. One of the standard Google Cloud tutorials uses Redis and has Redis Master and Redis Workers that shows how to do something like that. I kind of talked about this, but Docker runs on Linux machines only. To run it on a Mac, you need a tool called Boot to Docker that uses a virtual box to make a little Linux machine, a little Linux slice where you can run your Docker containers. It does it well, so I forget the fact that I'm not actually using Docker natively. There's tools for Windows that are the same. You can also just run this on Ubuntu. When I'm at work, I've got an Ubuntu desktop underneath my desk, a big tower, and I do most of my work as a station to that machine. And size. So, one of the things I found talking to folks who do containers and work with Docker a lot is that they're concerned about VM image size. And the VM, the images that I chose, the standard Rails image is huge. If you care about size, you can make your own by taking out the pieces you don't need. For example, I believe the standard Rails image has both Postgres database client libraries and my SQL client libraries. I was only using Postgres, so if I built my own, I could choose not to include the my SQL ones and save some space. But make the trade-off of your time versus how big an image is and figure out where your laziness actually lies on that. I was going for, you know, minimal effort here, which is why I chose those images. So, as I said, this is a 101 level talk. A lot of you in the audience have been nodding along, which means I probably didn't show you anything new. But if you want to learn more, first place to start is shipping RubyOps with Docker by Brian Helmkamp at red.rubyconf 18 months ago, a year ago. This is an awesome hour-long talk on exactly this topic, includes live code demos, because he is more daring than I. I don't do live code demos at a conference, because you never know about the Wi-Fi. If you're going to get into this, I recommend watching this talk first, because it will help you internalize the vocabulary. One of the things I struggled with learning this stuff from preparing this talk was that I would pull in a bunch of information and think I understood it and then get stuck. And I would unstick myself three days later once all the pieces sort of themselves out in my head. And I had the answer the whole time, but so much new vocabulary and so much dense documentation was making it hard for me to help myself. And once I watched this talk, I kind of got a big picture sense of how things were, and things made more sense. If you want to find out about Container Engine, cloud.google.com, container engine, doc, there are two demos, the, there's a simple hello world type demo, and then there's a guest book demo, and I use the guest book demo extensively when preparing this talk. If you want to learn about Kubernetes, that's the Kubernetes website. And if you want to talk to people who work on Container Engine and on Kubernetes, on FreeNode, go to the Google Container's room. The devs actively monitor this. No, really, they actively monitor this. You can talk to the people who are working on this and ask them very technical questions, and they will help you find answers. Google Cloud encourages folks to hit up Stack Overflow, make sure that your, it helps a lot if your question is tagged, so that the appropriate people who are monitoring those tags can answer your question for you. If you run into trouble, let me know, and I will make sure someone answers your question. And now time for the sales pitch part. I work on Google Cloud Platform. We have storage, we have VMs, we have App Engine, we have logging and monitoring tools, we have data analysis tools that are awesome, and I gave a talk about them on Western Ruby Conf. Data centers all over the world, Internet endpoints all over the world. All the stuff you'd expect for a cloud provider, and at least some of you probably didn't know that you could get a VM from Google before I said these words. And I want to point this out, because this is the really cool part for me, is I've been to a data center, and our cloud customers, their code runs on the same racks and the same physical hardware as YouTube, as Search. I can't tell in the data center what's running on any machine, so any work we do to improve the infrastructure for things like YouTube and Search benefits our cloud customers as well. We have a free trial, because you have a free trial. It does ask for a credit card, but you will not be billed unless you okay billing. The credit card is there for fraud protection and a bunch of other legal reasons. And if you have any trouble getting the free trial, or you've used your free trial and want to try something else, come chat with me. I have ways around that, but I encourage you to use the free trial, because that gives you $200 in credit for 60 days, and that's better than I can give you easily at this conference. So thank you. I want to thank the conference organizers for having me here, and for organizing this giant conference. I want to thank my coworkers who reviewed these slides for me, even though they're on the West Coast, and so I was sending them slides inopportune time zone times. When I gave conference talks, I have plastic dinosaurs. Someone told me this looks like just a random baggy stuff. There's actually a bunch of plastic dinosaurs in here that I give away to people who ask questions or come chat with me. I also have stickers up on the podium, right below the podium up here. Lots and lots and lots of stickers. Come get stickers from me. We've got about 10 minutes left, so I'm going to open the floor to questions.
mean specifically by lazy? I mean, doing the I can be lazy so that they'll maintain environment. And this takes me back to the old of tutorials out there for using Docker in Rails, machine or anything like that if I don't want to. this way, containers are flexible, you can make some labels. And these are just used so that you how many replicas there are. And then I can go Redis Workers that shows how to do something like and think I understood it and then get stuck. And