So You Want to Deploy Your Web App - a Goal Oriented Introduction
Everything you need to know, to start understanding the 'why' behind the 'why does this stuff seem so complicated'.
You can whip up a cool web app on your developer machine, but once it’s time to launch it, you feel stuck. There seem to be a lot of different tools out there. You heard that Docker is what people use to deploy stuff - so you started looking into it, but where to start? Does it have to be that friggin’ hard? Kubernetes seems to be really popular right now, but is it right for you?
Wow this stuff looks complicated. Well, guess the app has to sit on your local machine forever then, right?
Nope. You’re just jumping into details, tech and tactics before having a good overview. Developing a web app is a skill you had to learn step by step. Deploying web apps, is pretty similar. The only difference is, it can seem even more complex and daunting when getting started.
Everything falls into place, once you stop trying to find the right tool, but decide on what your goal is and work your way backwards from there.
Let’s start with the end in mind, and everything will fall into place. You have created a basic web app and want to launch it so others can check it out. The goal is to have your app ‘online’, so other people can start using it.
That’s a good start. See how there are no tools involved yet? Not even a mention of Docker - whoops. Ignore it please.
We’re not going to deal with tools until the basics are in place. Let’s continue approaching this thing from an abstract perspective. You probably already know about the following stuff, but please bear with me.
To have the app available to other people, means that they type the domain name in their browser, or click a link and see the response in their browser.
To make this possible, you usually buy a domain from a registrar, and point it to an IP. What happens under the hood, when accessing that domain, is finding out that IP, send a request there using the HTTP protocol, a black box receives the request, produces a response and sends it back to the user. You knew all of this, but the black box part is what you care about right now.
The Black Box
The black box, is a deployment of your application. Disregarding the exact technical ‘how’, it’s your code which is executed somewhere and produces a response for each request which makes sense. Your code is there already, but the deployment thing around it is what you want to get right.
What’s the conceptually most simple way to deploy an app? Well, you need a computer on which it runs - a server. Imagine your deployment as a single process, which runs on the server, receives HTTP requests which arrive at port 80 directly and handles them. Only, it would be completely unreasonable to do it this way.
This could be one single program, but for many practical reasons (security, efficiency, not reinventing the wheel each time you code up a new app) there’s usually multiple ones working together and making sure that responses are answered correctly and in a timely manner.
Web applications as we know them, have a lot of parts which are basically the same across nearly all of them. Take the task of serving static content.
If a browser requests a .css, .js or image file, it expects to receive the content in a response. Those are static files, and serving them is such a frequent problem, that it has been served very well by applications which developed for a long time, and made really good at serving static content - web servers. Nginx if a great example of an web server and one I like to use, but there’s others of course.
There’s really no point in writing your own web server functionality, if you can just use one which works out of the box and can be configured. They also do other common tasks (reacting to simple requests, doing some basic parsing, redirecting traffic according to fixed rules), in a similarly efficient manner. The idea is, that we want to do stuff as efficiently as possible to do more with less cpu/memory in less time, to save developer work and to prevent silly mistakes by providing well engineered and tested functionality. Neat!
With a web server in place, your app is responsible for handle the custom stuff, which is specific to your project. The web server makes sure that requests which are meant for it, arrive at your application and forwards the results back to where they are expected. Depending on your language of choice, there’s a hidden layer left to be aware of. Namely more boilerplate-functionality which can be taken care of for you.
Your app usually needs to be able to process several requests at nearly the same time. If there’s only one process running which is not made for it, that’d mean you need to handle the process of accepting stuff in a waiting queue at any time, continuously process incoming requests with multiple threads in parallel and make sure the results arrive at the web server. In Python, you usually have an something like Gunicorn (only for Python apps), which handles all of this busywork for you. Depending on the language you used for your app, there’s similar stuff or the language/framework has it built-in in production quality. Just tell it where to find your app, and it will take care of having 4 workers ready which are working on new requests together. This way the site does not slow down if there’s more traffic. The stuff around your app which takes care of this and your app together are the application server. The thing which is passed requests by the web server.
In a proper production setup, application servers are “behind” web servers (only available “through” them, not directly), as those handle the simple tasks and make sure that the processing-intensive app servers only get to work with the requests where their attention is needed. This functionality of a web server, is called a “reverse proxy”.
One confusing detail might be, that the application server and web server may appear as the same thing in some setups. Take Apache and PHP for example - they’re great together, and work as one would expect out of the box. You just don’t realize what’s going on internally, because you don’t have to make it work.
There’s a bunch of guidelines which are popular and considered industry-standard because they prevent some ugly problems, if the apps are designed to play by those rules.
One of them is, that apps usually don’t have to save data internally over long periods of time. That’s what databases are for. So, apart from your web server and app server, you usually have a database sitting somewhere, where the app can access it to pull data out, save new stuff and forget about everything until it’s needed.
Similar to the case above, it’s just easier to build something specifically for handling and saving data very well, than to build a database yourself for each project.
Of course, there’s many other possible backing services (short-term memory caching, queues, object stores) which sometimes come in handy, depending on your application. Having those makes it simpler to create applications that do what they’re supposed to do with high probability. It also makes deploying them more complex, as it’s lots of moving parts. Having backing services around and available is part of a deploying an app. At least you usually have a database of choice (PostgreSQL, MySQL) which you can use.
By now we have one server, with a web server, an application server and a database. When starting out, this setup is completely acceptable and will probably be enough. It still needs maintenance (installing updates when needed so security issues are resolved, making sure your app isn’t down for some reason, checking reports about bugs in your app which you might want to fix), but a good place to start.
Deploying Your App
Now, a brief detour into tools. You don’t actually need to set all of this up yourself, or deal with growing pains as your app grows and gets more users (more on that below). There’s great solutions to getting your app online, without having to learn all the details of deployment. Because all of this stuff takes effort, there’s services which make sure your app is deployed, but take care of all kinds of stuff for you. Heroku for example, is a great service to make sure your app is easy to deploy and taken care of. AWS has got CodeStar, which takes care of a lot for you, but might be a bit overkill for simple projects.
If you want to set up your own server, check out Digital Ocean, Linode or AWS EC2 and get started.
But take heed, this is just the simplest way of deploying a web application, good for small projects which don’t deal with important data. There’s a lot of technical skills you’ll need to acquire and even more things you should learn.
Stuff That Can go Wrong
Here are the reasons, why a single-server solution becomes impractical when web apps grow and get more users. There’s things which we don’t want to happen, which will cause the web app to be unusable, either because it’s broken, very slow, or completely unavailable. Sometimes, if the application codebase gets really complicated and lots of people are working on it, that’s a reason for more complicated deployment strategies and more tooling as well.
This is the reason complicated tools and architectures exist. They deal either with those problems or problems caused by dealing with those problems.
When starting out, most of this is not the end of the world, depending on your app. When it’s big enough or your application is important enough, you have to make sure those things don’t happen.
Let’s go over a few of the most prominent ones, what causes them and what people do to deal with them.
The server goes down - well, you just have multiple servers running. When one goes down the others take over.
There’s too many services running on the server and it’s totally messy - containers got you here. Tech like Docker makes it possible to ‘package’ your application using code, and have it run on a machine without messing with anything else on it, or being able to be messed with. Also, you can take the container and run it on a different server. The container won’t care and you’ll have to configure it once, instead of once on each new machine.
There’re too many services running on the server and it needs to be huge - you have multiple machines, each running one kind of application, so you can pick the exactly-right size of disk, cpu power and memory for each. Those servers talk to each other. A frequent split is having backing services, like a PostgreSQL database on single machines, the web server on one, and the app server on one.
Lots of people are using your app - vertical or horizontal scaling is the solution. Either you run a bigger server (vertical), or you build your app in such a way, that many small machines can process all the work independently. Load balancers come in here.
Lots of people are using your app all of a sudden - this is why scaling up and down on demand exists, and why cloud providers are popular. Need 20 more machines? They’re just a click away, and something like immutable infrastructure makes it easy to run them in no time at all.
Bad things happen to the place your server(s) are located - think of a truck hitting the actual data center. That’s why high availability exists - you run multiple servers in different places which can fill in for each other and are very unlikely to go down at the same time.
Performance issues - you either need to optimize your app, or use external services to improve the architecture and take care of heavy lifting or complexity.
Having many developers working on the app - They get in each other’s way. Having automated processes in place to make sure that the next thing to be deployed will work is what people use to solve this. Before a change from a developer is accepted, there’s automated tests which need to run. This happens often, so Changes developers have worked on in parallel are merged together as frequently as possible - continuous integration. Add automatic deployment of versions which pass the quality tests, and you get continuous delivery.
If it’s lots of developers and the really huge application does lots of things which can be decoupled, web applications are sometimes splitting it up into microservices - lots of small applications which can be developed independently and deployed without getting in the way of everybody else.
That’s Hard Problems
And it’s unlikely that you have to solve all of them right away when starting out. In the beginning, it’s completely acceptable to start small and work your way from there. I hope this overview has provided you with a good understanding of what deploying web apps is about, and why it can seem hard when diving right in.