The Python PaaS-travaganza (This Old Pony #104)
After many moons and maybe even an election cycle or two (depending where in the world you live), the Python PaaS-travaganza is finally here[0].
In this edition I'll put some parameters around what we mean by "platform as a service", add a touch of history, cover some of the criteria under which using a PaaS makes sense, and introduce a few of the major players including a basic Python web app deployment which can be used out of the box[1] for your own comparison!
If you're uninterested in the "what" or the "why" of PaaS deployment and only interested in the Python deployment comparison, skip to the end. The GitHub repository is linked there.
Deployment, PaaSt and future
PaaS - or platform as a service - is a way of deploying web applications such that the overwhelming majority of hosting and deployment concerns are managed directly by the hosting service. Instead of spinning up a virtual private server and installing your necessary software over SSH, or perhaps using an IaaS provider (infrastructure as a service, e.g. AWS, Google Cloud, etc) to deploy a fleet of machines running Kubernetes, you push your app code to the platform and et voilà your web app is running.
The PaaS concept was born in the 2000's and while not the first PaaS platform, Google's App Engine was the first publicly available Python PaaS[2]. With some modification to your model definitions you could deploy a Django application using the "cloud datastore" without ever having to so much as run an update on system packages.
Next came Heroku, arguably the first truly popular PaaS service, though with an initial focus solely on Ruby and Rack (the Ruby WSGI equivalent). Later Heroku added support for Python and Java, and more platforms emerged, including ep.io[3] and dotCloud[4]. That early landscape has since change (like everything else in tech), with the emergence of open source PaaS frameworks, the widespread adoption of containers, and container orchestration.
By why? Or, tradeoffs all the way down
With that brief history out of the way, it's worth considered what problems all of these solutions solve. Because assessing platforms as a service in 2023 is begging the question that they're still relevant.
Not every PaaS solves the same set of problems, but roughly they include:
- Automated releases
- System package management
- Scaling
- Application configuration (i.e. settings)
- Security
- Insomnia (I kid, kind of)
Releases
The big selling point for a lot of PaaS solutions was that you could just deploy with a git push
command. E.g. Heroku's original and default method of deployment is a Git repository to which you can simply push your latest changes. What I think is important here isn't the mechanism for triggering the deployment but everything underneath. It means you don't have to set up and test Fabric commands[x] managing release directories and you get fairly easy rollback.
System package management
The more traditional "ops" tasks are a bigger deal, in my opinion. You no longer need to worry about ensuring your system's packages are up to date with the latest security patches, for example, because that's something the underlying platform manages.
Scaling
Scaling with traditional VPS is not trivial. You can deploy another instance of a web server, and once it's up and running register it with a load balancer, and ensure to tear down in reverse if you want to scale down. We're not talking rocket science here, but at the same time this is something that needs to be set up and managed. With typical PaaS you can change the sizing of your "servers" or the number with a single button or CLI command.
Application configuration
By this I mean the variables that change from deployment to deployment (e.g. Django secret key, API keys, database connection information). In a traditional VPS deployment you might have these kept in a non-source controlled file, such as a "local settings" file or an encrypted secrets file read at server restart. Typical PaaS give you access to a separate interface for these settings, often both from a web console and CLI/API.
Security
Deploying with a PaaS service does not a secure deployment make, rather, the argument here is that for many of the security concerns of a deployed web application you're better off delegating that responsibility to a larger platform. This includes the aforementioned security patching but also things like access to the running application, access to configuration, etc.
Insomnia
I said I'm kidding, but really one of the values of a PaaS is being able to get sleep at night because you have one or a dozen fewer things to worry about. It's not the case that PaaS gives you 100% uptime or that the APIs will never break. You will see a console go down and you will have to wait an hour for an internal service to get fixed before you can issue a new deployment. But ideally these things are rare enough and fixed quickly enough by teams focused only on those things that you and your team can focus your energy on your application.
Opportunity costs and the tradeoff matrix
And that's really the kicker. Using a PaaS is not quite the technical decision in the same way that picking a database or a framework is. That's not to minimize the other factors, e.g. social factors, that go into those decisions, but they tend to be more focused on the technical benefits and their merits as tools for the particular job. When it comes to any individual aspect mentioned above - scaling, security, etc - you can arguably achieve better by implementing "directly", managing it yourself.
And further, the provider costs will almost certainly be lower, often massively lower. You'll have control over every aspect of your environment and deployment, likely with better performance. But you do have to take on the management of all the initial and ongoing operations responsibilities.
Every technical decision has tradeoffs, and the key tradeoff benefit for using PaaS is economic: it's seeing ops as the opportunity cost of spending time on stuff that isn't your product, that isn't product development that isn't delivering value to customers, and paying a higher upfront hosting cost and giving up some control to focus on shipping.
Lol wut, VPS?
A lot of the earlier examples were based on VPS deployment, and a "lol wut!?" would be a natural reaction to that. While I have no statistics about deployment, and doubt such stats' availability, the zeitgeist has long been been focused on deploying with containers and container orchestration. That solves some of the aforementioned problems! But like they say about regex[x]...
The thing about Kubernetes is that it's still a thing you have to manage and spend time and attention on.
That “fully managed” bit is a bit misleading, though. All the raw and gnarly bits of understanding everything about how Kubernetes services work for your application is 100% still your problem.[x]
Not only that, but using Kubernetes begs the question that your application architecture is that complicated.
Simple deployments in brief
We're going to look at only five Python-supporting PaaS vendors. I qualified the list thusly: (a) I had to know about them or find out about them, (b) they had to have fairly out of the box support for deploying Python apps, and (c) they had to be production friendly. By that I mean it had to be a platform on which you could reasonably be expected to run a production web application with a standard relational database. There are other services that let you deploy Python apps but did not seem geared toward that kind of support (e.g. Python Anywhere) and others which are close required just enough configuration that they seem more like really "friendly" infrastructure as a service components (e.g. AWS Elastic Beanstalk). They are, in no special order:
- Heroku
- Render
- Platform.sh
- Railway
- Fly.io
You can follow the directions per-service in the python-deployments-hello-world repository. It includes a very simple FastAPI application such that you can get a single WSGI process running with minimal configuration fuss and no external services.
I'll delegate most of the "how to" to the repository README, and include a brief overview of the deployment story per-service below, as well as some discussion of the various tradeoffs per service. This ensures that the how-to can be a bit more evergreen and keep all the technical documentation out of this progressively longer email.
The biggest shift since I first created that example app is that most if not all of the aforementioned services have gotten rid of their free tiers. It is still very inexpensive to test out the apps and play with a toy app, but even if you incur no charges you will probably still need a credit card on file to deploy anything.
Heroku
Heroku's default deployment is Git-based and uses Buildpacks behind the scenes. Practically what this implies two things for a simple Python app: (a) adding a file named Procfile
which defines how to run a web
process and (b) a root-level requirements.txt
file for installing dependencies with pip
.
In this case, Heroku will correctly identify this as a Python app and use Heroku's Python Buildpack to create the correct environment for your app, including installing specified dependencies. You can do all kinds of other things like specify alternate or multiple Buildpacks (e.g. add Javascript to build JS assets and an Nginx buildpack to control request proxying if you so wish).
Render
Render is a little different insofar as it does not work off its own Git repository, but from pushes to a linked GitHub or GitLab repository. Deployment requires a few extra trivial steps, including setting the runtime (e.g. "Python") in the application environment, but once set up subsequent deploys work based on a git push
.
Beyond this excessively manually configured (IMHO) deployment style, you can also configure one or more services using a YAML file. This permits not only encoding how an individual service should be deployed (e.g. it's build command(s) and run commands), which in and of itself is valuable, but orchestrating services in conjunction, including data stores, worker processes, multiple public facing web services, and private-only web services as well.
Platform.sh
Platform.sh was one of the earliest (first?) providers to introduce configured service orchestration. It requires a tiny bit more upfront configuration than the Heroku example, including at least a root YAML file, however deployments are managed in the same way as Heroku, by pushing to a Platform.sh specific Git repository.
Railway
Railway and Fly.io (below) are newer than the previous three entries and also seem to have the highest velocity of product updates, based on frequency of updates to their CLI tools and significant changes to their platforms. Originally a Buildpacks-based service, Railway now uses their own system of "Nixpacks" which effectively means defining build and deploy in a root level TOML file.
Deploying is a matter of using the CLI tool to kick off a deployment. Alternatively, link your GitHub repository to the Railway service and subsequent pushes will be deployed.
Fly.io
Fly.io's "killer app" is globally distributed deployment. Your individual app can be deployed and distributed to data centers around the world, which for your scenario is probably either highly valuable or not at all. Like Railway, Fly.io relies on a TOML file to define the application build and deployment parameters. And it also uses a Procfile
to define the web process itself.
Conclusions, next steps
In no small part due to the length of this edition I'm going to leave out my own thoughts about each service and spare the reader from any more editorializing (for now!). I would LOVE to hear about your experience with any of the above, from VPS deployment to Kubernetes to one of the mentioned PaaS platforms - or one I missed.
Restedly yours,
Ben
[0] Not only was the original concept a bit ambitious, some of the services changed, necessitating fixes to the deployment app, and then life intervened, as it is wont to do, but we are here now.
[1] "Out of the box" is the goal. I can guarantee that the deployment worked for each service at least once!
[2] As far as I know.
[3] I recall deploying one app on ep.io and it was rather for the time. Long since defunct, it was founded by Andrew Godwin of South and Django migrations fame.
[4] If recollection is correct, dotCloud was a convenient deployment experience. In a massive pivot, they turned their underlying deployment technology into a separate open source product and renamed the company after that product: Docker.
[5] From Robert Roskum's "To Kubernetes or not to Kubernetes"