Когда то я работал в одной молодой, но очень бурно развивающейся компании. Пока проекты были скромные по размерам все шло вполне успешно. Но когда проекты начали разрастаться как по сложности, так и по объемам, так и по количеству рендер нод, так и по количеству сотрудников, то тут нас ожидал очень неприятный сюрприз – а как же обеспечить совместную быструю, простую и удобную работу для всей этой студии. И к сожалению ни ответственными специалистами, ни кем либо другим решение не было найдено, в итоге проект начал проседать под собственным весом. Я через некоторые время не выдержал этой агонии и уволился, а сам проект еще через год был закрыт.

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

    Речь пойдет о следующих масштабах: более 100 сотрудников, более 200 рендер нод, более 100 ТБ единого файлового хранилища, более 30 Гб/с скорость файлового хранилища. Некоторые из приведенных здесь идей являются проверенными и являются реально действующими, некоторые приведены на уровне теории и эффект может оказаться непредсказуемым. Вся информация приведенная в статье приведена для размышления, и не имеет законченного логического решения. Все специализированные и сложные термины попытаюсь объяснить простым и доступным языком, пусть и не всегда технически точным, для детальных объяснений есть wiki.

     

    Раздел 1. Сеть хранения данных

    Первое что необходимо решить студии – это конечно где хранить рабочие файлы. Для этого специально покупается файловый сервер, допустим на 24 ТБ. Но со временем его начинает не хватать как по объемам так и по скорости работы. Тогда покупается второй сервер. И тут встает вопрос как раскидать файлы между двумя серверами. Кое как эта проблема решается. А тут и двух серверов начинает не хватать, купить еще сервер. Но как теперь файлы раскидывать? Понятно что так жить нельзя, образуется хаос и неразбериха. Кроме того если разом стартует 100 рендер нод, они разом грузят данные файлы сцен и текстур с сервера. Из за чего сервер надолго задумывается и недоступен для сотрудников.

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

    •  хранилище должно быть единым, т.е. при добавлении нового сервера  файловая структура оставалась та же, а его объемы и скорость увеличивались;
    • хранилище должно легко масштабироваться, т.е.  чтобы мы в любой момент без остановки его работы можно было увеличить его объем или скорость;
    • хранилище должно быть высокодоступным, т.е. время его доступности для всех пользователей должно быть выше 99,9%;
    • хранилище должно быть отказоустойчивым, т.е. любой из элементов хранилища должен иметь возможность горячей замены без потери доступности для пользователей.

    А чтобы проще было понять, этот раздел я разбил на несколько частей, который проведет от основ, до полного понимания как такого рода хранилища можно реализовать.

     

    Часть 1. Программный SAN. Протокол iSCSI.

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

    • NAS (Network Attached Storage) – сетевое хранилище, компьютер набитый винчестерами для общего доступа по сети.
    • SAN (Storage Area Network) – сеть хранения данных, несколько NAS в одной сети.

    А теперь давайте разбираться как же нам несколько NAS объединить в SAN. Для этого есть множество разных протоколов.

    • SMB (Server Message Block) – это всем нам знакомый протокол для расшаривания файлов, принтеров и прочих ресурсов по сети;
    • SCSI (Small Computer System Interface) – иногда произносится как “скази”, а это более подходящий нам протокол, который позволяет использовать диски другого компьютера как свои собственные. Создавая на них разделы, форматируя и производя абсолютно любые операции как будто эти диски полностью свои.

    В чем особенность протокола SCSI, и почему нам не подходит SMB. Дело в том что сетевые шары никак нельзя объединить в единое хранилище, а вот благодаря SCSI мы можем перекинуть диски с нескольких NAS на один компьютер и создать из них RAID. Тем самым получиться простейший вариант единого хранилища, который поможет нам понять основы сетей SAN.

    SCSI состоит из двух элементов. SCSI Target – цель, под целью понимают те диски которые будут доступны для передачи на другой компьютер. И SCSI Initiator – инициатор, эта служба подключает на свой компьютер диски из указанной цели.

    Для сбора простейшего SAN я буду использовать Windows Server 2012. Понадобятся две машины. У одной мы будет забирать диски, и на вторую их цеплять.

    Настройка SCSI Target:

    Сначала нам необходимо установить сервер целей. Ставится он только на те файловые сервера диски которых мы хотим передать клиентам, на клиента ставить цель не нужно. Для установки проделываем несколько простых шагов:

    1. Заходим в диспетчер серверов (стартует автоматически при входе в систему);
    2. Нажимаем кнопочку Добавить роли и компоненты.
    3. В открывшемся Мастере добавления ролей и компонентов щелкаем Далее до тех пор пока не дойдет до раздела Роли сервера;
    4. В окошке выборе ролей находим пункт Файловые службы и службы хранилища, в нем Файловые службы и службы iSCSI, и в нем уже выбираем роль Сервер цели iSCSI. В открывшемся окошке щелкаем кнопочки Добавить роли.
    5. В разделе компоненты, по желанию, можно выбрать Служба iSNS сервера. Она служит для централизованного управления службами iSCSI.
    6. Доходим до конца мастера и нажимаем кнопку Установить.

    Теперь приступим к настройке. Все так же несколько простых шагов:

    1. Заходим в Диспетчер серверов, слева находим новую закладку Файловые службы и службы хранилища, а в ней закладку iSCSI.
    2. Справа сверху находим раскрывающийся список Задачи, а в нем пункт Создать виртуальный диск iSCSI. Откроется просто и понятный мастер.
    3. В Мастере создания виртуальных дисков iSCSI вводим следующие параметры, по разделам:
      1. Расположение виртуального диска – здесь мы выбираем в каком месте он сохранит образ виртуального диска с которым мы будем дальше работать. Образ имеет всем знакомое расширение VHD.
      2. Имя виртуального диска – имя как будет называться наш файл, и описание если необходимо.
      3. Размер виртуального диска iSCSI – вводим размер нашего виртуального диска.
      4. Цель iSCSI – Это что то вроде точки доступа к которой будут цепляться инициаторы для добавления диска. На этом этапе нужно выбрать имеющуюся цель, но так как цели пока нет, выбираем Новая цель iSCSI.
      5. Имя цели доступа – вводим имя для создаваемой цели, и описание если необходимо.
      6. Серверы доступа – здесь нам надо указать какие инициаторы имеют право подключаться к этой машине. Щелкаем кнопочку Добавить, в появившемся окне выбираем пункт Ввести значение для выбранного типа, в раскрывающимся списке выбираем IP адрес. И вводим IP адреса компьютера на который мы будем перекидывать диски.
      7. Включение службы проверки подлинности – тут для опытных сис. администраторов все понятно, остальные могут пропустить этот пункт.
      8. Подтверждение – проверяем все что мы ввели, и нажимаем кнопочку Создать.
    4. Вот вообщем то и вся настройка. В Диспетчере серверов в разделе iSCSI должна появиться информация о наших дисках и целях, а также информация по их состоянию.

    Настройка SCSI Initiator:

    Инициатор устанавливать не надо, он предустановлен во всех версиях Windows. Настройка инициатора еще проще, и на всех, Windows даже не серверных, выполняется одинаково:

    1. Нажимаем на кнопку Пуск, или то что вместо нее в новых Windows, и набираем на клавиатуре iscsi.
    2. В появившемся списке выбираем Инициатор iSCSI, при первом запуске появиться окошко с сообщением о необходимости запустить службу, нажимаем Да.
    3. Далее в открывшемся окне Инициатор iSCSI на вкладке Конечные объекты, в группе быстрое подключение вводи IP адрес нашей цели и щелкаем кнопочку Быстрое подключение… .
    4. В появившемся окошке быстрое подключение должно отображаться имя нашей цели iSCSI.
    5. Нажимаем кнопку Готово. В окне Инициатор iSCSI на вкладке Конечные объекты в группе обнаруженные конечные объекты должна появиться наша цель и ее статус.
    6. Вообщем то и все. Теперь мы можем зайти в диспетчер Управление дисками и найти там наш подключенный виртуальный диск.

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

    Это и есть простейший вариант сети SAN с единым хранилищем. Пока оно бесполезное, но зато теперь мы знаем как работает SCSI. Основа почти всех возможных вариантов сетей SAN.

    Теперь встает вопрос как же нам наращивать объем этого хранилища. А ответ вообщем то прост, подключить еще несколько серверов с целями на инициатор.

    Теперь когда когда в системе есть много дисков, мы можем сделать из них RAID массив. А лучше Пул. На самом деле RAID массив нельзя собрать в кластере, и он не такой гибкий и удобный как пул. А почему мы не можем обойтись без кластера рассмотрим немного позже.

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

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

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

    Схема едина для всех возможных вариантов построения хранилища, когда конечные пользователи работают на Windows машинах, не зависимо от платформы и технологии которые мы дальше будем рассматривать. В случае когда конечный пользователь работает на Linux, пользователь может напрямую подключаться к хранилищу, или даже сам быть кусочком хранилища.

    Но тут, в Windows варианте, нас ждет большая проблема. Цель участвующий в массиве можно подключить только  к одному инициатору. Цель которая не участвует в массиве можно подключить к нескольким инициаторам, и каждый инициатор будет видеть его как нормальный диск, но есть одна проблема ставящий крест на этой схеме. При записи файлов на диск каждый инициатор будет думать что только он владеет диском и только он имеет право его использовать. В итоге инициаторы не будут видеть файлы друг друга, и даже хуже того, переписывать блоки чужого инициатора своими блоками. Проблема тут в файловой системе, ни NTFS, ни FAT, ни ReFS не являются кластерными файловыми система, а следовательно один диск можно подключить только к одному инициатору. Диски с кластерными ФС можно подключать сразу к нескольким инициаторам, в этом их главное отличие.  Но Windows к сожалению кластерных ФС не знает, но это еще не ставит крест на Windows как платформы для построения хранилища, чуть позже мы рассмотрим как же обойти эти ограничения.

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

    Но прежде чем приступить к построению кластерного хранилища рассмотрим варианты на каких протоколах и сетях передачи мы можем его построить.

     

    Часть 2. Железный SAN. Варианты протоколов.

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

    • Ethernet – технология пакетной передачи данных в основном для локальных сетей LAN, это то с чем мы сталкиваемся каждый день и представлен в любом компьютере. Представлен в основном Ethernet картой с портом RJ-45. Поверх него работает TCP/IP. А поверх TCP/IP уже работает известная нам SMB и iSCSI.
    • SCSI – технология для физического подключения и передачи данных между компьютерами и периферийными устройствами. В голом виде уже морально устарела. Представлен специальными картами расширения SCSI.
    • Fibre Channel – протокол для высокоскоростной передачи данных. Отличается от Ethernet тем что он ориентирован и оптимизирован исключительно на передачу данных блочных устройств (дисков, устройств хранения). За счет того что исключено все лишнее сильно возрастает производительность и уменьшается время доступа к данным. Внутри у него находиться SCSI и по сути является его логическим развитием. Представлен специальной картой расширения, HBA адаптером. Объединяется в сеть при помощи FC коммутаторов.
    • Fibre Channel over Ethernet – как можно понять обычный FC только работающий по сети Ethernet. Сделано это для объединения и удешевления суммарных затрат на построение сети. Но и время доступа к данным значительно возрастает. Представлен специальным HBA адаптером с поддержкой этой технологии. В сеть объединяется при помощи обыкновенного Ethernet коммутатора с поддержкой этой технологии.
    • InfiniBand – а эта уже целая шина, аналогичная PCI Express, на которой уже может базироваться много важных протоколов. Это совсем молодая технология, призванная объединить и упростить коммутацию, за счет объединения всех сетей в единую. Внутри находятся сразу пакет протоколов, но рассмотрим только интересные нам. IP over InfiniBand – как понятно из названия все тот же TCP/IP. RDMA – протокол прямого доступа к памяти другого компьютера, для нас мало интересен в голом виде. SCSI RDMA Protocol – а вот это уже интересно, через RDMA можно захватить и данные с блочного устройства. InfiniBand представлен платами расширения HCA. Объединяется в сеть при помощи InfiniBand коммутаторов.

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

     

    Часть 3. Программное объединения SAN в единое хранилище.

    Вариант 1. Windows Failover Cluster

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

    Но к сожалению у этого способа есть очень много минусов, поэтому рассмотрим этот вариант лишь поверхностно. Для тех кто желает более детально ознакомиться могут найти статьи в поисковиках по запросу “Clustered Storage Space Windows“. Проблема у этого решения в слишком высоких требованиях к оборудованию, и это при том что масштабируемость такого решения подойдет разве что малым, максимум средним предприятиям.

    Этапы построения выглядят следующим образом:

    1. Если вне кластера пул можно создать из любого устройства, хоть флешки, то в кластере исключительно из дисков SAS;
    2. Все диски SAS должны быть подключены ко всем узлам кластера, для этого необходим протокол FC, с соответствующим железом;
    3. Железо должно соответствовать очень высоким требованиям, в частности к стандартам, времени доступа и откликам;
    4. Кластер это всего лишь часть от всей сетевой инфраструктуры, поэтому так же должны быть настроенный домен и dns сервер, все узлы будующего кластера должны быть введены в домен.
    5. На всех узлах кластера должна быть установлена служба Отказоустойчивый кластер.
    6. Далее от имени администратора домена, на любом из будущих узлов кластера, запускаем Диспетчер отказоустойчивых серверов.
    7. В диспетчере находим пункт создать кластер, и следуем по инструкциям мастера.
    8. Вводим имена узлов которые будут обслуживать кластер.
    9. На определенном этапе будут произведены тесты пригодности оборудования для кластерной роли. Тут абсолютно все тесты должны быть отмечены зелеными значками. В случае наличия красных кластер просто не соберется, в случае желтых будут сбои при работе. Тестов очень много, но все условия должны быть выполнены.
    10. Когда кластер соберется откроется консоль управления кластером, очень похожая на консоль управления сервером.
    11. В этой консоли идем в раздел с пулами, и создаем там наш пул, в пуле виртуальный диск. И уже этот виртуальный диск расшаривается для общего доступа.

    А теперь о недостатках и почему этот кластер можно посоветовать лишь врагу:

    1. Огромная стоимость инфраструктуры, мало того что диски SAS стоят много дороже чем SATA, так к ним еще и FC подавай.
    2. Кластер на самом деле не Отказоустойчивый, а Кластер на отказ. Т.е. задачу выполняет только один из узлов кластера, другие задействуются только в случае если первый вышел из строя.
    3. Что бы все узлы кластера одновременно работали, необходимо настроить балансировщик нагрузки. В таком случае службы основанные на TCP/IP можно будет распарралелить между всеми узлами. Все остальные службы все равно будут работать на отказ.
    4. Масштабируемость такого кластера весьма спорная, да его можно нарастить по объему, но имеющийся том нельзя расширить, так как в кластере нельзя создавать тонкие диски, а следовательно придется создавать новый том и отдельную шару.
    5. Массив уровня 5 не доступен в кластере, доступны только 0 и 1. Следовательно придется либо терять объем или надежность, выбирая между массивами.
    6. В виду особенности архитектуры узел кластера не может быть файловым сервером, а стало быть опять большие потери на количестве обслуживающих серверов.
    7. Максимально количество узлов, которые могут обслуживать кластер на Server 2012, всего лишь 63.
    8. и еще много страшных пунктов…

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

     

    Вариант 2. Распределенные параллельные файловые системы с защитой от сбоев.  На примере Gluster FS.

    Существует большой выбор такого рода файловых систем GlusterFS, Lustre, Ceph, GPFS, Google FS, MooseFS, MogileFS, POHMELFS и мн. др. И к счастью большая часть из них бесплатна и даже OpenSource. Но есть и один недостаток, как мы выяснили ранее работают они только под Linux.

    Чтобы понять как они работают поднимем одну из них. Самая простая и народно любимая, при том же одна из самых мощных это GlusterFS. Самое сложное в его настройке оказалось правильно подобрать дистрибутив. Для этих целей было перепробовано более 10 дистрибутивов OpenSuse 12 и 11, SLES 11, Ubuntu 10 и 12, Fedora 16, CentOS 5 и 6 и др. На всех этих дистрибутивах она отказывалась толком работать. То собраться не может, то зависает напрочь, если использовать более 3 серверов, то скорости не дает. И только на старом добром Red Hat Enterprese Linux (RHEL) версии 5 она заводиться с пол оборота и исправно работает при больших нагрузках.

    Для GlusterFS совершенно не требуется какого либо специального железа. Архитектура так же разделена на серверную и клиентскую часть. Работает он либо по Ethernet, либо по Infiniband. Диски могут быть абсолютно любые. Конфигурация компьютеров тоже может быть произвольная. Вместо SCSI для получения файлов с диска используется Linux сервис Fuse. Кроме того GlusterFS абсолютно бесплатный и даже OpenSource. А вся настройка происходит буквально за 7  простых строчек с командами. Я для примера буду использовать GlusterFS версии 3.3.1.

    Установка GlusterFS на файловые сервера:

    Для начала устанавливаем RHEL 5 на файловые сервера. Во время установки можно не вводить серийный номер, нас интересует только его бесплатная часть. Также необходимо выбрать все пакеты разработки в разделе Development (для опытных только gcc и ядро).

    Теперь необходимо до установить пакет FUSE. По умолчанию он не идет на диске с RHEL, поэтому я воспользуюсь поиском по rpm.pbone.net для RHEL 5. Пакет называется fuse-2.7.4-1.el5.rf.x86_64.rpm. Скачиваем и устанавливаем.

    wget ftp://ftp.pbone.net/mirror/ftp.freshrpms.net/pub/freshrpms/pub/dag/redhat/el5/en/x86_64/RPMS.dag/fuse-2.7.4-1.el5.rf.x86_64.rpm

    rpm -i fuse-2.7.4-1.el5.rf.x86_64.rpm

    Теперь настало время установить сам GlusterFS. Для этого достаточно зайти на официальный сайт и посмотреть адрес репозитория для RHEL. Прописываем этот адрес в список репозиториев, после чего станут доступны пакеты GlusterFS. Устанавливаем пакеты glusterfs, glusterfs-fuse, glusterfs-server. Для Infiniband понадобиться еще пакет glusterfs-rdma.

    wget http://download.gluster.org/pub/gluster/glusterfs/3.3/3.3.1/EPEL.repo/glusterfs-epel.repo

    cp glusterfs-epel.repo /etc/yum.repos.d/

    yum install -y glusterfs glusterfs-fuse glusterfs-server

    После установки необходимо немного подправить файл конфигурации. Располагается он по адресу /etc/glustefs/glusterd.vol. Во первых необходимо в строчке option transport-type socket,rdma  удалить слово RDMA, что бы в логах через строчку не было сообщение об отсутствии Infiniband. Также в строчках option transport.socket.keepalive-time 10 и option transport.socket.keepalive-interval 2 необходимо добавить по два нуля. Это время отклика в миллисекундах, если узел не ответить за заданное время, он будет считаться сломанным. Для таких показателей железо должно быть очень высокого качества, а так как не у всех такое есть увеличиваем эти параметр. В итоге файл должен иметь следующий вид:

    volume management
    type mgmt/glusterd
    option working-directory /var/lib/glusterd
    option transport-type socket
    option transport.socket.keepalive-time 1000
    option transport.socket.keepalive-interval 200
    option transport.socket.read-fail-log off
    end-volume

    или для любителей командной строки вбить:

    cat > /etc/glusterfs/glusterd.vol<< EOF
    volume management
    type mgmt/glusterd
    option working-directory /var/lib/glusterd
    option transport-type socket
    option transport.socket.keepalive-time 1000
    option transport.socket.keepalive-interval 200
    option transport.socket.read-fail-log off
    end-volume
    EOF

    Осталось только запустить сервис серверной части командой

    service glusterd start

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

    cd
    cd /tmp

    wget ftp://ftp.pbone.net/mirror/ftp.freshrpms.net/pub/freshrpms/pub/dag/redhat/el5/en/x86_64/RPMS.dag/fuse-2.7.4-1.el5.rf.x86_64.rpm

    rpm -i fuse-2.7.4-1.el5.rf.x86_64.rpm

    wget http://download.gluster.org/pub/gluster/glusterfs/3.3/3.3.1/EPEL.repo/glusterfs-epel.repo

    cp glusterfs-epel.repo /etc/yum.repos.d/

    yum install -y glusterfs glusterfs-fuse glusterfs-server

    cat > /etc/glusterfs/glusterd.vol<< EOF
    volume management
    type mgmt/glusterd
    option working-directory /var/lib/glusterd
    option transport-type socket
    option transport.socket.keepalive-time 1000
    option transport.socket.keepalive-interval 200
    option transport.socket.read-fail-log off
    end-volume
    EOF

    service glusterd start

    cd
    mkdir /gluster
    mkdir /gluster/brick01

     

    Достаточно скопировать их в консоль сразу после установки RHEL и GlusterFS автоматически установится, и сделает предварительные настройки.

     

    Настройка серверной части:

    Для начало на каждом узле файлового хранилища создадим папку, которая будет использоваться для организации хранилища. У меня это будут /gluster/brick01 на первом сервере, /gluster/brick02 на втором сервере и т.д. Главное чтобы имена папок у всех серверов были разные.

    Далее на одном из узлов кластера, он будет называться мастером, выполняем команды по настройке GlusterFS.

     gluster peer probe IP

    При помощи этой команды происходит проверка другого узла на наличие GlusterFS и подключение его к общему кластеру. Проверить статус узлов кластера можно командой gluster peer status.  Если при добавлении узлов произошла ошибка или вы просто испортили конфигурацию можно просто из бракованного узла удалить все настройки и еще раз попробовать добавить узел.  Хранятся настройки в папке /var/lib/glusterd.

    Теперь необходимо создать диск в кластере, делается это командой

    gluster volume create VOL_NAME IP1:/patch1 IP2:/patch2 IP3:/patch3

    Здесь мы вводим название нашего диска, а также какие папки на каких серверах будут использоваться для обслуживания кластера. После имени диска можно задать тип массива который будет создан из папок. Всего вариантов массивов доступно около 9 штук. В документации все они детально описаны. Дисков так же может быть несколько, с разным типом массивов.

    Осталось только сделать наш диск активным

    gluster volume start VOL_NAME

    Вот вообще то и вся настройка. Всего три строчки. И у вас мощное кластерное хранилище. Проверить статус диска можно командой gluster volume info. Сервера и объем можно добавлять прям на ходу командой gluster volume add VOL_NAME IP:/patch. Если в кластер добавлялись новые машины или по другой причине произошла разбалансировка, выполняется команда gluster volume rebalance VOL_NAME start.

     

    Настройка клиентской части:

    Здесь на самом деле все еще проще. Точно так же как и на серверной части на клиентскую машину выполняется установка GlusterFS. За исключением того что пакет gluster-server можно не устанавливать и сервис glusterd можно не запускать.

    Для начала понадобится папка при обращение к которой нам будет выдано все содержимое кластера. Я для этого создал /gluster/storage. А дальше выполняется простая команда

    mount -t glusterfs IP:/VOL_NAME /gluster/storage

    Вообщем то и все. Теперь при обращении к папке /gluster/storage клиенту будет выдано все содержимое кластера. При записи в эту папку файлы будут равномерно распределены по узлам кластера. Масштабируемость такого кластера ограничивается несколькими петабайтами данных, скорости несколькими терабитами в секунду.

    Linux пользователи могут напрямую подключаться к кластеру или использовать протокол NFS. Windows пользователям файлы необходимо раздавать по протоколу SMB. Благодаря архитектуре каждый узел кластера также может быть и клиентом, что позволяет сильно с экономить на серверах. Но надо не забывать что SAN должен быть вынесена в отдельную сеть для лучшей производительности.

    Кроме GlusterFS хочется отметить еще и Lustre. Настройка ее не намного сложнее GlusteFS, а отличается тем что метаданные вынесены на отдельный сервер, и клиент уже не опрашивает все сервера в поисках файлов, а обращается к серверу метаданных за файлом, который уже и сообщает клиенту где храниться файл.  Благодаря этому Lustre является самым производительным и крупно масштабируемым решением из всех существующих. Чего стоит только статистика, в ТОП-30 супер компьютеров  Lustre установлена более чем на половине, включая самый топовый. Также такие компании как HP, DELL, CRAY, SGI и другие поставляют люстру в своих масштабируемых файловых хранилищах.

     

    Вариант 3. Облачная инфраструктура

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

    Везде трубят что облачность инфраструктуры способна сокращать ее стоимость в разы. Так давайте же разберемся за счет чего это происходит. Допустим у нас обычная, не облачная инфраструктура. В Этой инфраструктуре должны быть: 1. Файловые сервера, цена каждого от 130 т.р., 2. Рендер нода, цена 2*XEON E5 от 120 т.р., 3. Компьютеры пользователей, цена от 40 т.р., без учета мониторов. Сумма этих трех компьютеров составляет почти 300 т.р. А теперь смотрим кто из них как используется. В файловом сервере простаивают процессор и память, в рендер ноде винчестеры, у пользователя винчестеры, ядра и память.

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

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

    1. Должно поддерживаться проброс видеокарты (PCI Passtrough) в виртуальную машину. Иначе сотрудник не сможет нормально работать если у него все будет тормозить.
    2. Должно быть доступно резервирование ресурсов для каждой машины и настраиваемая система приоритетов. Нашему файловому серверу нужно хоть немного от частоты процессора для обслуживания файлового сервера, надо задать ему этот гарантированный объем, а также максимальный приоритет перед другими участниками облака на этой машине. Пользователю тоже необходимо задать гарантированный объем процессора, памяти и скоростей сети и хранилища, а также возможность использования всех ресурсов сервера с приоритетом над рендер нодой. Ну и рендер ноде, то что осталось.
    3. Облако должно поддерживать единое кластерное хранилище.

    Что получается в итоге, файловое хранилище работает с максимальным приоритетом, а за счет резервирования ему всегда будет доступен определенный объем ресурсов. Пользователь также работает в минимальном наборе ресурсов и никто на него не полезет, но если пользователю на сложную операцию понадобиться больше двух ядер, облако в считанные миллисекунды отнимет их у рендер ноды. И рендер нода работает всегда, в итоге нету простоев оставшихся ресурсов никогда.

     

    Часть 4. Железное объединение SAN в единое хранилище.

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

    Как правило такие компании разрабатывает собственное железо, но на основе общераспространенного. В качестве программной основы у них тоже своё программное обеспечение, но опять же на основе Linux с перечисленными файловыми системами.

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

     

    Раздел 2. Коммутация

    Теперь когда мы научились строить кластерное хранилище. Встает другой вопрос, а чем же все это соединять? Сначала студии покупают 48 портовые 1Гб/с коммутаторы, потом второй, потом третий. И вот тут сталкиваются с неприятным сюрпризом они начинают тормозить или того хуже давать сбои, причем сразу по двум причинам. Если первая банальная скорость коммутации между такими коммутаторами очень низкая и когда одна группа пользователей одного коммутатора лезет на порты другого коммутатора получается очень маленькая скорость. Вторая проблема гораздо более страшная. Коммутаторы способны обработать только определенное количество пакетов в секунду (IOPS). И при соединении нескольких коммутаторов между собой эта цифра не суммируется, а остается на прежнем уровне, а вот нагрузка на этот параметр возрастает. В итоге при достижение максимума пакеты просто выстраиваются в очередь в буфере коммутатора и ждут когда их обработают, а при скором достижении лимита буфера пакеты начинают просто теряться, тем самым связь просто обрывается. Особенно проблема актуально когда идет рендеринг на 100+ рендер нод.

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

    Существуют специальные так называемые модульные коммутаторы. Как понятно по названию состоят они из модулей. Минимальный набор для работы этого коммутатора состоит из 3 элементов. Первый это корпус, в котором имеется шасси для установки модулей, в корпусе также находиться разводка между модулями. Вторая это управляющая матрица (коммутирующая матрица). И третий это необходимые модули. Они представляют из себя лезвие с набором портов. Модули могут быть в самых разнообразных вариантов с совершенно разнообразным наборов портов. Модули вставляются в стойку и общаются между собой по коммутирующей матрице. Количество этих модулей добавляется по мере необходимости.

     

    Вариант 1. Модульные коммутаторы LAN.

    Компаний поставляющих модульные коммутаторы для сетей Ethernet много и разного класса.  Но мы рассмотрим только самые интересные.

    На бюджетные варианты ориентируются такие компании как DLink и Netgear. Стоимость каждого элемента и модуля составляет всего 50-70 тысяч рублей.

    Соответственно минимальный набор: корпус + управляющая матрица + 1 модуль обойдется от 150 т.р., каждый последующий модуль 50-70 т.р. Как видим от цены обыкновенного коммутатора почти не отличается, разве что предварительно надо вложиться в корпус и управляющую матрицу. Зато потом можно без проблем нарастить до 4-10 модулей, 10 модулей на 48 портов 1 Гб/с на каждом модуле, в итоге получится 480 гигабитных портов. И вся коммутация будет исправно работать даже при больших нагрузках.

    Но стоит сделать отступ. Если скорость он стабильно способен выдать между портами, какими бы они не были, хоть 10G. То такого параметра как пропускная способность, которая мерится в пакетах в секунду, в бюджетных коммутаторах может не хватить. В принципе мы работаем с весьма крупными файлами, и пакетов у нас хоть и много, но они большие. Но я бы не стал смотреть в сторону младших моделей.

    Средний ценовой диапазон занимает HP. Здесь цена каждого элемента и модуля идет уже от 100 тысяч рублей. Но здесь уже представлена большая линейка продуктов для любых потребностей. Для примера рассмотри линейку 12500.

    В этой линейке несколько вариантов стоек, начиная от самых маленьких (4 модуля) и заканчивая очень большими (18 модулей). И прелесть заключается в том что при росте компании нету необходимости менять модули, они едины во всей линейке, достаточно докупить более крупную стойку и ваш модульный коммутатор легко увеличивается в размерах.

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

    Стоимость адаптера Ethernet (c трансивером, в зависимости от технологии) со скоростью 10Гб/с составляет от 20 т.р.

     

    Вариант 2.  Модульные коммутаторы Fibre Chanel. Директоры.

    Модульные коммутаторы FC называются – Директоры. Здесь выбор уже поскромнее, количество фирм можно пересчитать по пальцам ATTO, Brocade, HP, IBM, Mellanox, QLogic, Cisco. Коммутаторы все так же состоят из трех элементов.

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

    Если рассматривать модульные FC коммутаторы то цена на каждый элемент начинается от 30 до 50 тысяч долларов, QLogic подешевле, HP подороже, остальные еще найти надо.  Соответственно цена минимальной комплектации из 1 модуля с 16 портами составит 100 000$ для QLogic и 150 000$ для HP.

    Если посчитать то минимальная цена 1-го порта FC составляет почти 60 т.рублей, для сравнения минимальная цена аналогичного порта Ethernet составляет всего 10 т. рублей. Теперь наверное понятно зачем придумали FCoE, хоть и в ущерб отзывчивости.

    Стоимость адаптера HBA с трансивером со скоростью 8 Гб/с составляет от 30 т.р.

     

    Вариант 3.  Модульные коммутаторы InfiniBand.

    Модульные InfiniBand коммутаторы также называются Директоры. Компании можно пересчитать по пальцам одной руки QLogic, Mellanox, Cisco.

    Бюджетный вариант тут вовсе не бюджетен, не модульный коммутатор стоит от 3 000$. Содержит до 36 портов.

    Модульные стоят еще дороже. Если брать линейку Mellanox GridDirector то корпус обойдется в 10 000$ и каждый модуль на 18-ть 40G портов обойдется еще в 10 000$. Линейка IS5000 имеет другую ценовую политику, базовый  корпус от 40 000$ и каждый модуль на 36-ть 10G портов стоит 7 000$. В некоторых линейках корпуса можно менять на более крупные не меняя модули.

    Стоимость адаптера HCA со скоростью 10 Гб/с составляет от 30 т.р.

    Вариант 4.  SAS коммутатор.

    Довольно таки интересная разработка компании LSI. Суть заключается в том что внешний выход SAS RAID контроллера цепляется на SAS коммутатор. Тем самым контроллеру становятся доступны все диски в такого рода сети. Но его практическое применение сильно ограничено низкой масштабируемостью, поэтому рассматривать не будем.

     

    Итог: Сводная таблица по выбору сети

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

    не модульный Ethernet, 1Gb/s Ethernet +FCoE, Netgear 1G/10G Ethernet + FCoE, HP 1G/10G FC, QLogic 8Gb/s InfiniBand, Mellanox IS5000 10G/40G
    Стоимость модуля, т.р. 50  50  100 1000 210/300
    Минимальная стоимость коммутатора, т.р 50 150 400 3000  1410
    Минимальное количество портов 48 48/10 48/10 16 36/18
    Максимальная стоимость 1 порта, т.р. 1 3/15 8/40 190  40/80
    Максимальная стоимость 1 Гб/с на порт, т.р. 1 3/1,5 8/4 24 4/2
    Максимальное количество портов 48  480/100 864/180  512  648/508
    Минимальная стоимость 1 порта, т.р. 1  1/5 2/10  60  8/11
    Минимальная стоимость 1 Гб/с на порт, т.р. 1 1/0,5 2/1 8 0,8/0,3
    Стоимость адаптера клиента, т.р. встроен встроен/20 встроен/20 30 30
    Стоимость 1 Гб/с для клиента, т.р. 1 1/2,5 2/3 9  3,8/3,3

    Как мы видим цена каждого порта падает по мере наращивания коммутатора и в итоге превращается в совсем демократичную цифру не отличающуюся от не модульного коммутатора. Стоимость 1Гб/с скорости в 10G порте не намного превосходит стоимость той же скорости в 1G порте, но зато способен обслужить гораздо большие объемы информации, а значит можно сэкономить на серверах.  А вот FC выходит совсем дорогим, стоимость 1 порта в полностью забитом коммутаторе составляет 60 т.р. + в каждого клиента необходимо установить еще и HBA адаптер ценой 30 т.р.. InfiniBand при всей его огромной цене, оказался самым эффективным по стоимости скорости 1 Гб/с для клиентской машины, а ведь в некоторых серверах адаптер уже встроен, тогда цена вообще падает до 300-800 рублей за 1Гб/с.

    Что использовать в студии каждый решит сам исходя из своих потребностей. Если FC слишком дорог, то InfiniBand слишком крут. Всем подряд пользователям не нужен 10G скорости. По мне так модульные Ethernet коммутаторы для студии являются самым оптимальным решением. Но в случае облачной инфраструктуры бесспорный лидер Infiniband.

     

    Раздел 3. Файловая система

    Теперь когда у нас есть где хранить файлы и мы можем обеспечить нашему хранилищу любые объемы и скорость сети, настал вопрос как и где хранить файлы и в каком виде. Обычно чтобы снизить нагрузки на сервер вводят специальные программы которые забирают файлы с сервера на локальный компьютер, на локальном компьютере происходит обработка файла и выгружается обратно на сервер. Такого рода системы называются SVN (Система Контроля Версия). Но я никогда не любил такого рода системы потому что они сильно замедляют и усложняют работу, и вообще редко когда там вообще можно встретить контроль версий. По мне так эта система придумывалась для программистов работающих на большом удалении друг от друга и в мультимедийной студии не очень удобны.

    Лично мне больше всего удобнее работать когда вместо локального диска для обработки файлов используется файловое хранилище. Располагать там все свои рабочие файлы и версии. В итоге мы получаем что с такими дисками как правило обмен происходит даже быстрее чем с локальным, при 10G сетях до 10 раз быстрее, и всегда имеем оперативный доступ ко всей информации хранящейся на сервере, к ее новейшим и старым, давно забытым версиям. Возможны даже такие ситуации когда один сотрудник не может справится с проблемой, кидает ссылку на файл в чат коллеге или лиду, а тот уже через секунду открывает и видит проблему.

    Но что бы на сервере не образовалась помойка из файлов их необходимо грамотно организовать. Для этого предлагается такого рода структура.

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

    1. pr001_demoProject – номер проекта и его название;
    2. dp019_compositing – номер и название задачи или отдела;
    3. в зависимости от задачи:
      1. ps001_Jon или ob001_tumba – номер и названия персонажа или объекта;
      2. sc001_firstFlight или sr001_firstFlight – сцена если это фильм или серия если это сериал, а также название серии. Название тут действует как подсказка если человек забыл в какой сцене что происходит. Серия тоже может состоять из сцен;
    4. sh001_flyInMyBlood – номер шота и его название;
    5. pr001_dp019_sc001_sh001_v077_likeProducer.comp – а вот в название файла мы вписываем полный путь до этого файла, версию файла и комментарий чтобы не забыть что интересного в этой версии и для чего мы ее сделали. Делается это для того чтобы случайно сохраненные файлы не в то место можно было легко идентифицировать и перенести туда где они должны лежать.

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

    Цифровое обозначение играет здесь ключевую роль при навигации, помогает расположить в правильной последовательности и упростить обозначение. Но главное за счет фиксированной нумерации позволяет заскриптовать и автоматизировать работу с файлами. Вплоть до того что пользователю надо будет вручную заходить на это хранилище только в экстренных случаях. Брать в работу файлы он будет из прожект менеджера, файл автоматом создастся в нужном месте с нужными исходниками и выходами, с нужным названием. А новая версия создаваться скриптом из интерфейса программы.

    Защита тоже весьма простая и суровая. На уровне прав доступа. Каждому отделу разрешено работать только в папке его отдела, до файлов другого отдела доступ либо только на чтение, либо вообще закрыт. Кроме того, периодически по файлам должен пробегаться скрипт и для определенных или всех файлов выставлять доступ только на чтение, чтобы сотруднику отдела ничего не оставалось кроме как создавать новую версию. Для тех у кого файлы помельче и работа поактивнее можно хоть каждый час, для крупных файлов с не столь быстрым редактированием раз в сутки.

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

     

    Раздел 4. Менеджер проектов

    Что касается менеджеров проектов, то тут я тоже не видел ни одного решения которые были бы оптимизированы для CG студий. Все что я видел (Jeera, Cerebro, Bolero, MS Project и мн. др.) слизаны с одного интерфейса и одной идеологии, оптимизированной на программистов и разработчиков, но не на мультимедиа.  Во всех этих программах просто напрочь игнорируется такие понятия как Пайплайн и KPI (Ключевой Показатель Эффективности), кроме того у них просто отсутствуют интерфейс для простой и наглядной записи и отображения информации по состоянию шота или сцены. И поэтому большинство руководителей предпочитают вести все свои записи в простых таблицах Excell. Как оказывается они намного наглядней и удобнее всех этих прожект менеджеров.

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

    1. Наглядность. Как видите достаточно просто посмотреть на таблицу и сразу становится ясно что в какой стадии находиться. Именно за это большинство и выбирают Excel вместо перечисленных менеджеров проектов. В качестве оптимизации наглядности каждая задача в шоте выделена в отдельный блок и окрашивается цветом по своему статусу. Последовательность из таких блоков образует подобие прогресс бара.
    2. Пайплайн. Производство любого шота здесь выстроено в жесткой последовательности по отделам (или задачам). Следовательно что либо забыть, перекрутить, отклониться от единой схемы производства, по забывчивости или по другим причинам, будет очень тяжело.
    3. KPI. Показатель эффективности сотрудника. Присутствует в любой уважающей себя отрасли, кроме русского CG. Но как оценить эффективность художника? Оказывается есть методы. Эффективность = Коэффициент сложности задачи * количество кадров /затраченное время. Коэффициент сложности это вовсе не абстрактная величина, а определяется экспериментальным путем, и означает во сколько раз более сложная задача будет делаться дольше чем менее сложная. По сложности любую задачу тоже можно разобрать на составляющие. Например для конвертации в стерео: Коэффициент сложности равен 1 если шот можно сделать автоматической конвертацией, 2 если в сцене только статика и камера почти не движется, 3 если в сцене 1 персонаж, 4 если в сцене много персонажей, 5 если много персонажей или сложная плановая сцена. Тоже самое с трекингом: 1 если в сцене только статика, 5 если пришлось делать трекинг по объектов. Анимация: 1 простые твердые объекты, 2 механизм, 3 персонаж, 5 многорукий монстр. Моделинг: 1 Вазы и тарелки, 5 сложный робот для анимации. Как видите опять же все просто, стоит только захотеть. KPI может сильно варьироваться при прочих равных от шота к шоту, тут все зависит не только от сложности, но и от настроения и самочувствия исполнителя. Но в среднемесячных показателях он очень предсказуем и позволяет точно распланировать производство.
    4. Похожий шот. К счастью в сцене бывает очень много похожих шотов, с одинаковой графикой. Но к сожалению я встречал много супервайзеров которые похожие шоты раздают разным исполнителям, что не есть эффективное использование ресурсов. Если одному сотруднику надо допустим 6 часов на исполнение 1 шота, и еще по 1 часу на перекидывание графики на похожие шоты, и при наличии 5 похожих шотов в сцене, такой сотрудник затратит только 10 часов. А если раскидывать по разным сотрудникам, то будет затрачено 6 часов*5 сотрудников=30 часов, и у каждого будет разная картинка. Поэтому похожие шоты лучше заранее отлавливать и заносить соответствующие записи.

    Но в один прекрасный момент возможностей офисных пакетов начинает не хватать, и тут на помощь может прийти веб версия менеджера проектов.

     Которая поможет уже решить следующие проблемы:

    1. Автоматизация. Excel конечно позволяет много чего автоматизировать, но не так как самописная программа. После сбора статистики автоматом можно назначать исполнителей, последовательность исполнения, предполагаемое время взятие и исполнением задачи.
    2. Планирование. После того как появиться статистика по производительности сотрудников, и появится возможность автоматически распределить исполнителей и время исполнения, можно будет полностью распланировать и просчитать весь проект еще до начала производства, опираясь на статистические данные, а не слова “Я так думаю”.
    3. Совместный доступ. Конечно хорошо когда задача назначается не криком через плечо взять задачу. Следовательно исполнители должны иметь доступ к менеджеру проектов, прочитать описание задачи, посмотреть скетчи и вспомогательные материалы, отметиться о взятие и сдачи, отписать об ошибках. Тем самым задача ведение проекта распределяется между сотрудниками.
    4. Веб приложение. Так как приложение будет работать в обычном браузере нет необходимости его устанавливать и обновлять на компьютерах исполнителей. Масштабируются такие приложения за счет добавление дополнительных веб серверов. А за счет масштабируемости на такие менеджеры проектов можно возложить еще и обслуживание задач, например сборку сцен из составных частей.

    Приведенная здесь версия делалась для студии которая занималась наложением графики на отснятый материал. Немножко модифицировав можно получить версию и для студий которые занимаются полностью компьютерным производством.

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

     

    Заключение

    В итоге мы имеем файловое хранилище, которое легко масштабируется до огромных размеров и скоростей. Сеть способную вместить в себя тысячи сотрудников на высоких скоростях. Простую и понятную каждому файловую систему, позволяющую разбить проект на простые составляющие. И не менее простой менеджер проектов, позволяющий быстрой оценить состояние проекта и автоматизировать большинство рутинных операций.

    Надеюсь теперь ни у кого не возникнут технические трудности при росте компании и объемов компьютерной графики. И художников ни что не отвлечет от их главной задачи. Когда техническая сторона налажена, остается надееться что бы и художественная часть не подвела.

     

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




    26th Май 2013 Главная, Статьи

    Leave a Reply