Контейнеризация существующего приложения Rails

Эта статья была первоначально написана Джеффри Морхоусом в блоге разработчиков Honeybadger.

Контейнеризация программного обеспечения — это упаковка его в стандартизированные блоки для облегчения разработки и развертывания. Контейнеры объединяют код вашего приложения вместе со всеми его зависимостями. Контейнер может быть полностью самостоятельным; он содержит пакет с вашим программным обеспечением, среду выполнения и системные библиотеки. Контейнеры помогают разработчикам и операционным командам обеспечить одинаковый запуск программного обеспечения независимо от его окружения. Благодаря отделению кода от инфраструктуры приложения, которые «контейнеризированы», работают одинаково в локальной среде, тестовой среде и на производстве.

Docker — одна из самых популярных платформ для разработки и развертывания программного обеспечения. Docker упаковывает программное обеспечение в виде «образа», который во время выполнения превращается в контейнер при выполнении на Docker Image. Изоляция позволяет разработчикам запускать множество контейнеров на одном хосте одновременно.

Разработчики Rails сталкиваются с уникальным набором проблем при контейнеризации существующего приложения. В этой статье мы рассмотрим процесс контейнеризации функционального приложения Rails и объясним важные концепции и подводные камни на этом пути. Эта статья не является базовым описанием контейнеров или Docker; вместо этого она представляет собой объяснение проблем, с которыми сталкиваются разработчики при контейнеризации производственных приложений.

Предварительные условия

Если вы следуете за мной, то вам, очевидно, понадобится приложение Rails, которое еще не докеризовано (это специфический термин Docker для «контейнеризации»).
Я буду использовать RailsWork, полнофункциональный сайд-проект, который я только что запустил. Это доска вакансий, написанная на Rails и развернутая на Heroku, но она не контейнеризирована.

Помимо этого, вам также потребуется установить Docker. Популярным способом установки является Docker Desktop, который можно загрузить с официального сайта.

После загрузки приложения запустите программу установки. После запуска он предложит вам перетащить приложение в папку приложений. Затем вам нужно будет запустить приложение из папки приложений и предоставить ему привилегированные разрешения, которые оно запрашивает. В качестве последней проверки правильности установки Docker попробуйте перечислить контейнеры, запущенные на вашей машине, в терминале, выполнив следующее:

docker ps
Войдите в полноэкранный режим Выйти из полноэкранного режима

Если Docker установлен (и у вас нет запущенных контейнеров), вы получите пустой список с заголовками, выглядящими следующим образом:

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
Войти в полноэкранный режим Выйти из полноэкранного режима

Dockerfile

Важно начать с четкой терминологии, прежде чем мы погрузимся слишком глубоко.

После того, как ваше Rails-приложение «докеризовано», оно будет работать в контейнере. Контейнер стоит отдельно, его можно заменять и часто перестраивать.

Контейнер создается из образа. Образ — это виртуальный снимок файловой системы в паре с метаданными.

Dockerfile — это исходный код, который описывает, как должен быть создан образ. Dockerфайлы часто включаются в репозиторий Dockerized приложения и отслеживаются в системе контроля версий вместе с остальной частью приложения.

Создать Dockerfile проще, чем кажется! Docker предоставляет нам специальный синтаксис, который абстрагирует нас от тяжелой работы по контейнеризации чего-либо. Сначала перейдите в корневой каталог приложения, которое вы хотите контейнеризировать. Теперь, когда вы готовы приступить к работе, хорошей идеей будет создать новую ветку, если вы используете git. Вы можете легко создать новую ветку с именем dockerize-this-app, выполнив следующие действия:

git checkout -b dockerize-this-app
Войдите в полноэкранный режим Выйти из полноэкранного режима

Далее создайте Dockerfile и направьте его на сборку образа на основе приложения Ruby. Это можно сделать из командной строки, выполнив следующее:

echo "FROM ruby:3.0.0" > Dockerfile
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь мы просто создаем Dockerfile и добавляем строку, указывающую, где найти образ контейнера Ruby. В моем проекте используется Ruby 3.0.0, поэтому я использовал соответствующий образ. Если вы используете другую версию Ruby, это не проблема. В Docker есть список всех поддерживаемых образов.

Далее вручную проинструктируйте Docker о создании образа Docker:

