Высокодоступный FTP-сервер с хранением данных в AWS S3

  • Tutorial
Добрый день, уважаемые читатели.
Снова хочу поделиться с вами приобретенным опытом.На одном из проектов была поставлена цель организовать FTP-сервер повышенной надёжности. Под повышенной надёжностью подразумевалось следующее:
  • Данные хранятся в AWS S3
  • Сам FTP-сервер (выбран был Pure-ftpd) должен быть максимально возможно доступен
  • Организовать балансировку нагрузки (опционально)

Шаг первый: Установка s3fs и монтирование S3 bucket в качестве дискового раздела.
Тут вариантов не много, вернее один (если ошибаюсь — поправьте) — s3fs. Разработчики s3fs на своей странице утверждают, что «s3fs is stable and is being used in number of production environment». Процесс установки s3fs расписывать нет смысла, он есть тут.Остановлюсь только на действительно важных моментах.Во-первых, у последней версии s3fs проблемы с синхронизацией данных. Когда вы заливаете новый файл на S3, он тут же появляется у вас на сервере, но если потом вы вносите изменения в этот файл на S3, то на сервере по-прежнему остается старая версия. Налицо проблема с кешированием. Попытки монтировать S3 bucket с различными опциями включения и выключения кеширования ничего не дали. После тестирования различных релизов s3fs была найдена версия, где данный баг себя не проявил. Скачиваем пакет, распаковываем и устанавливаем как написано в Makefile. Чтобы s3fs заработал корректно, убедитесь что в системе уже установлены следующие пакеты:
  • fuse
  • fuse-devel
  • fuse-libs
Для проверки можно попробовать примонтировать бакет командой:
#/usr/bin/s3fs mybucket /mnt/mybucket/ -o accessKeyId=XXXXXXXXXXXXX -o secretAccessKey=YYYYYYYYYYYYYYYYY -o allow_other,rw -o readwrite_timeout=120;

Шаг второй: Установка pure-ftpd.
Тут казалось бы ничего интересного. Достаточно просто установить при помощи любого пакетного менеджера. Однако pure-ftpd отличается своей параноидальностью, и перед тем, как удалить файл он сперва копирует его в новый временный файл. А когда размер файла составляет несколько гигабайт, то эта процедура занимает дополнительное время. А в нашем случае, когда данные хранятся не локально, а на S3, то и совсем не малое время.
Чтобы отключить создание временных файлов перед удалением, я пересобрал pure-ftpd с опцией --without-sendfile. Конечно было бы правильнее собрать свой пакет и установить его в систему, но я делал на быструю руку и на это отвлекаться не стал.

Шаг третий: Настройка прав пользователей.
Один из самых интересных нюансов. По требованиям заказчика в каждой домашней папке пользователя должны находиться каталоги, которые пользователь удалить не может или не может в них писать. Если бы мы имели дело с обычными дисковыми разделами, то мы могли просто сменить владельца папки. Но в нашем случае это не сработает так как права будут наследоваться от опции, с которой примонтирован раздел (ro ил rw). То есть пользователь или может всё, или только читать. Но у pure-ftpd есть одно полезное свойство, он умеет «ходить» по ссылкам. Для этого во время сборки добавляем ещё одну опцию --with-virtualchroot. Таким образом, мы можем примонтировать бакет дважды, в read-only и read-write режимах и сделать ссылки на них в домашних директориях пользователей.
#/usr/bin/s3fs mybucket /mnt/mybucketrw/ -o accessKeyId=XXXXXXXXXXXXX -o secretAccessKey=YYYYYYYYYYYYYYYYY -o allow_other,rw -o readwrite_timeout=120;
#/usr/bin/s3fs mybucket /mnt/mybucketro/ -o accessKeyId=XXXXXXXXXXXXX -o secretAccessKey=YYYYYYYYYYYYYYYYY -o allow_other,ro -o readwrite_timeout=120;

#mount | grep s3fs
s3fs on /mnt/mybucketro type fuse.s3fs (ro,nosuid,nodev,allow_other)
s3fs on /mnt/mybucketrw type fuse.s3fs (rw,nosuid,nodev,allow_other)


