diff --git a/Dockerfile.localdev b/Dockerfile.localdev new file mode 100644 index 00000000..aca208b3 --- /dev/null +++ b/Dockerfile.localdev @@ -0,0 +1,98 @@ +# (Keep the version in sync with the node install below) +FROM node:16 as frontend + +# Make build & post-install scripts behave as if we were in a CI environment (e.g. for logging verbosity purposes). +ARG CI=true + +# Install front-end dependencies. +COPY package.json package-lock.json webpack.config.js ./ +RUN npm ci + +# Compile static files +COPY ./apps/frontend/static_src/ ./apps/frontend/static_src/ +RUN npm run build + + +# We use Debian images because they are considered more stable than the alpine +# ones becase they use a different C compiler. Debian images also come with +# all useful packages required for image manipulation out of the box. They +# however weigh a lot, approx. up to 1.5GiB per built image. +FROM python:3.9 as production + +ARG POETRY_HOME=/opt/poetry +ARG POETRY_INSTALL_ARGS="" + +# IMPORTANT: Remember to review this when upgrading +ARG POETRY_VERSION=1.2.2 + +# Install dependencies in a virtualenv +ENV VIRTUAL_ENV=/venv + +RUN useradd guide --create-home && mkdir /app $VIRTUAL_ENV && chown -R guide /app $VIRTUAL_ENV + +WORKDIR /app + +# Set default environment variables. They are used at build time and runtime. +# If you specify your own environment variables on Heroku, they will +# override the ones set here. The ones below serve as sane defaults only. +# * PATH - Make sure that Poetry is on the PATH, along with our venv +# * PYTHONUNBUFFERED - This is useful so Python does not hold any messages +# from being output. +# https://docs.python.org/3.9/using/cmdline.html#envvar-PYTHONUNBUFFERED +# https://docs.python.org/3.9/using/cmdline.html#cmdoption-u +# * DJANGO_SETTINGS_MODULE - default settings used in the container. +# * PORT - default port used. Please match with EXPOSE. +# Heroku will ignore EXPOSE and only set PORT variable. PORT variable is +# read/used by Gunicorn. +# * WEB_CONCURRENCY - number of workers used by Gunicorn. The variable is +# read by Gunicorn. +# * GUNICORN_CMD_ARGS - additional arguments to be passed to Gunicorn. This +# variable is read by Gunicorn +ENV PATH=${POETRY_HOME}/bin:$VIRTUAL_ENV/bin:$PATH \ + POETRY_INSTALL_ARGS=${POETRY_INSTALL_ARGS} \ + PYTHONUNBUFFERED=1 \ + DJANGO_SETTINGS_MODULE=apps.guide.settings.production \ + PORT=8000 \ + WEB_CONCURRENCY=3 \ + GUNICORN_CMD_ARGS="-c gunicorn-conf.py --max-requests 1200 --max-requests-jitter 50 --access-logfile - --timeout 25" + +# Make $BUILD_ENV available at runtime +ARG BUILD_ENV +ENV BUILD_ENV=${BUILD_ENV} + +# Port exposed by this container. Should default to the port used by your WSGI +# server (Gunicorn). Heroku will ignore this. +EXPOSE 8000 + +# Install poetry using the installer (keeps Poetry's dependencies isolated from the app's) +# chown protects us against cases where files downloaded by poetry have invalid ownership +# chmod ensures poetry dependencies are accessible when packages are installed +RUN curl -sSL https://install.python-poetry.org | python3 - +RUN chown -R root:root ${POETRY_HOME} && \ + chmod -R 0755 ${POETRY_HOME} + +# Don't use the root user as it's an anti-pattern and Heroku does not run +# containers as root either. +# https://devcenter.heroku.com/articles/container-registry-and-runtime#dockerfile-commands-and-runtime +USER guide + +# Install your app's Python requirements. +RUN python -m venv $VIRTUAL_ENV +COPY --chown=guide pyproject.toml poetry.lock ./ +RUN pip install --upgrade pip && poetry install ${POETRY_INSTALL_ARGS} --no-root + +COPY --chown=guide --from=frontend ./apps/frontend/static ./apps/frontend/static + +# Copy application code. +COPY --chown=guide . . + +RUN poetry install ${POETRY_INSTALL_ARGS} + +# Collect static. This command will move static files from application +# directories and "static_compiled" folder to the main static directory that +# will be served by the WSGI server. +RUN SECRET_KEY=none python manage.py collectstatic --noinput --clear + +# Run the WSGI server. It reads GUNICORN_CMD_ARGS, PORT and WEB_CONCURRENCY +# environment variable hence we don't specify a lot options below. +CMD gunicorn apps.guide.wsgi:application diff --git a/Makefile b/Makefile index 1af312aa..c4eb8911 100644 --- a/Makefile +++ b/Makefile @@ -45,13 +45,21 @@ run: poetry run python manage.py runserver docker-build: - docker build -t guide:latest --build-arg POETRY_INSTALL_ARGS="" -f Dockerfile . + docker-compose build docker-run: - docker run --name guide_latest -p ${PORT}:${PORT} --env-file .env guide:latest sh -c 'poetry run python manage.py runserver 0.0.0.0:${PORT}' + docker-compose up -docker-exec: - docker exec -it guide_latest /bin/bash +docker-shell: + docker-compose exec web bash + +docker-shell-frontend: + docker-compose exec frontend bash docker-init: - docker exec -it guide_latest /bin/bash -c "make backend" + docker-compose exec web poetry install + docker-compose exec web poetry run python manage.py migrate + docker-compose exec web poetry run python manage.py createcachetable + docker-compose exec web poetry run python manage.py createsuperuser + docker-compose exec web poetry run python manage.py buildfixtures + diff --git a/README.md b/README.md index 4a92683d..33b8c04d 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ To activate Poetry's virtual environment, run: ### Setting up development with Docker +**Requirements:** [Docker](https://www.docker.com/) and Docker Compose (Docker Compose is included with Docker Desktop for Mac and Windows). + 1. Create a `.env` file in the project root containing these variables, you can adjust the values to your preferences: ``` ALLOWED_HOSTS=localhost @@ -74,6 +76,14 @@ To activate Poetry's virtual environment, run: 4. Run the init script in the container: `make docker-init` 5. You should now have access to the project in your browser at `http://localhost:8000` +To access the app container, run: + +`make docker-shell` + +To access the frontend container to run linting, etc: + +`make docker-shell-frontend` + # Gitpod With Gitpod you can deploy a ready-to-code Wagtail Guide development environment with a single click to evaluate the code. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..83d84bac --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +version: '3' + +volumes: + node_modules: +services: + web: + build: + context: . + dockerfile: Dockerfile.localdev + extra_hosts: + - 'host.docker.internal:host-gateway' + command: poetry run python manage.py runserver 0.0.0.0:${PORT:-8000} + volumes: + - .:/app:delegated,rw + - node_modules:/app/node_modules/ + ports: + - '${PORT:-8000}:${PORT:-8000}' + environment: + SECRET_KEY: $SECRET_KEY + ALLOWED_HOSTS: $ALLOWED_HOSTS + PORT: ${PORT:-8000} + DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE:-apps.guide.settings.dev} + frontend: + build: + context: . + dockerfile: Dockerfile.localdev + target: frontend + working_dir: /app + volumes: + - ./:/app:cached,rw + - node_modules:/code/node_modules/ + command: tail -f /dev/null + restart: 'no'