cenobites / flask-jsonrpc

Basic JSON-RPC implementation for your Flask-powered sites
BSD 3-Clause "New" or "Revised" License
289 stars 63 forks source link

Containerisation: Proposals for improvement and optimisation #597

Open alexted opened 2 weeks ago

alexted commented 2 weeks ago
  1. Based on these and other freely available materials, I propose to switch to using slim images instead of alpine. https://pythonspeed.com/articles/alpine-docker-python/ https://pythonspeed.com/articles/base-image-python-docker-images/

  2. Rename docker-compose.yml to compose.yaml With the latest version of Docker Compose, the default config file name is compose.yaml, although the docker compose command still supports existing docker-compose.yml files for backwards compatibility. We have therefore started to rename our development configuration files and will change other configuration examples (and our documentation) over time as we gain confidence that all users have a current version of Docker Compose installed. In addition, specifying a version at the beginning of compose.yaml files is deprecated. If you see a warning related to this, simply remove the first line ("version: 3.5" in our examples) from your config files. Related Docs & References: https://compose-spec.io/ https://docs.docker.com/compose/compose-application-model/ https://github.com/compose-spec/compose-spec/blob/master/spec.md

  3. To reduce the number of Dockerfiles and boilerplate code, you can create a single, reusable Dockerfile that accepts the Python version as a build argument. This allows you to use the same Dockerfile to build images with different Python versions, simplifying your setup and maintenance. Additionally, the compose file can be optimized to use a single, universal service that works for different Python versions by passing the desired version as an environment variable.

Step 1: Universal Single Dockerfile

Create a unified Dockerfile, such as Dockerfile.test, that uses a build argument for specifying the Python version:

# Dockerfile.test
ARG PYTHON_VERSION=3.12
FROM python:${PYTHON_VERSION}-alpine

ENV PYTHONUNBUFFERED=1 \
    PRAGMA_VERSION=py${PYTHON_VERSION} \
    DEBUG=0

WORKDIR /code

COPY requirements/ /code/requirements/

RUN set -ex \
    && apk add --no-cache --virtual .build-deps \
        gcc \
        musl-dev \
        python3-dev \
        git \
    && pip install pip setuptools wheel --upgrade \
    && pip install -r requirements/base.txt \
    && pip install -r requirements/style.txt \
    && pip install -r requirements/tests.txt \
    && pip install poetry-core>=1.0.0 \
    && apk del .build-deps \
    && addgroup -S kuchulu \
    && adduser \
        --disabled-password \
        --gecos "" \
        --ingroup kuchulu \
        --no-create-home \
        -s /bin/false \
        kuchulu

ARG VERSION=1
RUN echo "Version: ${VERSION}"

COPY . /code/

RUN chown kuchulu:kuchulu -R /code

USER kuchulu

Here, ARG PYTHON_VERSION=3.12 defines the default Python version (3.12), which can be modified during build time. This single Dockerfile now supports different Python versions as needed, making maintenance easier.

Step 2: Update the compose File to Use One Universal Service

Instead of having multiple services, you can simplify the compose file by defining a single service that accepts the Python version as a parameter. The Python version is passed through an environment variable PYTHON_VERSION, so the configuration remains flexible.

services:
  python:
    build:
      context: .
      dockerfile: Dockerfile.test
      args:
        PYTHON_VERSION: ${PYTHON_VERSION:-3.12}  # Defaults to 3.12 if not specified
    environment:
      - PRAGMA_VERSION=py${PYTHON_VERSION:-3.12}  # Defaults to 3.12 if not specified
    command: >
      sh -c "ruff check . &&
        pytest"

Running with Different Python Versions

You can now specify the Python version at runtime by setting the PYTHON_VERSION environment variable. Here’s how you would run compose with various versions:

PYTHON_VERSION=3.13 docker compose -f compose.test.yml up --build
PYTHON_VERSION=3.12 docker compose -f compose.test.yml up --build
PYTHON_VERSION=3.11 docker compose -f compose.test.yml up --build

With this setup:

This approach removes the need to duplicate services in compose, as a single universal service adapts to the specified Python version.

nycholas commented 1 week ago

@alexted Awesome! Perfect!

1) I agree! 2) Does change will reduce to three Dockerfiles (Dockerfile, Dockerfile.test, Dockerfile.it)? 3) For the case of docker-compose.{it,test}.yml, will it be compose.it.yml and compose.test.yml?

Thank you!

nycholas commented 1 week ago

By the way, the proposal of a universal single Dockerfile for each Python version supported is awesome.

alexted commented 1 week ago

Does change will reduce to three Dockerfiles (Dockerfile, Dockerfile.test, Dockerfile.it)?

Yes, that's exactly right. Probably in the future we can reduce everything to one Dockerfile, if you explain to me what Dockerfile.it and Dockerfile.local are used for now.

For the case of docker-compose.{it,test}.yml, will it be compose.it.yml and compose.test.yml?

Yes, it's. Likewise, I'm sure the compose files can be reduced to a single universal one as well. First I need to know why there are so many of them now and what each of them is for.

By the way, the proposal of a universal single Dockerfile for each Python version supported is awesome.

In programming, as in life, one should follow the principle of Occam's razor.

nycholas commented 1 week ago

@alexted I got it, perfect! Proposal accept! 🚀