Директория пользователя будет выглядеть так:
ls -la /mnt/Users/User1/
.
lrwxrwxrwx 1 root root   15 Mar 25 09:10 mybucketro/folder1 -> /mnt/mybucketro/folder1
lrwxrwxrwx 1 root root   15 Mar 25 09:10 mybucketrw/folder2 -> /mnt/mybucketrw/folder2

Теперь мы дали пользователю доступ на чтение в папку /mnt/mybucketro/folder1 и доступ на запись в папку /mnt/mybucketrw/folder2.На этом этапе можем считать, что пункт первый ТЗ (Данные хранятся в AWS S3) выполнен.

Шаг четвертый: Настройка высокой доступности.
Тут решено было задействовать старый добрый AWS LoadBalancer и его чудесный HealthCheck.
Открываем AWS Console и создаём новый балансер (Уверен, что повторять процесс создания балансера необходимости нет. Если что, вот напоминалочка).
В Ping Protocol выбираем TCP, Ping Port — 21.
Всё, теперь жизнеспособность сервера будет проверяться по доступности 21 порта, то есть нашего FTP-сервера.
Создаем AMI с нашего сервера (на котором уже настроен FTP и примонтированы разделы). Далее всё как всегда, делаем launch-config с созданной AMI и создаём auto-scaling-group.
При создании auto-scaling-group указываем наш новый Load Balancer и опцию --health-check-type ELB.В такой конфигурации, если наш FTP-сервер «упадёт», то Load Balancer удалит его и «поднимет» новый рабочий сервер.Учитывая, что все данные мы храним на S3, то нам такая процедура не навредит.

Шаг пятый(опциональный) Всячески приветствуются ваши наработки: Настройка балансировки нагрузки и автомасштабирования.
Вопрос по балансировке нагрузки на фтп далеко не так просто решается, как, допустим, нагрузка на веб. Я с таким столкнулся впервые и, не найдя, готового бесплатного решения, предложил заказчику балансировать нагрузку с помощью ДНС.
В AWS Route53 есть опция для записей A-типа — weight. Чем выше это значение у записи, тем выше её приоритет в момент ответа клиенту.
То есть, теоретически, мы можем завести 5 записей с одинаковым weight и таким образом равномерно распределять запросы клиентов по 5-ти серверам.Для автоматизации добавления записей в AWS Route53 я сделал два скрипта. Один для добавления записи:
instance_up.sh
#!/bin/bash

