Глава 6. Добавляем базу данных: Postgres

Не знаю как вы, но я очень вдохновлён нашим развитием. Мы постепенно совершенствуем свои навыки и теперь в шаговой доступности от своего приложения Dockerized Rails имеющего все те возможности, которые мы применяем когда запускаем Rails локально.

Тем не менее есть одно явное упущение: мы не настроили базу данных. Подавляющему большинству приложений Rails требуется постоянное хранилище.

В этой главе мы исправим его, основываясь на своём опыте добавления сервера Redis, для подключения базы данных Postgres.

При чтении данной главы помните об сохранении в памяти всей большой картины. Изучаемые вами навыки применимы ко всем службам, которые вы можете пожелать добавить в своё приложение, будь то запуск фоновых заданий (таких как Sidekiq), Elasticsearch или некий интерфейс JavaScript для API Rails.

Запуск сервера Postgres

Для применения в своём приложении Rails мы хотим запустить некий сервер Postgres. Этот процесс очень похож на то как мы добавляли Redis.

В своей предыдущей главе мы начали своё ознакомление с тем как запускать Redis при помощи docker run. Однако теперь, когда у нас уже имеется некий опыт по добавлению служб, давайте позаботимся о сохранности колёс и перепрыгнем сразу к настройке Postres при помощи Compose.

Давайте добавим в свой файл docker-compose.yml Postgres:


​ 	version: ​'​​3'​
​ 	
​ 	services:
​ 	
​ 	  web:
​ 	    build: ​.​
​ 	    ports:
​ 	      - ​"​​3000:3000"​
​ 	    volumes:
​ 	      - ​.:/usr/src/app​
​ 	
​ 	  redis:
​ 	    image: ​redis​
​ 	
»	  database:
»	    image: ​postgres​
»	    environment:
»	      POSTGRES_USER: ​postgres​
»	      POSTGRES_PASSWORD: ​some-long-secure-password​
»	      POSTGRES_DB: ​myapp_development​
 	   

Мы определили некую новую службу database применив официальный образ postgres. Мы полагаемся на установленную по умолчанию в этом образе инструкцию CMD - CMD ["postgres"] которая запустит наш сервер Postgres.

Как и в случае с redis нашей новой службе database не требуется установление соответствия порта или тома. Мы не хотим в своём приложении наличия доступа к нашей базе данных извне и нам не требуется монтировать какие бы то ни было файлы в этот контейнер Postres.

Нам приходится, тем не менее, определять некое новое свойство с названием environment. Вы скорее всего догадались что оно делает: оно указывает Docker на необходимость настройки последующих переменных среды внутри этого контейнера. Здесь мы определяем что POSTGRES_USER должен быть установлен в postgres; POSTGRES_PASSWORD следует определить значением some-long-secure-password; а POSTGRES_DB надлежит настроить на myapp_development.

Зачем мы это сделали?

В точности как и версия Postres без Docker позволяет вам определять некие переменные окружения, то же самое справедливо идля нашей версии с Docker. Когда запускается Postres, если установлена POSTGRES_USER, именно это значение будет применяться в качестве имени учётной записи для суперпользователя. Аналогично, если установлен POSTGRES_PASSWORD, он будет применяться в качестве пароля данного суперпользователя. Наконец, если установлена POSTGRES_DB, именно она будет применяться в качестве базы данных по умолчанию, которая создаётся при запуске.

Не очень разумно иметь пароль нашей базы данных в данном файле docker-compose.yml: этот файл должен фиксироваться контролем версий, но существует риск для безопасности фиксации файлов, содержащих секретные коды. Мы вернёмся к этому вопросу позднее. Помимо этого, чисто технически, нам не требуется устанавливать POSTGRES_USER, так как это значение имеется у нас установленным по умолчанию. Тем не менее, я включил его, поскольку хорошей практикой является делать такие вещи настраиваемыми.

Хорошо. Имея эти обновления docker-compose.yml, мы можем запустить Postres:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​database​
		

Мы запустили свою службу database в отключённом режиме (detached). Вы можете убедиться что она поднята и исполняется при помощи:


