Как заставить хлам приносить пользу?

:

О чём это я?


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

(Здесь и далее будет говориться о немного специфичной и оптимизированной настройке LTSP и поднятии на его основе кластера серверов).

Некая организация решила развернуть у себя терминальный сервер и кучу терминальных клиентов. Без всяких предварительных вычислений, без тестов и видимо без мозгов кем-то были закуплены HP-шные терминалы нескольких видов. Различались они в первую очередь объёмом оперативной памяти и размером встроенной флешки. Размер ОЗУ колебался в пределах от 128 до 512 мб, размер накопителя — от 64 до 256. Ну и для полноты картины стоит упомянуть используемые x86 процессоры Via/AMD ~800 Мгц, да видео от той же Via или SiS. Зоопарк короче.

Ну купили и купили чего уж тут. Развернули сервер, стали подключать клиентов… И примерно после подключения 70-го достаточно мощный сервер стал просто проседать. А вместе с сервером стали тормозить и клиенты, ну а как иначе? Не хватало ни сетевых ресурсов ни ресурсов самого сервера. Что делать? Ведь ещё валяется порядка 150 не подключенных терминалов! Ай-яй-яй сказали одновременно пользователи, админы и прочая. Помудохались с полгодика да и забили. Кстати да, терминалы изначально были под Windows CE и соответственно с терминальным сервером взаимодействовали по RDP.

А ещё через полгода внезапно вспомнили… И отдали на аутсорс. С напутствием «А вот если решите проблему производительности, всем хорошо будет!». Собственно что требовалось теперь от терминалов. Необходимо было, чтобы при запуске терминала запускалась собственноручно написанная софтина позволяющая пользователю авторизоваться на «неважно-что-там» сервере и если авторизация прошла успешно — запустить браузер. Делов то? Ага… И всё это должно работать даже на самом дохлом из терминалов. То есть планка — 128Мб ОЗУ, 64Мб на накопителе. За процессор можно не беспокоиться, он как нибудь справится…

Попытка номер раз, или Embedded Linux нам поможет!


Естественно, с учётом количества клиентов, было решено не использовать терминальные решения и сделать каждый клиент самодостаточным. Для этого был изначально заюзан Buildroot. Был собран маленький дистрибутив, включающий в себя ядро, минимальный набор системных и сетевых утилит, Qt Embedded, чуток собственного софта и… да по сути всё. Размером це вышло около 40 мб, то есть вполне помещалось на самой слабой железке. Правда заработало не для всех терминалов. Если на Via собранный комплекс заработал с полпинка, то вот на терминале с AMD оно заводиться отказалось напрочь. Пришлось отдельно собирать туда Debian, благо что места в том типе железок было дофига.

Проект был успешно завершён и даже сдан заказчику. Работало всё конечно не идеально, были затыки, приходилось ограничивать количество вкладок в браузере 5-ю, чтобы он особо не налегал на ресурсы терминалов. Уложились можно сказать тютелька в тютельку! Все были счастливы, вот только…

А как же весёлая ферма?


Да да… Вездесущий и так необходимый Flash. Ну и до кучи — Java-плагин для браузера. Проблемы всё те же — места в прошивке тупо не хватает, а вся оперативная память уже съедена WebKit-ом. Кроме того, даже если бы у нас хватило места, Qt Embedded не поддерживает плагины для браузера, так как на Linux для работы этих самых плагинов ей необходим X.Org. Ну да, тот самый XEmbedWindow, или как его там… Короче фигня, которая позволяет встраивать в свои приложения внешние окошки. А как я уже сказал, даже ели бы нам хватило места, то самый дохлый терминал со своими 128 мб ни разу не потянул бы иксы+Qt+запущенный браузер с вебкитом+Flash. Грустно. Чешем репу… Долго чешем… Наконец пробуем кое-что другое. Берём как вариант такую схему: ОС запускается с терминала, но при логине, сессия таки запускается с терминального сервера через NX-клиент. По идее это должно хоть немного на разгрузить сервер. Хрена лысого…

