понедельник, 30 ноября 2009 г.

Реанимационная машина

Уже достаточно давно известно, что запустить Solaris в виртуальной машине на Windows полностью на ZFS можно.

Теперь - сюрприз.

Виртуальная машина может оказаться реанимационной.

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

Удивлены?

Я тоже был нешуточно удивлен.

"Удивился Бэтмен, удивилась Красная Шапочка - но больше всех удивился Человек-Невидимка."

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

И все бы ничего было - да вот не со всеми файловыми системами такое прокатывает.

Итак, я цитирую буквально пункт 11.1.3 Troubleshooting от Sun (!) VirtualBox:

If desired, the virtual disk images (VDI) can be flushed when the guest issues the IDE FLUSH CACHE command. Normally these requests are ignored for improved performance.

То есть, это нормально, что виртуальная система игнорирует запросы гостевой ОС на сброс кэша.

А теперь - как это себя ведет?

Плохо ведет.

Если гостевая система стоит на ZFS - конец ей может наступить буквально в любой момент. Проверено на кошечках.

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

Зададимся вопросом - почему?

Транзакционная семантика ZFS, COW, ARC и ZIL играют тут в комбинации злую роль. Нет, это не баг ZFS. Это баг в башке разработчиков систем виртуализации.

Объясняю.

ZFS в принципе не исходит из предположения, что может находиться не непосредственно на физическом устройстве.

Соответственно, когда происходит атомарная запись уберблока, группа транзакций отправляется командой на сброс на виртуальный диск, туда же отправляется команда на запись ZIL. Хост игнорирует эти команды, однако все их он игнорировать не может, поэтому метки ZFS, содержащие уберблоки, попадают в конечном итоге на диск - но при следующем чтении выясняется, что данные, реально содержащиеся на диске и то, что показывают уберблоки - не совпадают!

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

В трех случаях из четырех скруббинг восстанавливает данные. Если есть зеркала - перманентный ресильверинг почти всегда поддерживает пулы в полуживом состоянии. Самое интересное - команда zpool clear сбрасывает счетчик ошибок, если следом выполнить zpool scrub <пул>;zpool scrub -s <пул>;zpool status -v - ошибки исчезают! Ненадолго. Любая последующая обширная операция записи - и все повторяется.

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

Окей, читаем мануал VirtualBox дальше:

11.1.3. Responding to guest IDE flush requests

If desired, the virtual disk images (VDI) can be flushed when the guest issues the IDE FLUSH CACHE command. Normally these requests are ignored for improved performance. To enable flushing, issue the following command:

VBoxManage setextradata VMNAME
"VBoxInternal/Devices/piix3ide/0/LUN#[x]/Config/IgnoreFlush" 0

The value [x] that selects the disk is 0 for the master device on the first channel, 1 for the slave device on the first channel, 2 for the master device on the second channel or 3 for the master device on the second channel. Only disks support this configuration option. It must not be set for CD-ROM drives.

Note that this doesn't affect the flushes performed according to the configuration described in 11.1.2. Restoring the default of ignoring flush commands is possible by setting the value to 1 or by removing the key.

Оказывается, нужно всего-то установить два внутренних параметра.

Что они означают? Они модифицируют установки драйвера виртуального контроллера IDE (нативного для VirtualBox) в режим, запрещающий игнорирование запросов IDE FLUSH.

То есть, теперь все должно быть в ажуре - правда, производительность немного упадет (а может, и не немного - но нам-то пофигу, разрушение пулов гораздо хуже!), но записи будут выполняться как ожидается.

Серия экспериментов показала, что данный параметр работает лишь для контроллеров PIIX3. Поддерживаемые VirtualBox контроллеры PIIX4, ICH6, SATA и SCSI - игнорируют данную установку и ZFS на них разлетается в клочья в любой момент. (Ну, может, я не знаю правильного синтаксиса команд для них - но ни одна попытка не была успешной. Я очень хотел оставить Солярис на виртуальных SCSI-дисках ;). Самое неприятное - нигде на форуме VirtualBox я не нашел даже намека на данный issue.).

