Linux hà parechje facce: cumu travaglià in ogni distribuzione

Linux hà parechje facce: cumu travaglià in ogni distribuzione

Crià una applicazione di salvezza chì travaglia in ogni distribuzione ùn hè micca un compitu faciule. Per assicurà chì l'Agent Veeam per Linux travaglia nantu à e distribuzioni da Red Hat 6 è Debian 6, à OpenSUSE 15.1 è Ubuntu 19.04, avete da risolve una serie di prublemi, soprattuttu cunsiderendu chì u pruduttu software include un modulu di kernel.

L'articulu hè statu creatu basatu annantu à i materiali da un discorsu à a cunferenza Linux Peter 2019.

Linux ùn hè micca solu unu di i sistemi operativi più populari. Essenzialmente, questa hè una piattaforma nantu à a basa di quale pudete fà qualcosa unicu, qualcosa di u vostru propiu. Grazie à questu, Linux hà parechje distribuzioni chì differiscenu in u so settore di cumpunenti di software. È quì hè un prublema: per un pruduttu software per funziunà in ogni distribuzione, avete da piglià in contu e caratteristiche di ognunu.

Gestori di pacchetti. .deb vs .rpm

Cuminciamu cù u prublema evidenti di distribuzione di u pruduttu in diverse distribuzioni.
A manera più tipica di distribuisce i prudutti di u software hè di mette u pacchettu in un repository per chì u gestore di pacchetti integratu in u sistema pò installallu da quì.
Tuttavia, avemu dui formati di pacchettu populari: rpm и deb. Questu significa chì tutti anu da sustene.

In u mondu di i pacchetti deb, u livellu di cumpatibilità hè stupente. U stessu pacchettu si stalla è funziona ugualmente bè in Debian 6 è Ubuntu 19.04. I normi per u prucessu di custruisce pacchetti è travaglià cun elli, stabiliti in vechji distribuzioni Debian, restanu pertinenti in u novu Linux Mint è OS elementari. Dunque, in u casu di Veeam Agent per Linux, un pacchettu deb per ogni piattaforma hardware hè abbastanza.

Ma in u mondu di i pacchetti rpm, e sferenze sò grandi. Prima, per via di u fattu chì ci sò dui distributori completamente indipendenti, Red Hat è SUSE, per quale a cumpatibilità hè cumplettamente inutile. Siconda, questi distributori anu kit di distribuzione da quelli. sustegnu è spirimintali. Ùn ci hè micca bisognu di cumpatibilità trà elli. Hè risultatu chì el6, el7 è el8 anu i so pacchetti. Pacchettu separatu per Fedora. Pacchetti per SLES11 è 12 è un separatu per openSUSE. U prublema principali hè a dependenza è i nomi di pacchetti.

Prublemu di dipendenza

Sfortunatamente, i stessi pacchetti spessu finiscinu sottu nomi diffirenti in distribuzioni differenti. Quì sottu hè una lista parziale di dipendenze di u pacchettu veeam.

Per EL7:
Per SLES 12:

  • libblkid
  • libgcc
  • libstdc++
  • ncurses-libs
  • fuse-libs
  • file-libs
  • veeamsnap=3.0.2.1185
  • libblkid1
  • libgcc_s1
  • libstdc ++ 6
  • libmagic1
  • libfuse2
  • veeamsnap-kmp=3.0.2.1185

In u risultatu, a lista di dipendenze hè unica per a distribuzione.

Ciò chì s'aggrava hè quandu una versione aghjurnata principia à nascondere sottu u vechju nome di pacchettu.

Esempiu:

U pacchettu hè statu aghjurnatu in Fedora 24 ncurses da a versione 5 à a versione 6. U nostru pruduttu hè statu custruitu cù a versione 5 per assicurà a cumpatibilità cù e distribuzioni più vechje. Per utilizà l'antica versione 5th di a biblioteca nantu à Fedora 24, aghju avutu aduprà u pacchettu ncurses-compat-libs.

In u risultatu, ci sò dui pacchetti per Fedora, cù diverse dipendenze.

In più più interessante. Dopu à u prossimu aghjurnamentu di distribuzione, u pacchettu ncurses-compat-libs cù a versione 5 di a bibliuteca risulta esse indisponibile. Hè caru per un distributore per arrastà i vechji biblioteche in una nova versione di a distribuzione. Dopu qualchì tempu, u prublema si ripete in distribuzioni SUSE.

