29 ноября в офисе Яндекса в Питере я буду проводить научный семинар «DTrace — проверочная работа для вашего кода». Те, кто на YaC 2012 слушал мой доклад «Инфраструктура облачных вычислений на основе ядра Illumos» — да и многие другие — знают, что в Semonix я занимаюсь облачными технологиями, тесно связанными со SmartOS. На семинаре я расскажу, как с помощью технологии DTrace проводить глубокий анализ производительности и детально изучать работу приложения. Поиск по Хабру находит только одну статью про SmartOS, и чтобы на семинар пришло больше тех, кто уже знаком с ней, я решил заранее написать про установку SmartOS и использование Node.js на ней.
Для начала расскажу, для чего нужна SmartOS. Если коротко, её предназначение – быть хост-системой для виртуальных машин. Она часто используется как основа для публичных и частных облаков, например, облачных служб Joyent и MITAC. Службой Joyent пользуется LinkedIn: вся его мобильная серверная часть сделана на Node.js, который запущен в облаке Joyent. Мы подробно писали об этом в блоге компании Semonix в статьях о SmartOS и об облаках, основанных на illumos, а я рассказывал в докладе на YaC 2012, который уже упоминал.
Типичные случаи применения SmartOS – системы для ЦОДов, системы для разработки и эксплуатации нагруженного веб-приложения, платформа для создания приватного или публичного облака. При работе над этой статьей я активно использовал статью Сту Радниджа, который проделывал примерно такой же путь установки SmartOS, как и я, но с использованием VMware вместо VirtualBox.
Планируем тестовую установку
Допустим, вы оказались одним из тех, кому интересно попробовать SmartOS. Как это проще сделать? Я пошел по самому простому пути, который не даст запустить Windows или Linux в вирутальной машине, зато позволит воспользоваться готовым образом SmartOS + Node.js. Запускать буду SmartOS в VirtualBox. Понятно, что KVM в виртуалке под VirtualBox не заработает, а остальное – должно заработать на ура.
SmartOS может быть хост-системой для гостей под управлением Windows, Linux и самой SmartOS. В этой статье я рассматриваю только вариант с гостевыми системами под управлением SmartOS. В них реализуется легковесная виртуализация (то же самое, что зоны в Solaris и нечто похожее на клетки во FreeBSD), и, стало быть, для их работы не требуется KVM, который поддерживается только на физических компьютерах. Вся конфигурация показана на рисунке ниже. SmartOS (host) – это система, которая является гостем по отношению к Windows на моем ноутбуке, и хостом — по отношению к системам SmartOS, запущенным в зонах внутри этой SmartOS.

SmartOS будем ставить в виртуальную машину с двумя сетевыми интерфейсами. Зачем? Очень просто: я хочу из своей хост-системы (в данном случае – Windows) подсоединяться обычным ssh к SmartOS, чтобы можно было открыть несколько окон — хотя бы по одной штуке для каждой гостевой системы внутри моей SmartOS.
Почему бы не воспользоваться графическим режимом в консоли и не запустить там нужное количество xterm или gnome-terminal? Дело в том, что графика («иксы» — X) в SmartOS не установлена. Существует проект по переносу поддержки X11 в SmartOS, но пока он не завершен, и Джонатан Перкин (Jonathan Perkin), ведущий этот проект на общественных началах, сообщил мне, что срок окончания работы над ним неизвестен. Поэтому, если я хочу открыть пять окон с ssh к своему SmartOS, то понадобится доступ по TCP с моей хост-системы к SmartOS в VirtualBox.
Кроме этого, в гостевую машину SmartOS нельзя установить VirtualBox Guest Additions, позволяющие выполнять cut-and-paste между гостевой системой и хост-системой, в которой я пишу статью (и хочу в нее копировать тексты из терминала). Теоретически, я думаю, установить Guest Additions можно (там по сути нужно установить модули ядра), но для Solaris-подобных систем Guest Additions поставляется в виде пакета формата .pkg, который в SmartOS не поддерживается. Я решил, что мороки с распаковкой пакета и ручным прикручиванием модуля к ядру будет больше, чем с доступом по ssh из хост-системы.
Зачем же нужен второй интерфейс? Чтобы SmartOS мог через NAT выйти за пакетами в интернет. Это ограничение на условия задачи накладывает VirtualBox – сделать в нем доступную из хост-системы виртуалку с NAT/DHCP иначе можно только через bridged network, а это не всегда удобно. Обсуждение случаев, когда не очень удобно (или не поддерживается в определенных хост-системах или версиях VirtualBox), выходит за рамки этой статьи, но поверьте – такое бывает. Самые простые варианты: кончились DHCP-адреса в той сети, куда у вас подключен физический компьютер, или не хочется каждый раз смотреть, какой адрес в этот раз выдан системе перед тем, как к ней цепляться ssh.
Как сделать два сетевых интерфейса в VirtualBox
Настроить виртуальную машину так, чтобы в ней было 2 сетевых адаптера – элементарно: на закладке «Сеть» надо просто указать, что активен не только первый адаптер в режиме host adapter, но и второй — в режиме NAT (см. рисунок ниже). Обратите внимание, что для SmartOS имеет смысл выбрать тип виртуальной машины Solaris 10 10/09 or later (64 bit). Возможно, что вариант Solaris 11 подойдет лучше. У меня без проблем и задержек заработало и так.
Что еще надо настроить?
К CD-ROM виртуальной машины надо подсоединить файл latest.iso — из него будет грузиться SmartOS.