docker build -t rails_work .
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь вы можете заменить rails_work на любое имя образа, которое вам нравится. Также не забудьте включить точку в конце!

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

docker image list
Войти в полноэкранный режим Выйти из полноэкранного режима

Однако это изображение в основном пустое; в настоящее время оно не содержит нашего приложения. Мы можем указать ему добавить код из нашего приложения, добавив в конец Dockerfile следующее:

ADD . /rails_work
WORKDIR /rails_work
RUN bundle install
Войти в полноэкранный режим Выйти из полноэкранного режима

Это скопирует файлы из вашего приложения и установит зависимости приложения. (Здесь вы замените rails_work на название вашего приложения).

В этот момент необходимо повторно выполнить команду для создания изображения:

docker image list
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь может возникнуть проблема, особенно если вы делаете это с существующим производственным приложением. Bundler может пожаловаться, что версия Bundler, которую пытается использовать образ, отличается от той, которая создала файл Gemfile.lock. Если это произойдет, у вас есть два очевидных варианта:

  • Изменить версию, которую использует образ.
  • Удалить Gemfile.lock полностью. **Если вы это сделаете, убедитесь, что все версии Gems, которые вам нужны, привязаны к определенным версиям, так как файл блокировки будет полностью перегенерирован.

Если установка бандла по-прежнему не удается, то вам может потребоваться дополнительная установка в вашем Dockerfile:

RUN apt-get update && apt-get install -y shared-mime-info
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Если у вас все еще возникают проблемы, возможно, вы выбрали неправильный образ Ruby для основы, поэтому стоит начать исследования с него.

Это хорошая возможность установить переменные окружения:

ENV RAILS_ENV production
ENV RAILS_SERVE_STATIC_FILES true
Войти в полноэкранный режим Выйти из полноэкранного режима

Далее добавьте строку для открытия порта 3000, на котором Rails запускается по умолчанию:

EXPOSE 3000
Войти в полноэкранный режим Выйти из полноэкранного режима

И наконец, проинструктируйте контейнер открывать оболочку bash при запуске:

CMD ["bash"]
Enter fullscreen mode Выйти из полноэкранного режима

В целом, ваш Dockerfile должен выглядеть следующим образом (с замененным именем rails_work):

FROM ruby:3.0.0

ADD . /rails_work
WORKDIR /rails_work
RUN bundle install

ENV RAILS_ENV production
ENV RAILS_SERVE_STATIC_FILES true

EXPOSE 3000
CMD ["bash"]
Войти в полноэкранный режим Выйти из полноэкранного режима

Объяснение команд Docker

Безусловно, полезно иметь полное представление о некоторых наиболее распространенных командах Dockerfile.

  • FROM -> Это определяет, какой образ будет взят за основу.
  • RUN -> Это выполняет команды внутри контейнера.
  • ENV -> Определяет переменные окружения.
  • WORKDIR -> Это изменяет директорию, которую использует контейнер.
  • CMD -> Определяет, какую программу запускать при старте контейнера.

Docker Compose

Согласно документации Docker, «Compose» — это их инструмент для создания (и запуска) приложений с несколькими контейнерами Docker. Все необходимое для запуска необходимых контейнеров приложения излагается в YAML. Когда кто-то запускает docker-compose up, контейнеры создаются! Docker-compose позволяет нам декларативно описать нашу конфигурацию контейнеров.

Перед созданием файла Docker Compose важно указать Docker, какие файлы должны быть исключены из собираемого образа. Создайте файл с именем .dockerignore. (Обратите внимание на точку!) В этот файл вставьте следующее:

.git
.dockerignore
.env
Войти в полноэкранный режим Выйти из полноэкранного режима

Если ваш Gemfile поддерживается процессом сборки, то не забудьте добавить Gemfile.lock в игнорирование выше.

Далее создайте файл docker-compose.yml. Здесь мы опишем конфигурацию нашего контейнера. Начнем с этого содержимого файла:

version: '3.8'
services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    volumes:
      - postgres:/var/lib/postgresql/data
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/Rails-Docker
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes:
  postgres:
Войти в полноэкранный режим Выйти из полноэкранного режима

