Photomath: Što smo naučili iz grešaka pri upravljanju cloud infrastrukturom

Napisao: Davorin Gradečak, DevOps Engineer u Photomathu

Ako se bar malo vremena bavite Cloudom, a pogotovo infrastrukturom na cloudu, sigurno ste čuli popularne fraze i savjete: odvoji okoline, zero-trust security itd. Ukoliko se držite ovih principa, vaš život u Cloudu će uglavnom biti bezbolan.

No što ako se u vašoj firmi rijetko tko ili čak nitko toga ne drži?

Kako popraviti dio infrastrukture koji je u produkciji bez da vaši korisnici to primijete?

Slijedi par kratkih priča koje uključuju neke najveće greške koje smo napravili u Photomathu tokom učenja i razvijanja softvera na cloudu, što je bilo potrebno da ih popravimo, i lekcije koje smo usput naučili. Nijedna nije stvarno utjecala na naše krajnje korisnike, ali da ih nismo ulovili na vrijeme, mogle su nas skupo koštati.

Mali servisni account s velikim pravima

Tog dana sam u ured došao relativno rano za moje standarde – što je za ostale i dalje relativno kasno, te sam primijetio da su kolege u sobi aktivniji nego inače. Izgleda da je jedan servis dobio čudnu poruku s našeg Pub/Sub subscriptiona koja nije imala smisla. Netko od kolega je pitao sasvim razumno pitanje: “Kako se to dogodilo? Tko ima pravo objaviti poruku na topic?”. Nakon kratkog vremena kopanja po Google Cloud-ovim logovima i konzoli, vratio sam se s dobrim i lošim vijestima. Dobra vijest je bila da servisni account za servis koji bi stvarno trebao objavljivati poruke doista ima odgovarajuća prava. S druge strane, loša vijest je to što je postojao još jedan servisni account sličnog naziva, koji je imao tolika prava da bi ga čovjek lako mogao zamijeniti s diktatorom iz 20-tog stoljeća. Provjerio sam konfiguraciju, i naš Kubernetes servis je uistinu koristio odgovarajući servisni account s ispravnim manjim skupom prava.

“Ok, dakle samo deaktiviraj ključeve s drugog accounta, i zatim pobriši taj account.” – “Ne možemo” – “Zašto ne?” – “Izgleda da se taj servisni account i dalje koristi kako bi autentificirao pozive na API od Google Clouda” – “Znamo li tko?” – “Trenutno, nemamo pojma. Možda samo neki developer ima pristup ključu, a moguće je i da je neki servis u produkcijskoj okolini pogrešno konfiguriran. Prvo možemo lako popraviti, ali drugi slučaj je ono što me brine”. Tako je nastao mukotrpan zadatak pregledavanja svakog servisa, i provjere imaju li svoj odgovarajući servisni account s ispravnim pravima.

Davorin Gradečak

Ukupno smo pronašli tri različita tipa konfiguracije (dok je trebao postojati samo jedan). Postojali su servisi koji su ispravno konfigurirani. Ključ za servisni account je mountan i njegova lokacija je proslijeđena kroz environment varijablu. Servis je pročitao tu varijablu te je koristio ključ za autentifikaciju na Google Cloud servise.

S druge strane, neki su servisi identično postavljeni, ali nisu koristili ispravan ključ za autentifikaciju. Broj API poziva za te servisne accounte kroz protekli mjesec je bio 0! Kako je to moguće? Nakon što smo još jednom zasukali rukave, otkrili smo da je developer koristio isti sumnjivi ključ iz početka naše priče i kopirao ga direktno u repozitorij. Time je zaobišao servisni account koji je podešen kroz environment varijablu. Popravak toga je bio jednostavan, obrisali smo file s ključem iz repozitorija. File je i dalje postojao u git history-ju, ali kako će sam account uskoro biti obrisan, ta informacija bi ionako uskoro postala beskorisna. Nakon što smo riješili taj problem, vidjeli smo značajan pad u broju API poziva za naš neispravan servisni account. Ali posao još nije bio gotov.

Bilo je servisa koji uopće nisu imali servisni account! Imali smo isti problem, file s ključem za servisni account je pushan u git repo tijekom inicijalnog developmenta. Naš zadatak je sad postao kreirati nove accounte za takav tip servisa. Razgovor koji smo često vodili s našim kolegama je izgledao otprilike ovako: “Hej, kreiram servisni account za tvoj servis, možeš li mi reći koja prava treba – u smislu usluga na cloudu koje koristi? Možeš me i samo uputiti na neku dokumentaciju, ako ti je tako lakše.” – “Samo mu daj ista prava koja je imao prošli servisni account.” – “Pa to je i problem, koristio je generični servisni account koji trebamo obrisati”. Stoga smo morali scrapeati kod da otkrijemo koja prava mu trebaju, i uz metode pokušaja i pogreške uspjeli dobiti novi servisni account s puno užim skupom prava. U tu svrhu smo razvili jednostavan samodokumentirajući terraform modul, čime smo pojednostavili proces kreiranja i pregleda servisnih accounta.

