Docker замечательно справляется с изолированием приложений и их окружений, облегчая распространение и репликацию состояний между различными средами (dev, test, beta, prod и т. д.). Его использование позволяет избавиться от проблемы «на моей машине все работает» и помогает с легкостью масштабировать приложение по мере его роста.
Docker особенно хорош в том случае, когда у приложения много зависимостей или оно требует использования специфических версий библиотек и инструментов конфигурирования.
В этой статье мы возьмем простое приложение на Rails и подготовим его для использования в Docker-контейнере («докеризуем»).
Необходимые компоненты
Наше приложение будет написано под Rails 5; базу данных возьмем PostgreSQL. Если вы хотите подключить другую СУБД, то потребуется поправить несколько файлов.
Вы можете воспользоваться заранее подготовленным шаблоном для создания приложения, которое сконфигурировано с помощью Dockerfile
и config/database.yml
:
$ rails new --database=postgresql --skip-bundle --template=https://gist.githubusercontent.com/cblunt/1d3b0c1829875e3889d50c27eb233ebe/raw/01456b8ad4e0da20389b0b91dfec8b272a14a635/rails-docker-pg-template.rb my-app
$ cd my-app
Конфигурация базы данных
Для задания параметров базы данных мы воспользуемся переменными окружения. Они понадобятся позже для подключения к контейнеру с PostgreSQL.
Отредактируйте файл конфигурации config/database.yml
Если вы воспользовались приведенным выше шаблоном, то редактировать файл не нужно.
Добавьте в config/database.yml
переменные окружения:
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: db
username: <%= ENV.fetch('POSTGRES_USER') %>
password: <%= ENV.fetch('POSTGRES_PASSWORD') %>
development:
<<: *default
database: my-app_development
test:
<<: *default
database: my-app_test
production:
<<: *default
database: my-app_production
Создание Dockerfile
Наше приложение подготовлено, настало время для Docker. Начнем с создания Dockerfile
. Это простой текстовый файл, в котором содержатся инструкции по созданию образа для приложения. Его используют для установки зависимостей, задания переменных окружения по умолчанию, копирования кода в контейнер и т. д.
Для экономии дискового пространства я предпочитаю использовать базовый образ alpine-linux Ruby. Alpine linux — крошечный linux-дистрибутив, идеально подходящий для использования в контейнерах. В Docker доступен базовый образ ruby:alpine
, которым мы и воспользуемся.
Начнем с создания простого Dockerfile
, который необходимо поместить в корневую директорию приложения.
Если вы воспользовались приведенным выше шаблоном, то редактировать файл не нужно.
# /path/to/app/Dockerfile
FROM ruby:2.3-alpine
# Установка часового пояса
RUN apk add --update tzdata &&
cp /usr/share/zoneinfo/Europe/London /etc/localtime &&
echo "Europe/London" > /etc/timezone
# Установка в контейнер runtime-зависимостей приложения
RUN apk add --update --virtual runtime-deps postgresql-client nodejs libffi-dev readline sqlite
# Соберем все во временной директории
WORKDIR /tmp
ADD Gemfile* ./
RUN apk add --virtual build-deps build-base openssl-dev postgresql-dev libc-dev linux-headers libxml2-dev libxslt-dev readline-dev &&
bundle install --jobs=2 &&
apk del build-deps
# Копирование кода приложения в контейнер
ENV APP_HOME /app
COPY . $APP_HOME
WORKDIR $APP_HOME
# Настройка переменных окружения для production
ENV RAILS_ENV=production
RACK_ENV=production
# Проброс порта 3000
EXPOSE 3000
# Запуск по умолчанию сервера puma
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
А что если я не хочу использовать PostgreSQL?
Если вы используете другую СУБД (например, MySQL), то для установки соответствующих пакетов потребуется внести изменения в Dockerfile.
Произвести поиск необходимых пакетов можно с помощью следующей команды Docker:
$ docker run --rm -it ruby:2.3-alpine apk search --update mysql | sort
...
mariadb-client-libs-10.1.22-r0
mariadb-dev-10.1.22-r0
mariadb-libs-10.1.22-r0
mysql-10.1.22-r0
mysql-bench-10.1.22-r0
...
Поскольку Dockerfile уже готов, пора запустить сборку Docker-образа для нашего приложения:
Собираем образ
$ docker build . -t my-app
Образ готов, можно начинать! Запустите контейнер следующей командой:
$ docker run --rm -it --env RAILS_ENV=development --env POSTGRES_USER=postgres --env POSTGRES_PASSWORD=superSecret123 --publish 3000:3000 --volume ${PWD}:/app my-app
Мы передали команде docker run
несколько аргументов:
-it
— на самом деле это 2 аргумента, которые позволяют взаимодействовать с контейнером с помощью командной оболочки (например, чтобы передать комбинацию клавиш Ctrl+C);--env
— позволяет передать контейнеру переменные окружения. Здесь они используются для установки параметров подключения к базе данных;--rm
— говорит докеру удалить контейнер после завершения его работы (например, после нажатия Ctrl+C);--publish
— пробрасывает порт 3000 контейнера на порт 3000 хоста. Таким образом у нас появляется возможность подключиться к сервису так, как будто он запущен напрямую на хосте (например,http://localhost:3000
);--volume
— говорит докеру подмонтировать в контейнер текущую директорию хоста. Таким образом вы получаете возможность редактировать код на хосте, но при этом он будет доступен в контейнере. Без этого вам пришлось бы после каждого изменения кода заново создавать контейнер.
Запуск контейнера базы данных
Хотя контейнер с приложением и запустился, попытка открыть ссылку localhost:3000, к сожалению, приведет к ошибке:
could not translate host name “db” to address: Name does not resolve
У нас пока нет доступного приложению PostgreSQL-сервера. Сейчас мы это починим, запустив Docker-контейнер с PostgreSQL:
Совет. Не забывайте, что в Docker один контейнер должен выполнять одну и только одну функцию.
В нашем случае будет 2 контейнера: один для приложения и один для базы данных (PostgreSQL).
Запуск нового контейнера с PostgreSQL
Для остановки (и удаления) контейнера с приложением нажмите Ctrl+C, затем запустите новый контейнер с PostgreSQL:
$ docker run -d -it --env POSTGRES_PASSWORD=superSecret123 --env DB_NAME=my-app_development --name mydbcontainer postgres:9.6
Флаг -d
нужен для того, чтобы отсоединить контейнер от терминала, позволяя ему работать в фоновом режиме. Контейнер мы назовем mydbcontainer
, это имя нам понадобится дальше.
Использование однозадачных (Single–Task) контейнеров
Docker-контейнеры предназначены для однократного употребления, а их однозадачная натура означает, что, как только они выполнили свою задачу, их останавливают и, может быть, удаляют.
Они идеальны для разовых задач, таких как команды rails (например, bin/rails db:setup
).
Для настройки базы данных в mydbcontainer мы сейчас и выполним такую команду .
Выполнение задачи rails db:migrate
с использованием контейнера
Для запуска копии контейнера с приложением выполните следующую команду. Затем запустите в контейнере bin/rails db:setup
и выключите его.
Обратите внимание: вам потребуется настроить переменные окружения для соединения с базой данных (они вставляются в config/database.yml
, который вы ранее редактировали).
Опция --link
позволит подключиться к контейнеру с PostgreSQL (mydbcontainer
), используя имя хоста db
:
$ docker run --rm --env RAILS_ENV=development --env POSTGRES_USER=postgres --env POSTGRES_PASSWORD=superSecret123 --link mydbcontainer:db --volume ${PWD}:/app my-app bin/rails db:create db:migrate
Флаг --rm
удалит контейнер после завершения его работы.
После выполнения этой команды в контейнере mydbcontainer
будет настроенная под нужны приложения база данных. Наконец-то мы сможем его запустить!
Запуск приложения
Давайте запустим еще один контейнер на основе образа нашего приложения. Обратите внимание на несколько дополнительных опций команды:
$ docker run --rm -it --env RAILS_ENV=development --env POSTGRES_USER=postgres --env POSTGRES_PASSWORD=superSecret123 --publish 3000:3000 --volume ${PWD}:/app --link mydbcontainer:db my-app
=> Puma starting in single mode...
=> * Version 3.8.2 (ruby 2.4.1-p111), codename: Sassy Salamander
=> * Min threads: 5, max threads: 5
=> * Environment: development
=> * Listening on tcp://0.0.0.0:3000
=> Use Ctrl-C to stop
Откройте в браузере страницу localhost:3000
, где вы должны увидеть наше приложение, работающее полностью из-под Docker!
Следующие шаги
Docker — это очень удобный инструмент разработчика. Со временем вы можете перенести в него все компоненты своего приложения (БД, redis, рабочие процессы sidekiq, cron и т. д.).
Следующим шагом будет использование Docker Compose, предназначенного для описания контейнеров и способов их взаимодействия.
Ссылки:
Автор: olemskoi