Собираем новый Buildroot, на этот раз выкинув Qt и запихав X.Org и NX-клиент. На сервере ставим ради теста официальный NoMachine NX Server. Это вот здесь если кто не в курсе. Грузимся, видим мега диалог входа на английском, разбираемся, подключаемся к кастомной сессии… Вуаля, браузер с сервака мы запустили. Грузим в 5 вкладок ютубы, пускаем видео. работает. Смотрим нагрузку на тестовом сервере… Слово «Лять!» так и хочется сорваться с уст: ~100-120 мб ОЗУ, отнятой одним клиентским браузером никак не приближают нас к нашей цели оптимизации… Прибавим к этому ещё и нагрузку на сеть, становится совсем не весело…

Было бы у нас хоть 15-30 клиентов, ещё можно было бы потерпеть… Но никак не over 300. Хотя опять же… Никаких настроек автовхода у NX-клиента найдено не было, как не было найдено и необходимых параметров которые можно передать ему при запуске — типа запустись с этим конфигом, покажи мне такую то софтину на серваке, сам при этом не мозоль глаза своей басурманской мордой и вообще заткнись!

Ха-Ха! И у тебя ничего не вышло?


Ну что вы… Тогда бы не было всего этого текста =) В общем, посмотрев ещё в сторону парочки вариантов (типа запускать всё в том же билдруте что-то вроде ssh -X) я пришёл к выводу что всё это не то и действовать надо иначе.

В этот момент в своих поисках я наткнулся на LTSP — Linux termianl Server Project. В принципе, о нём я читал ещё в самом начале, но посчитал, что «всё таки там терминальный сервер, вернёмся к тому же с чего начали» и скептически закрыл страничку в википедии. Однако, отчасти за неимением других вариантов, а отчасти оттого что в разделе Ubuntu Help посвященному LTSP как фича описывалась возможность запуска локальных приложений на самом терминале, я решил попробовать…

Dust Jo It!


Итак. В качестве изначальных условий, допустим, мы имеем i386-компьютер или виртуалку (с двумя сетевыми картами на борту) и дистрибутив Ubuntu Server 11.04. Плюс пара-тройка терминалов, которые умеют грузиться по сети. Для теста…

Первым делом конечно же ставим на наш сервер Ubuntu. Сразу после чего выкидываем лишние репозитории и доустанавливаем необходимое ПО.

echo "deb http://ftp.ubuntu.com/ubuntu/ natty main restricted universe multiverse" > /etc/apt/sources.list
apt-get update
apt-get install bash-completion openbox xserver-xorg xorg xbase-clients xterm ltsp-server-standalone arora gettext -y

Далее, нам необходимо настроить оба сетевых интерфейса. так. чтобы один глядел в общую сеть, а второй в свою собственную подсеть. Правим файлик /etc/network/interfaces:
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.1.170
  netmask 255.255.255.0
  gateway 192.168.1.1

auto eth1
iface eth1 inet static
  address 192.168.22.1
  netmask 255.255.255.0

После чего собираем клиент:
ltsp-build-client

Предпоследним мы настраиваем DHCP-сервер, файл /etc/ltsp/dhcpd.conf:
  #authoritative;
  class "ltsp-clients" {
      match if substring (hardware,1,8) = "00:14:85";
  }
  subnet 192.168.22.0 netmask 255.255.255.0 {
      #range 192.168.22.20 192.168.22.250;
      option domain-name "example.com";
      option domain-name-servers 192.168.22.1;
      option broadcast-address 192.168.22.255;
      option routers 192.168.22.1;
      option root-path "192.168.22.1:/opt/ltsp/i386";
      next-server 192.168.22.1;
      option subnet-mask 255.255.255.0;
      option root-path "/opt/ltsp/i386";
      if substring( option vendor-class-identifier, 0, 9 ) = "PXEClient" {
          filename "/ltsp/i386/pxelinux.0";
      } else {
          filename "/ltsp/i386/nbi.img";
      }
      pool {
        allow members of "ltsp-clients";
        range 192.168.22.20 192.168.22.250;
      }
  group {
  next-server 192.168.22.1;
  host test {
     deny-unknown-clients;
     hardware ethernet 00:14:85:47:AA:73;
     fixed-address 192.168.22.53;
  }
 }
}

Здесь мы указываем что и как грузить, и в какой подсети раздавать IP-адреса нашим клиентам. Кроме того, мы ведь лишь тестируем наше решение, а следовательно не хотим чтобы наш DHCP сервер ни в коем случае не конфликтовал с основным DHCP находящемся в сети организации. Именно для этого мы сначала обзываем его не авторитетным(когда машинка попросит у него определённый IP, о котором она бредит ночами, наш сервер просто отфутболит её) и затем выделяем пул MAC-адресов, которым наш сервер может назначать IP.

