til: docker_docker-compose-for-django-development.md
This data as json
| path | topic | title | url | body | html | shot | created | created_utc | updated | updated_utc | shot_hash | slug |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| docker_docker-compose-for-django-development.md | docker | Docker Compose for Django development | https://github.com/simonw/til/blob/main/docker/docker-compose-for-django-development.md | I had to get Docker Compose working for a Django project, primarily to make it easier for other developers to get a working development environment. Some features of this project: - Uses GeoDjango, so needs GDAL etc for the Django app plus a PostgreSQL server running PostGIS - Already has a `Dockerfile` used for the production deployment, but needed a separate one for the development environment - Makes extensive use of Django migrations (over 100 and counting) I ended up with this `docker-compose.yml` file in the root of the project: ```yaml version: "3.1" volumes: postgresql-data: services: database: image: postgis/postgis:13-3.1 restart: always expose: - "5432" ports: - "5432:5432" volumes: - postgresql-data:/var/lib/postgresql/data environment: POSTGRES_USER: postgres POSTGRES_DB: mydb POSTGRES_PASSWORD: postgres web: container_name: myapp platform: linux/amd64 build: context: . dockerfile: Dockerfile.dev command: python manage.py runserver 0.0.0.0:3000 environment: DATABASE_URL: postgres://postgres:postgres@database:5432/mydb DEBUG: 1 volumes: - .:/app ports: - "3000:3000" depends_on: - migrations - database migrations: platform: linux/amd64 build: context: . dockerfile: Dockerfile.dev command: python manage.py migrate --noinput environment: DATABASE_URL: postgres://postgres:postgres@database:5432/mydb volumes: - .:/app depends_on: - database ``` The `db` container runs PostGIS. It uses a named volume to persist PostgreSQL data in between container restarts. The `web` container runs the Django development server, built using the custom `Dockerfile.dev` Dockerfile. The `migrations` container simply runs the apps migrations and then terminates - with `depends_on` used to ensure that migrations run after the hdatabase server starts and before the web server. Both `web` and `migrations` include a `platform: linux/amd64` property - this ensures they will work on M1 Macs even if the Python dependencies are not yet easily compiled for that architecture, see [Running Docker on an M1 Mac](https://til.simonwillison.net/macos/running-docker-on-remote-m1). The `container_name: myapp` field on the `web` container is a convenience which means you can later run commands like this: docker exec -it myapp ./manage.py collectstatic Here's `Dockerfile.dev`: ```dockerfile FROM python:3.9-slim ENV APP_HOME /app WORKDIR $APP_HOME ENV PYTHONUNBUFFERED 1 # gdal for GeoDjango RUN apt-get update && apt-get install -y \ binutils \ gdal-bin \ libproj-dev \ git \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt WORKDIR $APP_HOME/myapp RUN ./manage.py collectstatic --no-input CMD python manage.py runserver 0.0.0.0:3000 ``` | <p>I had to get Docker Compose working for a Django project, primarily to make it easier for other developers to get a working development environment.</p> <p>Some features of this project:</p> <ul> <li>Uses GeoDjango, so needs GDAL etc for the Django app plus a PostgreSQL server running PostGIS</li> <li>Already has a <code>Dockerfile</code> used for the production deployment, but needed a separate one for the development environment</li> <li>Makes extensive use of Django migrations (over 100 and counting)</li> </ul> <p>I ended up with this <code>docker-compose.yml</code> file in the root of the project:</p> <div class="highlight highlight-source-yaml"><pre><span class="pl-ent">version</span>: <span class="pl-s"><span class="pl-pds">"</span>3.1<span class="pl-pds">"</span></span> <span class="pl-ent">volumes</span>: <span class="pl-ent">postgresql-data</span>: <span class="pl-ent">services</span>: <span class="pl-ent">database</span>: <span class="pl-ent">image</span>: <span class="pl-s">postgis/postgis:13-3.1</span> <span class="pl-ent">restart</span>: <span class="pl-s">always</span> <span class="pl-ent">expose</span>: - <span class="pl-s"><span class="pl-pds">"</span>5432<span class="pl-pds">"</span></span> <span class="pl-ent">ports</span>: - <span class="pl-s"><span class="pl-pds">"</span>5432:5432<span class="pl-pds">"</span></span> <span class="pl-ent">volumes</span>: - <span class="pl-s">postgresql-data:/var/lib/postgresql/data</span> <span class="pl-ent">environment</span>: <span class="pl-ent">POSTGRES_USER</span>: <span class="pl-s">postgres</span> <span class="pl-ent">POSTGRES_DB</span>: <span class="pl-s">mydb</span> <span class="pl-ent">POSTGRES_PASSWORD</span>: <span class="pl-s">postgres</span> <span class="pl-ent">web</span>: <span class="pl-ent">container_name</span>: <span class="pl-s">myapp</span> <span class="pl-ent">platform</span>: <span class="pl-s">linux/amd64</span> <span class="pl-ent">build</span>: <span class="pl-ent">context</span>: <span class="pl-s">.</span> <span class="pl-ent">dockerfile</span>: <span class="pl-s">Dockerfile.dev</span> <span class="pl-ent">command</span>: <span class="pl-s">python manage.py runserver 0.0.0.0:3000</span> <span class="pl-ent">environment</span>: <span class="pl-ent">DATABASE_URL</span>: <span class="pl-s">postgres://postgres:postgres@database:5432/mydb</span> <span class="pl-ent">DEBUG</span>: <span class="pl-c1">1</span> <span class="pl-ent">volumes</span>: - <span class="pl-s">.:/app</span> <span class="pl-ent">ports</span>: - <span class="pl-s"><span class="pl-pds">"</span>3000:3000<span class="pl-pds">"</span></span> <span class="pl-ent">depends_on</span>: - <span class="pl-s">migrations</span> - <span class="pl-s">database</span> <span class="pl-ent">migrations</span>: <span class="pl-ent">platform</span>: <span class="pl-s">linux/amd64</span> <span class="pl-ent">build</span>: <span class="pl-ent">context</span>: <span class="pl-s">.</span> <span class="pl-ent">dockerfile</span>: <span class="pl-s">Dockerfile.dev</span> <span class="pl-ent">command</span>: <span class="pl-s">python manage.py migrate --noinput</span> <span class="pl-ent">environment</span>: <span class="pl-ent">DATABASE_URL</span>: <span class="pl-s">postgres://postgres:postgres@database:5432/mydb</span> <span class="pl-ent">volumes</span>: - <span class="pl-s">.:/app</span> <span class="pl-ent">depends_on</span>: - <span class="pl-s">database</span></pre></div> <p>The <code>db</code> container runs PostGIS. It uses a named volume to persist PostgreSQL data in between container restarts.</p> <p>The <code>web</code> container runs the Django development server, built using the custom <code>Dockerfile.dev</code> Dockerfile.</p> <p>The <code>migrations</code> container simply runs the apps migrations and then terminates - with <code>depends_on</code> used to ensure that migrations run after the hdatabase server starts and before the web server.</p> <p>Both <code>web</code> and <code>migrations</code> include a <code>platform: linux/amd64</code> property - this ensures they will work on M1 Macs even if the Python dependencies are not yet easily compiled for that architecture, see <a href="https://til.simonwillison.net/macos/running-docker-on-remote-m1" rel="nofollow">Running Docker on an M1 Mac</a>.</p> <p>The <code>container_name: myapp</code> field on the <code>web</code> container is a convenience which means you can later run commands like this:</p> <pre><code>docker exec -it myapp ./manage.py collectstatic </code></pre> <p>Here's <code>Dockerfile.dev</code>:</p> <div class="highlight highlight-source-dockerfile"><pre><span class="pl-k">FROM</span> python:3.9-slim <span class="pl-k">ENV</span> APP_HOME /app <span class="pl-k">WORKDIR</span> $APP_HOME <span class="pl-k">ENV</span> PYTHONUNBUFFERED 1 <span class="pl-c"><span class="pl-c">#</span> gdal for GeoDjango</span> <span class="pl-k">RUN</span> apt-get update && apt-get install -y \ binutils \ gdal-bin \ libproj-dev \ git \ && rm -rf /var/lib/apt/lists/* <span class="pl-k">COPY</span> requirements.txt . <span class="pl-k">RUN</span> pip install --no-cache-dir -r requirements.txt <span class="pl-k">WORKDIR</span> $APP_HOME/myapp <span class="pl-k">RUN</span> ./manage.py collectstatic --no-input <span class="pl-k">CMD</span> python manage.py runserver 0.0.0.0:3000</pre></div> | <Binary: 64,804 bytes> | 2021-05-24T22:08:23-07:00 | 2021-05-25T05:08:23+00:00 | 2021-05-26T11:32:27-07:00 | 2021-05-26T18:32:27+00:00 | 52bf5cb1337977b903d48ff3d30949df | docker-compose-for-django-development |