Konačno, kad smo sredili svaki servis, mogli smo početi razmišljati o brisanju starog servisnog accounta. Još uvijek smo vidjeli da je aktivan, ali istraživanje tko ili što ga još koristi smatrali smo prevelikim troškom vremena. Uvjerili smo se da ga naše kritične aplikacije ne koriste i odlučili smo postupno smanjivati prava, te smo pritom bili na oprezu zbog mogućnosti novih 403 grešaka u našim aplikacijama. Greške se nisu pojavile, pa smo zaključili da su ostali API pozivi morali dolaziti od nekog developera koji čuva ključ kao Smeagol svoj ‘precious’. Naposljetku smo izbrisali sam account, te nas nije iznenadilo da nas je nekoliko developera kontaktiralo tražeći nove key-eve 🙂

Uvijek počisti za sobom

Krajem 2020., Photomath je odlučio prenijeti većinu naših servisa odgovornih za rješavanje zadataka u cloud. Jedan od dragulja u Photomathovom tech kraljevstvu bila je mogućnost da je mogao čitati i rješavati probleme na samom telefonu. No ta prednost je uskoro počela postajati smetnja. Hardver mobitela nas je ograničavao u tome što sve možemo postići na njemu, i osjećali smo da smo napravili što smo mogli. Bilo je vrijeme da pređemo u cloud i učinimo naš solver pouzdanijim i učinkovitijim.

Tijekom ovog prijelaza našli smo se u situaciji da moramo odlučiti hoćemo li koristit Google-ov managed Redis – “Memorystore”, ili pokrenuti Redis na vlastitom VM-u kojim ćemo sami upravljati. Da bismo mogli odlučiti, morali smo instalirati Redis na VM u našem dev okruženju. Nisam osobno sudjelovao u donošenju odluke, ali krajnji rezultat je pobjeda Google-managed Redisa.

Promijenili smo konfiguracije naših workloada da ga koriste te smo bili gotovi u rekordnom vremenu. Nismo naišli ni na kakve probleme, te smo svi bili sretni što nas ništa nije usporilo u našem procesu. Naša sreća, naravno, nije potrajala.

Mjesecima kasnije dolazi mladi DevOps inženjer te pregledava naš Terraform kod, te primjećuje na prvi pogled bezazleni string pod nazivom `redis_uri` koji pokazuje na instancu na kojoj se nalazi naša MongoDB baza sa standardnim Redis portom 6379. Ovaj string zaintrigira našeg inženjera te odluči zagrebati dublje ispod površine. Pronalazi zalutalo firewall pravo `allow_redis` koje doista utječe na dotični MongoDB server. Još jedna neobičnost u slučaju, no to bi mogao biti samo ostatak stare infrastrukture koja je bila kreirana kako bi se mogla donijeti odluka.

Konačno, naš inženjer se odluči sam spojiti na server te vlastitim očima vidjeti što se točno događa. Proprčka po serveru i… pronađe pravi pravcat Redis koji se vrti uz MongoDB, sa stvarnim Kubernetes servisima spojenim na njega. Ali ako su servisi spojeni na VM, što radi naš managed Redis?

Brzo otvara novi tab kako bi provjerio metrike za Googleov Redis i… ne nađe ništa. Samo skuplja prašinu (i povećava naš račun). Pogađa ga strah koji svi koji su bili odgovorni za bilo koju vrstu infrastrukture dobro poznaju:  “Što se događa u našim testnim i produkijskim okruženjima? Jesmo li samo brojili dane do kvara, poput zakovica na Titanicu koje samo čekaju da puknu?” Brzo odjuri u testno okruženje te otkrije… Googleov Redis radi punom parom. Nema skrivenog Redisa na instanci na kojoj ga ni ne bi trebalo biti. Jedino što je preostalo je relativno beznačajno firewall pravilo koje i dalje otvara port 6379.

Kada je završio, izvijestio je ostatak tima o svom pronalasku, pritom pitajući zašto je dev okruženje poslovično srednje dijete u ovom slučaju. Zvuk cvrčaka. Nema odgovora. Konačno pita svog menadžera zašto to nije popravljeno te zašto smo uopće pokretali Redis na istoj instanci gdje je MongoDB. Kao rezultat toga je nastao svima najdraži Jira ticket. Međutim, s obzirom da nitko drugi nije imao problema, a cijeli tim je imao bezbroj drugih zadataka višeg prioriteta, problem je pao u backlog.

