testdrivenio / django-on-docker Goto Github PK
View Code? Open in Web Editor NEWDockerizing Django with Postgres, Gunicorn, and Nginx
License: MIT License
Dockerizing Django with Postgres, Gunicorn, and Nginx
License: MIT License
Thanks a lot for the detailed article @mjhea0.
I was able to follow it from start to end and the only part that didn't work for me was STATIC_ROOT = BASE_DIR / "staticfiles"
, where I'm getting TypeError: unsupported operand type(s) for /: 'str' and 'str'
, but replacing it with STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
made it work without major inconvenience.
Other than that, here's my main question: how can I make the media dir accessible from the host?
I tried in the same fashion as I was doing with my database (making sure the directory was created beforehand), but no luck.
Here's the relevant change: lynx-chess/OpenBench@cc99ad4
And here the sad result of the attempt.
Request Method: POST
Request URL: http://localhost/
Django Version: 4.2.1
Python Version: 3.11.4
Installed Applications:
['OpenBench',
'OpenBench.templatetags',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'upload']
Installed 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',
'htmlmin.middleware.HtmlMinifyMiddleware',
'htmlmin.middleware.MarkRequestMiddleware']
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/app/web/upload/views.py", line 9, in image_upload
filename = fs.save(image_file.name, image_file)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/files/storage/base.py", line 38, in save
name = self._save(name, content)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/files/storage/filesystem.py", line 106, in _save
fd = os.open(full_path, self.OS_OPEN_FLAGS, 0o666)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Exception Type: PermissionError at /
Exception Value: [Errno 13] Permission denied: '/home/app/web/media/quota.PNG'
Hi. Been following your tutorial here and encountering the issue that the entrypoint.sh cannot be found.
django-nginx-docker-web-1 | exec /usr/src/app/entrypoint.sh: no such file or directory
When i run-
docker-compose exec web python manage.py startapp upload
It creates that app folder in sudo mode. How to prevent this?
hello, I have followed your django-on-docker series, but when I Deploy Django to AWS with Docker and Let's Encrypt I'm unable to create a migration file whenever I add new field in my django model and run docker-compose -f docker-compose.prod.yml exec web python manage.py makemigrations --noinput
unable to create migration file .
docker-compose.prod.yml
version: '3.7'
services:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
image: <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:web
command: gunicorn djnago_on_docker.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
expose:
- 8000
env_file:
- .env.prod
depends_on:
- redis
celery:
build:
context: ./app
dockerfile: Dockerfile.prod
image: <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:celery
command: celery -A djnago_on_docker worker -l INFO
env_file:
- .env.prod
depends_on:
- web
- redis
nginx-proxy:
container_name: nginx-proxy
build: nginx
image:<aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:nginx-proxy
restart: always
ports:
- 443:443
- 80:80
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- /var/run/docker.sock:/tmp/docker.sock:ro
depends_on:
- web
nginx-proxy-letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
env_file:
- .env.prod.proxy-companion
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
depends_on:
- nginx-proxy
redis:
image: redis:5-alpine
volumes:
static_volume:
media_volume:
certs:
html:
vhost:
Note: I hide my aws-account-id and region. Image properties to use images from ECR, i also removed the db service (and related volume) since i'll use RDS rather than managing Postgres in a container.
Dockerfiel.prod
###########
# BUILDER #
###########
# pull official base image
FROM python:3.8.3-alpine as builder
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install psycopg2 dependencies
RUN apk update \
&& apk add postgresql-dev gcc libxslt-dev libxml2-dev python3-dev musl-dev jpeg-dev zlib-dev
# lint
RUN pip install --upgrade pip
RUN pip install flake8
COPY . .
RUN flake8 --ignore=E501,F401 ./djnago_on_docker
# install dependencies
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt
#########
# FINAL #
#########
# pull official base image
FROM python:3.8.3-alpine
# create directory for the app user
RUN mkdir -p /home/app
# create the app user
RUN addgroup -S app && adduser -S app -G app
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/web
RUN mkdir $APP_HOME
RUN mkdir $APP_HOME/staticfiles
RUN mkdir $APP_HOME/mediafiles
WORKDIR $APP_HOME
# install dependencies
RUN apk update && apk add libpq
RUN apk add libjpeg libxslt-dev
COPY --from=builder /usr/src/app/wheels /wheels
COPY --from=builder /usr/src/app/requirements.txt .
RUN pip install --upgrade pip
RUN pip install --no-cache /wheels/*
# copy entrypoint-prod.sh
COPY ./entrypoint.prod.sh $APP_HOME
RUN chmod +x entrypoint.prod.sh
# copy project
COPY . $APP_HOME
# chown all the files to the app user
RUN chown -R app:app $APP_HOME
# change to the app user
USER app
# run entrypoint.prod.sh
ENTRYPOINT ["/home/app/web/entrypoint.prod.sh"]
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 "$@"
Note: I remove migrate commands in the entrypoint.prod.sh script so they don't run on every container start or re-start. Instead, i can run them manually, after the containers spin up, like so:
docker-compose -f docker-compose.prod.yml exec web python manage.py makemigrations --noinput
docker-compose -f docker-compose.prod.yml exec web python manage.py migrate --noinput
Please help me. @jangia
Am I correct in thinking that this setup is not suitable for production without a supervisor process as there is nothing to restart gunicorn in case it crashes?
Hey,
before this tutorial, I was using the default PostgreSQL superuser simply named postgres
and when I changed it to something I get this error:
django.db.utils.OperationalError: FATAL: role "hello_django" does not exist
I guess you need to create this user first but I haven't seen that mentioned in the article anywhere.
how I can add and use certbot to NginX image
Error because debug flag gots interpreted only as bool not int (Version: Django 3.2.7)
in settings.py, line 26
change: DEBUG = int(os.environ.get("DEBUG", default=0))
to: DEBUG = bool(os.environ.get("DEBUG", default=0))
When I run docker compose I get this error
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/usr/src/app/entrypoint.sh": permission denied: unknown
`FROM python:3.11.4-slim-buster
WORKDIR /usr/src/app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && apt-get install -y netcat
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
COPY ./entrypoint.sh .
RUN sed -i 's/\r$//g' /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh
COPY . .
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]`
Can you please for the few using this setup from Windows mention that Unix line endings are a good way to quietly break Docker builds (container doesn't start with a 211 error)? And it appears there's no need to chmod the entrypoint files first as Windows defaults to 777 (love it!).
Kinda weird the Dockerfile(s) themselves don't seem to trigger this behavior, see This post addressing the 211 error in Dockerfiles {hmmmmm...}
Thanks!
first followed your blog and applied changes with that didn't serving static files
Then even cloned you project .Your project is also not serving the static file
Tried it on https://labs.play-with-docker.com
just cloned the repo.
added * to allowed hosts.
docker-compose up.
clicked on 1337 port and got bad gateway error 502.
Hi, sorry if this is the wrong place to do this, but could you explain why you switched from Pipenv to Pip for this repo and the accompanying tutorial?
Many thanks,
Alex
postgres db is deleted every time I run
docker-compose -f docker-compose.prod.yml up -d - build
My goal is just to update the interior of the /home/app/web/
Where am I making a mistake.
Hi,
This tutorial is awesome for me. Thank you so much sharing this tutorial.
If possible can you please let me know, How we can setup for https letsencript.
Thanks
Django's dev server handles this transparently.
Not able to load static and media files when running on dev mode. Getting 404 response code in logs.
Hi
How to add worker_connections to my nginx config?
worker_processes 1;
events {
worker_connections 10240; # increase if you have lots of clients
}
http {
upstream my-app {
server web-prod: 8000;
}
server {
listen 80;
server_name my-app.com;
client_max_body_size 4G;
access_log off;
gzip on;
gzip_min_length 10240;
gzip_comp_level 1;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
# allow the server to close connection on non responding client, this will free up memory
reset_timedout_connection on;
# request timed out -- default 60
client_body_timeout 10;
# if client stop responding, free up memory -- default 60
send_timeout 2;
# server will close connection after this time -- default 75
keepalive_timeout 30;
# number of requests client can make over keep-alive -- for testing environment
keepalive_requests 100000;
location / {
proxy_pass http://my-app;
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/my-app/staticfiles/;
expires 365d;
}
location /mediafiles/ {
alias /home/app/web/mediafiles/;
}
}
}`
error nginx
nginx: [emerg] "worker_processes" directive is not allowed here in /etc/nginx/conf.d/nginx.conf:1
I guess there is a problem with my dockerfile and configuration nginx files. I tried many configurations and I am looking for a problem on many pages but I can't find a solution right now. I will be grateful for the tips
I did exactly as specified to get this running. Cloned, ran and browsed to 127.0.0.1:1337 (localhost:1337) results in a bad gateway. Image upload appears to work but when you try to browse to the image you get a 404 not found
404 Not Found
nginx/1.15.0
Any input on resolving this?
Hi,
I don’t understand why you install flake8 with pip in the app/Dockerfile.prod file.
Is this an error?
Hi Michael,
thank you for the great tutorial!
I only have one issue:
the command:
python manage.py collectstatic --no-input --clear
in app/entrypoint.sh
deletes all static files on the host machine since the whole app folder is mounted inside the web container:
see
volumes: - ./app/:/usr/src/app/
in docker-compose.yml
So I'm trying to style my App with css files in the directory app/staticfiles
and they get deleted everytime I build the image.
Thanks for your help!
Jan
In your docker-compose you put:
volumes:
- ./app/:/usr/src/app/
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
Static_volume and media_volume are inside bind mount /usr/src/app/. It's kinda double mount. One overrides another.
I've noticed that after some file uploads or some service restarts it stops working correctly: nginx stops serve files, like in this issue .
After removing - ./app/:/usr/src/app/ from docker-compose volumes service begins to work correctly.
I thing volumes shouldn't be used like that. But I understand that bind mount - ./app/:/usr/src/app/ is convenient for development. In production mode this mount should be switched off.
It could be two separate docker-compose files in this case: dev and prod. And in dev you could use just django default web server without nginx. See this guide -- they do exactly that: separate nginx from app.
What do you think?
P.S. Thank you for your tutorial: it's very clear and well written. Your work helped me a lot.
Hello, this is the second time I have tried this tutorial. Each time I get right to the end of the NGINX section and am stopped by the fact that NGINX will not launch. That container will build, but not run...
Successfully built c11338b4c378
Successfully tagged django-on-docker_web:latest
Building nginx
Step 1/3 : FROM nginx:1.21-alpine
---> b46db85084b8
Step 2/3 : RUN rm /etc/nginx/conf.d/default.conf
---> Using cache
---> 210df3735621
Step 3/3 : COPY nginx.conf /etc/nginx/conf.d
---> Using cache
---> fbc4340866f8
Successfully built fbc4340866f8
Successfully tagged django-on-docker_nginx:latest
Creating django-on-docker_db_1 ... done
Creating django-on-docker_web_1 ... done
Creating django-on-docker_nginx_1 ... done
When I check the processes...
❯ docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------
django-on-docker_db_1 docker-entrypoint.sh postgres Up 5432/tcp
django-on-docker_web_1 /home/app/web/entrypoint.p ... Up 8000/tcp
When I go to the url (http://localhost:1337), the page is not found. This appears to be the most popular tutorial on the subject. I have already abandoned it once before when I was block on this and had to move on to other work. What is the missing magic that gets nginx to work. Last time I did the deep dive on this it seemed like their convoluted configuration system was somehow irredeemable broken. How are you getting it to work as specified in this tutorial?
Hey
Thank you so much for the tutotial , its good material for even beginners like me who are starting with docker.
Am trying to attach self created ( via open ssl command) ssl certificate , can you please help me with it
Modified nginx.conf :
upstream hello_django {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_django;
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/;
}
}
server{
listen 443 ssl;
ssl_certificate /etc/ssl/nginx.crt;
ssl_certificate_key /etc/ssl/nginx.key;
server_tokens off;
location / {
proxy_pass http://hello_django;
proxy_pass_header Server;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
and i have modified the docker file as
`
nginx:
build: ./nginx
volumes:
- static_volume:/usr/src/app/staticfiles:Z
ports:
- 1337:80
- 8443:443
`
It would be great if you could help me with it, Thanks.
i am following this method to add ssl certificate
The error am getting is
400 Bad Request
The plain HTTP request was sent to HTTPS port
Following the post and using the repo's code, everything goes as expected until doing
docker-compose exec db psql --username=hello_django --dbname=hello_django_dev
There are none of the tables indicated by the walk-through:
hello_django_dev=# \dt
Did not find any relations.
Though the volume seems attached:
c:\...git\django-on-docker> docker volume inspect django-on-docker_postgres_data
[
{
"CreatedAt": "2021-11-01T03:04:36Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "django-on-docker",
"com.docker.compose.version": "1.29.2",
"com.docker.compose.volume": "postgres_data"
},
"Mountpoint": "/var/lib/docker/volumes/django-on-docker_postgres_data/_data",
"Name": "django-on-docker_postgres_data",
"Options": null,
"Scope": "local"
}
]
Docker version 20.10.8, build 3967b7d
Any thoughts?
There appear to be additional considerations when adding encryption to support https access. Is this addressed elsewhere in your (excellent) work?
This will override entrypoint.sh, isn't it?
django-on-docker/app/Dockerfile
Lines 20 to 26 in ce7c04c
Hi!
I'm trying to make sense on the different containers built here and the relation between them. It seems like there is a conflict between the database and web for the two environments? If I build the prod compose first, then that db will be prevalent. So if I then try to bring up the development stack before the production stack, then the database will be "stuck" as the development one. There doesn't seem like these two stacks can co-exist?
I've followed the tutorial, getting stuck on creating the upload with startapp. I get the error:
(env) usr@thinkpad:~/django/django-on-docker$ docker-compose exec web python manage.py startapp medups │
Traceback (most recent call last): │
File "manage.py", line 21, in <module> │
main() │
File "manage.py", line 17, in main │
execute_from_command_line(sys.argv) │
File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line │
utility.execute() │
File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute │
self.fetch_command(subcommand).run_from_argv(self.argv) │
File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 328, in run_from_argv │
self.execute(*args, **cmd_options) │
File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 369, in execute │
output = self.handle(*args, **options) │
File "/usr/local/lib/python3.8/site-packages/django/core/management/commands/startapp.py", line 14, in handle │
super().handle('app', app_name, target, **options) │
File "/usr/local/lib/python3.8/site-packages/django/core/management/templates.py", line 159, in handle │
with open(new_path, 'w', encoding='utf-8') as new_file: │
OSError: [Errno 126] No error information: '/usr/src/app/medups/apps.py'
This happens on the step where we build the dev environment and try to create a new app with start app. An empty dir is created with the name of the starapp in ./app directory. If you try to run the command again:
docker-compose exec web python manage.py startapp medups
CommandError: 'medups' conflicts with the name of an existing Python module and cannot be used as an app name. Please try another name. │
I used "medups" because i thought "upload" was too generic and there was a conflict. The above error happens since the directory already exists. I tried cloning this repo and the same error happened. It seems like either some dependency updated and is too new, that is causing issues or it's my machine. Any help appreciated.
This happens on Ubuntu 20.04 LTS
Docker version 19.03.12, build 48a66213fe
docker-compose version 1.26.2, build unknown
Hi,
Thanks for the tutorials and code!
If this is the repo corresponding to the django-tdd-docker tutorial, then thought you'd like to know that the Dockerfile presented on https://testdriven.io/courses/tdd-django/postgres-setup/ is missing netcat from the package list apt installs and so the entrypoint.sh script fails. I'm running Ubuntu 20.04 everything latest version and installed as per course directions so I think it's a genuine bug.
The line:
&& apt-get -y install gcc postgresql
Needs to be:
&& apt-get -y install gcc netcat postgresql \
Just FYI,
Jonathan
Hi,
I was using your nice setup for building a django project. I am absolutely new to django, but I have experience with Docker, so I am trying to do this containerized. Your setup was pretty much exactly what I was looking for, so I adopted it.
I got it working, but there was a frustrating half hour of me not understanding why the database wont persist, despite the docker volume setup. I overlooked the flush command in the bash file, but eventually managed to track it down.
Perhaps that makes sense in the setup, but please add a section in the readme here on the page explaining that on restarting the container, the database is wiped clean.
Thank you for making this available.
This is a useful lesson and example, I customized it to ignore the advice of a managed database, because I wanted to keep costs down for my deployed staging instances on sites I was working on.
So this may not be present in the subject of this repo, and feel free to close this. However the fix was hard fought and I wanted to share it just in case someone else stumbled into it
I've detailed the issue of these troublesome 502's in this post. Hope this helps someone.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.