ugrás a tartalomhoz

Apache2 - workerek szép lassan egyesével beragadnak, majd a kiszolgálás leáll.

mATTIAS · 2010. Júl. 20. (K), 18.09
Sziasztok!

Nem tudok dűlőre jutni az alábbi problémával, valaki már találkozott vele, vagy sejti a megoldást?

Adott egy élesben futó apache2 webszerver, ami működik szépen 3-4 órát, majd szépen lassan elkezdenek kifagyni* a workerek. Ezután a kéréseket nem tudja milyen processznek adni az apache s bekerülnek egy várólistába, így a kiszolgálás végtelen sorbanállási idő végett használhatatlan.
Jelenleg két óránként egy cronjob újraindítja a szervert. (GRACEFUL nem használ)

*8.000-10.000 másodperce nem vesznek fel új feladatopt kiszolgálásra...

A PHP szkriptemben keressem a hibát, vagy nincs összefüggés?
Létezik szkript, ami kilövi a beragadt workereket, vagy nekem kéne megírnom?



A szerver adatai a következők:
Apache Server Status for IP :)
Server Version: Apache/2.2.9 (Debian) PHP/5.2.6-1+lenny8 with Suhosin-Patch (PREFORK)
Server Built: Mar 28 2010 18:03:05
Current Time: Tuesday, 20-Jul-2010 17:55:19 CEST
Restart Time: Tuesday, 20-Jul-2010 15:10:04 CEST
Parent Server Generation: 0
Server uptime: 2 hours 45 minutes 15 seconds
Total accesses: 44797 - Total Traffic: 563.9 MB
CPU Usage: u6.71 s.9 cu0 cs0 - .0768% CPU load
4.52 requests/sec - 58.2 kB/second - 12.9 kB/request
96 requests currently being processed, 49 idle workers
WWWWWWWWWWW_WWWWWWWW.WWWWWW_W_WWWW_WW__WW_WWWWWWWWWW_W_WWWWW_WWW
__.W_W_WWWWW_W_WW__WWWC_WW_WWWWWWWW_WWWW___WW____W___W__WWW__W__
W_W_W__..W..W___...W..W_____....................................
................................................................

Scoreboard Key:
"_" Waiting for Connection, "S" Starting up, "R" Reading Request,
"W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup,
"C" Closing connection, "L" Logging, "G" Gracefully finishing,
"I" Idle cleanup of worker, "." Open slot with no current process
Srv PID Acc M CPU SS Req Conn Child Slot



Itt a processzek nagyrészénél az "SS"* mező már több ezres érték, s többet már nem is dolgoznak. A maradéknál 10 alatti , s még tökéletesen működnek

*Seconds since beginning of most recent request.


-----------------------------------------------------------------------------
-----------------------------------------------------------------------------

Az MPM beállítása.
<IfModule mpm_prefork_module>
StartServers 100
MinSpareServers 50
MaxSpareServers 100
MaxClients 1000
MaxRequestsPerChild 5
</IfModule>
 
1

Keepalive?

janoszen · 2010. Júl. 20. (K), 20.38
A keepalive nincs veletlen valami borzalmasan magas ertekre tekerve?

A masik tippem, hogy slow request tamadas aldozata vagy, az ugyanis hasonlo jelenseget produkal. Ez ellen a mindenfele timeoutok betekerese segithet.

Esetleg tamadd meg egy straceval az egyik Apache szalat, lassuk, mit csinal.

Egyebkent meg szerintem, frissitsd meg a szoftvereidet, mert egyik sem mai darab, lehet, hogy ez onmagaban is megoldja a problemat.
2

A keepalive nem lehet

mATTIAS · 2010. Júl. 21. (Sze), 12.11
A keepalive egy az egyben ki van kapcsolva. (én is gondoltam rá)

Arra van egysoros válasz hogyan eresztek strace-t egy adott id-jű processzre?

Nekiállok a szoftverek frissítésnek, de éles rendszernél bajos :)
Csinálok egy másolatot előbb azon letesztelem.
3

Strace

janoszen · 2010. Júl. 21. (Sze), 15.39
strace -p pid

Ha nagyon nem boldogulsz, keress meg privatban.
4

Update

mATTIAS · 2010. Júl. 22. (Cs), 17.31
A "slow request" DOS valóban bajos lehet az én verziómmal:
http://security-tracker.debian.org/tracker/TEMP-0533661-015545


A strace az alábbit mondja:
server:~# strace -p 13981
Process 13981 attached - interrupt to quit
restart_syscall(<... resuming interrupted call ...>) = 0
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 7000
5

poll

janoszen · 2010. Júl. 22. (Cs), 18.23
A poll hívás a megadott socketen eventre vár. Ergó esélyes, hogy a slow request támadás áldozata vagy. Esetleg állítsd be ezt:

Timeout 90

Nézd meg, ettől jobb lesz-e. Ha nagyon vakmerő , nézd meg a mod_security és mod_evasive modulokat.
6

re

mATTIAS · 2010. Júl. 22. (Cs), 19.52
A Timeout eleve 10-re van állítva. (csak kis fájlokkal dolgozik a szerver)

A belinkelt debain jelentés alapján egyértelműen kijelenthető hogy egy 2.2.15-re frissítéssel védve vagyok a támadás elől, vagy szórakoznom kell az evasive befordításával?

Egyébként nagyon köszi a segítséget! :)

Most nézem a link nem is működik.
Elvileg ez:
http://security-tracker.debian.org/tracker/TEMP-0533661-015546
7

Bármi lehet

