FastNetMon

Monday 29 December 2014

Профайлинг Perl приложений

Всем привет! Сегодня мы будем профайлить Perl приложения и искать в них узкие места!

Итак, я веду эксперименты над своей новой утилиткой - linux_network_activity_tracker.pl, которая на машинах с десятками тысяч процессов работает довольно медленно.

Итак, запускаем профайлинг:
perl -d:DProf linux_network_activity_tracker.pl
После того, как он отработал нам нужно запустить обработчик полученных данных:
dprofpp
В итоге нам будет сгенерирована удобная и доступная для понимания табличка:
Total Elapsed Time = 78.18037 Seconds
  User+System Time = 57.55037 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 57.1   32.89 32.893  26894   0.0012 0.0012  Antidoto::get_process_connections
 30.0   17.30 17.620    342   0.0506 0.0515  Antidoto::parse_tcp_connections
 6.52   3.750  3.750    342   0.0110 0.0110  Antidoto::parse_unix_connections
 1.00   0.576  1.019   6986   0.0001 0.0001  Antidoto::get_proc_status
 0.98   0.563  0.563   7328   0.0001 0.0001  Antidoto::read_file_contents_to_li
                                             st
 0.56   0.323  0.323 136504   0.0000 0.0000  Antidoto::_hex2ip
 0.28   0.160  0.160    342   0.0005 0.0005  Antidoto::build_inode_to_socket_lo
                                             okup_table
 0.13   0.073  1.092    342   0.0002 0.0032  Antidoto::get_init_pid_for_contain
                                             er
 0.05   0.028  0.037    342   0.0001 0.0001  Antidoto::parse_udp_connections
 0.02   0.010  0.010      4   0.0025 0.0025  main::BEGIN
 0.00       - -0.000      1        -      -  DynaLoader::dl_load_file
 0.00       - -0.000      1        -      -  DynaLoader::dl_undef_symbols
 0.00       - -0.000      1        -      -  DynaLoader::dl_find_symbol
 0.00       - -0.000      1        -      -  DynaLoader::dl_install_xsub
 0.00       - -0.000      1        -      -  Digest::MD5::bootstrap
По которой отлично видно, кто пожрал время CPU :)

Wednesday 24 December 2014

Как работает механизм SYN Cookie в ядре Linux?

Всем привет! 

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

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

Уверен, многие в своей ежедневной практике используют  механизм  syn cookie, который позволяет решать проблемы с syn флудом.

Как известно, эта фича включается так:
sysctl -w net.ipv4.tcp_syncookies = 1
Но когда именно они включаются? Как работают? Всегда ли это панацея? На эти вопросы я постараюсь ответить ниже.

Вся обработка входящего tcp соединения начинается в файле net/ipv4/tcp_input.c в функции tcp_conn_request. Там сразу после приема пакета осуществляется следующая проверка:
if ((sysctl_tcp_syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) {      want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name); }
Дословно она означает, что syn cookie включаются (не посылаются! А просто включаются!) если параметр sysctl -w net.ipv4.tcp_syncookies = 2 либо когда переполнится syn очередь для сокета (а она в свою очередь задается через параметр net.ipv4.tcp_max_syn_backlog = 2048).

Уже сейчас можно понять, что 2048 - это очень много и почти любое приложение после получения такого числа запросов на подключение погибнет. При этом syn cookie включенные в режиме "1" еще даже не включатся!

Чуточку дальше в том же файле tcp_input.c осуществляется проверка - а не переполнена ли очередь ожидания на слушающем (listen) сокете, она в свою очередь задается на уровне приложения, через второй аргумент системного вызова listen. И если она полна - то соединение просто отбрасывается, безотносительно режиму syn cookie вообще! Система предполагает, что приложение не может больше отработать и в принципе не принимает трафик. На этом обработка кончается. Если же очередь не забита - мы движемся дальше.

Обращаю внимание! Что syn cookie еще НЕ был отправлен! Даже если он был включен на какой-то из предыдущих проверок.

Теперь мы вызываем функцию cookie_init_sequence, которая в свою очередь дергает cookie_v4_init_sequence, которая объявлена в net/ipv4/syncookies.c. И там параметры входящего соединения хэшируются с помощью хэш функции sha1 (secure_tcp_syn_cookie). В свою очередь, стоит обратить внимание, что sha1 - это довольно тяжелая функция для процессора и довольно серьезный Intel E5 2620 может хэшировать не более 400 мегабайт/секунду в расчете на 1 ядро. Постойте пугаться! Не съедят syn cookie Ваш процессор, даже довольно слабый 2620 может обработать до 32Gb/s чисто на хэшировании, это огромные цифры, так что нагрузки на процессор пугаться не стоит - я просто привел информацию о том, что это не бесплатная операция.

И после того как эта cookie сформирована, мы посылаем ее клиенту. Как можно заметить - режим "2" работы syn cookie на сервере, где ожидаются атаки - предпочтительнее. Но стоит понимать, что syn cookie - это хак, который не особо согласуется с протоколом tcp и бывает, что он приводит к неработоспособности ряда фич, поэтому использовать его нужно лишь после тщательных тестов. Но при этом такой подход все равно  не защищает приложения должным образом.

Как хорошее и надежное решение данной проблемы стоит использовать iptables/netfilter цель SYN PROXY, которая блокирует  syn флуд раньше, чем они пойдет в обработку ядром (на этапе фильтрации через netfilter). Но тут стоит отметить, что данный функционал был добавлен в 3.12 ядре и присутствует только в RHEL/CentOS 7, в более младших версиях дистрибутивов такой возможности нету и вряд ли появится.

Но я бы хотел добавить, что syn cookie должны быть ВКЛЮЧЕНЫ в обязательном порядке. Вообще без них Linux упадет от самой слабой атаки syn флудом.

В чем же отличия 1 и 2го режимов syn cookie? В первом режиме они подключаются, когда переполняется очередь syn соединений на 1 порт, а во втором режиме они включаются безусловно даже очередь пуста. То есть для ВСЕХ соединений.

Подробнее читайте в документации ядра Linux - тут :) Все рассуждения основаны на анализе ядра ядра 3.18, в Вашей версии ядра все может быть ИНАЧЕ!

Tuesday 23 December 2014

Сборка конструктора сетевых пакетов для C++ Crafter

Ставим зависимости:
sudo apt-get install libpcap0.8 libpcap0.8-dev

Берем здесь файлик crafter-0.3.tar.gz https://drive.google.com/folderview?id=0B4PDTNA2TABgVWRSaW5yLW5UbFk&usp=sharing

Кладем его на сервер в папку /usr/src
cd /usr/src
tar -xf crafter-0.3.tar.gz
cd crafter-0.3/
./configure --prefix=/opt/crafter
make
make install
Добавляем библиотеку в систему:
echo "/opt/crafter/lib/" > /etc/ld.so.conf.d/crafter.conf
ldconfig
А теперь идем изучать документацию! Штука очень занятная и аналогичная Scapy во многом. 

Monday 22 December 2014

Демонизация процессов на CentOS и Debian

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

Для Debian есть пакет daemon.