Этот файл создает два сервиса: один называется db, а другой — web. Контейнер db будет собран из готового образа, предназначенного для postgres, и вы должны подставить соответствующие значения для POSTGRES_USER и POSTGRES_PASSWORD. Вы должны быть осторожны, чтобы не поместить производственные секреты в этот файл — смотрите раздел «Управление секретами» ниже для получения дополнительной информации об этом.

Веб-контейнер собирается из нашего Dockerfile и затем запускает Rails-сервер на порту 3000 по IP-адресу 0.0.0.0. Внутренний порт 3000 затем сопоставляется с реальным портом 3000.

И, наконец, у нас есть том Postgres для хранения данных.

Управление секретами

Аутентификация во время сборки может быть проблемой для производственных приложений. Возможно, ваше приложение ищет Gems в частном хранилище, или вам просто нужно хранить учетные данные базы данных.

Любая информация, содержащаяся непосредственно в Dockerfile, навсегда сохраняется в образе контейнера, и это распространенная проблема безопасности.

Если вы используете менеджер учетных данных Rails, то предоставить доступ Docker (или любому другому хосту, если на то пошло) относительно просто. В файле Docker Compose вы просто указываете переменную окружения RAILS_MASTER_KEY. Для данной цели compose вы указываете ключ в заголовке environment, который вам нужно создать, если вы этого еще не сделали. Файл docker-compose, приведенный выше, будет выглядеть следующим образом:

version: '3.8'
services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    volumes:
      - postgres:/var/lib/postgresql/data
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/Rails-Docker
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      - RAILS_MASTER_KEY=this_would_be_the_key
volumes:
  postgres:
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь вы окажетесь на перепутье. Вы, вероятно, хотите, чтобы этот файл был зафиксирован в системе контроля исходных текстов, но вы определенно не хотите, чтобы ваш мастер-ключ или даже пароль базы данных отслеживался системой контроля исходных текстов, так как это будет еще одной опасной проблемой безопасности. Лучшим решением на данный момент будет использование гема dotenv, чтобы вы могли получить доступ к этим учетным данным через прокси, храня их в другом файле, который не отслеживается в системе контроля исходных текстов.

Запуск докеризованного приложения

Наконец, вы можете запустить докеризированное приложение с помощью следующей команды:

docker compose up
Войти в полноэкранный режим Выйти из полноэкранного режима

Хотите верьте, хотите нет, но это все! Docker-compose упрощает создание контейнера, особенно когда речь идет об аргументах командной строки.

Если вам нужен список запущенных контейнеров, просто выполните следующее:

docker ps
Войти в полноэкранный режим Выйти из полноэкранного режима

Если имя вашего контейнера Rails web, вы можете выполнять команды на нем довольно простым способом. Например, если вы хотите запустить консоль Rails, все, что вам нужно сделать, это выполнить следующее:

docker exec -it web rails console
Войти в полноэкранный режим Выйти из полноэкранного режима

Если же вам просто нужна оболочка bash внутри контейнера, то вместо этого нужно выполнить следующее:

docker exec -it web bash
Войти в полноэкранный режим Выйти из полноэкранного режима

Еще несколько подводных камней, подобных перечисленным здесь

Одна из распространенных проблем с докеризованными Rails-приложениями в производстве — это работа с журналами. Они не должны находиться в контейнерной системе длительное время. Docker предлагает просто перенаправлять журналы в STDOUT. Это может быть явно настроено в config/application.rb.

Другой распространенной проблемой являются почтовые программы. Если ваше приложение использует почтовые программы, вы должны явно определить параметры соединения. SMTP — отличный способ доставки и обычно хорошо работает с настройками по умолчанию, но мы должны быть внимательны, чтобы установить расположение сервера и другие настройки в соответствии с конфигурацией нашего контейнера.

Если у вас есть рабочие или фоновые задания, такие как sidekiq, то вы должны запускать их в собственном контейнере.

Заключение

Контейнеризация производственного Rails-приложения сопряжена с рядом трудностей, в чем вы, несомненно, убедились. По мере роста ваше приложение, вероятно, накопило ряд зависимостей, которые делают подобную миграцию сложной. Будь то фоновые рабочие, почтовые программы или секреты, существуют установленные шаблоны, позволяющие справиться с большинством подводных камней. После завершения начальной работы по обеспечению работы производственного приложения с Docker, легкость будущих изменений и развертывания сделает инвестиции оправданными.

Оцените статью
devanswers.ru
Добавить комментарий