mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-09 11:28:14 +08:00
Merge pull request #168 from optuna/server-option
Add --server option for Gunicorn support
This commit is contained in:
+1
-1
@@ -40,7 +40,7 @@ You can use the Docker image like below:
|
||||
|
||||
```
|
||||
# SQLite3
|
||||
$ docker run -it --rm -p 8080:8080 -v `PWD`:/app -w /app optuna-dashboard sqlite:///db.sqlite3
|
||||
$ docker run -it --rm -p 8080:8080 -v `PWD`:/app -w /app sqlite:///db.sqlite3
|
||||
```
|
||||
|
||||
### Running dashboard server
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ FROM python:3.8-buster AS python-builder
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN pip install --upgrade pip setuptools
|
||||
RUN pip install --progress-bar off PyMySQL[rsa] psycopg2-binary
|
||||
RUN pip install --progress-bar off PyMySQL[rsa] psycopg2-binary gunicorn
|
||||
|
||||
ADD ./setup.cfg /usr/src/setup.cfg
|
||||
ADD ./setup.py /usr/src/setup.py
|
||||
@@ -32,5 +32,5 @@ RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["optuna-dashboard", "--port", "8080", "--host", "0.0.0.0"]
|
||||
ENTRYPOINT ["optuna-dashboard", "--port", "8080", "--host", "0.0.0.0", "--server", "gunicorn"]
|
||||
|
||||
|
||||
@@ -29,6 +29,23 @@ Listening on http://localhost:8080/
|
||||
Hit Ctrl-C to quit.
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Running optuna-dashboard with Gunicorn</summary>
|
||||
|
||||
optuna-dashboard uses [wsgiref](https://docs.python.org/3/library/wsgiref.html) module
|
||||
which is provided as a Python standard library. But it has not been reviewed for security
|
||||
issues, so not suitable for the production use. You can run optuna-dashboard with Gunicorn
|
||||
more secure and/or more fast.
|
||||
|
||||
```console
|
||||
$ pip install gunicorn
|
||||
$ optuna-dashboard sqlite:///db.sqlite3 --server gunicorn
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary>More command line options</summary>
|
||||
@@ -43,15 +60,32 @@ positional arguments:
|
||||
storage DB URL (e.g. sqlite:///example.db)
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--port PORT port number (default: 8080)
|
||||
--host HOST hostname (default: 127.0.0.1)
|
||||
--version, -v show program's version number and exit
|
||||
--quiet, -q quiet
|
||||
-h, --help show this help message and exit
|
||||
--port PORT port number (default: 8080)
|
||||
--host HOST hostname (default: 127.0.0.1)
|
||||
--server {wsgiref,gunicorn}
|
||||
server (default: wsgiref)
|
||||
--version, -v show program's version number and exit
|
||||
--quiet, -q quiet
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Python Interface</summary>
|
||||
|
||||
**`run_server(storage: Union[str, BaseStorage], host: str = 'localhost', port: int = 8080) -> NoReturn`**
|
||||
|
||||
Start running optuna-dashboard and blocks until the server terminates.
|
||||
This function uses wsgiref module which is not intended for the production use.
|
||||
|
||||
**`wsgi(storage: Union[str, BaseStorage]) -> WSGIApplication`**
|
||||
|
||||
This function exposes WSGI interface for people who want to run on the
|
||||
production-class WSGI servers like Gunicorn or uWSGI.
|
||||
|
||||
</details>
|
||||
|
||||
## Using an official Docker image
|
||||
|
||||
@@ -102,20 +136,6 @@ You can walk-through trials by filtering and sorting.
|
||||
|
||||

|
||||
|
||||
## Python Interface
|
||||
|
||||
### `run_server(storage: Union[str, BaseStorage], host: str = 'localhost', port: int = 8080) -> NoReturn`
|
||||
|
||||
Start running optuna-dashboard and blocks until the server terminates.
|
||||
This function uses wsgiref module which is not intended for the production
|
||||
use. If you want to run optuna-dashboard more secure and/or more fast,
|
||||
please use WSGI server like Gunicorn or uWSGI via `wsgi()` function.
|
||||
|
||||
### `wsgi(storage: Union[str, BaseStorage]) -> WSGIApplication`
|
||||
|
||||
This function exposes WSGI interface for people who want to run on the
|
||||
production-class WSGI servers like Gunicorn or uWSGI.
|
||||
|
||||
## Submitting patches
|
||||
|
||||
If you want to contribute, please check [Developers Guide](./DEVELOPMENT.md).
|
||||
|
||||
+44
-2
@@ -1,6 +1,8 @@
|
||||
import argparse
|
||||
import os
|
||||
from typing import NoReturn
|
||||
|
||||
from bottle import Bottle
|
||||
from bottle import run
|
||||
from optuna.storages import BaseStorage
|
||||
from optuna.storages import RDBStorage
|
||||
@@ -11,9 +13,38 @@ from .app import create_app
|
||||
|
||||
|
||||
AUTO_RELOAD = os.environ.get("OPTUNA_DASHBOARD_AUTO_RELOAD") == "1"
|
||||
SERVER_CHOICES = ["wsgiref", "gunicorn"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
def run_wsgiref(app: Bottle, host: str, port: int, quiet: bool) -> NoReturn: # type: ignore
|
||||
run(
|
||||
app,
|
||||
host=host,
|
||||
port=port,
|
||||
server="wsgiref",
|
||||
quiet=quiet,
|
||||
reloader=AUTO_RELOAD,
|
||||
)
|
||||
|
||||
|
||||
def run_gunicorn(app: Bottle, host: str, port: int, quiet: bool) -> NoReturn: # type: ignore
|
||||
# See https://docs.gunicorn.org/en/latest/custom.html
|
||||
|
||||
from gunicorn.app.base import BaseApplication
|
||||
|
||||
class _Application(BaseApplication):
|
||||
def load_config(self) -> None:
|
||||
self.cfg.set("bind", f"{host}:{port}")
|
||||
if quiet:
|
||||
self.cfg.set("loglevel", "error")
|
||||
|
||||
def load(self) -> Bottle:
|
||||
return app
|
||||
|
||||
_Application().run()
|
||||
|
||||
|
||||
def main() -> NoReturn:
|
||||
parser = argparse.ArgumentParser(description="Real-time dashboard for Optuna.")
|
||||
parser.add_argument("storage", help="DB URL (e.g. sqlite:///example.db)", type=str)
|
||||
parser.add_argument(
|
||||
@@ -22,6 +53,12 @@ def main() -> None:
|
||||
parser.add_argument(
|
||||
"--host", help="hostname (default: %(default)s)", default="127.0.0.1"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--server",
|
||||
help="server (default: %(default)s)",
|
||||
default="wsgiref",
|
||||
choices=SERVER_CHOICES,
|
||||
)
|
||||
parser.add_argument("--version", "-v", action="version", version=__version__)
|
||||
parser.add_argument("--quiet", "-q", help="quiet", action="store_true")
|
||||
args = parser.parse_args()
|
||||
@@ -33,7 +70,12 @@ def main() -> None:
|
||||
storage = RDBStorage(args.storage)
|
||||
|
||||
app = create_app(storage)
|
||||
run(app, host=args.host, port=args.port, quiet=args.quiet, reloader=AUTO_RELOAD)
|
||||
if args.server == "wsgiref":
|
||||
run_wsgiref(app, args.host, args.port, args.quiet)
|
||||
elif args.server == "gunicorn":
|
||||
run_gunicorn(app, args.host, args.port, args.quiet)
|
||||
else:
|
||||
raise Exception("must not reach here")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user