После загрузки в первый раз система задаст несколько вопросов про настройки (в частности, пароль root’a), отформатирует диск, создаст пул ZFS на диске и запишет настройки на диск. После перезагрузки вы должны увидеть нечто, подобное рисунку ниже.

Войдите как root, используя пароль, который вы задали раньше. Уже сейчас можно подсоединиться к SmartOS с помощью ssh, для этого только надо узнать, какой у сервера адрес:
ipadm show-addr
Как сделать два сетевых интерфейса в SmartOS
Конфигурация системы в SmartOS хранится в файле /usbkey/config. Файловая система /usbkey монтируется с диска, в отличие от /etc, которая всегда загружается с LiveCD или LiveUSB и монтируется на RAM-диск. Поэтому изменения, внесенные в файлы из /etc будут потеряны при перезагрузке, а в /usbkey – сохранятся.
Файл /usbkey/config создается при первом запуске SmartOS на компьютере, и по умолчанию он описывает только первый интерфейс в системе. Для того чтобы видел и второй, надо немного изменить файл. Для его модификации нам потребуется узнать MAC-адреса интерфейсов. Для этого можно использовать команду
dladm show-phys -m
LINK SLOT ADDRESS INUSE CLIENT
e1000g0 primary 8:0:27:f7:e9:a3 yes e1000g0
e1000g1 primary 8:0:27:d0:35:3b no --
Теперь изменим файл /usbkey/config, чтобы он выглядел вот так:
#
# This file was auto-generated and must be source-able by bash.
#
# admin_nic is the nic admin_ip will be connected to for headnode zones.
admin_nic=8:0:27:f7:e9:a3
admin_ip=dhcp
admin_netmask=
admin_network=...
admin_gateway=dhcp
external_nic=8:0:27:d0:35:3b
external_ip=dhcp
external_netmask=255.255.255.0
external_gateway=dhcp
dns_resolvers=8.8.8.8,8.8.4.4
dns_domain=
ntp_hosts=pool.ntp.org
compute_node_ntp_hosts=dhcp
Создание виртуальной машины внутри SmartOS
Вначале надо скачать образ виртуальной машины. Для этого надо обновить список образов:
imgadm update
А теперь посмотрим, какие готовые образы есть в репозитории:
imgadm avail
UUID OS PUBLISHED URN
78ab4d60-2610-11e2-b3f7-b3bd2c369427 linux 2012-11-04 sdc:jpc:ubuntu-12.04:2.1.2
6a67c702-2083-11e2-b4fa-03f9d1d64ef0 linux 2012-10-28 sdc:jpc:ubuntu-12.04:2.1.1
b00acc20-14ab-11e2-85ae-4f03a066e93e smartos 2012-10-12 sdc:sdc:mongodb:1.4.0
1fc068b0-13b0-11e2-9f4e-2f3f6a96d9bc smartos 2012-10-11 sdc:sdc:nodejs:1.4.0
8700b668-0da4-11e2-bde4-17221283a2f4 linux 2012-10-03 sdc:jpc:centos-6:1.3.0
55330ab4-066f-11e2-bd0f-434f2462fada smartos 2012-09-25 sdc:sdc:base:1.8.1
60a3b1fa-0674-11e2-abf5-cb82934a8e24 smartos 2012-09-25 sdc:sdc:base64:1.8.1
...
Теперь перед нами – список образов, доступных для установки. Здесь приведена только часть списка – он значительно длиннее. На момент написания статьи в репозитории были доступны 96 образов. Нас интересует образ base64 – минимальная система, включающая в себя node.js. В данный момент самая свежая версия этого образа — 1.8.1 (включает node 0.8.9), и его UUID — 60a3b1fa-0674-11e2-abf5-cb82934a8e24.
Скачаем образ виртуальной машины:
imgadm import 60a3b1fa-0674-11e2-abf5-cb82934a8e24
Теперь сделаем описание виртуальной машины, которую мы создадим на основе только что загруженного образа и запустим ее внутри хоста SmartOS.
Описание делается в виде файла JSON (подробности процесса описаны в документации на smartos.org, я здесь просто положу работающий пример – пусть у нас он лежит в /zones/template.json). Файл должен лежать в файловой системе, которая размещается на диске и сохраняется между перезагрузками, удобнее всего – в /zones.
Обратите внимание на dataset_uuid – это тот самый UUID, который принадлежит только что полученному из репозитория образу. В зоне тоже будет два сетевых интерфейса — «внутренний» и «внешний». Такая настройка используется в ЦОДах в реальной жизни: к внутреннему интерфейсу подключается внутренняя сеть, в которой работают администраторы ЦОДа, к внешнему — интернет, откуда приходят клиенты.
{
"brand": "joyent",
"dataset_uuid": "60a3b1fa-0674-11e2-abf5-cb82934a8e24",
"max_physical_memory": 1024,
"nics": [
{
"nic_tag": "admin",
"ip": "dhcp",
"gateway": "dhcp"
},
{
"nic_tag": "external",
"ip": "dhcp",
"gateway": "10.0.3.2"
}
]
}
Теперь создаем новую виртуальную машину SmartOS (для краткости будем дальше ее назвать «зона»):
vmadm create -f /zones/ template.json
Если все получилось нормально, можно увидеть запущенную новую зону, дав команду
vmadm list
UUID TYPE RAM STATE ALIAS
570cccb2-0511-400b-9143-7616b2ca8a3d OS 1024 running -
Теперь можно подключиться к новой зоне либо из консоли SmartOS, либо через ssh:
zlogin 570cccb2-0511-400b-9143-7616b2ca8a3d
Обратите внимание: UUID зоны отличается от UUID образа зоны, потому что из одного и того же образа можно создать много зон. Одновременно в одной хост-системе SmartOS может быть запущено до 8191 виртуальных машин.
Для подключения к зоне через ssh следует уточнить, какой адрес ей выдан, для этого надо вначале к ней присоединиться через zlogin, а затем узнать адрес:
zlogin 570cccb2-0511-400b-9143-7616b2ca8a3d
ipadm show-addr
Управление пакетами
Теперь можно установить любые нужные нам в зоне пакеты. Для этого надо обновить информацию о доступных пакетах с помощью pkgin update, получить их список pkgin list или искать требуемое с помощью pkgin search. Для установки пакета следует использовать pkgin install.
Как запустить Node.js
Предположим, вы не были знакомы с Node.js ранее. Тогда для начала можно сделать очень простое веб-приложение, выдающее банальное Hello, World! Освоение Node.js можно начать с чтения широко известного руководства. Следуя ему, создаем наш Hello, World.
Для этого соединяемся с зоной
zlogin 570cccb2-0511-400b-9143-7616b2ca8a3d
и создаем, например, в каталоге /home/node файл server.js с таким содержимым:
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(80);
Теперь запускаем node /home/node/server.js и обращаемся к нашей зоне из обычного браузера. В моем случае это потребовало настройки маршрутизации между зоной и хост-системой Windows:
В зоне: route add 192.168.56.1 192.168.56.101
В Windows: route add 192.168.56.102 192.168.56.101
Адрес 192.168.56.1 – это интерфейс VirtualBox в Windows, 192.168.56.101 – хост-система SmartOS, 192.168.56.102 – зона в ней.
Как использовать DTrace с Node.js?
Провайдер DTrace для node называется (сюрприз!) node. Так как это провайдер типа USDT, то его датчики надо указывать либо в виде node*:::<название датчика>, либо с указанием конкретного PID процесса node, например, node3297:::<название датчика>.
Живой пример использования DTrace для наблюдения за работой вашего сервера под Node.js — протоколирование обращений к нему. Чтобы узнать, какие запросы получают серверы из разных зон (типичный случай для
# dtrace -L /var/lib/dtrace -n 'node*:::http-server-request{printf("%s: %s of %sn",zonename, args[0]->method, args[0]->url)}' -q
570cccb2-0511-400b-9143-7616b2ca8a3d: GET of /
570cccb2-0511-400b-9143-7616b2ca8a3d: GET of /favicon.ico
570cccb2-0511-400b-9143-7616b2ca8a3d: GET of /
570cccb2-0511-400b-9143-7616b2ca8a3d: GET of /favicon.ico
570cccb2-0511-400b-9143-7616b2ca8a3d: GET of /
570cccb2-0511-400b-9143-7616b2ca8a3d: GET of /favicon.ico
Чтобы посмотреть, с каких адресов приходят запросы, можно слегка модифицировать скрипт:
# dtrace -L /var/lib/dtrace -n 'node*:::http-server-request{printf("%s: %s of %sn",args[1]->remoteAddress, args[0]->method, args[0]->url)}' -q
192.168.56.1: GET of /
192.168.56.1: GET of /
192.168.56.1: GET of /favicon.ico
192.168.56.1: GET of /
192.168.56.1: GET of /favicon.ico
192.168.56.1: GET of /
192.168.56.1: GET of /
Для node в DTrace доступны следующие датчики:
gc-start
gc-done
http-client-request
http-client-response
http-server-request
http-server-response
net-server-connection
net-socket-read
net-socket-write
net-stream-end
Если вы обнаруживаете, что какие-то датчики недоступны, это – верный признак того, что файла /usr/lib/dtrace/node.d у вас нет. Тогда надо создать каталог /var/lib/dtrace/, скачать туда файл (он лежит по адресу https://raw.github.com/joyent/node/master/src/node.d) и запускать dtrace с ключом –L /var/lib/dtrace/ (он означает «включить в путь поиска библиотек DTrace указанный после ключа L каталог).
Подробное описание датчиков с примерами кода (на английском) есть на странице dtrace.org/blogs/rm/2011/03/01/dtrace-probes-for-node-v0-4-x/#httpd (там, правда, речь идет об устаревшей версии Node.js).
Заключение
Особая прелесть настроек SmartOS в том, что единожды установив, их не надо повторять после выхода обновления. Можно скачать очередной latest.iso, заменить им прежний и загрузиться с нового. Все настройки, виртуальные машины и файлы внутри них будут подхвачены автоматически, так как в них никто и ничего не меняет — изменяется только загружаемый образ, а настройки существуют отдельно от него.
Тех, кому интересно послушать мой рассказ про DTrace, буду ждать 29 ноября в 19:00 в петербургском офисе Яндекса. Семинар совершенно бесплатный, но количество мест ограничено, поэтому нужно зарегистрироваться. Для тех, кто не попадёт на него, организаторы обеспечат онлайн-трансляцию. А тем, кто собирается, скажу, что нужно взять с собой ноутбуки с установленной SmartOS под VirtualBox, потому что важной частью семинара будет решение практических задач.
Встреча будет полезна всем, кто интересуется анализом производительности приложений в UNIX-подобных системах, особенно разработчикам ПО под Solaris, разработчикам ПО под Node.js, системным администраторам и другим ответственным за производительность (если они работают с системами FreeBSD, Solaris, QNX, OpenIndiana, SmartOS и другими, поддерживающими DTrace).
Автор: philipto