Вот с таким набором параметров:
daemon
Invalid arguments: no command supplied
usage: daemon [options] [--] [cmd arg...]
options:

      -h, --help              - Print a help message then exit
      -V, --version           - Print a version message then exit
      -v, --verbose[=level]   - Set the verbosity level
      -d, --debug[=level]     - Set the debugging level

      -C, --config=path       - Specify the configuration file
      -N, --noconfig          - Bypass the system configuration file
      -n, --name=name         - Guarantee a single named instance
      -X, --command=cmd       - Specify the client command as an option
      -P, --pidfiles=/dir     - Override standard pidfile location
      -F, --pidfile=/path     - Override standard pidfile name and location

      -u, --user=user[:group] - Run the client as user[:group]
      -R, --chroot=path       - Run the client with path as root
      -D, --chdir=path        - Run the client in directory path
      -m, --umask=umask       - Run the client with the given umask
      -e, --env="var=val"     - Set a client environment variable
      -i, --inherit           - Inherit environment variables
      -U, --unsafe            - Allow execution of unsafe executable
      -S, --safe              - Deny execution of unsafe executable
      -c, --core              - Allow core file generation

      -r, --respawn           - Respawn the client when it terminates
      -a, --acceptable=#      - Minimum acceptable client duration (seconds)
      -A, --attempts=#        - Respawn # times on error before delay
      -L, --delay=#           - Delay between spawn attempt bursts (seconds)
      -M, --limit=#           - Maximum number of spawn attempt bursts
          --idiot             - Idiot mode (trust root with the above)

      -f, --foreground        - Run the client in the foreground
      -p, --pty[=noecho]      - Allocate a pseudo terminal for the client

      -l, --errlog=spec       - Send daemon's error output to syslog or file
      -b, --dbglog=spec       - Send daemon's debug output to syslog or file
      -o, --output=spec       - Send client's output to syslog or file
      -O, --stdout=spec       - Send client's stdout to syslog or file
      -E, --stderr=spec       - Send client's stderr to syslog or file

          --running           - Check if a named daemon is running
          --restart           - Restart a named daemon client
          --stop              - Terminate a named daemon process


Для  CentOS есть пакет daemonize.

Вот с таким набором параметров:
 daemonize
daemonize, version 1.7.3
Usage: daemonize [OPTIONS] path [arg] ...

OPTIONS

-a             Append to, instead of overwriting, output files. Ignored
               unless -e and/or -o are specified.
-c <dir>       Set daemon's working directory to <dir>.
-e <stderr>    Send daemon's stderr to file <stderr>, instead of /dev/null.
-E var=value   Pass environment setting to daemon. May appear multiple times.
-o <stdout>    Send daemon's stdout to file <stdout>, instead of /dev/null.
-p <pidfile>   Save PID to <pidfile>.
-u <user>      Run daemon as user <user>. Requires invocation as root.
-l <lockfile>  Single-instance checking using lockfile <lockfile>.
-v             Issue verbose messages to stdout while daemonizing.
Такое ощущение, что лучше демонизироваться самому.

Saturday 20 December 2014

Сборка ZeroMQ на Debian 7 Wheezy

Довольно много приложений последнее время переходят к использованию ZeroMQ и не зря! Очень крутая библиотека :)

Итак, соберем же ее:
cd /usr/src
wget http://download.zeromq.org/zeromq-4.0.5.tar.gz
tar -xf zeromq-4.0.5.tar.gz
cd zeromq-4.0.5
./configure --prefix=/opt/zeromq_405
make install
ln -s /opt/zeromq_405/ /opt/zeromq

Добавляем библиотеку в систему:
echo "/opt/zeromq/lib" > /etc/ld.so.conf.d/zeromq.conf
ldconfig
Все, теперь можно собирать любые приложения с данный библиотекой:

g++ server.c -I /opt/zeromq/include -l/opt/zeromq/lib/libzmq.so -oserver

Wednesday 17 December 2014

Настройка OpenVPN сервера на CentOS 7 и активация клиентам на Mac OS

Подключаем  Epel репозиторий:
yum install -y epel-release
Устанавливаем OpenVPN пакет:
yum install -y openvpn
Копируем пример конфига OpenVPN в боевой путь:
cp /usr/share/doc/openvpn-2.3.2/sample/sample-config-files/server.conf /etc/openvpn/server.conf
Корректируем конфиг:
vim /etc/openvpn/server.conf
Добавляем в самый низ (подключаем DNS серверы Google):
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"

И следом активируем перенаправление всего трафка в туннель:
push "redirect-gateway def1 bypass-dhcp"
Включаем форвардинг трафика в ядре:
echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/forwarding.conf
sysctl --system 
Активируем фаерволл:
systemctl enable firewalld
systemctl start firewalld
systemctl status firewalld
Применяем изменения:

firewall-cmd --reload
И разрешаем OpenVPN и ssh трафик:
firewall-cmd --add-service=ssh --permanent
firewall-cmd --add-service=openvpn --permanent
firewall-cmd --add-masquerade --permanent

Теперь нам нужно сгенерировать сертификаты для сервера;
mkdir /root/openvpn_keys
yum install -y unzip zip
mkdir /root/openvpn_keys
cd /root/openvpn_keys
wget https://github.com/OpenVPN/easy-rsa/archive/master.zip
unzip master.zip
cd easy-rsa-master/easyrsa3
Создаем собственный PKI (не забудьте пароль от CA, иначе в будущем придется создавать его заново):
mv vars.example  vars
./easyrsa init-pki
./easyrsa build-ca
./easyrsa gen-dh
Создаем сертификаты для сервера:
./easyrsa gen-req myservername
./easyrsa sign-req server myservername
Снимаем пароль с приватного ключа:
openssl rsa -in /root/openvpn_keys/easy-rsa-master/easyrsa3/pki/private/myservername.key -out /root/openvpn_keys/easy-rsa-master/easyrsa3/pki/private/mmyservername.key.without_password

Заменяем ключ на безпарольный:
mv /root/openvpn_keys/easy-rsa-master/easyrsa3/pki/private/myservername.key.without_password /root/openvpn_keys/easy-rsa-master/easyrsa3/pki/private/myservername.key
Переносим сертификаты и ключи в папку OpenVPN:
cp pki/ca.crt   /etc/openvpn/ca.crt
cp pki/dh.pem /etc/openvpn/dh1024.pem
cp pki/issued/myservername.crt /etc/openvpn/server.crt
cp pki/private/myservername.key  /etc/openvpn/server.key
Создаем конфиг клиенту (уже с флажком nopass, так как нам тут пароль не нужен):
./easyrsa gen-req clientmac nopass
./easyrsa sign-req client clientmac

Запускаем OpenVPN:
systemctl enable openvpn@server
systemctl start openvpn@server
systemctl status -l openvpn@server

При настройке клиента стоит учесть, что ему нужно передать каким-либо безопасным способом следующие файлы:
/root/openvpn_keys/easy-rsa-master/easyrsa3/pki/issued/clientmac.crt
/root/openvpn_keys/easy-rsa-master/easyrsa3/pki/private/clientmac.key
/root/openvpn_keys/easy-rsa-master/easyrsa3/pki/private/ca.crt
Продолжаем настройку - настроим клиента OpenVPN на MacOS.



Настройка OpenVPN клиента на Mac OS - Tunnelblick

Все проще некуда :) Сначала стягиваем Tunnelblick и устанавливаем.

После этого стягиваем с OpenVPN сервера файлы:
client.crt
client.key
ca.crt
Создаем конфиг для Tunnelblick:
mkdir ~/Desktop/openvpn_secure.tblk
cd ~/Desktop/openvpn_secure.tblk
Создаем конфиг OpenVPN (не забываем менять a.b.c.d на адрес сервера):
vim config.ovpn
Содержимое:
client
proto udp
verb 3
dev tun
remote a.b.c.d
port 1194
ca ca.crt
cert clientmac.crt
key clientmac.key
nobind
persist-key
persist-tun
comp-lzo
После этого открываем рабочий стол и дважды щелкаем по openvpn_secure.tblk и соглашаемся на добавление туннеля. После этого в углу экрана тыкаем по значку  Tunnelblick и подключаемся. Ура!

Источник: тыц

Sunday 14 December 2014

trafgen и ошибка Flushing TX_RING failed

Боюсь, что оно чинится лишь обновлением ядра на что-то в районе  3.16. Соответствующий багрепорт: https://github.com/netsniff-ng/netsniff-ng/issues/136

Самый удобный и правильный способ обновить ядро на Debian 7 Wheezy

Открываем конфиг репозиториев:
vim /etc/apt/sources.list

Добавляем там:
deb http://http.debian.net/debian wheezy-backports main

