Copying code using Dockerfile or mounting volume using docker-compose - django

I am following the official tutorial on the Docker website
The docker file is
FROM python:3
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
The docker-compose is:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
I do not understand why in the Dockerfile they are copying the code COPY . /code/ , but then again mounting it in the docker-compose - .:/code ? Is it not enough if I either copy or mount?

Both the volumes: and command: in the docker-compose.yml file are unnecessary and should be removed. The code and the default CMD to run should be included in the Dockerfile.
When you're setting up the Docker environment, imagine that you're handed root access to a brand-new virtual machine with nothing installed on it but Docker. The ideal case is being able to docker run your-image, as a single command, pulling it from some registry, with as few additional options as possible. When you run the image you shouldn't need to separately supply its source code or the command to run, these should usually be built into the image.
In most cases you should be able to build a Compose setup with fairly few options. Each service needs an image: or build: (both, if you're planning to push the image), often environment:, ports:, and depends_on: (being aware of the limitations of the latter option), and your database container(s) will need volumes: for their persistent state. That's usually it.
The one case where you do need to override command: in Compose is if you need to run a separate command on the same image and code base. In a Python context this often comes up to run a Celery worker next to a Django application.
Here's a complete example, with a database-backed Web application with an async worker. The Redis cache layer does not have persistence and no files are stored locally in any containers, except for the database storage. The one thing missing is the setup for the database credentials, which requires additional environment: variables. Note the lack of volumes: for code, the single command: override where required, and environment: variables providing host names.
version: '3.8'
services:
app:
build: .
ports: ['8000:8000']
environment:
REDIS_HOST: redis
PGHOST: db
worker:
build: .
command: celery worker -A queue -l info
environment:
REDIS_HOST: redis
PGHOST: db
redis:
image: redis:latest
db:
image: postgres:13
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Where you do see volumes: overwriting the image's code like this, it's usually an attempt to avoid needing to rebuild the image when the code changes. In the Dockerfile you show, though, the rebuild is almost free assuming the requirements.txt file hasn't changed. It's also almost always possible to do your day-to-day development outside of Docker – for Python, in a virtual environment – and use the container setup for integration testing and deployment, and this will generally be easier than convincing your IDE that the language interpreter it needs is in a container.
Sometimes the Dockerfile does additional setup (changing line endings or permissions, rearranging files) and the volumes: mount will hide this. It means you're never actually running the code built into the image in development, so if the image setup is buggy in some way you won't see it. In short, it reintroduces the "works on my machine" problem that Docker generally tries to avoid.

It used for saving the image after with the code.
When you use COPY it save it as part of the image.
While mounting is only while developing.

Ideally we use a single Dockerfile to create the image we use for both production and development. This increases the similarity of the app's runtime environment, which is a good thing.
In contrast to what #David writes: it's quite handy to do your day-to-day development with a Docker container. Your code runs in the same environment in production and development. If you use virtualenv in development you're not making use of that very practical attribute of Docker. The environments can diverge without you knowing and prod can break while dev keeps on working.
So how do we let a single Dockerfile produce an image that we can run in production and use during development? First we should talk about the code we want to run. In production we want to have a specific collection of code to run (most likely the code at a specific commit in your repository). But during development we constantly change the code we want to run, by checking out different branches or editing files. So how do we satisfy both requirements?
We instruct the Dockerfile to copy some directory of code (in your example .) into the image, in your example /code. If we don't do anything else: that will be the code that runs. This happens in production.
But in development we can override the /code directory with a directory on the host computer using a volume. In the example the Docker Compose file sets the volume. Then we can easily change the code running in the dev container without needing to rebuild the image.
Also: even if rebuilding is fast, letting a Python process restart with new files is a lot faster.

Related

have different base image for an application in docker

I am new in docker world.I am trying to understand Docker concepts about parent images. Assume that I want to run my django application on docker. I want to use ubuntu and python, I want to have postgresql as my database backend, and I want to run my django application on gunicorn web server. Can I have different base image for ubuntu, python, postgres and gunicorn and create my django container like this:
FROM ubuntu
FROM python:3.6.3
FROM postgres
FROM gunicorn
...
I am thinking about having different base image because if someday I want to update one of these image, I only have to update base image and not to go into ubuntu and update them.
You can use multiple FROM in the same Dockerfile, provided you are doing a multi-stage build
One part of the Dockerfile would build an intermediate image used by another.
But that is generally use to cleanly separate the parents used for building your final program, from the parents needed to execute your final program.
No, you can not create your image like this, the only image that will treat as the base image in the Dockerfile you posted will be the last FROM gunicor. what you need is multi-stage builds but before that, I will clear some concept about such Dockerfile.
A parent image is the image that your image is based on. It refers to
the contents of the FROM directive in the Dockerfile. Each subsequent
declaration in the Dockerfile modifies this parent image. Most
Dockerfiles start from a parent image, rather than a base image.
However, the terms are sometimes used interchangeably.
But in your case, I will not recommend putting everything in one Dockerfile. It will kill the purpose of Containerization.
Rule of Thumb
One process per container
Each container should have only one concern
Decoupling applications into multiple containers makes it much easier to scale horizontally and reuse containers. For instance, a web application stack might consist of three separate containers, each with its own unique image, to manage the web application, database, and an in-memory cache in a decoupled manner.
dockerfile_best-practices
Apart from Database, You can use multi-stage builds
If you use Docker 17.05 or higher, you can use multi-stage builds to
drastically reduce the size of your final image, without the need to
jump through hoops to reduce the number of intermediate layers or
remove intermediate files during the build.
Images being built by the final stage only, you can most of the time
benefit both the build cache and minimize images layers.
Your build stage may contain several layers, ordered from the less
frequently changed to the more frequently changed for example:
Install tools you need to build your application
Install or update library dependencies
Generate your application
use-multi-stage-builds
With the multi-stage build, The Dockerfile can contain multiple FROM lines and each stage starts with a new FROM line and a fresh context. You can copy artifacts from stage to stage and the artifacts not copied over are discarded. This allows to keep the final image smaller and only include the relevant artifacts.
Is it possible? Yes, Technically multiple base images (FROM XXXX) can appear in single docker file. But it is not for what you are trying to do. They are used for multi-stage builds. You can read more about it here.
The answer to your question is that, if you want to achieve this type of docker image, you should use one base image and install everything else in it with RUN commands like this.
FROM ubuntu
RUN apt install postgresql # install postgresql
...
Obviously it is not that simple. base ubuntu image is very minimal you have to install all dependencies and tools needed to install python, postgres and gunicorn yourself with RUN commands. For example, if you need to download python source code using
RUN wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz
wget (most probably) is not pre installed in ubuntu image. You have to install it yourself.
Should I do it? I think you are going against the whole idea of dockerization of apps. Which is not to build a monolithic giant image containing all the services, but to divide services in separate containers. (Generally there should be one service per container) and then make these containers talk to each other with docker networking tools. That is you should use one container for postgres one for nginx and one for gunicorn, run them separately and connect them via network.There is an awesome tool, docker-compose, comes with docker to automate this kind of multi-container setup. You should really use it. For more practical example about it, please read this good article.
You can use official docker image for django https://hub.docker.com/_/django/ .
It is well documented and explained its dockerfile.
If you wants to use different base image then you must go with docker-compose.
Your docker-compose.yml will look like
version: '3'
services:
web:
restart: always
build: ./web
expose:
- "8000"
links:
- postgres:postgres
- redis:redis
volumes:
- web-django:/usr/src/app
- web-static:/usr/src/app/static
env_file: .env
environment:
DEBUG: 'true'
command: /usr/local/bin/gunicorn docker_django.wsgi:application -w 2 -b :8000
nginx:
restart: always
build: ./nginx/
ports:
- "80:80"
volumes:
- web-static:/www/static
links:
- web:web
postgres:
restart: always
image: postgres:latest
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data/
redis:
restart: always
image: redis:latest
ports:
- "6379:6379"
volumes:
- redisdata:/data
volumes:
web-django:
web-static:
pgdata:
redisdata:
follow this blog for details https://realpython.com/django-development-with-docker-compose-and-machine/

Docker container set up for Django & VueJS

Good afternoon,
I am looking for some guidance on where to concentrate my efforts. I keep getting off down these rabbit holes and cannot seem to find the path I am looking for.
I have developed a couple small internal django apps but wish to integrate VueJS into the mix for more dynamic front end.
My goals are:
I want to use Django-restframework for the backend calls
I want to use VueJS for the front end and make calls back to the REST API.
I want all of this to live in Docker container(s) that I can sync using Jenkins.
My questions / concerns:
I keep trying to build a single docker container for both VueJS and Django but starting with either Node or Python, I seem to end up in dependency hell. Does anyone have a good reference link?
I can't decide if I want it completely decoupled or to try to preserve some of the Django templates. The reason for the latter is that I don't want to lose the built in Django authentication. I am not skilled enough to write the whole authentication piece so I would rather not lose that already being done.
If I am complete decoupled and django is strictly the API, I could also have a single docker container for the django, and a second docker container for the front end. Thoughts?
Finally, these webapps are all the same risk level and exist on the same web app server with a separate postgres database server. Should nginx be on the server, then gunicorn in the docker container with django? Where do most devs draw the line on what is native on the server and what is being served from a docker container? These are all pretty low volume apps targeted for specific purposes.
Thanks for all your guidance as I continue to venture into new territory.
Kevin
*Update December 2020
If you are interested in SSR, here's an outline for an approach I have been working on:
Update December 2020
Here's another way to do Django with Vue in containers on DigitalOcean with docker swarm. It a lot simpler than the approach with AWS shown below, but not as scalable:
Update June 2020
I have made some changes to the project that make use of the Cloud Development Kit (CDK) and AWS Fargate instead of ECS with container instances to take advantage of serverless infrastructure.
Here's an overview of the project architecture: https://verbose-equals-true.gitlab.io/django-postgres-vue-gitlab-ecs/start/overview/
and here's an updated architecture diagram:
Edit May 2019
Here is a setup for Django and Vue using ECS, the AWS container orchestration tool and GitLab for CI/CD. The repo is here.
I have been working on a project that demonstrates how to set up a Django + Vue project with docker. It is an ope source project called verbose-equals-true (https://verbose-equals-true.tk). The source code for this project is available here: https://gitlab.com/briancaffey/verbose-equals-true
Here is an overview of how I'm structuring the project for production. The project uses docker compose to orchestrate the different containers for production and also for development.
Let me know if you have any questions any questions about how I'm using Django/Vue/docker. I have documentation with detailed descriptions at https://verbose-equals-true.tk/docs.
Here are some thoughts on your questions/concerns:
I started with the official recommendations from VueJS for how to dockerize a Vue app, and an official example from Docker about how to dockerize a postgres + Django app. You can probably put everything in the same container, but I like separating things out as a way of keeping things modular and clear.
I'm using JWT for authentication with djangorestframework_jwt package. I can also use the builtin Django authentication system and Django admin.
I think it makes sense to have separate containers. In development you can serve the Vue app from the node container running npm run serve, and in production you can serve the production app's static files from an nginx container (you can use a multi-stage build for this part).
I would keep everything in containers with nothing on the server except the docker engine. This will simplify setup and will allow you to keep your application portable to wherever you decide to deploy it. The only thing that makes sense to keep separate is the postgres database. It is often much easier to connect to a managed database service like AWS RDS, but it is also possible to run a postgres container on your docker host machine to keep things even simpler. This will require that you do backups on your own, so you will need to be familiar with docker volumes.
I've been working with Django/Vue and this is what I do:
Create the Django project
Initialize the project's folder as new Vue project using the vue-cli
From here I can start two development servers, one for Django and the other for Vue:
python manage.py runserver
In another terminal:
npm run serve
In order to consume my API in Vue this I use this configuration in vue.config.js:
module.exports = {
baseUrl: process.env.NODE_ENV === 'production'
? '/static/'
: '/',
outputDir: '<PROJECT_BASE_DIR>/static',
indexPath: '../templates/index.html',
filenameHashing: false,
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8000'
}
}
},
}
devServer redirects the requests to the API, outputDir and indexPath help to build the app to the project's folder, <PROJECT_BASE_DIR>/templates/ and <PROJECT_BASE_DIR>/static/
The next thing is to create a TemplateView and set the template_name to index.html (the file built by Vue), with this you have your SPA inside a Django view/template.
With this approach you can use a Docker container for Django.
I hope this gives you some basic guidance to continue.
Alejandro
#docker-compose.yml
version: "3.9"
services:
db:
image: postgres
environment:
- POSTGRES_NAME=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
server:
build: server/
command: python manage.py runserver 0.0.0.0:8000
ports:
- "8000:8000"
environment:
- POSTGRES_NAME=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
depends_on:
- db
client:
build: client/
command: npm run serve
ports:
- '8081:8080'
depends_on:
- server
#server django Dockerfile
# pull the official base image
FROM python:3
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DJANGO_SUPERUSER_PASSWORD="admin"
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app
RUN pip install -r requirements.txt
# copy project
COPY . /usr/src/app
EXPOSE 8000
CMD ["python", "manage.py", "runserver"]
#client vue Dockerfile
FROM node:14.17.5 as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY ./ .
RUN npm run build
EXPOSE 8080
CMD [ "npm", "run", "serve" ]
it's working

