Na jednym z serwerów współdzielonych ustanowiony jest limit procesów dla każdego konta. Oczywiście o limicie dowiedziałem się dopiero, gdy na serwerze zaczął się pojawiać błąd 500 Internal Server Error.
Akurat na tym serwerze podpiętych było kilka domen firmowych, a na każdej było założonych kilka skrzynek pocztowych. W sumie uzbierało się kilkanaście adresów e-mail, które w jednym czasie były używane przy użyciu oprogramowania Thunderbirda poprzez protokół IMAP.
Cenię sobie IMAP za jego możliwości i np. możliwość równoległej pracy kilku użytkowników na jednej skrzynce e-mail (tak, czasem się to przydaje przy prowadzeniu chociażby niewielkiego sklepu internetowego). Natomiast pewną właściwością protokołu IMAP jest to, że program pocztowy dla każdego odczytania folderu otwiera oddzielne połączenie i by najmniej nie zamyka go! Przy kilku osobach korzystających z kilku skrzynek może się uzbierać całkiem niezła ilość oddzielnych otwartych połączeń, a każde takie połączenie to oddzielny proces na serwerze. Po wyczerpaniu dostępnego limitu procesów dochodzić może do takiej sytuacji, że nie można się dostać na stronę przez www.
Rozwiązaniem będzie zamykanie starych procesów IMAP po stronie serwera. Postaram się przedstawić ostateczne rozwiązanie wraz z całym tokiem.
Lista procesów:
user@serv63:~$ ps auxw USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND user 11760 0.0 0.0 8408 1504 ? S 07:22 0:00 sshd: user@pts/0 user 11764 0.0 0.0 6756 3732 pts/0 SNs 07:22 0:00 -bash user 15117 0.0 0.0 15428 3796 ? S 07:29 0:00 imap [email321@domenajeden.com.pl 83.11.11.11] user 15118 0.0 0.0 5100 2076 ? S 07:29 0:00 imap [email543@domenadwa.pl 83.11.11.11] user 15119 0.0 0.0 7240 2524 ? S 07:29 0:00 imap [email654@domenatrzy.pl 83.11.11.11] user 21214 0.0 0.0 4212 1740 ? S 07:42 0:00 imap [email098@domena4.com.pl 83.11.11.11] user 25117 0.0 0.0 3888 1472 ? S 07:52 0:00 imap [email098@domena4.com.pl 83.11.11.11] user 29872 0.0 0.0 4036 988 pts/0 RN+ 08:04 0:00 ps auxw
W tym przykładzie (po wywołaniu komendy ps auxw) akurat jest niedużo procesów, ale natomiast widać, że jest kilka procesów, które mają więcej niż 30 minut (a mogą mieć znaaaaacznie więcej). Należałoby stworzyć skrypt uruchamiany cyklicznie z crona, który zamykałby procesy o wieku dłuższym niż 30 minut. Nie będzie to miało jakiegokolwiek wpływu na pracę zwykłych użytkowników, program pocztowy po prostu nawiąże nowe połączenie przy kolejnej czynności.
Wylistowanie id, wieku i nazwy aktualnych procesów:
user@serv63:~$ ps -eo pid,etime,comm PID ELAPSED COMMAND 7983 21:18 imap 7996 21:17 imap 7997 21:17 imap 8003 21:15 imap 8353 20:20 imap 8557 19:54 imap 8563 19:54 imap 8564 19:54 imap 8565 19:54 imap 10484 14:36 imap 10485 14:36 imap 10486 14:36 imap 12700 08:36 imap 13256 06:53 imap 13257 06:53 imap 13258 06:53 imap 13259 06:53 imap 13261 06:53 imap 13262 06:53 imap 14787 02:14 imap 14800 02:12 imap 15193 01:19 imap 15194 01:19 imap 15715 00:00 ps 29997 52:12 sshd 29998 52:12 bash
Parametry:
- e – wypisuje wszystkie procesy
- o – format podawanych danych
pid – identyfikator procesu,
etime – czas od kiedy został uruchomiony, w formacie [[dd-]hh:]mm:ss,
comm – nazwa polecenia
Następnie należałoby wybrać tylko procesy imap i tylko starsze niż 30 minut:
ps -eo pid,etime,comm | awk '((length($2) > 5) || $2~/^[3-5]/) && $3~/imap/ { print $1 }' | xargs kill -15
Po znaku | informacje dalej przekazywane są do programu awk, który dokonuje operacji wyszukiwania na ciągach znaków, a następnie proces jest zamykany programem kill.
- zmienne $1, $2, $3 – to zmienne pobrane z poprzedniego programu (id, wiek, nazwa procesu)
- (length($2) > 5) || $2~/^[3-5]/) – pierwszy warunek sprawdza, czy aby czas nie ma więcej znaków niż 5 co oznaczałoby, że jest to ponad godzina (hh:mm:ss), drugi warunek przy użyciu wyrażenia regularnego sprawdza, czy aby rozpoczyna się od cyfry 3,4 lub 5
- $3~/imap/ – nazwa procesu musi nazywać się imap
- { print $1 } – zwraca pierwszą zmienną, czyli identyfikator procesu
- xargs kill -15 – powoduje przekazanie zwróconego identyfikatora do programu kill zamykającego proces.
Parametr -15 powoduje, że zostaje wywołane polecenie zamknięcia procesu i ma możliwość „posprzątania po sobie”, natomiast gdyby użyć -9 to proces od razu i bezapelacyjnie jest zamykany „na tzw. chama”
Ten skrypt działa prawidłowo w przypadku, gdy znajdzie jakieś stare procesy do zamknięcia. Natomiast, gdy takowych nie znajdzie, wówczas program kill wyrzuca standardową informację o braku identyfikatora procesu do zamknięcia, a w przypadku umieszczenia tego w zadaniach crona, każdy wynik tekstowy polecenia wysyłany jest na e-mail administratora. Tak więc aby uniknąć niepotrzebnego zaśmiecania skrzynki należy ostatecznie zmodyfikować skrypt:
for pid in $(ps -eo pid,etime,comm | awk '((length($2) > 5) || $2~/^[3-5]/) && $3~/imap/ { print $1 }'); do kill -15 $pid; done
Na koniec pozostaje tylko umieszczenie go w zadaniach cron:
crontab -e */15 7-18 * * 1-5 for pid in $(ps -eo pid,etime,comm | awk '((length($2) > 5) || $2~/^[3-5]/) && $3~/imap/ { print $1 }'); do kill -15 $pid; done
Oznacza to, że skrypt uruchamiać się będzie co 15 minut w godzinach między 7 a 18 od poniedziałku (1) do piątku (5). Ewentualnie całość można sobie zapisać gdzieś do pliku gdzieś, a w cronie wywoływać zawartość pliku.