3 ms·
Do you actually need hot reload? I structure my images like so to avoid long build times for images: - Base image that just has the run time (usually an alpine
by imauld 9y ago
Do you actually need hot reload? I structure my images like so to avoid long build times for images:
- Base image that just has the run time (usually an alpine image for Python, Java etc). These are left as generic as possible so they can be reused easily. This is usually an official image however you can also make your own.
- Dependencies image where the application dependencies that are unlikely to change are installed. For Python this means creating a virtual env and installing the required packages. Any system packages for Python that aren't likely to change (cffi, libjpeg, openssl etc) will go here as well. Dockerfile.base in the example below.
- Application image. Copy in the code/configuration for Python or the binary for Java. If you are developing on Mac and compiling for Linux you can do this with a multistage build so you can be sure anything you compile will be for the right architecture.
Examples:
Dockerfile.base
# Alpine images are great because they are small but if you are still early in development
# you may want to use Debian/Ubuntu/Jessie as they are a more "complete" OS and will make it
# to debug inside the container. Alpine linux doesn't even come with bash (it has sh)so it can be difficult
# to figure out what is wrong if you encounter some weirdness
FROM python:alpine3.6
You can obviously put this anywhere but this usually what I do
RUN mkidr /app
COPY requirements.txt /app/requirements.txt
RUN python3 -m venv /venv && \
/venv/bin/pip install -r /app/requirements.txt
Dockerfile
# Image created from previous example
FROM base:0.0.1
# Assuming your Dockerfile is a sibling to your src directory
COPY ./your_package /app/
And that's it. Unless your project is a huge monolith new images should be rebuilt very quickly. This takes heavy advantage of Docker's layer caching. Generally only the very last thing that is built will be changing so you won't have to rebuild the previous layers. I usually don't include CMD, RUN, or ENTRYPOINT in my Dockerfiles. I leave that up to whatever I'm using to run them, Docker Compose or Kubernetes. I also leave environment configuration up to the tool doing the running as well. Things like environment variables, exposed ports and secrets are usually better handled by these tools than by the image itself. Volume mounts are also best setup via compose or k8s rather than being baked in to the image. Example:
docker-compose.yaml
version: "3"
services:
yourapp:
# The image we built earlier
image: sidcool/yourapp:app-0.1.0
# Expose port 8080 of the container on port 8080 of the host
ports:
- "8080:8080"
# How to run the app
command: "/venv/bin/gunicorn -c app/config.py app.server:app"
# Lets compose know that this service needs the db service to start
links:
- db
db:
image: postgres:10
# Use a volume to persist DB data between container restarts
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
However if hot reload is a requirement you can achieve that with mounting the code inside the container via volume mount. That way you can edit the code on your host with whatever tools your used to and the changes will be reflected in the container fairly quickly. This adds a bit of complexity and can be slow if your copying large numbers of files over.