Ставим новое ядро:
apt-get update
apt-get -t wheezy-backports install linux-image-3.16.0-0.bpo.4-amd64linux-headers-3.16.0-0.bpo.4-amd64

После этого в ребут:
shutdown -r now
Но обращаю внимание - что ядра из бэкпортов намного менее объезжены и оттестированы. И очень легко налететь на какой-либо чудесный баг.

Установка packETH на Debian 7 Wheezy

Вот так:
apt-get install -y make gcc pkg-config libgtk-3-dev libgtk2.0-dev
cd /usr/src
wget http://sourceforge.net/projects/packeth/files/latest/download?source=files -OpackETH-1.8.tar.bz
tar -xf packETH-1.8.tar.bz
cd packETH-1.8/
./configure --prefix=/opt/packeth
make
make install
Сама тулза очень странная, требует иксы. А консольная версия очень урезанная, ибо умеет посылать лишь подготовленный ранее кусок данных в формате pcap.

Так что задача высокоскоростного генератора трафика остается открытой.

Thursday 11 December 2014

Tuesday 9 December 2014

OpenVZ: TCP: time wait bucket table overflow (CT1001)

 Да, очень странный стандартный конфиг:
sysctl -a|grep tcp_max_tw_buckets
net.ipv4.tcp_max_tw_buckets = 262144
net.ipv4.tcp_max_tw_buckets_ub = 16536
Особо заметно будет при нескольких контейнерах на машину. Детально: http://kb.sp.parallels.com/en/118693

Как просмотреть содержимое L2TP пакетов не шифрованного туннеля?

Итак, имеется L2TP трафик, внутри которого по IP транспорту идет трафик.

Как его распаковать? Ни tcpdump, ни tshark его стандартно не видят.

Итак, нам нужен Wireshark 1.99. Входим в его настройки, идем в Protocols, выбираем там L2TP. L2TPv3 cookie size: Detect, L2TPv3 L2-Specific sublayer: Default L2-Specific. Decode L2TPv3 packet contents as: Ethernet. И все! Оно распакуется и будет видно содержимое пакетов :)

Monday 8 December 2014

Детектирование DDoS атак на отраженном и ответном трафике

Рекомендую к прочтению очень полезную информацию о том, как косвенно фиксировать атаки: http://labs.spritelink.net/clever-ddos-detection

Чтобы не потерялось привожу здесь:

The detection of DDoS attacks is typically based on some form of threshold value and typically on traffic that is going to a potential target, ie to the host that we want to protect from attacks.
It can be a threshold value for total traffic or for a certain traffic class, like UDP packets, and the actual threshold value can either be configured statically or it can be more dynamic and based on previously collected data, ie baselining and from that finding anomalous traffic patterns.
Using traffic classes can help us differentiate between a large file transfer and a large DNS reflection & amplification attack but not all attacks are that easy to filter out.
One approach is simply to let the end hosts tell us when they are under attack. A server knows what traffic it wants and anything else would be counted in an "unwanted" bucket and once we see enough unwanted traffic it could notify the DDoS mitigation system via an API. This approach requires integration with the end hosts though and while it might be possible in a homogeneous environment it can be very difficulty in a mixed environment.
How do we know when a host is receiving unwanted packets?
A normal TCP stack sends TCP RST packets for incoming TCP packets it doesn't want and similarly a firewall will send ICMP administratively prohibited messages.
By analysing TCP RST & ICMP messages we should be able to get a hint on when an attack is occurring and we could hone in classic threshold based methods for an end result which should be a highly accurate DDoS detection system.
So how can we collect the information? NetFlow collects flows on the router which it then exports to the collector and while this is great for traffic statistics it doesn't lend itself very well for this type of DDoS detection. We will not be able to see inside that ICMP message to see what packet caused the sending of the ICMP packet. sFlow on the other hand provides just this possibility as it merely send the N first bytes of a sampled packet.
Further, Netflow is inherently slow. Most router implementations allow a minimum timer of 60 and 15 seconds for active and inactive flows respectively. Packets are put in a flow table and subsequent packets in the same flow will increase the bits and number of packets seen for that flow. Once the expiration timer is hit the flow is exported. That means you will have to wait up to a minute before you receive data at your collector.
sFlow on the other hand works completely differently. It takes the first N bytes of a packet and sends it off directly to the collector allowing you to do analysis on just seconds old data.
When it comes to DDoS detection the difference between minutes and a few seconds means the world.
By analysing TCP RST and ICMP via sFlow one should be able to build a DDoS detection system without match... or has anyone already done this?

Набор данных о различных протоколах в формате PCAP

Как запустить tshark, tcpdump или iftop из под высокопроизводительного PF_RING?

Для этого нужно собрать pcap с поддержкой pf_ring. Сначала собираем модуль ядра - PF_RING. Потом собираем похаченую библиотеку pcap, в которой опрос данных переделан на PF_RING.
cd /usr/src
wget http://sourceforge.net/projects/ntop/files/PF_RING/PF_RING-6.0.2.tar.gz/download -OPF_RING.6.0.2.tar.gz
tar -xf PF_RING.6.0.2.tar.gz
cd PF_RING-6.0.2
Собираем либу (но не ставим):
cd lib
make
Собираем патченный pcap:
cd userland/libpcap-1.1.1-ring
./configure --prefix=/opt/pcap_pfring_602
make install
Врубаем tshark или любой другой софт с поддержкой pcap:

LD_PRELOAD=/opt/pcap_pfring_602/lib/libpcap.so tshark -i eth3
Можно iftop:
LD_PRELOAD=/opt/pcap_pfring_602/lib/libpcap.so iftop -i eth3
И даже tcpdump:

LD_PRELOAD=/opt/pcap_pfring_602/lib/libpcap.so tcpdump -i eth4 'proto l2tp'
И ура - скорость будет дикая! :)

Monday 1 December 2014

CMake Error at /usr/lib64/boost/Boost.cmake:536 (message) - ошибка при работе cmake на CentOS 6 при сборке проекта с Boost

Вот поймал такую ошибку:



cmake ..
-- The C compiler identification is GNU 4.4.7
-- The CXX compiler identification is GNU 4.4.7
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
CMake Error at /usr/lib64/boost/Boost.cmake:536 (message):
The imported target "boost_date_time-static" references the file

"/usr/lib64/lib64/libboost_date_time.a"

but this file does not exist. Possible reasons include:

* The file was deleted, renamed, or moved to another location.

* An install or uninstall procedure did not complete successfully.

* The installation package was faulty and contained

"/usr/lib64/boost/Boost.cmake"

but not all the files it references.

Call Stack (most recent call first):
/usr/lib64/boost/BoostConfig.cmake:28 (include)
/usr/share/cmake/Modules/FindBoost.cmake:177 (find_package)
CMakeLists.txt:30 (find_package)


-- Configuring incomplete, errors occurred!
See also "/usr/src/fastnetmon/build/CMakeFiles/CMakeOutput.log".
Фиксится вот так - перед функцией find_package добавляем следующее:
set(Boost_NO_BOOST_CMAKE ON)

Вот запись в трекере cmake: http://public.kitware.com/Bug/view.php?id=15270

Friday 28 November 2014

Отвратительнейщий Альфа-Банк

Привет, друзья!

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

Итак, мой негативный опыт связан с Альфа Банком (alfabank.ru), есть такой российский на первый взгляд привлекательный банк, но такое впечатление сохраняется до того момента, пока этот банк не оставит вас без денег.

Меня он оставлял ВООБЩЕ БЕЗ ДЕНЕГ ровно ДВАЖДЫ.

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

Но сегодняшняя ситуация меня просто убила.

Альфа-Банка БЕЗ ПРЕДУПРЕЖДЕНИЯ АННУЛИРОВАЛ МОЮ ДЕБЕТНУЮ КАРТУ, КОТОРУЮ САМ ГОД НАЗАД ПОДАРИЛ!!!!

