Балансировщик соотношения входящего/исходящего трафика linux-сервера на PHP

в 8:32, , рубрики: administration, colocation, linux, php, системное администрирование, метки: , , ,

Как я некоторое время назад выяснил на собственном опыте, ещё остались провайдеры, для получения безлимитного тарифа от которых требуется выдерживать определённое соотношение входящего и исходящего трафиков.
В моём случае, при размещении сервера на colocation в одном московском датацентре, исходящего должно было быть не менее чем в 4 раза больше чем входящего (уж извините за такой оборот). Соотношение рассчитывается как за сутки, так и общее за месяц. Нарушение любого из них — штраф.

Само по себе соотношение не выдерживалось из-за переодических объёмных заливок бэкапов на сервер. Вручную (гиг заслал, 4 гига скачал) — надоело.

Тут я вспомнил, что когда-то давно на каком-то сайте видел примитивный скрипт в пару строк на sh-скрипте, который делал такую балансировку сам. Тогда не сохранил, сейчас найти не смог. Пришлось писать самому. Результат и выставляю на всеобщее обозрение — авось кому пригодится.

Скрипт тестировался только под Ubuntu, но и под другими линуксами работать должен.

В отличие от того примитивного на sh, мой скрипт умеет выдерживать как дневное, так и месячное соотношение, ведёт логи, и тщательно шифруется от своего обнаружения (фальшивый трафик делается путём передачи файлов случайного размера через SSH, между вбросами делается интервал случайной длины). Ещё одно отличие (но уже со знаком минус) — требуется ответная нода с безлимитным трафиком, до которой можно поднять ssh соединение по ключу. Я использую свой домашний канал и роутер на нём.

Поставьте свои значения в переменные $interface, $logfile и $server.
В переменной $server фактически указаны параметры для команды ssh.
То есть, в моём случае, выполнив в консоли «ssh my-host.no-ip.org -p 1022 -i /home/user/.ssh/my-key» я подключаюсь без ввода пароля к своей ответной ноде (данные условные, конечно).
Вызов этого скрипта надо сделать при запуске системы (например из /etc/rc.local) или настроить в виде сервиса.

<?php
$interface="eth0"; // какой интерфейс отслеживать
$server="my-host.no-ip.org -p 1022 -i /home/user/.ssh/my-key"; // куда валить мусор
$logfile="/usr/local/logs/trafic-balancer.log"; // для всего скрипта перенаправление вывода делать нельзя, так как logrotate прибивает файл
$curdate=0;

function echolog($str)
{
  global $logfile;
  file_put_contents($logfile, $str, FILE_APPEND);
  echo($str);
}

echolog("n".date('r')." tЗапускn");
while(true)
{
	$f=@fopen("/proc/net/dev", "r");
	$stat=null;
	
	while($str=fgets($f))
	{
		if(strpos($str, $interface)!==false)
		{
			$stat=$str;
			break;
		}
	}
	fclose($f);
	
	if($stat)
	{
		$stat=preg_replace("/s+/", " ", $stat);
		$nums=explode(" ", $stat);
		// трафик глобальный с пересчётом в мегабайты
		$rx_g=round($nums[2]/1024/1024);
		$tx_g=round($nums[10]/1024/1024);
		$delta_g=$rx_g*4-$tx_g;
		// трафик дневной
		if($curdate!=date('j')) // начинается новый день
		{
			if($curdate)
				echolog("n".date('r')." tРезультаты за $curdate число: RX:$rx_d Mb, TX:$tx_d Mb, дельта: $delta_d Mbn");
			$curdate=date('j');
			$rx_d=0;
			$tx_d=0;
			$rx_s=$rx_g;
			$tx_s=$tx_g;
		}
		$rx_d=$rx_g-$rx_s;
		$tx_d=$tx_g-$tx_s;
		$delta_d=$rx_d*4-$tx_d;
		

		$delta=max($delta_g, $delta_d);

		if(($delta>0)&&(date('G')>8))
		{
			if($delta>1000) // если больше 1Г, то бьём на части
				$size=round(rand(1000, min($delta/2, 5000))); // за один раз посылаем блок произвольного размера, но не слишком здоровый
			else
				$size=round(rand($delta*2, $delta*3)); // добиваем остаток с запасом чтоб кусками мелкими не качать
			echolog("n".date('r')." tПревышение: общая дельта: $delta_g Mb, дневная дельта: $delta_d Mb, качаем $size Mbn");

			passthru("dd if=/dev/zero bs=1M count=$size | ssh $server 'cat > /dev/null'");
		}
		else echolog('.');
	}
	  else 
		echolog("n".date('r')." tСтатистика не читается!n");
	sleep(round(rand(10, 600)));
}	
?>

Автор: unwrecker


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js