Завершился саммит разработчиков Ubuntu Linux, на котором были приняты решения, касающиеся подготовки релиза Ubuntu 12.04 LTS и рассмотрены некоторые предложения по дальнейшему развитию проекта. Ниже представлена подборка интересных обсуждений. Читать
Архив рубрики: Linux
Использование виртуальных машин для автоматического тестировани
В системном программирования достаточно часто возникает ситуация, когда значительная часть функциональности программы перекладывается на внешние компоненты. Типичный пример — операции с iptables, дисковыми образами и виртуальными машинами. Классически для тестирования такого кода используются массовые моки, отрезающие тестируемый код от всех внешних зависимостей.
При очевидных достоинствах (полная независимость тестов от внешнего мира, скорость исполнения, etc) у моков есть некоторое количество недостатков — самый главный это переход от тестирования того что должно быть сделано к тестирования того как это сделано. Если нужно проверить функцию, которая настраивает проброс порта, то вместо тестирования результата (правильного прохождения пакетов) проверяется, что iptables вызвалась с правильными параметрами.
По итогу юнит тест проверяет не правильность работы кода, а является отражением его структуры. Такой тест помогает обеспечить постоянную проверку на отсутствие AttributeError и ему подобных (python), но на этом его полезность оканчивается. Учитывая желание менеджера и/или заказчика получить заветные X% покрытия ситуация становится совсем идиотской. Несколько последних проектов, в которых я учавствовал были именно такие — тонкая прослойка из python, связывающая вместе БД, REST, xen, iptables и еще горстку linux утилит в небольшой специализированный клауд. По итогу заметная часть UT требует переписывания после каждого рефакторинга, потому как изменилось взаимодействие с внешними компонентами. То что должно поощрять рефакторинг и улучшение кода становится одним из главных его тормозов.
Частично эта ситуация отражает объективный факт — мы не можем позволить юнит-тестам бесконтрольтно модифицировать файловую систему на локальной машине, изменять правила прохождения пакетов или ip маршруты. Дополнительный минус — рабочая машина разработчика не всегда соответствует требованиям к конечному серверу.
Решение совершенно очевидное — использовать для тестов виртуальные машины и проводить тесты на необходимой конфигурации + исключить бОльшую часть моков из тестов.
Итого — что хотелось получить:
- исполнять отдельные юнит-тесты на виртуальных машинах или группах машин
- интеграция с nosetests и coverage
- максимально простое использование
- высокая скорость — юнит-тесты должны исполняться быстро
Как хотелось это использовать:
@on_vm('worker-1')
def test_iptables():
make_iptables_rules()
check_packages_goes_ok()
@on_vm(‘worker-2’)
def test_something():
make_something()
check_something_works()
@on_vm('worker-1')
def test_iptables():
make_iptables_rules()
check_packages_goes_ok()
@on_vm(‘worker-2’)
def test_something():
make_something()
check_something_works()
Доводить идею до рабочего варианта в рамках внутреннего проекта взялись интерны нашей компании — Игорь Гарагатый и Настя Криштопа.
Для начала было решено реализовать достаточно простой вариант: перед исполнением каждого теста, требующего виртуальную машину, запускалась соответствующая vm, на нее копировался код и тесты, запускались тесты и их результаты тестов возвращались назад на хост машину. Если тест выбросит исключение оно должно передаваться назад на хост и выбрасываться из локального теста — nose не должен замечать разницы между локальным и удаленным исполнением теста.
В итоге были выбраны два варианта — LXC и KVM. LXC позволяет запустить виртуальную машину менее чем за секунду и не требует аппаратной поддержки виртуализации, а KVM это более надежный вариант, позволяющий запускать виртуальные машины любых конфигураций (LXC использует ядро хост системы, поэтому поднять в нем другую версию ядра или другую OS невозможно).
Хотелось иметь в vm файловую систему доступную для записи, но возвращаемую в начальное состояние после окончания теста. Для kvm это естественным образом решается возможностями qcow2, который позволяет сохранять все изменения в отдельный файл, не трогая оригинальный образ. Для LXC же нужна была файловая система с поддержкой снимков и быстрым откатом к ним. После рассмотрения btrfs, LVM+XFS и aufs решили остановиться на первом варианте.
Что в итоге получилось:
- Пользователь подготавливает образы и конфигурации виртуальных машин, которые будут использоваться для UT
- Оборачивает отдельные тесты декоратором on_vm с указанием на какой конфигурации его запускать
- nosetests unitTests
- profit (итоги тестов и coverage)
Примерная схема работы:
- Декоратор on_vm создает отдельный процесс, для поднятия ВМ и запускает поток, слушающий результаты на сокете
- test_executor.py создает с помощью libvirt необходимую конфигурацию vm, предварительно сделав слепок btrfs или подключив qcow2 файл для сохранения изменений (в зависимости от типа виртуальной машины)
- test_executor.py дожидается окончания запуска vm, копирует туда необходимые файлы и запускает только выбранные тест на исполнение, предварительно выставив переменные окружения
- on_vm по переменным окружения определяет, что это реальных запуск и исполняет тест
- при возникновении ошибки она сериализуется и передается на хост
- итоги теста передаются на хост вместе с результатами покрытия
- процесс на хосте принимает результаты, гасит vm, откатывает состояние образа и имитирует локальное исполнение теста.
На текущий момент результат пока в состоянии альфа готовности, еще много чего хотелось бы добавить (иммитацию правильного времени исполнения, повторное использование уже запущенных vm, поднятие групп vm с определенными сетевыми настройками), но текущая реализация уже готова для проб. Код можно найти тут vm_ut.
Ссылки:
github.com/koder-ua/vm_ut
koder-ua.blogspot.com/2012/01/lxc.html
mirantis.com
github.com/ogirOK
github.com/anakriya
pypi.python.org/pypi/coverage
readthedocs.org/docs/nose/en/latest
Исходники этого и других постов со скриптами лежат тут — github.com/koder-ua. При использовании их, пожалуйста, ссылайтесь на koder-ua.blogspot.com.
Автор: konstantin danilov
Запуск процессов в linux с ограничением ресурсов
Иногда хочется ограничить максимальное количество ресурсов доступных процессу. Последней пинком стали юнит-тесты из текущего проекта — из-за ошибок они несколько раз съели все 8Gb ОЗУ и отправили систему в глубокий своп, откуда она со скрипом доставалась минут 15. Полная виртуализация таких случаях избыточна — нужно что-то по легче. В linux для этого есть cgroups (control groups) — они позволяют поместить процесс (вместе со всеми его потомками) в контейнер, имеющий доступ только к части ресурсов системы. На самом деле cgroups умеют больше, чем просто ограничение ресурсов — тут и счетчики производительности и другая статистика.
cgroups можно манипулировать вручную или с помощью libcgroup. Последний способ значительно удобнее и по нему есть отличная документация от redhat. Она обязательна к прочтению — есть несколько не очевидных моментов (для пользователей ubuntu — в этом дистрибутиве по умолчанию cgroups монтируются в /sys/fs/cgroups).
Краткий пересказ документации на примере ubuntu/libcgroup.
сgroups основываются на контроллерах и иерархии групп. Процессы входят в группы, группы организовываются в деревья, где подгруппы получают часть ресурсов родительских групп, а контроллеры управляют ресурсами. Контроллер может быть привязан или только к одной иерархии или к нескольким если он в каждой единственный контроллер, а каждый процесс может быть привязан только к одной группе в каждой иерархии, но может быть привязан к другим группам в других иерархиях. Деревья групп отображаются на файловую систему и все работа с ними идет через специальные файлы.
Основные контроллеры позволяют управлять привязкой процессов к ядрам, ограничивать максимальную долю процессорного времени, объем используемой памяти и свопа и использование пропускной способности сети, блочных устройств и другое.
# apt-get install cgroup-bin
# apt-get install cgroup-bin
После установки в системе появится сервис cgconfig, который автоматически создает иерархию групп и контроллеров по описанию из файла /etc/cgconfig.cfg. После правки файла сервис необходимо перезапустить. Иногда он не может корректно перезапуститься из-за не отмонтированных при останове контроллеров. Лечится это ручным отмонтированием контроллеров:
$ sudo service cgconfig restart
[sudo] password for koder:
stop: Job failed while stopping
$ lscgroups
cpuset:/
cpuset:/sysdefault
$ rm -rf /sys/fs/cgroup/cpuset/sysdefault
$ umount /sys/fs/cgroup/cpuset
$ umount /sys/fs/cgroup
# # теперь можно перезапускать
$ sudo service cgconfig restart
[sudo] password for koder:
stop: Job failed while stopping
$ lscgroups
cpuset:/
cpuset:/sysdefault
$ rm -rf /sys/fs/cgroup/cpuset/sysdefault
$ umount /sys/fs/cgroup/cpuset
$ umount /sys/fs/cgroup
# # теперь можно перезапускать
Второй интересный сервис — cgred. Он умеет автоматически распределять процессы по группам в зависимости от правил, описанных в /etc/cgrules.conf. По умолчанию не запущен.
После установки cgroup-bin в /sys/fs/cgroup появится группы из стартового конфига:
$ ls /sys/fs/cgroup
cpu cpuacct device
s freezer memory
$ ls /sys/fs/cgroup
cpu cpuacct devices freezer memory
К каждой иерархии привязан один контроллер с соответствующим именем. Что-бы поместаить процесс под управление одного из контроллеров нужно выполнить 3 шага:
- Сделать группу. sudo mkdir /sys/fs/cgroup/memory/test создаст группу test под управлением контроллера memory. В папке /sys/fs/cgroup/memory/test тут же появятся специальные файлы для управления и мониторинга группы.
- Настроить контроллер. Некоторые контроллеры готовы к работе сразу, а некотрые требуют предварительной настройки. Правила настройки контроллеров не очень очевидны и описаны в документации. Для memory минимально необходимо записать ограничение в файл limit_in_bytes.
$ sudo echo 4M > /sys/fs/cgroup/memory/test/limit_in_bytes
$ sudo echo 4M > /sys/fs/cgroup/memory/test/limit_in_bytes
Теперь процессы в группе могут использовать не больше 4M ОЗУ на всех.
- Перенести процесс в группу — для этого нужно записать его pid в файл /sys/fs/cgroup/memory/test/tasks.
Когда группа станет не нужна ее можно удалить через rm -rf. Что-бы создать подгруппу нужно сделать новую папку в папке группы.
cgroups-bin предлагает чуть более удобный интерфейс для управления группами:
# cgcreate -g memory:/test # создать группу tt под управлением контроллера memory
# lscgroup memory:/
memory:///
memory:///test
memory:///sysdefault
# cgset -r memory.limit_in_bytes=4M memory:test # устанавливаем limit
# cgexec -g memory:test python -c "range(10000000000)" # пытаемся сделать всем плохо
Killed
# cgdelete memory:test # удалить группу
# lscgroup memory:/
memory:///
memory:///sysdefault
# cgcreate -g memory:/test # создать группу tt под управлением контроллера memory
# lscgroup memory:/
memory:///
memory:///test
memory:///sysdefault
# cgset -r memory.limit_in_bytes=4M memory:test # устанавливаем limit
# cgexec -g memory:test python -c "range(10000000000)" # пытаемся сделать всем плохо
Killed
# cgdelete memory:test # удалить группу
# lscgroup memory:/
memory:///
memory:///sysdefault
Чтобы не делать это каждый раз руками можно настроить cgred или написать автоматизирующий скрипт; я сделал себе такой:
#!/bin/bash
#set -x
USAGE="Usage: `basename $0` [-h] [-m MEM_LIMIT_IN_MEGS] [-c CPUS] [-i IOLIMIT] [-u USER] CMD"
cgroup_mount=/sys/fs/cgroup
GROUP=app_cell_1
while [ true ] ; do
OK=1
for cgroup in `lscgroup | awk -F: '{print $1}' | uniq`; do
if [ -d $cgroup_mount/$cgr
oup/$GROUP ] ; then
OK=0
break
fi
done
if (( $OK==1 )) ; then
break
fi
GROUP=app_cell_$RANDOM
done
MEMLIMIT=
CPUS=
USER=
IOLIMIT=
while getopts hm:c:i:u: OPT; do
case "$OPT" in
h)
echo $USAGE
exit 0
;;
c)
CPUS=$OPTARG
echo "CPUS = $CPUS"
;;
i)
IOLIMIT=$OPTARG
;;
m)
MEMLIMIT=$OPTARG
;;
u)
USER=$OPTARG
;;
?)
echo $USAGE >&2
exit 1
;;
esac
done
shift $(($OPTIND - 1))
if [ $# -eq 0 ]; then
echo $USAGE >&2
exit 1
fi
CMD=$@
#cgdelete memory:/$GROUP 2>/dev/null
#cgdelete cpuset:/$GROUP 2>/dev/null
#cgdelete blkio:/$GROUP 2>/dev/null
CGEXEC_OPT=
LIMITS=0
if [ -n "$MEMLIMIT" ] ; then
LIMITS=1
cgcreate -g memory:/$GROUP
cgset -r memory.limit_in_bytes=$MEMLIMIT $GROUP
cgset -r memory.memsw.limit_in_bytes=$MEMLIMIT $GROUP
CGEXEC_OPT="$CGEXEC_OPT -g memory:$GROUP"
fi
if [ -n "$CPUS" ] ; then
LIMITS=1
cgcreate -g cpuset:/$GROUP
cgset -r cpuset.cpus=$CPUS $GROUP
cgset -r cpuset.mems=0 $GROUP
CGEXEC_OPT="$CGEXEC_OPT -g cpuset:$GROUP"
fi
if [ -n "$IOLIMIT" ] ; then
echo "IO limits not supported yet" >&2
fi
if (( $LIMITS==0 )) ; then
echo "At least one limit should be set" >&2
echo $USAGE >&2
exit 1
fi
if [ -e "$USER" ] ; then
cgexec $CGEXEC_OPT $CMD
else
cgexec $CGEXEC_OPT su "$USER" -c "$CMD"
fi
#!/bin/bash
#set -x
USAGE="Usage: `basename $0` [-h] [-m MEM_LIMIT_IN_MEGS] [-c CPUS] [-i IOLIMIT] [-u USER] CMD"
cgroup_mount=/sys/fs/cgroup
GROUP=app_cell_1
while [ true ] ; do
OK=1
for cgroup in `lscgroup | awk -F: '{print $1}' | uniq`; do
if [ -d $cgroup_mount/$cgroup/$GROUP ] ; then
OK=0
break
fi
done
if (( $OK==1 )) ; then
break
fi
GROUP=app_cell_$RANDOM
done
MEMLIMIT=
CPUS=
USER=
IOLIMIT=
while getopts hm:c:i:u: OPT; do
case "$OPT" in
h)
echo $USAGE
exit 0
;;
c)
CPUS=$OPTARG
echo "CPUS = $CPUS"
;;
i)
IOLIMIT=$OPTARG
;;
m)
MEMLIMIT=$OPTARG
;;
u)
USER=$OPTARG
;;
?)
echo $USAGE >&2
exit 1
;;
esac
done
shift $(($OPTIND - 1))
if [ $# -eq 0 ]; then
echo $USAGE >&2
exit 1
fi
CMD=$@
#cgdelete memory:/$GROUP 2>/dev/null
#cgdelete cpuset:/$GROUP 2>/dev/null
#cgdelete blkio:/$GROUP 2>/dev/null
CGEXEC_OPT=
LIMITS=0
if [ -n "$MEMLIMIT" ] ; then
LIMITS=1
cgcreate -g memory:/$GROUP
cgset -r memory.limit_in_bytes=$MEMLIMIT $GROUP
cgset -r memory.memsw.limit_in_bytes=$MEMLIMIT $GROUP
CGEXEC_OPT="$CGEXEC_OPT -g memory:$GROUP"
fi
if [ -n "$CPUS" ] ; then
LIMITS=1
cgcreate -g cpuset:/$GROUP
cgset -r cpuset.cpus=$CPUS $GROUP
cgset -r cpuset.mems=0 $GROUP
CGEXEC_OPT="$CGEXEC_OPT -g cpuset:$GROUP"
fi
if [ -n "$IOLIMIT" ] ; then
echo "IO limits not supported yet" >&2
fi
if (( $LIMITS==0 )) ; then
echo "At least one limit should be set" >&2
echo $USAGE >&2
exit 1
fi
if [ -e "$USER" ] ; then
cgexec $CGEXEC_OPT $CMD
else
cgexec $CGEXEC_OPT su "$USER" -c "$CMD"
fi
Используется так:
$ sudo ./incontainer -u koder -m 1G -c 0-1 nosetests
# #nosetests ограничен 1 гигабайтом ОЗУ и только двумя ядрами.
$ sudo ./incontainer -u koder -m 1G -c 0-1 nosetests
# #nosetests ограничен 1 гигабайтом ОЗУ и только двумя ядрами.
Скрипт каждый раз создает новую группу вида app_cell_случайное_число, которые нужно периодически удалять, когда в них не останется ни одного процесса. Например, так:
#!/bin/bash
cgroup_mount=/sys/fs/cgroup
for cgroup in `lscgroup | awk -F: '{print $1}' | uniq` ; do
for group in `ls -1d $cgroup_mount/$cgroup/app_cell_* 2>/dev/null` ; do
#group=$cgroup_mount/$cgroup/$_group
if [ -d $group ] ; then
TC=`cat $group/tasks | wc -l`
if (( $TC==0 )) ; then
gname=$cgroup:/`basename $group`
echo "Group $gname is empty - clear it"
cgdelete -r $gname
fi
fi
done
done
#!/bin/bash
cgroup_mount=/sys/fs/cgroup
for cgroup in `lscgroup | awk -F: '{print $1}' | uniq` ; do
for group in `ls -1d $cgroup_mount/$cgroup/app_cell_* 2>/dev/null` ; do
#group=$cgroup_mount/$cgroup/$_group
if [ -d $group ] ; then
TC=`cat $group/tasks | wc -l`
if (( $TC==0 )) ; then
gname=$cgroup:/`basename $group`
echo "Group $gname is empty - clear it"
cgdelete -r $gname
fi
fi
done
done
P.S. Ковыряясь в cgroup наткнулся на очень интересный системный вызов
prctl(PR_SET_SECCOMP, 0, 0, 0, 0)
prctl(PR_SET_SECCOMP, 0, 0, 0, 0)
После него текущий процесс не может делать никакие системные вызовы, кроме записи/чтения в уже открытые файлы, _exit и sigreturn. Появился в 2.6.33, вместе с cgroups отличная идея для интерпретации/валидации потенциально опасных данных, например для реализации интерпретаторов, получающих скрипты из не доверенных источников.
Ссылки:
www.mjmwired.net/kernel/Documentation/cgroups.txt
libcg.sourceforge.net
docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Resource_Management_Guide
www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html
Исходники этого и других постов со скриптами лежат тут — github.com/koder-ua. При использовании их, пожалуйста, ссылайтесь на koder-ua.blogspot.com.
Автор: konstantin danilov
Видео уроки для начинающих пользователей Linux Ubuntu
Видеоуроки для начинающих пользователей Linux.
0. Подготовка к установке Ubuntu 10.04 (130 Мб)
1. Настройка Bios (30,2 Мб)
2. Установка Linux Ubuntu 10.04 (108 Мб)
3. Файловый менеджер в Linux Ubuntu 10.04 (65,6 Мб)
4. Работа с файловой системой в командной строке (14,6 Мб)
5. Установка драйвера видеокарты NVIDIA (117 Мб)
6. Обзор графической среды Gnome (80,6 Мб)
7. Настройка внешнего вида графической среды Gnome (67,7 Мб)
8. Установка программ в Linux Ubuntu 10.04 (94 Мб)
Читать
Релиз Google Earth 6.2
Google Earth 6.2 – программа содержащая земной глобус, карты и различную географическую информацию, которую можно найти путешествуя по виртуальному миру, смотря картинки, трёхмерные здания, карты..
Релиз Google Earth 6.2 вышел на днях и включает множество различных нововведений. Наиболее важным улучшением стало устранение эффекта «стёганного одеяла», которое было вызвано тем, что фотографии делались при различных погодных условиях. Для устранения этого недостатка был применён новый механизм обработки изображения, результат работы которого можно посмотреть на примере скриншота ниже:
Другими изменением стало улучшение работы поиска, который теперь поддерживает автодополнение, а также поиск по слоям, велосипедным, транспортным и пешеходным маршрутам.
Установка Google Earth 6.2 в Ubuntu
Для установки Google Earth 6.2 потребуется скачать .deb пакет со страницы проекта, предварительно согласившись с лицензией.
Фикс проблем со шрифтом
Для того чтобы исправить проблему с некорректным отображением шрифта потребуется установить пакет «msttcorefonts».
sudo apt-get install msttcorefonts
Автор: ГАЗЕНВАГЕН™
Вышел релиз Unity 5.2
Вслед за выходом второй альфа Ubuntu 12.04 Precise Pangolin была представлена новая версия Unity 5.2, которая включает многие из уже рассмотренных характеристик, такие как: подсказку с выводом горячих клавиш, новую домашнюю линзу и переключатель Super+Tab для приложений на панели Unity.
Среди других изменений стоит отметить улучшенную поддержку многомониторных конфигурацией, переключение приложений (Alt-Tab) в пределах одного рабочего стола, отключение масштабирования окон программ на экранах с разрешением выше 1024×600 и доработку линз приложений и музыки.
Одним из наиболее важных изменений стало улучшение работы с несколькими мониторами. Теперь панель Unity запускается на каждом из подключенных мониторов, что позволяет избавиться от необходимости перехода на первый экран для запуска требуемых программ.
Также был улучшен интерфейс линз, в частности музыкальная линза получила трёхколоночный стиль оформления фильтра.
У линзы приложений появился выбор между источниками приложений, который позволяет искать как среди установленных программ, так и доступных для загрузки из центра приложений.
Ещё одним изменением стало новое поведение Alt + Tab, который теперь по умолчанию переключает окна ограниченные в пределах только одного рабочего стола. Для возможности работы с программами расположенными на нескольких рабочих столах потребуется снять галочку с опции «Bias alt-gab to prefer windows on the current viewport», как на скриншоте ниже:
Теперь доступен новый режим «push to reveal», который позволяет избавиться от случайного появления панели Unity (например при нажатие кнопки назад в Firefox). Настройка доступна через менеджер CompizConfig Settings Manager.
Полный список изменения в Unity 5.2 доступен тут: launchpad.net
Пакет с Unity 5.2 доступна для Precise из Unity PPA.
Автор: ГАЗЕНВАГЕН™