ソースを参照

explorer/site: run testnet and mainnet on WSGI server

This commit updates the explorer web app to run on a Gunicorn-based WSGI server for testnet and mainnet rather than the built-in dev server. In doing so, it provides for improved scalability, reliability, integration with Nginx, and flexible run configuration.

We’ve introduced a dedicated configuration file (gunicorn_config.py) allowing control over the number of worker processes and threads. By adjusting these values, we can handle more simultaneous requests, fine-tune performance based on CPU resources, and tweak other operational parameters to suit deployment needs.

Update Summary:
- Added gunicorn_config.py to configure bind address, workers, threads, pidfile, and logs
- Updated the testnet and mainnet ports to run on 8000 instead of the 5000 dev port
- Created wsgi.py to load and expose the Flask app via `create_app()`
- Updated requirements.txt to include Gunicorn dependency
- Modified makefile start-% target to start with Gunicorn for testnet/mainnet instead of the development server
- Adjusted stop task to stop Gunicorn server when stopping the explorer
- We updated the requirements.txt install to run quietly
- Updated Makefile to export LOG_HOME so it can be accessed by gunicorn_config.py
kalm 1 年間 前
コミット
5cd8c6c035

+ 26 - 21
bin/explorer/site/Makefile

@@ -1,7 +1,7 @@
 BIN := venv/bin/activate
 PYTHON := python
 
-LOG_HOME := $(shell echo ~/.local/share/darkfi/explorer_site)
+export LOG_HOME := $(shell echo ~/.local/share/darkfi/explorer_site)
 
 help:
 	@echo "Explorer Site Makefile Commands:"
@@ -24,47 +24,52 @@ $(BIN): requirements.txt
 	@if [ ! -d venv ]; then \
 		$(PYTHON) -m venv venv; \
 	fi; \
-	. venv/bin/activate && pip install -r requirements.txt
+	. venv/bin/activate && pip install -q -r requirements.txt
 
 # Remove virtual environment
 clean:
 	@rm -rf venv
 	@echo "Cleaned the virtual environment!"
 
-# Start the Flask server for the specified network (localnet, testnet, mainnet)
+# Start the server for the specified network (localnet, testnet, mainnet)
 start-%: install
 	@if [ "$*" != "localnet" ] && [ "$*" != "testnet" ] && [ "$*" != "mainnet" ]; then \
 		echo "Error: Unsupported environment '$*'. Supported values are 'localnet', 'testnet', and 'mainnet'."; \
 		exit 1; \
 	fi
-	@if [ -f flask.pid ]; then \
+	@if [ -f flask.pid ] && [ "$*" = "localnet" ]; then \
 		echo "Explorer site is already running (PID=$$(cat flask.pid)). Stop it first before starting."; \
 		exit 1; \
+	elif [ -f gunicorn.pid ] && { [ "$*" = "testnet" ] || [ "$*" = "mainnet" ]; }; then \
+		echo "Explorer site is already running (PID=$$(cat gunicorn.pid)). Stop it first before starting."; \
+		exit 1; \
 	fi
-	@. venv/bin/activate && \
+	@. venv/bin/activate && if [ "$*" = "testnet" ] || [ "$*" = "mainnet" ]; then \
+		FLASK_ENV=$* gunicorn --config gunicorn_config.py wsgi:app & PID=$$!; \
+		echo $$PID > gunicorn.pid; \
+		echo "Explorer site started on $* network (PID=$$PID)"; \
+		if [ "$*" = "testnet" ]; then \
+			echo "See site logfile $(LOG_HOME)/testnet/app.log for server startup details"; \
+		fi; \
+		if [ "$*" = "mainnet" ]; then \
+			echo "See site logfile $(LOG_HOME)/mainnet/app.log for server startup details"; \
+		fi; \
+	else \
 		FLASK_ENV=$* python -m flask run & PID=$$!; \
 		echo $$PID > flask.pid; \
-		echo "Started explorer site on $* network (PID=$$PID)"
-
-	@if [ "$*" = "testnet" ]; then \
-		echo "See site logfile $(LOG_HOME)/testnet/app.log for server startup details"; \
-	fi
-
-	@if [ "$*" = "mainnet" ]; then \
-		echo "See site logfile $(LOG_HOME)/mainnet/app.log for server startup details"; \
+		echo "Started explorer site on $* network (PID=$$PID)"; \
 	fi
 
-# Stop the explorer site if running
+# Stop the explorer sites that are running
 stop:
-	@if [ -f flask.pid ]; then \
-		PID=$$(cat flask.pid); \
-		kill $$PID; \
+	@if [ -f flask.pid ] || [ -f gunicorn.pid ]; then \
+		[ -f flask.pid ] && kill $$(cat flask.pid) 2>/dev/null || true; \
 		rm -f flask.pid; \
-		echo "Stopped explorer site"; \
+		[ -f gunicorn.pid ] && kill $$(cat gunicorn.pid) 2>/dev/null || true; \
+		rm -f gunicorn.pid; \
+		echo "Stopped running explorer sites"; \
 	else \
-		if [ "$(suppress_not_running)" != "1" ]; then \
-			echo "Explorer site is not running, nothing to stop."; \
-		fi; \
+		echo "An explorer site is not running, nothing to stop."; \
 	fi
 
 # Declare PHONY targets

+ 31 - 0
bin/explorer/site/gunicorn_config.py

@@ -0,0 +1,31 @@
+## explorer gunicorn configuration
+
+# Bind address and port
+bind = "127.0.0.1:8000"
+
+# Number of worker processes
+workers = 1
+
+# Number of threads per worker
+threads = 2
+
+# Type of worker class
+worker_class = "gthread"
+
+# Maximum number of pending connections
+backlog = 2048
+
+# Timeout for workers (in seconds)
+timeout = 30
+
+# PID file location
+pidfile = "gunicorn.pid"
+
+# Log configuration
+import os
+environment = os.getenv("FLASK_ENV", "testnet")
+log_home = os.getenv("LOG_HOME", "/tmp")
+log_path = os.path.join(log_home, environment, "app.log")
+accesslog = log_path
+errorlog = log_path
+loglevel = "info"

+ 2 - 1
bin/explorer/site/requirements.txt

@@ -1,3 +1,4 @@
 flask[async]
 Pygments
-tomli
+tomli
+gunicorn

+ 28 - 0
bin/explorer/site/wsgi.py

@@ -0,0 +1,28 @@
+# This file is part of DarkFi (https://dark.fi)
+#
+# Copyright (C) 2020-2025 Dyne.org foundation
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+"""
+Module: wsgi.py
+
+This module acts as the entry point for the Gunicorn server to run the Flask application.
+It initializes and exposes the Flask application created by the `create_app()` factory
+function from the `app` module.
+"""
+
+from app import create_app
+
+app = create_app()