Хер бы с ним будь эта карта платной, но аннулировать свой же подарок, это просто театр дебилов какой-то. 

Нет, это не смешно, именно так. Год назад мне позвонили с Альфа-Банка и мило поздравили меня с выигрышем и получением подарка! На что я ответил со скепсисом, предвкушая  очередную разводку. Но нет, мне дарили карту Platinum сроком на 3 года (до 2016го) совершенно без оплаты за ее обслуживание (довольно немаленькая сумма)! Но меня не обманули и через неделю в отделении вручили заветную карту, которой я начал сразу пользоваться. Кроме этого, меня заверили, что никаких условий и подвохов в этой карте нету и меня ни за что не заставят платить.

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

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

Что еще более смешно, изначально мне специалист по общению с наиболее важными клиентами (выделенная линия Platinum!!!) заявил, что я лично в отделении Банка закрыл эту карту вчера (когда сидел дома)! Знаете, я чуть не поседел предвосхищая ту их многих историй, когда по поддельным документам снимали все деньги. Но аж после 10 минут висения на линии меня обрадовали реальной причиной =) Даже не извинились, ах-ах! 

Итого, друзья. Если Вы хотите, что бы Вам блокировали карты в тот самый момент, когда Вы не можете ну никак обойтись без денег - ОБРАЩАЙТЕСЬ В САМЫЙ ХУДШИЙ БАНК РОССИИ - АЛЬФАБАНК!

Вот вам такой небольшой эдвайс от меня :)

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

Tuesday 25 November 2014

Отличная презентация об эффекте в сокращении нагрузки на CPU за счет offload фич современных сетевых карт


Ага, на Японском, но таблички понятные. Кто переведет на английский будет молодец.

GRE, DDoS и туннели в нашей жизни и их неизменные грабли

GRE — очень большой источник проблем с MTU, если http их переживет более-менее нормально благодаря path mtu discovery, то UDP особенно с флагами «do not fragment» будет нищадно дропаться, на что пользователи будут очень сильно возмущаться. 

Поэтому вариант переброски всего трафика вообще по GRE — не лучшая идея сама по себе. vlan, MPLS или даже какой-либо из VPN с аппаратной поддержкой и поддержкой фрагментации внутри протокола - будет лучше. 

Решить проблемы GRE можно подняв MTU на магистрали (если у вас есть-таки резервный линк до компании по защите), но магистрали вряд ли захотят передавать пакеты с MTU более 1500, а чтобы GRE мог пропускать через себя пакеты с нормальным MTU — нужно пропускать по магистрали пакеты с MTU около 1524, что почти неосуществимо без долгих переговоров с конкретными аплинками. А если такая возможность есть, то скорее всего можно сделать и l2/vlan/mpls, а значит можно просто сделать свой анонс из другого места и это наиболее прямой и предпочтительный вариант «полной защиты» префиксов.

Monday 24 November 2014

Обработка и фильтрация 80GE трафика на дешевом железе

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

Стоить задача обсчитать 80GE из этого трафика и пропустить его к клиентам имея лишь 4 сервера (будем звать их фильтр-1/2/3/4 соответственно) с двумя 10GE картами (20GE на 1 сервер). За фильтр серверами стоит один диапазон /24. При этом, трафик даже на 1 IP (/32) должен распределяться по ВСЕМ машинам фильтрам.

Как это реализовать?

Очевидно, входящий трафик должен разделять исключительно роутер, поэтому мы должны выдать каждому фильтр-серверу отдельный IP и создать 4 идентичных правила роутинга по аналогии с Linux:
ip route add 31.xx.xx.xx/24 via 10.10.10.101
ip route add 31.xx.xx.xx/24 via 10.10.10.102
ip route add 31.xx.xx.xx/24 via 10.10.10.103
ip route add 31.xx.xx.xx/24 via 10.10.10.104
Но Juniper тут себя поведет не особо предсказуемо - он будет лить трафик только в один из путей, так как он устроен так :) Но это поведение легко поменять! С помощью директивы - load-balance per-packet которая активирует равномерное распределение трафика по всем путям. Таким образом, все фильтр серверы начнут получать одинаковый объем трафика.

А далее фильтр-серверы передают трафик клиентам через обычные правила роутинга (они должны быть в том же vlan).

Но как сделать чтобы трафик исходящий также балансировался? Тут нам поможет Линукс! А именно вот этот гайд - http://lartc.org/howto/lartc.rpdb.multiple-links.html Схема простая - машина должна получить все 4 фильтр-сервера как дефалт гейтвеи с идентичным weight. Тогда и исходящий трафик размажется пропорционально.

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

Monday 10 November 2014

Почему IPv6 нужен каждому сайту?

Довольно часто задают вопрос "зачем мне ваш IPv6", поэтому решил сформулировать тут.

1) Вы не платите деньги за IPv4 (если Вы явно их не платите - это не значит, что они бесплатны, цена включена в тариф). В случае IPv6 у хостера никаких затрат нету и потенциально тариф будет дешевле.
2) Вы получаете миллионы IPv6 совершенно бесплатно (/64, /96 на клиента - норма). Вынести админку на отдельный айпи? Легко. На отдельный айпи повесить почту? Каждый сайт из тысячи на отдельном IP? Легко!
3) Решаются вопросы с блэклистингом IPv4 (речь про странные списки) почтовиками, в случае IPv6, этот процесс можно сказать почти не работает, а для IPv6 взять несколько /64 сети - довольно сложно
4) Вы получаете бОльшую скорость работы сайтов. Так как оборудование для отработки v6 часто стоит отдельно и почти всегда простаивает. Кроме этого, IPv6 сам по себе решительно "легче" навороченного IPv4 и проще для отработки железом
5) Проще обеспечивать сложные сетевые конфигурации. Если Вы хотите в одно мгновение ока перекидывать сайты с ДЦ на ДЦ, делать балансировку через anycast и прочее Вы должны покупать как минимум префикс /24 IPv4, а это ОЧЕНЬ дорогое удовольствие. В случае IPv6 даже /48 сеть не стоит почти ничего, платежи в RIPE минимальны
6) При использовании виртуализации преимущества в целом также очевидны - не нужно городить огород, а просто каждая машина получает свой IPv6, в случае IPv4 это выходит крайне дорого. А учитывая мощности железа - уже почти каждый владелец дедика использует ту или иную виртуализацию, ибо иначе все ресурсы тупо не использовать
7) Отсутствие геморроя при связи с клиентом, если Вам нужна двунаправленный канал связи, то в случае NAT это превращается в страшный ад.

Thursday 6 November 2014

Tuesday 4 November 2014

OpenVZ, ploop и флаг O_DIRECT для MySQL

После долгого обсуждения сошлись на очень хитром эффекте, а именно, если для Innodb выставить тип коммита O_DIRECT вместо стандартного sync/fdatasync, то на OpenVZ это приведет к очень крутому ускорению работы базы данных.

Но как раз в данной статьей красиво объяснено то, что OpenVZ игнорирует O_DIRECT и тем самым указывая такой флаг для MySQL мы отключаем вообще как таковую работу с транзакциями. То есть что есть транзакция, что нету — данные будут записаны по flush таймауту ext4 (5 секунд), а не тогда, когда этого хочет MySQL.

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

Thursday 30 October 2014

Как собрать RAID-10 на ZFS?

Самый надежный при высокой скорости работы вариант конфигурации диском на мой взгляд. Я буду собирать на Linux, но те же самые команды (за исключением имен устройств применимы и к другим ОС).

Во-первых, нужно на всех дисках, которые планируется добавить в массив создать gpt таблицу разделов, это можно сделать через parted /dev/sdX и там вбит mklabel gpt

После этого собираем первое зеркало:
zpool create data mirror /dev/sdb /dev/sdc

А вот активация страйпа будет хитрая - надо просто в этот же вольюм добавить еще несколько зеркал!

