skip to Main Content

I am building a docker container for a couple of services and it occured to me that I don’t really understand how to properly decide where to place certain commands.

It seems to me there are 2 alternatives when it comes to running commands on a Docker container.

  • Using the RUN instruction (e.g RUN composer -n install)
  • Running the same command above inside a script that is used as entrypoint for the container (i.e inside docker-entrypoint.sh)

With regards to best practices, how should one decide when to use one approach vs the other? Specially with regards to containers that are not meant to be the base for any others, should commands be entirely contained in the Dockerfile, with only the application startup be done with the entrypoint, or should some commands be moved to the entrypoint script?

Thanks.

2

Answers


  1. Running commands at build time will increase the image size, while running them at startup will obviously increase the startup time. Is there anything else to consider when choosing one vs the other?

    Everything that has to do with the building and configuration of software should take place in the build stage. The entrypoint should only perform the minimum set of tasks required to get the container up and running properly.

    If you are concerned about image size, then you should most certainly adopt the best practice of combining statements in a single RUN command, as this will result in a single layer (so less overall size), e.g.:

    RUN echo hello && 
        echo world
    

    You may also look into multi-stage builds, which can help in reducing the final size of the image.

    For reference: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#minimize-the-number-of-layers

    Login or Signup to reply.
  2. You can run multiple copies of a container off of a single image. A Dockerfile RUN command only runs once when the image is built; the entrypoint script runs every time you start the container.

    As a general rule, I might suggest doing as much as you can in the Dockerfile. There are some parts of the system that aren’t available until runtime, like environment-variable settings and other containers; you can’t access these until the container starts, which means the entrypoint script.

    So, for example:

    • You should RUN composer ... and other commands that install software or libraries in the Dockerfile; these don’t depend on the runtime environment and you don’t want to repeat them every time the container runs.
    • If you have database migrations, you should COPY the migration files into the image, but the database isn’t available at image-build time, so the actual migrations need to be run in the entrypoint script.
    • If you need to interact with the contents of volumes, those aren’t available until the container starts, and this needs to be done in the entrypoint script. (But it’s better to avoid needing this; prefer a database in another container to using mounted files for storage.)

    I’m a little less familiar with PHP, but a typical Python example might look like:

    # Dockerfile
    FROM python:3.11
    WORKDIR /app
    
    # Install this application's library dependencies
    # (Only once, during the image build)
    COPY requirements.txt ./
    RUN pip install -r requirements.txt
    
    # Copy in the rest of the application code
    # (Including the entrypoint script and database migrations)
    COPY ./ ./
    
    # Metadata to run the application
    EXPOSE 8000
    ENTRYPOINT ["./entrypoint.sh"]
    CMD ["./manage.py", "runserver", "0.0.0.0:8000"]
    
    #!/bin/sh
    # entrypoint.sh
    
    # Run migrations at application startup.  The database isn't
    # available until now, and its contents are independent of
    # this image.  Gets repeated every time the container starts.
    ./manage.py migrate
    
    # Switch to the main container `CMD`.
    exec "$@"
    
    # docker-compose.yml
    version: '3.8'
    services:
      app:
        build: ./
        ports: ['8000:8000']
        environment:
          PGHOST: db
          other PostgreSQL-related variables: as well
        # Note, no volumes:
      db:
        image: postgres:14
        environment:
          POSTGRES_PASSWORD: passw0rd
          et: cetera
        volumes:
          - dbdata:/var/lib/postgresql/data
    volumes:
      dbdata:
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search