 My name is Ben Sherman. I'm a work for H.I.1 in Houston. I'm a Microsoft ASP Insider and MVP, a Certified Scrum Master, and I've sort of left the .NET community and have been doing full-time rails and iPhones since February. And I'm not at all affiliated with Heroku. I just really, really like their stuff. So let's say you have this awesome Rails app that you've been working really hard on, maybe by yourself in startup mode, you have a limited budget. But eventually you're going to need to deploy it. So some of your choices that you have in the deployment arena is shared hosting, virtual dedicated and dedicated. Anybody here use shared hosting in the past? So it's pretty great, you know, it's like five bucks. They'll put your app on the server. Of course that server costs money and they have to pay for the bandwidth of that server so they're going to put another app, probably another one. And before you know it, that server gets overloaded with applications and you're then competing for resources on a single box. So shared hosting kind of sucks. In addition, you're probably tied to one Ruby version and it's usually outdated. So if you want to run a Rails site on a shared host, you've got to get a good one. And so this is virtual dedicated. Virtual dedicated is a lot better because you're still sharing raw metal with other people that you're isolated and you can generally have a guaranteed amount of RAM. But of course it's a lot more expensive. You can spend $20, $30, $40 to start up on a virtual server. But they're a little bit more portable and you can scale them up. You know, if you need to grow your application, you can add more RAM for more money. And then there's dedicated which is awesome. Everybody likes computers but they're expensive. So then what about scale? They're like, how do you scale a website? Let's say we have our application and we chose one of those options. And we've got a database server running on the same machine as the web application and we've got a few users. So let's say our user base grows a little bit. We might decide to change off, move our database server off to the other tier to another box to leave the web server just for serving web requests. And of course, you know, hopefully our business will grow and we'll get more customers. And then all of a sudden we have another problem. We've got to add more servers. We have to do master slave on our database to send writes to the master and reads to the slaves. And then we probably need a load balancer to share the load between multiple web servers. So let's say we have this awesome problem of having lots and lots of customers. And we end up with this telescopic problem of we need lots and lots of servers and each one of those needs a redundancy. We need load balancers. We need sharding on our databases. So we send, you know, a certain number of records to this system, a certain one to this one and so on. What would happen if I were to say write something to the local file system on one of these web servers, right? Your web request, a subsequent web request might go to a different server so you can't rely on the fact that there's, you know, a local file system on any of these things. And server memory is another issue. You know, if you're storing session state and server memory, you got to watch out there as well. So if we started off on just one server, our own server, and we weren't sort of playing in those guidelines and we might have problems scaling up later. In addition, it takes a pretty good skill set of somebody to manage this infrastructure for you. And me as an application developer, and probably most of us in here, I know enough to get my hands dirty, but like somebody said earlier, I don't know how enough to get them clean again when it comes to IT admin. So I'm going to change the subject just for a minute and talk about scale and scale on demand. So if we go back to the slide for a minute, if all of these are our servers and we're paying for bandwidth and we're paying for the hardware, that's pretty expensive. And sometimes we don't need all of that all the time. For example, if we have a number of users by the time of day, you can easily see here that we need the scale during this period, but we don't necessarily need it during the hours of 9 p.m. to 9 a.m. Why not try and save some money there? Now the scenario is an expected PR campaign. We might get a sudden rush of users and it'll settle down to some reasonable value, but we don't necessarily want to blow our budget on servers just in case we might handle that amount of load. So wouldn't it be cool if we could just crank up a dial and then crank it down again and only pay for what you use? Wouldn't that be awesome? Yeah. All right, yeah. All right, so I want you to forget about servers for a minute and I'll talk about Heroku. So Heroku, you can install it as a gem. Gem install Heroku. It's a platform as a service for rack-based apps so you can serve up, you know, Sinatra or Rails or whatever. You can scale easily by adding what are called dinos. It has a really, really cool workflow which I'll show you in a minute. It's free to start, which is, I think, one of the most important things about a service like this. There's a lot of cloud offerings out there, but you need to be able to tinker with them. In fact, my first Rails website that I deployed into production, I put on Heroku and it lasted for about eight months on the free plan. That was eight months I did not have to pay, you know, $30 a month for a virtual host or $5 a month for a shared host or whatever. It's also got a complete command line API, which is what we'll use the most. So this is how you deploy a Rails app. So assuming you don't already have a Git repository, we're going to initialize one, add all the files, commit them, then you run Heroku Create, which gives us this nice Zen name of Morning Summer 18, and then we push it. And anybody here not familiar with Git? Okay, good. So you can probably tell that Heroku is just another remote on our system. So it can be the same Git repository that you commit to, your regular source code, whether you're on GitHub or wherever. And so we just push our changes to the Heroku remote and it puts our site online. So let that soak in for a second. We didn't deal with any servers. We didn't deal with any Capistrano recipes. I didn't know how to set up Nginx, Apache, Mongrels, any of that. Yay, big win. So I really want to stress that I really like using Git for deployment. I didn't have to SSH anywhere. I really, really think that's a fantastic solution. Okay, so Heroku is built on some common things that we're all familiar with. Amazon Web Services, EC2 specifically, caching layer. They do HTTP caching with varnish, which is really, really fast. So if you're serving up static pages, you get that for free. They run on Debian Linux. There's this Erlang routing mesh, which basically routes the request through the caching tier to whatever Dino your application is running on. And the web server's Nginx and the database that you'll be using is Postgres. So that's actually probably the most important thing to note is that you use Postgres on the server. And if anybody's ever used MySQL locally or SQLite locally and then deployed to Postgres, you might be in for some surprises, like, for example, case-sensitive like statements and some other just database-specific things. So I recommend running Postgres locally just so you have a consistent environment. So this is their pricing page, which among being just cool to look at and play with is pretty informative. On the left side, we have the database. Basically, you choose based on your size of database and whether you want shared database or dedicated. So in my case, on my first production Rails app, my database was less than 5 megs, so I was able to stay on that one for a long time. And on the right side where it says Dinos and Workers, you're given one Dino for free and you can scale up from there for pennies per hour. So to give some context, was it FlightCaster? Anybody familiar with FlightCaster? Okay, so for those that don't know, FlightCaster promises that they'll tell you when your flight is delayed before the airlines will and they're pretty accurate. So they gather tons of data and they serve up, you know, they have a website. They run on three dinos, so like a pretty big application is able to run on three dinos. So what the hell is a Dino? I didn't know. A Dino is sort of like a mongrel in that it will only serve up one concurrent web request per second. So in the free plan, similar to a grocery store, you know, you walk in and you've got this guy who's got cheese whiz and beer in front of you and he's going to pay with a check and use 10 coupons. You have to wait for him until they open up another lane. So these requests are going to come into the system and be served up one at a time. So if you get a really fat request in the system, it's going to delay everybody else. So we added Dino for money, then we get both of them at the same time. So we improve our throughput through the system. So how do you know when it's time to add, go from the free Dino plan to the, you know, adding more? Obviously, increasing response times. The person at the end of the line has to wait until they go through that backlog until he gets to a valid Dino, an open Dino. And you should be watching those. And then you might not be watching those, in which case if you get, you know, that million users that I was talking about earlier, you'll probably get this backlog to deep error, which means I don't remember how many or too many in the backlog, but it's like a hundred, which is a lot. And so at that point they're going to start throwing 500 errors because by the time you get to those requests, they're going to be so far gone, the browser might have timed out already anyway. So of course you should always measure so that you know what your performance is. And you can do this with a couple of tools. One of them is New Relic RPM, which I'm sure most of you have heard of. New Relic RPM is a plug-in with Heroku. So you basically just click a box, say, yes, I want it, and it's automatically installed. And you go through there and it'll show you how many requests per minute. And you can see in this fuzzy, you know, low-res diagram that we're getting, you know, a few requests per minute. And then all of a sudden the request per minute starts scaling up, right? We're getting more traffic. And we're handling it okay except the backlog is starting to grow. And that's what these yellow bars are. They're showing you your backlog depth or your queue depth. So in this case, during those periods we had a queue depth of one, one, and then it went up to two. So you can see as we get more web requests, the queues are going to get longer and longer. And unless we either get less traffic or we crank up our dinos, that number is going to start growing and eventually you're going to start throwing out 500 errors. So it's always important to measure to see what your performance is. You can use Apache Bench. It's probably already on your machine. So you can type AB and take a look at the docs for that. But basically you can run, say, you know, 40,000 requests, 20 at a time, and see the numbers that you get back from that. So some tips that I've come to use in my time with Heroku. You can use multiple Heroku apps in the same folder. So say I have an application, you know, my blog. And I want a blog's stupid example. My awesome randomizer for conference prizes. And I want a testing and staging and production environment. So you can just create a new Heroku app for each one of those. And when you do that, every Heroku command you just have to append with dash dash app in the app name. So where I work, when we deploy, we deploy to dash dash app, you know, app dash test or app dash staging or app dash production. And it'll go to that correct environment. Anybody in here use taps? Okay, for those that don't use it, do you know what it is? Okay, man, taps is so awesome. The creator of taps is actually here. I think he's preparing his keynote. Taps is a solution for serializing schema and data from a database in a database agnostic way and pushing that to another server. So Heroku aside, you can run taps as a server on both ends. Say you've got, you know, a linode and a slice host and you want to move a database that's MySQL over here to a database that's Postgres over here. You run taps on both sides and you can send off all of those records on a fresh database. It works really, really well. Heroku leverages this. So you can say HerokuDB push and HerokuDB pull and it will work with SQLite, MySQL or Postgres. So if you have some development data that you want to push out to your site on the internet, you just say HerokuDB push. So we do this, we take data out of our production environment, we pull it down locally and we push it up to our test environment. So we are always working with relevant data. Of course, stuff goes wrong every once in a while. So you can type Heroku logs and you'll get, like, the last 100 lines from your log. Of course, if errors come in, if you add the exceptional gem, you'll get all those exception, the stack traces and everything through the exceptional service. And there's Heroku console, which is just like your local Rails console, but on the server. And I already mentioned New Relic and Exceptional, but those are just two of the many add-ons so check, yes, I want this. And those are both free, by the way. Okay, so every one of us has probably been in a situation where we deploy something and it doesn't work, so we kind of want this oh shit button that we can back up to what we were before. And in the Heroku world, that's called a bundle. And for free, as an add-on, you can add a single bundle. So what that'll do is it'll take your database, you know, all your data, take a snapshot of your database and all your code and zip it up for you. And then later on, you can reanimate that bundle. And it's really useful, but not only having one bundle, it only gives you that one window. So you can add on other bundles for free, not for free, for money, to have unlimited bundles so that you can capture a bundle every time you do a deployment and you can always roll back to another one. But we're still on the free bundle plan and we've found a little way that we can work with this, with a rake script. So I have a backup task in a rake script. Can you guys read that okay? I'm going to highlight them as I go. So the first thing is to just set the name of the bundle and the Heroku app that we're using and I pass this in as a command line parameter. So I just say app equals in my app name. And this is just a function that's going to call the Heroku bundles command on the shell and then wait for the text that I specify because what will happen is I say when you capture a bundle, it's not going to be there like right away. So I've got to wait until the bundle is actually there. So I use that in a couple places so we just have a helper method here. So the first step is to check for an existing bundle because we're on the free bundle plan. We can't capture a bundle if there's already one in there. So what we do is we look for a bundle that's there. If there is one, then we destroy it and you'll see why in a second. Then we capture the bundle. So by running the Heroku bundles capture method or command, then we download the bundle and just put it in a local backups folder. You could also easily just use one of the Amazon S3 libraries on Ruby and put it in S3. That way you have a copy of it at any point in time. And then this is where we put that file. So you can't easily give Heroku a backup that you saved on disk and have them reanimate it. So it's still valuable to have the unlimited bundles planned but this is a way that we were able to cope with the free single bundle plan. And so then we do one button deployments with Heroku. Like this, we're going to do the nice highlighting on this one. So that first block, it turns maintenance mode on. So basically just your app changes to a static page. It says maintenance. And I believe that causes your slug to be recompiled on Heroku. So you can't do a git push immediately after setting your maintenance mode on. So we sleep for a few seconds. Then we do a git push to that remote. So if I'm deploying to test, we're going to use the test git remote. If we're deploying to production, we'll use the production git remote. Once those changes are there, you're going to need to migrate your database. And you can do that with Heroku rake db migrate. And then in some cases you'll have convict variables like s3 keys or chargeify keys or whatever. And so we'll loop through a hash there to put those up on Heroku. And then finally we'll turn maintenance mode off. So these were tasks that we had to do repeatedly every time we did a deployment. So we want to be able to do those a lot quicker. So delayed jobs is something that I just recently came across. So like I mentioned earlier, getting your dinos or your web requests as slim as possible is really key to leveraging the most out of the dinos that you have. So if you have some stuff that may not belong in the web request but has to be performed, you might push that off to a queue and have it run later. One example of that is sending bulk emails. In this case, there's really nothing we can do except tell the user that we sent them because that thing is going to take a while. Some of them might fail or whatever. But the user wouldn't really be able to do anything about it. So at this point they're probably waiting for a little while while we go fetch all the emails and we send them all and meanwhile we're plugging up that dyno from serving up other requests. So if you have delayed jobs, you can just change this instead of saying send a ton. You say send later and pass in send a ton as a symbol. That's all you do. It inserts that into the queue and then later on the worker is going to pick that up. So that way you immediately return from that method and are able to keep your web requests fast and responsive. So some other scenarios that might be useful is sending off metrics or analytics to a third party. To install delayed job, you just do gem install delayed job. That comes with a migration so you have to generate it. So you type RailsG delayed job. This is Rails 3 by the way. And then you do rakeDB migrate which will create the queue table for you. You can scale up your workers by saying Heroku workers and the number of workers you want. And you can also do relative numbers so you can set plus one minus one and it'll tell you how many is running. So I just found out this today that you charge five cents per hour per worker on the same slider thing on that pricing that page that I was showing you earlier. So I did the math and that's 36 bucks a month which is kind of hefty from like, okay I'm on the free plan and I need a worker and all of a sudden I'm at 36 bucks a month. It's kind of like a weird jump. You probably only need that job running every once in a while but you're going to be charged even when you're not processing the jobs. So I thought it would be really cool to write some magical script that would check the queue to see if you have any jobs to run. And then scale up if you do and scale down if you don't. Turns out somebody had already done this. I've shortened the URL there for you if you're interested. But it's basically Pedro's fork of delayed job. And what he did, which is pretty cool, oops I'll go back, he basically patched the job class so when it is created it checks the quantity and scales up. So if the manager, the manager is just some Heroku manager class that he wrote. And if it doesn't have any jobs and this is the first one then it's going to crank up a worker. And then in the worker class once it's finished it's going to scale down if that was the last job. So at this point like as soon as your queue is empty you're not charged for workers anymore. Which is a pretty elegant solution. I like that a lot. There are some gotchas. Like I mentioned there's a read-only file system. If you're going to write your application in a way that's scalable across servers it's not a good idea to write to a local disk. And because of that some gems don't work. Like for example paperclip. I think you have to change the temp directory where paperclip uses. Heroku does give you a temp directory that's writable but you obviously shouldn't store stuff there. It's temporary. Radiant CMS is another one that I had trouble getting working. And the reason was there was no Git sub modules support on Heroku. But there's ways to get around that by making those sub modules first class files So whatever. SSL and memcached and full text search are a little bit expensive I think. Aside from that though we're able to get up really quickly and we're happy to pay for the things that we end up needing like SSL recently and I can see us move into memcached pretty soon. And then the last thing is that they're bound by Amazon's SLA so by definition they're going to be a little bit less than Amazon's uptime. So that's the only gripe I really have is that we can't guarantee 9 nines of uptime because they're built on Amazon's infrastructure. Which does go down occasionally. So let's go ahead and do a little demo. We're done here at 4? Yes. Alright. So I've got this little Rails app here and it doesn't do a whole lot. In fact I'll show you. It's got a couple of controllers one for managing projects and she's just scaffolding so I can just go in here and type stuff up. And then it's got an issues controller to have issues for those projects. So I like this application and I'm going to put it up on Heroku. So... can I read that? Should I bump up the font size? Okay. So I'm going to create a new git repository add my files Okay. Now I'm going to say Heroku create and I can give it a name at the end or I can just hit enter and get a zen name created for me. Quiet Night 20. Alright. So now we have our git remote and that's going to be our URL. So I'm just going to push to that URL. Anybody timing me yet? All of this talk was a scalable Rails app deployed in 60 seconds and then all of a sudden I started using Rails 3 and Bundler and Heroku sort of switched off to using to where you specify all of your gems including rails and so that sort of cranked up the time it took to spin it up a little bit but pretty close anyway. Anybody have any questions while this is running? Oh it's a how does Heroku know what gems I have? So before Bundler you would use a .gems manifest file which just you just take out the Heroku docs and you basically just paste in the names of the gems in a file. Right. I think before it because it's not necessarily Rails specific so if you're running a Sinatra app for example you have to tell it before it boots up the Rails app. Okay. So Quiet Night 20 builds up on the internet. It's pretty impressive I think. Yay? Alright. So now I'm going to go add a project and oh wait something went wrong. Anybody know what went wrong? Ah you guys have done this before. Okay. So Heroku comes with a ton of commands in fact I'll just hit enter and show you. You basically control almost the entire thing from here. So what we need to do is Heroku rake db migrate and I did include the delayed job gem in this so that table should be created for us and I also added a generate action on my issues controller so that I could generate a bunch of issues just test issues and so it's basically like generate a hundred issues and you don't necessarily want the user waiting around for the web request to return. So now that that's done I can go over here and refresh this. Looks like it should have run. I didn't have to do this earlier. Alright let's take a look. Heroku log. This is all part of the talks by the way. Oh okay. Yeah. There it goes. Okay interesting that was weird. Okay. But my projects that I had nice and formatted on my local machine aren't there. So and it took me a long time to create both of those records. So what I'm going to do is just say Heroku db push which I don't have taps installed so let's do that gem install taps. Do you have a question or are you just going to tell me that? Jim? Okay. Any other questions while this gem is installing? Yeah. Yes I have like 25. So yeah. Yeah some of them I mean I think it's really great because I'm using it more most of the sites get zero traffic but it's just a way for me to keep my sites deployed and then some of them like my ones at work eventually we need bigger than a 5 meg database so we start paying for that and eventually we need SSL so we pay for that and in general you know if you were to set up like a slicehoster you know you're paying 30 something bucks a month for even the time during development where you don't need it. There are some small companies that are on a budget and should have not installed RI. Yes. Thank you. Yeah. Yeah because when you look at yeah when you look at your Heroku apps page well I'm not going to do it right now but yeah you'll see like a bunch of apps and then you don't remember what they were. Notes of self, no RDoC next time. Do you have a question? Right so the one bundle that's sitting on Heroku can be animated but I kind of want to have a history of all my backups locally just in case. So yeah that is dangerous obviously like oh we have a backup but we can't put it anywhere. So yeah I would talk Yeah so I could even show you if I have time but you'll so that's why we delete the old bundle and capture the new one and then at the end of the deployment if we broke everything I can just say Heroku bundles animate and then bundle name and it'll restore the old bundle with data and code. So it's just the last one that's the one I can restore and so I just saved them locally just in case I need them or if we need to put it on another server somewhere. That's smart Jesse where do you work? Yeah you had a question? Yeah the other does the other message say your slug is being recompiled? I would check status.heroku.com just to make sure that when you were having that problem that there were no issues so like all systems go right now which is good for my talk but yeah I mean like oh yeah yeah I mean occasionally we run into snags but I'd say the biggest snag that I've run into is I try to I turn maintenance mode on and then I immediately have to do a push and this is something recent I don't know if it's because this app is growing bigger and my slug size is bigger but these slugs are packaged up version of your app and in order to keep them portable I'm assuming that's what it is to keep them portable they have to compile the slug right? Okay so that's why I added a sleep in the script so that after I turn maintenance mode off for whatever reason it causes a slug recompile to wait like I said 15 seconds on here when I tried it this week it was more like 60 seconds for this one app okay I'm going to do this Heroku DB push and it's going to tell me that the database will be overwritten and sure why not? See a prompt hit yes enter and you can specify which database I'm just using the default so it's going to use my dev database and push it on to the production database on Heroku you know indexes all the data all the tables all the records and I refresh and then there so I have a question over here somewhere yeah you meaning get there's a slug ignore file okay that's yeah okay did everybody hear that if you have files that you don't necessarily need on the production server then you add a dot slug ignore file and it will be ignored okay so now we have our handcrafted projects that we wanted in our solution I'm going to go ahead and go to the if we go to issues I don't think there are any okay good and I type generate which is my you know batch run action that should be inserting this into a queue so if you notice it's not there yet it generated a hundred issues but they're not there yet and let me do this Heroku workers one and it's now running run worker one worker and I'm being charged five cents an hour yeah I left my worker running all night because I thought that it was going to not charge me while it wasn't running so there goes a couple dollars and at some point it should add yep there it goes alright so then you know my worker was the one who went in and added all these things and another cool thing to note is a delayed job will capture errors stuff the error message in the queue and then it will do exponential retry so when I was actually searching for how to get these things to retry it retried on its own and I had already fixed the bug and it worked but I'm out of time any last questions before I wrap it up there is log worm which is in private beta and I'm really thinking I might get in soon but yeah log worm you can basically send them all the logs and hopefully we can just have that for all of our applications just send them all the logs because when you do Heroku logs because running on multiple machines sometimes the log statements are like not in order and you'll get new relic type of logging statements in there so yeah and it's only 100 lines anyway so you gotta be there when the issue happened to run Heroku logs so that's a good question and I think there's some shirts up here how do you want to do that four shirts alright how do you want to do that run up here you, you, you and oh man we could but that was alright alright Scott come on up do you have a tiny shirt for Scott thanks a lot