In a Nutshell

Serverless computing is the atomization of cloud computing into units of functions, which are usually endpoints. Then, instead of getting billed for the servers that host your application 24/7 (or when your servers are on), you get billed for the execution time of those functions.

Migrating to a Stateless Architecture

You can ‘cheat’ on the statelessness by linking to databases that hosts your state(s) - this can make sense if your state isn’t something that changes too frequently.

  1. Atomize an app into (mostly stateless) functions.
  2. Host on a platform of your choice, such as AWS Lambda or GCP Functions.
  3. Profit!

Advantages

  • Pay only for the execution time of your application.

Disadvantages

  • Security concerns of hosting servers outside of your premisis.
  • It might make more economic sense to use entire servers 24/7 depending on your application.
  • Not all languages are supported on all platforms, although new technologies such as wasmer are promising to offset this particular disadvantage.

Practical Example

Let us build a very simple application: a password generator.

Requirements

  1. Easy to remember.
  2. Easy to type.
  3. Difficult to guess.
  4. Difficult to brute-force.
  5. Meets requirements of modern passwords; a mixture of
    • uppercase letters,
    • lowercase letters,
    • special characters, and
    • numbers.

Solution

Use xkcd-ish passwords.

To generate xkcd-ish passwords, we need a dictionary of simple words. Luckily, the Wordle game craze meant many such suitable dictionaries popped up in the open-source world. Let’s use MikhaD’s Wordle dictionary.

# words.py

WORDS = ["cigar", "rebut", "sissy", ...] # copied from MikhaD's dictionary

Now we will use this dictionary to generate a password in the <Word1><Delimiter><Number><Delimiter><Word2> format:

# passgen.py

import secrets
import string
import words

def passgen(word_count, delimiter, contains_number, title_case):
    # do sanity checks here
    base_pass = [secrets.choice(words.WORDS) for _ in range(word_count)]
    if title_case:
        base_pass = [x.title() for x in base_pass]
    if contains_number:
        pos = round(word_count / 2.0)
        base_pass[pos:pos] = [str(secrets.randbelow(100))]
    return delimiter.join(base_pass)

For other miscillaneous functionalities, we group them into a single module.

# utils.py

def str_to_bool(string):
    if string.lower() in ["true", "t", "1"]:
        return True
    if string.lower() in ["false", "f", "0"]:
        return False
    raise ValueError(f"Could not parse '{string}' to bool type.")

Deployment

AWS Lambda

You need to have an AWS account for this!

  1. Go to the AWS Lambda Dashboard.
  2. Click on Create Function.
  3. Enter a function name, such as ‘generatePassword’.
  4. Choose Python as the runtime.
  5. Click on Create Function.
  6. Under Advanced Settings, make sure ‘Enable function URL’ is ticked and the Auth type is set to ‘NONE’.

Caution: this makes your function callable by anyone; you will be billed for each call!

If you have followed all the instructions correctly, you should be presented with a screen showing your function’s URL and an IDE to add or edit your code.

Using said IDE, copy and paste the codes we created earlier into the IDE, and edit the special file called lambda_function.py as follows.

# lambda_function.py

from passgen import passgen
from utils import str_to_bool

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": {
            "password": passgen(
                word_count=int(event.get("word_count", 2)),
                delimiter=str(event.get("delimiter", "-")),
                contains_number=str_to_bool(event.get("contains_number", "True")),
                title_case=str_to_bool(event.get("title_case", "True")),
            )
        },
    }

Your folder structure should now look like this:

.
└── generatePassword
    ├── lambda_function.py
    ├── passgen.py
    ├── utils.py
    └── words.py

The module called lambda_function is the default entrypoint module and a the function with the signature lambda_handler(event, context) is the default entrypoint.

Here, event is a dictionary containing the request parameters; in our case these are:

  • word_count, an integer to indicate how many words will be used.
  • delimiter, the string delimiter between words.
  • contains_number, a boolean indicating whether or not a number will be used.
  • title_case, a boolean indicating whether or not the words will be in title case.

Refer to the documentation for more information on context.

We return a dictionary of two keys:

  • statusCode, set to 200 because we have successfully executed our function, and
  • body, the response payload. In addition to the password, information such as the password difficulty in terms of entropy could be given.

Once done, click on Deploy. After a few seconds, your function should be deployed and visiting function URL using your browser should print a password.

GCP Functions

You need to have a GCP account for this!

  1. Go to the GCP Functions Dashboard.
  2. Click on CREATE FUNCTION.
  3. Set the environment to 2nd gen and enter a function name, such as ‘generatePassword’.
  4. Under Trigger, make sure the trigger type is HTTPS, and you are allowing unathenticated invocations.
  5. Click on Next.
  6. Choose Python as the runtime and set Entry point to generate_password.

Caution: this makes your function callable by anyone; you will be billed for each call!

Using the IDE given, copy and paste the codes we created earlier into the IDE, and edit the special file called main.py as follows.

# main.py

import functions_framework
from passgen import passgen
from utils import str_to_bool

@functions_framework.http
def generate_password(request):
    return {
        "password": passgen(
            word_count=request.args.get("word_count", default=2, type=int),
            delimiter=request.args.get("delimiter", default="-", type=str),
            contains_number=request.args.get("contains_number", default=True, type=str_to_bool),
            title_case=request.args.get("title_case", default=True, type=str_to_bool),
        )
    }

Your folder structure should now look like this:

.
├── main.py
├── passgen.py
├── requirements.txt
├── utils.py
└── words.py

The module called main is the default entrypoint module and we set the function with the signature generate_password(request) as the default entrypoint.

Here, request is a Flask Request structure containing the request parameters; in our case these are:

  • word_count, an integer to indicate how many words will be used.
  • delimiter, the string delimiter between words.
  • contains_number, a boolean indicating whether or not a number will be used.
  • title_case, a boolean indicating whether or not the words will be in title case.

We return the response payload as a dictionary. In addition to the password, information such as the password difficulty in terms of entropy could be given.

Once done, click on DEPLOY. After a few seconds, your function should be deployed and visiting function URL using your browser should print a password.

Congratulations: you’ve just created your first serverless application!