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

Fix docker image build #26

Merged
merged 1 commit into from
Nov 28, 2023
Merged
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
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
.dockerignore
.gitignore
.git
.github
.env
.pylintrc
__pycache__
*.pyc
*.egg-info
.idea/

.vscode
4 changes: 2 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Test project outside docker
PYTHONPATH=/app/
DEBUG=0
DJANGO_SETTINGS_MODULE=config.settings.test
DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_SECRET_KEY=t3st-s3cr3t#-!k3y
ETHEREUM_NODES_URLS=https://ethereum.publicnode.com,https://polygon-rpc.com
ETH_HASH_BACKEND=pysha3
PRICES_CACHE_TTL_MINUTES=60
6 changes: 4 additions & 2 deletions .env.test
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Test project outside docker
PYTHONPATH=/app/
DEBUG=0
DJANGO_SETTINGS_MODULE=config.settings.test
DJANGO_SECRET_KEY=t3st-s3cr3t#-!k3y
ETHEREUM_MAINNET_NODE=https://ethereum.publicnode.com
ETHEREUM_NODES_URLS=http://localhost:8545
ETH_HASH_BACKEND=pysha3
PRICES_CACHE_TTL_MINUTES=60

# Only required for testing
ETHEREUM_MAINNET_NODE=https://ethereum.publicnode.com
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@ Returns fiat prices for base currencies and ERC20 tokens.

## Configuration

Environment variables:
```bash
cp .env.sample .env
```

Configure environment variables on `.env`:
- `DJANGO_SECRET_KEY`: **IMPORTANT: Update it with a secure generated string**.
- `ETHEREUM_NODES_URLS`: Comma separated list of the node RPCS for the chains supported for fetching prices.
- `PRICES_CACHE_TTL_MINUTES`: Minutes to keep a price in cache.

## Execution

```bash
docker compose build
docker compose up
```

Then go to http://localhost:8000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that we are mixing configuration and execution here, could we separate them in different sections?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right



## Contributors
[See contributors](https://github.com/safe-global/safe-price-service/graphs/contributors)
3 changes: 0 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ services:
dockerfile: docker/web/Dockerfile
env_file:
- .env
depends_on:
- db
- redis
working_dir: /app
ports:
- "8888:8888"
Expand Down
2 changes: 1 addition & 1 deletion docker/web/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ COPY --chown=python:python . .
# Use numeric ids so kubernetes identifies the user correctly
USER 999:999

RUN DJANGO_SETTINGS_MODULE=config.settings.production DJANGO_DOT_ENV_FILE=.env.tracing.sample python manage.py collectstatic --noinput
RUN DJANGO_SETTINGS_MODULE=config.settings.production DJANGO_DOT_ENV_FILE=.env.sample python manage.py collectstatic --noinput
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that here should be .env instead .env.sample.

Copy link
Member Author

@Uxio0 Uxio0 Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, as this is only a placeholder as it needs a file to exist, so it doesn't matter which one, but good catch

3 changes: 0 additions & 3 deletions docker/web/run_web.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,5 @@ rm -rf $DOCKER_SHARED_DIR/*
# STATIC_ROOT=$DOCKER_SHARED_DIR/staticfiles python manage.py collectstatic --noinput &
cp -r staticfiles/ $DOCKER_SHARED_DIR/

echo "==> $(date +%H:%M:%S) ==> Send via Slack info about service version and network"
python manage.py send_slack_notification &

echo "==> $(date +%H:%M:%S) ==> Running Gunicorn... "
exec gunicorn --config gunicorn.conf.py --pythonpath "$PWD" -b unix:$DOCKER_SHARED_DIR/gunicorn.socket -b 0.0.0.0:8888 config.wsgi:application
24 changes: 24 additions & 0 deletions gunicorn.conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from config.gunicorn import (
gunicorn_request_timeout,
gunicorn_worker_connections,
gunicorn_workers,
)

access_logfile = "-"
error_logfile = "-"
max_requests = 20_000 # Restart a worker after it has processed a given number of requests (for memory leaks)
max_requests_jitter = (
10_000 # Randomize max_requests to prevent all workers restarting at the same time
)
# graceful_timeout = 90 # https://stackoverflow.com/a/24305939
keep_alive = 2
log_file = "-"
log_level = "info"
logger_class = "safe_price_service.utils.loggers.CustomGunicornLogger"
preload_app = False # Load application code before the worker processes are forked (problems with gevent patching)
# For timeout to work with gevent, a custom GeventWorker needs to be used
timeout = gunicorn_request_timeout

worker_class = "gunicorn_custom_workers.MyGeventWorker" # "gevent"
worker_connections = gunicorn_worker_connections
workers = gunicorn_workers
27 changes: 27 additions & 0 deletions gunicorn_custom_workers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import gevent
from gunicorn.workers.ggevent import GeventWorker


class MyGeventWorker(GeventWorker):
def patch_psycopg2(self) -> bool:
try:
from psycogreen.gevent import patch_psycopg

patch_psycopg()
self.log.info("Patched Psycopg2 for gevent")
return True
except ImportError:
self.log.info("Cannot patch psycopg2 for gevent, install psycogreen")
return False

def patch(self):
super().patch()
self.log.info("Patched all for gevent")
self.patch_psycopg2()

def handle_request(self, listener_name, req, sock, addr):
try:
with gevent.Timeout(self.cfg.timeout):
super().handle_request(listener_name, req, sock, addr)
except gevent.Timeout:
self.log.error("TimeoutError on %s", req.path)
3 changes: 0 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
cachetools==5.3.1
django==4.2.6
django-cors-headers==4.3.0
django-db-geventpool==4.0.1
django-debug-toolbar
django-debug-toolbar-force
django-environ==0.11.2
Expand All @@ -13,8 +12,6 @@ drf-yasg[validation]==1.21.7
gunicorn[gevent]==21.2.0
hexbytes==0.3.1
packaging>=21.0
psycogreen==1.0.2
psycopg2==2.9.9
requests==2.31.0
safe-eth-py[django]==6.0.0b7
web3==6.11.1