The smartest way to create a multistage Dockerfile for a python application and be compatible with OpenShift non-root / arbitrary user is to use virtual environments.
FROM python:3.9-alpine3.13 as base
# in the builder, install build-base and build some python modules
FROM base as builder
RUN apk add --no-cache --virtual .build-deps build-base
# Always update pip
RUN pip install --upgrade pip
# Create an Virtual Environent
# and activate it Itamar style (see link to pythonspeed)
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir "uvicorn[standard]" gunicorn
ADD ./requirements.txt /
RUN pip install --no-cache-dir -r /requirements.txt
# Then in the runner, copy in the virtual environment and the app with config
FROM base
COPY --from=builder /opt/venv /opt/venv
COPY ./docker-scripts/start.sh ./docker-scripts/gunicorn_conf.py /
COPY ./app/ /app/
RUN chmod +x /start.sh
WORKDIR /app/
ENV PYTHONPATH=/app:/opt/venv
ENV PATH=/opt/venv/bin/:$PATH
EXPOSE 8000
# And then will start Gunicorn with Uvicorn
CMD ["/start.sh"]
This article is inspired by the great article on pythonspeed and the Dockerfile and start.sh and gunicorn_conf.py is inspired and mostly copied from Sebastián Ramírez's fastapi docker image