I'm trying to serve a simple Django app over NGINX using Docker Compose. I'm not really sure where the problem is coming from but the build was successful. Any idea why is not serving correctly?
This is the setup:
├── app
│ ├── templates
│ ├── webapp
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── Dockerfile
│ ├── Dockerfile.prod
│ ├── entrypoint.sh
│ ├── entrypoint.prod.sh
│ └── requirements.txt
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
├── .env
├── docker-compose.ci.yml
├── docker-compose.prod.yml
└── docker-compose.yml
app/webapp/settings.py:
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = int(os.environ.get('DEBUG', default=0))
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'webapp.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'webapp.wsgi.application'
DATABASES = {
'default': {
'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.sqlite3'),
'NAME': os.environ.get('SQL_DATABASE', os.path.join(BASE_DIR, 'db.sqlite3')),
'USER': os.environ.get('SQL_USER', 'user'),
'PASSWORD': os.environ.get('SQL_PASSWORD', 'password'),
'HOST': os.environ.get('SQL_HOST', 'localhost'),
'PORT': os.environ.get('SQL_PORT', '5432'),
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/staticfiles/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_URL = '/mediafiles/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
YOUTUBE_DATA_API_KEY = os.environ.get('YOUTUBE_DATA_API_KEY')
.env
DEBUG=0
SECRET_KEY=<foo>
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=webapp_django_prod
SQL_USER=webapp_django
SQL_PASSWORD=<password>
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres
YOUTUBE_DATA_API_KEY=<foo>
app/Dockerfile
# pull official base image
FROM python:3.9.6-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install psycopg2
RUN apk update \
&& apk add --virtual build-deps gcc python3-dev musl-dev \
&& apk add postgresql-dev \
&& pip install psycopg2 \
&& apk del build-deps
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r /usr/src/app/requirements.txt
# copy entrypoint.sh
COPY ./entrypoint.sh /usr/src/app/entrypoint.sh
# copy project
COPY . /usr/src/app/
# run entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
app/entrypoint.sh
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
python manage.py flush --no-input
python manage.py migrate
python manage.py collectstatic --no-input --clear
exec "$@"
app/Dockerfile.prod
# pull official base image
FROM python:3.9.6-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install psycopg2
RUN apk update \
&& apk add --virtual build-deps gcc python3-dev musl-dev \
&& apk add postgresql-dev \
&& pip install psycopg2 \
&& apk del build-deps
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r /usr/src/app/requirements.txt
# copy entrypoint-prod.sh
COPY ./entrypoint.prod.sh /usr/src/app/entrypoint.prod.sh
# copy project
COPY . /usr/src/app/
# run entrypoint.prod.sh
ENTRYPOINT ["/usr/src/app/entrypoint.prod.sh"]
app/entrypoint.prod.sh
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
exec "$@"
nginx/Dockerfile
FROM nginx:1.21-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
nginx/nginx.conf
upstream webapp {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://webapp;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /staticfiles/ {
alias /usr/src/app/staticfiles/;
}
location /mediafiles/ {
alias /usr/src/app/mediafiles/;
}
}
docker-compose.yml
version: '3.8'
services:
web:
build: ./app
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./app/:/usr/src/app/
ports:
- 8000:8000
environment:
- DEBUG=1
- SECRET_KEY=<foo>
- SQL_ENGINE=django.db.backends.postgresql
- SQL_DATABASE=webapp_django_prod
- SQL_USER=webapp_django
- SQL_PASSWORD=<foo>
- SQL_HOST=db
- SQL_PORT=5432
- DATABASE=postgres
depends_on:
- db
db:
image: postgres:13-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=webapp_django
- POSTGRES_PASSWORD=<foo>
- POSTGRES_DB=webapp_django_prod
volumes:
postgres_data:
docker-compose.ci.yml
version: '3.8'
services:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
cache_from:
- "${WEB_IMAGE}"
image: "${WEB_IMAGE}"
command: gunicorn webapp.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
expose:
- 8000
env_file: .env
nginx:
build:
context: ./nginx
cache_from:
- "${NGINX_IMAGE}"
image: "${NGINX_IMAGE}"
volumes:
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
ports:
- 80:80
depends_on:
- web
volumes:
static_volume:
media_volume:
docker-compose.prod.yml
version: '3.8'
services:
web:
image: "${WEB_IMAGE}"
command: gunicorn webapp.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
ports:
- 8000:8000
env_file: .env
nginx:
image: "${NGINX_IMAGE}"
volumes:
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
ports:
- 80:80
depends_on:
- web
volumes:
static_volume:
media_volume:
CodePudding user response:
Since I cannot comment still. The easiest way to identify the problem is to look through the logs.
You could check the Docker containers by running this on each instance:
docker logs --follow your-container-name
Or you could add this to your docker-compose file if the docker container gets killed you can access it with journalctl:
mycontainer:
image: myimage
logging:
driver: journald
options:
tag: mytag
And then access it using:
journalctl -u docker CONTAINER_NAME=mycontainer_name
journalctl -u docker CONTAINER_TAG=mytag
Answer taken partially from: https://stackoverflow.com/a/43663253/16891224