Почти всё готово для первой загрузки, но IP нашего сервера далеко не 192.168.1.1, а следовательно при загрузке система на клиенте будет не вкурсах где искать NBD-сервер. Поэтому нам нужно подправить файл /var/lib/tftpboot/ltsp/i386/pxelinux.cfg/default, заменив параметр nbdport на nbdroot. Этот файл качается клиентом при загрузке PXE в качестве базового конфига:

default ltsp
label ltsp
kernel vmlinuz
append ro initrd=initrd.img quiet splash nbdroot=192.168.1.170:2000

Перезагружаем сервер, запускаем на клиенте загрузку по сети и… видим экран входа в систему:

image

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

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

useradd ltspclient -p $(perl -e'print crypt("1234", "aa")') -m
echo "ltspclient ALL = NOPASSWD: ALL" >> /etc/sudoers
echo "ltspclient ALL = NOPASSWD: ALL" >> /opt/ltsp/i386/etc/sudoers

В LTSP есть ещё один конфигурационный файл, в котором могут содержаться важные и разнообразные настройки, это файл /var/lib/tftpboot/ltsp/i386/lts.conf. Параметры указанные в нём используются уже во время загрузки основной операционной системы, образ которой по NFS монтируется сразу после загрузки PXE. Заполним его примерно так:
[Default]
LDM_AUTOLOGIN=true
LDM_USERNAME=ltspclient
LDM_PASSWORD=1234
LOCAL_APPS=True
XkbModel = pc104
XkbLayout = us,ru
XkbOptions = "grp:alt_shift_toggle,grp_led:scroll"

Как несложно догадаться это позволит нашему пользователю автоматически войти в систему. Клёво, ага. Только что ему делать в пустом X.Org? Надо бы запустить наш собственный софт после входа. Для этого всегда есть замечательный /home/ltspclient/.xinitrc. Создаём его и пишем:
#!/usr/bin/env bash
export LANG="en_US.UTF-8"
export LC_ALL="en_US.UTF-8"
export LANGUAGE="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
openbox &
authorize -style cleanlooks
#xterm

И конечно же создаём на него симлинк:
ln -s /home/ltspclient/.xinitrc /home/ltspclient/.xsession

Перезапускаем клиент. Вуаля, после загрузки запустилось наше собственное приложение (допустим мы уже развернули его на сервере). Оно запустилось с сервера, но оно совсем мелкое, памяти занимает 2 мб и о нём можно особенно не беспокоиться. Другое дело браузер. после запуска которого сервер неплохо так проседает… опять двадцать пять приехали к тому что на сервере жрутся ресурсы =(. Во первых сеть — постоянно для всех клиентов рисовать полностью браузер (помним что Qt рисует в иксы просто картинками?). Во вторых — это 100 мб ОЗУ. На сервере с 8 Гб ОЗУ можно будет развернуть максимум 80 клиентов и то при условии что они не будут постоянно жрать процессор и сеть.

Ладно… Фигня война, главное манёвры! Что там было про локальные приложения? Как оказалось, локальные приложения, это реально приложения, которые расположены не на сервере, а установлены прямиком в монтируемый образ системы. И через некоторую обёртку из текущей серверной сессии их таки можно запустить. В этом случае вся нагрузка ложится на терминал. Для реализации подобной задумки нам следует доставить в образ необходимые библиотеки и наш браузер. Кратко это выглядит так:

cd /opt/ltsp/i386/
mount -o bind /proc ./proc
chroot ./ 
apt-get update
apt-get install arora sudo flashplugin-nonfree sun-java6-plugin ssh gettext -y 
exit
umount /opt/ltsp/i386/proc

ltsp-update-sshkeys
ltsp-update-image --arch i386

Всё просто — мы делаем crhrot в каталог /opt/ltsp/i386/ в котором лежат кишки образа, доставляем прям туда нужный софт (предположим ради примера, что наш браузер — это arora), а затем просто обновляем образ системы. Вот и всё, теперь у нас есть приложение которое мы можем запустить локально!

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

Файл widget.h:

...
class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void readDeviceInfo();
...
protected:
    QString deviceUuidInfoFileName;
...
};
...

