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 all 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
41 changes: 41 additions & 0 deletions gunicorn/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (C) 2024 Gramine contributors
# SPDX-License-Identifier: BSD-3-Clause

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
48 changes: 48 additions & 0 deletions gunicorn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 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 applications 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. 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
```

Because these commands will start the Gunicorn server in the foreground, you will need to open
another console to run the client.

Once the server has started, you can test it with `curl`.

```
curl http://127.0.0.1:8000/hello
```
49 changes: 49 additions & 0 deletions gunicorn/gunicorn.manifest.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright (C) 2024 Gramine contributors
# SPDX-License-Identifier: BSD-3-Clause

loader.entrypoint = "file:{{ gramine.libos }}"
libos.entrypoint = "{{ entrypoint }}"

loader.log_level = "{{ log_level }}"

# We need `--timeout 600` to work around the problem of a constantly restarting worker process:
# the worker process is supposed to update the ctime of the shared file, and the parent process
# verifies that the worker process is alive by checking the ctime of this file every second.
# This ctime file metadata sharing between processes is currently not supported by Gramine;
# see also https://github.com/gramineproject/gramine/issues/1134.
loader.argv = ["gunicorn", "--timeout", "600", "main:app"]

sys.enable_sigterm_injection = true

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

fs.mounts = [
{ path = "{{ entrypoint }}", uri = "file:{{ entrypoint }}" },
{ path = "/gramine_lib", uri = "file:{{ gramine.runtimedir() }}" },
{% for path in python.get_sys_path(python_exe_path) %}
{ path = "{{ path }}", uri = "file:{{ path }}" },
{% endfor %}
{ path = "/usr/bin", uri = "file:/usr/bin" },
{ path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" },
{ 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.use_exinfo = true

sgx.trusted_files = [
"file:{{ entrypoint }}",
"file:{{ gramine.libos }}",
"file:{{ gramine.runtimedir() }}/",
{% for path in python.get_sys_path(python_exe_path) %}
"file:{{ path }}{{ '/' if path.is_dir() else '' }}",
{% endfor %}
"file:/usr/bin/",
"file:{{ arch_libdir }}/",
"file:/usr/{{ arch_libdir }}/",
"file:main.py",
]
16 changes: 16 additions & 0 deletions gunicorn/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (C) 2024 Gramine contributors
# SPDX-License-Identifier: BSD-3-Clause

from flask import Flask, jsonify, request

app = Flask(__name__)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False

@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)