Search code examples
ruby-on-railsrubydocker

Could not find any gems in Docker when i run rails console


When i start to run a Rails console in my docker container i got the error Could not find ..... in locally installed gems with the list of all gems

rails s or rails routes work fine and i don't got errors when i build

bundle install change nothing

The one way i found to fix it is with docker-compose run web bundle install --binstubs

I doesnt find same problems here with all gems bundle install issue and i would like know what is wrong in my Docker files

Dockerfile:

# Use the official Ruby 2.5.0 image as the parent image
FROM ruby:2.5.0-slim

# Install dependencies
RUN apt-get update -qq \
    && apt-get install -y \
    # Needed for certain gems
    build-essential \
    shared-mime-info \
    # Needed for postgres gem
    libpq-dev \
    # Needed for mysql
    default-libmysqlclient-dev \
    # Needed for redis
    libssl-dev \
    # Needed for elastic search
    libcurl4-openssl-dev \
    # Needed for foreman
    procps

# Set the working directory to /app
WORKDIR /app

# Copy the Gemfile and Gemfile.lock into the image and install gems
ENV BUNDLER_VERSION=2.3.26
COPY Gemfile Gemfile.lock ./
RUN gem install bundler -v $BUNDLER_VERSION && bundle install --jobs 20 --retry 5

# Copy the rest of the application code into the image
COPY . .

# Expose port 3000 for the Rails app to listen on
EXPOSE 3000

# Start the Rails app with Puma
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Docker-compose:

version: '3.3'

services:
  db:
    image: mysql/mysql-server:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: app_api_dev
      MYSQL_USER: app_api
      MYSQL_PASSWORD: app_api
    volumes:
      - ./tmp/db:/var/lib/mysql
    ports:
      - "3306:3306"
  redis:
    image: redis
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    depends_on:
      - db
      - redis
    environment:
      DATABASE_HOST: db
      DATABASE_USERNAME: app_api
      DATABASE_PASSWORD: app_api
      DATABASE_NAME: app_api_dev

Solution

  • The other answer doesn't make much sense:

    1. --binstubs does not install gems; it generates stubs in bin/ from the gems that are already installed as part of the bundle, allowing them to be invoked without prefixing the commands with bundle exec.
    2. Splitting the command into two separate RUN statements that run each command exactly the same as before has no impact on the outcome of the commands being run, it just results in two separate Docker layers which will increase the overall size of the Docker image for no reason.

    There are a few actual issues that I think you're seeing. First is that you're:

    1. Setting WORKDIR to /app in your container
    2. Using COPY . . to copy the contents of your app into /app
    3. Configuring Docker Compose to mount . into your Docker container at /app

    The result is that step 2 may as well not be happening at all; the files that you copied into /app are ignored and Docker Compose instead reads directly from your local computer's filesystem. This is probably not the intended behavior if you're expecting to build a Docker image that contains your app.

    This can be especially problematic if you're using a .dockerignore file and expect that certain files and folders are not being copied into the image; volumes don't respect .dockerignore and the entire directory is available at /app.

    And again this can be problematic if you're using bundle config options like BUNDLE_PATH and BUNDLE_CACHE_PATH that you don't expect to be loaded into your app. You might be expecting gems to be installed to and read from locations within your Docker image's system paths but if bundle config options you don't expect to have in place are pointing to a local path like vendor/bundle and vendor/cache then mounting the volume to the local path is going to use these pre-existing paths on your local system, not what's in the image.

    The second issue is that you're not running commands with bundle exec. You don't make it clear how you're attempting to run rails console or rails server or rails routes but these commands need to be prefixed with bundle exec, e.g., bundle exec rails console. For example, if you've already run docker compose up and the server is running:

    docker exec -it web bash
    

    And then when you're in your container:

    cd /app
    bundle exec rails console
    

    If you want to use binstubs instead then before you create your Docker image you need to run bundle binstubs --all on your local computer to create the stubs in bin/, then to invoke those commands using the binstubs you need to run them with their relative path included, e.g.:

    bin/rails console
    bin/rails server
    bin/rails routes
    

    You would need to do this anywhere that you would otherwise run bundle exec rails ....

    Finally, attempting to run them with just rails console or rails server is likely not going to work because it requires the Docker image itself to have those gems installed globally, and those global versions may not match your bundled versions. If you just run rails <something> then what happens is the system will look in $PATH to attempt to find a binary named rails -- it does not automagically find the version of Rails that you have defined in Gemfile.lock and does not automagically find the binary installed by bundle install. It just finds the first responsive binary which may or may not know about your bundled application and its dependencies. Always specify the command with either bundle exec rails ... or bin/rails ....