Файл widget.cpp:
...
Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    ...

    //test for memory
    QString uuid = QUuid::createUuid ().toString().remove("{").remove("}");
    deviceUuidInfoFileName = QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/.info_" + uuid + "_mem.txt";
    QString runFileName = QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/deviceInfo_" + uuid +".sh";

    QString program = "/usr/bin/ltsp-localapps \"free -m | grep \"Mem:\" > " + deviceUuidInfoFileName + "\"";

    QFile runFile(runFileName);
    if (runFile.open(QIODevice::WriteOnly | QIODevice::Text)){
        QTextStream out(&runFile);
        out << program << "\n";

        QFile::Permissions Permissions = runFile.permissions();
        //  Permissions == 0x6600;
        Permissions |= QFile::ExeOther|QFile::ExeUser|QFile::ExeOwner|QFile::ExeGroup;
        //  Permissions == 0x7711;
        if(!runFile.setPermissions(Permissions)) {
            qDebug() << "Cannot set file Permitions";
            return;
        }
        Permissions = runFile.permissions();
        //  Permissions == 0x6600;
        if(Permissions & (QFile::ExeOther|QFile::ExeUser|QFile::ExeOwner|QFile::ExeGroup)) {
            qDebug() << "Info", "Executable file";
        }
        runFile.close();
    }

    QProcess *ltspla = new QProcess(this);
    ltspla->start(runFileName);
    ltspla->waitForFinished();
    runFile.remove(runFile.fileName());

    QTimer::singleShot ( 500, this, SLOT(readDeviceInfo()));
}

void Widget::readDeviceInfo()
{
    QFile file(deviceUuidInfoFileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "Error reading memory info!";
        QTimer::singleShot ( 500, this, SLOT(readDeviceInfo()));
        return;
    }

    QTextStream in(&file);
    QString memory = in.readAll().split(" ",QString::SkipEmptyParts).at(1).trimmed();
    qDebug() << "Memory:" << memory;
    file.close();
    file.remove(file.fileName());

    if (memory.toInt()<220){
        ui->rbRunBrowserRemote->setChecked(true);
    }
}
...

Костляво, но… По сути мы здесь запускаем команду /usr/bin/ltsp-localapps «free -m | grep „Mem:“ > filename.txt» и затем просто считываем данные из файла filename.txt и принимаем решение какую версию браузера запускать. Если ОЗУ меньше определённого уровня, то пускаем серверную версию, если больше — локальную.

Что это даёт? А вполне ощутимые результаты… После запуска локального браузера, который разворачивается во весь экран, клиент по сути забывает о сервере. Ибо одномоментно локально запущены и иксы и приложение — ничег ос сервера не рисуется, а значит передачи данных по сети не осуществляется. Кроме того вычисления все происходят так же локально и сервер совсем не нужен. Круто? Ага! Это позволит отделить все более-менее нормальные клиенты от их слабоватых собратьев. Первые будут сами обеспечивать себя ресурсами, а вторые всё также паразитировать на сервере.

Если примем что нормальных и долбанутых клиентов у нас примерно поровну, то часть поставленной задачи мы достигли — смогли оптимизировать процесс так, чтобы вполовину разгрузить сервер. Ведь нормальным клиентам он будет нужен только во время загрузки и кратковременно при авторизации. Хотя узкое место конечно остаётся — это утро, когда куча народу приходит и жмякает кнопкой включения на своих клиентах. Здесь уже ничего конечн оне поделаешь, нагрузка по трафику будет ощутима. Но это тоже достаточно относительно. И вот почему: процесс загрузки можно условно разделить на два этапа:
1) Загрузка PXE — на это тратится примерно 8 мб трафика с интерфейса eth1. После чего следует небольшая пауза, инициализация и запрашивается образ основной ОС для монтирования
2) Загрузка основной ОС — это загрузка ядра, библиотек, запуск демонов и прочей фигни. Данный этап требует ещё примерно 16 мб данных передаваемых по сети по интерфейсу eth0. Сюда также входит автологин и запуск первого приложения.

