Systemd – Linux init sistem – deo II

U prethodnom delu, dat je prikaz osnove ideje i karakteristika init sistema Systemd. Takođe su opisani resursi koje systemd „ume“ da kontroliše, (jedinice) i način na koji može njima da upravlja (preko unit datoteka). Dalje su definisani tipovi unit datoteka i njihove uloge, kao i funkcionalnosti systemd-a. Na kraju su predstavljena 2 važna systemd alata(systemctl i journalctl), i komande pomoću kojih se oni koriste.

U ovom delu će biti opisani osnovni primeri korišćenja systemd-a uz upotrebu NodeJS-a.

NodeJS je programerska platforma – okruženje za izvršavanje JavaScript-a, na računarima van browser okruženja. U primerima koji slede programi napisani u NodeJS-u, će početi sa izvršavanjem u trenutku pokretanja odgovarajućih systemd servisa a završiće se kada ti servisi budu završili sa radom.

U nastavku će biti prikazano nekoliko primera, koji pokazuju različite funkcionalnosti systemd-a.

Primer 1. – Osnovna upotreba systemd servisa

Koji problem se rešava i šta se želi postići?

Ovim primerom se rešava problem restartovanja NodeJS servera, ukoliko prestane da radi. Ukoliko se pokrene neka aplikacija pisana u NodeJS-u, posle izvesnog vremena može doći do nekog neočekivanog problema i aplikacija može prestati sa radom. Ako se to desi, nema načina da se automatski restartuje i nastavi sa radom, već se mora ručno ponovo pokrenuti. Systemd omogućava da se aplikaciji pisanoj u NodeJS-u pridruži odgovarajući servis, tako da kad se desi da aplikacija stane sa radom, servis automatski može restartovati aplikaciju posle određenog unapred zadatog broja sekundi.

Kao primer, biće korišćena jednostavna NodeJS aplikacija koja se konektuje na localhost na određenom portu i ispisuje određeni tekst.

Kôd NodeJS aplikacije izgleda ovako:

const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => 
{
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});
server.listen(port, hostname, () => 
{
  console.log(`Server running at http://${hostname}:${port}/`);
});

Značenje kôda: Prva linija zahteva da se uključi NodeJS modul „http“ i pridružuje ga promenljivoj http. Ovaj modul je potreban da bi klijent mogao da se konektuje na localhost. Druga i treća linija postavaju host koji je u ovom slučaju localhost i port koji je u ovom slučaju 3000. Ostale linije do kraja „govore“ kompjuteru tj. localhost-u, da ispiše „Hello World“ ako neko (npr. web browser) pokuša da pristupi kompjuteru na portu 3000.

Dati ovoj datoteci naziv „primer1.js“ i sačuvati je na lokaciji /opt/primer1/.

U nastavku, sledi kôd za servis koji omogućava NodeJS aplikaciji da se restartuje ako dođe do problema.

[Unit]
Description=NodeJS primer sa restartovanjem aplikacije

[Service]
ExecStart=/usr/bin/node /opt/nodeserver/primer1.js
Restart=always
RestartSec=3
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=Primer_restart

[Install]
WantedBy=multi-user.target

Sledi opis stavki po sekcijama.

  • [Unit]
    • Description – Nalazi se opis servisa koji se koristi
  • [Service]
    • ExecStart=/usr/bin/node /opt/primer1/primer1.js – Definiše se lokacija gde se nalazi izvršna datoteka za NodeJS(/usr/bin/node) i sama lokacija gde se nalazi aplikacija(/opt/primer1/primer1.js), kako bi aplikacija mogla da se pokrene.
    • Restart=always je glavni deo i govori da servis omogućava aplikaciji da se restartuje, ako prestane da radi.
    • RestartSec=3 definiše vreme posle kojeg će servis restartovati aplikaciju ako prestane da radi.
    • StandardOutput=syslog – standardni izlaz servisa će se pisati u log datoteku, tako da se može videti izlaz određenog servisa ako zatreba u budućnosti
    • StandardError=syslog – standardni izlaz za greške servisa će se pisati u log datoteku, tako da se mogu pronaći greške vezane za server ukoliko dođe do problema
    • SyslogIdentifier=Primer_restart – Dodaje „Primer_restart“ na početak svake linije loga, koja je vezana za ovaj servis tako da lakše mogu da se nađu informacije vezane za servis
  • [Install]
    • WantedBy=multi-user.target – Servis se aktivira kada sistem uđe u stanje, koje je definisano targetom multi-user-target. Ovako napisan servis nazvati „primer1.service“ i sačuvati na lokaciji /etc/systemd/system, kako bi systemd „znao“ za postojanje ovog servisa.