Docker compose for production and development

So I use Python+Django (but it does not really matter for this question)
When I write my code I simply run
./manage.py runserver
which does the webserver, static files, automatic reload, etc.
and and to put it on production I use series of commands like
./manage.py collectstatic
./manage.py migrate
uwsgi --http 127.0.0.1:8000 -w wsgi --processes=4
also I have few other services like postgres, redis (which are common for both production and dev)
So I'm trying to adapt here docker(+ -compose) and I cannot understand how to split prod/dev with it.
basically in docker-compose.yml you define your services and images - but in my case image in production should run one CMD and in dev another..
what are the best practices to achieve that ?
You should create additional docker-compose.yml files like docker-compose-dev.yml or docker-compose-pro.yml and override some of the original docker-compose.yml configuration with -f command:
docker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d
Sometimes, I also use different Dockerfile for different environments and specify dockerfile parameter in docker-compose-pro.yml build section, but I didn't recommend it because you will end with duplicated Dockerfiles.
Update
Docker has introduced multi-stage builds feature https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds which allow to create a Dockerfile for different environments.
Usually having a different production and dev starting workflow is a bad idea. You should always try to keep both dev and prod environments very similar, even in the way you launch your applications. You should always externalize the configuration that is different between the different environments.
Having different startup sequence is maybe acceptable, however having multiple docker images (or dockerfiles) for each environment is a very bad idea. Docker images should be immutable and portable.
However, you might have some constraints. Docker-compose allows you to override the command that is specified in the image. There is the command property that will override the default command in the image. I would recommend that you keep the image production ready,
i.e. use something like CMD ./manage.py collectstatic && ./manage.py migrate && uwsgi --http 127.0.0.1:8000 -w wsgi --processes=4 in the Dockerfile.
In the compose file just override the CMD by specifying:
command: ./manage.py runserver
Having multiple compose file is usually not a big issue. You can keep your compose files clean and manageable by using some nice compose file features such as extends, where once compose file can extend another one.

