I just create a single stage dockerfile in my basic CRUD application.
I want to multistaging because of reducing space of my docker image.
Dockerfile
FROM python:3.9-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Set the working directory in the container
WORKDIR /app
# Copy the dependencies file to the working directory
COPY requirements.txt .
RUN apt-get update && apt-get install -y default-mysql-client netcat-traditional && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN pip3 install -r requirements.txt
# Copy the content of the local src directory to the working directory
COPY . .
COPY wait-for.sh .
# Expose port 8000 to the outside world
EXPOSE 8000
RUN chmod +x wait-for.sh
CMD ["uvicorn", "index:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
So I do this in my Dockerfile
Dockerfile.dev
FROM python:3.9-slim as BUILD
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
RUN apt-get update && apt-get install -y default-mysql-client netcat-traditional && rm -rf /var/lib/apt/lists/*
COPY . .
#CMD
FROM python:3.9-slim
COPY --from=BUILD . .
COPY wait-for.sh .
# Expose port 8000 to the outside world
EXPOSE 8000
RUN chmod +x wait-for.sh
CMD ["uvicorn", "index:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
But the second step cannot hold the index.py file.
docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev #Here change the Dockerfile and Dockerfile.dev
#Dockerfile for single stage and Dockerfile.dev for multistage
ports:
- 8000:8000
# restart: on-failure
environment:
DATABASE_HOST: db
DATABASE_USER: root
DATABASE_PASSWORD: 12345678
DATABASE_NAME: Student
depends_on:
- db
entrypoint: [ "./wait-for.sh", "db:3306", "--", "uvicorn", "index:app", "--reload", "--host", "0.0.0.0", "--port", "8000" ]
db:
image: mysql:5.7
volumes:
- ./my.cnf:/etc/mysql/my.cnf
environment:
MYSQL_USER: user
MYSQL_ROOT_PASSWORD: 12345678
MYSQL_DATABASE: Student
MYSQL_PASSWORD: 12345678
ports:
- "3307:3306"
My question is "is it ever possible to dockerize my fastapi application with multistage docker?"
If it is possible how can I do this. Also If it is not possible then why can’t I do this.
What will be the entrypoint of this?
2
Answers
The image you’ve shown won’t benefit from a multistage build.
The setups where multistage builds save space are if there are tools you need to build your image, but not to actually run the container. For a Python application, libraries with C extensions need the C compiler, which is quite large, but only at installation time. For those sorts of applications, the first stage can install the compiler and C header files, but the second stage only needs the corresponding runtime libraries.
An example Python-based Dockerfile could look like this. Note that we’re using the exact same base image for both the build and runtime stage, and the virtual environment is in the same directory. The first stage installs
build-essential
, which includes a full C toolchain, but is quite large; the second stage doesn’t need that.You also asked about a correct value for an entrypoint. This doesn’t change in a multistage build, noting that only the value from the final image is used.
You shouldn’t normally need to override the
entrypoint:
in the Compose file, and many cases do not need to overridecommand:
either. I might move the call towait-for.sh
into the DockerfileA more robust solution could be to wrap it in a dedicated shell script, that honors the deploy-time database hostname
This script can be the image’s
ENTRYPOINT
(make sure to use JSON-array syntax), and you don’t need to put anything special in the Compose file.Yes, you can create docker image with multistage-build for application that uses fastAPI. For your second question the entry point will remain the same.
Although for cases like this multi-stage build is likely to provide less value (save less space) for interpreted languages like python as your specific case. If you still wish to approach this, You can approach it in a similar way for creating different stages for build and runtime environment for compiled languages.
You can consider the below example:
For the second stage you can create a runtime environment for your purpose.
keep in mind that multistage builds usually benefit cases where you need to build the container without running or running the container doesn’t require entirety of the build tools. David Maze‘s answer is more appropriate for you specific use case.