Sa ovako definisanom aplikacijom i servisom kada se servis pokrene komandom

sudo systemctl start primer1.service

pokrenuće se i aplikacija i klijenti će moći da pristupe localhost-u na portu 3000 i videće poruku „Hello world“. To se može proveriti ukucavanjem http://localhost:3000/ u address bar nekog browser-a.

Komandom

ps -ef | grep primer1.js

može se videti identifikator procesa koji je pokrenuo NodeJS aplikaciju. Da bi se testirala uloga servisa i da bi se simulirao prekid rada aplikacije potrebno je, nakon što je pronađen identifikator procesa aplikacije, ukucati u terminalu sledeće

sudo kill identifikator_procesa

Ovom komandom ubijamo željeni proces i nasilno prekidamo rad aplikacije. Ako se sada pokuša pristupiti localhost-u, na portu 3000 neće uspeti da se uspostavi konekcija. Posle 5 sekundi prekida rada aplikacije, servis restartuje aplikaciju i ponovo se može pristupiti localhost-u na portu 3000.

Napomena: U primerima koji slede, kad se bude objašnjavala uloga servisa, neće se ponovo objašnjavati stavke koje su ranije bile objašnjene.

Primer 2. – Korišćenje funkcije tajmera kod systemd-a

Koji problem se rešava i šta se želi postići?

Ukoliko se želi da se neka određena akcija izvršava periodično u vremenu, može se iskoristiti systed-ova jedinica tajmera u tu svrhu.

U ovom primeru će biti opisano simuliranje mail-ing liste korišćenjem NodeJS aplikacije.

Kôd za NodeJS aplikaciju izgleda ovako:

var nodemailer = require('nodemailer');
var fs = require('fs');

var transporter = nodemailer.createTransport({
    service: 'Gmail',
    auth: {
        user: 'Vasa email adresa',
        pass: 'Vasa lozinka'
    }
});

var mailOptions = {
    from: 'Vasa email adresa',
    to: 'email adresa prvog primaoca, email adresa drugog primaoca',
    subject: 'Slanje email-a koršćenjem NodeJS-a',
    text: fs.readFileSync('/opt/primer2/tekstZaSlanje.txt').toString()

};

transporter.sendMail(mailOptions, function(error, info)
{
    if(error)
    {
        console.log('Email nije poslat');
        console.log(error);
    }
    else
      console.log('Email je poslat: ' + info.response);
        
})

Značenje kôda: Na početku dadoteke se zahteva da se uključe 2 NodeJS modula: nodemailer i fs i pridružuju se odgovarajućim promenljivama. Modul nodemailer se mora instalirati komandom

sudo npm install nodemailer --save

Ovom komandom će se pravi direktorijum node_modules, u kome se čuvaju moduli za NodeJS.

Povratna vrednost funkcija nodemailer.createTransport, se pridružuje promenljivoj transporter i definiše vrstu email servisa kao i email i lozinku korisnika koji želi da pošalje email. Promenljiva mailOptions čuva osnovne podatke o email-u koji se šalje: adresu pošiljalaca, adresu primalaca, naziv email-a i tekst koji se želi poslati. Tekst se nalazi u datoteci na lokaciji, /opt/primer2/tekstZaSlanje.txt pa se tako prostom izmenom datoteke, mogu slati različite poruke u različitim vremenskim periodima. Na kraju funkija transporter.sendMail šalje email, koristeći prethodno definisane podatke o email-u i štampa informaciju o tome da li je ili nije, uspešno poslat email i ako nije štampa informaciju o grešci.

Dati ovoj datoteci naziv „primer2.js“ i sačuvati je na lokaciji /opt/primer2/. Kreirati datoteku sa nazivom „tekstZaSlanje.txt“ na lokaciji /opt/primer2/ i u njoj napisati tekst koji će da se šalje na mail-ove primalaca.

Odgovarajući servis koji če pokretati ovu datoteku izgleda ovako:

[Unit]
Description=Primer timera

[Service]
ExecStart=/usr/bin/node /opt/primer2/primer2.js

Dati ovoj datoteci naziv „primer2.service“ i sačuvati je na lokaciji /etc/systemd/system kako bi systemd „znao“ za postojanje ovog servisa.

Da bi se slanje email-a izvršavalo u određeno vreme, potrebno je da se definiše systemd-ova jedinica tajmera tako što će se kreirati datoteka koja je opisuje. Jedinica tajmera će pokretati prethodno definisani servis periodično. Kôd za datoteku tajmera izgleda ovako

[Unit]
Description=Primer timera

