Dockerfile ========== Dockerfile instructions ----------------------- Reference: `Dockerfile reference `__, Best practices for `Dockerfile instructions `__ USER ~~~~ - Use the ``user:group`` form for the `USER `__ instruction, unless you want the group to be ``root``. - Use the name ``runner`` for the `non-root user `__: .. code-block:: docker RUN groupadd -r runner && useradd --no-log-init -r -g runner runner WORKDIR ~~~~~~~ - Use a leading ``/`` with the ``WORKDIR`` instruction. - Set `WORKDIR `__ to ``/workdir``: .. code-block:: docker WORKDIR /workdir COPY ~~~~ - Use the ``--chown=user:group`` option with the `COPY `__ instruction, unless you want the ownership of the files to be ``root:root``. - Prefer the ``COPY`` instruction to the `ADD `__ instruction, `as recommended `__. Layer order ----------- To `leverage the build cache `__, order the instructions from least-to-most likely to change over time. In general, the order is: #. Declare the base image #. Install system packages #. Create a non-root user #. Copy requirements files #. Install project dependencies #. Set the working directory #. Switch to the non-root user #. Copy project files .. note:: For Node, set the working directory before copying requirements files. Base images ----------- For Python, use the default image, `as recommended `__, of the minor version, to ensure predictable behavior. Do not use the ``-slim`` or ``-alpine`` versions. For Node, use the default image, `as recommended `__, of the major version. Do not use the ``-slim`` or ``-alpine`` versions. For a web server, use the `nginxinc/nginx-unprivileged:latest `__ image. Note that the default port is changed to 8080 (instead of 80). Set ``server_tokens off;`` to prevent false positives from penetration tests (Ubuntu backports security patches, without changing version numbers). .. note:: For reference, the default ``/etc/nginx/conf.d/default.conf`` file in the Nginx image is: .. literalinclude:: samples/default.conf :language: nginx .. To update the samples/default.conf file: docker pull nginxinc/nginx-unprivileged docker run -it --entrypoint sh nginxinc/nginx-unprivileged $ cat /etc/nginx/conf.d/default.conf .. seealso:: `nginx playground `__ System packages --------------- Before installing a system package, check whether it's included in a base image. For example, the ``psycopg2`` Python package `requires `__ the ``libpq-dev`` system package. To check whether it's included, when using the `python:3.10 image `__: #. Find the tag on the DockerHub page of the base image (the ``3.10`` tag is under *Shared Tags*) #. Click the link to view the Dockerfile #. Check the ``apt-get install`` commands for the package name #. If not found, look for ``FROM`` instructions #. Repeat from step 1 for the ``FROM`` image(s) We find that the `buildpack-deps:bullseye image `__ installs the ``libpq-dev`` system package. If it's not included, install it following `best practices `__: .. code-block:: docker RUN apt-get update && apt-get install -y --no-install-recommends \ package-a \ package-b \ package-c \ && rm -rf /var/lib/apt/lists/* Bind mounts ----------- .. note:: In general, do not use absolute paths on the host's filesystem as an API between projects, because the projects might not share the same filesystem, like in the case of Docker containers. Instead, use paths relative to a configurable setting. If a project needs to read or write data to the filesystem: #. Add a setting with a default value. For example, for a :ref:`Django project`: .. code-block:: python :caption: settings.py KINGFISHER_COLLECT_FILES_STORE = os.getenv( "KINGFISHER_COLLECT_FILES_STORE", "/data" if production else BASE_DIR / "data" ) #. Create the directory using the default value in the Dockerfile. For example: .. code-block:: docker :caption: Dockerfile # Must match the settings.KINGFISHER_COLLECT_FILES_STORE default value. RUN mkdir -p /data && chown -R runner:runner /data #. `Mount `__ the host's directory to the default value in the Docker Compose file. For example: .. code-block:: yaml :caption: docker-compose.yaml services: django: volumes: - /data/storage/kingfisher-collect:/data If a project needs to read or write data to multiple directories, set the default values to subdirectories of the ``/data`` directory. Templates --------- .. note:: If Dockerfiles are similar across projects, we can consider creating our own base images and using the `ONBUILD `__ instruction to copy source code. Python ~~~~~~ .. literalinclude:: samples/Dockerfile_python :language: docker .. seealso:: :doc:`django` Node ~~~~ See: - `Dockerising a Node.js web app `__ - `Best Practices Guide `__ - `Node.js Language-specific guide `__