​ 	​$ ​​docker-compose​​ ​​ps​
​ 	      Name               Command              State     Ports
​ 	----------------------------------------------------------------------------
​ 	myapp_database_1  docker-entrypoint.sh pos…   Up     5432/tcp
​ 	myapp_redis_1     docker-entrypoint.sh red…   Up     6379/tcp
​ 	myapp_web_1       bin/rails s -b 0.0.0.0      Up     0.0.0.0:3000->3000/tcp
		

Теперь у нас имеются три контейнера для нашего приложения и мы можем видеть своё новое прибавление - нашу базу данных - она исполняется.

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


​ 	​$ ​​docker-compose​​ ​​logs​​ ​​database​
​ 	Attaching to myapp_database_1
​ 	...
​ 	database_1  | PostgreSQL init process complete; ready for start up.
​ 	database_1  |
​ 	database_1  | 2019-01-15 10:07:29.394 UTC [1] LOG: listening on IPv4 address
​ 	"0.0.0.0", port 5432
​ 	database_1  | 2019-01-15 10:07:29.394 UTC [1] LOG: listening on IPv6 address
​ 	"::", port 5432
​ 	database_1  | 2019-01-15 10:07:29.397 UTC [1] LOG: listening on Unix socket
​ 	"/var/run/postgresql/.s.PGSQL.5432"
​ 	database_1  | 2019-01-15 10:07:29.409 UTC [60] LOG:  database system was shut
​ 	down at 2019-01-15 10:07:29 UTC
​ 	database_1  | 2019-01-15 10:07:29.414 UTC [1] LOG:  database system is ready
​ 	to accept connections
		

Помните, что эта команда отображает регистрационные записи самого контейнера - то есть его вывод отличается от вывода файла регистрационных записей Postgres.

Подключение к Postgres из отдельного контейнера

По мере того вам будет становиться всё удобнее и удобнее при использовании Compose, вы обнаружите что вы доверяете ему делать всё что вам нужно. Скорее всё что вам потребуется для проверки того что некая служба исполняется, это быстрый запуск docker-compose ps (а порой вы даже можете пропускать и это).

Однако так как запуск служб подобных Postres внутри некого контейнера всё ещё внове для нас, давайте проделаем дополнительный шаг вручную подключившись к нему с другого контейнера, в точности как мы проделывали это с Redis. При обучении, по крайней мере для меня лично, это помогало моей уверенности в работе с данными инструментами.

Как это было и в случае с Redis, наш образ postgres поступает с предустановленным psql - клиентом Postgres. Это означает что мы можем зацепиться прицепом к своей новой службе database чтобы запустить одноразовый контейнер на основе своего образа postgres. Тем не менее, вместо применения для этого образа установленную по умолчанию команду, которая запускает сервер Postgres, мы вместо этого исполним некую команду для запуска клиента Postgres.

Мы можем сделать это при помощи такой команды:


​ 	​$ ​​docker-compose​​ ​​run​​ ​​--rm​​ ​​database​​ ​​psql​​ ​​-U​​ ​​postgres​​ ​​-h​​ ​​database​
		

В ней мы сообщаем: "Запусти новый одноразовый контейнер (run --rm) для нашей службы database и исполни внутри него команду psql -u postgres -h database". Эта команда запустит нужного нам клиента Postres, сообщит ему о необходимости подключения к имеющемуся хосту с именем database от имени пользователя postgres. Мы полагаемся на тот факт, что Compose магическим образом настраивает сетевую среду для нашего приложения с настроенной DNS с тем чтобы указанное название хоста database достигало того контейнера, в котором запущена наша служба database.

Вместо run --rm нам возможно придётся применить exec, который мог бы избежать запуска некого нового контейнера и вместо этого исполнил бы соответствующую команду в уже запущенном контейнере с базой данных. Однако мы намеренно желаем дополнительно убедиться в подключении из иного контейнера.

Когда вы выполните эту команду, вы получите приглашение на ввод пароля:


​ 	Password for user postgres:
		

Пройдите далее и введите some-long-secure-password - тот пароль, который мы установили в своём файле docker-compose.yml. Он должен быть принят и вы получите приглашение psql:


​ 	psql (11.1 (Debian 11.1-1.pgdg90+1))
​ 	Type "help" for help.
​ 	
​ 	​postgres=#​
		