In u risultatu, alcune distribuzioni anu da abbandunà a so dipendenza esplicita ncurses-libs, è riparà u pruduttu in modu chì pò travaglià cù qualsiasi versione di a biblioteca.

Per via, in a versione 8 di Red Hat ùn ci hè più un pacchettu meta pitone, chì si riferisce à u bonu vechju pitone 2.7... ci hè python2 и pitone3.

Alternativa à i gestori di pacchetti

U prublema cù i dependenzii hè vechju è hè statu longu evidenti. Basta à ricurdà l'infernu di a dipendenza.
Per unisce diverse biblioteche è applicazioni in modu chì tutti travaglianu in modu stabile è micca cunflittu - in fattu, questu hè u compitu chì ogni distributore Linux prova di risolve.

U gestore di pacchetti prova di risolve stu prublema in una manera completamente diversa. scattante da Canonical. L'idea principale: l'applicazione funziona in una sandbox isolata è prutetta da u sistema principale. Se una applicazione richiede biblioteche, sò furnite cù l'applicazione stessa.

Flatpak permette ancu di eseguisce applicazioni in un sandbox cù Linux Containers. L'idea sandbox hè ancu utilizata AppImage.

Queste soluzioni permettenu di creà un pacchettu per ogni distribuzione. In casu di Flatpak a stallazione è u lanciu di l'applicazione hè pussibule ancu senza a cunniscenza di l'amministratore.

U prublema principali hè chì micca tutte l'applicazioni ponu esse in una sandbox. Certi pirsuni anu bisognu di accessu direttu à a piattaforma. Ùn sò mancu parlà di i moduli di u kernel, chì sò strettamente dipindenti di u kernel è ùn si mette micca in u cuncettu di sandbox.

U sicondu prublema hè chì e distribuzioni populari in l'ambiente di l'impresa da Red Hat è SUSE ùn anu micca ancu supportu per Snappy è Flatpak.

In questu sensu, Veeam Agent per Linux ùn hè micca dispunibule snapcraft.io micca nantu flathub.org.

Per cuncludi a quistione nantu à i gestori di pacchetti, vogliu nutà chì ci hè una opzione per abbandunà i gestori di pacchetti cumminendu i schedarii binari è un script per installallu in un pacchettu.

Un tali bundle permette di creà un pacchettu cumuni per e diverse distribuzioni è piattaforme, realizà un prucessu di stallazione interattiva, realendu a persunalizazione necessaria. Aghju scontru solu tali pacchetti per Linux da VMware.

Prublemu di aghjurnà

Linux hà parechje facce: cumu travaglià in ogni distribuzione
Ancu s'è tutti i prublemi di dependenza sò risolti, u prugramma pò eseguisce in modu diversu nantu à a stessa distribuzione. Hè una questione di aghjurnamenti.

Ci sò 3 strategie di aghjurnamentu:

  • U più simplice hè di ùn aghjurnà mai. Aghju stallatu u servitore è mi sò scurdatu. Perchè aghjurnà se tuttu funziona? I prublemi cumincianu a prima volta chì cuntattate u supportu. U creatore di a distribuzione sustene solu a versione aghjurnata.
  • Pudete cunfidà u distributore è stabilisce l'aghjurnamenti automatichi. In questu casu, una chjama à u supportu hè prubabilmente immediatamente dopu una aghjurnazione senza successu.
  • L'opzione di l'aghjurnamentu manuale solu dopu avè eseguitu nantu à una infrastruttura di prova hè a più affidabile, ma caru è tempu. Ùn ognunu pò permette.

Siccomu diversi utilizatori utilizanu diverse strategie di aghjurnamentu, hè necessariu di sustene l'ultima versione è tutte e liberate precedentemente. Questu complica u prucessu di sviluppu è di prova è aghjunghje mal di testa à a squadra di supportu.

Varietà di piattaforme hardware

Diversi piattaforme hardware sò un prublema chì hè largamente specificu à u codice nativu. À u minimu, avete da cullà binari per ogni piattaforma supportata.

In u prughjettu di l'Agente Veeam per Linux, ùn pudemu ancu sustene nunda cum'è questu RISC.