Igrom slučaja, tempirana bomba se konačno detonirala istog dana kad je netko preuzeo zadatak. Redis je postao prevelik, OS je odlučio ubiti MongoDB, a uznemiren dev je pitao što se događa. Srećom, popravak je već bio pri kraju. Nakon gašenja Redisa te brisanja `redis_uri` stringa koji je sve ovo započeo, dev okruženje više nije bilo srednje dijete.

(Ne) vjeruj alatima

Ova priča uključuje moju malenkost i relativno je mala nezgoda, ali za mene je ipak vrijedna spomena. Igrao sam se s Terraformom i nekim open source projektima vezanim sigurnosnim skeniranjem terraform koda: Checkov i tfsec. Htio sam vidjeti kako će neki od naših projekata proći, očekujući da će se zasvijetliti poput božićnog drvca – većina naše rane infrastrukture ipak nije izgrađena sa maksimalnom sigurnošću na prvom mjestu. I report stvarno zasvijetli kao božićno drvce, ali iznenađujuće to su bila uglavnom zelena svjetla s nekoliko žutih warninga za dev okruženje za koja smo već znali.

U isto vrijeme smo eksperimentirali s Googleovim CloudSQL managed SQL bazama podataka. Migrirali smo relativno nebitan servis da koristi dotični produkt u našem testnom okruženju, te me posebno zanimalo jesmo li slijedili najbolje prakse prilikom postavljanja baze. Rezultat je bio samo jedan warning: Uključite require_ssl. “Ha”, pomislio sam. “Ovo je trebalo biti uključeno. Uključit ću ga da se riješim warninga, i udovoljim svom OCD-u.”, i tako sam učinio. Otišao sam raditi nešto drugo i proveo ugodan vikend daleko od svog računala brinući se konačno o nečem drugom osim o našoj infrastrukturi.

U ponedjeljak sam kao i obično došao relativno kasno na posao, i vidio da je dotični servis imao nekih problema u testnom okruženju – nije se mogao povezati s bazom. “Popravljeno”, netko je rekao na Slacku. Super, to više nije problem zbog kojeg se morem brinuti – i nisam. Tjedan dana kasnije razgovaram s kolegom i on priča o problemu kojeg je imao prije tjedan dana – baza je iz nekog razloga tražila SSL certifikat. “Ha”, pomislio sam ponovno. “Pretpostavljam da SSL nije trebao biti uključen prije nego što sam provjerio s našim developerima. Za sada ću to zadržati za sebe – ali možda će to biti zanimljiva priča jednog dana.”

Nemoj (naslijepo) updateati svoj build server

Ova priča nas vraća u ne tako davnu prošlost kad su aktualne verzije Terraforma bile 0.x. Photomath nije baš bio rani usvajač Terraforma, ali smo ga koristili za upravljanje praktički svega u cloudu, uključujući deployanje naših Kubernetes servisa. U to vrijeme, nadogradnja verzije Terraforma bi često uključivala promjene koje su zahtijevale ručnu intervenciju prije pokretanja nadogradnje. Popravak koji, ako se ne učini prije same nadogradnje, može slomiti cijeli naš CI/CD pipeline (odnosno dio CD-a). S druge strane, ažuriranje softvera je naravno važno kako ne bi koristili softver s hrpom sigurnosnih propusta te kako bi općenito ostali u korak s vremenom.

I tako je jednog kobnog ljetnog jutra, kada se naš cijeli tim pripremao za posljednje napore prije povratka u školu (izuzetno važno vrijeme za nas, s obzirom na to da smo EdTech tvrtka), jedan sigurnosno osviješteni kolega je odlučio osigurati da je naš build server up-to-date sa svim svojim softverskim paketima. Napravili su dobri stari pregled kako bi se uvjerili da ne rade neke major nadogradnje i zaključili da je sve u redu. Nekoliko minuta kasnije, naši Jenkins buildovi su se počeli crveniti.

“To je malo čudno”, pomislim. “Kako je moguće da svi zabrljaju u isto vrijeme? Mora da ih pritisak povratka u školu pogodio.” Trebalo je svega par sekundi prije nego su Slack poruke krenule stizati. Terraform je izbacivao grešku i morali smo brzo saznati što se dogodilo ako bi išta htjeli obaviti tog dana.

Dok se znojim pokušavajući shvatiti što se točno dogodilo, shvatim da mi se karma vratila i da sad mene pogađa pritisak povratka u školu. Konačno uspijemo shvatiti da je Terraform ranije tog dana releaseao novu verziju, a errori koje vidimo se podudaraju s dokumentiranim breaking promjenama.