Это что же получается? Если скорость наших сетевых карточек 100 МБит/сек, то это 12.5 мбайт/сек. Округлим до 10, чтобы выкинуть различные огрехи связанные с коллизиями и т.д. Далее. К примеру у нас терминалы трёх типов — если все они будут включены одновременно в 9 утра, то загружаться они начнут всё же немного с разной скоростью. Разбор между разными типами думаю составит 1-3 секунды.для каждого клиента нам нужно при загрузке передать ~26 мб данных, то есть в общем по ~3 секунды на клиента (не сразу конечно же), умножим 3 * 300 (от фонаря количество одновременно или с периодом в минуту-две включенных клиентов).выходит 900 секунд или 900/60=15 минут, или 900*26 = 23 400 мегабайт данных. Хреновато выглядит, не правда ли? Вообще конечно но не сказать что очень. У нас ведь два интерфейса, и через каждый из них будет прокачиваться свой объём данных — 900*16=14 400 мб через eth0 и 900*8=7 200 мб через eth1. нагрузка на сеть будет оставаться высокой, но нагрузка на каждый из интерфейсов существенно снизится. К сожалению, тут как ни крути, а очень узкое место остаётся и казалось бы никуда от него не деться. Остаётся либо ставить гигабитные интерфейсы, либо уповать на время загрузки одного терминала. которое равняется ~45 секундам, по которым и размазан весь процесс скачивания данных с сервера.

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

Промежуточные выводы...


По сравнению с предыдущими вариантами и по сравнение с тем что было использовано на этих железяках изначально. в идеальных условиях мы смогли в половину разгрузить наш сервер, переложив часть работы на тот из нашего хлама, который не совсем хлам… И больше того мы смогли запихать туда полноценный WebKit-браузер с подключаемыми плагинами, а стало быть пользователи смогут невозбранно резаться во множество Flash/html5-игрушек и смотреть порно прямо на рабочем месте (хотя заказчик и говорит что плагины были нужны для банк-клиентов, но мы то с вами знаем =) ).

Так это коенчно всё хорошо… Половина дела сделана. Но ведь у нас остаётся ещё порядка 150 дохлых клиентов при том что выше мы уже поняли что обслужить сервер сможет максимум 80, а с учётом того что серверу нужно не просто обслужить на грани возможностей, а ещё и работать, да кроме того держать подключение с ещё 150 более-менее нормальными клиентами, то выходит что комфортно смогут расположиться на сервере ~40-50 одновременно подключенных дохлых терминальных клиентов + 150 нормальных. Не густо, хоть и не пусто… Что же делать дальше? А дальше нам на помощь придёт…

Клейстер! Кластер!


Да да, именно кластер! Дело в том. что за время своего развития, LTSP дорос до того что у него появилась возможность создавать кластер серверов, с балансировщиком нагрузки и даже Web-интерфейсом. «Уууууу....» — сказали суровые сибирские лесорубы. Ну а я пошёл читать техническое описание и мануал по разворачиванию LTSP-кластера.

Для начала разберёмся что такое из каких компонентов состоит ltsp-cluster. Во первых, сам по себе LTSP-Cluster представлят собой набор плагинов к ltsp-серверу, включённых в поставку по умолчанию дистрибутива LTSP. Кроме того, для сборки клиента поддерживающего кластеризацию не нужно ничего дополнительно ставить, достаточно запустить сборку клиента с параметром --ltsp-cluster

Проект включает в себя следующие компоненты:

  • Load Balancer Server — главный сервер контроля нагрузки, следящий за всеми подключёнными серверами приложений.
  • Control Center (Web Interface) — веб-интерфейс, позволяющий получить информацию о подключенных клиентах и нагрузке на сервера. а также произвести некоторые настройки.
  • Load Balancer Agent for GNU/Linux/Windows — ставится на каждый сервер приложений, посылает информацию о загруженности сервера в Load Balancer Server
  • Accounts Manager — демон ставящийся на одной машине с агентом Load Balancer Agent. Может динамически создавать и удалять пользователей при необходимости. Ну то есть вот завели мы на основном сервере пользователя ltspclient, а на app-серверах то его как бы и нету… А у нас автологин… Вот якобы Accounts Manager и следит за автоматическим созданием этого пользователя при обращении к одному из серверов.
  • PXE Configuration Editor — получает из Control Center параметры и на их основе генерирует файл конфигурации PXE.
  • NX Loadbalancer — запускает SSH сервер и следит за подключение пользователя nx в случае необходимости перекидывает его на тот или иной сервер.

Попробуем, чо…

Для начала на нашем сервере необходимо пересобрать нашего клиента с опциями поддержки кластера:

