{"id":85646,"date":"2020-06-18T01:42:15","date_gmt":"2020-06-17T23:42:15","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular"},"modified":"2020-06-18T01:42:15","modified_gmt":"2020-06-17T23:42:15","slug":"ci-cd-v-github-actions-dlya-proekta-na-flask-angular","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","title":{"rendered":"CI\/CD in Github Actions per un progetto Flask+Angular","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/26d0945a41bbf3c98fa92f15be5b8c6c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn questo articolo condivider\u00f2 la mia esperienza nella configurazione di CI\/CD utilizzando il pannello di controllo Plesk e Github Actions. Oggi impareremo a distribuire un semplice progetto chiamato \u00abHelloworld\u00bb. \u00c8 scritto nel framework Python Flask, con worker su Celery e frontend su Angular 8.<\/p>\n<p><b>Collegamenti ai repository:<\/b> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">backend<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\">frontend<\/a><\/noindex>.<\/p>\n<p>Nella prima parte dell\u2019articolo daremo un\u2019occhiata al nostro progetto e alle sue parti. Nella seconda parte vedremo come configurare Plesk e installare le estensioni e componenti necessari (DB, RabbitMQ, Redis, Docker, ecc.).<\/p>\n<p>Nella terza parte scopriremo finalmente come impostare il pipeline per distribuire il nostro progetto sul server negli ambienti dev e prod. E poi lanceremo il sito sul server.<\/p>\n<p>E s\u00ec, ho dimenticato di presentarmi. Mi chiamo Oleg Borzov e sono uno sviluppatore fullstack nel team CRM per i manager dei prestiti ipotecari in Domklik. <br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1>Panoramica del progetto<\/h1>\n<p>\nPer iniziare, diamo un\u2019occhiata ai due repository del progetto \u2014 backend e frontend \u2014 e facciamo un breve giro del codice.<\/p>\n<h2>Parte backend: Flask+Celery<\/h2>\n<p>\nPer la parte backend ho scelto una combinazione piuttosto popolare tra gli sviluppatori Python: il framework Flask (per le API) e Celery (per la coda delle attivit\u00e0). Come ORM utilizzo SQLAlchemy. Per le migrazioni si usa Alembic. Per la validazione JSON nei controller \u2014 Marshmallow.<\/p>\n<p>In <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">repository<\/a><\/noindex> c'\u00e8 un file Readme.md con una descrizione dettagliata della struttura e istruzioni per l'avvio del progetto.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_web\">API della parte web<\/a><\/noindex> \u00e8 piuttosto semplice, composta da 6 endpoint:<\/p>\n<ul>\n<li><code>\/ping<\/code> \u2014 per controllare la disponibilit\u00e0;<\/li>\n<li>endpoint per registrazione, autorizzazione, disautorizzazione e per ottenere l'utente autorizzato;<\/li>\n<li>endpoint per inviare email, che mette un compito nella coda Celery.<\/li>\n<\/ul>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_celery\">La parte Celery<\/a><\/noindex> \u00e8 ancora pi\u00f9 semplice, c'\u00e8 solo un compito <code>send_mail_task<\/code>.<\/p>\n<p>Nella cartella <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/conf\/\">\/conf<\/a><\/noindex> ci sono due sottocartelle: <\/p>\n<ul>\n<li><code>docker<\/code> con due file Docker (<code>base.dockerfile<\/code> per costruire un'immagine di base che cambia raramente e <code>Dockerfile<\/code> per le costruzioni principali);<\/li>\n<li><code>.env_files<\/code> \u2014 con i file delle variabili d'ambiente per diversi ambienti. <\/li>\n<\/ul>\n<p>\nNella radice del progetto ci sono quattro file docker-compose:<\/p>\n<ul>\n<li><code>docker-compose.local.db.yml<\/code> per avviare un database locale per lo sviluppo;<\/li>\n<li><code>docker-compose.local.workers.yml<\/code> per avviare localmente un worker, database, Redis e RabbitMQ;<\/li>\n<li><code>docker-compose.test.yml<\/code> per eseguire test durante il deployment;<\/li>\n<li><code>docker-compose.yml<\/code> per il deployment.<\/li>\n<\/ul>\n<p>\nE l'ultima cartella che ci interessa \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/.ci-cd\">.ci-cd<\/a><\/noindex>. Contiene script shell per il deployment:<\/p>\n<ul>\n<li><code>deploy.sh<\/code> \u2014 avvio della migrazione e del deployment. Viene eseguito sul server dopo la build e il passaggio dei test in Github Actions;<\/li>\n<li><code>rollback.sh<\/code> \u2014 rollback dei contenitori alla versione precedente della build;<\/li>\n<li><code>curl_tg.sh<\/code> \u2014 invio di notifiche di deployment su Telegram.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Frontend in Angular<\/h2>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\">Repository del frontend<\/a><\/noindex> \u00e8 molto pi\u00f9 semplice di quello del backend. Il frontend \u00e8 composto da tre pagine:<\/p>\n<ul>\n<li>Pagina principale con un modulo per l'invio di email e un pulsante di logout.<\/li>\n<li>Pagina di accesso.<\/li>\n<li>Pagina di registrazione.<\/li>\n<\/ul>\n<p>\nLa pagina principale appare semplice:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/4db36add2c4277397724b0d71b96ff06.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNella root ci sono due file <code>Dockerfile<\/code> e <code>docker-compose.yml<\/code>, insieme alla conosciuta cartella <code>.ci-cd<\/code> con un numero leggermente inferiore di script rispetto al repository del backend (sono stati rimossi gli script per l'esecuzione dei test).<\/p>\n<h1>Creiamo un progetto in Plesk<\/h1>\n<p>\nIniziamo configurando Plesk e creando un abbonamento per il nostro sito.<\/p>\n<h2>Installazione delle estensioni<\/h2>\n<p>\nIn Plesk abbiamo bisogno di quattro estensioni: <\/p>\n<ul>\n<li><code>Docker<\/code> per gestire e visualizzare lo stato dei contenitori nell'interfaccia di Plesk;<\/li>\n<li><code>Git<\/code> per configurare il passo del deployment sul server;<\/li>\n<li><code>Let's Encrypt<\/code> per generare (e rinnovare automaticamente) certificati TLS gratuiti;<\/li>\n<li><code>Firewall<\/code> per configurare il filtraggio del traffico in entrata.<\/li>\n<\/ul>\n<p>\n\u00c8 possibile installarli tramite l'interfaccia di Plesk nella sezione Extensions:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/350cf7d7ca3ae1a965a0d3f8a73594ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNon tratteremo la configurazione dettagliata delle estensioni; per i nostri scopi dimostrativi, le impostazioni di default sono sufficienti.<\/p>\n<h2>Creazione di un abbonamento e di un sito<\/h2>\n<p>\nOra dobbiamo creare un abbonamento per il nostro sito helloworld.ru e aggiungere il sottodominio dev.helloworld.ru.<\/p>\n<ol>\n<li>Creiamo l'abbonamento per il dominio helloworld.ru e indichiamo login e password per l'utente di sistema:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3932dc7492aa523b6cbd540ad7a7fe6b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn fondo alla pagina, selezioniamo la casella <i>Proteggi il dominio con Let's Encrypt<\/i>, se vogliamo configurare HTTPS per il sito:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f4d23a34764fdeb411fe2ca9cc95cb45.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Successivamente, in questo abbonamento, creiamo il sottodominio dev.helloworld.ru (per cui \u00e8 possibile emettere anche un certificato TLS gratuito):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d57e6ba71454cb537917400de4275e92.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>Installazione dei componenti del server<\/h1>\n<p>\nAbbiamo a disposizione un server con <i>OS Debian Stretch 9.12<\/i> e il pannello di controllo installato <i>Plesk Obsidian 18.0.27<\/i>.<\/p>\n<p>Dobbiamo installare e configurare per il nostro progetto:<\/p>\n<ul>\n<li>PostgreSQL (nel nostro caso ci sar\u00e0 un server con due DB per l'ambiente dev e prod).<\/li>\n<li>RabbitMQ (lo stesso, un'istanza con diversi vhost per gli ambienti).<\/li>\n<li>Due istanze di Redis (per gli ambienti dev e prod).<\/li>\n<li>Docker Registry (per la memorizzazione locale delle immagini Docker costruite).<\/li>\n<li>Interfaccia UI per il registro Docker.<\/li>\n<\/ul>\n<p><\/p>\n<h2>PostgreSQL<\/h2>\n<p>\nInsieme a Plesk \u00e8 gi\u00e0 inclusa una DBMS PostgreSQL, ma non dell'ultima versione (al momento della scrittura dell'articolo, Plesk Obsidian <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.plesk.com\/release-notes\/obsidian\/software-requirements\/\">supportava<\/a><\/noindex> Postgres versioni 8.4\u201310.8). Vogliamo installare per la nostra applicazione l'ultima versione disponibile (12.3 al momento della scrittura), quindi la installeremo manualmente.<\/p>\n<p>Ci sono molte istruzioni dettagliate per installare Postgres su Debian (<noindex><a rel=\"nofollow\" href=\"https:\/\/tecadmin.net\/install-postgresql-on-debian-9-stretch\/\">esempio<\/a><\/noindex>), quindi non entrer\u00f2 nei dettagli, ma elencher\u00f2 i comandi:<\/p>\n<pre><code class=\"bash\">wget -q https:\/\/www.postgresql.org\/media\/keys\/ACCC4CF8.asc -O - | sudo apt-key add -\nsudo sh -c 'echo \"deb http:\/\/apt.postgresql.org\/pub\/repos\/apt\/ stretch-pgdg main\" &gt;&gt; \/etc\/apt\/sources.list.d\/pgdg.list'\n\nsudo apt-get update\nsudo apt-get install postgresql postgresql-contrib\n<\/code><\/pre>\n<p>\nConsiderando che PostgreSQL ha impostazioni di default piuttosto mediocri, \u00e8 fondamentale rivedere la configurazione. In questo ci aiuter\u00e0 <noindex><a rel=\"nofollow\" href=\"http:\/\/pgconfigurator.cybertec.at\/\">calcolatore<\/a><\/noindex>: inserire i parametri del server e sostituire le impostazioni nel file <code>\/etc\/postgresql\/12\/main\/postgresql.conf<\/code>con quelle suggerite. Va detto che tali calcolatori non sono una soluzione magica, e il database dovrebbe essere ottimizzato in modo pi\u00f9 specifico, tenendo conto dell'hardware, dell'applicazione e della complessit\u00e0 delle query. Ma per iniziare, \u00e8 sufficiente.<\/p>\n<p>Oltre alle impostazioni suggerite dal calcolatore, cambiamo nel <code>postgresql.conf<\/code>la porta predefinita 5432 con un'altra (nel nostro esempio \u2014 <strong>53983<\/strong>).<\/p>\n<p>Dopo aver modificato il file di configurazione, riavviamo il postgresql-server con il comando:<\/p>\n<pre><code class=\"bash\">service postgresql restart\n<\/code><\/pre>\n<p>\nAbbiamo installato e configurato PostgreSQL. Ora creeremo un database, gli utenti per gli ambienti dev e prod e daremo agli utenti i diritti per gestire il database:<\/p>\n<pre><code class=\"bash\">$ su - postgres\npostgres:~$ create database hw_dev_db_name;\nCREATE DATABASE\npostgres:~$ create user hw_dev_db_user with password 'hw_dev_db_password';\nCREATE ROLE\npostgres:~$ grant ALL privileges ON database hw_dev_db_name to hw_dev_db_user;\nGRANT\npostgres:~$ create database hw_prod_db_name;\nCREATE DATABASE\npostgres:~$ create user hw_prod_db_user with password 'hw_prod_db_password';\nCREATE ROLE\npostgres:~$ grant ALL privileges ON database hw_prod_db_name to hw_prod_db_user;\nGRANT\n<\/code><\/pre>\n<p><\/p>\n<h2>RabbitMQ<\/h2>\n<p>\nProcediamo con l'installazione di RabbitMQ, il broker di messaggi per Celery. Si installa facilmente su Debian:<\/p>\n<pre><code class=\"bash\">wget https:\/\/packages.erlang-solutions.com\/erlang-solutions_1.0_all.deb\nsudo dpkg -i erlang-solutions_1.0_all.deb\n\nsudo apt-get update\nsudo apt-get install erlang erlang-nox\n\nsudo add-apt-repository 'deb http:\/\/www.rabbitmq.com\/debian\/ testing main'\nwget -O- https:\/\/www.rabbitmq.com\/rabbitmq-release-signing-key.asc | sudo apt-key add -\n\nsudo apt-get update\nsudo apt-get install rabbitmq-server\n<\/code><\/pre>\n<p>\nDopo l'installazione, dobbiamo creare <i>vhosts<\/i>, utenti e dare i diritti necessari:<\/p>\n<pre><code class=\"bash\">sudo rabbitmqctl add_user hw_dev_amqp_user hw_dev_amqp_password \nsudo rabbitmqctl set_user_tags hw_dev_amqp_user administrator\nsudo rabbitmqctl add_vhost hw_dev_vhost\nsudo rabbitmqctl set_permissions -p hw_dev_vhost hw_dev_amqp_user \".*\" \".*\" \".*\"\n\nsudo rabbitmqctl add_user hw_prod_amqp_user hw_prod_amqp_password \nsudo rabbitmqctl set_user_tags hw_prod_amqp_user administrator\nsudo rabbitmqctl add_vhost hw_prod_vhost\nsudo rabbitmqctl set_permissions -p hw_prod_vhost hw_prod_amqp_user \".*\" \".*\" \".*\"\n<\/code><\/pre>\n<p><\/p>\n<h2>Redis<\/h2>\n<p>\nOra installiamo e configuriamo l'ultimo componente per la nostra applicazione: Redis. Sar\u00e0 utilizzato come backend per memorizzare i risultati delle attivit\u00e0 di Celery.<\/p>\n<p>Solleveremo due contenitori Docker con Redis per gli ambienti dev e prod utilizzando l'estensione <code>Docker<\/code> per Plesk.<\/p>\n<ol>\n<li>Accediamo a Plesk, andiamo nella sezione Estensioni, cerchiamo l'estensione Docker e la installiamo (abbiamo bisogno della versione gratuita):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/5638f9b40274e4f7441434279c76a426.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Passiamo all'estensione installata, troviamo tramite ricerca l'immagine <code>redis bitnami<\/code> e installiamo l'ultima versione:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/578bbbed6a66a60a4f7410f7d61ea542.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Accediamo al contenitore scaricato e regoliamo la configurazione: indichiamo la porta, la dimensione massima della RAM allocata, la password nelle variabili d'ambiente e montiamo il volume:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/8bc804b794735412f5e510b3049c5667.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Eseguiamo i passi 2-3 per il contenitore prod, cambiando solo le impostazioni: porta, password, dimensione della RAM e percorso della cartella volume sul server:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3af50181c8b3e1ace23a8de11826cb51.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h2>Docker Registry<\/h2>\n<p>\nOltre ai servizi di base, sarebbe utile installare sul server un proprio repository di immagini Docker. Fortunatamente, lo spazio sui server \u00e8 ora abbastanza economico (certamente pi\u00f9 economico di un abbonamento a DockerHub), e il processo di installazione di un repository privato \u00e8 molto semplice. <\/p>\n<p>Vogliamo avere installati:<\/p>\n<ul>\n<li>un repository Docker protetto da password, accessibile tramite un sottodominio <noindex><a rel=\"nofollow\" href=\"https:\/\/docker.helloworld.ru\">https:\/\/docker.helloworld.ru<\/a><\/noindex>;<\/li>\n<li>Interfaccia UI per visualizzare le immagini nel repository, accessibile all'indirizzo <noindex><a rel=\"nofollow\" href=\"https:\/\/docker-ui.helloworld.ru\">https:\/\/docker-ui.helloworld.ru<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nPer fare ci\u00f2:<\/p>\n<ol>\n<li>Creiamo in Plesk due sottodomini nel nostro abbonamento: docker.helloworld.ru e docker-ui.helloworld.ru, e configuriamo i certificati Let\u2019s Encrypt per essi.<\/li>\n<li>Nella cartella del sottodominio docker.helloworld.ru aggiungiamo il file <code>docker-compose.yml<\/code> con il seguente contenuto:\n<pre><code class=\"plaintext\">version: \"3\"\n\nservices:\n  docker-registry:\n    image: \"registry:2\"\n    restart: always\n    ports:\n      - \"53985:5000\"\n    environment:\n      REGISTRY_AUTH: htpasswd\n      REGISTRY_AUTH_HTPASSWD_REALM: basic-realm\n      REGISTRY_AUTH_HTPASSWD_PATH: \/auth\/.htpasswd\n      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: \/data\n    volumes:\n      - .\/.docker-registry.htpasswd:\/auth\/.htpasswd\n      - .\/data:\/data\n\n  docker-registry-ui:\n    image: konradkleine\/docker-registry-frontend:v2\n    restart: always\n    ports:\n      - \"53986:80\"\n    environment:\n      VIRTUAL_HOST: '*, https:\/\/*'\n      ENV_DOCKER_REGISTRY_HOST: 'docker-registry'\n      ENV_DOCKER_REGISTRY_PORT: 5000\n    links:\n      - 'docker-registry'\n<\/code><\/pre>\n<p>\n<\/li>\n<li>Via SSH generiamo il file .htpasswd per l'autenticazione Basic nel Docker repository:\n<pre><code class=\"plaintext\">htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password<\/code><\/pre>\n<\/li>\n<li>Costruiamo e avviamo i contenitori:\n<pre><code class=\"plaintext\">docker-compose up -d<\/code><\/pre>\n<\/li>\n<li>E dobbiamo reindirizzare Nginx ai nostri contenitori. Questo pu\u00f2 essere fatto tramite Plesk. <\/li>\n<\/ol>\n<p>\nLe seguenti operazioni devono essere eseguite per i sottodomini docker.helloworld.ru e docker-ui.helloworld.ru:<\/p>\n<p>Nella sezione <i>Dev Tools<\/i> del nostro sito entriamo in <i>Regole di Proxy Docker<\/i>:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d9c05901d95d61ce5db27a1a7cfddf49.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nE aggiungiamo una regola per il proxy del traffico in entrata al nostro contenitore:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/faa85cf6da648ef55b7c87c44040efb1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<ol>\n<li>Controlliamo di poter accedere al nostro container dalla macchina locale:\n<pre><code class=\"plaintext\">$ docker login docker.helloworld.ru -u hw_docker_admin -p hw_docker_password\nATTENZIONE! Utilizzare --password tramite CLI \u00e8 insicuro. Utilizza --password-stdin.\nAccesso riuscito<\/code><\/pre>\n<\/li>\n<li>Verificheremo anche il funzionamento del sottodominio docker-ui.helloworld.ru:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/fdd6e1396c27c02a88bcbd7efb12f247.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nQuando si clicca su Browse repositories, il browser mostrer\u00e0 una finestra per l'autenticazione, dove sar\u00e0 necessario inserire il nome utente e la password per il repository. Dopodich\u00e9 verremo reindirizzati a una pagina con l'elenco dei repository (per ora sar\u00e0 vuota):<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f2d75edb530e9d746095ae02d7793c1f.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h2>Apriamo le porte nel Plesk Firewall<\/h2>\n<p>\nDopo aver installato e configurato i componenti, dobbiamo aprire le porte in modo che i componenti siano accessibili dai container Docker e dalla rete esterna.<\/p>\n<p>Vediamo come farlo, usando come esempio l'estensione Firewall che abbiamo installato in precedenza per Plesk.<\/p>\n<ol>\n<li>Andiamo su <i>Strumenti e impostazioni &gt; Impostazioni &gt; Firewall<\/i>:<br \/>\n<img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d54b4941944fe191574472a475cbaebe.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Andiamo su <i>Modifica le regole del firewall di Plesk &gt; Aggiungi regola personalizzata<\/i> e apriamo le seguenti porte TCP per la sottorete Docker (<strong>172.0.0.0\/8)<\/strong>:<br \/>\nRabbitMQ: 1883, 4369, 5671-5672, 25672, 61613-61614<br \/>\nRedis: 32785, 32786<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3359845f9bd6dc303274da737d4b27a2.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Aggiungeremo anche una regola che aprir\u00e0 al mondo esterno le porte di PostgreSQL e della pannello di gestione di RabbitMQ:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f812cfc7127da469507538ea48b2c80d.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Applichiamo le regole utilizzando il pulsante Applica modifiche:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/e85444ae717aaefa796664003fc86578.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>Impostazione di CI\/CD in Github Actions<\/h1>\n<p>\nPassiamo alla parte pi\u00f9 interessante: la configurazione della pipeline di integrazione continua e distribuzione del nostro progetto sul server.<\/p>\n<p>Questo pipeline sar\u00e0 composto da due parti: <\/p>\n<ul>\n<li>la creazione dell'immagine e l'esecuzione dei test (per il backend) \u2014 sul lato di Github;<\/li>\n<li>l'esecuzione delle migrazioni (per il backend) e il deployment dei container \u2014 sul server.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Deployment in Plesk<\/h2>\n<p>\nIniziamo con il secondo punto (poich\u00e9 dipende dal primo).<\/p>\n<p>Configureremo il processo di deployment usando l'estensione Git per Plesk.<\/p>\n<p>Consideriamo un esempio con l'ambiente Prod per il repository Backend.<\/p>\n<ol>\n<li>Accediamo alla sottoscrizione del nostro sito Helloworld e passiamo alla sezione Git:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/b55b371560ea87a2954b6553a7cefc49.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Inseriamo nel campo \u00abRemote Git repository\u00bb il link al nostro repository su Github e cambiamo la cartella predefinita <code>httpdocs<\/code> in un'altra (ad esempio, <code>\/httpdocs\/hw_back<\/code>):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/db9ba9adb6e2e1bed5da7ae8e88c76e1.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Copiare la chiave SSH Public dal passaggio precedente e <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/settings\/ssh\/new\">aggiungerla<\/a><\/noindex> nelle impostazioni di Github.<\/li>\n<li>Facciamo clic su OK sullo schermo nel punto 2, dopo di che saremo reindirizzati alla pagina del repository in Plesk. Ora dobbiamo configurare l'aggiornamento del repository al momento dei commit nel ramo master. Per fare ci\u00f2, passiamo a <i>Impostazioni del Repository<\/i> e salviamo il valore <code>Webhook URL<\/code> (ci servir\u00e0 pi\u00f9 tardi durante la configurazione delle Github Actions):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/14d48a357f821d734d40dc434ab3682a.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Nel campo Actions sullo schermo del passaggio precedente, inseriamo lo script per avviare il deployment:\n<pre><code class=\"plaintext\">cd {REPOSITORY_ABSOLUTE_PATH}\n.ci-cd\/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID} <\/code><\/pre>\n<p>\ndove: <\/p>\n<p><code>{REPOSITORY_ABSOLUTE_PATH}<\/code> \u2014 percorso della cartella del backend del repository prod sul server;<br \/>\n<code>{ENV}<\/code> \u2014 ambiente (dev\/prod), nel nostro caso <code>prod<\/code>;<br \/>\n<code>{DOCKER_REGISTRY_HOST}<\/code> \u2014 host del nostro repository docker<br \/>\n<code>{TG_BOT_TOKEN}<\/code> \u2014 token del bot di Telegram;<br \/>\n<code>{TG_CHAT_ID}<\/code> \u2014 ID della chat\/canale per l'invio di notifiche.<\/p>\n<p>Esempio di script:<\/p>\n<pre><code class=\"plaintext\">cd \/var\/www\/vhosts\/helloworld.ru\/httpdocs\/hw_back\/\n.ci-cd\/deploy.sh dev docker.helloworld.ru docker_user docker_password 12345678:AAbcdEfghCH1vGbCasdfSAs0K5PALDsaw -1001234567890<\/code><\/pre>\n<\/li>\n<li>Aggiungiamo l'utente dal nostro abbonamento al gruppo Docker (affinch\u00e9 possa gestire i container):\n<pre><code class=\"plaintext\">sudo usermod -aG docker helloworld_admin<\/code><\/pre>\n<\/li>\n<\/ol>\n<p>\nL'ambiente Dev per il repository backend e il frontend sono configurati in modo analogo.<\/p>\n<h2>Pipeline di deployment in Github Actions<\/h2>\n<p>\nPassiamo alla configurazione della prima parte del nostro piano CI\/CD in Github Actions.<\/p>\n<h3>Backend<\/h3>\n<p>\nLa pipeline \u00e8 descritta nel <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/blob\/master\/.github\/workflows\/deploy.yml\">file deploy.yml<\/a><\/noindex>. <\/p>\n<p>Ma prima di analizzarlo, compiliamo in Github le Secret-variable necessarie. A questo scopo, andiamo su <i>Impostazioni -&gt; Segreti<\/i>:<\/p>\n<ul>\n<li><code>DOCKER_REGISTRY<\/code> \u2014 host del nostro repository Docker (docker.helloworld.ru);<\/li>\n<li><code>DOCKER_LOGIN<\/code> \u2014 login per il repository Docker;<\/li>\n<li><code>DOCKER_PASSWORD<\/code> \u2014 password per accedervi;<\/li>\n<li><code>DEPLOY_HOST<\/code> \u2014 host dove \u00e8 disponibile il pannello di amministrazione Plesk (esempio: <noindex><a rel=\"nofollow\" href=\"https:\/\/helloworld.ru\">helloworld.ru<\/a><\/noindex>:8443 oppure <noindex><a rel=\"nofollow\" href=\"http:\/\/123.4.56.78\">123.4.56.78<\/a><\/noindex>:8443);<\/li>\n<li><code>DEPLOY_BACK_PROD_TOKEN<\/code> \u2014 token per il deployment nel repository prod sul server (lo abbiamo ottenuto nella fase di Distribuzione in Plesk p. 4);<\/li>\n<li><code>DEPLOY_BACK_DEV_TOKEN<\/code> \u2014 token per il deployment nel repository dev sul server.<\/li>\n<\/ul>\n<p>\nIl processo di deployment \u00e8 semplice e si compone di tre passaggi principali: <\/p>\n<ul>\n<li>assemblaggio e pubblicazione dell'immagine nel nostro repository;<\/li>\n<li>esecuzione dei test all'interno di un container basato sull'immagine appena costruita;<\/li>\n<li>implementazione nell'ambiente desiderato in base al branch (dev\/master).<\/li>\n<\/ul>\n<p><\/p>\n<h3>Frontend<\/h3>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\/blob\/master\/.github\/workflows\/deploy.yml\">Il file deploy.yml per il repository frontend<\/a><\/noindex> difficilmente si differenzia da quello backend. In esso manca il passo con l'esecuzione dei test e cambiano i nomi dei token per il deployment. I segreti per il repository frontend, tra l'altro, devono essere compilati separatamente.<\/p>\n<h1>Configurazione del sito<\/h1>\n<p><\/p>\n<h2>Proxying del traffico attraverso Nginx<\/h2>\n<p>\nBene, siamo arrivati alla fine. Resta solo da configurare il proxy del traffico in entrata e in uscita nel nostro container tramite Nginx. Questo processo l'abbiamo gi\u00e0 esaminato nel punto 5 della configurazione di Docker Registry. Lo stesso va ripetuto per le parti backend e frontend negli ambienti dev e prod.<\/p>\n<p>Riporter\u00f2 degli screenshot delle impostazioni.<\/p>\n<h3>Backend<\/h3>\n<p>\n<img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f3c38dc1c25b648db000c72ef2d30b2a.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h3>Frontend<\/h3>\n<p>\n<img decoding=\"async\" alt=\"CI\/CD in Github Actions per un progetto Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/35d4d8ef3489608db1e173b7f214ca03.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<strong>Importante precisazione<\/strong>. Nel container frontend verranno proxyizzati tutti gli URL, tranne quelli che iniziano con <code>\/api\/<\/code> \u2014 questi saranno proxyizzati al container backend (quindi <strong>nel container backend tutti i gestori devono iniziare con <\/strong><strong><code>\/api\/<\/code><\/strong>).<\/p>\n<h2>Risultati<\/h2>\n<p>\nOra il nostro sito dovrebbe essere accessibile agli indirizzi helloworld.ru e dev.helloworld.ru (rispettivamente per gli ambienti prod e dev).<\/p>\n<p>In sintesi, abbiamo appreso come preparare una semplice applicazione su Flask e Angular e configurare un pipeline in Github Actions per il deploy su un server gestito da Plesk.<\/p>\n<p>Ripropongo i link ai repository con il codice: <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">backend<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\">frontend<\/a><\/noindex>.<br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/domclick\/blog\/506802\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 CI\/CD \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Plesk \u0438 Github Actions. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u0443\u0434\u0435\u043c \u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043d\u0435\u0437\u0430\u043c\u044b\u0441\u043b\u043e\u0432\u0430\u0442\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u00abHelloworld\u00bb. \u041e\u043d \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 Python-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Flask, \u0441 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438 \u043d\u0430 Celery \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c \u043d\u0430 Angular 8. \u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438: \u0431\u044d\u043a\u0435\u043d\u0434, \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434. \u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":85647,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-85646","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 CI\/CD \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Plesk \u0438 Github Actions. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u0443\u0434\u0435\u043c \u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043d\u0435\u0437\u0430\u043c\u044b\u0441\u043b\u043e\u0432\u0430\u0442\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u00abHelloworld\u00bb. \u041e\u043d \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 Python-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Flask, \u0441 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438 \u043d\u0430 Celery \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c \u043d\u0430 Angular 8. \u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438: \u0431\u044d\u043a\u0435\u043d\u0434, \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434. \u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442\" \/>\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\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\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\udd47CI\/CD \u0432 Github Actions \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 Flask+Angular | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 CI\/CD \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Plesk \u0438 Github Actions. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u0443\u0434\u0435\u043c \u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043d\u0435\u0437\u0430\u043c\u044b\u0441\u043b\u043e\u0432\u0430\u0442\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u00abHelloworld\u00bb. \u041e\u043d \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 Python-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Flask, \u0441 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438 \u043d\u0430 Celery \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c \u043d\u0430 Angular 8. \u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438: \u0431\u044d\u043a\u0435\u043d\u0434, \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434. \u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular\" \/>\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-06-17T23:42:15+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-06-17T23:42:15+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\udd47CI\/CD in Github Actions per un progetto su Flask+Angular | ProHoster","description":"In questo articolo condivider\u00f2 la mia esperienza nella configurazione del CI\/CD utilizzando il pannello di controllo Plesk e Github Actions. Oggi impareremo a fare il deploy di un semplice progetto dal titolo \u00abHelloworld\u00bb. \u00c8 scritto nel framework Python Flask, con worker su Celery e frontend su Angular 8. Link ai repository: backend, frontend. Nella prima parte dell'articolo daremo un'occhiata al nostro progetto.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","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\udd47CI\/CD \u0432 Github Actions \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 Flask+Angular | ProHoster","og:description":"\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 CI\/CD \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Plesk \u0438 Github Actions. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u0443\u0434\u0435\u043c \u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043d\u0435\u0437\u0430\u043c\u044b\u0441\u043b\u043e\u0432\u0430\u0442\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u00abHelloworld\u00bb. \u041e\u043d \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 Python-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Flask, \u0441 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438 \u043d\u0430 Celery \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c \u043d\u0430 Angular 8. \u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438: \u0431\u044d\u043a\u0435\u043d\u0434, \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434. \u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","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-06-17T23:42:15+00:00","article:modified_time":"2020-06-17T23:42:15+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"85646","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 13:12:50","updated":"2022-09-29 06:56:26"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/85646","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=85646"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/85646\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/85647"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=85646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=85646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=85646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}