Language and Framework
Deploy Python based apps
This page takes you through setting up instellar for a Django application. You will learn how to configure your application to run on your PaaS
Example Application
We’ve created an example application in case you wish to skip right to the source code.
A note on INSTELLAR_ENDPOINT
The examples in the codebase we referenced the .github/workflows/deployment.yml
file will contain a value for INSTELLAR_ENDPOINT
this is only necessary in those repositories since they’re deploying to our staging environment. For our customers we recommend omitting this value.
In this example we build python into a zipapp using a tool called shiv. It adheres to the PEP 441
main.py
In our example application you will notice a file in the monty
directory. It looks something like this. You will need a variation of this in your app. In our example we’re running the app using gunicorn. We also enable the migrate
command.
import os
import sys
import django
import gunicorn.app.wsgiapp as wsgi
from django.core import management
def run():
# setup django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "monty.settings.development")
django.setup()
match sys.argv[1]:
case "web":
# This is just a simple way to supply args to gunicorn
sys.argv = [".", "monty.wsgi", "--bind=0.0.0.0:8000", "-p server.pid"]
wsgi.run()
case "migrate":
management.call_command("migrate")
case _:
sys.stdout.write("please pass in a valid command [web, migrate]")
if __name__ == "__main__":
run()
instellar.yml
This file is used to describe to PAKman how to build your application. The following is taken from a Django based application. This configuration will change depending on your application. This file will work with a freshly generated django app.
Dependencies
Let’s begin with the dependencies
. You will also notice we’re enabling trace: true
in our dependencies. This will enable dependency tracing, which means even if we miss some dependency in our runtime
the build tool will trace the dependency and automatically include it in our package. That’s cool!
dependencies:
trace: true
build:
- python3
- py3-pip
runtime:
- bash
- curl
- jq
- s6
- python3
- py3-pip
- monty-openrc
- libpq
- ca-certificates
Choosing a Stack
We’re using alpine as the base image for all the containers that run the applications. We recommend using alpine/3.17
for all applications. However please check the alpine packages listing to see compatibility for your language and framework. In this case alpine/3.17 will run python 3.10.11
.
You can choose from the following:
- alpine/edge
- alpine/3.17
- alpine/3.16
- alpine/3.15
stack: alpine/3.17
Build
The below section tells PAKman how to actually build your application. In this case we’re simply exporting the $PATH
so our pip based binaries can be called. We then install shiv
and then install our application dependencies into the dist/
directory. We then copy the monty
, polls
, and manage.py
script into the dist/
directory. Now our dist
directory is ready for shiv
.
We call shiv with the --site-packages
option set to dist
. We add the --compressed
flag so shiv
will compress the build. We set the -p
flag to define the shebag that shiv will use to locate python. The -o
flag stands for output, shiv will build our application into a single executable binary as dist/bin/monty
. Finally we add the -e
flag, this defines the entry point for our application. We reference the run
function in the main.py
that we created earlier.
build:
destinations:
- dist/bin
command: |
export PATH=$PATH:$HOME/.local/bin
pip install shiv
pip install -r requirements.txt --target dist/
cp -r monty polls manage.py dist/
shiv --site-packages dist --compressed -p '/usr/bin/env python3' -o dist/bin/monty -e monty.main:run
Run
The below section explains how your application is run. You can see we have the ability to define commands
. These are commands you wish to run like migrations or seeds if you have them.
The services
section enables you to define how your application is run. How does it start? In this case we’re using the kill pid. In the above main.py
section we set the -p
flag to output the pid file in the same directory as our app, so we will use the same path for our pid_path
.
run:
name: monty
commands:
- name: migrate
binary: monty
call: 'migrate'
services:
- name: web
binary: monty
start:
call: web
Hooks
These are the lifecycle of the alpine packages. You can define their behaviour. Generally we don’t expect this to change from app to app. However feel free to adjust as required.
In this case rc-update add monty
will add our monty
app to the default run. This will ensure our app automatically starts when the container is restarted (incase it needs to restart).
The rc-service monty migrate
will run the migration. In this case it will run after the package is installed and after the upgrade.
rc-service monty start
will start the applicationrc-service monty stop
will simply stop the application
Your application will be built into openrc and supervised by the s6
supervisor suite.
hook:
post-install: |
rc-update add monty
rc-service monty migrate
pre-upgrade: |
rc-service monty stop
post-upgrade: |
rc-service monty migrate
rc-service monty start
post-deinstall: |
rc-service monty stop
rc-update del monty
Putting it together
The final file looks like the following:
dependencies:
trace: true
build:
- python3
- py3-pip
runtime:
- bash
- curl
- jq
- s6
- python3
- py3-pip
- monty-openrc
- libpq
- ca-certificates
stack: alpine/3.17
build:
destinations:
- dist/bin
command: |
export PATH=$PATH:$HOME/.local/bin
pip install shiv
pip install -r requirements.txt --target dist/
cp -r monty polls manage.py dist/
shiv --site-packages dist --compressed -p '/usr/bin/env python3' -o dist/bin/monty -e monty.main:run
run:
name: monty
commands:
- name: migrate
binary: monty
call: 'migrate'
services:
- name: web
binary: monty
start:
call: web
hook:
post-install: |
rc-update add monty
rc-service monty migrate
pre-upgrade: |
rc-service monty stop
post-upgrade: |
rc-service monty migrate
rc-service monty start
post-deinstall: |
rc-service monty stop
rc-update del monty