ltsp-build-client --arch i386 --ltsp-cluster --prompt-rootpass --accept-unsigned-packages

Во время сборки нам будет задано несколько вопросов, на которые нужно будет ответить:
Configuration of LTSP-Cluster
NOTE: booleans must be answered as uppercase Y or N
Server name: 192.168.1.170
Port (default: 80): 
Use SSL [y/N]: N
Enable hardware inventory [Y/n]: Y
Request timeout (default: 2): 0

В будущем подправить эти параметры можно будет в файле /opt/ltsp/i386/etc/ltsp/getltscfg-cluster.conf. После ещё будет приглашение к вводу пароля root.
Когда с этим будет покончено, нужно решить как разворачивать Control Center и Load Balancer Server — на одном физическом сервере, на разных или просто на разных виртуалках. Ну для простоты развернём их там же где и основной сервер.

Первым делом конечно поставим Control Center:

apt-get install ltsp-cluster-control postgresql --no-install-recommends

Подправим конфиг /etc/ltsp/ltsp-cluster-control.config.php
...
$CONFIG['loadbalancer'] = "192.168.1.170"; #Hostname of the loadbalancer
...

Создадим нового пользователя и новую базу для PostgreSql (пароль как в файле /etc/ltsp/ltsp-cluster-control.config.php — ltspcluster ):
sudo -u postgres createuser -SDRlP ltsp
sudo -u postgres createdb ltsp -O ltsp

далее необходимо развернуть саму схему в созданную базу, колдунство короче:
cd /usr/share/ltsp-cluster-control/DB/
cat schema.sql functions.sql | psql -h localhost ltsp ltsp
wget http://bazaar.launchpad.net/%7Eltsp-cluster-team/ltsp-cluster/ltsp-cluster-control/download/head%3A/controlcenter.py-20090118065910-j5inpmeqapsuuepd-3/control-center.py
wget http://bazaar.launchpad.net/%7Eltsp-cluster-team/ltsp-cluster/ltsp-cluster-control/download/head%3A/rdpldm.config-20090430131602-g0xccqrcx91oxsl0-1/rdp%2Bldm.config
apt-get install python-pygresql
/etc/init.d/apache2 stop
python control-center.py rdp+ldm.config
/etc/init.d/apache2 start

После всего этого переходим по ссылке 192.168.1.170/ltsp-cluster-control/Admin где уже можно потыкаться по интерфейсу… Тут же на первой странице стоит указать некоторые важные параметры:
LANG = en_US.UTF-8
LDM_DIRECTX = True
LDM_SERVER = %LOADBALANCER%
LOCAL_APPS_MENU = True
SCREEN_07 = ldm
TIMESERVER = ntp.ubuntu.com
XKBLAYOUT = us,ru

С первым покончили… Следующий… Балансировщик нагрузки… также разворачиваем на этой же машине:
apt-get install ltsp-cluster-lbserver --no-install-recommends

Его конфиг /etc/ltsp/lbsconfig.xml:
<?xml version="1.0"?>
<lbsconfig>
    <lbservice listen="*:8008" max-threads="2" refresh-delay="60" returns="$IP"/>
    <lbslave is-slave="false"/>
    <mgmtservice enabled="true" listen="*:8001"/>
    <nodes>
        <group default="true" name="default">
            <node address="http://192.168.1.171:8000" name="ltsp-appserv01"/>
        </group>
    </nodes>
    <rules>
        <variable name="LOADAVG" weight="50">
            <rule capacity=".7"/>
        </variable>
        <variable name="NBX11SESS" weight="25">
            <rule capacity="$CPUFREQ*$CPUCOUNT*$CPUCOUNT/120" critical="$CPUFREQ*$CPUCOUNT*$CPUCOUNT/100"/>
        </variable>
        <variable name="MEMUSED" weight="25">
            <rule capacity="$MEMTOTAL-100000"/>
        </variable>
    </rules>
</lbsconfig>

Скажу только, что для наших целей в будущем нужно лишь изменять параметры внутри.
    <nodes>
        <group default="true" name="default">
            <node address="http://192.168.1.171:8000" name="ltsp-appserv01"/>
        </group>
    </nodes>

Кроме того можно изменить количество потоков. По умолчанию рекомендуется (яхз почему) выставить 1:
<lbservice listen="*:8008" max-threads="1" refresh-delay="60" returns="$IP"/>

