Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Gunicorn example #80

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions gunicorn/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine)

ifeq ($(DEBUG),1)
GRAMINE_LOG_LEVEL = debug
else
GRAMINE_LOG_LEVEL = error
endif

.PHONY: all
all: gunicorn.manifest
ifeq ($(SGX),1)
all: gunicorn.manifest.sgx gunicorn.sig
endif

gunicorn.manifest: gunicorn.manifest.template
gramine-manifest \
-Dlog_level=$(GRAMINE_LOG_LEVEL) \
-Darch_libdir=$(ARCH_LIBDIR) \
-Dentrypoint=$(realpath $(shell sh -c "command -v gunicorn")) \
-Dpython_exe_path=$(realpath $(shell sh -c "command -v python3")) \
$< >$@

# Make on Ubuntu <= 20.04 doesn't support "Rules with Grouped Targets" (`&:`)
gunicorn.manifest.sgx gunicorn.sig: sgx_sign
@:

.INTERMEDIATE: sgx_sign
sgx_sign: gunicorn.manifest
gramine-sgx-sign \
--manifest $< \
--output $<.sgx

.PHONY: clean
clean:
$(RM) -rf *.token *.sig *.manifest *.manifest.sgx __pycache__

.PHONY: distclean
distclean: clean
41 changes: 41 additions & 0 deletions gunicorn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Gunicorn example

This directory contains an example for running Gunicorn in Gramine, including the Makefile and a
template for generating the manifest. Gunicorn is a webserver to deploy Flask application in
Python. Although, Flask comes with an internal webserver, this is widely considered to be not
viable for production. Common practice in production is to put Flask behind a real webserver that
communicates via the WSGI protocol. A common choice for that webserver is Gunicorn. Users can
protect their confidentiality and integrity of the Python based ML APIs and models using Gramine
for a more secure production deployment using Gunicorn. For more documentation, refer to
https://docs.gunicorn.org/en/stable/.

# Generating the manifest

## Installing prerequisites

Please run the following command to install Gunicorn and its dependencies on Ubuntu 22.04:
```
sudo apt-get install python3 python3-flask gunicorn
```

## Building for Linux

Run `make` (non-debug) or `make DEBUG=1` (debug) in the directory.

## Building for SGX

Run `make SGX=1` (non-debug) or `make SGX=1 DEBUG=1` (debug) in the directory.

# Running Gunicorn with Gramine

Here's an example of running Gunicorn under Gramine:

Without SGX:
```
gramine-direct gunicorn
```

With SGX:
```
gramine-sgx gunicorn
```
40 changes: 40 additions & 0 deletions gunicorn/gunicorn.manifest.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
loader.entrypoint = "file:{{ gramine.libos }}"
libos.entrypoint = "{{ entrypoint }}"

loader.log_level = "{{ log_level }}"

loader.argv = ["gunicorn", "--timeout", "600", "main:app"]

sys.enable_sigterm_injection = true

loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}"

fs.mounts = [
{ path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" },
{ path = "{{ entrypoint }}", uri = "file:{{ entrypoint }}" },
{ path = "/lib", uri = "file:{{ gramine.runtimedir() }}" },
{% for path in python.get_sys_path(python_exe_path) %}
{ path = "{{ path }}", uri = "file:{{ path }}" },
{% endfor %}
{ path = "/usr/bin/python3", uri = "file:/usr/bin/python3" },
{ path = "/usr/{{ arch_libdir }}", uri = "file:/usr/{{ arch_libdir }}" },
{ type = "tmpfs", path = "/tmp" },
]

sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}
sgx.enclave_size = "512M"
sgx.max_threads = 64

sgx.trusted_files = [
"file:{{ arch_libdir }}/",
"file:{{ entrypoint }}",
"file:{{ gramine.libos }}",
"file:{{ gramine.runtimedir() }}/",
"file:main.py",
{% for path in python.get_sys_path(python_exe_path) %}
"file:{{ path }}{{ '/' if path.is_dir() else '' }}",
{% endfor %}
"file:/usr/bin/python3",
"file:/usr/{{ arch_libdir }}/",
]

12 changes: 12 additions & 0 deletions gunicorn/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/hello', methods=['GET'])
def helloworld():
if(request.method == 'GET'):
data = {"data": "Hello World"}
return jsonify(data)

if __name__ == '__main__':
app.run(debug=True)