Приступаем:
zpool add data mirror /dev/sdd /dev/sde
zpool  add data mirror /dev/sdf /dev/sdh
zpool  add data mirror /dev/sdg /dev/sdi
Вуаля, у нас создался RAID10 из 8 дисков, то есть stripe по 4м зеркалам:
zpool status
  pool: data
 state: ONLINE
  scan: none requested
config:

    NAME        STATE     READ WRITE CKSUM
    data        ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
        sdb     ONLINE       0     0     0
        sdc     ONLINE       0     0     0
      mirror-1  ONLINE       0     0     0
        sdd     ONLINE       0     0     0
        sde     ONLINE       0     0     0
      mirror-2  ONLINE       0     0     0
        sdf     ONLINE       0     0     0
        sdh     ONLINE       0     0     0
      mirror-3  ONLINE       0     0     0
        sdg     ONLINE       0     0     0
        sdi     ONLINE       0     0     0
Размером в 11Тб:
df -h /data
Filesystem      Size  Used Avail Use% Mounted on
data             11T  128K   11T   1% /data
На этом все :)

Wednesday 29 October 2014

PF_RING ZC vs PF_RING non zc

Update: тесты были проведены из рук вон плохо, не ориентируйтесь на них ВООБЩЕ. Скоро выполню повторно. 

В процессе разработки и тестирования FastNetMon столкнулся с тем, что довольно мало информации о явном преимуществе в скорости выше указанного режима zero copy.

Итак, я провел немного тестов на нагрузке в районе 350 kpps ~2Gbps на i7 2600.

Non zero copy режим, multichannel  опрос сетевой в 8 потоках:



Активирован PF_RING zero copy mode:


Как можно видеть, прирост скорости просто огромный. Причем, что приятно, вовсе не потребовалось изменять интерфейсы приложения написанного под режим one-copy, чтобы заработал ZC режим. Дабы никого не смущать - всплеск на ядре - это моя работа (моего приложения) :)

UPDATE:
Самое смешное, что это оказался не ZC режим, а похоже чудесная конфигурация сетевой скриптом load_driver.sh из папки с патченным ixgbe драйвером.


Tuesday 28 October 2014

Как отлажить фильтры шейпера tc в Linux и не сойти с ума?

Ваш покорный слуга занимается этим уже долгие недели и решил, что хватит это терпеть!

Если Вы работали с фильтрами tc, то, уверен, вспоминаете это время с дрожью и ненавистью.

Почему?

Ну, например, потому, что обычный фильтр (tc -s filter show dev bond0) выглядит так:
filter parent 1: protocol ip pref 10 u32 fh b:37:800 order 2048 key ht b bkt 37 flowid 1:3c  (rule hit 11 success 0)
  match 9ffd1737/ffffffff at 12 (success 0 )

Да, Вы правильно подумали, 9ffd1737 - это IP адрес, который используется в фильтре. Как же его привести в вид IP адреса из этого шестнадцатеричного бреда?

Итак, скрипт спаситель: https://gist.github.com/pavel-odintsov/8bcddca3673f97d18b8f

Он работает как фильтр и запускать его так:

tc -s filter show dev bond0| perl convert_hex_ip_to_human_ip.pl
В итоге указанный Выше участок примет вид:
filter parent 1: protocol ip pref 10 u32 fh 800::80a order 2058 key ht 800 bkt 0 link b:  (rule hit 269307 success 0)
  match 159.253.23.0/255.255.255.0 at 12 (success 32668 )
    hash mask 0.0.0.255 at 16
Пожалуйста :)

Friday 24 October 2014

Очень требуется помощь спеца по Linux шейперу - tc, htb

Буду очень благодарен за помощь вот в такой проблемине:  http://forum.nag.ru/forum/index.php?showtopic=98376

Если есть идеи, можно либо на Nag.ru либо в комментариях :)

Thursday 23 October 2014

PF_RING and Transparent Mode

PF_RING has been designed for enhancing packet capture performance. This means that the RX path must be accelerated, and in particular a way to accelerate this is by reducing the journey of the packet from the adapter to userland. This is obtained by allowing the driver to push the packet from the NIC to PF_RING directly and not through the usual kernel path. For this reason PF_RING has introduced an option named “transparent mode” whose goal is to tune how packets are moved from the NIC to PF_RING. This option (that can be specified when inserting the PF_RING module via insmod) can have three values:

  1. insmod pf_ring.ko transparent_mode=0
    This is the default and it means that packets are sent to PF_RING via the standard kernel mechanisms. In this setup the packets are both sent to PF_RING but to all other kernel components. All NIC drivers support this mode.
  2. insmod pf_ring.ko transparent_mode=1
    In this mode, packets are sent directly by the NIC driver to PF_RING, packets are still propagated to other kernel components. In this mode packet capture is accelerated because packets are copied by the NIC driver without passing through the usual kernel path. Please note that in order to enable this mode, you must use a NIC driver that supports PF_RING. Available PF_RING-enabled drivers can be found in the drivers/ directory of PF_RING.
  3. insmod pf_ring.ko transparent_mode=2
    In this mode, packets are sent directly by the NIC driver to PF_RING, packets are not propagated to other kernel components as this slows down packet capture. Please note that:
    • in order to enable this mode, you must use a NIC driver that supports PF_RING.
    • Packets are not sent to the kernel after they have been delivered to PF_RING. This means that you won’t have connectivity from NICs driven by PF_RING-aware drivers.
    • This mode is the fastest one as packets are quickly copied to PF_RING and immediately discarded after they have been processed.
    Копипаста из: http://www.ntop.org/pf_ring/pf_ring-and-transparent-mode/ А-то постоянно теряю хелп :)

Wednesday 22 October 2014

Защита от DDoS атак с помощью FastNetMon

В наши дни почти у любой компании вне зависимости от сферы деятельности очень часто встает вопрос - как защититься от DDoS атак?

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

Стоимость оборудования для защиты от атак начинается с 1 миллиона рублей за 1Gbps (а такие атаки уже подбираются к категории "самые слабые", с которых все только начинается), услуги по защите стоят решительно дешевле, расценки начинаются от 10 000 рублей до 50 тысяч и более в особо сложных случаях.

Как же быть компании, которая не готова к таким тратам? Разумеется, ответ есть! Продолжение можно прочесть на LinkedIn: вот ссылка

Friday 17 October 2014

Парсинг бинарных UDP DNS сообщений на Go lang

Эту таску легко решить с помощью совершенно волшебного модуля от miekg (на нем работает DNS в CloufFlare, да).

Стягиваем модуль:
go get github.com/miekg/dns 
 Подключаем:

import "github.com/miekg/dns"
А вот простенький пример кода, который извлекает имя домена из пакета:
var msg dns.Msg
err = msg.Unpack(binary_date_payload)

if err != nil { // Send a FormatError back
//fmt.Println("Can't parse!", err)
continue
}

domain_name := strings.ToLower(msg.Question[0].Name)
fmt.Println(domain_name)



Wednesday 15 October 2014

Лучшая на Планете замена grep - ack!

Друзья, это пожалуй лучшая тулза изобретенная со времен динозавров :) Не знаю как вы, но я каждый день по полсотни раз использую grep -HiR 'pattern' * для поиска текста в конфигах/коде, это занимает очень много времени и очень неудобно.

Но решение есть - установите ack, что в Дебияне, что в Центосе оно есть!

Прошу:
ack 'Downloaded signature is too small'
fileserver/ovz/CodeLib.pm
346:        CodeLib::debug_print("error", "Downloaded signature is too small  and we can't use it! Please check backup server!");
 За рекомендацию спасибо Alexander Fenster :)

Friday 10 October 2014

Как добавить море полезных функций в Puppet?

Вот так:
puppet module install puppetlabs-stdlib
Подробно о полезных функциях:  https://forge.puppetlabs.com/puppetlabs/stdlib

А на клиенты эта библиотека притянется автоматически при синхронизации :)