Ùn aghju micca affruntà stu prublema in dettagliu. Solu delinearaghju i prublemi principali: tippi dipendenti di a piattaforma, cum'è size_t, allineamentu di struttura è ordine di byte.

Ligame staticu è / o dinamicu

Linux hà parechje facce: cumu travaglià in ogni distribuzione
Ma a quistione hè "Cumu ligà cù e biblioteche - dinamicamente o staticamente?" vale a pena discutiri.

In regula, l'applicazioni C/C++ sottu Linux utilizanu ligami dinamichi. Questu funziona bè se l'applicazione hè custruita specificamente per una distribuzione specifica.

Se u compitu hè di copre diverse distribuzioni cù un schedariu binariu, allora avete da fucalizza nantu à a più antica distribuzione supportata. Per noi, questu hè Red Hat 6. Contene gcc 4.4, chì ancu u standard C++ 11 ùn sustene micca. cumpletu.

Custruemu u nostru prughjettu utilizendu gcc 6.3, chì sustene cumplettamente C ++ 14. Naturalmente, in questu casu, in Red Hat 6 duvete portà a libstdc++ è rinfurzà e librerie cun voi. A manera più faciule hè di ligà cun elli staticamente.

Ma sfortunatamente, micca tutte e biblioteche ponu esse ligati staticamente.

Prima, biblioteche di sistema cum'è libfuse, libblkid hè necessariu di ligà dinamicamente per assicurà a so cumpatibilità cù u kernel è i so moduli.

Siconda, ci hè una sutilezza cù licenze.

A licenza GPL vi permette basicamente di ligà biblioteche solu cù u codice opensource. MIT è BSD permettenu ligami statichi è permettenu e librerie per esse incluse in un prughjettu. Ma l'LGPL ùn pare micca cuntradisce u ligame staticu, ma esige chì i schedarii necessarii per u ligame sò spartuti.

In generale, l'usu di ligami dinamichi impediscenu di avè da furnisce qualcosa.

Custruì applicazioni C/C++

Per custruisce applicazioni C / C ++ per e diverse piattaforme è distribuzioni, hè abbastanza per selezziunà o custruisce una versione adatta di gcc è aduprà cumpilatori incruciati per architetture specifiche è assemble l'inseme di biblioteche. Stu travagliu hè abbastanza fattibile, ma abbastanza fastidiosu. È ùn ci hè micca garanzia chì u compilatore è e biblioteche selezziunate furnisceranu una versione praticabile.

Un vantaghju evidenti: l'infrastruttura hè assai simplificata, postu chì tuttu u prucessu di custruzzione pò esse cumpletu nantu à una macchina. Inoltre, hè abbastanza per cullà un inseme di binari per una architettura è pudete imballà in pacchetti per diverse distribuzioni. Hè cusì chì i pacchetti veeam sò custruiti per Veeam Agent per Linux.

In uppusizione à sta opzione, pudete solu preparà una splutazioni di custruzzione, vale à dì, parechje macchine per l'assemblea. Ognuna di tali macchine furnisce a compilazione di l'applicazioni è l'assemblea di pacchetti per una distribuzione specifica è una architettura specifica. In questu casu, a compilazione hè realizata cù i mezi preparati da u distributore. Questu hè, a tappa di a preparazione di u compilatore è a selezzione di biblioteche hè eliminata. Inoltre, u prucessu di custruzzione pò esse facilmente parallelizatu.

Ci hè, però, un svantaghju à questu approcciu: per ogni distribuzione in a listessa architettura, avete da cullà u vostru propiu set di schedarii binari. Un altru svantaghju hè chì un gran numaru di machini deve esse mantinutu è una grande quantità di spaziu di discu è RAM deve esse attribuita.

Hè cusì chì i pacchetti KMOD di u modulu di u kernel veeamsnap sò compilati per e distribuzioni Red Hat.

Open Build Service

I culleghi di SUSE anu pruvatu à implementà una terra media in a forma di un serviziu speciale per cumpilà applicazioni è assemblee pacchetti - serviziu openbuild.

Essenzialmente, hè un ipervisore chì crea una macchina virtuale, stalla tutti i pacchetti necessarii in questu, compila l'applicazione è custruisce u pacchettu in questu ambiente isolatu, dopu chì a macchina virtuale hè liberata.

Linux hà parechje facce: cumu travaglià in ogni distribuzione