[Timer]
OnBootSec=1min
OnUnitActiveSec=1 day
Unit=primer2.service

[Install]
WantedBy=multi-user.target

Sledi opis stavki sekcije [Timer].

  • OnBootSec=1min – Vreme koje protekne od pokretanja računara, do prvog pokretanja tajmera.
  • OnUnitActiveSec=1day– Vreme koje protekne između 2 pokretanja tajmera.
  • Unit=primer2.service – Koja će jedinica biti pokrenuta(u ovom slučaju servis primer2.service) kada istekne vreme tajmera.

Dati ovoj datoteci naziv „primer2.timer“ i sačuvati je na lokaciji /etc/systemd/system kako bi systemd „znao“ za postojanje ovog servisa.

Izvršavanjem komande

sudo systemctl start primer2.timer

pokreće se jedinica tajmera koja pokreće jedinicu servisa, a ona pokreće NodeJS aplikaciju za slanje mail-ova na period od 1 dan.

Primer 3. – Soket aktivacija -pokretanje servisa na zahtev

Koji problem se rešava i šta se želi postići?

Postoje neki servisi na sistemu koji se retko koriste, pa nema potrebe da se pokreću pri svakom pokretanju računara. Soket aktivacija omogućava da se takvi servisi pokreću na zahtev, što znači onda kada neko zatraži uslugu od njih. Systemd ima ulogu da otvara sokete na određenim portovima i onda kada npr. korisnik na web broswer-u, koji želi usluge određenog servisa, pokuša da pristupi soketu na određenom portu, systemd pokreće servis koji je zahtevao korisnik.

U ovom jednostavnom primeru, će biti opisan soket koji otvara systemd, servis koji se pokreće na zahtev, kao i NodeJS aplikacija koja služi za prihvatanje korisnika na localhost-u i pružanje usluge korisnicima.

Kôd za NodeJS aplikaciju izgleda ovako:

require('systemd');
 require('autoquit');

var http = require('http');

var server = http.createServer(function(req, res) {
 res.writeHead(200, {'Content-Type': 'text/plain'});
 res.end('Hello World\n'); 
});

server.autoQuit({ timeOut: 300 });
 server.listen('systemd'); 

Značenje kôda: Na početku dadoteke se zahteva da se uključe 3 NodeJS modula: „systemd“, „autoquit“ i „http“. Modul „systemd“ se koristi za soket aktivaciju, a modul „autoquit“ za prekidanje rada NodeJS aplikacije, ukoliko bude neaktivna određeno vreme. Prekidanje NodeJS aplikacije se postiže pomoću funkcije server.autoQuit, a vreme posle kojeg će se prekinuti NodeJS aplikacija, ukoliko ne bude aktivna, je postavljeno na 5 minuta. Slično kao i u prvom primeru, modul http se koristi da bi klijent mogao da se konektuje na localhost. Jedina razlika u odnosu na prvi primer, je što se port na kome sluša localhost definiše od strane systemd-a, tj. definiše ga jedinica soketa systemd-a. Kôd za datoteku koja opisuje systemd soket je dat u nastavku.

[Socket] ListenStream=3003
[Install] WantedBy=sockets.target

U sekciji [Socket] ListenStream=3003 znači da će localhost slušati na portu 3003.

Ovu datoteku nazvati „primer3.socket“ i staviti na lokaciju /etc/systemd/system kako bi systemd „znao“ za postojanje ove jedinice.

Kôd servisa koji će biti pokrenut kada neko pristupi localhost-u na portu 3003, dat je u nastavku.

[Unit]
Description=Primer 3 - Soket aktivacija

[Service]
ExecStart=/usr/bin/node /opt/primer3/primer3.js
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=primer3

Sačuvati ovu datoteku na lokaciji /etc/systemd/system i nazvati je „primer3.service“.

Kada se pokrene ovaj servi,s pokrenuće se i NodeJS aplikacija na lokaciji /opt/primer3/primer3.js. Ako se pokrene komanda

systemctl status primer3.service

videće se da servis primer3.service nije aktivan. Ukoliko se sada pokrene soket jedinica komandom

sudo systemctl start primer3.socket

i korisnik ukuca u address bar-u web browser-a http://localhost:3003/ pokrenuće se servis primer3.service. Servis će pokrenuti NodeJS aplikaciju primer3.js i korisnik će videti poruku „Hello world“. Sada ako se pokrene komanda za proveru statusa servisa, primer3.service, videće se da je servis aktivan, tj. pokrenut.

Nakon 5 minuta neaktivnosti, ako se ponovo pokrene komanda za proveru statusa servisa, videće se da servis više nije aktivan, tj. da se zaustavio jer se prekinuo rad NodeJS aplikacije.