Friday 26 September 2014

Как понять в каком месте завис Perl скрипт?

Берем вот этот скрипт: https://raw.githubusercontent.com/ahiguti/gdbperl/master/gdbperl.pl

Стягиваем gdb: yum install -y gdb

Получаем нечто вот такое:
perl gdbperl.pl 755934
command:
gdb -silent -nw /usr/bin/perl 755934

environ:
SHELL=/bin/sh
MAILTO=root
USER=root
PATH=/usr/bin:/bin
PWD=/root
SHLVL=1
HOME=/root
LOGNAME=root
_=/usr/bin/fastvz_backup.pl

c_backtrace:
#0  0x0000003f7540da70 in __read_nocancel () from /lib64/libpthread.so.0
#1  0x0000003f774f0990 in PerlIOUnix_read () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#2  0x0000003f774f5055 in PerlIOBuf_fill () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#3  0x0000003f774ef848 in Perl_PerlIO_fill () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#4  0x0000003f774f4cbf in PerlIOBase_read () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#5  0x0000003f774f4dfb in PerlIO_getc () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#6  0x0000003f774a29b0 in Perl_sv_gets () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#7  0x0000003f7748dbd3 in Perl_do_readline () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#8  0x0000003f7748a37e in Perl_runops_standard () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#9  0x0000003f774380ba in perl_run () from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
#10 0x00000000004017bc in main ()

perl_backtrace:
[5] Tool::get_backups_list("7xxxxx", "xxx", "7549") <- /usr/local/lib/perl/FastBackup.pm:253(FastBackup)
[4] Tool::get_last_full_backup_info("xxxx", "xxxx", "7549") <- /usr/bin/fastssad.pl:268(main)
[3] (other) <- /usr/bin/faxxx.pl:266(main)
[2] (loop) <- /usr/bin/fasxxxxx.pl:224(main)
[1] main::function() <- /usr/bin/faxxxx.pl:156(main)
Источник: http://www.slideshare.net/hirose31/inspect-runningperl


Tuesday 23 September 2014

Странные проблемы rsync при работе с флагом -z

Итак, есть 16 Гб крупным бинарным файлом.

Пробуем перелить его силами rsync без флага -z:

time rsync -a --progress /vz/private/1210 root@technode4.fv.ee:/tmp/1210
Потребление процессора разумное (32%):
395053 root      20   0  106m 1500  988 R 32.3  0.0   0:06.84 rsync                                                         
Итоги:
sent 16906158370 bytes  received 96 bytes  103 401 580.83 bytes/sec
total size is 16904094532  speedup is 1.00
real 2m42.437s
user 1m23.115s
sys 0m38.441s

Пробуем перелить с флагом -z:
time rsync -az --progress /vz/private/1210 root@technode4.fv.ee:/tmp/1210

Сразу в процессе видно, что скорость падает с 100 мегабайт/секунду до 20, а rsync начинает висеть вот в таком мерзком виде в top (да, да, он целиком выедает ядро процессора): 
391791 root      20   0  106m 1812 1020 R 99.7  0.0   0:54.36 rsync   

В итоге пришлось прервать процесс, так как он не завершился бы в разумные сроки - половина скопировалось лишь за 5 минут и скорость упала до 5 мегабайт/секунду:

real 5m54.894s
user 4m24.249s
sys 0m20.234s
В итоге данные в лучшем случае перелились бы за 10-20 минут, что уже в 4-5 раз хуже, чем вариант без использования сжатия.

Почеум так? Сжатие силами протокола gzip довольно требовательно к процессору и очень быстро упирается в производительность одного ядра, которая даже на топовых процессорах очень слабая.

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


Monday 15 September 2014

Утилита для монтирования OpenVZ ploop дисков на любой машине с Linux

Предлагаю к Вашему вниманию очередную разработку нашей компании: ploop userspace. Это утилита для монтирования дисков в формате ploop, которые используются системой виртуализации OpenVZ.

Поддерживаются оба формата хранения - v1 и v2. Поддержка записи не планируется в виду высокой сложности задачи.

Прошу: GitHub

Thursday 28 August 2014

Что делать если atop показывает не все сетевые интерфейсы?

То нажмите l в окне atop, потом прощелкайте Enter до - Maximum lines for interface statistics и забивайте там нужное число, например - 10.

Самый удобный и просто метод посчитать pps (число пакетов-секунду) на интерфейсе

Как починить падения Puppet DB от ошибок OOM killer?

Проявляется бага как:
Warning: Error 400 on SERVER: Could not retrieve facts for ovz14.fastvps.ru: Failed to submit 'replace facts' command for ovzxxx.ru to PuppetDB at xxxxxx:8081: Connection refused - connect(2)
Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Failed to submit 'replace facts' command for oxxxxs.ru to PuppetDB at xxxxx:8081: Connection refused - connect(2)


В логе (/var/log/puppetdb/puppetdb-daemon.log ) при этом:
# java.lang.OutOfMemoryError: Java heap space
# -XX:OnOutOfMemoryError="kill -9 %p"
#   Executing /bin/sh -c "kill -9 15190"...
java.lang.OutOfMemoryError: Java heap space
Dumping heap to /var/log/puppetdb/puppetdb-oom.hprof ...
Heap dump file created [225222767 bytes in 1,713 secs]
Чиним, открываем конфиг: /etc/sysconfig/puppetdb и там заменяем Xmx192m на Xmx1024m.

Применяем:
/etc/init.d/puppetdb restart
Убеждаемся, что Java завелась с новым лимитом памяти:
 ps aux|grep Xmx -i --color
puppetdb 29429 11.7  1.8 1654504 593832 ?      Sl   11:35   0:26 /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java -XX:OnOutOfMemoryError=kill -9 %p -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/puppetdb/puppetdb-oom.hprof -Djava.security.egd=file:/dev/urandom -cp /usr/share/puppetdb/puppetdb.jar clojure.main -m com.puppetlabs.puppetdb.core services -c /etc/puppetdb/conf.d

Monday 25 August 2014

perf trace - эффективная замена strace в ряде случаев!

perf trace /bin/echo "Php suxx"
     0.000 ( 0.000 ms):  ... [continued]: read()) = 0
Problems reading syscall 59(execve) information
     0.288 ( 0.000 ms):  ... [continued]: execve()) = 0
