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, предназначенного для описания контейнеров и способов их взаимодействия.
Ссылки:
Комментарии (4)
therhino
01.08.2017 16:21Один вопрос: Зачем?!
foxmuldercp
02.08.2017 00:45Зачем что?
Приложение в контейнерах? Для идентичности окружения, для быстроты запуска и прогона килотонн тестов. я знаю некоторые проекты (нет, не рельсы) где тесты могут гоняться часами на десятках агентов.
Базы данных в контейнерах тоже удобно для тестирования и разработки — отличия в версиях, библиотеках, даже в разных дистрибутивах могут давать различия.
Но рабочие сервера баз данных и docker — весьма плохое сочетание, кроме, разве что, кешей вроде редиса
AndrewDr
Спасибо за статью, но наверное стоит вынести данные из контейнера базы, чтобы после переборки контейнера или обновлении версии Postgres не потерять все данные.