Primer 4. – Zavisnosti među systemd servisima

U ovom primeru će biti opisana zavisnost 2 systemd servisa. Onaj koji zahteva komunikaciju će imati ulogu klijenta, a onaj koji se odaziva na istu će imati ulogu servera. Kada se pokrene onaj servis koji zahteva komunikaciju, automatski se pokreće i drugi servis, tj. onaj koji treba da se odazove. Na kraju, servis koji treba da se odazove šalje poruku prvom servisu.

Izgled NodeJS aplikacije koju će pokretati servis koji treba da se odazove na poziv, izgleda ovako

const http = require('http');

const hostname = '127.0.0.1';
const port = 3004;
 
const server = http.createServer((req, res) => 
{
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Pozdrav za klijenta!\n');
});
 
server.listen(port, hostname, () => 
{
  console.log(`Server running at http://${hostname}:${port}/`);
});

Značenje koda: I jedna i druga aplikacija će koristiiti modul http, da bi aplikacija servisa koji zahteva uslugu mogla da se konektuje na localhost. U ovom slučaju localhost sluša na portu 3004. Nazvati ovu datoteku „primer4a.js“ i staviti je na lokaciju /opt/primer4/.

Izgled NodeJS aplikacije koji će pokrenuti servis koji traži usluge, izgleda ovako

const http = require('http');

var options = {
  host: 'localhost',
  port: 3004
};

http.get(options, function(res) {

  res.on('data', function (podaci) {
    console.log('Podaci od servera: ' + podaci);
  });
}).on('error', function(e) {
  console.log('problem sa  zahtevom: ' + e.message);
});

Na početku se inicijalijuju parametri konekcije tj. host i port na kome se sluša. Moraju da budu identični onima u prvoj aplikaciji, da bi se ostvarila uspešna konekcija. Nakon toga, aplikacija pravi zahtev serveru koristeći gore pomenute podatke i kada joj stignu podaci od servera ona ih štampa. Na kraju se štampa greška ukoliko postoji. Nazvati ovu datoteku „primer4b.js“ i staviti je na lokaciju /opt/primer4/.

Izgled servisa koji se odaziva na poziv je prikazan u nastavku.

[Unit]
Description=Servis koji se odaziva na komunikaciju

[Service]
ExecStart=/usr/bin/node /opt/primer4/primer4a.js
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=primer4a

[Install]
WantedBy=multi-user.target

Nazvati ovu datoteku „primer4a.service“. Izgled servisa koji inicira komunikaciju, je dat u nastavku.

[Unit]
Description=Servis koji inicira komunikacju
Requires=primer4a.service
After=primer4a.service

[Service]
ExecStart=/usr/bin/node /opt/primer4/primer4b.js
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=primer4b

Nazvati ovu datoteku primer4b.service. Datoteke za oba ova servisa moraju biti smeštene na lokaciji /etc/systemd/system.

Važno je zapaziti sledeće 2 linije:

Requires=primer4a.service
After=primer4a.service

Prva linija označava da će, kada se pokrene ovaj servis, da se automatski pokrene i servis primer4a.service. Druga linija određuje raspored izvršavanja ova 2 servisa. U ovom slučaju, prilikom pokretanja servisa koji inicira komunikaciju, servis koji se odaziva na istu, će se pre njega pokrenuti da bi bio spreman da prihvati konekciju i pošalje poruku prvom servisu.

Da bi se ovo demonstriralo, treba da se izvrši sledeća komanda (pretpostavlja se da nijedan od dva servisa nije pokrenut):

sudo systemctl start primer4b.service

Sada ako se proveri status servisa primer4a.service komandom

systemctl status primer4a.service

videće se da je servis primer4a.service pokrenut, iako nije eksplicitno izvršena komanda za njegovo pokretanje. Da bi se proverilo da li je servis koji je inicirao komunikaciju, dobio poruku od servisa od kog je tražio komunikaciju, treba da se izvrši sledeća komanda

journalctl -u primer4b.service

U dnevniku se vidi poruka koju je dobio servis primer4b.service, što znači da je komunikacija bila uspešna.

Zaključak

Primeri koji su predstavljeni, prikazuju osnovne mogućnosti systemd-a. Većina Linux distribucija je prešla na systemd, što pokazuje da je systemd ostvario veliki uticaj i da je na dobrom putu da ostavi značajno mesto u Linux svetu. To što nije ispratio osnovne stavove Unix-a, a ostvario je veliku popularnost, ukazuje na činjenicu da se Linux razvija u nekim drugim, možda boljim pravcima.