Итак, установка параметров для всех виртуальных машин на ZFS проблему решила:

VBoxManage.exe getextradata pegasus enumerate

VBoxManage setextradata pegasus "VBoxInternal/Devices/piix3ide/0/LUN#0/Config/IgnoreFlush" 0
VBoxManage setextradata pegasus "VBoxInternal/Devices/piix3ide/0/LUN#3/Config/IgnoreFlush" 0

VBoxManage setextradata vader "VBoxInternal/Devices/piix3ide/0/LUN#0/Config/IgnoreFlush" 0

Причем проблема была решена полностью, раз и навсегда.

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

Все ли виртуализационные среды имеют такую неожиданную и милую особенность?

Я не тестировал их все без исключения - это не было и не будет моей целью - но предполагаю, что таким поведенческим свойством не обладают гипервизоры (по определению) и Solaris Containers (Zones). Сильно подозреваю, что чуть больше половины сред виртуализации имеют такую же бомбу под желудком у гостевых ОС.

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

Кстати, когда происходят непрерывные попытки восстановления - загрузка процессоров возрастает феерически. Идут непрерывные операции ввода-вывода, гостевая система (вместе с хостом) начинают резко тормозить. Иногда доходит до автоматического старта ресильверинга.

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

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

А еще лучше - откажитесь от использования виртуальных сред. Право же, откажитесь.

Применение их без крайней на то необходимости - действительно крайней! - дурной тон.

"- Ты еще винограду сверху положи! - сказала Гелла, пихая Бегемота локтем".

среда, 25 ноября 2009 г.

RAM-диски в Solaris, часть III

Все же как много воды утекло с момента написания первой части статьи. Оказывается, еще в далеком 2008м году писалась. ;)

Уже две версии SMF-сервиса RAM-дисков написано:

Версия 1 - простенькая, для подъема всего одного RAM-диска, с конфигурированием статическим, в заголовке управляющего метода.
В ней, правда, был один маленький глюк с автобэкапом, который не далее, как вчера был-таки исправлен ;)

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

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

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

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

2. Вторая версия сервиса использует для конфигурирования дисков файл /etc/ram.conf, создаваемый при установке сервиса. Для каждого диска можно (и нужно) задавать раздельные уникальные свойства - это касается логов, точек монтирования и имен. При этом нужно помнить, что конфигурационный файл используется и при удалении сервиса (с последующим удалением самого конфигурационного файла).

3. Точки монтирования RAM-дисков сохраняются при остановке сервиса. Во второй версии они будут удалены лишь при деинсталляции сервиса. В первой версии необходимо удалять их вручную.

среда, 18 ноября 2009 г.

Shared-сервер и large_pool_size

Когда мы строим масштабируемое решение на основе Oracle Shared Server, не следует забывать о правильном выборе large_pool_size.

Если вы не хотите получить проблем - минимальной из которых будет неожиданное снижение скорости отклика БД при резком увеличении нагрузки (даже при использовании connection pooling), а максимальной - падение экземпляра с ошибкой 7445 (даже при использовании ASMM) - необходимо устанавливать достаточно большое значение вышеупомянутого параметра.

Окей, каковы оценки и как можно определить потребное количество памяти либо как промониторить использование large pool?

Достаточно приличной начальной цифрой будет следующее соотношение: примерно 30 Мб large pool на каждые 100 коннектов к БД.

Однако, при использовании shared server позади веб-сервера этого может оказаться недостаточно. Средняя цифра shared_pool_size для относительно некрупного веб-сервера с ожидаемой нагрузкой 700 веб-сессий (при настройках shared-сервера на прием до 2000 одновременных сессий) составляет свыше 64 Мбайт.

Насколько выше?

Попробуем взглянуть.

SQL> show parameter large_pool_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
large_pool_size big integer 100M

SQL> select sum(value) || ' Bytes' "Total memory for all sessions"
from v$sesstat, v$statname
where name = 'session uga memory'
and v$sesstat.statistic# = v$statname.statistic#; 2 3 4