U pianificatore implementatu in OpenBuildService determinerà quante macchine virtuali pò lancià per una velocità ottimale di creazione di pacchetti. U meccanismo di firma integratu firmarà i pacchetti è li caricherà in u repositoriu integratu. U sistema di cuntrollu di versione integrata salvarà a storia di i cambiamenti è e custruzzioni. Tuttu ciò chì resta hè solu di aghjunghje i vostri fonti à stu sistema. Ùn avete mancu à stallà u servitore stessu; pudete aduprà un apertu.

Ci hè, però, un prublema: una tale cugliera hè difficiule di mette in l'infrastruttura esistenti. Per esempiu, u cuntrollu di versione ùn hè micca necessariu; avemu digià u nostru propiu per i codici fonte. U nostru mecanismu di firma hè diversu: usemu un servitore speciale. Un repository ùn hè ancu necessariu.

Inoltre, u supportu per altre distribuzioni - per esempiu, Red Hat - hè implementatu piuttostu pocu, chì hè comprensibile.

U vantaghju di un tali serviziu hè un supportu rapidu per a prossima versione di a distribuzione SUSE. Prima di l'annunziu ufficiale di a liberazione, i pacchetti necessarii per l'assemblea sò publicati in un repository publicu. Un novu appare in a lista di distribuzioni dispunibili nantu à OpenBuildService. Cuntrollamu a casella è hè aghjuntu à u pianu di custruisce. Cusì, aghjunghje una nova versione di a distribuzione hè fatta in quasi un clic.

In a nostra infrastruttura, utilizendu OpenBuildService, tutta a varietà di pacchetti KMP di u modulu di kernel veeamsnap per distribuzioni SUSE hè assemblata.

In seguitu, mi piacerebbe aspittà nantu à prublemi specifichi à i moduli di u kernel.

kernel ABI

I moduli di kernel Linux sò storicamente distribuiti in forma di fonte. U fattu hè chì i creatori di u kernel ùn anu micca a carica di a preoccupazione di sustene una API stabile per i moduli di u kernel, è sopratuttu à u nivellu binariu, più chjamatu kABI.

Per custruisce un modulu per un kernel vanilla, avete bisognu di l'intestazione di stu kernel particulari, è travaglià solu nantu à questu kernel.

DKMS permette di automatizà u prucessu di custruisce moduli quandu aghjurnà u kernel. In u risultatu, l'utilizatori di u repositoriu Debian (è i so numerosi parenti) utilizanu moduli di kernel sia da u repositoriu di u distributore sia cumpilati da a fonte cù DKMS.

Tuttavia, sta situazione ùn hè micca particularmente adatta à u segmentu Enterprise. I distributori di codice pruprietariu volenu distribuisce u pruduttu cum'è binari compilati.

L'amministratori ùn volenu micca mantene e strumenti di sviluppu nantu à i servitori di produzzione per ragioni di sicurezza. I distributori Linux Enterprise cum'è Red Hat è SUSE anu decisu chì puderanu sustene kABI stabile per i so utilizatori. U risultatu era pacchetti KMOD per Red Hat è pacchetti KMP per SUSE.

L'essenza di sta suluzione hè abbastanza sèmplice. Per una versione specifica di a distribuzione, l'API di u kernel hè congelatu. U distributore dichjara ch'ellu usa u kernel, per esempiu, 3.10, è face solu currezzione è migliurà chì ùn anu micca affettatu l'interfacce di u kernel, è i moduli cullati per u primu kernel pò esse usatu per tutti i successivi senza recompilazione.

Red Hat pretende a compatibilità kABI per a distribuzione in tuttu u so ciclu di vita. Questu hè, u modulu assemblatu per rhel 6.0 (liberazione di nuvembre 2010) deve ancu travaglià in a versione 6.10 (liberazione di ghjugnu 2018). È questu hè quasi 8 anni. Di sicuru, stu compitu hè abbastanza difficiule.
Avemu registratu parechji casi induve u modulu veeamsnap hà cessatu di travaglià per via di prublemi di cumpatibilità kABI.

Dopu chì u modulu veeamsnap, compilatu per RHEL 7.0, hè statu incompatibile cù u kernel da RHEL 7.5, ma hà carricatu è era garantitu di crash u servitore, avemu abbandunatu l'usu di a cumpatibilità kABI per RHEL 7 in tuttu.