Тут как несложно догадаться можно добавлять/удалять сервера приложений. Наконец перезапускаем Load balancer:
/etc/init.d/ltsp-cluster-lbserver restart

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

Ах да, также необходимо, чтобы балансировщик умел распознавать доменное имя ltsp-appserv01, для этого в /etc/hosts на нашем lbserver-е необходимо дописать строчку вроде:

192.168.1.171 ltsp-appserv01

Кстати говоря, уже сейчас загрузившись с любого клиента можно будет увидеть информацию о том, что данный клиент подключён к серверу приложений 192.168.1.170 — то есть к нашему главному серверу. В Control Center по этому поводу уже вовсю пишутся логи и в базе записывается MAC-адрес нашего клиента. Что-то мне подсказывает, что на рутовый сервер так же необходимо поставить Account Manager, но делать этого пока не хочется, лучше перейдём к настройке первого из наших серверов приложений. Ну и скриншот веб-интерфейса. чтобы как то разнообразить этот текст:
image

Итак, на виртуальную машину либо на другой компьютер ставим Ubuntu Server 11.04 32bit. В процессе установки hostname указываем как ltsp-appserv01 и отмечаем для установки Open SSH Server. Для удобства… После успешной установки загружаемся и первым делом правим файл /etc/network/interfaces. У меня какое-то странное ощущение дежавю, а у Вас?

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.1.171
  netmask 255.255.255.0
  gateway 192.168.1.1

Указали что это компьютер с адресом 171 — именно такой IP прописан для него в конфиге lbserver-а. Перезапускаем интерфейс:
$ sudo ifdown eth0 && sudo ifup eth0

Адрес успешно сменился. Идём дальше. А дальше у нас на выбор есть два варианта развития событий, в зависимости от стоящей задачи:
1) Мы просто ставим ubuntu-desktop и при логине пользователя запускается стандартная оболочка убунты
2) Мы можем запускать ровно то что необходимо нам. По этому пути и пойдём…
Снова топаем по проторенной дорожке:
echo "deb http://ftp.ubuntu.com/ubuntu/ natty main restricted universe multiverse" > /etc/apt/sources.list
apt-get update
apt-get install bash-completion openbox xserver-xorg xorg xbase-clients xterm ltsp-server ltsp-server ltsp-cluster-lbagent ltsp-cluster-accountmanager arora -y
update-rc.d -f nbd-server remove

Ждём пока поставится всё это дело… По сути здесь больше делать нечего, перезагружаем сначала сервер с агентом, а затем и основной. Пробуем вновь загрузиться с клиента. И так уж выходит что в качестве Application сервера используется наш старый добрый 192.168.1.170, то есть главный сервер. В этом нет ничего удивительного, потому как его загруженность очень низкая и Load Balancer просто напросто не перекидываает нагрузку на другие сервера. Со временем это конечно изменится и другие сервера будут вынуждены принять на себя часть нагрузки.

Ага, щаз! Для того чтобы заработала балансировка придётся ещё самостоятельно выдумать её алгоритм, занести в пул весь список серверов и наконец добавить все сервера в файле ssh_known_hosts в образе. Тыц!

1) Пул адресов. Для этого в конец файла /var/lib/tftpboot/ltsp/i386/lts.conf дописываем строчку:

MY_SERVER_LIST = "192.168.1.170 192.168.1.171"

2) Выдумываем алгоритм (я просто стащил отесда) и записываем его в файл:

#!/bin/sh

max_rank=-1
max_server=''
for server in $MY_SERVER_LIST; do
    rank=$(nc $server 9571 | sed -n 's/rating://p')
    if [ "$rank" -gt "$max_rank" ]; then
        max_rank="$rank"
        max_server="$server"
    fi
done
echo "$max_server"

Выполняем:
chmod a+x /opt/ltsp/i386/usr/share/ltsp/get_hosts

Ничего сложного, просто проходимся по всем серверам NetCat-ом и выбираем наименее загруженный из них. Этот скрипт можно изменить так чтобы он использовал сервер балансировки, но мне этого делать не хочется, потому что используя такой простенький скрипт в принципе можно будет избавиться от lbagent и lbserver.

