Bash czeka na sukces pingowania

10

Piszę na skrypcie restartującym różne serwery. Po ponownym uruchomieniu chcę „poczekać”, aż wszystkie serwery powrócą do trybu online. (Dla uproszczenia zdefiniowałem dla niego online = możliwość pingowania)

Tak więc dla każdego serwera

ServerXY_W=1
echo -n "waiting for ServerXY ..."
while (($ServerXY_W == 1))
do
   if ping -c 1 -w 0.2 192.168.123.123 &> /dev/null
   then
      echo "ServerXY is back online!"
      ServerXY_W=0
   else
      echo -n "."
   fi
done

Czego bym się spodziewał (i lubił) to wynik taki jak np

waiting for ServerXY .................
ServerXY is back online!

gdzie kropki ... pojawiałyby się jedna po drugiej.

Ale to, co się faktycznie dzieje, jest pierwsze

waiting for ServerXY ...

przez jakiś czas i kiedy serwer powraca, dostaję ostatnią kropkę i ostatnią linię jak

waiting for ServerXY ....
ServerXY is back online!

Dlaczego pętla while jest wykonywana tylko dwa razy, tak jak raz z niepowodzeniem ping i raz z pingiem? Co muszę zmienić, aby dodać więcej kropek w pętli while?

Zrobiłem test również z nieistniejącym adresem IP. Ale utknęło

waiting for NonExistentServer...

i oczywiście nigdy nie wygasły. Ale to samo pytanie, dlaczego nie ........zostanie dodane?

derHugo
źródło
Działa dobrze dla mnie ...: /
Ravexina

Odpowiedzi:

9

Problem

Problem polega na tym, że ustawiłeś -w 0.2. Gdy wartość jest mniejsza niż 1, wartości deadline ( -w) i timeout ( -W) są ignorowane. Zostało to wspomniane wcześniej w tym pytaniu . Kiedy używasz -w 1, twój skrypt (który nieco zmodyfikowałem, aby usunąć niepotrzebne bity) działa poprawnie:

$ ./ping_server.sh                                                 
waiting for ServerXY ....................
Server is back online

$ cat ./ping_server.sh
#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! ping -c 1 -n -w 1 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"

Rozwiązanie

Oczywistym rozwiązaniem jest użycie -w 1. Jeśli zamierzasz używać wartości mniejszej niż 1 sekunda, timeoutpolecenie powinno być lepsze:

$ timeout 0.2 ping -c 1 147.153.237.192                            
PING 147.153.237.192 (147.153.237.192) 56(84) bytes of data.
64 bytes from 147.153.237.192: icmp_seq=1 ttl=124 time=2.61 ms

--- 147.153.237.192 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.612/2.612/2.612/0.000 ms

Ponownie użyj go z !operatorem w pętli:

#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! timeout 0.2 ping -c 1 -n 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"

Oczywiście odwrotność może być zastosowana do wyświetlania wiadomości tylko wtedy, gdy serwer jest włączony i informuje o awarii serwera, przykład:

$ while ping -q -c 1 172.16.127.2 >/dev/null ; do sleep 1; done ; echo "Server stopped responding"
Server stopped responding

Pamiętaj jednak, że nie jest to idealne:

  • pingujemy przy użyciu tylko 1 pakietu na sekundę. Niska przepustowość, słaba łączność, zły sprzęt między serwerem a klientem pingujący serwer spowoduje wyzwolenie pętli i fałszywe pozytywne powiadomienie

  • Opieramy się na pingowaniu, czyli wykorzystaniu echa ICMP. Zapory ogniowe, a nawet pojedyncze serwery blokują odpowiedzi na echo ping / ICMP. Można korzystać ncz ncat(który jest udoskonaloną wersją nc). Coś jak w powyższej pętli będzie działać dobrze zamiast ping:

    nc -w5 -z 172.16.127.2 80

    To robi to połączyć się z serwerem na 172.16.127.2 na porcie 80. -zto uniknąć I / O - po prostu podłącz i rozłącz. -wodczeka 5 sekund przed zgłoszeniem nieudanego połączenia. Oczywiście jest to całkiem dobre, gdy masz serwer pod kontrolą i wiesz, że port 80 jest otwarty. Z UPD można dobrze korzystać, ale jeśli zainstalowana jest zapora ogniowa, prawdopodobnie preferowany jest protokół TCP.

    Ukrytą korzyścią jest to, że jeśli masz usługę działającą na określonym porcie (np. HTTP na porcie 80 lub RTSP na 554), niepowodzenie połączenia z portem może służyć jako wskaźnik, że twoja usługa wymaga ponownego uruchomienia.

  • Oczywiście nci pingmoże być trochę spamerski. Lepszym sposobem byłoby zameldowanie się na serwerze z innym serwerem centralnym, wysyłanie okresowego raportu, może co godzinę; w ten sposób, jeśli twój serwer straci „czas uderzenia”, możesz generować błędy. Lepszym sposobem jest skorzystanie z usługi takiej jak Nagios, która to robi. Ale w tym momencie wchodzimy w sferę obliczeń na poziomie przedsiębiorstwa z wieloma serwerami. Jeśli masz w domu coś takiego jak Raspberry Pi, prawdopodobnie nie potrzebujesz niczego złożonego.

Sergiy Kolodyazhnyy
źródło
Cześć, dziękuję bardzo za wyjaśnienie tej rzeczy! Czy istnieje inny sposób niż robienie tego w warunkach pętli? Jest idealny do oczekiwania na jeden serwer, ale jak wspomniano, czekam na wiele serwerów później Zrobię coś takiego, while (( $ServerA_W==1 || $ServerB_W==1 || .....))co wstrzyma się, gdy każdy serwer wróci.
derHugo
Gdy np. Jeden serwer powróci i czeka na pozostałe, nie chcę
pingować
Sugeruję napisanie tego jako funkcji i uruchomienie instancji każdej funkcji z adresem IP jako argumentem w tle. Ale odradzam drukowanie kropek, po prostu pozwól każdej funkcji wydrukować komunikat, gdy serwer się uruchomi. Daj mi znać, jeśli chcesz, żebym napisał przykład takiej funkcji
Sergiy Kolodyazhnyy
1
@Joanne Tak, to możliwe. Mogę zaktualizować swoją odpowiedź później dzisiaj lub jutro. Osobiście nie
wysyłałbym
1
@Joanne Zobacz moje aktualizacje w odpowiedzi. Daj mi znać, czy to pomoże lub czy są jakieś inne pytania
Sergiy Kolodyazhnyy,