Attualmente, u pacchettu KMOD per RHEL 7 cuntene un assemblea per ogni versione di liberazione è un script chì carica u modulu.

SUSE hà avvicinatu u compitu di a cumpatibilità kABI più attentamente. Forniscenu a cumpatibilità kABI solu in un pacchettu di serviziu.

Per esempiu, a liberazione di SLES 12 hè accadutu in settembre 2014. È SLES 12 SP1 era digià in dicembre 2015, vale à dì, un pocu più di un annu hè passatu. Ancu s'è e duie versioni utilizanu u kernel 3.12, sò kABI incompatibili. Ovviamente, mantene a cumpatibilità kABI per solu un annu hè assai più faciule. U ciclu annuale di l'aghjurnamentu di u modulu di u kernel ùn deve micca causà prublemi per i creatori di moduli.

In u risultatu di sta pulitica SUSE, ùn avemu micca registratu un solu prublema cù a cumpatibilità kABI in u nostru modulu veeamsnap. True, u numeru di pacchetti per SUSE hè quasi un ordine di grandezza più grande.

Patch è backports

Ancu i distributori pruvate d'assicurà a cumpatibilità kABI è a stabilità di u kernel, pruvate ancu di migliurà u rendiment è eliminà i difetti di stu kernel stabile.

À u listessu tempu, in più di u so propiu "travagliu nantu à l'errore", i sviluppatori di l'impresa Linux kernel monitor cambianu in u kernel di vaniglia è i trasfirìanu à u so "stabile".

Calchì volta questu porta à novi sbagli.

In l'ultima versione di Red Hat 6, un sbagliu hè statu fattu in una di l'aghjurnamenti minori. Hà purtatu à u fattu chì u modulu veeamsnap era garantitu per crash the system when the snapshot was released. Dopu avè paragunatu e fonti di u kernel prima è dopu l'aghjurnamentu, avemu scupertu chì u backport era culpèvule. Una correzione simili hè stata fatta in a versione 4.19 di vanilla kernel. Hè solu chì sta correzione hà travagliatu bè in u kernel di vaniglia, ma quandu si trasfirìu à u "stabile" 2.6.32, un prublema hè ghjuntu cù u spinlock.

Di sicuru, tutti anu sempre l'errore, ma valeva a pena di trascinà u codice da 4.19 à 2.6.32, risichendu a stabilità ?.. Ùn sò micca sicuru...

U peghju hè quandu u marketing s'implica in u tig-of-war trà "stabilità" è "modernizazione". U dipartimentu di marketing hà bisognu di u core di a distribuzione aghjurnata per esse stabile, da una banda, è à u stessu tempu esse megliu in u rendiment è avè novi funziunalità. Questu porta à strani cumprumessi.

Quandu aghju pruvatu à custruisce un modulu nantu à u kernel 4.4 da SLES 12 SP3, sò stata sorpresa di truvà funziunalità da vanilla 4.8 in questu. In u mo parè, l'implementazione I / O di bloccu di u kernel 4.4 da SLES 12 SP3 hè più simile à u kernel 4.8 chì a versione precedente di u kernel stabile 4.4 da SLES12 SP2. Ùn possu micca ghjudicà quale percentuale di codice hè stata trasferita da u kernel 4.8 à SLES 4.4 per SP3, ma ùn possu mancu chjamà u kernel u listessu stabile 4.4.

A cosa più dispiacevule di questu hè chì quandu si scrive un modulu chì funziona ugualmente bè nantu à i diversi kernels, ùn pudete micca più cunfidendu in a versione di u kernel. Avete ancu piglià in contu a distribuzione. Hè bonu chì qualchì volta pudete participà à una definizione chì appare cù una nova funziunalità, ma sta opportunità ùn hè micca sempre.

In cunsiquenza, u codice diventa sopratuttu cù direttive di compilazione cundizionale strane.

Ci sò ancu patch chì cambianu l'API di kernel documentatu.
Aghju trovu a distribuzione KDE neon 5.16 è era assai sorpresu di vede chì a chjama di lookup_bdev in questa versione di u kernel hà cambiatu a lista di i paràmetri di input.

Per riunite, aghju avutu aghjunghje un script à u makefile chì verifica se a funzione lookup_bdev hà un paràmetru di maschera.

