gunicorn
Server #
- tags
- Python , Python Apps
Gunicorn ‘Green Unicorn’ is a Python WSGI HTTP Server for UNIX.
- It’s a pre-fork worker model.
- The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.
basic configuration #
$ pip install gunicorn
$ cat myapp.py
def app(environ, start_response):
data = b"Hello, World!\n"
start_response("200 OK", [
("Content-Type", "text/plain"),
("Content-Length", str(len(data)))
])
return iter([data])
$ gunicorn -w 4 $ pip install gunicorn
$ cat myapp.py
def app(environ, start_response):
data = b"Hello, World!\n"
start_response("200 OK", [
("Content-Type", "text/plain"),
("Content-Length", str(len(data)))
])
return iter([data])
$ gunicorn -w 4 -b 127.0.0.1:8000 myapp:app
[2014-09-10 10:22:28 +0000] [30869] [INFO] Listening at: http://127.0.0.1:8000 (30869)
[2014-09-10 10:22:28 +0000] [30869] [INFO] Using worker: sync
[2014-09-10 10:22:28 +0000] [30874] [INFO] Booting worker with pid: 30874
[2014-09-10 10:22:28 +0000] [30875] [INFO] Booting worker with pid: 30875
[2014-09-10 10:22:28 +0000] [30876] [INFO] Booting worker with pid: 30876
[2014-09-10 10:22:28 +0000] [30877] [INFO] Booting worker with pid: 30877myapp:app
[2014-09-10 10:22:28 +0000] [30869] [INFO] Listening at: http://127.0.0.1:8000 (30869)
[2014-09-10 10:22:28 +0000] [30869] [INFO] Using worker: sync
[2014-09-10 10:22:28 +0000] [30874] [INFO] Booting worker with pid: 30874
[2014-09-10 10:22:28 +0000] [30875] [INFO] Booting worker with pid: 30875
[2014-09-10 10:22:28 +0000] [30876] [INFO] Booting worker with pid: 30876
[2014-09-10 10:22:28 +0000] [30877] [INFO] Booting worker with pid: 30877
using configuration file #
import os
print("gunicorn.config.py called")
workers = os.environ.get("GUNICORN_WORKERS", 2)
worker_class = "uvicorn.workers.UvicornWorker"
port = os.environ.get("GUNICORN_PORT", 8000)
def post_fork(server, worker):
print("post_fork called")
server.log.info("Worker spawned (pid: %s)", worker.pid)
from invoicing_apis.apm import initialize_tracing
initialize_tracing()
gunicorn -c gunicorn.config.py invoicing_apis.asgi:application
Adding OpenTelemetry for the APM #
ref using post_fork in the gunicorn.config.py and tracking there post_form ref 2 opentelemetry with gunicorn and asgi I used these configurations in invoicing-apis project.
import os
import MySQLdb
from opentelemetry import trace
from opentelemetry.exporter.zipkin.json import ZipkinExporter
from opentelemetry.instrumentation.celery import CeleryInstrumentor
from opentelemetry.instrumentation.django import DjangoInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.instrumentation.celery import CeleryInstrumentor
from opentelemetry.instrumentation.dbapi import trace_integration
from opentelemetry.instrumentation.django import DjangoInstrumentor
from opentelemetry.instrumentation.mysql import MySQLInstrumentor
APM_ENDPOINT_URL = os.environ.get("APM_ENDPOINT_URL")
APM_PUBLIC_KEY = os.environ.get("APM_PUBLIC_KEY")
apm_upload_endpoint_url = f"https://{APM_ENDPOINT_URL}/20200101/observations/public-span?dataFormat=zipkin&dataFormatVersion=2&dataKey={APM_PUBLIC_KEY}"
print("APM Endpoint URL:")
print(apm_upload_endpoint_url)
zipkin_exporter = ZipkinExporter(
endpoint=apm_upload_endpoint_url,
# Add other parameters as necessary
)
# Set up the tracer provider and processor
trace.set_tracer_provider(
TracerProvider(
resource=Resource.create(
{
"service.name": "invoicing-apis",
"service.instance.id": "instance-replace-with-env-var",
}
)
)
)
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(zipkin_exporter))
# DjangoInstrumentor uses DJANGO_SETTINGS_MODULE to instrument the project.
# Make sure the var is available before you call the DjangoInstrumentor.
def initialize_tracing():
trace_integration(MySQLdb, "connect", "mysql")
DjangoInstrumentor().instrument()
CeleryInstrumentor().instrument()
MySQLInstrumentor().instrument()
try:
from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor
print("using sqlite3 tracingg")
except ImportError:
print("NOT using sqlite3 tracing")
else:
SQLite3Instrumentor().instrument()
return trace.get_tracer(__name__)
gunicorn.config.py, above.
Commands to start the server #
venv_dir /bin/gunicorn -c ./server/gunicorn.config.py server.main:app -b 0.0.0.0: app_port -t 10000 --log-file /var/log/gunicorn/app.log --error-logfile /var/log/gunicorn/error.log --access-logfile /var/log/gunicorn/access.log --capture-output
Restart=on-failure
Running as a service #
[Unit] Description= app_name server After=network.target
[Service] User= app_user Group= app_user WorkingDirectory= repo_dir /backend
Environment=“PATH= venv_dir /bin” Environment=“APM_ENDPOINT_URL= APM_ENDPOINT_URL " Environment=“APM_PUBLIC_KEY= APM_PUBLIC_KEY " Environment=“OPENAI_API_KEY= OPENAI_API_KEY " Environment=“COHERE_GENERATIVE_AI_CONFIG_FILE_PATH= COHERE_GENERATIVE_AI_CONFIG_FILE_PATH " Environment=“OPENSEARCH_SEARCH_DOMAIN= OPENSEARCH_SEARCH_DOMAIN " Environment=“PDFS_DIR= pdfs_dir " ExecStart= venv_dir /bin/gunicorn -c ./server/gunicorn.config.py server.main:app -b 0.0.0.0: app_port -t 10000 –log-file /var/log/gunicorn/app.log –error-logfile /var/log/gunicorn/error.log –access-logfile /var/log/gunicorn/access.log –capture-output Restart=on-failure
[Install] WantedBy=multi-user.target