Total memory for all sessions
----------------------------------------------
16537248 Bytes

16 мегабайт при не очень высокой установившейся нагрузке.

Сколько памяти UGA за период задействовалось в максимуме?

SQL> select sum(value) || ' Bytes' "Max memory for all sessions"
from v$sesstat, v$statname
where name = 'session uga memory max'
and v$sesstat.statistic# = v$statname.statistic#; 2 3 4

Max memory for all sessions
----------------------------------------------
22971128 Bytes

22 мегабайта.

Величина кажется не очень большой и становится непонятно, зачем мы выделили 100 мегабайт large pool?

Однако не следует забывать, что у нас включен пулинг соединений:

SQL> show parameters dispatchers

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
dispatchers string (ADDRESS=(PROTOCOL=tcp)(POOL=o
n)(TICK=1)(CONNECTIONS=100)(SE
SSIONS=1000)(SERVICE=SUN11_XPT
))(DISPATCHERS=2)
max_dispatchers integer 5

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

Вывод из всего вышесказанного следующий.

Относительно маленький large pool бывает лишь в небольших базах. В масштабируемых конфигурациях большой пул оправдывает свое название и должен становиться действительно большим.CVYFV92SG7U4

понедельник, 16 ноября 2009 г.

Высокопроизводительный shared-сервер

Я уже писал ранее о том, что сконфигурировать shared-сервер в Oracle сравнительно просто. Однако я не упомянул о том, каким образом сконфигурировать по-настоящему высокопроизводительный и масштабируемый shared-сервер.

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

Как это сделать?

Правило 1. Узким местом номер 1 в shared-серверах является, прежде всего, недостаточный размер large pool (задаваемого параметром large_pool_size).

Для сервера с нагрузочной способностью примерно в 2000 сессий хорошим стартовым отсчетом для большого пула будет величина 64 Мб (желательно использование ASMM и желательно явно задать минимальную величину large_pool_size в указанное значение.).

Правило 2. Весьма желательно иметь более одного запущенного диспетчера (особенно при резких скачках нагрузки). В зависимости от конфигурационных параметров, средняя нагрузочная способность одного диспетчера может составлять 500-1500 входящих сессий. При этом, весьма удобно, имея несколько сервисов БД, слушать их все на порту 1521.

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

Правило 3. Для повышения нагрузочной способности (и для защиты от DDoS) весьма желательно использовать как минимум connection pooling с правильно выбранной величиной сетевых таймаутов (tick). В особо тяжелых случаях connection multiplexing спасет гигантов мысли. ;)

Важно - включение connection pooling при недостаточном размере large pool может привести к падению сначала сессий, а затем и экземпляра с ошибками 7445 и 600.

Правильно настроенный shared-сервер (с учетом данных рекомендаций) не только становится устойчивым к резким всплескам нагрузки из Web-сервера приложений, но и позволяет зачастую существенно улучшить время отклика веб-приложений.

Как это выглядит на практике?

Изменим описанную здесь конфигурацию:

SQL> alter system set dispatchers='' scope=both;

SQL> alter system set dispatchers='(ADDRESS=(PROTOCOL=tcp)(POOL=on)(TICK=1)(CONNECTIONS=100)(SESSIONS=1000)(SERVICE=SUN10_XPT))(DISPATCHERS=2)' scope=both;

Обратите внимание, что время таймаута сессии до пулинга выбрано 1 сек, что почти идеально описывает веб-активность - "долго думаю, редко выполняю запросы". В вашем случае время таймаута можно увеличить, однако, тогда емкость БД по сессиям будет существенно снижена а стойкость к стрессам упадет.

Модифицируем клиентские настройки tnsnames.ora для БД и OHS:

SUN10 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = SUN10.host.com)
(SERVER=dedicated)
)
)

SUN10_XPT =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = SUN10_XPT.host.com)
(SERVER=shared)
)
)

Изменим настройки mod_plsql в dads.conf:

