Tworząc oprogramowanie i definiując wymagania serwerowe dla administratora, bardzo często dołączany jest tam punkt "potrzebny cron". I od razu mała dygresja - niektórzy powiedzą, że wskazywanie tej pozycji osobno jest bezsensowne, gdyż jest to klasyczny element każdego systemu na serwerze. Może i tak, ale:
- pamiętamy w firmie sytuacje, w których administrator nie przygotował crona, gdyż "uznał, że nie jest potrzebny",
- nawet, jeśli był, nie zawsze był do niego dostęp.
Rzeczywiście, jeśli to jeden z podstawowych składników serwerów, ale przede wszystkim niezwykle potrzebne narzędzie w większości aplikacji sieciowych. Z tego powodu jego używanie jest tak naturalne, jak tworzenie kolejnych endpointów API czy zapewnienie kolejnych funkcji w aplikacji klienckiej. A przynajmniej tak się wydaje - programistom (szczególnie bardziej doświadczonym) nazwa "cron" jest tak znana, że aż stała się synoninem wszystkich podobnych mechanik, niezależnie od tego, jak one się nazywają w danym środowisku czy technologii. Ale trzeba pamiętać, że bardzo młodzi adepci informatyki lub klienci nie zawsze mogą rozumieć ten slang. A nawet, jeśli rozumieją, to nie zawsze wykorzystanie crona jest takie oczywiste - jakieś gwiazdki, liczby, nic nie działa jak trzeba...
Dlatego w tym artykule wytłumaczymy, czym jest cron, kiedy się go wykorzystuje i jak to zrobić. Tekst jest przeznaczony raczej dla początkujących, ale nie wykluczamy, że parę zamieszczonych informacji, które dotyczą bardziej kwestii aplikacyjnych, przyda się również lub zainspiruje osoby bardziej zaawansowane.
Co to jest cron?
Najprościej mówiąc, cron to program do cyklicznego uruchamiania zaplanowanych zadań. Jest to narzędzie typowo linuksowe, a właściwie powinniśmy powiedzieć "uniksowe" - powstało w maju 1975 roku w firmie AT&T Bell Laboratories (notabene, jak cały UNIX), która obecnie nazywa się Nokia Bell Labs. I tak, cząstka "Bell" nie jest przypadkowa, gdyż fundamenty tej zasłużonej instytucji powstały za sprawą znanego wszystkim Alexandra Grahama Bella pod koniec XIX wieku. Sama nazwa programu pochodzi od greckiego słowa chronos oznaczającego czas. Ale do rzeczy.
Wyobraźmy sobie sytuację, w której pewne zadania system musi wykonać bez naszej ingerencji. Przykładem może być generowanie pewnego raportu co noc i wysyłanie go do użytkowników. Wykorzystywanie do tego człowieka, aby zawsze o określonej godzinie znalazł się przy komputerze i kliknął określony przycisk, jest bezcelowe i męczące. Lepiej zdać się na system komputerowy, który działa cały czas i o odpowiedniej porze zrobi to za nas. Do tego właśnie służy cron - możemy za jego pomocą definiować, o której (lub co ile) ma wykonać określone akcje. Technicznie rzecz biorąc, jest to serwis (w Linuksie nazywany demonem, ang. daemon), który działa w tle i cały czas sprawdza listę zadanych operacji pod kątem tego, czy powinien je teraz uruchomić.
Cron jest tak naprawdę pierwszym programem tego typu i doczekał się kilku implementacji, wśród których popularny jest np. fcron. Jednak w praktyce, gdy zostanie wskazana chęć posiadania lub użycia crona, to wszyscy wiedzą, o co chodzi i rzadko wymagana jest decyzja, która konkretna implementacja powinna zostać zainstalowana. Warto natomiast odróżnić termin "cron" od "crontaba" - to drugie to program lub tabele programu cron, z których pobierane są definicje zadań. Możemy sobie wyobrazić crontaby jako listy działań do wykonania zapisane przez różne osoby mające dostęp do serwera. Z tego powodu ich definiowanie odbywa się właśnie poprzez polecenie crontab
, gdzie każdy operator systemu ma swoją listę, którą potem serwis crona zbiera i uruchamia na uprawnieniach tej osoby.
Jak wygląda definiowanie zadań?
Z poprzedniego akapitu dowiedzieliśmy się, że aby zdefiniować zadania crona, powinniśmy skorzystać z polecenia crontab
, a dokładnie crontab -e
, które pozwala edytować wpisy. Dzieje się to za pomocą edytora tekstowego, w związku z czym przy pierwszej próbie skorzystania z tej komendy użytkownik może zostać zapytany przez system, jaki program chce wykorzystać. Jest to kwestia wyłącznie wygody i przyzwyczajeń - niżej podpisany zawsze wybiera Vima, aczkolwiek większość pozostaje przy prostym nano.
Tablica składa się z wpisów, które muszą zachować następującą strukturę:
m h dom mon dow command
Jak widać, każde zadanie będzie składało się z sześciu kolumn, z których pierwsze pięć określają moment lub częstotliwość wykonania, a ostatnia informacja to konkretne polecenie. I tutaj właśnie dzieje się magia w postaci tych dziwnie wyglądających linijek z gwiazdkami. Jest to jednak stosunkowo proste do zrozumienia.
Te pięć pierwszych kolumn należy interpretować w następujący sposób:
m
- minuta, zakres od 0 do 59h
- godzina, zakres od 0 do 23dom
- dzień miesiąca, zakres od 1 do 31mon
- miesiąc, zakres od 1 do 12dow
- dzień tygodnia, zakres od 0 do 7, gdzie niedziela jest zarówno pod 0, jak i 7, poniedziałek to 1 itd.
Zwykle nie wszystkie kolumny chcemy uzupełnić konkretnymi wartościami i bardzo często będziemy wykorzystywać znaki specjalne. Cron pozwala na wstawienie:
- gwiazdki (
*
) - oznacza każdą wartość, a więc np. "o każdej minucie". - łącznika (
-
) - oznacza zakres wartości, a więc np.1-3
to "gdy wartość jest od 1 do 3". - slasha (
/
) - służy do określenia częstotliwości, np.*/2
to "co dwie wartości". Przed ukośnikiem nie musi być gwiazdki i można dodatkowo zawęzić zakres, np. pisząc6-16/2
mamy na myśli "od wartości 6 do 16, ale co dwie jednostki (czyli 6, 8, 10, 12, 14, 16)". - przecinka (
,
) - pozwala podać konkretne wartości, jeśli nie chcemy lub nie możemy używać zakresu. Przykładowo,2,5
oznacza "o wartości 2 lub 5".
Jest to całkiem potężne narzędzie, ale dobrze pamiętamy, że na początku korzystania z niego można mieć pewne wątpliwości, jak w praktyce definiować czasy. Możemy sobie to wyobrazić tak, że system co minutę sprawdza, czy obecny czas pasuje do jakiejś definicji - jeśli tak, wykonuje to zadanie. Aby lepiej to zrozumieć, przyjrzyjmy się kilku przykładom (dość sztucznym, ale ciekawym):
0 10 * * * php ~/script.php
- dokładnie o 10:00 (m = 0, h = 10) każdego dnia (dom, mon, dow = *) wykonaj skrypt script.php
znajdujący się w katalogu domowym użytkownika. Przy tej okazji warto nadmienić, że znak ~
(tylda) odniesie się właśnie do użytkownika, który zdefiniował zadanie, tj. w którego tabeli crontab znajduje się ten wpis. Gdy nie chcemy popełnić błędu, warto używać ścieżek bezwzględnych.
*/30 6 * * 1,5 cp ~/file1.txt ~/copy.txt
- w poniedziałki i piątki (1 oraz 5), gdy wybije godzina szósta (6), to co pół godziny (*/30) wykonaj kopię pliku file1.txt
do copy.txt
. W praktyce w ciągu tygodnia ta operacja zostanie uruchomiona cztery razy - w poniedziałek o 6:00 oraz 6:30 i w piątek o tych samych porach. Tylko te momenty pasują do podanej definicji czasu.
22 * * * * mysqldump -u user -d db > ~/"backup_$(date +\%Y-\%m-\%d_\%H-\%M-\%S).sql"
- co godzinę o minucie 22 (a więc 14:22, 15:22, 16:22 itd.) każdego dnia wykonaj kopię zapasową bazy MySQL do danego pliku. Przy tej okazji można zobaczyć, że w komendzie można używać zmiennych i wewnętrznych poleceń, tak jak np. daty, aby odpowiednio nazwać wynikowy plik. Trzeba tylko pamiętać o odpowiednim formatowaniu danych. Warto zajrzeć do tego wątku oraz przy okazji poznać polecenie date
.
0 20 1 3 * php ~/report.php
- dokładnie 1 marca o 20:00 każdego roku (bez względu na dzień tygodnia) wykonaj skrypt z raportem.
Jak widać, instrukcje potrafią być czasem zawiłe, ale opanowanie ich to kwestia praktyki. I spokojnie - jeśli nadal trochę się gubicie, to nie jesteście jedyni. Z myślą o początkujących w świecie crona, powstały różne interfejsy pomagające wyklikać pożądane czasy, a także generator z przykładami w postaci crontab.guru/.
Na koniec warto wspomnieć o tym, że jeśli nie chcemy już wykonywać jakiegoś zadania, ale nie jest nam na rękę jego usunięcie, to pamiętajmy, że edytowanie crontaba to działanie na pliku tekstowym - taką linijkę możemy zakomentować, wstawiając hasz (#
) na początku linii.
Rodzaje dostępu do crona
Warto też nadmienić, iż w zależności od hostingodawcy i rodzaju serwera dostęp do definiowania zadań crona może być inny. Nie zawsze administrator udostępni polecenie crontab
- czasem jest to interfejs webowy, w którym wybieramy opcje z listy, a niekiedy są to wytyczne dotyczące umieszczania plików tekstowych o ściśle zdefiniowanych nazwach w konkretnych lokalizacjach. W przypadku wątpliwości, warto zajrzeć do systemu pomocy serwerodawcy lub skontaktować się z nim.
W przypadku, kiedy mamy dostęp poprzez konsolę i większe niż zwykle uprawnienia, warto wiedzieć, iż logi dotyczące uruchamiania crona są dostępne w /var/log/cron
lub /var/log/syslog
, w zależności od dystrybucji Linuxa oraz konfiguracji. Możemy tam podejrzeć, czy dane zadanie rzeczywiście zostało uruchomione o wyznaczonym czasie.
W tym artykule nie będziemy w szczegółach omawiać kwestii konfiguracyjnych - zazwyczaj nie są one istotne dla programistów i zajmują sie nimi administratorzy. Oczywiście, nie jest to żadna wymówka i warto wiedzieć jak najwięcej, natomiast trzeba mieć świadomość, że konfiguracja crona (jako programu, a nie poszczególnych zadań) to działanie znacznie wykraczające poza tworzenie aplikacji - to obszar pod panowaniem osób zarządzających serwerem i systemem operacyjnym na nim. Tym niemniej, warto zajrzeć do artykułu Mateusza Mazurka oraz Zairy Hiry, gdzie jest opisane trochę więcej.
Na koniec tej sekcji wróćmy jeszcze na chwilę do definiowania zadań - zazwyczaj polega to na umieszczeniu kilkunastu osobnych poleceń w crontabie. Nie jest to jednak jedyny sposób, a mimo prostoty, może być nawet nieco zgubny. Jeśli w rozwijanej aplikacji pojawia się wiele akcji, które będą uruchamiane periodycznie, to za każdym razem trzeba pamiętać o uzupełnianiu crontaba przy wdrożeniu lub zautomatyzować to. Problem jest też w przekazaniu tej informacji między programistami i wdrażającym. Jednak niektóre frameworki podchodzą do tego trochę inaczej - przykładowo, Laravel wymaga dodania tylko jednej komendy do tabeli systemowej (uruchamianej co minutę), a to sama aplikacja zarządza czasami uruchomienia poszczególnych zadań, które ma w sobie zaprogramowane. Atut tego rozwiązania polega na tym, że programista nie musi przejmować się uzupełnianiem crona, a w dodatku już w kodzie ma informację o tym, co ile wykonywana jest dana operacja (w dodatku mogąc śledzić zmiany w repozytorium).
Przy okazji, warto uzmysłowić sobie, że nie jest wymagane ręczne edytowanie pliku crontaba, aby dodać nową operację czy zmienić istniejącą. Reguły można przechowywać w innym pliku tekstowym i zaimportować go poprzez polecenie crontab file.txt
. To może pomóc w automatyzacji tego procesu podczas wdrożeń. Tak samo prosto można zrobić kopię zapasową obecnych reguł komendą crontab -l > backup.txt
.
Do czego można wykorzystać crona?
Z pewnością podczas czytania dotychczasowej części artykułu, w głowie każdego czytelnika pojawiło się wiele pomysłów na to, w jakich sytuacjach można wykorzystać crona. Dotyczą one zarówno aplikacji, jak i różnych operacji systemowych. Dla formalności wypiszmy najczęstsze przykłady użycia menedżera zadań wykonywanych cyklicznie. I to z życia, a nie teoretycznych rozważań.
- tworzenie zadań na bazie dat (np. "dzisiaj kończy się twoja umowa"),
- tworzenie periodycznych raportów,
- wysyłanie zaplanowanych powiadomień,
- ustalanie statusów obiektów w systemie,
- usuwanie nieużywanych plików,
- przeliczenie zagregowanych danych,
- przygotowywanie kopii zapasowych bazy danych bądź plików,
- pobieranie danych z zewnętrznego API (np. kursy walut),
- odnowienie certyfikatu SSL,
- aktualizowanie plików (np. mapa strony lub kanał RSS),
- czyszczenie cache'a.
Możliwości jest bardzo dużo - to tylko ich garstka. Prawie w każdej aplikacji znajdzie się kilka funkcji, które są uruchamiane nie na żądanie użytkownika, tylko automatycznie, ułatwiając choćby monitorowanie terminów czy obiektów wymagających interwencji. Trudno wyobrazić sobie choćby mechanikę newslettera bez zastosowania crona - to, że odbiorcy dostają nowe wiadomości np. co poniedziałek o 9 rano nie jest wynikiem tylko regularności nadawcy, ale właśnie takich "schedulerów", jak niepozorne uniksowe narzędzie.
Jak uruchamiać zadania co sekundę?
Bardziej spostrzegawczy zauważyli, że najmniejszą jednostką czasową crona jest minuta - linijka * * * * *
oznacza wywołanie danej komendy co 60 sekund. Jak zatem robić to częściej, nawet co 1 sekundę? Na wstępie należy powiedzieć, że jeśli takie jest wymaganie aplikacji, to... warto je przemyśleć. Pamiętajmy, że zadania wykonywane w systemie w tle nadal go obciążają, a dodatkowo określenie momentu uruchomienia operacji nie oznacza, że znamy moment jej końca. W przypadku większych działań może się okazać, że zadanie uruchomione planowo np. o 6:12 zakończy się dopiero o 6:20. Oczywiście, większość zadań na serwerze zostanie sfinalizowana się przed upłynięciem sekundy, ale w przypadku bardziej złożonych systemów może to być np. kilka sekund. Uruchamiając takie zadanie co sekundę system będzie coraz bardziej obciążony, co może skończyć się fatalnie. W większości wypadków uruchamianie co minutę jest jak najbardziej wystarczające, a i tak nierozważnie wykorzystane może spowodować przekroczenie zużycia zasobów.
To jednak nie oznacza, że nie istnieje sposób na "oszukanie" crona i uruchomienie zadania w konkretnej sekundzie. Nie jest to też bardzo trudne, o ile znamy obecną tak w Linuksie, jak i praktycznie każdym języku programowania operację sleep
, pozwalając "zasnąć" aplikacji na określoną liczbę sekund. Kluczem jest to, aby uruchomić zadanie w sekundzie 0 i poczekać chwilę na interesujący nas moment. W niektórych przypadkach można to zrobić w samym crontabie za pomocą komendy systemowej, natomiast, jeśli technologia aplikacji nam na to pozwoli, można to zrobić także w niej.
Nie mam crona - co robić?
Obecnie brak możliwości skorzystania z crona jest dość rzadkim zjawiskiem, jednak z naszych doświadczeń wynika, że może się to zdarzyć, szczególnie w przypadku bardzo tanich lub małych hostingów. Czasem wynika to też z kwestii uprawnień, choć nie powinno być to wytłumaczeniem z uwagi na to, że każdy użytkownik ma swojego crontaba i odpowiednio zawężone możliwości.
Załóżmy jednak, że faktycznie taka sytuacja nas spotkała - co możemy zrobić poza ręcznym uruchamianiem komend? Możliwości są co najmniej dwie. Pierwsza z nich to wykonywanie pewnych działań podczas pierwszego logowania się użytkownika w danym dniu, oczywiście, w miarę możliwości w tle. Dotyczy to szczególnie operacji przeliczenia zagregowanych danych, np. do kokpitu firmy wchodzącej do systemu. Tym niemniej, potężną wadą jest czasochłonność operacji oraz wymóg, aby ktoś się zalogował, co nie wiadomo, czy nastąpi. Drugie rozwiązanie, znacznie lepsze, to wystawienie usługi API i codzienne odwoływanie się do niej z zewnętrznego źródła, które z kolei jest sterowane cronem. Jest to właśnie imitacja okresowego wywoływania operacji, jednak wymaga publicznego dostępu do serwisu oraz pisania kolejnej usługi. Tym niemniej, jest to jakaś opcja w przypadku braku możliwości skorzystania ze standardowej ścieżki.
Warto też wspomnieć o tym, że czasem cron może nie działać, gdyż zwyczajnie... nikt go nie uruchomił lub nie zainstalował. W takim wypadku, o ile posiadamy odpowiednie uprawnienia, warto sprawdzić stan demona za pomocą komendy sudo service cron status
lub innej, odpowiedniej dla danej dystrybucji systemu Linux. Różne możliwości i sposób uruchamiana zostały opisane w tym poradniku.
Cron w różnych środowiskach
Mówiąc o cronie, cały czas poruszamy się w tematyce Linuxa. Nie jest to dziwne, gdyż na większości serwerów króluje właśnie ten system operacyjny. Ponieważ omawiane narzędzie zostało stworzone za czasów UNIXa, to można się spodziewać, że znajdzie się również w macOS i tak jest w istocie - w tym środowisku możemy z niego skorzystać.
A jak to wygląda w przypadku Windowsa? W tym przypadku prostego dostępu do crona nie mamy, ale za to mamy dwie drogi, jak sobie z tym poradzić. Istnieje Windows Subsystem for Linux (WSL), który pozwala odtworzyć Linuxa w Okienkach. W zależności od dystrybucji, program może już tam być, ale wyłączony (jak to ma miejsce w przypadku Debiana), jednak łatwo to zmienić, korzystając z porad znajdujących się w poprzedniej sekcji. Uruchomienie usługi do zadań cyklicznych automatycznie nie jest aż tak proste, jakby się mogło wydawać, ale również jest możliwe.
Natomiast warto pamiętać, że sam Windows oferuje zamiennik w postaci Harmonogramu Zadań dostępnego z poziomu menu start lub jako Taskschd
(wielkość liter jest ważna) z wiersza poleceń. Ten program umożliwia tworzenie zadań za pomocą interfejsu graficznego, choć istnieje też opcja zarządzania nimi z poziomu konsoli, której warto się przyjrzeć (nie od dzisiaj wiadomo, że programiści lepiej się dogadują z interfejsem konsolowym niż GUI).
Podsumowanie
Zadania cykliczne pojawiają się praktycznie w każdej aplikacji tworzonej przez software house i z cronem po prostu trzeba nauczyć się żyć oraz z niego korzystać. Zwłaszcza, że nie jest to skomplikowane narzędzie, za to dające duże możliwości, także w sytuacjach, w których pozornie nie należy się tego spodziewać (np. przy przeliczaniu pomocniczych wartości w tle). Zachęcamy do samodzielnych prób, gdyż tylko praktyka pozwoli opanować tego typu techniki we właściwy sposób.
Pozdrawiam i dziękuję - Jakub Rojek.