ruby-on-railsdockerdocker-composerakedocker-entrypoint

docker-compose service build path not found


I have a rake task in my application, that should start a TCP server. I have moved rake command to an entrypoint.sh file and in docker-compose added new service, which is called tcp.

That tcp service return incorrect path, when I run docker-compose up -d. I tried different ways to setup, but no luck. How do I build docker image for a rake task?

unable to prepare context: path "/Users/mac_user/Projects/fmt100/lib/tasks/socketing.rake" not found

docker-compose.yml

version: '3.9'
services:
tcp:
    build: ./lib/tasks/socketing.rake
    depends_on:
      - app
      - database
      - redis
      - sidekiq
    env_file: .env
    volumes:
      - .:/app
      - tcp_server:/app/lib/tasks
    entrypoint: ./entrypoints/tcp-entrypoint.sh

volumes:
  tcp_server:

Solution

  • You should have it build: the same image that contains your Rails application. Override the command: to run the Rake task. In the Compose file it should look roughly like:

    version: '3.8'
    services:
      tcp:
        build: .
        depends_on: [database, redis]
        env_file: .env
        command: rake socketing
      app:
        build: .
        depends_on: [database, redis]
        env_file: .env
        ports: ['3000:3000']
      database: { ... }
      redis: { ... }
      sidekiq: { ... }
    

    The sidekiq container probably looks very similar.

    Since you have two containers labeled build: ., Compose will try to build the image twice. However, these images have identical Dockerfiles and run on an identical source tree. This means that Docker's layer caching will take effect, and the second build will run extremely quickly and will not actually produce a new image.

    To make this work correctly, you also need to correctly structure your Dockerfile. It's important here that you put the actual command to run as the Dockerfile CMD and not the ENTRYPOINT (or at the very least it must match the override you have in the docker-compose.yml). As always, make sure the image is self-contained and includes all of its library dependencies and application code (note the absence of volumes: in the Compose file above).

    FROM ruby:2.7
    WORKDIR /app
    RUN gem install bundler:2.3.12
    COPY Gemfile Gemfile.lock .
    RUN bundle install
    COPY . .
    EXPOSE 3000
    # ENTRYPOINT ["./entrypoint.sh"]
    CMD rails server -b 0.0.0.0
    

    You should not put the command in the ENTRYPOINT or an entrypoint script. Instead, use the entrypoint to do some first-time setup, and maybe inject related wrappers like Bundler.

    #!/bin/sh
    # entrypoint.sh
    
    # Run database migrations, but only if we're running the main server.
    if [ "$1" = rails -a "$2" == server ]; then
      bundle exec rake db:migrate
    fi
    
    # Run whatever command we were given under Bundler.
    exec bundle exec "$@"
    

    The command: is very straightforward to override, and whatever you provide appears in the "$@" in the final statement of the script. So if you want a Pry console, for example, you can

    docker-compose run app \
      rails console
    

    which will override the command: at the command line, leaving the image's ENTRYPOINT unchanged. The sample Compose file does the same thing for your TCP server launched as a Rake task (and, again, I'd expect your Sidekiq worker to work the same way).