#PlsqlDatabaseConnectString localhost:1521:SUN10.host.com ServiceNameFormat
#PlsqlDatabaseConnectString localhost:16384:SUN10_XPT.host.com ServiceNameFormat
PlsqlDatabaseConnectString SUN10_XPT TNSFormat

listener.ora оставляем без изменений:

SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = SUN10.host.com)
(SID_NAME = SUN11)
(ORACLE_HOME = /export/home/OraHome1/app/oracle/product/10.2.0)
)
(SID_DESC =
(GLOBAL_DBNAME = SUN10_XPT.host.com)
(SID_NAME = SUN11)
(ORACLE_HOME = /export/home/OraHome1/app/oracle/product/10.2.0)
)
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = /export/home/OraHome1/app/oracle/product/10.2.0)
(PROGRAM = extproc)
)
)

ADMIN_RESTRICTIONS_LISTENER = ON

LOGGING_LISTENER = ON

LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = www.host.com)(PORT = 1521)(QUEUESIZE = 10)(SEND_BUF_SIZE = 65536)(RECV_BUF_SIZE = 65536))
)
)
)

Собственно говоря, готово. Осталось перезапустить сервисы Oracle и вуаля!

PS. Не следует забывать о мониторинге и, в некоторых случаях, тюнинге shared-сервера. Представления v$circuit, v$dispatcher будут в этом случае весьма полезными, так же, как и команда lsnrctl services.

понедельник, 2 ноября 2009 г.

ZFS в Windows

Не очень давно я наткнулся на одно совершенно феерическое заявление в нете. Дескать, не работает ZFS в виртуальных машинах на Windows. Обратите внимание, кто заявляет - яйцеголовые мальчуганы! - и в каких выражениях. "Вероятно", "по-видимому".

Видно, что мальчики сами не пробовали и полагаются на слова двоюродного брата одного ковбоя в Матаморасе.

Я тем более сильно удивлен, что "У меня все работает!" :)))))))

Как я и писал раньше, я давно поставил на Win2k3 VirtualBox, установил в него Solaris 10 на ZFS (полностью) и странным образом удивляюсь, как это у меня все до сих пор работало, когда работать-то не должно ни фига! :) Пруфпик в предыдущей статье. Официально подтверждаю, что все стоит на ZFS и могу представить в подтверждение ноутбук для инспекции. ;)

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

Маленькое пояснение.

Когда на файловой системе нужно изобразить raw-девайс, он создается файлом. При этом raw-девайс может содержать абсолютно все, что угодно. ОС это не лимитирует.

Виртуальный диск виртуальной машины - это файл. Его потрошки совершенно абстрагированы от родительской ОС. Файловая система, которая в нем находится, совершенно оторвана от API родительской системы. Ergo - как может ZFS не работать поверх файла на NTFS?

Однако я совсем по другому поводу хотел высказаться. Предыдущие экзерсисы просто к слову пришлись.

Предупреждение желающим воспроизвести мой практический опыт с ZFS на Windows.

Самая большая проблема с ZFS в виртуалбоксе - это виртуальные диски расширяющегося размера.

"Дело в следующем, папаня."

Дело в структуре vdev ZFS.

Vdev ZFS имеет четыре метки, две в начале устройства и две в конце. Обновляемые двухстадийным транзакционным механизмом, причем write-on-copy не действует для меток (в отличие от всех прочих структур ZFS). Соответственно, каждый раз, когда виртуализационный софт увеличивает размер виртуального диска, две метки в конце (а это в аккурат 512 Кбайт) оказываются посреди блоков данных. Контрольные суммы летят к чертям, пул сыпется до такой степени, что даже зеркалирование не помогает.

"Короче, все умерли."

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

Создавайте пулы на виртуальных дисках, заранее созданных и фиксированного размера. Мало станет - страйпами увеличите, добавив новые файлы VDI. Фиксированного же размера.

Компрене?

PS. Огромный недостаток ZFS - нужно учитывать особенности ее устройства при работе в нестандартных энвайронментах. Правда, сюрприз? Такой же, как легендарное ограничение UNIX - нельзя, понимаешь, выполнять команды из командной строки, когда размер команды превышает 2 Гб!