janoszen · 2010. Júl. 22. (Cs), 21.22
Bármi lehet, Debianék jó szokás szerint valszeg szanaszét patchelték az Apache-ot, úgyhogy meg kell nézni. Én személy szerint sem a Debiant, sem az Apacheot nem szeretem annak ellenére, hogy mindkettővel mélységeiben volt és van is dolgom. (Apacheba fejlesztek is.) Nem tudom, milyen szolgátlatást futtatsz, de lehet, hogy érdemes lenne megnézni majd a LigHTTPd-t vagy az nginx-et.
8

Egy lehetséges megoldás

mATTIAS · 2010. Júl. 26. (H), 14.15
Nem túl elegáns módon tüneti kezeléssel oldottam meg a problémát.

Percenként lefut egy PHP szkript a szerveren, ami az apache status modulja alapján megállapítja a "befagyott" (SS > 500) apache childrenek pid-jét, s kilövi őket.

ha nem érné el a /server-status-t újraindítja az apache-t. (még nincs implementálva...)

A problémát megoldotta, már 26 órás uptime van apache restart nélkül. (eddig max 2 órát bírt a slotok beteléséig.)


Ha valakinek hasonló problémája lenne, én ezt a kódot "csaptam össze" hozzá:
php5-cli + cronjob + apache2 extended status kombóval működik.

A parancs hozzá:
/php/executable -f /php/thisscript.php

<?php
########################################
## ApacheProcessMonitor CLASS
########################################

class ApacheProcessMonitor
	{
		var $url;
		var $source;
		var $tempSource;
		var $apacheChildrens;
		
		
		function ApacheProcessMonitor($_url)
			{
				$this->url = $_url;					
			}
			
		function GetSource()
			{
				$this->source = file_get_contents($this->url);
			}
			
		function ShowStateAndKillProblematics()
			{
				for($i=0;$i<count($this->apacheChildrens);$i++)
					{
						
						if($this->apacheChildrens[$i]->isWorking())
							{
								
								if($this->apacheChildrens[$i]->isProblematic())
									{
										echo "\r\n" .'==> ';
									}
									
								$this->apacheChildrens[$i]->ShowState();
								echo "\r\n";
					
							
								if($this->apacheChildrens[$i]->isProblematic())
									{
										$this->apacheChildrens[$i]->kill();
									}
							}
					}
			}
			
		function TableToArray($string)
			{
				#####################################################
				##  Trim functions
				#####################################################
				
				//remove text before $needle in $string
				function TrimBefore($needle,$string)
					{
						$temp = explode($needle,$string,2);
						return($temp[1]);
					}
				
				//remove text after $needle in $string
				function TrimAfter($needle,$string)
					{
						$temp = explode($needle,$string,2);
						return($temp[0]);
					}
					
				//remove text before $needle1 and after $needle2 in $string, so between the two needles is remaning
				function TrimBetween($needle1,$needle2,$string)
					{
						return(TrimAfter($needle2,TrimBefore($needle1,$string)));
					}
				
				#####################################################
				##  Trim functions END
				#####################################################
				
				
				$temp = explode('<tr',$string);
				
				foreach ($temp as &$value)
					{
						$value = TrimBetween('>','</tr>',$value);
					}
					
				foreach ($temp as &$value)
					{
						$value = explode('<td',$value);
						
						foreach ($value as &$value2)
							{
								$value2 = TrimBetween('>','</td>',$value2);
							}
					}
				
				return($temp);
			}

			
		function HTMLtoDataStruct()
			{
			
				$this->tempSource = explode('<table border="0">',$this->source);
				$this->tempSource = $this->tempSource[1];
			
				$this->tempSource = explode('</table>',$this->tempSource);
				$this->tempSource = $this->tempSource[0];
				
				$this->tempSource = '<table border="0">' . $this->tempSource;
				$this->tempSource = $this->tempSource . '</table>';
				
				$this->tempSource = $this->TableToArray($this->tempSource);
				
				for($i=3;$i<count($this->tempSource)+1;$i++)
					{
						$this->apacheChildrens[$i-3] = new ApacheChildren($this->tempSource[$i-1][2],trim(strip_tags($this->tempSource[$i-1][4])),$this->tempSource[$i-1][6],$this->tempSource[$i-1][13]);
					}
					
				$this->tempSource = '';
				
			}
			
		
	
	
	}

########################################
## ApacheChildren CLASS
########################################
	
class ApacheChildren
	{
		var $pid;
		var $state;
		var $ss;
		var $request;
		var $problemLimit = 600;
		
		function ApacheChildren($pid,$state,$ss,$request)
			{
				$this->pid     = $pid;
				$this->state   = $state;
				$this->ss      = $ss;
				$this->request = $request;
			}
			
		function ShowState()
			{
				echo $this->state . ' - Since:' . $this->ss . ' seconds.' . ' --- ' . $this->pid . ' - ';// . $this->request;
			}
			
		function isWorking()
			{
				if($this->state == "W")
					{
						return true;
					}
				else
					{
						return false;
					}
			}
			
		function isProblematic()
			{
				if($this->isWorking() && $this->ss > $this->problemLimit)
					{
						return true;
					}
				else
					{
						return false;
					}
			}
			
		function kill()
			{
				echo '====> Killing process No. ' . $this->pid . "\r\n\r\n";
				
				$cmd = "kill " . $this->pid;
				shell_exec($cmd);
				
			}
	}

########################################
## PROGRAM CODE
########################################
	
$ApacheCleanup = new ApacheProcessMonitor("http://YOUR.IP/server-status");
$ApacheCleanup->GetSource();
$ApacheCleanup->HTMLtoDataStruct();
$ApacheCleanup->ShowStateAndKillProblematics();

?>