Firma di moduli di kernel

Ma vultemu à u prublema di a distribuzione di pacchetti.

Unu di i vantaghji di kABI stabile hè chì i moduli di kernel ponu esse firmati cum'è un schedariu binariu. In questu casu, u sviluppatore pò esse sicuru chì u modulu ùn hè micca statu danatu accidentalmente o intenzionalmente mudificatu. Pudete cuntrollà questu cù u cumandimu modinfo.

Red Hat è SUSE distribuzioni permettenu di verificà a firma di u modulu è carica solu se u certificatu currispundente hè registratu in u sistema. U certificatu hè a chjave publica cù quale u modulu hè firmatu. Distribuemu cum'è un pacchettu separatu.

U prublema quì hè chì i certificati ponu esse integrati in u kernel (i distributori l'utilizanu) o deve esse scrittu à a memoria non volatile EFI utilizendu una utilità. mokutil. Utilità mokutil Quandu installate un certificatu, deve esse riavviatu u sistema è, ancu prima di carricà u kernel di u sistema operatore, invita l'amministratore per permette a carica di un novu certificatu.

Cusì, aghjunghje un certificatu richiede l'accessu di l'amministratore fisicu à u sistema. Se a macchina hè situata in un locu in u nuvulu o solu in una stanza di servitore remota è l'accessu hè solu per via di a reta (per esempiu, via ssh), allora serà impussibile aghjunghje un certificatu.

EFI nantu à e macchine virtuali

Malgradu u fattu chì l'EFI hè longu sustinutu da quasi tutti i pruduttori di a scheda madre, quandu si stallanu un sistema, l'amministratore ùn pò micca pensà à a necessità di EFI, è pò esse disattivatu.

Ùn sò micca tutti l'ipervisori supportanu EFI. VMWare vSphere supporta EFI da a versione 5.
Microsoft Hyper-V hà ancu guadagnatu supportu EFI cuminciendu cù Hyper-V per Windows Server 2012R2.

Tuttavia, in a cunfigurazione predeterminata, sta funziunalità hè disattivata per e macchine Linux, chì significa chì u certificatu ùn pò micca esse installatu.

In vSphere 6.5, stabilisce l'opzione boot sicuru solu pussibule in a vechja versione di l'interfaccia web, chì viaghja via Flash. U Web UI in HTML-5 hè sempre luntanu.

Distribuzioni sperimentali

E finarmenti, cunsideremu u prublema di distribuzioni sperimentali è distribuzioni senza supportu ufficiale. Da una banda, tali distribuzioni sò improbabile di truvà nantu à i servitori di l'urganisazioni serii. Ùn ci hè micca supportu ufficiale per tali distribuzioni. Dunque, furnisce quelli. U pruduttu ùn pò esse supportatu nantu à una tale distribuzione.

Tuttavia, tali distribuzioni diventanu una piattaforma còmuda per pruvà novi suluzioni sperimentali. Per esempiu, Fedora, OpenSUSE Tumbleweed o versioni Unstable di Debian. Sò abbastanza stabile. Anu sempre novi versioni di prugrammi è sempre un novu kernel. In un annu, sta funziunalità sperimentale pò finisce in un RHEL, SLES o Ubuntu aghjurnatu.

Allora se qualcosa ùn viaghja micca nantu à una distribuzione sperimentale, questu hè un mutivu per capisce u prublema è risolve. Avete bisognu à esse preparatu per u fattu chì sta funziunalità apparirà prestu nantu à i servitori di produzzione di l'utilizatori.

Pudete studià a lista attuale di distribuzioni supportati ufficialmente per a versione 3.0 ccà. Ma u veru listinu di distribuzioni nantu à quale u nostru pruduttu pò travaglià hè assai più largu.

In modu persunale, era interessatu à l'esperimentu cù l'Elbrus OS. Dopu avè finalizatu u pacchettu veeam, u nostru pruduttu hè statu stallatu è travagliatu. Aghju scrittu annantu à questu esperimentu nantu à Habré in articulu.

Ebbè, u supportu per e novi distribuzioni cuntinueghja. Aspittemu a versione 4.0 per esse liberata. Beta hè vicinu à apparisce, cusì tene un ochju fora ciò chì hè novu!

Source: www.habr.com

Add a comment