3) Во время настройки и поднятия всех дополнительных серверов у нас уже были сгенерированы пары DSA и RSA ключей. Для того, чтобы клиенты могли подключаться к дополнительным серверам, следует скопировать сгенерированные публичные ключи с дополнительных серверов сначала на основной сервер, а затем в образ. Для этого, сначала на каждом дополнительном сервере делаем следующее:

ltsp-update-sshkeys --export ssh_known_hosts.192.168.1.171
scp ssh_known_hosts.192.168.1.171 root@192.168.1.170:/etc/ltsp/

А на главном сервере:
ltsp-update-sshkeys && ltsp-update-image --arch i386 && echo -e 'default ltsp\nlabel ltsp\nkernel vmlinuz\nappend ro initrd=initrd.img quiet splash nbdroot=192.168.1.170:2000\n' > /var/lib/tftpboot/ltsp/i386/pxelinux.cfg/default

Проверяем:
cat /opt/ltsp/i386/etc/ssh/ssh_known_hosts

Единственное чего я не понимаю — нафига вообще тогда нужен сервер балансировки… Вероятно в будущих релизах файл /opt/ltsp/i386/usr/share/ltsp/get_hosts, но по ходу ейчас это не так. Но так или иначе, теперь балансировка точно работает.

Кстати говоря, проверить насколько загружен тот или иной сервер приложений, можно используя скрипт /usr/share/pyshared/lbserver/test_comm.py. Для начала указываем в нём адрес необходимого сервера:

rpcserver = xmlrpclib.ServerProxy("http://192.168.1.171:8000")

А затем выполняем его и смотрим на результат:
$ python /usr/share/pyshared/lbserver/test_comm.py
[{'name': 'MEMTOTAL', 'value': 507788}, {'name': 'CPUFREQ', 'value': 3502.64}, {'name': 'CPUCOUNT', 'value': 1}, {'name': 'DISTRO', 'value': 'Ubuntu 11.04'}, {'name': 'IP', 'value': '192.168.1.171'}, {'name': 'HOSTNAME', 'value': 'ltsp-appserv01'}, {'name': 'ADDRESSES', 'value': '192.168.1.171'}, {'name': 'MEMFREE', 'value': 476144}, {'name': 'MEMUSED', 'value': 31676}, {'name': 'LOADAVG', 'value': 0.0}, {'name': 'USERS', 'value': 'kafeg'}, {'name': 'NBUSERS', 'value': 1}, {'name': 'NBX11SESS', 'value': 1}]

Не менее полезным может оказаться скрипт /usr/share/pyshared/lbserver/test_mgmt.py, который может обратиться к главному серверу и также вернуть кое-какую информацию:
rpcserver = xmlrpclib.ServerProxy("http://192.168.1.170:8001")
$ python /usr/share/pyshared/lbserver/test_mgmt.py 
[{'default': True, 'nodes': [{'active': True, 'lastWeight': 100, 'values': [{'critic': False, 'name': 'MEMTOTAL', 'value': 507788}, {'name': 'LOADAVG'}, {'critic': False, 'name': 'ADDRESSES', 'value': '192.168.1.171'}, {'critic': False, 'name': 'IP', 'value': '192.168.1.171'}, {'critic': False, 'name': 'HOSTNAME', 'value': 'ltsp-appserv01'}, {'critic': False, 'name': 'NBUSERS', 'value': 1}, {'critic': False, 'name': 'NBX11SESS', 'value': 1}, {'critic': False, 'name': 'MEMFREE', 'value': 476052}, {'critic': False, 'name': 'CPUCOUNT', 'value': 1}, {'critic': False, 'name': 'CPUFREQ', 'value': 3502.64}, {'critic': False, 'name': 'USERS', 'value': 'kafeg'}, {'critic': False, 'name': 'MEMUSED', 'value': 31768}, {'critic': False, 'name': 'DISTRO', 'value': 'Ubuntu 11.04'}], 'name': 'ltsp-appserv01', 'address': 'http://192.168.1.171:8000'}], 'name': 'default'}]

Всё


Вот пожалуй и всё что я смог накопать по поводу LTSP. Напоследок хотелось бы сказать, что с такой подробностью я постарался расписать здесь всё лишь потому, что на русском языке информаии по технологии практически нет, хотя выглядит она довольно привлекательно. Чего только стоит моё удивление, когда после первой загрузки я услышал из терминала проброшенный и заработавший без всяких сношательств звук входа в KDE…

Пожалуй, точка.