Исключительно. Мы подключились к своей службе database, исполняющей Postres и что всё работает как мы и надеялись. Когда вы будете готовы, вы можете покинуть своего клиента psql набрав \q <Enter>.

Подключение нашего прикладного приложения Rails к Postgres

Только что мы рассмотрели как поднять свою базу данных и достичь её из другого контейнера в своей сетевой среде приложения. Однако прежде чем мы начнём её применять, нам понадобится настроить своё приложение Rails на подключение к ней.

Давайте это сделаем прямо сейчас.

  Установка Gem Postgres

Обо всём по порядку. Чтобы заставить наше приложение Rails общаться с Postgres, нам нужно установить соответствующий gem клиента Postgres. Откройте свой Gemfile и обновите его чтобы заменить:


​ 	gem ​'sqlite3'​
 	   

на:


​ 	gem ​'pg'​, ​'~> 1.0'​
 	   

Для реальной установки этого нового gem нам потребуется исполнить bundle install, что мы делаем для перестроения своего образа (мы обсудим это в Главае 9, Расширенное управление Gem. Давайте остановим свой сервер Rails:


​ 	​$ ​​docker-compose​​ ​​stop​​ ​​web​
		

и после этого выполним повторную сборку своего образа:


​ 	​$ ​​docker-compose​​ ​​build​​ ​​web​
​ 	Building web
​ 	Step 1/8 : FROM ruby:2.6
​ 	...
​ 	Step 6/8 : RUN bundle install
​ 	...
​ 	Installing pg 1.1.4 with native extensions
​ 	...
​ 	Bundle complete! 16 Gemfile dependencies, 69 gems now installed.
​ 	Bundled gems are installed into `/usr/local/bundle`
​ 	...
​ 	Removing intermediate container 9b01b1fa29fc
​ 	​ --->​​ ​​f9e6330d40b6​
​ 	Step 7/8 : COPY . /usr/src/app/
​ 	​ --->​​ ​​70fb0e2e0091​
​ 	Step 8/8 : CMD ["bin/rails", "s", "-b", "0.0.0.0"]
​ 	​ --->​​ ​​Running​​ ​​in​​ ​​16cc0923b855​
​ 	Removing intermediate container 16cc0923b855
​ 	​ --->​​ ​​d4ffbe8f72d3​
​ 	Successfully built d4ffbe8f72d3
​ 	Successfully tagged myapp_web:latest
		

Имея установленным gem Postgres, мы можем перейти к настройке своего database.yml.

  Создание базы данных нашего приложения

Когда мы создали свой проект Rails, мы сделали это с установками по умолчанию, которые предполагали что мы применяем для своей базы данных sqlite. Теперь, когда мы вместо неё настроили Postgres, выработанный файл database.yml не является верным. Нам необходимо изменить его на нечто более подходящее.

Давайте откроем config/database.yml в неком редакторе и заменим его содержимое следующими настройками Postgres:


​ 	default: &default
​ 	  adapter: ​postgresql​
​ 	  encoding: ​unicode​
​ 	  host:     ​<%= ENV.fetch('DATABASE_HOST') %>​
​ 	  username: ​<%= ENV.fetch('POSTGRES_USER') %>​
​ 	  password: ​<%= ENV.fetch('POSTGRES_PASSWORD') %>​
​ 	  database: ​<%= ENV.fetch('POSTGRES_DB') %>​
​ 	  pool: ​5​
​ 	  variables:
​ 	    statement_timeout: ​5000​
​ 	
​ 	development:
​ 	  ​<<​: *default
​ 	
​ 	test:
​ 	  ​<<​: *default
​ 	  database: ​myapp_test​
​ 	
​ 	production:
​ 	  ​<<​: *default
 	   

К счастью, всё выглядит для нас достаточно знакомым.

Мы определили наиболее важные установки (host, username, password и database) через переменные среды. Обычно это рассматривается в качестве хорошей практики, однако, как мы увидим позднее, Docker предоставляет некий даже более безопасных подход. В настоящий момент, тем не менее, эти переменные среды не установлены для нашей веб службы web.

Давайте исправим это. Нам придётся изменить свой docker-compose.yml чтобы обеспечить эти переменные настроенными на наш контейнер приложения Rails, подобно следующему:


​ 	version: ​'​​3'​
​ 	
​ 	services:
​ 	
​ 	  web:
​ 	    build: ​.​
​ 	    ports:
​ 	      - ​"​​3000:3000"​
​ 	  volumes:
​ 	    - ​.:/usr/src/app​
»	  environment:
»	    DATABASE_HOST: ​database​
»	    POSTGRES_USER: ​postgres​
»	    POSTGRES_PASSWORD: ​some-long-secure-password​
»	    POSTGRES_DB: ​myapp_development​
​ 	
​ 	redis:
​ 	  image: ​redis​
​ 	
​ 	database:
​ 	  image: ​postgres​
​ 	  environment:
​ 	    POSTGRES_USER: ​postgres​
​ 	    POSTGRES_PASSWORD: ​some-long-secure-password​
​ 	    POSTGRES_DB: ​myapp_development​
 	   

В нашем мире до Docker мы обычно устанавливали значение DATABASE_HOST в localhost, так как эта база данных исполнялась бы в нашей локальной машине. Здесь, однако, мы определяем название той службы, которая исполняет Postgres: database. Это приводит к разрешению имён для контейнеров службы нашей database благодаря DNS, предоставляемой нашей сетевой средой приложения.

Мы также настраиваем переменные среды POSTGRES_USER, POSTGRES_PASSWORD и POSTGRES_DB на соответствие настройкам для данной службы database; это означает, что наша служба web будет иметь верные параметры доступа для регистрации в нашей базе данных.

Это должно теперь работать, однако обратим ваше внимание на то, что теперь у нас достаточно много переменных среды и два дубля для служб web и database. Мы также сказали, что не будем включать секретные коды в свой файл docker-compose.yml чтобы мы могли зафиксировать их в своём источнике репозитория. Давайте убъём двух зайцев одним залпом и выделим переменные среды в отдельные файлы.

Прежде всего давайте создадим некие каталоги для хранения своих специфичных для среды настроек:


​ 	​$ ​​mkdir​​ ​​-p​​ ​​.env/development​
		

Затем создайте соответствующий файл .env/development/web (без какого бы то ни было расширения файла), который сожержит особые для службы web переменные среды:


​ 	​DATABASE_HOST=database​
	   ​

и другой файл, .env/development/database, содержащий следующее для нашей службы database:


 	​POSTGRES_USER=postgres​
​ 	​POSTGRES_PASSWORD=some-long-secure-password​
​ 	​POSTGRES_DB=myapp_development​
 	   

Теперь нам требуется сообщить Compose о необходимости использования этих файлов вместо явной установки значений переменных напрямую. Мы делаем это с помощью соответствующей директивы env_file:


​ 	version: ​'​​3'​
​ 	
​ 	services:
​ 	
​ 	  web:
​ 	    build: ​.​
​ 	    ports:
​ 	      - ​"​​3000:3000"​
​ 	    volumes:
​ 	      - ​.:/usr/src/app​
»	    env_file:
»	      - ​.env/development/database​
»	      - ​.env/development/web​
​ 	
​ 	  redis:
​ 	    image: ​redis​
​ 	
​ 	  database:
​ 	    image: ​postgres​
»	    env_file:
»	      - ​.env/development/database​
 	   

Мы могли бы назвать файлы своей среды как угодно, но я выбрал простую осмысленную схему именования. Точно также вы можете применять любую файловую структуру и соглашения об именовании которые вам нравятся для данных файлов переменных среды (в настоящее время в .env), при условии вы верно ссылаетесь на них в самом файле Compose.

Закончив это небольшое форматирование мы готовы создавать свои базы данных для разработки и тестирования при помощи стандартной команды bin/rails db:create нацелив эту команду на свою службу web:


​ 	​$ ​​docker-compose​​ ​​run​​ ​​--rm​​ ​​web​​ ​​bin/rails​​ ​​db:create​
		

В данном случае мы воспользовались run --rm вместо exec, так как у нас в данный момент исполняется контейнер web, который не имеет наших вновь добавленных переменных окружения пока мы его не перезапустим. Это сделает наш новый одноразовый контейнер, который и запускает данную команду.

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


​ 	Database 'myapp_development' already exists
​ 	Created database 'myapp_test'
 	   

Вы могли бы заметить, что наша база данных myapp_development уже существует, что несколько странно, так как это самый первый раз когда мы просим её создания. Это происходит по той причине, что наш образ postgres автоматически создаст некую базу данных при своём самом первом запуске; если она устанавливается, он применяет своё значение переменной среды POSTGRES_DB в качестве того самого названия для этой таблицы. В нашем случае это myapp_development, и именно по этой причине данная таблица уже имеется.

Отлично, мы уже почти на месте.

  Перезапуск нашего сервера Rails

Получив настроенный на применение запущенной в контейнере базы данных Postgres Rails и создав базы данных, окончательный шаг состоит в запуске нашего сервера Rails с вновь установленными настройкой и переменными среды. Однако поскольку Compose будет только повторно применять для сервера имеющийся контейнер, нам придётся в явном виде сообщить ему о необходимости повторной сборки контейнера для своей службы web.

Вот как мы это делаем:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​--force-recreate​​ ​​web​
		

Параметр --force-recreate сообщает: "Собери заново контейнер этой службы."

Теперь двигайтесь далее и зайдите на http://localhost:3000 чтобы убедиться что ваше приложение подключилось к Postgres; если всё хорошо, вы обнаружите стандартный экран запуска Rails, в то время как в случае невозможности подключения ActiveRecord возбудит некую ошибку PG::ConnectionBad.

Мы сделали это - он поднят и исполняется с Postgres.

 

Рисунок 6-1



Практическое применение базы данных

Мы знаем, что настроив своё приложение Rails для общения с нашей базой данных Postgres это наше приложение Rails успешно запустилось; однако мы полагаемся на отсутствие ошибки в качестве подтверждения того что наша база данных корректно подключилась. Хотя с технической точки зрения это всё что нам требуется, давайте убедимся что всё работает должным образом выполняя взаимодействие с базой данных нашего приложения. Это также позволит нам получить дополнительную практику при разработке своего приложения Rails с Docker через CLI Compose.

Давайте выработаем для своего приложения Rails некий базовый UsersController. Для ускорения мы намерены воспользоваться командой Rails generate scaffold:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​\​
​ 	​    ​​bin/rails​​ ​​g​​ ​​scaffold​​ ​​User​​ ​​first_name:string​​ ​​last_name:string​
​ 	      invoke  active_record
​ 	      create    db/migrate/20190115100954_create_users.rb
​ 	      create    app/models/user.rb
​ 	      invoke  resource_route
​ 	       route    resources :users
​ 	      invoke  scaffold_controller
​ 	      create    app/controllers/users_controller.rb
​ 	      invoke    erb
​ 	      create      app/views/users
​ 	      create      app/views/users/index.html.erb
​ 	      create      app/views/users/edit.html.erb
​ 	      create      app/views/users/show.html.erb
​ 	      create      app/views/users/new.html.erb
​ 	      ...
		
[Замечание]Пользователи Linux: владение файлами

Убедитесь что вы выполнили chown для данных файлов запустив:


​ 	​$ ​​sudo​​ ​​chown​​ ​​<your_user>:<your_group> ​​-R​​ ​​.​
		

За дополнительными подробностями отсылаем к Дополнению A. Владельцы файлов и полномочия.

Теперь нам требуется запустить миграцию для создания своей таблицы Users. web. Вы уже должны начать привыкать к запуску стандартных команд Rails, нацеленных на нашу веб- службу с использованием Compose, в точности так:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​db:migrate​
​ 	== 20190115100954 CreateUsers: migrating ===================================
​ 	-- create_table(:users)
​ 	​   ->​> ​​0.0585s​
​ 	== 20190115100954 CreateUsers: migrated (0.0587s) ==========================
		

Ладно, всё дложно пройти успешно - давайте попробуем. Имея всё ещё запущенным данное приложение, перейдите в своём браузере к http://localhost:3000/users. Вы должны обнаружить знакомые строительные леса (scaffold) Rails и перечисленных пользователей как это показано на следующем рисунке. Убедитесь что у вас имеется возможность создавать и удалять пользователей.

 

Рисунок 6-2



Великолепно, у нас имеется Postgres целиком настроенный в Compose.

Отделение данных от контейнера

Это прекрасно что мы настроили свою базу данных и поэтому мы теперь способны удерживать данные в своём приложении Rails. Однако, в настоящий момент существует основной провал в том как это работает. Давайте рассмотрим в чём состоит проблема и как его обойти.

Часть философии применения Docker состоит в том, что мы должны рассматривать контейнеры как эфемерные - одноразовые раскручиваемые нами штуки, которые мы применяем и затем удаляем. Однако наша база данных Postgres запускается в некотором контейнере и удерживает наши данные записывая и изменяя файлы на диске внутри самого контейнера. Что произойдёт с нашими данными когда мы удалим свой контейнер базы данных? Ага, вы уже догадались: мы скажем пока- пока всем своим горячо любимым данным. Как- то не совсем то чего бы мы хотели.

Теперь, когда мы намерены сохранять в своей базе данных важную информацию, нам необходимо подумать об этом слегка более аккуратно.

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

Ответ такой: мы храним постоянные данные в томах, которые по своей природе отсоединены от самого жизненного цикла контейнеров. Даже если мы удаляем некий контейнер с каким- то подключённым томом, сам том продолжит существовать независимо, сохраняя в безопасности наши данные. Затем мы можем воссоздать этот контейнер, подцепить этот том и всё шито- крыто.

Docker позволяет нам создавать несколько различных видов томов, каждый из которых выполняет собственное задание. Например, мы уже видели как смонтировать некий локальный том. Однако существует и другой вид тома, который лучше отвечает нашей цели. На самом деле нас не заботит где или как хранятся наши файлы, нас беспокоит лишь то, что они хранятся где- то отдельно. Для этого мы можем создать некий именованный том: автономную корзину (bucket) файлового хранилища, полностью управляемую Docker.

Но хватит теории; давайте посмотрим как это делать практически.

Именованные тома могут создаваться и управляться посредством соответствующей команды docker volume. Хотя об этом и стоит знать, раз уж мы применяем Compose, мы можем ему позволить обрабатывать всё необходимое управление такими томами за нас.

Вот наш docker-compose.yml изменённый для хранения наших постоянных данных в неком томе:


​1: 	version: ​'​​3'​
​- 	
​- 	services:
​- 	
​5: 	  web:
​- 	    build: ​.​
​- 	    ports:
​- 	      - ​"​​3000:3000"​
​- 	    volumes:
​10: 	      - ​.:/usr/src/app​                          
​- 	    env_file:
​- 	      - ​.env/development/database​
​- 	      - ​.env/development/web​
​- 	
​15: 	  redis:
​- 	    image: ​redis​
​- 	
​- 	  database:
​- 	    image: ​postgres​
​20: 	    env_file:
​- 	      - ​.env/development/database​
​- 	    volumes:                                  
​- 	      - ​db_data:/var/lib/postgresql/data​      
​- 	
​25: volumes:
​- 	  db_data:
 	   

Самый первый шаг состоит в том чтобы сообщить Compose что нам требуется некий именованный том. Именованные тома определяются под имеющимся свойством верхнего уровня volumes (строка 25); здесь мы определили некий именованный том с названием db_data (строка 26).

Затем нам требуется сообщить Compose о необходимости монтирования этого именованного тома внутри своего контейнера базы данных с применением уже знакомого нам свойства volumes (строка 22). Монтирование некого именованного тома аналогично монтированию какого- то локального каталога (строка 10) - единственное отличие в том, что та часть, которая расположена до имеющейся точки ссылается на само название такого именованного тома вместо локального пути. Здесь (строка 23) мы сообщаем, "Смонтируй наш именованный том db_data в /var/lib/postgresql/data" - том каталоге, в котором наш образ Postgres хранит свои файлы базы данных которые мы желаем видеть постоянными.

Хорошо, давайте раскрутим это. Мы уже изменили определение своего Compose для нашей службы database, поэтому нам потребуется перезапустить его. Однако, опять же, поскольку Compose повторно использует тот же самый контейнер для некоторой службы пока мы не попросим его о чём- то ином, нам придётся в явном виде сообщать Compose о необходимости создавать заново этот контейнер database чтобы прихватывать настройки своего нового тома.

Вначале остановим свою службуdatabase:


​ 	​$ ​​docker-compose​​ ​​stop​​ ​​database​
		

Затем давайте явным образом удалим её контейнер:


​ 	​$ ​​docker-compose​​ ​​rm​​ ​​-f​​ ​​database​
		

Compose не запросит у нас подтверждения перед удалением этого контейнера - параметр принудительного исполнения -f (force) сообщает о необходимости двигаться далее и выполнить это.

Хорошо, самое время вернуть нашу базу данных обратно:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​database​
​ 	Creating volume "myapp_db_data" with default driver
​ 	Creating myapp_database_1 ... done
		

Так как наш новый том теперь смонтирован в данном контейнере, мы стёрли все предыдущие базы данных и хранимые данные, следовательно нам требуется воссоздать необходимые базы данных и выполнить миграцию.

Давайте сделаем это сейчас:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​db:create​​ ​​db:migrate​
​ 	Database 'myapp_development' already exists
​ 	Created database 'myapp_test'
​ 	== 20190115100954 CreateUsers: migrating ===================================
​ 	-- create_table(:users)
​ 	​   ->​> ​​0.0127s​
​ 	== 20190115100954 CreateUsers: migrated (0.0143s) ==========================
		

Замечательно, теперь давайте убедимся что наше приложение всё ещё работает. Посетим в своём браузере http://localhost:3000/users и убедимся что нам видны строительные подпорки нашего User. Великолепно - наш том представляется работающим.

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

 

Рисунок 6-3



Теперь когда у нас имеются некие сохранённые данные, давайте остановим свой контейнер базы данных:


​ 	​$ ​​docker-compose​​ ​​stop​​ ​​database​
​ 	Stopping myapp_database_1 ... done
		

а затем и удалим его (вам потребуется выдать подтверждение после получения запроса):


​ 	​$ ​​docker-compose​​ ​​rm​​ ​​database​
​ 	Going to remove myapp_database_1
​ 	Are you sure? [yN] y
​ 	Removing myapp_database_1 ... done
		

Затем мы воссоздадим его и запустим его через:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​database​
		

Если это работает, мы должны увидеть данные своего пользователя, в точности как это было до того как мы удалили свой контейнер базы данных. Перезагрузите свой браузер (http://localhost:3000/users) и ... наши данные всё ещё здесь. Ура!

[Замечание]Но где на самом деле мои данные?

Мы сказали что Docker управляет некой областью файловой системы для именованных томов, но где она расположена в действительности? Мы можем обнаружить где расположен наш именованный том db_data (который снабжён со стороны Compose префиксом папки нашего приложения и превращён в myapp_db_data), исполнив:


​ 	​$ ​​docker​​ ​​volume​​ ​​inspect​​ ​​--format​​ ​​'{{ .Mountpoint }}'​​ ​​myapp_db_data​
​ 	/var/lib/docker/volumes/myapp_db_data/_data
		

Как мы можем видеть, именованные тома хранятся в /var/lib/docker/volumes/. А именно: в Linux это будет некий путь в вашей локальной файловой системе, но в macOS или Windows это ссылка на значение пути внутри соответствующей ВМ Dockerhost.

Беглый обзор

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

Давайте повторим то что мы прошли:

  • При помощи Compose мы запустили некий сервер Postgres в контейнере.

  • Мы убедились что наш сервер Postgres был запущен подключившись с помощью клиента Postgres из некого отдельного контейнера.

  • Мы настроили своё приложение Rails на общение с Postgres установив требующийся gem Postgres, изменив свой файл database.yml и запустив соответствующую задачу Rake для создания требуемой базы данных.

  • Мы продвинули свою новую базу данных, создав подмостки, выполнив миграции а также вставки, удаления и обновления записей.

  • Мы обсудили почему будет хорошей мыслью отделить наши контейнеры базы данных от сам их данных, которые мы хотим видеть постоянными.

  • Наконец, мы воспользовались именованным томом для хранения своих данных обособленно, позволяя нам управлять их жизненным циклом независимо от самого контейнера.

Теперь нам уже известно как добавлять две службы: Redis и Postgres. Вы должны быть способны применять те же самые знания для добавления прочих служб, о которых вы можете помышлять. {Прим. пер.: например, взамен Potgres поставить MS SQL Server, ха!} На самом деле в своей следующей главе мы добавим ещё одну службу, когда отвлечёмся от внутренних технологий чтобы изучить как внедрять современный интерфейс для вашего приложения Rails.