Debugging Django on Docker on Vagrant with IDE

I have quite the Docker stack at the moment, compiled from many containers, one of which is running an instance of Django.
At the moment, I'm limited to debugging by importing logging and using
logger = logging.getLogger(__name__)
logger.debug("your variable: " + variableName)
It's totally inefficient and requires me to rebuild the docker stack every time I want to re-evaluate a change.
I'm used to working in Komodo and having a robust, step-able debugger at my disposal, but I can's seem to find any documents on how to wire up a Docker container inside a vagrant VM to an IDE (or command line debugger) that will let me step through code without a rebuild.
How can I wire up a debugging IDE to a docker container inside a Vagrant VM? Thanks.
I recommend you to use Docker Compose to handle and link your containers. I'm also using a Docker stack on my dev env with a container for
- django
- postgres
- nginx
You just have to synchronize your code with the code inside your docker container. To do that, use the volumes command in your docker-compose file. Here is an example, with 2 containers (django and postgres) :
db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/webapp
ports:
- "8000:8000"
links:
- db
This portion of code will do what you want. All your project . will be synchronized with the /webapp folder of your docker container then, no more need to rebuild your docker image:
volumes:
- .:/webapp
Then, to debug, I recommend you to use pdb, which is in my opinion , the best way to debug your django app, run :
docker-compose -f [path/to/your/docker-compose.yml] --service-ports [name-of-your-django-container] python manage.py runserver
E.g :
docker-compose -f django_project/docker-compose.yml --service-ports django python manage.py runserver
Let's debug a view :
1. import pdb in the view :import pdb
2. add pdb.set_trace() in a method or a class in your view
3. request the right url
and you will be able to debug through your terminal
You should have something like that :
(Pdb) > /webapp/car/views.py(18)get()
-> for car in serializer.data:
Here is a tutorial to use Compose and Django from Docker : Quickstart Guide: Compose and Django

What is the correct way to set up a Django/PostgreSQL project with Docker Compose?

Can you tell me what is the best way to way to set up and configure a Django/PostgreSQL project with Docker Compose?
(with the latest versions of everything, python 3.4, django 1.8.1, etc.)
Have you looked at the examples on the Docker website? Here's a link describing exactly this.
Basically, you need two services, one for your Django app and one for your Postgres instance. You would probably want to build a Docker image for your Django app from you current folder, hence you'll need to define a Dockerfile:
# Dockerfile
FROM python:3.4-onbuild
That's the whole Dockerfile! Using the magic -onbuild image, files are automatically copied to the container and the requirements are installed with pip. For more info, read here.
Then, you simply need to define your docker-compose.yml file:
# docker-compose.yml
db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
links:
- db
Here, you've defined the Postgres service, which is built from the latest postgres image. Then, you've defined your Django app's service, built it from the current directory, exposed port 8000 so that you can access it from outside your container, linked to the database container (so that they can magically communicate without anything specific from your side - more details here) and started the container with the classic command you use to normally start your Django app. Also, a volume is defined in order to sync the code you're writing with the one from inside your container (so that you don't need to rebuild your image every time you change the code).
Related to having the latest Django version, you just have to specify it in your requirements.txt file.