[Из песочницы] Настройка Minio и Nginx для RoR приложения
Minio — что это
Minio это простое, быстрое и совместимое с AWS S3 хранилище объектов. Minio создан для размещения неструктурированных данных, таких как фотографии, видеозаписи, файлы журналов, резервные копии, а также образы виртуальных машин и контейнеров. Небольшой размер позволяет включать его в состав стека приложений, аналогичного Node.js, Redis и MySQL. В minio также поддерживается распределенный режим (distributed mode), который предоставляет возможность подключения к одному серверу хранения объектов множества дисков, в том числе расположенных на разных машинах.
Установка Minio
Установка сервера
Поставить и запустить Minio очень просто
brew install minio/stable/minio
minio server ~/data
Как советует документация, если была ранее установлена предыдущая версия без указания стабильности, ее необходимо удалить и поставить новую стабильную
brew uninstall minio
brew install minio/stable/minio
Справка по командам сервера покажет основные способы его запуска и конфигурации. Локально я его запускаю
minio server --address localhost:9000 ~/data
После запуска сервера он напишет по какому адресу его можно найти, а также ключ с секретным паролем для доступа к нему. Этого в принципе достаточно для начала работы с Minio, подробности — в официальной документации
Установка клиента и основные команды
С помощью клиента Minio можно работать с хранилищем аналогично командной строке Unix. Поставить клиента и посмотреть команды
brew install minio/stable/mc
mc --help
из полезного:
mc mb minio/my-uploads-store # создать каталог
mc policy -r public minio/my-uploads-store # дать полный доступ к каталогу
mc ls minio/my-uploads-store # посмотреть что у нас там есть
В официальном справочном руководстве достаточно подробно все описано
Nginx
Базовые настройки
Nginx позволит сделать много всего полезного, например:
- Спрятать хранилище во внутренней сети
- Организовать кеширование
- Организовать балансировку нагрузки для распределенного хранилища
- Ну и единое место входа для ваших приложений, которое позволит в дальнейшем безболезнено изменить хранилище
Простейшая настройка Nginx для использования Minio
server {
listen 80;
server_name minio.dev;
location / {
proxy_set_header Host $http_host;
proxy_pass http://localhost:9000;
}
}
Теперь мы можем обращаться к нашему хранилищу по адресу minio.dev
Кеширование
С кешированием Minio в Nginx как описано в статье у меня были проблемы. Как выяснилось — не только у меня. Выкрутились следующим образом:
- Пишем в хранилище по прямой ссылке на хранилище мимо Nginx (или через него, но без кеширования)
- Чтение из хранилища происходит по другой ссылке с кешированием
- Соответствено в нашем RoR приложении ссылка меняется — при записи одна, при чтении — другая с кешированием
proxy_cache_path /var/cache/nginx/minio_cache levels=1:2 keys_zone=minio_cache:10m max_size=10m inactive=60m use_temp_path=off;
upstream minio_servers { # можно добавить еще серверов и настроить Load Balancing
server localhost:9000;
}
server {
listen 80;
server_name minio.dev;
location / {
proxy_cache minio_cache;
add_header X-Cache-Status $upstream_cache_status;
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_ignore_headers Set-Cookie;
proxy_cache_valid 1m;
proxy_set_header Host $http_host;
proxy_pass http://minio_servers;
}
}
RoR приложениe
Общие настройки
Картинки в Minio загружаем с помощью Shrine (все подробности работы — в неплохой официальной документации и прилагаемых к документации статьях, тут — только особенности для Minio). Непосредственно для работы с Minio необходимо добавить в Gemfile
gem 'shrine-fog'
gem 'fog-aws'
require 'shrine/storage/fog'
require 'fog/aws'
require 'image_processing/mini_magick'
minio = Fog::Storage.new(
provider: 'AWS',
aws_access_key_id: '',
aws_secret_access_key: '',
region: 'us-east-1',
endpoint: 'http://localhost:9000/', # та самая прямая ссылка
path_style: true,
)
Shrine.storages[:cache] = Shrine::Storage::Fog.new(
connection: minio,
directory: '',
public: true,
)
Shrine.storages[:store] = Shrine::Storage::Fog.new(
connection: minio,
directory: '',
public: true,
)
Само-собой, необходимо через клиента Minio создать соответствующие папки и дать полный доступ к ним (см. раздел выше — клиент Minio).
class AvatarUploader < Shrine
include ImageProcessing::MiniMagick
plugin :activerecord
plugin :determine_mime_type, analyzer: :mime_types
plugin :logging, logger: Rails.logger
plugin :remove_attachment
plugin :store_dimensions
plugin :remote_url, max_size: 20*1024*1024
plugin :versions
plugin :default_url
plugin :processing
plugin :host_url, host: 'minio.dev', port: '80' # это собственный плагин, описан чуть дальше
Attacher.default_url do |options| # не обязательно, дефолтная картинка
'/images/default.svg'
end
process(:cache) do |io, context| # не обязательно, просто аватар мы у себя уменьшаем при загрузке
resize_to_fill!(io.to_io, 300, 300)
end
end
Собственно, большинство плагинов не обязательны. Подробнее см. официальную документацию к shrine. Обращение как обычно:
User.find(1).avatar_url
Замена ссылки
С заменой ссылки пришлось несколько повозиться. У Shrine достаточно разных плагинов, которые можно использовать для решения большинства задач. Есть плагин и для замены хоста default_url_options, но в данном конкретном случае он не помог — хост он меняет, а порт игнорирует. Хм, что же делать? А давайте напишем свой собственный плагин к Shrine. Код плагина ниже, как подключается — в предыдущем листинге (80й порт по-умолчанию в итоговой ссылке не прописывается, все остальные — появятся. Можно не писать его, кстати).
class Shrine
module Plugins
module HostUrl
def self.configure(uploader, options = {})
uploader.opts[:host_url] = (uploader.opts[:host_url] || {}).merge(options)
end
module FileMethods
def url(**options)
new_uri(
URI.parse(super),
uploader.opts[:host_url][:host],
uploader.opts[:host_url][:port]
)
end
private
def new_uri(uri, new_host, new_port)
URI::HTTP.new(
uri.scheme,
uri.userinfo,
new_host,
new_port,
uri.registry,
uri.path,
uri.opaque,
uri.query,
uri.fragment
).to_s
end
end
end
register_plugin(:host_url, HostUrl)
end
end
Код нового плагина положим в lib/shrine/plugins/host_url.rb
Миграция данных
В официальной инструкции, в принципе, все подробно описано. Есть, правда, несколько нюансов — ну там if (attacher = user.avatar_attacher).stored? почему-то возвращает false ну и еще по-мелочи — нужно добавить новое хранилище при существующем старом, сделать миграцию и потом выпилить старое…
Но в данном конкретном случае все можно сделать гораздо проще:
rsync -zavP <юзер>@<удаленный.сервер>:/<путь_к_немигрированным_данным> ~/data/
Собственно все из коробки работает с новым кодом.
Прочее
Да, при переезде на Minio в следующем приложении перенес этот плагин в гем.
Использованные источники
Enterprise-Grade Cloud Storage with NGINX Plus and Minio и русский перевод