diff --git a/.env.sample b/.env.sample index d83a7bfdbf1..1775ebaac70 100644 --- a/.env.sample +++ b/.env.sample @@ -11,6 +11,9 @@ C_FORCE_ROOT=1 FORCE_REINIT=false INVOKE_LOG_STDOUT=true +# Linux user that nginx, django and geoserver will run as +GEONODE_UID=0 + # LANGUAGE_CODE=it-it # LANGUAGES=(('en-us','English'),('it-it','Italiano')) @@ -236,7 +239,7 @@ LDAP_GROUP_PROFILE_MEMBER_ATTR=uniqueMember # CELERY__OPTS="--without-gossip --without-mingle -Ofair -B -E" # CELERY__BEAT_SCHEDULE="/mnt/volumes/statics/celerybeat-schedule" # CELERY__LOG_LEVEL="INFO" -# CELERY__LOG_FILE="/var/log/celery.log" +# CELERY__LOG_FILE="/var/log/geonode/celery.log" # CELERY__WORKER_NAME="worker1@%h" # PostgreSQL diff --git a/Dockerfile b/Dockerfile index c808fc00711..9cc3d3bee64 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,13 @@ -FROM geonode/geonode-base:latest-ubuntu-22.04 -LABEL GeoNode development team +ARG GEONODE_BASE_IMAGE=geonode/geonode-base:latest-ubuntu-22.04 +FROM ${GEONODE_BASE_IMAGE} + +ARG GEONODE_VERSION=master + +# Some label best practices +# https://www.docker.com/blog/docker-best-practices-using-tags-and-labels-to-manage-docker-image-sprawl/ +LABEL org.opencontainers.image.title="GeoNode" \ + org.opencontainers.image.version=${GEONODE_VERSION} \ + org.opencontainers.image.vendor="GeoNode Development Team" # copy local geonode src inside container COPY . /usr/src/geonode/ @@ -11,16 +19,15 @@ WORKDIR /usr/src/geonode #RUN touch /var/log/cron.log #RUN service cron start -COPY wait-for-databases.sh /usr/bin/wait-for-databases -RUN chmod +x /usr/bin/wait-for-databases -RUN chmod +x /usr/src/geonode/tasks.py \ - && chmod +x /usr/src/geonode/entrypoint.sh - -COPY celery.sh /usr/bin/celery-commands -RUN chmod +x /usr/bin/celery-commands - -COPY celery-cmd /usr/bin/celery-cmd -RUN chmod +x /usr/bin/celery-cmd +RUN chmod +x \ + wait-for-databases.sh \ + tasks.py \ + entrypoint.sh \ + celery.sh \ + celery-cmd && \ + mv wait-for-databases.sh /usr/local/bin/wait-for-databases && \ + mv celery.sh /usr/local/bin/celery-commands && \ + mv celery-cmd /usr/local/bin/celery-cmd # # Install "geonode-contribs" apps # RUN cd /usr/src; git clone https://github.com/GeoNode/geonode-contribs.git -b master @@ -28,16 +35,23 @@ RUN chmod +x /usr/bin/celery-cmd # RUN cd /usr/src/geonode-contribs/geonode-logstash; pip install --upgrade -e . \ # cd /usr/src/geonode-contribs/ldap; pip install --upgrade -e . -RUN yes w | pip install --src /usr/src -r requirements.txt &&\ - yes w | pip install -e . +RUN yes w | pip install --no-cache --src /usr/src -r requirements.txt &&\ + yes w | pip install --no-cache -e . # Cleanup apt update lists -RUN apt-get autoremove --purge &&\ - apt-get clean &&\ +# This is needed to reduce the size of the image, but it's better to move this RUN to geonode-base +RUN apt-get autoremove --purge && \ + apt-get clean && \ rm -rf /var/lib/apt/lists/* +# Create specific log dir to geonode and statics and grant permissions to root group. +# This is needed to avoid permission issues +RUN mkdir -p /var/log/geonode /mnt/volumes/statics && \ + chmod -R g=u /var/log/geonode /mnt/volumes/statics + # Export ports EXPOSE 8000 -# We provide no command or entrypoint as this image can be used to serve the django project or run celery tasks -# ENTRYPOINT /usr/src/geonode/entrypoint.sh +# We provide no command as this image can be used to serve the django project or run celery tasks +ENTRYPOINT [ ./entrypoint.sh ] + diff --git a/celery-cmd b/celery-cmd index a600163fc77..780ea5ccf9d 100644 --- a/celery-cmd +++ b/celery-cmd @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash # A configurable celery command. # Luca Pasquali CELERY_BIN=${CELERY_BIN:-"$(which celery||echo celery)"} @@ -11,7 +11,7 @@ CELERY__MAX_TASKS_PER_CHILD=${CELERY__MAX_TASKS_PER_CHILD:-"10"} CELERY__OPTS=${CELERY__OPTS:-"--without-gossip --without-mingle -Ofair -B -E"} CELERY__BEAT_SCHEDULE=${CELERY__BEAT_SCHEDULE:-"celery.beat:PersistentScheduler"} CELERY__LOG_LEVEL=${CELERY__LOG_LEVEL:-"INFO"} -CELERY__LOG_FILE=${CELERY__LOG_FILE:-"/var/log/celery.log"} +CELERY__LOG_FILE=${CELERY__LOG_FILE:-"/var/log/geonode/celery.log"} CELERY__WORKER_NAME=${CELERY__WORKER_NAME:-"worker1@%h"} CELERY__WORKER_CONCURRENCY=${CELERY__WORKER_CONCURRENCY:-"4"} diff --git a/celery.sh b/celery.sh index f5746569558..01231e735ed 100755 --- a/celery.sh +++ b/celery.sh @@ -1,4 +1,4 @@ #!/bin/bash -nohup celery -A geonode.celery_app:app beat -l DEBUG -f /var/log/celery.log &>/dev/null & -nohup celery -A geonode.celery_app:app worker --without-gossip --without-mingle -Ofair -B -E --statedb=worker.state --scheduler=celery.beat:PersistentScheduler --loglevel=INFO --concurrency=2 -n worker1@%h -f /var/log/celery.log &>/dev/null & +nohup celery -A geonode.celery_app:app beat -l DEBUG -f /var/log/geonode/celery.log &>/dev/null & +nohup celery -A geonode.celery_app:app worker --without-gossip --without-mingle -Ofair -B -E --statedb=worker.state --scheduler=celery.beat:PersistentScheduler --loglevel=INFO --concurrency=2 -n worker1@%h -f /var/log/geonode/celery.log &>/dev/null & nohup celery -A geonode.celery_app:app flower --auto_refresh=True --debug=False --broker=${BROKER_URL} --basic_auth=${ADMIN_USERNAME}:${ADMIN_PASSWORD} --address=0.0.0.0 --port=5555 &>/dev/null & \ No newline at end of file diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 3f496a6445b..d1633aed615 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -3,11 +3,14 @@ version: '3.9' # Common Django template for GeoNode and Celery services below x-common-django: &default-common-django - image: geonode/geonode:local + image: geonode/geonode:dev build: context: ./ - dockerfile: Dockerfile + args: + GEONODE_BASE_IMAGE: geonode/geonode-base:latest-ubuntu-22.04 + GEONODE_VERSION: master restart: unless-stopped + user: '${GEONODE_UID}' env_file: - .env volumes: @@ -52,15 +55,17 @@ services: # Nginx is serving django static and media files and proxies to django and geonode geonode: - image: geonode/nginx:1.25.3-latest + image: geonode/nginx:dev container_name: nginx4${COMPOSE_PROJECT_NAME} env_file: - .env environment: - RESOLVER=127.0.0.11 + user: '${GEONODE_UID}' ports: - - "${HTTP_PORT}:80" - - "${HTTPS_PORT}:443" + # change container ports to run rootless nginx (80->8080 and 443->8443) + - "${HTTP_PORT}:8080" + - "${HTTPS_PORT}:8443" volumes: - nginx-confd:/etc/nginx - nginx-certificates:/geonode-certificates @@ -69,17 +74,20 @@ services: # Gets and installs letsencrypt certificates letsencrypt: - image: geonode/letsencrypt:2.6.0-latest + image: geonode/letsencrypt:dev container_name: letsencrypt4${COMPOSE_PROJECT_NAME} env_file: - .env + user: '${GEONODE_UID}' volumes: - nginx-certificates:/geonode-certificates restart: unless-stopped + # deploy: + # replicas: 0 # Geoserver backend geoserver: - image: geonode/geoserver:2.24.3-latest + image: geonode/geoserver:dev container_name: geoserver4${COMPOSE_PROJECT_NAME} healthcheck: test: "curl -m 10 --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null http://geoserver:8080/geoserver/ows" @@ -87,6 +95,7 @@ services: interval: 60s timeout: 10s retries: 2 + user: '${GEONODE_UID}' env_file: - .env ports: @@ -105,9 +114,10 @@ services: condition: service_healthy data-dir-conf: - image: geonode/geoserver_data:2.24.3-latest + image: geonode/geoserver_data:dev container_name: gsconf4${COMPOSE_PROJECT_NAME} entrypoint: sleep infinity + user: '${GEONODE_UID}' volumes: - geoserver-data-dir:/geoserver_data/data restart: unless-stopped diff --git a/docker-compose.yml b/docker-compose.yml index fb0e0190a00..31d438885b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,11 @@ x-common-django: image: geonode/geonode:latest-ubuntu-22.04 build: context: ./ - dockerfile: Dockerfile + args: + GEONODE_BASE_IMAGE: geonode/geonode-base:latest-ubuntu-22.04 + GEONODE_VERSION: master restart: unless-stopped + user: '${GEONODE_UID}' env_file: - .env volumes: @@ -34,7 +37,7 @@ services: retries: 2 environment: - IS_CELERY=False - entrypoint: ["/usr/src/geonode/entrypoint.sh"] + # entrypoint: ["/usr/src/geonode/entrypoint.sh"] command: "uwsgi --ini /usr/src/geonode/uwsgi.ini" # Celery worker that executes celery tasks created by Django. @@ -46,7 +49,7 @@ services: condition: service_healthy environment: - IS_CELERY=True - entrypoint: ["/usr/src/geonode/entrypoint.sh"] + # entrypoint: ["/usr/src/geonode/entrypoint.sh"] command: "celery-cmd" # memcached service @@ -68,11 +71,13 @@ services: container_name: nginx4${COMPOSE_PROJECT_NAME} env_file: - .env + user: '${GEONODE_UID}' environment: - RESOLVER=127.0.0.11 ports: - - "${HTTP_PORT}:80" - - "${HTTPS_PORT}:443" + # change container ports to run rootless nginx (80->8080 and 443->8443) + - "${HTTP_PORT}:8080" + - "${HTTPS_PORT}:8443" volumes: - nginx-confd:/etc/nginx - nginx-certificates:/geonode-certificates @@ -83,6 +88,7 @@ services: letsencrypt: image: geonode/letsencrypt:2.6.0-latest container_name: letsencrypt4${COMPOSE_PROJECT_NAME} + user: '${GEONODE_UID}' env_file: - .env volumes: @@ -99,6 +105,7 @@ services: interval: 60s timeout: 10s retries: 2 + user: '${GEONODE_UID}' env_file: - .env ports: @@ -120,6 +127,7 @@ services: image: geonode/geoserver_data:2.24.3-latest container_name: gsconf4${COMPOSE_PROJECT_NAME} entrypoint: sleep infinity + user: '${GEONODE_UID}' volumes: - geoserver-data-dir:/geoserver_data/data restart: unless-stopped diff --git a/entrypoint.sh b/entrypoint.sh index bcb925467f7..ad3396980d2 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,29 +3,65 @@ # Exit script in case of error set -e -INVOKE_LOG_STDOUT=${INVOKE_LOG_STDOUT:-FALSE} +# Parse bools +parse_bool () { + case $1 in + [Tt][Rr][Uu][Ee]|[Yy][Ee][Ss]|[Oo][Nn]|1) echo 'true';; + *) echo 'false';; + esac +} + +INVOKE_LOG_STDOUT=${INVOKE_LOG_STDOUT:-false} + invoke () { - if [ $INVOKE_LOG_STDOUT = 'true' ] || [ $INVOKE_LOG_STDOUT = 'True' ] - then + if [ $(parse_bool $INVOKE_LOG_STDOUT) = 'true' ]; then /usr/local/bin/invoke $@ else - /usr/local/bin/invoke $@ > /usr/src/geonode/invoke.log 2>&1 + /usr/local/bin/invoke $@ > /var/log/geonode/invoke.log 2>&1 fi echo "$@ tasks done" } # Start cron && memcached services -service cron restart +# Not necessary, because cron jobs coiuld be run by the docker host, or use a Cronjob in Kubernetes +# Shoud be fine if we remove cron from base image. +# service cron restart echo $"\n\n\n" echo "-----------------------------------------------------" echo "STARTING DJANGO ENTRYPOINT $(date)" echo "-----------------------------------------------------" +# check if user exists in passwd file +# if not, change HOME to /tmp +HAS_USER=$(getent passwd $(id -u) | wc -l) +if [ $HAS_USER -eq 1 ]; then + echo "User $_USER exists in passwd file" + + if [ $HOME = "/" ]; then + echo "HOME is /, changing to /tmp" + export HOME=/tmp + fi +else + echo "User does not exist in passwd file, changing HOME to /tmp" + export HOME=/tmp +fi +unset HAS_USER + invoke update +# Preserving the original behavior. +if [ ! -e $HOME/.bashrc ]; then + echo "No $HOME/.bashrc found, using skeleton" + cp /etc/skel/.bashrc $HOME/.bashrc +fi + source $HOME/.bashrc -source $HOME/.override_env + +# Load the environment variables, if exists +if [ -e $HOME/.override_env ]; then + source $HOME/.override_env +fi echo DOCKER_API_VERSION=$DOCKER_API_VERSION echo POSTGRES_USER=$POSTGRES_USER @@ -44,15 +80,14 @@ echo MONITORING_DATA_TTL=$MONITORING_DATA_TTL cmd="$@" -if [ ${IS_CELERY} = "true" ] || [ ${IS_CELERY} = "True" ] -then +if [ $(parse_bool $IS_CELERY) = 'true' ]; then echo "Executing Celery server $cmd for Production" -else +else invoke migrations invoke prepare - if [ ${FORCE_REINIT} = "true" ] || [ ${FORCE_REINIT} = "True" ] || [ ! -e "/mnt/volumes/statics/geonode_init.lock" ]; then + if [ $(parse_bool $FORCE_REINIT) = 'true' ] || [ ! -e "/mnt/volumes/statics/geonode_init.lock" ]; then invoke fixtures invoke monitoringfixture invoke initialized diff --git a/uwsgi.ini b/uwsgi.ini index c519637152e..986a7741b22 100644 --- a/uwsgi.ini +++ b/uwsgi.ini @@ -1,7 +1,7 @@ [uwsgi] # uwsgi-socket = 0.0.0.0:8000 http-socket = 0.0.0.0:8000 -logto = /var/log/geonode.log +logto = /var/log/geonode/geonode.log # pidfile = /tmp/geonode.pid chdir = /usr/src/geonode/