Ministry of Silly Runtimes: Vintage Python on Cloud Run
Tags: python, serverless, googlecloud, cloudrun
Published: 2019-04-09 16:06:57

---
title: Ministry of Silly Runtimes: Vintage Python on Cloud Run
published: true
description: Or, "How I made my own serverless Python 1.x runtime with Cloud Run"
tags: python, serverless, google cloud, cloud run
---

## Introducing Cloud Run

Today, Google announced [Cloud Run](http://cloud.google.com/run), a product that is really beautiful in its simplicity:

1) You write an service/app/function/whatever that listens for HTTP requests on `$PORT`;
2) You write a `Dockerfile` that inherits from any base image, installs any necessary dependencies, and starts your service;
3) You deploy the image to Cloud Run, and it scales on demand (even to zero).

Basically, it's "serverless Docker containers as a service" and it's exactly what I think the PaaS/FaaS development community could use right now, for a number of reasons:

**The most idiomatic runtime possible**
There's no potential for vendor-specific weirdness here. Really, you can't get more idiomatic than using `FROM python:3.7`, or whatever other base image you want to use. 

**Customizability without paying for idle virtual machines**
If you have specific workloads that don't fit within the bounds of the most "common" runtimes usually provided, before now your best option was to spin up a custom VM which won't scale to zero and will spend a lot of time sitting idle. With Cloud Run, you can use the `Dockerfile` to customize your runtime to your heart's content. 

**Containerization means portability**
Defining your runtime with a standard `Dockerfile` means you can take it anywhere—_even locally_—which means that you're not locked into a specific vendor, and that  local development of your application is built right in without needing to do anything special.

These are all great, and they'll certainly help developers do great things...

...But what if you wanted to do something really _silly_ with it instead?

## Introducing the first Python 1.x serverless runtime

When I first started playing with Cloud Run, I really wanted to test the limits of what I could do with it. Could I really run _any_ runtime I wanted? Could I run a super modern Python 3.8.0a3 alpha release? (Yes). Well, what about a "vintage" version of Python? Could I deploy a "Hello World" app running on Python 1.x to Cloud run? 🤔

#### Searching for "vintage" images

The first problem was finding a base image for Python 1.x. The [official Python Docker images](https://hub.docker.com/_/python/) only publish images for 2.7 and up, so no luck there. I couldn't find it anywhere else, so it looked like I'd have to build the base image from scratch myself.

#### Searching for "vintage" source

Building the base image from scratch meant that I had to:

a) Find the old source code for a Python 1.x version;
b) Get it to compile on a modern distribution's base image;
c) Publish this new base image.

Finding the source was slightly challenging. The source for modern Python releases is published on https://www.python.org/downloads/source/, but this only goes back as far as Python 2.0.1, released June 22, 2001.

Luckily, I found https://legacy.python.org/download/releases/src/, which includes source releases as far back as Python 1.0.1, which was released just over 25 years ago on February 15th, 1994.

I decided to attempt to target the latest Ubuntu distribution, and amazingly, with just some small patches, I was able to get Python 1.0.1, 1.1, 1.2 and 1.3 to compile under Ubuntu 18.04.

#### Publishing some "vintage" images

I published these resulting builds as Docker images under the [`vintage-python`](https://hub.docker.com/r/dustingram/vintage-python) repository, so this means you can now start a vintage Python REPL whenever you want:

```
$ docker run -it dustingram/vintage-python:1.0.1
Python 1.0.1 (Feb 23 2019)
Copyright 1991-1994 Stichting Mathematisch Centrum, Amsterdam
>>> print "Hello world!"
Hello world!
```

It also means I can do `FROM dustingram/vintage-python:1.0.1` in my `Dockerfile`, which is a huge step towards having my silly runtime.

#### Searching for an HTTP server

I had my heart set on Python 1.0.1, but after realizing that Python didn't ship with an HTTP server until Python 1.3, I decided to use that instead of attempting to backport the HTTP server to an older version.

In Python 1.3, I was able to write this:

```python
import sys
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler

class MyHandler(SimpleHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write("Hello from Python " + sys.version)


if __name__ == '__main__':
    print('Running...')
    HTTPServer(('0.0.0.0', 8080), MyHandler).serve_forever()
```

This sets up a basic HTTP/1.1 server on port 8080 that responds to every request with the given message. I _really_ wanted to prove that this was a vintage version of Python serving the request, so I included the `sys.version` along with the response.

#### Containerizing some vintage Python

After saving that as `app.py`, all that was left to do was write the `Dockerfile`. Since my app doesn't have any dependencies, the `Dockerfile` is pretty close to [the `Dockerfile` in the Cloud Run quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy)

```dockerfile
FROM dustingram/vintage-python:1.3

# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . .

# Service must listen to $PORT environment variable.
# This default value facilitates local development.
ENV PORT 8080

# Run the web service on container startup.
CMD ["python", "app.py"]
```

Here, we inherit from my vintage Python image, copy in my `app.py` to the container, set the `PORT` environment variable (for local development), and then start the server by running `python app.py`.

#### Deploying some vintage Python

With Cloud Run, there's a two step deploy:

Build and submit your image with Cloud Build:

```
$ gcloud builds submit --tag gcr.io/my_project_id/helloworld
```

Then, deploy the submitted image to Cloud Run:

```
$ gcloud beta run deploy --image gcr.io/my_project_id/helloworld
```

And then it was deployed!

See it in action here: https://helloworld-bpzdnjbrsq-uc.a.run.app/

## Conclusion

[Cloud Run](http://cloud.google.com/run) is definitely flexible enough to run any runtime I could throw at it -- even one of the oldest versions of Python. Obviously I wouldn't seriously encourage anyone to use Python 1.x in production: any version past it's EOL is essentially unmaintained and severely feature-limited.

From here, you can:
* See the [`vintage-python` Github repo](https://github.com/di/vintage-python) for more details on the runtimes;
* Use the [`vintage-python` images](https://hub.docker.com/r/dustingram/vintage-python) in your own silly project;
* See the [Cloud Run quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy);
* [Follow me on Twitter](https://twitter.com/di_codes).