Php suxx
     0.304 ( 0.001 ms): brk(brk: 0                                                            ) = 9400320
     0.320 ( 0.002 ms): mmap(addr: 0, len: 4096, prot: 3, flags: 34, fd: 4294967295, off: 0   ) = -1309794304
     0.329 ( 0.005 ms): access(filename: 140268025023360, mode: 4                             ) = -1 ENOENT No such file or directory
     0.337 ( 0.003 ms): open(filename: 140268025016833, flags: 0, mode: 1                     ) = 3
     0.340 ( 0.001 ms): fstat(fd: 3, statbuf: 140735595271856                                 ) = 0
     0.343 ( 0.003 ms): mmap(addr: 0, len: 32186, prot: 1, flags: 2, fd: 3, off: 0            ) = -1309827072
     0.345 ( 0.001 ms): close(fd: 3                                                           ) = 0
     0.355 ( 0.003 ms): open(filename: 140268027119178, flags: 0, mode: 0                     ) = 3
     0.357 ( 0.002 ms): read(fd: 3, buf: 140735595272280, count: 832                          ) = 832
     0.361 ( 0.001 ms): fstat(fd: 3, statbuf: 140735595271936                                 ) = 0
     0.366 ( 0.003 ms): mmap(addr: 0, len: 3750152, prot: 5, flags: 2050, fd: 3, off: 0       ) = -1315766272
     0.371 ( 0.004 ms): mprotect(start: 140268022771712, len: 2093056, prot: 0                ) = 0
     0.376 ( 0.004 ms): mmap(addr: 140268024864768, len: 20480, prot: 3, flags: 2066, fd: 3, off: 1613824) = -1312055296
     0.382 ( 0.003 ms): mmap(addr: 140268024885248, len: 18696, prot: 3, flags: 50, fd: 4294967295, off: 0) = -1312034816
     0.386 ( 0.001 ms): close(fd: 3                                                           ) = 0
     0.396 ( 0.002 ms): mmap(addr: 0, len: 4096, prot: 3, flags: 34, fd: 4294967295, off: 0   ) = -1309831168
     0.401 ( 0.002 ms): mmap(addr: 0, len: 4096, prot: 3, flags: 34, fd: 4294967295, off: 0   ) = -1309835264
     0.405 ( 0.001 ms): mmap(addr: 0, len: 4096, prot: 3, flags: 34, fd: 4294967295, off: 0   ) = -1309839360
     0.407 ( 0.001 ms): arch_prctl(option: 4098, arg2: 140268027086592, arg3: 140268027080704, arg4: 34, arg5: 4294967295) = 0
     0.459 ( 0.003 ms): mprotect(start: 140268024864768, len: 16384, prot: 1                  ) = 0
     0.467 ( 0.002 ms): mprotect(start: 140268027129856, len: 4096, prot: 1                   ) = 0
     0.472 ( 0.004 ms): munmap(addr: 140268027092992, len: 32186                              ) = 0
     0.531 ( 0.001 ms): brk(brk: 0                                                            ) = 9400320
     0.534 ( 0.002 ms): brk(brk: 9535488                                                      ) = 9535488
     0.545 ( 0.005 ms): open(filename: 140268022569584, flags: 0, mode: 140268024885264       ) = 3
     0.547 ( 0.001 ms): fstat(fd: 3, statbuf: 140268024885312                                 ) = 0
     0.552 ( 0.003 ms): mmap(addr: 0, len: 99158576, prot: 1, flags: 2, fd: 3, off: 0         ) = -1414926336
     0.555 ( 0.001 ms): close(fd: 3                                                           ) = 0
     0.577 ( 0.001 ms): fstat(fd: 1, statbuf: 140735595273776                                 ) = 0
     0.580 ( 0.002 ms): mmap(addr: 0, len: 4096, prot: 3, flags: 34, fd: 4294967295, off: 0   ) = -1309798400
     0.587 ( 0.004 ms): write(fd: 1, buf: 140268027121664, count: 9                           ) = 9
     0.592 ( 0.001 ms): close(fd: 1                                                           ) = 0
     0.596 ( 0.003 ms): munmap(addr: 140268027121664, len: 4096                               ) = 0
     0.599 ( 0.001 ms): close(fd: 2                                                           ) = 0
     0.602 ( 0.000 ms): exit_group(error_code: 0                                              

Thursday 14 August 2014

CPULIMIT, CPUUNITS и CPUS - детальное тестирование и описание параметров управляющих нагрузкой на CPU со стороне контейнеров OpenVZ

Выводы в самом конце - листайте :)

Тестовая среда: 2.6.32-042stab090.5, в контейнерах: Debian-7-x86_64 minimal. Процессор: 3.4 Ghz, число ядер: 8

На ноде выставлен режим максимальной частоты:
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

На машине 8 ядер

Два контейнера - 101 и 102, им по дефалту выдано: CPUUNITS="1000" CPUS/CPULIMIT не использованы.

Изучение поведения шедулера OpenVZ:

1) Запускаем  stress --cpu 8 на первом контейнере:
 1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 2  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 4  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
 5  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 6  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 7  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 8  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
То есть машина получила все доступные ресурсы.


2) Включаем ту же самую команду на второй машине (то есть теперь она включена на обоих). Получаем тоже самое:
 1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 2  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 4  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
 5  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 6  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 7  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 8  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
Но теперь каждый контейнер грузит отдельно ядро не более чем на 50%.

open_vestat показывает следующее:
101:    cpu: 50.1 %   
102:    cpu: 49.8 %

3) Меняем лимиты на 100:
vzctl set 101 --cpuunits 100 --save; vzctl set 102 --cpuunits 100 --save;

И 10000:
vzctl set 101 --cpuunits 10000 --save; vzctl set 102 --cpuunits 10000 --save;

Ничего при этом не меняется! Все ок, важно лишь отношение этих величин
4) Ставим неравные лимиты:
vzctl set 101 --cpuunits 100 --save; vzctl set 102 --cpuunits 200 --save;

И сразу приоритет перешел к машине 101:
101: cpu: 37.2 %
102: cpu: 62.7 %
Схема нагрузки на процы ноды:
 1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 2  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]   
 4  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
 5  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 6  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 7  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
 8  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]

5) Теперь выдаем каждой машине ровно по 1 логическому ядру, тем самым ограничивая пиковую нагрузку на ядра:
vzctl set 101 --cpus 1 --cpuunits 1000 --save; vzctl set 102 --cpuunits 1000 --cpus 1 --save;

Картина мгновенно меняется - ограничение на число ядер, которое может выедать машина включилось в действие.

1 [ 0.0%]
2 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
3 [ 0.0%]
4 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
5 [ 0.0%]
6 [| 0.7%]
7 [ 0.0%]
8 [ 0.0%]

Разделение как и прежде идеальное:
101:    cpu: 49.8 %   
102:    cpu: 49.8 %

Мегагерцы также выдаются как и ранее:
vzctl exec 101 "cat /proc/cpuinfo|grep Mhz -i"; vzctl exec 102 "cat /proc/cpuinfo|grep Mhz -i";
cpu MHz        : 3401.000
cpu MHz        : 3401.000

6) Теперь выдаем каждой машине по 2 ядра:
vzctl  set 101 --cpus 2 --cpuunits 1000 --save; vzctl set 102 --cpuunits 1000 --cpus 2 --save;

Разделение продолжает быть идеальным:
101: cpu: 49.8 %
102: cpu: 49.8 %

Картина нагрузки следующая:
1 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
2 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
3 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
4 [|| 1.3%]
5 [ 0.0%]
6 [ 0.0%]
7 [ 0.0%]
8 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||99.3%]
7) Вводим параметр cpulimit.
Для машины с 1 логическим процессором поидее поменяться нчиего не должно.
vzctl set 101 --cpus 1 --cpulimit 100 --save; vzctl set 102 --cpulimit 100 --cpus 1 --save;

Ну в общем ничего и не поменялось:
1 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
2 [ 0.0%]
3 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
4 [ 0.0%]
5 [ 0.0%]
6 [ 0.0%]
7 [ 0.0%]
8 [| 0.7%]
8) Срезаем пиковую нагрузку до 50:
vzctl set 101 --cpus 1 --cpulimit 50 --save; vzctl set 102 --cpulimit 50 --cpus 1 --save;

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

1 [||||||||||||||||||||||||||||||| 49.3%]
2 [ 0.0%]
3 [||||||||||||||||||||||||||||||| 49.7%]
4 [ 0.0%]
5 [ 0.0%]
6 [ 0.0%]
7 [ 0.0%]
8 [| 1.3%]

И мегагерцы поменялись:
vzctl exec 101 "cat /proc/cpuinfo|grep Mhz -i"; vzctl exec 102 "cat /proc/cpuinfo|grep Mhz -i";
cpu MHz        : 1700.500
cpu MHz        : 1700.500

9) Срезаем нагрузку до 10:
vzctl set 101 --cpus 1 --cpulimit 10 --save; vzctl set 102 --cpulimit 10 --cpus 1 --save;

1 [||||||| 9.9%]
5 [ 0.0%]
2 [ 0.0%]
6 [ 0.0%]
3 [ 0.0%]
7 [ 0.0%]
4 [||||||| 10.5%]
8 [| 0.7%]
И тут все работает идеально.

10) отключаем один контейнер, второй оставляем с ограничнием в 10%:
1 [||||||| 9.9%]
5 [|| 1.3%]
2 [ 0.0%]
6 [ 0.0%]
3 [ 0.0%]
7 [ 0.0%]
4 [ 0.0%]
8 [ 0.0%]
И тут все ок!