Nismo imali vremena shvatiti kako se ažuriranje dogodilo ili tko ga je napravio. Ručno smo vratili verziju unatrag i smislili plan za rješavanje breaking promjena pod našim uvjetima. Naš kolega se otkrio ubrzo nakon toga, iako je rekao da nije primijetio da je terraform ažuriran niti je bio svjestan da će to dovesti promjena koje će slomiti build. Uvijek još jednom provjerite što radite, i osigurajte da imate plan b ako stvari ne prođu kako ste originalno zamislili.

S velikom moći dolazi velika prilika da zezneš

U startupima je često vrlo važno brzo iterirati moguće rješenje problema koji pokušavate riješiti. Kada ste u cloudu, za programera to često znači “daj mi prava sada, a postavljaj pitanja kasnije”. Često je na nama, članovima core infrastructure tima, da pritisnemo kočnicu i vidimo što točno treba a da pritom ne usporavamo razvoj previše. Ali što se dogodi kada smo i mi zauzeti, i ne uočimo ili primjetimo polovičan zahtjev?

Ovisno o tome koliko ste naporni, možda ćete dobiti ono što želite. Ili barem ono što mislite da želite. I tako je bilo jednog posebno užurbanog tjedna kada je jedan developer zatražio neka prava na starom Google projektu koji nije imao gotovo ništa u produkciji. Tražena dopuštenja su možda činila prekomjerna, ali kako potrebne role nisu mogle utjecati na produkciju (bio bi potreban niz iznimno nesretnih događaja da pronađete način da na to utječete), smatrao sam da je u redu odobriti njegov zahtjev dok god ja pazim na potencijalno osjetljive dijelove infrastrukture. I… ništa se nije dogodilo. Isprobao je svoje rješenje, to nije bilo ono što je tražio, i počistio za sobom.

Želio je još jednom pokušati riješiti svoj problem i ovaj put je trebao malo drukčiji skup prava. Ponovno se zahtjev činio razumnim, a u međuvremenu je moj tjedan postajao sve naporniji, pa je to i dobio. Rješenje mu opet nije bilo po volji, pa smo odlučili počistiti.

E sada, naš dev je uporan (volimo ih takve) i pronašao je rješenje koje bi moglo uroditi plodom. Ovaj put su mu bila potrebna prava za kreiranje cloud funkcija i subscriptiona u Pub/Subu. S obzirom na to da se pokazao odgovornim, a ja sam gubio razum na sada vjerojatno nevažnom tasku, dao sam mu prava da kreira subscription. Nekoliko puta je naišao na zapreku i trebala su mu dodatna prava da rekreira subscription i funkciju. Napokon je sve radilo iznad očekivanja, te smo već krenuli planirati proslavu uz pivo.

U međuvremenu, u istom uredu u kojem ja sjedim, naš data tim primjećuje da ne prima podatke o photomathovim pretplatama. Nedostaju podaci od zadnjih tjedan dana. Korisnici nisu izravno pogođeni, ali analitičaru podataka je teško raditi svoj posao kad nema podataka koje bi analizirao. Ne mislim ništa o tome, jer ni ne znam kako taj sustav obrađuje naše pretplate.

Ispostavilo se da, između ostalog, koristi Pub/Sub subscription koja je nekako izbrisana prije tjedan dana. Alarmi u glavi su mi odmah počeli zvoniti i nakon što sam provjerio – da, slučajno smo obrisali taj subscription. Ispričali smo se, ali se više ništa nije moglo učiniti – podaci se nisu mogli povratiti, ali je subscription lako ponovno kreiran za samo nekoliko minuta. Tog dana smo naučili vrijednu lekciju: nemojte prerano planirati svoje proslave pivom.

Kroz ovih nekoliko priča, dali smo vam uvid u pozadinu nekih naših nezgoda. Iako se ponosimo time da svi naši servisi rade uz minimalan downtime, ne možemo očekivati da ništa neće poći po zlu. Učenje iz neuspjeha, davanje sve od sebe da ih ispravimo, i volja da se nakon toga našalimo – sve to čini Photomath tako izvrsnim mjestom za rad.

Koje su vaše najveće greške? Jesu li se pretvorile u sretne male nesreće? Photomath zapošljava u gotovo svim inženjerskim timovima, stoga svakako dodajte svoju priču u vaše motivacijsko pismo. Voljeli bismo čitati i razgovarati o tome s vama na intervjuu!

Photomathovu ekipu moće ćete upoznati i na .debugu!

Kupi ulaznicu

999 kn

Ulaznica

250 kn

Studentska ulaznica

650 kn

Online ulaznica

200 kn

Studentska online ulaznica