zone_id="Z3KU6XBKO52XV4"
dns_record="example.com."
instance_dns=$(/usr/bin/curl -s http://169.254.169.254/latest/meta-data/public-hostname)
instance_ip=$(/usr/bin/curl -s http://169.254.169.254/latest/meta-data/public-ipv4)

let number_nodes=$(route53 get $zone_id | grep $dns_record | wc -l)+1
weight="50"
id=$(date "+%Y_%m_%d_%H:%M")

route53 get $zone_id | grep $instance_ip > /dev/null
if [ $? -ne 0 ]; then
	route53 get $zone_id | grep $dns_record | awk '{print $4" "$3" "$6" "$7}' | sed 's/id=//' | sed 's/\,//' | sed 's/w=//' | sed 's/)//' | while read i; do 
	    route53 del_record $zone_id $dns_record A $i
	    route53 add_record $zone_id $dns_record A $(echo $i | awk '{print $1" "$2" "$3}') $weight
	done
	route53 add_record $zone_id $dns_record A $instance_ip 60 $id $weight
fi
Другой для удаления:
instance_down.sh
#!/bin/bash

zone_id="Z3KU6XBKO52XV4"
dns_record="example.com."
instance_dns=$(/usr/bin/curl -s http://169.254.169.254/latest/meta-data/public-hostname)
instance_ip=$(/usr/bin/curl -s http://169.254.169.254/latest/meta-data/public-ipv4)

let number_nodes=$(route53 get $zone_id | grep $dns_record | wc -l)+1
weight="50"
id=$(date "+%Y_%m_%d_%H:%M")

route53 get $zone_id | grep $instance_ip > /dev/null

if [ $? -eq 0 ]; then
	route53 del_record $zone_id $(route53 get $zone_id | grep $instance_ip | awk '{print $1" "$2" "$4" "$3" "$6" "$7}' | sed 's/id=//' | sed 's/\,//' | sed 's/w=//' | sed 's/)//')
fi
Скрипты использует утилиту route53, которая идёт с пакетом python-boto.
Оба скрипта помещаем на сервер, с которого делаем AMI и добавляем их вызов в стартовый скрипт Pure-Ftpd
Теперь при запуске Pure-Ftpd сам добавит в AWS Route53 новую «A» запись со своим IP-адресом, а при выключении удалит её.
Остается только добавить политики для ScaleUP и ScaleDown для нашей auto-scaling-group.

Вот и вся настройка. Такая конфигурация успешно работает на проекте уже полгода.
Если остались вопросы — пишите комментарии, по возможности отвечу. Также буду рад, если кто-то поделится своим опытом в организации подобных систем.
EPAM 193,73
Компания
Поделиться публикацией
Похожие публикации

Вакансии компании EPAM

Комментарии 18
  • 0
    Высокодоступный тогда уж.
  • +1
    правильно ли я понимаю, что s3fs заливает файлы на s3 асинхронно, в фоне, грубо говоря?

    выходит, что вполне вероятен кейс, в котором файл (бакап, например), был залит через ftp на ФС сервера, и pureftp отчитался, что все окай, а в это время происходит отправка этого самого файла с сервера в s3, которая не контролируется, не мониторится и в случае ошибки не создаются инциденты и не отправляется нотификация.

    получается не очень-то и «высокодоступно»…
    • +2
      нет, асинхронной заливки нет, файлы заливаются прямо на S3 и pure-ftpd отчитается что всё ок, только когда файл уже на полностью загрузится на S3.
      • +1
        в таком случае, все еще возможен вариант с потерей соединения с s3.
        по идее, также возможен и вариант с ошибками во время put'a на s3, которые пока не проверяются code.google.com/p/s3fs/issues/detail?id=37
        • 0
          да, такой вариант тоже исключать нельзя.
    • 0
      Тогда уж ДНС с хелсчеками. RR — это высокодоступность?
      • 0
        RR — это что такое? ДНС с хелсчеками — Amazon это позволяет?
        • +1
          RR — round robin. Да, habrahabr.ru/post/170471/
          • 0
            На момент реализации решения такого функционала ещё не было.
            Обязательно изучу его возможности.
      • +1
        Интересно сделано.
        Каждый раз, глядя на фтп с ужасом вспоминаю сколько гемора доставляет этот архаичный протокол сейчас. Не поделитесь, что за задача заставила изобретать такие хитровыверты?
        • 0
          Как насчет скорости получилось? Помню s3fs весьма неспешно работает, особенно если много мелких файлов закачивать.
          • 0
            Спасибо за вопрос. Цифр дать, к сожалению, не могу. Замеры мною не делались. Визуально тормозов замечено не было. Постараюсь выкроить время и сделать замеры.
          • +6
            Привет!
            Являюсь разработчиком Open Source проекта RioFS: https://github.com/skoobe/riofs. Проект предоставляет доступ к S3 через локаольную файловую систему. В нашей компании мы использовали примерно такую же схему, как и автор поста: высоконагруженный proftpd + s3fs, но при использовании возникло огромное количество проблем и исправить их, не меная саму архитектуру проекта, не предоставлялось возможным.

            Наш проект — прямой аналог s3fs, но мы учли многие недочеты и ошибки в архитектуре s3fs. Наша главная цель: стабильности и скорость. В настоящий момент наш проект находиться в ранней бете, но уже какое-то время работает в режиме «только чтение» на одном из наших высокозагружженых серверов. Результатами мы довольны.
            Мы планируем активно развивать проект. Хотел бы, как разработчик проекта, попрасить Вас попробовать установить RioFS и потестировать его в действии. Если возникнуть проблемы или же Вы желаете чтобы была реализована какая-либо дополнительная функция: пожалуйста не поленитесь и составьте «bug report» или отошлите мне тут сообщение.
            Спасибо!
            • +4
              С удовольствием протестирую вашу разработку.
              • +3
                Напишите нам статью об этом. Много программ, полезных и разных:)
                • 0
                  Это в планах, как только будет готов «продакшн» релиз.
                • НЛО прилетело и опубликовало эту надпись здесь

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

                Самое читаемое