{"id":55870,"date":"2020-01-30T00:00:00","date_gmt":"2020-01-29T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/ot-skriptov-k-sobstvennoj-platforme-kak-my-avtomatizirovali-razrabotku-v-tsian"},"modified":"2020-02-18T14:04:02","modified_gmt":"2020-02-18T11:04:02","slug":"ot-skriptov-k-sobstvennoj-platforme-kak-my-avtomatizirovali-razrabotku-v-tsian","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ot-skriptov-k-sobstvennoj-platforme-kak-my-avtomatizirovali-razrabotku-v-tsian","title":{"rendered":"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/965f1944c95e2020bec512a05d980a32.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDurante il RIT 2019, il nostro collega Alexander Korotkov ha parlato <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=0wvUyjexoIg\">relazione<\/a><\/noindex> dell'automazione nello sviluppo in CIAN: per semplificare la vita e il lavoro, utilizziamo la nostra piattaforma Integro. Essa monitora il ciclo di vita delle attivit\u00e0, solleva gli sviluppatori dalle operazioni routinarie e riduce significativamente il numero di bug in produzione. In questo post, completeremo la presentazione di Alexander e racconteremo come siamo passati da semplici script all'integrazione di prodotti open source tramite la nostra piattaforma e di cosa si occupa un team separato dedicato all'automazione.<br \/>\n\u00a0<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Livello zero<\/h2>\n<p>\n<i><b>\u00abNon esiste un livello zero, non lo so\u00bb<\/b><br \/>\nMaster Shifu dal film \u00abKung Fu Panda\u00bb<\/i><\/p>\n<p>L'automazione in CIAN \u00e8 iniziata 14 anni dopo la fondazione dell'azienda. All'epoca, il team di sviluppo era composto da 35 persone. Fa fatica a crederci, vero? Certamente, in qualche forma l'automazione esisteva gi\u00e0, ma un segmento dedicato all'integrazione continua e alla consegna del codice ha iniziato a prendere forma nel 2015.\u00a0<\/p>\n<p>A quel tempo avevamo un enorme monolite scritto in Python, C# e PHP, distribuito su server Linux\/Windows. Per il deploy di questo mostro avevamo un insieme di script che eseguivamo manualmente. C'era anche la compilazione del monolite, che portava dolore e sofferenza a causa dei conflitti durante la fusione dei rami, delle correzioni dei difetti e della ricompilazione \u00abcon un altro insieme di attivit\u00e0 nella build\u00bb. Il processo semplificato appariva cos\u00ec:<\/p>\n<p><img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/03c4dd97e2ba0073bab1c33f6003a9ec.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nQuesto non ci soddisfaceva, e volevamo costruire un processo di compilazione e deploy ripetibile, automatizzato e gestito. Per questo avevamo bisogno di un sistema CI\/CD, e dovevamo scegliere tra la versione gratuita di Teamcity e Jenkins gratuito, dato che avevamo gi\u00e0 esperienza con entrambi e soddisfacevano entrambe le nostre esigenze funzionali. Abbiamo scelto Teamcity come prodotto pi\u00f9 recente. A quel tempo, non utilizzavamo ancora un'architettura a microservizi e non prevedevamo un numero elevato di attivit\u00e0 e progetti.<\/p>\n<h2>Siamo arrivati all'idea di un nostro sistema<\/h2>\n<p>\nL'implementazione di Teamcity ha ridotto solo una parte del lavoro manuale: \u00e8 rimasta ancora la creazione di Pull Request, la gestione delle attivit\u00e0 negli stati in Jira e la selezione delle attivit\u00e0 per il rilascio. Su questo aspetto, il sistema Teamcity non era in grado di gestire. Era necessario scegliere una strada per ulteriori automazioni. Abbiamo considerato opzioni di scripting in Teamcity o la transizione a sistemi di automazione esterni. Ma alla fine abbiamo deciso che avevamo bisogno della massima flessibilit\u00e0, che solo una soluzione interna poteva offrire. Cos\u00ec \u00e8 nata la prima versione del sistema di automazione interno chiamato Integro.<\/p>\n<p>Teamcity si occupa dell'automazione a livello di avvio dei processi di build e deployment, mentre Integro si \u00e8 focalizzata sull'automazione di alto livello dei processi di sviluppo. Era necessario integrare il lavoro con le attivit\u00e0 in Jira con la gestione del codice sorgente correlato in Bitbucket. In questa fase, in Integro sono iniziati a emergere flussi di lavoro propri per gestire attivit\u00e0 di diversi tipi.\u00a0<\/p>\n<p>Con l'aumento dell'automazione nei processi aziendali, \u00e8 cresciuto il numero di progetti e run in Teamcity. Cos\u00ec \u00e8 emerso un nuovo problema: un'istanza gratuita di Teamcity non era pi\u00f9 sufficiente (3 agenti e 100 progetti), abbiamo aggiunto un'altra istanza (altri 3 agenti e 100 progetti), poi un'altra ancora. Alla fine, abbiamo ottenuto un sistema composto da pi\u00f9 cluster, difficile da gestire:<\/p>\n<p><img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/d95565e01e70e31f6b139dfb9b9caf95.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nQuando \u00e8 emersa la necessit\u00e0 di una quarta istanza, ci siamo resi conto che non era pi\u00f9 possibile continuare in questo modo, poich\u00e9 i costi complessivi per supportare 4 istanze stavano gi\u00e0 oltrepassando ogni limite. Si \u00e8 presentata la questione dell'acquisto di Teamcity a pagamento o della scelta di Jenkins gratuito. Abbiamo effettuato calcoli sulle istanze e sui piani di automazione e abbiamo deciso di optare per Jenkins. Dopo qualche settimana, siamo passati a Jenkins e abbiamo alleviato parte del mal di testa associato al supporto di pi\u00f9 istanze di Teamcity. Cos\u00ec siamo riusciti a concentrarci sullo sviluppo di Integro e sull'ottimizzazione di Jenkins per le nostre esigenze.<\/p>\n<p>Con l'aumento dell'automazione di base (attraverso la creazione automatica di Pull Request, la raccolta e pubblicazione della copertura del codice e altri controlli) \u00e8 nata la forte volont\u00e0 di abbandonare il pi\u00f9 possibile i rilascio manuali e delegare questo lavoro ai robot. Inoltre, all'interno dell'azienda \u00e8 iniziato il passaggio ai microservizi, che richiedevano rilasci frequenti, separati l'uno dall'altro. Cos\u00ec siamo arrivati progressivamente ai rilasci automatici dei nostri microservizi (mentre il monolite viene ancora rilasciato manualmente a causa della complessit\u00e0 del processo). Ma, come accade spesso, \u00e8 emersa una nuova difficolt\u00e0.\u00a0<\/p>\n<h2>Automatizziamo i test<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/9518ee0956922c6eb615bdef068ac525.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nGrazie all'automazione dei rilasci, i processi di sviluppo si sono accelerati, parzialmente a causa della saltazione di alcune fasi di testing. Questo ha comportato una temporanea perdita di qualit\u00e0. Sembra banale, ma insieme all'accelerazione dei rilasci era necessario cambiare anche la metodologia di sviluppo del prodotto. \u00c8 stato necessario riflettere sull'automazione dei test, sull'assegnazione della responsabilit\u00e0 personale (qui si parla di 'accettazione dell'idea nella mente', e non di multe monetarie) per il codice rilasciato e i relativi bug, cos\u00ec come sulla decisione di rilasciare o non rilasciare una task attraverso un deploy automatico.\u00a0<\/p>\n<p>Affrontando i problemi di qualit\u00e0, siamo giunti a due importanti decisioni: abbiamo iniziato a effettuare test canarini e abbiamo implementato il monitoraggio automatico degli errori in background con una risposta automatica al superamento di una soglia. La prima decisione ha permesso di identificare errori evidenti prima che il codice arrivasse in produzione, la seconda ha ridotto il tempo di risposta ai problemi in produzione. Gli errori, ovviamente, ci sono, ma dedichiamo la maggior parte del nostro tempo e delle nostre energie non a correggerli, bens\u00ec a minimizzarli.\u00a0<\/p>\n<h2>Il team di automazione<\/h2>\n<p>\nAttualmente abbiamo un organico di 130 sviluppatori e continuiamo <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/article\/469965\/\">a crescere<\/a><\/noindex>. Il team di integrazione e consegna continua del codice (di seguito \u2014 team Deploy and Integration o DI) \u00e8 composto da 7 persone e lavora in 2 direzioni: sviluppo della piattaforma di automazione Integro e DevOps.\u00a0<\/p>\n<p>Il team DevOps \u00e8 responsabile degli ambienti Dev\/Beta del sito CIAN, dell'ambiente Integro, aiuta gli sviluppatori nella risoluzione dei problemi e sviluppa nuove strategie per scalare gli ambienti. La direzione dello sviluppo di Integro si occupa sia di Integro stessa, sia di servizi correlati, come plugin per Jenkins, Jira, Confluence, e sviluppa anche strumenti e applicazioni di supporto per i team di sviluppatori.\u00a0<\/p>\n<p>Il team DI lavora a stretto contatto con il team della Piattaforma, che si occupa dello sviluppo dell'architettura, delle librerie e delle metodologie di sviluppo all'interno dell'azienda. Inoltre, qualsiasi sviluppatore all'interno di CIAN pu\u00f2 contribuire all'automazione, ad esempio, realizzando microautomazioni per le esigenze del team o condividendo idee brillanti su come migliorare ulteriormente l'automazione.<\/p>\n<h2>La torta stratificata dell'automazione in CIAN<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/22dcd2d0cc283d53bdaac081885657f8.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nTutti i sistemi coinvolti nell'automazione possono essere suddivisi in pi\u00f9 livelli:<\/p>\n<ol>\n<li>Sistemi esterni (Jira, Bitbucket, ecc.). Questi sono utilizzati dai team di sviluppo.\n<\/li>\n<li>Piattaforma Integro. Di solito, gli sviluppatori non lavorano direttamente con essa, ma \u00e8 fondamentale per il funzionamento di tutta l'automazione.\n<\/li>\n<li>Servizi di distribuzione, orchestrazione e discovery (ad esempio, Jenkins, Consul, Nomad). Ci aiutano a distribuire il codice sui server e a garantire il corretto funzionamento dei servizi tra di loro.\n<\/li>\n<li>Livello fisico (server, OS, software correlato). A questo livello opera il nostro codice. Pu\u00f2 trattarsi di un server fisico o virtuale (LXC, KVM, Docker).\n<\/li>\n<\/ol>\n<p>\nIn base a questo concetto, suddividiamo le aree di responsabilit\u00e0 all'interno del team DI. I primi due livelli sono sotto la responsabilit\u00e0 della direzione dello sviluppo di Integro, mentre gli ultimi due livelli sono sotto la responsabilit\u00e0 del DevOps. Questa divisione consente di concentrarsi sui compiti senza ostacolare l'interazione, poich\u00e9 siamo fisicamente vicini e ci scambiamo continuamente conoscenze ed esperienze.<\/p>\n<h2>Integro<\/h2>\n<p>\nConcentriamoci su Integro e iniziamo con lo stack tecnologico:<\/p>\n<ul>\n<li>CentOs 7\n<\/li>\n<li>Docker + Nomad + Consul + Vault\n<\/li>\n<li>Java 11 (il vecchio monolite di Integro rimarr\u00e0 su Java 8)\n<\/li>\n<li>Spring Boot 2.X + Spring Cloud Config\n<\/li>\n<li>PostgreSql 11\n<\/li>\n<li>RabbitMQ\u00a0\n<\/li>\n<li>Apache Ignite\n<\/li>\n<li>Camunda (embedded)\n<\/li>\n<li>Grafana + Graphite + Prometheus + Jaeger + ELK\n<\/li>\n<li>Interfaccia Web: React (CSR) + MobX\n<\/li>\n<li>SSO: Keycloak\n<\/li>\n<\/ul>\n<p>\nSeguiamo il principio dello sviluppo a microservizi, anche se abbiamo una legacy rappresentata dal monolite della vecchia versione di Integro. Ogni microservizio gira nel proprio contenitore Docker e i servizi comunicano tra loro tramite richieste HTTP e messaggi RabbitMQ. I microservizi si trovano l'uno con l'altro tramite Consul e effettuano una richiesta, passando l'autenticazione tramite SSO (Keycloak, OAuth 2\/OpenID Connect).<\/p>\n<p><img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/e7630c0bd62a40f397de1bf1c1097926.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nCome esempio concreto, consideriamo l'interazione con Jenkins, che consiste nei seguenti passaggi:<\/p>\n<ol>\n<li>Il microservizio di gestione del workflow (d'ora in poi microservizio Flow) vuole avviare una build in Jenkins. A tal fine, trova tramite Consul l'IP:PORT del microservizio di integrazione con Jenkins (d'ora in poi microservizio Jenkins) e invia a questo una richiesta asincrona per avviare la build in Jenkins.\n<\/li>\n<li>Il microservizio Jenkins, dopo aver ricevuto la richiesta, genera e restituisce un Job ID, che poi sar\u00e0 possibile utilizzare per identificare il risultato del lavoro. Contestualmente, avvia la build in Jenkins tramite una chiamata all'API REST.\n<\/li>\n<li>Jenkins esegue la build e, al termine, invia un webhook con i risultati dell'esecuzione al microservizio Jenkins.\n<\/li>\n<li>Il microservizio Jenkins, ricevuto il webhook, genera un messaggio di conclusione dell'elaborazione della richiesta e allega i risultati dell'esecuzione. Il messaggio generato viene inviato in coda a RabbitMQ.\n<\/li>\n<li>Attraverso RabbitMQ, il messaggio pubblicato arriva al microservizio Flow, che scopre il risultato dell'elaborazione della sua attivit\u00e0 confrontando il Job ID della richiesta con quello del messaggio ricevuto.\n<\/li>\n<\/ol>\n<p>\nAttualmente abbiamo circa 30 microservizi, che possono essere suddivisi in diversi gruppi:<\/p>\n<ol>\n<li>Gestione delle configurazioni.\n<\/li>\n<li>Informazione e interazione con gli utenti (messaggeri, email).\n<\/li>\n<li>Gestione del codice sorgente.\n<\/li>\n<li>Integrazione con gli strumenti di deployment (Jenkins, Nomad, Consul, ecc.).\n<\/li>\n<li>Monitoraggio (rilascio, errori, ecc.).\n<\/li>\n<li>Web utility (interfaccia utente per la gestione degli ambienti di test, raccolta statistiche, ecc.).\n<\/li>\n<li>Integrazione con i tracker di attivit\u00e0 e sistemi simili.\n<\/li>\n<li>Gestione del workflow per compiti differenti.\n<\/li>\n<\/ol>\n<p><\/p>\n<h2>Workflow del compito<\/h2>\n<p>\nIntegro automatizza le azioni legate al ciclo di vita di un compito. In modo semplificato, per ciclo di vita di un compito intendiamo il workflow di un'attivit\u00e0 in Jira. Nei nostri processi di sviluppo, ci sono diverse varianti di workflow a seconda del progetto, del tipo di compito e delle opzioni selezionate per il compito specifico.\u00a0<\/p>\n<p>Consideriamo il workflow che utilizziamo pi\u00f9 frequentemente:<\/p>\n<p><img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/62555be6bab024dd30f3cf70d4fe3cba.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNello schema, l'ingranaggio indica che il transition viene attivato automaticamente da Integro, mentre la figura umana indica che il transition \u00e8 attivato manualmente da una persona. Esaminiamo alcuni percorsi che un'attivit\u00e0 pu\u00f2 seguire in questo workflow.<\/p>\n<p>Testing manuale completo su DEV+BETA senza test canary (di solito cos\u00ec rilasciamo il monolite):<\/p>\n<p><img decoding=\"async\" alt=\"Dagli script alla nostra piattaforma: come abbiamo automatizzato lo sviluppo in CIAN\" src=\"\/wp-content\/uploads\/2020\/01\/775528862e14817dff5f69db6de654dd.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPossono esserci anche altre combinazioni di transition. A volte il percorso che seguir\u00e0 l'attivit\u00e0 pu\u00f2 essere scelto tramite le opzioni in Jira.<\/p>\n<h2>Movimento dell'attivit\u00e0<\/h2>\n<p>\nEsaminiamo i passaggi principali che vengono eseguiti durante il movimento dell'attivit\u00e0 nel workflow \"Testing su DEV + test canary\":<\/p>\n<p>1. Uno sviluppatore o un PM crea l'attivit\u00e0.<\/p>\n<p>2. Lo sviluppatore prende in carico l'attivit\u00e0. Una volta completata, la sposta nello stato IN REVIEW.<\/p>\n<p>3. Jira invia un Webhook al microservizio Jira (responsabile dell'integrazione con Jira).<\/p>\n<p>4. Il microservizio Jira invia una richiesta al servizio Flow (responsabile dei workflow interni, nei quali viene eseguito il lavoro) per avviare il workflow.<\/p>\n<p>5. All'interno del servizio Flow:<\/p>\n<ul>\n<li>Vengono assegnati i revisori per l'attivit\u00e0 (microservizio Users, che conosce tutto sugli utenti + microservizio Jira).<\/li>\n<li>Attraverso il microservizio Source (che conosce i repository e i branch, ma non lavora con il codice stesso) viene eseguita la ricerca dei repository che contengono il branch della nostra attivit\u00e0 (per semplificare la ricerca, il nome del branch corrisponde al numero dell'attivit\u00e0 in Jira). Di solito, l'attivit\u00e0 ha solo un branch in un unico repository, il che semplifica la gestione della coda per il deployment e riduce la dipendenza tra i repository.<\/li>\n<li>Per ogni branch trovato viene eseguita la seguente sequenza di azioni:\n<p> i) Unione del branch master (microservizio Git per lavorare con il codice).<br \/>\n ii) Il branch viene bloccato da modifiche da parte dello sviluppatore (microservizio Bitbucket).<br \/>\n iii) Viene creato un Pull Request per questo branch (microservizio Bitbucket).<br \/>\n iv) Viene inviata una notifica riguardo al nuovo Pull Request nelle chat degli sviluppatori (microservizio Notify per gestire le notifiche).<br \/>\n v) Viene avviata la compilazione, il test e il deployment dell'attivit\u00e0 su DEV (microservizio Jenkins per lavorare con Jenkins).<br \/>\n vi) Se tutti i punti precedenti sono stati completati con successo, Integro approva il Pull Request (microservizio Bitbucket).<\/li>\n<li>Integro attende un'approvazione nel Pull Request dai revisori assegnati.<\/li>\n<li>Non appena vengono ricevute tutte le approvazioni necessarie (compresi i test automatizzati superati positivamente), Integro sposta l'attivit\u00e0 nello stato Test on Dev (microservizio Jira).<\/li>\n<\/ul>\n<p>\n6. I collauder effettuano il test del compito. Se non ci sono problemi, spostano il compito nello stato Pronto per la Build.<\/p>\n<p>7. Integro 'vede' che il compito \u00e8 pronto per il rilascio e avvia il suo deploy in modalit\u00e0 canary (microservizio Jenkins). La prontezza al rilascio \u00e8 determinata da un insieme di regole. Ad esempio, il compito si trova nello stato corretto, non ci sono blocchi su altri compiti, attualmente non ci sono distribuzioni attive di questo microservizio, ecc.<\/p>\n<p>8. Il compito viene spostato nello stato Canary (microservizio Jira).<\/p>\n<p>9. Jenkins avvia tramite Nomad il deploy del compito in modalit\u00e0 canary (solitamente 1-3 istanze) e notifica la distribuzione al servizio di monitoraggio delle release (microservizio DeployWatch).<\/p>\n<p>10. Il microservizio DeployWatch raccoglie il background degli errori e vi reagisce, se necessario. Se il background degli errori supera il limite (il limite \u00e8 calcolato automaticamente), viene inviata una notifica agli sviluppatori tramite il microservizio Notify. Se dopo 5 minuti lo sviluppatore non ha reagito (premendo Revert o Stay), viene avviato il rollback automatico delle istanze canary. Se il limite non viene superato, lo sviluppatore deve avviare manualmente il deploy del compito in Produzione (premendo un bottone nell'interfaccia). Se entro 60 minuti lo sviluppatore non ha avviato il deploy in Produzione, le istanze canary saranno anch'esse ritirate per motivi di sicurezza.<\/p>\n<p>11. Dopo l'avvio del deploy in Produzione:<\/p>\n<ul>\n<li>Il compito viene spostato nello stato Produzione (microservizio Jira).<\/li>\n<li>Il microservizio Jenkins avvia il processo di deploy e notifica la distribuzione al microservizio DeployWatch.<\/li>\n<li>Il microservizio DeployWatch controlla che tutti i contenitori siano stati aggiornati in Produzione (si sono verificate situazioni in cui non tutti venivano aggiornati).<\/li>\n<li>Viene inviata una notifica sui risultati del deploy in Produzione tramite il microservizio Notify.<\/li>\n<\/ul>\n<p>\n12. Gli sviluppatori avranno 30 minuti per avviare il rollback del compito in Produzione in caso di comportamento anomalo del microservizio. Dopo questo periodo, il compito sar\u00e0 automaticamente fuso nel master (microservizio Git).<\/p>\n<p>13. Dopo un merge riuscito nel master, lo stato del compito verr\u00e0 modificato in Chiuso (microservizio Jira).<\/p>\n<p>Lo schema non pretende di essere completamente dettagliato (nella realt\u00e0 i passaggi sono ancora di pi\u00f9), ma consente di valutare il livello di integrazione nei processi. Non consideriamo questo schema ideale e stiamo migliorando i processi di supporto automatico per le release e i deploy.<\/p>\n<h2>E ora?<\/h2>\n<p>\nAbbiamo grandi progetti per sviluppare l'automazione, ad esempio, eliminare le operazioni manuali durante il rilascio del monolite, migliorare il monitoraggio durante il deployment automatico e ottimizzare l'interazione con gli sviluppatori.<\/p>\n<p>Ma ci fermeremo qui per ora. Abbiamo trattato molte tematiche nell'overview sull'automazione in modo superficiale e alcune non le abbiamo affrontate affatto, quindi siamo felici di rispondere alle vostre domande. Aspettiamo suggerimenti su cosa approfondire, scriveteci nei commenti.<br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/cian\/blog\/486056\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041d\u0430 \u0420\u0418\u0422 2019 \u043d\u0430\u0448 \u043a\u043e\u043b\u043b\u0435\u0433\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440 \u041a\u043e\u0440\u043e\u0442\u043a\u043e\u0432 \u0441\u0434\u0435\u043b\u0430\u043b \u0434\u043e\u043a\u043b\u0430\u0434 \u043f\u0440\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432 \u0426\u0418\u0410\u041d: \u0447\u0442\u043e\u0431\u044b \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0436\u0438\u0437\u043d\u044c \u0438 \u0440\u0430\u0431\u043e\u0442\u0443, \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 Integro. \u041e\u043d\u0430 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0437\u0430\u0434\u0430\u0447, \u0441\u043d\u0438\u043c\u0430\u0435\u0442 \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0440\u0443\u0442\u0438\u043d\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0438 \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0431\u0430\u0433\u043e\u0432 \u0432 production. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u043c\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u043c \u0434\u043e\u043a\u043b\u0430\u0434 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c, \u043a\u0430\u043a \u043f\u0440\u043e\u0448\u043b\u0438 \u043f\u0443\u0442\u044c \u043e\u0442 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-55870","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041d\u0430 \u0420\u0418\u0422 2019.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ot-skriptov-k-sobstvennoj-platforme-kak-my-avtomatizirovali-razrabotku-v-tsian\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041e\u0442 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u043a \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435: \u043a\u0430\u043a \u043c\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0432 \u0426\u0418\u0410\u041d | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041d\u0430 \u0420\u0418\u0422 2019.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ot-skriptov-k-sobstvennoj-platforme-kak-my-avtomatizirovali-razrabotku-v-tsian\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-01-29T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:04:02+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47 Dai script a una piattaforma proprietaria: come abbiamo automatizzato lo sviluppo in CIAN | ProHoster","description":"Al RIT 2019.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ot-skriptov-k-sobstvennoj-platforme-kak-my-avtomatizirovali-razrabotku-v-tsian","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041e\u0442 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u043a \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435: \u043a\u0430\u043a \u043c\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0432 \u0426\u0418\u0410\u041d | ProHoster","og:description":"\u041d\u0430 \u0420\u0418\u0422 2019.","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ot-skriptov-k-sobstvennoj-platforme-kak-my-avtomatizirovali-razrabotku-v-tsian","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-01-29T21:00:00+00:00","article:modified_time":"2020-02-18T11:04:02+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"55870","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 19:35:40","updated":"2022-09-30 17:42:13","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/55870","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=55870"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/55870\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=55870"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=55870"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=55870"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}