{"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\/sl\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","title":{"rendered":"CI\/CD v Github Actions za projekt na Flask+Angular","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/26d0945a41bbf3c98fa92f15be5b8c6c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nV tem \u010dlanku bom delil svojo izku\u0161njo nastavitve CI\/CD z uporabo nadzorne plo\u0161\u010de Plesk in Github Actions. Danes se bomo nau\u010dili, kako postaviti preprost projekt z nepremi\u0161ljenim imenom 'Helloworld'. Napisan je v Python okviru Flask, z delavci na Celery in frontendov z Angular 8.<\/p>\n<p><b>Povezave do repozitorijev:<\/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>V prvem delu \u010dlanka bomo pregledali na\u0161 projekt in njegove dele. V drugem se bomo osredoto\u010dili na nastavitev Pleska ter namestitev potrebnih raz\u0161iritev in komponent (Baza podatkov, RabbitMQ, Redis, Docker itd.).<\/p>\n<p>V tretjem delu se bomo kon\u010dno ukvarjali z nastavitvijo pipeline-a za postavitev na\u0161ega projekta na stre\u017enik v dev- in prod-okolje. Nato bomo zagnali spletno stran na stre\u017eniku.<\/p>\n<p>In ja, pozabil sem se predstaviti. Ime mi je Oleg Borzov, sem fullstack razvijalec v ekipi CRM za upravljavce hipotekarnih posojil v Domklik. <br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1>Pregled projekta<\/h1>\n<p>\nNajprej si poglejmo dva repozitorija projekta \u2014 backend in frontend \u2014 in preletimo kodo.<\/p>\n<h2>Backend del: Flask+Celery<\/h2>\n<p>\nZa backend sem izbral precej priljubljeno kombinacijo med Python razvijalci: okvir Flask (za API) in Celery (za obdelavo nalog). Kot ORM se uporablja SQLAlchemy. Za migracije se uporablja Alembic. Za validacijo JSON v ro\u010dajih \u2014 Marshmallow.<\/p>\n<p>V <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">repozitoriji<\/a><\/noindex> obstaja datoteka Readme.md s podrobnim opisom strukture in navodili za zagon projekta.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_web\">API spletnega dela<\/a><\/noindex> je dovolj preprost, sestoji iz 6 ro\u010dajev:<\/p>\n<ul>\n<li><code>\/ping<\/code> \u2014 za preverjanje dostopnosti;<\/li>\n<li>ro\u010daji za registracijo, avtentifikacijo, deavtentifikacijo in pridobitev avtentificiranega uporabnika;<\/li>\n<li>ro\u010daj za po\u0161iljanje emailov, ki dodaja nalogo v \u010dakalno vrsto Celery.<\/li>\n<\/ul>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_celery\">Del Celery<\/a><\/noindex> je \u0161e bolj preprost, tam je le ena naloga <code>send_mail_task<\/code>.<\/p>\n<p>V mapi <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/conf\/\">\/conf<\/a><\/noindex> so dve podmapi: <\/p>\n<ul>\n<li><code>docker<\/code> s dvema Docker datotekama (<code>base.dockerfile<\/code> za gradnjo redko spreminjajo\u010de se osnovne slike in <code>Dockerfile<\/code> za glavne gradnje);<\/li>\n<li><code>.env_files<\/code> \u2014 z datotekami z okolijskimi spremenljivkami za razli\u010dna okolja. <\/li>\n<\/ul>\n<p>\nV korenu projekta so \u0161tiri datoteke docker-compose:<\/p>\n<ul>\n<li><code>docker-compose.local.db.yml<\/code> za dvig lokalne baze podatkov za razvijanje;<\/li>\n<li><code>docker-compose.local.workers.yml<\/code> za lokalni dvig delavca, baze, Redis-a in RabbitMQ;<\/li>\n<li><code>docker-compose.test.yml<\/code> za izvajanje testov pri razporejanju;<\/li>\n<li><code>docker-compose.yml<\/code> za postavitev.<\/li>\n<\/ul>\n<p>\nIn zadnja zanimiva mapa je \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/.ci-cd\">.ci-cd<\/a><\/noindex>. V njej so shell skripti za postavitev:<\/p>\n<ul>\n<li><code>deploy.sh<\/code> \u2014 zagon migracij in postavitve. Za\u017eene se na stre\u017eniku po kon\u010danju gradnje in izvajanju testov v Github Actions;<\/li>\n<li><code>rollback.sh<\/code> \u2014 povratak kontejnerjev na prej\u0161njo razli\u010dico gradnje;<\/li>\n<li><code>curl_tg.sh<\/code> \u2014 po\u0161iljanje obvestil o postavitvi v Telegram.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Frontend na Angular<\/h2>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\">Repozitorij z frontom<\/a><\/noindex> je precej la\u017eji od backenda. Fronta sestavlja tri strani:<\/p>\n<ul>\n<li>Glavna stran s obrazcem za po\u0161iljanje e-po\u0161te in gumbom za izhod.<\/li>\n<li>Stran za prijavo.<\/li>\n<li>Stran za registracijo.<\/li>\n<\/ul>\n<p>\nGlavna stran izgleda skromno:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/4db36add2c4277397724b0d71b96ff06.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nV korenu le\u017eita dva datoteka <code>Dockerfile<\/code> in <code>docker-compose.yml<\/code>, prav tako pa znana mapa <code>.ci-cd<\/code> s nekoliko manj skripti kot v repozitoriju backend (odstranjeni so skripti za zagon testov).<\/p>\n<h1>Ustanovitev projekta v Plesk<\/h1>\n<p>\nZa\u010dnimo s konfiguracijo Pleska in ustvarjanjem naro\u010dnine za na\u0161o spletno stran.<\/p>\n<h2>Namestitev raz\u0161iritev<\/h2>\n<p>\nV Plesku potrebujemo \u0161tiri raz\u0161iritve: <\/p>\n<ul>\n<li><code>Docker<\/code> za upravljanje in vizualno prikazovanje stanja kontejnerjev v administraciji Plesk;<\/li>\n<li><code>Git<\/code> za nastavitev koraka prenosa na stre\u017eniku;<\/li>\n<li><code>Let's Encrypt<\/code> za generiranje (in samodejno podalj\u0161anje) brezpla\u010dnih TLS-certifikatov;<\/li>\n<li><code>Firewal<\/code> za nastavitev filtriranja prihajajo\u010dega prometa.<\/li>\n<\/ul>\n<p>\nNamestimo jih lahko preko administracije Plesk v razdelku Raz\u0161iritve:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/350cf7d7ca3ae1a965a0d3f8a73594ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPodrobno nastavitev raz\u0161iritev ne bomo obravnavali, za na\u0161e demo-namen bomo uporabili privzete nastavitve.<\/p>\n<h2>Ustvarjanje naro\u010dnine in strani<\/h2>\n<p>\nNato moramo ustvariti naro\u010dnino za na\u0161o spletno stran helloworld.ru in dodati poddomeno dev.helloworld.ru.<\/p>\n<ol>\n<li>Ustvarimo naro\u010dnino za domeno helloworld.ru in navedemo uporabni\u0161ko ime-geslo za sistemskega uporabnika:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3932dc7492aa523b6cbd540ad7a7fe6b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNa dnu strani ozna\u010dite <i>Za\u0161\u010ditite domeno z Let&#8217;s Encrypt<\/i>, \u010de \u017eelimo nastaviti HTTPS za spletno stran:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f4d23a34764fdeb411fe2ca9cc95cb45.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Nato v tej naro\u010dnini ustvarimo poddomeno dev.helloworld.ru (za katero je prav tako mo\u017eno izdati brezpla\u010den TLS-certifikat):\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d57e6ba71454cb537917400de4275e92.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>Namestitev stre\u017eni\u0161kih komponent<\/h1>\n<p>\nImamo stre\u017enik z <i>OS Debian Stretch 9.12<\/i> in name\u0161\u010denim upravljanjem <i>Plesk Obsidian 18.0.27<\/i>.<\/p>\n<p>Moramo namestiti in konfigurirati za na\u0161 projekt:<\/p>\n<ul>\n<li>PostgreSQL (v na\u0161em primeru bo en stre\u017enik z dvema DB za dev- in prod-okolje).<\/li>\n<li>RabbitMQ (isto, en instanca z razli\u010dnimi vhosts za okolja).<\/li>\n<li>Dve instanci Redis (za dev- in prod-okolje).<\/li>\n<li>Docker Registry (za lokalno shranjevanje zbranih Docker-slik).<\/li>\n<li>U\u010dinkovinski vmesnik za Docker registry.<\/li>\n<\/ul>\n<p><\/p>\n<h2>PostgreSQL<\/h2>\n<p>\nV kompletu s Pleskom \u017ee pride SSQL PostgreSQL, vendar ne najnovej\u0161e razli\u010dice (ob \u010dasu pisanja \u010dlanka Plesk Obsidian <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.plesk.com\/release-notes\/obsidian\/software-requirements\/\">je podpiral<\/a><\/noindex> Postgres razli\u010dic 8.4\u201310.8). Mi pa \u017eelimo za svojo aplikacijo najnovej\u0161o razli\u010dico (12.3 ob \u010dasu pisanja \u010dlanka), zato jo bomo namestili ro\u010dno.<\/p>\n<p>Podrobnih navodil za namestitev Postgresa na Debianu je na spletu ogromno (<noindex><a rel=\"nofollow\" href=\"https:\/\/tecadmin.net\/install-postgresql-on-debian-9-stretch\/\">primer<\/a><\/noindex>), zato jih ne bom podrobno opisoval, le navedel bom ukaze:<\/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>\n\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432 PostgreSQL \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0440\u0435\u0434\u043d\u0438\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u0440\u0435\u0433\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e. \u0412 \u044d\u0442\u043e\u043c \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 <noindex><a rel=\"nofollow\" href=\"http:\/\/pgconfigurator.cybertec.at\/\">\u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440<\/a><\/noindex>: \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>\/etc\/postgresql\/12\/main\/postgresql.conf<\/code>\u043d\u0430 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435. \u0422\u0443\u0442 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0443\u0442\u043e\u0447\u043d\u0438\u0442\u044c, \u0447\u0442\u043e \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440\u044b \u2014 \u043d\u0435 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0438 \u0431\u0430\u0437\u0443 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f, \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041d\u043e \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u044d\u0442\u043e\u0433\u043e \u0432\u043f\u043e\u043b\u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e.<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440, \u0442\u0430\u043a\u0436\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0432 <code>postgresql.conf<\/code>\u043f\u043e\u0440\u0442 5432, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 (\u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u2014 <strong>53983<\/strong>).<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 postgresql-server \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<\/p>\n<pre><code class=\"bash\">service postgresql restart\n<\/code><\/pre>\n<p>\n\u041c\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 PostgreSQL. \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0411\u0414, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043b\u044f dev- \u0438 prod-\u0441\u0440\u0435\u0434, \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u0438\u043c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0411\u0414:<\/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>\n\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 RabbitMQ \u2014 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0434\u043b\u044f Celery. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043d\u0430 Debian \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e:<\/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>\n\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c <i>vhosts<\/i>, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0430:<\/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>\n\u0422\u0435\u043f\u0435\u0440\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u2014 Redis. \u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0437\u0430\u0434\u0430\u0447 Celery.<\/p>\n<p>\u041c\u044b \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u043c \u0434\u0432\u0430 Docker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 Redis \u0434\u043b\u044f dev- \u0438 prod-\u0441\u0440\u0435\u0434 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f <code>Docker<\/code> \u0434\u043b\u044f Plesk.<\/p>\n<ol>\n<li>Vstopimo v Plesk, pojdimo na razdelek Raz\u0161iritve, poi\u0161\u010dimo raz\u0161iritev Docker in jo namestimo (potrebna nam je brezpla\u010dna razli\u010dica):\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/5638f9b40274e4f7441434279c76a426.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Pojdimo na name\u0161\u010deno raz\u0161iritev, poi\u0161\u010dimo sliko <code>redis bitnami<\/code> in namestimo najnovej\u0161o razli\u010dico:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/578bbbed6a66a60a4f7410f7d61ea542.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Vstopimo v preneseni posod in prilagodimo konfiguracijo: dolo\u010dimo vrata, najve\u010djo dodeljeno velikost RAM-a, geslo v spremenljivkah okolja in montiramo volumen:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/8bc804b794735412f5e510b3049c5667.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Izvedemo korake 2-3 za prod-posodo, v nastavitvah samo spremenimo parametre: vrata, geslo, velikost RAM-a in pot do mape volume na stre\u017eniku:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na 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>\nPoleg osnovnih storitev bi bilo dobro namestiti na stre\u017enik lastno skladi\u0161\u010de Docker slik. Na sre\u010do je prostor na stre\u017enikih zdaj precej poceni (resni\u010dno ceneje kot naro\u010dnina na DockerHub), pa tudi postopek namestitve zasebnega skladi\u0161\u010da je zelo enostaven. <\/p>\n<p>\u017delimo, da imamo name\u0161\u010deno:<\/p>\n<ul>\n<li>z geslom za\u0161\u010diteno Docker skladi\u0161\u010de, dostopno preko poddomene <noindex><a rel=\"nofollow\" href=\"https:\/\/docker.helloworld.ru\">https:\/\/docker.helloworld.ru<\/a><\/noindex>;<\/li>\n<li>UI vmesnik za ogled slik v skladi\u0161\u010du, dostopen na naslovu <noindex><a rel=\"nofollow\" href=\"https:\/\/docker-ui.helloworld.ru\">https:\/\/docker-ui.helloworld.ru<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nZato:<\/p>\n<ol>\n<li>Ustvarili bomo v Plesku dve poddomeni v na\u0161i naro\u010dnini: docker.helloworld.ru in docker-ui.helloworld.ru, in konfigurirali bomo certifikate Let\u2019s Encrypt zanje.<\/li>\n<li>V mapo poddomene docker.helloworld.ru dodamo datoteko <code>docker-compose.yml<\/code> \u0441 \u0442\u0430\u043a\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:\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>Preko SSH bomo ustvarili datoteko .htpasswd za osnovno avtentikacijo v Docker skladi\u0161\u010du:\n<pre><code class=\"plaintext\">htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password<\/code><\/pre>\n<\/li>\n<li>Sestavili bomo in zagnali posode:\n<pre><code class=\"plaintext\">docker-compose up -d<\/code><\/pre>\n<\/li>\n<li>In treba je preusmeriti Nginx na na\u0161e posode. To lahko storimo preko Pleska. <\/li>\n<\/ol>\n<p>\nNaslednje korake je treba izvesti za poddomene docker.helloworld.ru in docker-ui.helloworld.ru:<\/p>\n<p>V razdelku <i>Razvijalna orodja<\/i> na\u0161e strani gremo v <i>Docker Proxy Rules<\/i>:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d9c05901d95d61ce5db27a1a7cfddf49.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn dodamo pravilo za posredovanje vhodnega prometa v na\u0161o posodo:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/faa85cf6da648ef55b7c87c44040efb1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<ol>\n<li>Preverimo, ali se lahko prijavimo v na\u0161o posodo z lokalnega ra\u010dunalnika:\n<pre><code class=\"plaintext\">$ docker login docker.helloworld.ru -u hw_docker_admin -p hw_docker_password\nWARNING! Using --password via the CLI is insecure. Use --password-stdin.\nPrijava uspe\u0161na<\/code><\/pre>\n<\/li>\n<li>Prav tako bomo preverili delovanje poddomene docker-ui.helloworld.ru:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/fdd6e1396c27c02a88bcbd7efb12f247.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nKo boste kliknili na Browse repositories, bo brskalnik odprl okno za prijavo, v katerega boste morali vnesti uporabni\u0161ko ime in geslo za repozitorij. Nato vas bo preusmerilo na stran s seznamom repozitorijev (zaenkrat bo ta prazna):<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f2d75edb530e9d746095ae02d7793c1f.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h2>Odprite porte v Plesk Firewall<\/h2>\n<p>\nPo namestitvi in nastavitvi komponent moramo odpreti porte, da bodo komponente dostopne iz Docker kontejnerjev in zunanje mre\u017ee.<\/p>\n<p>Poglejmo, kako to storiti, na primeru raz\u0161iritve Firewall za Plesk, ki smo jo namestili prej.<\/p>\n<ol>\n<li>Pojdite na <i>Orodja in nastavitve &gt; Nastavitve &gt; Firewall<\/i>:<br \/>\n<img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d54b4941944fe191574472a475cbaebe.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Pojdite na <i>Spremenite pravila po\u017earnega zidu Plesk &gt; Dodajte po meri pravilo<\/i> in odprite naslednje TCP porte za podomre\u017eje 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 v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3359845f9bd6dc303274da737d4b27a2.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Prav tako dodajmo pravilo, ki bo odprlo porte PostgreSQL in upravljalske plo\u0161\u010de RabbitMQ za zunanji svet:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f812cfc7127da469507538ea48b2c80d.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Pravila uporabimo s klikom na gumb Uporabi spremembe:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/e85444ae717aaefa796664003fc86578.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>Nastavitev CI\/CD v Github Actions<\/h1>\n<p>\nZa\u010dnimo s najbolj zanimivim delom \u2014 nastavitvijo pipeline za neprekinjeno integracijo in dostavo na\u0161ega projekta na stre\u017enik.<\/p>\n<p>Ta pipeline bo sestavljen iz dveh delov: <\/p>\n<ul>\n<li>zbiranje slike in izvajanje testov (za backend) \u2014 na strani Githuba;<\/li>\n<li>zagon migracij (za backend) in deploy kontejnerjev \u2014 na stre\u017eniku.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Deploy v Plesk<\/h2>\n<p>\nNajprej se osredoto\u010dimo na drugi del (saj je od njega odvisen prvi).<\/p>\n<p>Postopek deploya bomo nastavili z uporabo raz\u0161iritve Git za Plesk.<\/p>\n<p>Oglejmo si primer s produkcijskim okoljem za Backend repozitorij.<\/p>\n<ol>\n<li>Prijavite se v naro\u010dnino na\u0161ega spletnega mesta Helloworld in pojdite v pododdelek Git:\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/b55b371560ea87a2954b6553a7cefc49.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>V polje 'Remote Git repository' prilepite povezavo na na\u0161 Github repozitorij in spremenite privzeto mapo <code>httpdocs<\/code> na drugo (na primer, <code>\/httpdocs\/hw_back<\/code>):\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/db9ba9adb6e2e1bed5da7ae8e88c76e1.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Kopirajte javni klju\u010d SSH z prej\u0161njega koraka in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/settings\/ssh\/new\">dodajamo<\/a><\/noindex> ga prilepite v nastavitvah Githuba.<\/li>\n<li>Kliknite OK na zaslonu v to\u010dki 2, nato pa vas bo preusmerilo na stran repozitorija v Plesku. Zdaj moramo nastaviti posodabljanje repozitorija ob commitih v osnovno vejo. Zato pojdite na <i>Nastavitve repozitorija<\/i> in shranite vrednost <code>Webhook URL<\/code> (bomo ga potrebovali kasneje pri nastavitvi Github Actions):\n<p><img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/14d48a357f821d734d40dc434ab3682a.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>V polju Dejanja na zaslonu iz prej\u0161nje to\u010dke vnesite skripto za zagon deploya:\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>\nkde: <\/p>\n<p><code>{REPOSITORY_ABSOLUTE_PATH}<\/code> \u2014 pot do mape prod backend repozitorija na stre\u017eniku;<br \/>\n<code>{ENV}<\/code> \u2014 okolje (dev\/prod), v na\u0161em primeru <code>prod<\/code>;<br \/>\n<code>{DOCKER_REGISTRY_HOST}<\/code> \u2014 gostitelj na\u0161ega docker repozitorija<br \/>\n<code>{TG_BOT_TOKEN}<\/code> \u2014 \u017eeton Telegram bota;<br \/>\n<code>{TG_CHAT_ID}<\/code> \u2014 ID klepeta\/kanala za po\u0161iljanje obvestil.<\/p>\n<p>Primer skripte:<\/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>Dodajamo korisnika iz na\u0161e pretplate u Docker grupu (kako bi mogao upravljati kontejnerima):\n<pre><code class=\"plaintext\">sudo usermod -aG docker helloworld_admin<\/code><\/pre>\n<\/li>\n<\/ol>\n<p>\nDev-okru\u017eenje za backend repozitorij i frontend se postavlja na sli\u010dan na\u010din.<\/p>\n<h2>Pipeline za deploy u Github Actions<\/h2>\n<p>\nPrelazimo na pode\u0161avanje prvog dela na\u0161eg CI\/CD pipeline-a u Github Actions.<\/p>\n<h3>Backend<\/h3>\n<p>\nPipeline se opisuje u <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/blob\/master\/.github\/workflows\/deploy.yml\">datoteci deploy.yml<\/a><\/noindex>. <\/p>\n<p>Ali pre nego \u0161to ga analiziramo, popuni\u0107emo potrebne tajne varijable u Github-u. Za to prelazimo u <i>Settings -&gt; Secrets<\/i>:<\/p>\n<ul>\n<li><code>DOCKER_REGISTRY<\/code> \u2014 host na\u0161eg Docker repozitorijuma (docker.helloworld.ru);<\/li>\n<li><code>DOCKER_LOGIN<\/code> \u2014 korisni\u010dko ime za Docker repozitorijum;<\/li>\n<li><code>DOCKER_PASSWORD<\/code> \u2014 lozinka za njega;<\/li>\n<li><code>DEPLOY_HOST<\/code> \u2014 host na kojem je dostupna admin Plesk (primer: <noindex><a rel=\"nofollow\" href=\"https:\/\/helloworld.ru\">helloworld.ru<\/a><\/noindex>:8443 ili <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 za deploy u prod-repozitorijum na serveru (dobijen u Razvijanje u Plesk ta\u010dka 4);<\/li>\n<li><code>DEPLOY_BACK_DEV_TOKEN<\/code> \u2014 token za deploy u dev-repozitorijum na serveru.<\/li>\n<\/ul>\n<p>\nProces deploy-a je jednostavan i sastoji se od tri osnovna koraka: <\/p>\n<ul>\n<li>izgradnja i objavljivanje slike u na\u0161em repozitorijumu;<\/li>\n<li>pokretanje testova u kontejneru zasnovanom na sve\u017ee izgra\u0111enoj slici;<\/li>\n<li>deploy u odgovaraju\u0107e okru\u017eenje u zavisnosti od grane (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\">Datoteka deploy.yml za front-repozitorij<\/a><\/noindex> malo se razlikuje od back repozitorija. Nedostaje korak sa pokretanjem testova i menjaju se imena tokena za deploy. Tajne za front-repozitorij, ina\u010de, treba popuniti odvojeno.<\/p>\n<h1>Pode\u0161avanje sajta<\/h1>\n<p><\/p>\n<h2>Proksiranje saobra\u0107aja putem Nginx-a<\/h2>\n<p>\nPa, do\u0161li smo do kraja. Ostalo je samo da postavimo proksiranje dolaznog i odlaznog saobra\u0107aja u na\u0161 kontejner preko Nginx-a. Ovaj proces smo ve\u0107 razmotrili u ta\u010dki 5 pode\u0161avanja Docker Registry. Isto to treba ponoviti za back i front deo u dev i prod okru\u017eenjima.<\/p>\n<p>Prikazat \u0107u snimke pode\u0161avanja.<\/p>\n<h3>Backend<\/h3>\n<p>\n<img decoding=\"async\" alt=\"CI\/CD v Github Actions za projekt na 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 v Github Actions za projekt na Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/35d4d8ef3489608db1e173b7f214ca03.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<strong>Va\u017eno poja\u0161njenje<\/strong>. U frontend kontejner \u0107e se proksirati svi URL-ovi, osim onih koji po\u010dinju sa <code>\/api\/<\/code> \u2014 oni \u0107e se proksirati u back kontejner (zato <strong>u back kontejneru svi obrtnici moraju po\u010deti sa <\/strong><strong><code>\/api\/<\/code><\/strong>).<\/p>\n<h2>Zaklju\u010dki<\/h2>\n<p>\nSada bi na\u0161 sajt trebao biti dostupan na adresama helloworld.ru i dev.helloworld.ru (prod i dev okru\u017eenje odgovaraju\u0107e).<\/p>\n<p>Sve u svemu, saznali smo kako da pripremimo jednostavnu aplikaciju na Flask-u i Angular-u i kako da postavimo pipeline u Github Actions za njen isporuku na server pod upravom Plesk.<\/p>\n<p>Ponovo \u0107u podeliti linkove na repozitorijume sa kodom: <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 \/>Izvor: <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 5.0.4 - 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.\" \/>\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\/sl\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sl_SI\" \/>\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.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/sl\/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 u Github Actions za projekat na Flasku+Angularu | ProHoster","description":"U ovom \u010dlanku \u0107u podeliti svoje iskustvo.","canonical_url":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sl_SI","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.","og:url":"https:\/\/prohoster.info\/sl\/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","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/85646","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/comments?post=85646"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/85646\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media\/85647"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media?parent=85646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/categories?post=85646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/tags?post=85646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}