11) Что будет если сделать --cpulimit 200 при выданном одном процессоре для контейнера? Да ничего плохого - больше 100% он не съест :)

1  [                                                          0.0%]    
5  [|                                                         0.7%]
2  [                                                          0.0%]    
6  [                                                          0.0%]
3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
7  [                                                          0.0%]
4  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
8  [                                                          0.0%]

12) Выдаем двум контейнера по два проца, но при этом жестко ограничиваем нагрузку 100% одного ядра:
vzctl set 101 --cpus 2 --cpulimit 100 --save; vzctl set 102 --cpulimit 100 --cpus 2 --save;

В итоге каждая машина получит по 2 ядра, но по 1.7 Ghz (половина мощности ядра аппаратной ноды):
vzctl exec 101 "cat /proc/cpuinfo|grep Mhz -i"; vzctl exec 102 "cat /proc/cpuinfo|grep Mhz -i";
cpu MHz        : 1700.500
cpu MHz        : 1700.500
cpu MHz        : 1700.500
cpu MHz        : 1700.500

Схема нагрузки будет вот такая:
1  [                                                          0.0%]    
5  [|                                                         0.7%]
2  [                                                          0.0%]    
6  [                                                          0.0%]
3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
7  [                                                          0.0%]
4  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
8  [                                                          0.0%]
13) Выдаем полную нагрузку по обоим ядрам:
vzctl set 101 --cpus 2 --cpulimit 200 --save; vzctl set 102 --cpulimit 200 --cpus 2 --save;

Схема нагрузки меняется мгновенно:
1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
5  [||                                                        1.3%]
2  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
6  [                                                          0.0%]
3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
7  [                                                          0.0%]
4  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||99.3%]    
8  [                                                          0.0%]   
14) А если дать одному контейнеру приоритет в два раза:
vzctl set 101 --cpus 2 --cpulimit 200 --cpuunits 100 --save; vzctl set 102 --cpulimit 200 --cpus 2 --cpuunits 200 --save;

То ничего не поменяется, так как нету конкуренции за ресурс - у ноды достаточно ресурсов.

15) Устроим небольшой оверселл по ядрам:

set 101 --cpus 6 --cpulimit 600 --cpuunits 100 --save; vzctl set 102 --cpulimit 600 --cpus 6 --cpuunits 200 --save;
1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
5  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
2  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
6  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
7  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||99.3%]
4  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
8  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]

  
И при этом процессор поделится 1 к 2:
102:    cpu: 66.5 %   
101:    cpu: 33.3 %

16) Теперь рассмотрим интересный кейс - что будет какой контейнер позволит получить больше ресурсов. Контейнер с полным доступом к одному ядру или контейнер с двумя ядрами, но с лимитом суммарной нагрузки 50я % на каждое.

vzctl set 101 --cpus 2 --cpulimit 100 --cpuunits 100 --save; vzctl set 102 --cpulimit 100 --cpus 1 --cpuunits 100 --save;

1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
5  [                                                          0.0%]
2  [                                                          0.0%]    
6  [                                                          0.0%]
3  [                                                          0.0%]    
7  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
4  [                                                          0.0%]    
8  [||                                                        1.3%]
 
Разделение при этом будет следующее:
102:    cpu: 49.8 %   
101:    cpu: 49.8 %

Но схема с полным доступом к одному ядру будет предпочтительнее при использовании ПО, которое плохо распараллеливается.

17) Рассмотрим кейс, когда контейнер с большим числом ядер получает меньший вес cpuunits, чем контейнер с меньшим числом ядер
vzctl set 101 --cpus 10 --cpulimit 1000 --cpuunits 100 --save; vzctl set 102 --cpulimit 500 --cpus 5 --cpuunits 200 --save;
Получим полную прогрузку машины:
1  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||97.4%]    
5  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
2  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
6  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
3  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
7  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
4  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]    
8  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]

Приоритет возьмет свое и вторая машина получи ресурсов больше:
101:    cpu: 43.4 %
102:    cpu: 56.4 %   
Но тут нельзя сказать, что получит она ресурсов вдвое больше.

18) Этот пункт я добавил уже после публикации статьи, он исследует кейс поведения, как поведут себя контейнеры с одинаковым CPUUNITS, но разным числом процессоров, выданных контейнеру:
vzctl set 101 --cpus 8 --cpulimit 800 --cpuunits 100 --save; vzctl set 102 --cpulimit 200 --cpus 2 --cpuunits 100 --save;
А поведут они себя вот так (для чистоты эксперимента я запустил по 16 потоков утилиты stress в контейнерах):
101: cpu: 75.0 %  
102: cpu: 24.8 %
То есть, контейнер, которому дали большую процессорную мощность получит приоритет в 3-4 раза, как и ожидается. Но стоит обратить внимание, что очень сложно выдать приоритет контейнеру, у которого меньше ресурсов даже силами CPUUNITS.

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

CPUS
 
CPUS - задает число логичеcких процессоров, которые доступны контейнеру. Стоит понимать, что если выдать  контейнеру 1 ядро, то его производительность будет жестко ограничена мощностью 1 ядра хост-сервера. Даже если на хост-серверер будут свободные ядра - контейнер их использовать не сможет. Это стоит учитывать всегда. Если данный параметр не указан или установлен в нуль - это означает, что контейнер может использовать все ядра хост-сервера.

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

Также параметр CPUS имеет важное значение при использовании параметра CPULIMIT.
CPULIMIT
 
CPULIMIT - задает максимальную суммарную нагрузку ядер контейнера. Максимально возможная нагрузка контейнера определяется так: N * 100, где N = числу ядер контейнера. То есть, если у контейнера 8 логических процессоров, то максимальное значение CPULIMIT может достигать 800.

В случае 1 процессора, выданного контейнеру, все просто - 50% означает 50% нагрузки на единственное ядро, то есть максимальная нагрузка на процессор ограничивается половиной ядра хост-сервера.

Если процессоров несколько, то все резко усложняется. Например, если задать CPULIMIT=100 для контейнера с двумя процессорами, то контейнер сможет нагружать каждое из выданных ему ядер не более чем на 50%.

Если же нужно полностью дать контейнеру, скажем, 4 ядра, то ему нужно выдать (4*100) CPULIMIT 400.

Именно этот параметр отображается внутри контейнера как частота виртуального процессора (Mhz /proc/cpuinfo). Частота рассчитывается вот так: HWN_FREQ * CPULIMIT / (100 * N_CPU), где N_CPU - число процессоров, выданных контейнеру. А HWN_FREQ - тактовая максимальная частота 1 ядра ноды. Например, в случае, когда у контейнера 2 ядра и CPULIMIT=100 мы получим: 1/2 HWN_FREQ (1.7 Ghz для моей тестовой ноды с процессором 3.4Ghz)

Лимит этот жесткий, то есть если на аппаратной ноде есть ресурсы, контейнер их не получит. Стоит обращать на это внимание!

CPUUNITS
 
CPUUNITS - задает отношение выделяемого процессорного времени одному контейнеру к другому. То есть, если задать для двух контейнеров 100 и 200, то второй получит вдвое больше ресурсов. CPUUNITS идеально работает когда контейнеру выдано 1 ядро. Но в случае многопроцессорных контейнеров и серьезной нагрузки на аппаратную ноду эти отношения перестают выполняться честно и второй контейнер будет получать больше ресурсов, чем первый, но не в два раза. Это объясняется особенности шедулера CFQ. Поэтому с разделением ресурсов между контейнерами с большим числом логических процессоров есть довольно серьезные проблемы.

Кого заинтересовала тулза open_vestat - могу поделиться, это скрипт обвязка cgroup на Perl, считает потребление cpu/blkio.time/blkio.sectors и отрисовывает топ10 грузящих контейнеров.