Почему bundler exec не может загрузить команду rails в мою точку входа Docker?

#ruby-on-rails #ruby #docker #bundler

#ruby-on-rails #ruby #docker #bundler

Вопрос:

Я создал образ и контейнер Docker с помощью docker-compose, который состоит из приложения Rails, поддерживаемого PostgreSQL. Rails предназначен для запуска через docker-entrypoint.sh файл, который выглядит следующим образом:

 #!/bin/sh

# Exit early if there are any errors.
set -e

# Check that there won't be any server conflicts.
if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi

# -b binds the server to all IP addresses, rather than to localhost.
bundle exec rails s -b 0.0.0.0
  

Однако контейнер останавливается сразу после запуска с этой ошибкой:

 bundler: failed to load command: rails (/usr/local/bundle/bin/rails)
LoadError: cannot load such file -- /usr/local/bundle/specifications/exe/rails
  /usr/local/bundle/bin/rails:23:in `load'
  /usr/local/bundle/bin/rails:23:in `<top (required)>'
  

Поскольку, насколько мне известно, вы не можете напрямую исследовать файловую систему остановленного контейнера, я экспортировал всю файловую систему в .tar файл:

 docker export a2410e604db9 > container.tar
  

Заглянув внутрь container.tar , я обнаружил, что там есть /usr/local/bundle/bin/rails исполняемый файл, поэтому я не знаю, почему bundle не сможет загрузить эту команду.

Другой каталог, упомянутый в ошибке, /usr/local/bundle/specifications/exe/rails не существует. Есть только /usr/local/bundle/specifications , который содержит кучу .gemspec файлов для проекта Rails и ничего больше.

Что вызывает эту bundler / LoadError проблему?

Комментарии:

1. Как вы запускаете контейнер? Если изображение не настроено на его игнорирование (предоставленный вами скрипт запускается как изображение CMD , а не его ENTRYPOINT ), вы можете docker run --rm -it your-image bash создать интерактивную оболочку для просмотра изображения.

2. @DavidMaze Я знал об интерактивной оболочке, но мой контейнер останавливается сразу после его запуска, поэтому, насколько я могу судить, получить интерактивную оболочку невозможно. Приведенный выше скрипт выполняется как ENTRYPOINT , однако, в моем Dockerfile: ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"] .

3. Если вы измените это на CMD , вы можете переопределить его во время выполнения, как показано выше. Вы также можете разделить ENTRYPOINT и CMD ; если вы создадите последнюю строку скрипта, exec bundle exec "$@" тогда любая переданная вами команда (по умолчанию: CMD rails s -b 0.0.0.0 ) будет запущена в контексте пакета.

4. @DavidMaze Не уверен, что понимаю. Я изменил последние две строки моего Dockerfile на ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"] и CMD rails s -b 0.0.0.0 . Я изменил последнюю строку моего скрипта точки входа на exec bundle exec "$@" . Контейнер все еще разбился сразу после запуска. Затем я запустил docker run --rm -it 64e8e33e8fc1 bash , и сервер Rails запустился прямо в консоли — без интерактивной оболочки, которая, как я думал, была целью, — но сервер Rails недоступен в браузере, как будто он не был запущен. Я полностью потерян.

5. Хорошо, я смог получить оболочку, отказавшись от попытки в моем предыдущем комментарии и просто изменив ENTRYPOINT на CMD , и ничего больше. Затем я мог бы запустить docker run --rm -it 93350aeeab9c sh , чтобы вставить оболочку в изображение, которое не удалось запустить. Так что это уже кое-что.

Ответ №1:

Какой образ docker вы используете для своего приложения Rails? Если это общедоступный library/ruby , то я предполагаю, что все необходимые env переменные будут установлены правильно для работы вашего скрипта.

Я предлагаю поделиться вашим Dockerfile , если это не общедоступный ruby.

Сначала посмотрите, PATH (и сценарий) использует ли ваш пакет и rails:

 docker run -it <YOUR_DOCKER_IMAGE> bash
which bundle
which rails
  

Если нет, попробуйте сначала запустить его вручную, прежде чем писать сценарий для диагностики проблем:

 docker run -it <YOUR_DOCKER_IMAGE> bash
cd /path/to/app
bundle exec rails s -b 0.0.0.0
  

Наконец, убедитесь, что ваш docker-compose.yml файл предоставляет порт, который вы ожидаете. Если это 4000 :

 ports:
      - "4000:4000"