From 0ec4e74f8f31b6cbb53eddb5750692b2bf3e3c0d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 11 Apr 2025 10:46:33 +0200 Subject: [PATCH] wip: add nginx in docker-compose.yml --- Dockerfile | 6 +-- docker-compose.yml | 19 ++++++-- docker/nginx.conf | 111 ++++++++++++++++++++++++++++++++++++++++++ docs/changelog.md | 7 +++ umap/settings/base.py | 4 +- 5 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 docker/nginx.conf diff --git a/Dockerfile b/Dockerfile index d4df0f5c..a35602c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # This part installs deps needed at runtime. -FROM python:3.11-slim AS runtime +FROM python:3.12-slim AS common RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -13,7 +13,7 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # This part adds deps needed only at buildtime. -FROM runtime AS build +FROM common AS build RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -40,7 +40,7 @@ COPY . /srv/umap RUN /venv/bin/pip install .[docker,s3,sync] -FROM runtime +FROM common COPY --from=build /srv/umap/docker/ /srv/umap/docker/ COPY --from=build /venv/ /venv/ diff --git a/docker-compose.yml b/docker-compose.yml index ba958081..e66a7a8a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,10 +26,10 @@ services: condition: service_healthy redis: condition: service_healthy - image: umap/umap:3.0.0 - ports: - - "${PORT-8000}:8000" + image: umap/umap:3.0.2 environment: + - STATIC_ROOT=/srv/umap/static + - MEDIA_ROOT=/srv/umap/uploads - DATABASE_URL=postgis://postgres@db/postgres - SECRET_KEY=some-long-and-weirdly-unrandom-secret-key - SITE_URL=https://umap.local/ @@ -39,7 +39,20 @@ services: - REDIS_URL=redis://redis:6379 volumes: - data:/srv/umap/uploads + - static:/srv/umap/static + + proxy: + image: nginx:latest + ports: + - "8000:80" + volumes: + - ./docker/nginx.conf:/etc/nginx/nginx.conf:ro + - static:/static:ro + - data:/data:ro + depends_on: + - app volumes: data: + static: db: diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 00000000..fe73d2a6 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,111 @@ +events { + worker_connections 1024; # Adjust this to your needs +} + + + +http { + proxy_cache_path /tmp/nginx_ajax_proxy_cache levels=1:2 keys_zone=ajax_proxy:10m inactive=60m; + proxy_cache_key "$uri$is_args$args"; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + types { + application/javascript mjs; + } + + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + + # Server block + server { + listen 80; + server_name localhost; + + # Static file serving + location /static/ { + alias /static/; + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css; + expires 365d; + access_log /dev/null; + } + + # Geojson files + location /uploads/ { + alias /data/; + expires 30d; + } + + location /favicon.ico { + alias /static/favicon.ico; + } + + # X-Accel-Redirect + location /internal/ { + internal; + gzip_vary on; + gzip_static on; + add_header X-DataLayer-Version $upstream_http_x_datalayer_version; + alias /data/; + } + + # Ajax proxy + location ~ ^/proxy/(.*) { + internal; + add_header X-Proxy-Cache $upstream_cache_status always; + proxy_cache_background_update on; + proxy_cache_use_stale updating; + proxy_cache ajax_proxy; + proxy_cache_valid 1m; # Default. Umap will override using X-Accel-Expires + set $target_url $1; + # URL is encoded, so we need a few hack to clean it back. + if ( $target_url ~ (.+)%3A%2F%2F(.+) ){ # fix :// between scheme and destination + set $target_url $1://$2; + } + if ( $target_url ~ (.+?)%3A(.*) ){ # fix : between destination and port + set $target_url $1:$2; + } + if ( $target_url ~ (.+?)%2F(.*) ){ # fix / after port, the rest will be decoded by proxy_pass + set $target_url $1/$2; + } + resolver 8.8.8.8; + add_header X-Proxy-Target $target_url; # For debugging + proxy_pass_request_headers off; + proxy_set_header Content-Type $http_content_type; + proxy_set_header Content-Encoding $http_content_encoding; + proxy_set_header Content-Length $http_content_length; + proxy_read_timeout 10s; + proxy_connect_timeout 5s; + proxy_ssl_server_name on; + proxy_pass $target_url; + proxy_intercept_errors on; + error_page 301 302 307 = @handle_proxy_redirect; + } + location @handle_proxy_redirect { + resolver 8.8.8.8; + set $saved_redirect_location '$upstream_http_location'; + proxy_pass $saved_redirect_location; + } + + # Proxy pass to ASGI server + location / { + proxy_pass http://app:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_redirect off; + proxy_buffering off; + } + } +} diff --git a/docs/changelog.md b/docs/changelog.md index ef6e7f3f..5aca981b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -32,6 +32,13 @@ Other notable changes: Note: you may want to update your search index to include the category search, see https://docs.umap-project.org/en/stable/config/settings/#umap_search_configuration + +### Breaking change + +* The Docker image will not serve assets and data files anymore, an Nginx container must + be configured. See [docker-compose.yml](https://github.com/umap-project/umap/blob/master/docker-compose.yml) + for an example. + ### New features * add collaborative real-time map editing * add atomic undo redo by @yohanboniface in #2570 diff --git a/umap/settings/base.py b/umap/settings/base.py index 5faf21c1..6edb7fb4 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -164,8 +164,8 @@ LOGIN_REDIRECT_URL = "login_popup_end" STATIC_URL = "/static/" MEDIA_URL = "/uploads/" -STATIC_ROOT = os.path.join("static") -MEDIA_ROOT = os.path.join("uploads") +STATIC_ROOT = env("STATIC_ROOT", default=os.path.join("static")) +MEDIA_ROOT = env("MEDIA_ROOT", default=os.path.join("uploads")) STATICFILES_FINDERS = [ "django.contrib.staticfiles.finders.FileSystemFinder",