diff --git a/.dockerignore b/.dockerignore index 6975847e..668c490d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,4 +7,12 @@ docker-compose* .gitignore README.md LICENSE -.vscode \ No newline at end of file +.vscode +.env +.env.template +.github +.idea +LICENSE.md +docs +nginx +update.sh \ No newline at end of file diff --git a/.env.template b/.env.template index 1a55d7a9..4713b3a2 100644 --- a/.env.template +++ b/.env.template @@ -1,7 +1,3 @@ -VIRTUAL_HOST= -LETSENCRYPT_HOST= -LETSENCRYPT_EMAIL= - DEBUG=1 ALLOWED_HOSTS=* SECRET_KEY= diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 723bd801..98d24354 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,6 +21,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt + python3 manage.py collectstatic --noinput - name: Django Testing project run: | python3 manage.py test diff --git a/.gitignore b/.gitignore index c92107cc..8d4ecedd 100644 --- a/.gitignore +++ b/.gitignore @@ -76,4 +76,4 @@ staticfiles/ postgresql/ -/docker-compose.yml +/docker-compose.override.yml diff --git a/Dockerfile b/Dockerfile index f2d9c0aa..0b182b27 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,23 @@ -FROM python:3.8-slim-buster - -RUN mkdir /Recipes -WORKDIR /Recipes - -ADD . /Recipes/ - -RUN apt-get update -RUN apt-get -y upgrade -RUN apt-get install -y \ - python3 \ - python3-pip \ - postgresql-client \ - gettext - -RUN pip3 install --upgrade pip - -RUN pip3 install -r requirements.txt - -RUN apt-get autoremove -y +FROM python:3.8-alpine +RUN apk add --no-cache postgresql-libs gettext zlib libjpeg libxml2-dev libxslt-dev ENV PYTHONUNBUFFERED 1 +EXPOSE 8080 -EXPOSE 8080 \ No newline at end of file +# Don't run container as root +RUN adduser -D recipes + +RUN mkdir /opt/recipes +RUN chown recipes:recipes /opt/recipes +WORKDIR /opt/recipes +COPY --chown=recipes:recipes . ./ +RUN chmod +x boot.sh setup.sh +RUN ln -s /opt/recipes/setup.sh /usr/local/bin/createsuperuser + +RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev && \ + python -m venv venv && \ + venv/bin/pip install -r requirements.txt --no-cache-dir &&\ + apk --purge del .build-deps + +USER recipes +ENTRYPOINT ["/opt/recipes/boot.sh"] \ No newline at end of file diff --git a/README.md b/README.md index be0aebf9..2a86988d 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,10 @@ When clicking submit, every recipe containing the word will be updated (tags are ### Docker-Compose 1. Clone this repository to your desired install location -2. Choose one of the included `docker-compose.yml` files [here](https://github.com/vabene1111/recipes/tree/develop/docs/docker). -3. Copy it to the root directory (where this readme is) +2. Choose one of the included configurations [here](https://github.com/vabene1111/recipes/tree/develop/docs/docker). +3. Copy (if needed) the `docker-compose.override.yml.template` to `docker-compose.override.yml` and uncomment the configurations you need. 4. Start the container (`docker-compose up -d`) -5. This time and **on each update** run `update.sh` to apply migrations and collect static files -6. Create a default user by executing into the container with `docker-compose exec web_recipes sh` and run `python3 manage.py createsuperuser`. +5. Create a default user by running `docker-compose exec -it createsuperuser`. **Replace "" with your instance name which can be found via running `docker ps`** ### Manual Copy `.env.template` to `.env` and fill in the missing values accordingly. @@ -63,9 +62,7 @@ Otherwise simply follow the instructions for any django based deployment ## Updating 0. Before updating it is recommended to **backup your database** -1. Stop the container using `docker-compose down`. -2. Pull the project files and start the container again using `docker-compose up -d --build`. -3. Run `update.sh` +1. Pull the project files and start the container again using `docker-compose up -d --build`. ## Contributing Pull Requests and ideas are welcome, feel free to contribute in any way. diff --git a/boot.sh b/boot.sh new file mode 100644 index 00000000..0605d78d --- /dev/null +++ b/boot.sh @@ -0,0 +1,9 @@ +#!/bin/sh +source venv/bin/activate + +echo "Updating database" +python manage.py migrate +python manage.py collectstatic --noinput +echo "Done" + +exec gunicorn -b :8080 --access-logfile - --error-logfile - recipes.wsgi \ No newline at end of file diff --git a/docs/docker/nginx-proxy/README.md b/docs/docker/nginx-proxy/README.md index ceca1510..21073cf0 100644 --- a/docs/docker/nginx-proxy/README.md +++ b/docs/docker/nginx-proxy/README.md @@ -2,3 +2,10 @@ This is a docker compose example when using [jwilder's nginx reverse proxy](http in combination with [jrcs's letsencrypt companion](https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion/). Please refer to the appropriate documentation on how to setup the reverse proxy and networks. + +Remember to add the appropriate environment variables to `.env` file: +``` +VIRTUAL_HOST= +LETSENCRYPT_HOST= +LETSENCRYPT_EMAIL= +``` \ No newline at end of file diff --git a/docs/docker/nginx-proxy/docker-compose.yml b/docs/docker/nginx-proxy/docker-compose.yml index e1c0e011..db36ac5d 100644 --- a/docs/docker/nginx-proxy/docker-compose.yml +++ b/docs/docker/nginx-proxy/docker-compose.yml @@ -2,42 +2,42 @@ version: "3" services: db_recipes: restart: always - image: "postgres:11-alpine" + image: postgres:11-alpine volumes: - - ./postgresql:/var/lib/postgresql/data + - ./postgresql:/var/lib/postgresql/data env_file: - - ./.env + - ./.env networks: - - default + - default web_recipes: - build: . + image: vabene1111/recipes restart: always env_file: - - ./.env - command: "gunicorn --bind 0.0.0.0:8080 recipes.wsgi" + - ./.env volumes: - - .:/Recipes + - ./staticfiles:/opt/recipes/staticfiles + - ./mediafiles:/opt/recipes/mediafiles depends_on: - - db_recipes + - db_recipes networks: - - default + - default - nginx_recipes: - image: "nginx" - restart: always - env_file: - - ./.env - volumes: - - ./nginx/conf.d:/etc/nginx/conf.d - - ./staticfiles:/static - - ./mediafiles:/media - networks: - - default - - nginx-proxy + nginx_recipes: + image: nginx:mainline-alpine + restart: always + env_file: + - ./.env + volumes: + - ./nginx/conf.d:/etc/nginx/conf.d + - ./staticfiles:/static + - ./mediafiles:/media + networks: + - default + - nginx-proxy networks: default: nginx-proxy: external: - name: nginx-proxy + name: nginx-proxy \ No newline at end of file diff --git a/nginx/conf.d/Recipes.conf b/docs/docker/nginx-proxy/nginx/conf.d/Recipes.conf similarity index 100% rename from nginx/conf.d/Recipes.conf rename to docs/docker/nginx-proxy/nginx/conf.d/Recipes.conf diff --git a/docs/docker/plain/README.md b/docs/docker/plain/README.md new file mode 100644 index 00000000..7c9684be --- /dev/null +++ b/docs/docker/plain/README.md @@ -0,0 +1,5 @@ +This is the most basic configuration to run this image with docker compose. + +> **NOTE**: There is no proxy included in this configuration and gunicorn is directly exposed as the webserver which is +> not recommended by according to the [gunicorn devs](https://serverfault.com/questions/331256/why-do-i-need-nginx-and-something-like-gunicorn). +> It is higly recommended to configure an additional proxy (nginx, ...) in front of this. \ No newline at end of file diff --git a/docs/docker/plain/docker-compose.yml b/docs/docker/plain/docker-compose.yml index 30f32c48..3c6bebb8 100644 --- a/docs/docker/plain/docker-compose.yml +++ b/docs/docker/plain/docker-compose.yml @@ -2,39 +2,28 @@ version: "3" services: db_recipes: restart: always - image: "postgres:11-alpine" + image: postgres:11-alpine volumes: - - ./postgresql:/var/lib/postgresql/data + - ./postgresql:/var/lib/postgresql/data env_file: - - ./.env + - ./.env networks: - - default + - default web_recipes: - build: . + image: vabene1111/recipes restart: always env_file: - - ./.env - command: "gunicorn --bind 0.0.0.0:8080 recipes.wsgi" + - ./.env volumes: - - .:/Recipes - depends_on: - - db_recipes - networks: - - default - - nginx_recipes: - image: "nginx" - restart: always - env_file: - - ./.env - volumes: - - ./nginx/conf.d:/etc/nginx/conf.d - - ./staticfiles:/static - - ./mediafiles:/media + - ./staticfiles:/opt/recipes/staticfiles + - ./mediafiles:/opt/recipes/mediafiles ports: - - 80:80 - networks: - - default + - 80:8080 depends_on: - - web_recipes \ No newline at end of file + - db_recipes + networks: + - default + +networks: + default: \ No newline at end of file diff --git a/docs/docker/traefik/docker-compose.yml b/docs/docker/traefik/docker-compose.yml index c72b510f..5fc21c39 100644 --- a/docs/docker/traefik/docker-compose.yml +++ b/docs/docker/traefik/docker-compose.yml @@ -2,39 +2,34 @@ version: "3" services: db_recipes: restart: always - image: "postgres:11-alpine" + image: postgres:11-alpine volumes: - - ./postgresql:/var/lib/postgresql/data + - ./postgresql:/var/lib/postgresql/data env_file: - - ./.env + - ./.env + networks: + - default web_recipes: - build: . + image: vabene1111/recipes restart: always env_file: - - ./.env - command: "gunicorn --bind 0.0.0.0:8080 recipes.wsgi" + - ./.env volumes: - - .:/Recipes + - ./staticfiles:/opt/recipes/staticfiles + - ./mediafiles:/opt/recipes/mediafiles depends_on: - - db_recipes - - nginx_recipes: - image: "nginx" - restart: always - env_file: - - ./.env - volumes: - - ./nginx/conf.d:/etc/nginx/conf.d - - ./staticfiles:/static - - ./mediafiles:/media - labels: + - db_recipes + labels: # This lables are only examples! - "traefik.enable=true" - "traefik.http.routers.recipes.rule=Host(`recipes.mydomain.com`, `recipes.myotherdomain.com`)" - "traefik.http.routers.recipes.entrypoints=web_secure" - "traefik.http.routers.recipes.tls.certresolver=le_resolver" + networks: + - default + - traefik networks: - default: - external: - name: traefik + default: + traefik: # This is you external traefic network + external: true \ No newline at end of file diff --git a/recipes/settings.py b/recipes/settings.py index 1c1532a6..54af7185 100644 --- a/recipes/settings.py +++ b/recipes/settings.py @@ -58,6 +58,7 @@ INSTALLED_APPS = [ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', + 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -133,8 +134,8 @@ USE_L10N = True USE_TZ = True LANGUAGES = [ - ('de', _('German')), - ('en', _('English')), + ('de', _('German')), + ('en', _('English')), ] # Static files (CSS, JavaScript, Images) @@ -145,3 +146,6 @@ STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles") + +# Serve static files with gzip +STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' diff --git a/requirements.txt b/requirements.txt index 1e99bb04..d87ec433 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,5 @@ lxml webdavclient3 python-dotenv psycopg2-binary +whitenoise gunicorn diff --git a/setup.sh b/setup.sh new file mode 100644 index 00000000..49286f83 --- /dev/null +++ b/setup.sh @@ -0,0 +1,5 @@ +#!/bin/sh +source venv/bin/activate +echo "Creating Superuser." +python manage.py createsuperuser +echo "Done" \ No newline at end of file diff --git a/update.sh b/update.sh deleted file mode 100644 index a0127149..00000000 --- a/update.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -docker-compose run web_recipes python3 manage.py migrate -docker-compose run web_recipes python3 manage.py collectstatic --noinput