{"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\/de\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","title":{"rendered":"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/26d0945a41bbf3c98fa92f15be5b8c6c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn diesem Artikel teile ich meine Erfahrung bei der Einrichtung von CI\/CD mit der Plesk-Verwaltungskonsole und GitHub Actions. Heute lernen wir, ein einfaches Projekt mit dem wenig einfallsreichen Namen \u201eHelloworld\u201c zu deployen. Es wurde mit dem Python-Framework Flask, mit Workern auf Celery und einem Frontend auf Angular 8 geschrieben.<\/p>\n<p><b>Links zu den Repositories:<\/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>Im ersten Teil des Artikels werfen wir einen Blick auf unser Projekt und seine Teile. Im zweiten Teil werden wir uns damit besch\u00e4ftigen, wie man Plesk einrichtet und die erforderlichen Erweiterungen und Komponenten installiert (Datenbank, RabbitMQ, Redis, Docker usw.).<\/p>\n<p>Im dritten Teil werden wir schlie\u00dflich kl\u00e4ren, wie man eine Pipeline zum Deployen unseres Projekts auf dem Server in die Dev- und Prod-Umgebung einrichtet. Anschlie\u00dfend starten wir die Website auf dem Server.<\/p>\n<p>\u00dcbrigens, ich habe vergessen, mich vorzustellen. Mein Name ist Oleg Borzov, ich bin Fullstack-Entwickler im CRM-Team f\u00fcr Hypothekenmanager bei Domklik. <br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1>Projekt\u00fcbersicht<\/h1>\n<p>\nZun\u00e4chst betrachten wir die beiden Repositories des Projekts \u2013 das Backend und das Frontend \u2013 und gehen den Code durch.<\/p>\n<h2>Backend-Teil: Flask+Celery<\/h2>\n<p>\nF\u00fcr den Backend-Teil habe ich eine unter Python-Entwicklern recht beliebte Kombination gew\u00e4hlt: Das Framework Flask (f\u00fcr die API) und Celery (f\u00fcr die Aufgabenwarteschlange). Als ORM kommt SQLAlchemy zum Einsatz. F\u00fcr Migrationen wird Alembic verwendet. Zur Validierung von JSON in den Handlers \u2013 Marshmallow.<\/p>\n<p>Im <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">das Repository<\/a><\/noindex> Es gibt eine Datei Readme.md mit einer ausf\u00fchrlichen Beschreibung der Struktur und Anweisungen zum Starten des Projekts.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_web\">API der Webanwendung<\/a><\/noindex> eher einfach, besteht aus 6 Endpunkten:<\/p>\n<ul>\n<li><code>\/ping<\/code> \u2014 zum \u00dcberpr\u00fcfen der Verf\u00fcgbarkeit;<\/li>\n<li>Endpunkte f\u00fcr Registrierung, Authentifizierung, De-Authentifizierung und Abruf des authentifizierten Benutzers;<\/li>\n<li>ein Endpunkt zum Versenden von E-Mails, der eine Aufgabe in die Celery-Warteschlange legt.<\/li>\n<\/ul>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_celery\">Celery-Teil<\/a><\/noindex> noch einfacher, es gibt nur eine Aufgabe <code>send_mail_task<\/code>.<\/p>\n<p>Im Verzeichnis <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/conf\/\">\/conf<\/a><\/noindex> liegen zwei Unterordner: <\/p>\n<ul>\n<li><code>docker<\/code> mit zwei Docker-Dateien (<code>base.dockerfile<\/code> zum Erstellen eines selten ge\u00e4nderten Basisimages und <code>Dockerfile<\/code> f\u00fcr die Hauptbuilds);<\/li>\n<li><code>.env_files<\/code> \u2014 mit Umgebungsvariablen-Dateien f\u00fcr verschiedene Umgebungen. <\/li>\n<\/ul>\n<p>\nIm Wurzelverzeichnis des Projekts befinden sich vier docker-compose-Dateien:<\/p>\n<ul>\n<li><code>docker-compose.local.db.yml<\/code> zur Einrichtung einer lokalen Datenbank f\u00fcr die Entwicklung;<\/li>\n<li><code>docker-compose.local.workers.yml<\/code> zum lokalen Hochfahren von Workern, Datenbank, Redis und RabbitMQ;<\/li>\n<li><code>docker-compose.test.yml<\/code> zum Ausf\u00fchren von Tests beim Deployen;<\/li>\n<li><code>docker-compose.yml<\/code> zum Deployen.<\/li>\n<\/ul>\n<p>\nUnd der letzte interessante Ordner f\u00fcr uns ist \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/.ci-cd\">.ci-cd<\/a><\/noindex>. Darin befinden sich Shell-Skripte f\u00fcr das Deployen:<\/p>\n<ul>\n<li><code>deploy.sh<\/code> \u2014 startet Migrationen und das Deployen. Wird auf dem Server nach dem Build und dem Ausf\u00fchren von Tests in GitHub Actions ausgef\u00fchrt;<\/li>\n<li><code>rollback.sh<\/code> \u2014 rollt die Container auf die vorherige Version des Builds zur\u00fcck;<\/li>\n<li><code>curl_tg.sh<\/code> \u2014 Benachrichtigungen \u00fcber das Deployment in Telegram senden.<\/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 mit dem Frontend<\/a><\/noindex> ist viel einfacher als das Backend. Das Frontend besteht aus drei Seiten:<\/p>\n<ul>\n<li>Die Hauptseite mit einem Formular zum Versenden von E-Mails und einem Abmeldebutton.<\/li>\n<li>Login-Seite.<\/li>\n<li>Registrierungsseite.<\/li>\n<\/ul>\n<p>\nDie Hauptseite sieht minimalistisch aus:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/4db36add2c4277397724b0d71b96ff06.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIm Root-Verzeichnis liegen zwei Dateien <code>Dockerfile<\/code> und <code>docker-compose.yml<\/code>, sowie ein uns bekanntes Verzeichnis <code>.ci-cd<\/code> mit etwas weniger Skripten als im Backend-Repository (die Skripte zum Starten von Tests wurden entfernt).<\/p>\n<h1>Wir richten ein Projekt in Plesk ein<\/h1>\n<p>\nBeginnen wir mit der Konfiguration von Plesk und dem Erstellen eines Abonnements f\u00fcr unsere Website.<\/p>\n<h2>Installation von Erweiterungen<\/h2>\n<p>\nIn Plesk ben\u00f6tigen wir vier Erweiterungen: <\/p>\n<ul>\n<li><code>Docker<\/code> zum Verwalten und visuellen Anzeigen des Zustands von Containern im Plesk-Adminbereich;<\/li>\n<li><code>Git<\/code> zur Konfiguration des Deployment-Schritts auf dem Server;<\/li>\n<li><code>Let's Encrypt<\/code> zur Generierung (und automatischen Verl\u00e4ngerung) kostenloser TLS-Zertifikate;<\/li>\n<li><code>Firewall<\/code> zur Konfiguration der Filterung des eingehenden Datenverkehrs.<\/li>\n<\/ul>\n<p>\nSie k\u00f6nnen sie \u00fcber das Plesk-Adminpanel im Abschnitt Erweiterungen installieren:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/350cf7d7ca3ae1a965a0d3f8a73594ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWir werden die detaillierte Konfiguration der Erweiterungen nicht betrachten, die Standardeinstellungen sind f\u00fcr unsere Demo-Zwecke ausreichend.<\/p>\n<h2>Erstellung eines Abonnements und einer Website<\/h2>\n<p>\nAls n\u00e4chstes m\u00fcssen wir ein Abonnement f\u00fcr unsere Website helloworld.ru erstellen und die Unterdomain dev.helloworld.ru hinzuf\u00fcgen.<\/p>\n<ol>\n<li>Wir erstellen ein Abonnement f\u00fcr die Domain helloworld.ru und geben den Benutzernamen und das Passwort f\u00fcr den Systembenutzer an:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3932dc7492aa523b6cbd540ad7a7fe6b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nUnten auf der Seite setzen wir ein H\u00e4kchen <i>Sichern Sie die Domain mit Let's Encrypt<\/i>, wenn wir HTTPS f\u00fcr die Website einrichten m\u00f6chten:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f4d23a34764fdeb411fe2ca9cc95cb45.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Dann erstellen wir in diesem Abonnement die Unterdomain dev.helloworld.ru (f\u00fcr die ebenfalls ein kostenloses TLS-Zertifikat ausgestellt werden kann):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d57e6ba71454cb537917400de4275e92.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>Installation der Serverkomponenten<\/h1>\n<p>\nWir haben einen Server mit <i>OS Debian Stretch 9.12<\/i> und dem installierten Verwaltungspanel <i>Plesk Obsidian 18.0.27<\/i>.<\/p>\n<p>Wir m\u00fcssen f\u00fcr unser Projekt installieren und konfigurieren:<\/p>\n<ul>\n<li>PostgreSQL (in unserem Fall wird es einen Server mit zwei Datenbanken f\u00fcr die dev- und prod-Umgebung geben).<\/li>\n<li>RabbitMQ (das gleiche, eine Instanz mit verschiedenen vhosts f\u00fcr die Umgebungen).<\/li>\n<li>Zwei Instanzen von Redis (f\u00fcr die dev- und prod-Umgebung).<\/li>\n<li>Docker Registry (f\u00fcr die lokale Speicherung der erstellten Docker-Images).<\/li>\n<li>UI-Schnittstelle f\u00fcr die Docker-Registry.<\/li>\n<\/ul>\n<p><\/p>\n<h2>PostgreSQL<\/h2>\n<p>\nIm Lieferumfang von Plesk ist bereits eine PostgreSQL-Datenbank enthalten, jedoch nicht die neueste Version (zum Zeitpunkt der Erstellung des Artikels unterst\u00fctzte Plesk Obsidian <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.plesk.com\/release-notes\/obsidian\/software-requirements\/\">Postgres-Versionen 8.4\u201310.8). Wir m\u00f6chten jedoch die aktuellste Version f\u00fcr unsere Anwendung (12.3 zum Zeitpunkt der Erstellung des Artikels), daher werden wir sie manuell installieren.<\/a><\/noindex> Detaillierte Anleitungen zur Installation von Postgres auf Debian finden sich zahlreich im Internet (<\/p>\n<p>Beispiel<noindex><a rel=\"nofollow\" href=\"https:\/\/tecadmin.net\/install-postgresql-on-debian-9-stretch\/\">Beispiel<\/a><\/noindex>), deshalb werde ich sie nicht im Detail beschreiben, sondern nur die Befehle anf\u00fchren:<\/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>\nDa die Standardkonfiguration in PostgreSQL ziemlich durchschnittlich ist, ist es erforderlich, die Konfiguration unbedingt anzupassen. Dabei hilft uns <noindex><a rel=\"nofollow\" href=\"http:\/\/pgconfigurator.cybertec.at\/\">Rechner<\/a><\/noindex>: Sie m\u00fcssen die Parameter Ihres Servers eingeben und die Einstellungen in der Datei <code>\/etc\/postgresql\/12\/main\/postgresql.conf<\/code>durch die vorgeschlagenen ersetzen. Hier muss ich anmerken, dass solche Rechner keine Wunderwaffe sind und die Datenbank gezielt an Ihre Hardware, Ihre Anwendung und die Komplexit\u00e4t der Abfragen angepasst werden sollte. Aber f\u00fcr den Anfang reicht das aus.<\/p>\n<p>Neben den vom Rechner vorgeschlagenen Einstellungen \u00e4ndern wir auch in <code>postgresql.conf<\/code>den standardm\u00e4\u00dfig eingetragenen Port 5432 auf einen anderen (in unserem Beispiel - <strong>53983<\/strong>).<\/p>\n<p>Nach der \u00c4nderung der Konfigurationsdatei starten wir den postgresql-Server mit dem Befehl:<\/p>\n<pre><code class=\"bash\">service postgresql restart\n<\/code><\/pre>\n<p>\nWir haben PostgreSQL installiert und konfiguriert. Jetzt erstellen wir eine DB, Benutzer f\u00fcr die Entwicklungs- und Produktionsumgebung und vergeben den Benutzern die Rechte zum Verwalten der DB:<\/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>\nKommen wir zur Installation von RabbitMQ - einem Nachricht broker f\u00fcr Celery. Die Installation auf Debian ist recht einfach:<\/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>\nNach der Installation m\u00fcssen wir <i>vhosts<\/i>, Benutzer erstellen und die erforderlichen Berechtigungen festlegen:<\/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>\nJetzt installieren und konfigurieren wir die letzte Komponente f\u00fcr unsere Anwendung - Redis. Es wird als Backend f\u00fcr die Speicherung von Ergebnissen der Celery-Aufgaben verwendet.<\/p>\n<p>Wir werden zwei Docker-Container mit Redis f\u00fcr Dev- und Prod-Umgebungen mithilfe des Erweiterung starten <code>Docker<\/code> f\u00fcr Plesk.<\/p>\n<ol>\n<li>Gehe zu Plesk, gehe zum Abschnitt Erweiterungen, suche nach der Docker-Erweiterung und installiere sie (wir ben\u00f6tigen die kostenlose Version):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/5638f9b40274e4f7441434279c76a426.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Gehe zur installierten Erweiterung, finde das Abbild \u00fcber die Suche <code>redis bitnami<\/code> und installiere die neueste Version:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/578bbbed6a66a60a4f7410f7d61ea542.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Gehe in den heruntergeladenen Container und passe die Konfiguration an: gib den Port, die maximal zugewiesene RAM-Gr\u00f6\u00dfe, das Passwort in den Umgebungsvariablen an und mounte das Volume:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/8bc804b794735412f5e510b3049c5667.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>F\u00fchre die Schritte 2-3 f\u00fcr den Prod-Container aus, \u00e4ndere nur die Parameter in den Einstellungen: Port, Passwort, RAM-Gr\u00f6\u00dfe und den Pfad zum Volume-Ordner auf dem Server:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit 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>\nNeben den grundlegenden Diensten w\u00e4re es sinnvoll, auf dem Server ein eigenes Repository f\u00fcr Docker-Images einzurichten. Gl\u00fccklicherweise sind die Pl\u00e4tze auf den Servern jetzt recht g\u00fcnstig (sicherlich g\u00fcnstiger als ein Abonnement bei DockerHub) und der Prozess zur Installation eines privaten Repositories ist sehr einfach. <\/p>\n<p>Wir m\u00f6chten folgendes installieren:<\/p>\n<ul>\n<li>ein durch ein Passwort gesch\u00fctztes Docker-Repository, das \u00fcber eine Subdomain zug\u00e4nglich ist <noindex><a rel=\"nofollow\" href=\"https:\/\/docker.helloworld.ru\">https:\/\/docker.helloworld.ru<\/a><\/noindex>;<\/li>\n<li>eine Benutzeroberfl\u00e4che zur Anzeige von Images im Repository, zug\u00e4nglich unter der Adresse <noindex><a rel=\"nofollow\" href=\"https:\/\/docker-ui.helloworld.ru\">https:\/\/docker-ui.helloworld.ru<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nDaf\u00fcr:<\/p>\n<ol>\n<li>Erstelle in Plesk zwei Subdomains in unserem Abonnement: docker.helloworld.ru und docker-ui.helloworld.ru, und richte f\u00fcr sie Let's Encrypt-Zertifikate ein.<\/li>\n<li>F\u00fcge in den Ordner der Subdomain docker.helloworld.ru die Datei hinzu <code>docker-compose.yml<\/code> mit folgendem Inhalt:\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>Wir generieren \u00fcber SSH die Datei .htpasswd f\u00fcr die Basis-Authentifizierung im Docker-Repository:\n<pre><code class=\"plaintext\">htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password<\/code><\/pre>\n<\/li>\n<li>Wir bauen und starten die Container:\n<pre><code class=\"plaintext\">docker-compose up -d<\/code><\/pre>\n<\/li>\n<li>Und wir m\u00fcssen Nginx auf unsere Container umleiten. Das kann \u00fcber Plesk erfolgen. <\/li>\n<\/ol>\n<p>\nDie folgenden Schritte m\u00fcssen f\u00fcr die Subdomains docker.helloworld.ru und docker-ui.helloworld.ru durchgef\u00fchrt werden:<\/p>\n<p>Im Abschnitt <i>Dev-Tools<\/i> unserer Website gehen zu <i>Docker Proxy-Regeln<\/i>:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d9c05901d95d61ce5db27a1a7cfddf49.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nUnd wir f\u00fcgen eine Regel zum Proxy des eingehenden Traffics in unseren Container hinzu:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/faa85cf6da648ef55b7c87c44040efb1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<ol>\n<li>Wir \u00fcberpr\u00fcfen, dass wir uns von einem lokalen Rechner aus in unseren Container einloggen k\u00f6nnen:\n<pre><code class=\"plaintext\">$ docker login docker.helloworld.ru -u hw_docker_admin -p hw_docker_password\nWARNUNG! Die Verwendung von --password \u00fcber die CLI ist unsicher. Verwenden Sie --password-stdin.\nAnmeldung erfolgreich<\/code><\/pre>\n<\/li>\n<li>Lassen Sie uns auch die Funktionalit\u00e4t der Subdomain docker-ui.helloworld.ru \u00fcberpr\u00fcfen:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/fdd6e1396c27c02a88bcbd7efb12f247.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWenn Sie auf Repositories durchsuchen klicken, zeigt der Browser ein Anmeldefenster an, in das Sie den Benutzernamen und das Passwort f\u00fcr das Repository eingeben m\u00fcssen. Danach werden wir auf die Seite mit der Liste der Repositories weitergeleitet (diese wird zun\u00e4chst leer sein):<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f2d75edb530e9d746095ae02d7793c1f.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h2>\u00d6ffnen Sie die Ports in der Plesk-Firewall<\/h2>\n<p>\nNach der Installation und Konfiguration der Komponenten m\u00fcssen wir die Ports \u00f6ffnen, damit die Komponenten von Docker-Containern und aus dem externen Netzwerk erreichbar sind.<\/p>\n<p>Sehen wir uns an, wie man dies am Beispiel der zuvor installierten Firewall-Erweiterung f\u00fcr Plesk macht.<\/p>\n<ol>\n<li>Gehen Sie zu <i>Tools &amp; Einstellungen &gt; Einstellungen &gt; Firewall<\/i>:<br \/>\n<img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d54b4941944fe191574472a475cbaebe.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Gehen Sie zu <i>\u00c4ndern Sie die Plesk-Firewallregeln &gt; Benutzerdefinierte Regel hinzuf\u00fcgen<\/i> und \u00f6ffnen Sie die folgenden TCP-Ports f\u00fcr das Docker-Subnetz (<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 f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3359845f9bd6dc303274da737d4b27a2.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Wir werden auch eine Regel hinzuf\u00fcgen, die die Ports f\u00fcr PostgreSQL und das Management-Panel von RabbitMQ f\u00fcr die externe Welt \u00f6ffnet:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f812cfc7127da469507538ea48b2c80d.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Wenden Sie die Regeln mit der Schaltfl\u00e4che \u00c4nderungen anwenden an:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/e85444ae717aaefa796664003fc86578.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>CI\/CD-Konfiguration in Github Actions<\/h1>\n<p>\nKommen wir zum interessantesten Teil \u2014 der Konfiguration des Pipelines f\u00fcr die kontinuierliche Integration und Bereitstellung unseres Projekts auf dem Server.<\/p>\n<p>Diese Pipeline wird aus zwei Teilen bestehen: <\/p>\n<ul>\n<li>Bau des Abbilds und Ausf\u00fchrung von Tests (f\u00fcr das Backend) \u2014 auf der Seite von Github;<\/li>\n<li>Ausf\u00fchrung der Migrationen (f\u00fcr das Backend) und Bereitstellung der Container \u2014 auf dem Server.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Bereitstellung in Plesk<\/h2>\n<p>\nLassen Sie uns zun\u00e4chst den zweiten Punkt bearbeiten (da der erste davon abh\u00e4ngt).<\/p>\n<p>Wir werden den Bereitstellungsprozess mit der Git-Erweiterung f\u00fcr Plesk konfigurieren.<\/p>\n<p>Sehen wir uns ein Beispiel mit der Prod-Umgebung f\u00fcr das Backend-Repository an.<\/p>\n<ol>\n<li>Gehen Sie zu unserem Helloworld-Webseiten-Abonnement und navigieren Sie zum Git-Unterbereich:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/b55b371560ea87a2954b6553a7cefc49.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>F\u00fcgen Sie im Feld \u201eRemote Git-Repository\u201c den Link zu unserem Github-Repository ein und \u00e4ndern Sie den Standardordner <code>httpdocs<\/code> in einen anderen (zum Beispiel, <code>\/httpdocs\/hw_back<\/code>):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/db9ba9adb6e2e1bed5da7ae8e88c76e1.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Kopieren Sie den SSH-\u00d6ffentlichen Schl\u00fcssel aus dem vorherigen Schritt und <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/settings\/ssh\/new\">f\u00fcgen Sie ihn in den Einstellungen von Github hinzu.<\/a><\/noindex> Klicken Sie im Punkt 2 auf OK, danach werden wir auf die Repository-Seite in Plesk weitergeleitet. Jetzt m\u00fcssen wir die Aktualisierung des Repositories bei Commits in den Master-Zweig einrichten. Dazu gehen wir zu<\/li>\n<li>Repository-Einstellungen <i>und speichern den Wert<\/i> Webhook-URL <code>(dies wird sp\u00e4ter bei der Konfiguration der Github Actions ben\u00f6tigt):<\/code> Geben Sie im Feld Aktionen auf dem vorherigen Bildschirm das Skript zum Starten der Bereitstellung ein:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/14d48a357f821d734d40dc434ab3682a.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>cd {REPOSITORY_ABSOLUTE_PATH}\n.ci-cd\/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID}\n<pre><code class=\"plaintext\">wo: <\/code><\/pre>\n<p>\n{REPOSITORY_ABSOLUTE_PATH} <\/p>\n<p><code>\u2014 der Pfad zum Prod-Ordner des Backend-Repositories auf dem Server;<\/code> {ENV}<br \/>\n<code>\u2014 die Umgebung (dev\/prod), in unserem Fall<\/code> \u2014 Umgebung (dev\/prod), in unserem Fall <code>prod<\/code>;<br \/>\n<code>{DOCKER_REGISTRY_HOST}<\/code> \u2014 der Host unseres Docker-Repositorys<br \/>\n<code>{TG_BOT_TOKEN}<\/code> \u2014 Token des Telegram-Bots;<br \/>\n<code>{TG_CHAT_ID}<\/code> \u2014 ID des Chats\/Kanals f\u00fcr den Versand von Benachrichtigungen.<\/p>\n<p>Beispiel-Skript:<\/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>F\u00fcgen Sie einen Benutzer aus unserem Abonnement zur Docker-Gruppe hinzu (damit er Container verwalten kann):\n<pre><code class=\"plaintext\">sudo usermod -aG docker helloworld_admin<\/code><\/pre>\n<\/li>\n<\/ol>\n<p>\nDie Entwicklungsumgebung f\u00fcr das Backend-Repository und das Frontend wird \u00e4hnlich konfiguriert.<\/p>\n<h2>Deployment-Pipeline in Github Actions<\/h2>\n<p>\nGehen wir zur Konfiguration des ersten Teils unserer CI\/CD-Pipeline in Github Actions \u00fcber.<\/p>\n<h3>Backend<\/h3>\n<p>\nDie Pipeline wird in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/blob\/master\/.github\/workflows\/deploy.yml\">der Datei deploy.yml<\/a><\/noindex>. <\/p>\n<p>Aber bevor wir dazu kommen, f\u00fcgen wir in Github die ben\u00f6tigten Secret-Variablen ein. Dazu gehen wir zu <i>Einstellungen -&gt; Secrets<\/i>:<\/p>\n<ul>\n<li><code>DOCKER_REGISTRY<\/code> \u2014 der Host unseres Docker-Repositorys (docker.helloworld.ru);<\/li>\n<li><code>DOCKER_LOGIN<\/code> \u2014 Benutzername f\u00fcr das Docker-Repository;<\/li>\n<li><code>DOCKER_PASSWORD<\/code> \u2014 Passwort daf\u00fcr;<\/li>\n<li><code>DEPLOY_HOST<\/code> \u2014 der Host, auf dem das Plesk-Admin-Panel verf\u00fcgbar ist (Beispiel: <noindex><a rel=\"nofollow\" href=\"https:\/\/helloworld.ru\">helloworld.ru<\/a><\/noindex>:8443 oder <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 f\u00fcr das Deployment im Prod-Repository auf dem Server (wir haben ihn in Punkt 4 der Bereitstellung in Plesk erhalten);<\/li>\n<li><code>DEPLOY_BACK_DEV_TOKEN<\/code> \u2014 Token f\u00fcr das Deployment im Dev-Repository auf dem Server.<\/li>\n<\/ul>\n<p>\nDer Deployment-Prozess ist einfach und besteht aus drei Hauptschritten: <\/p>\n<ul>\n<li>Erstellen und Ver\u00f6ffentlichen des Images in unserem Repository;<\/li>\n<li>Ausf\u00fchren von Tests im Container basierend auf dem frisch erstellten Image;<\/li>\n<li>Bereitstellung in die jeweilige Umgebung je nach Branche (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\">Die Datei deploy.yml f\u00fcr das Front-Repository<\/a><\/noindex> unterscheidet sich kaum vom Backend. Der Schritt zum Ausf\u00fchren von Tests fehlt und die Namen der Token f\u00fcr das Deployment \u00e4ndern sich. Die Secrets f\u00fcr das Front-Repository m\u00fcssen \u00fcbrigens separat ausgef\u00fcllt werden.<\/p>\n<h1>Website-Konfiguration<\/h1>\n<p><\/p>\n<h2>Traffic \u00fcber Nginx proxyen<\/h2>\n<p>\nNun, wir sind am Ende angekommen. Es bleibt nur noch, den eingehenden und ausgehenden Traffic \u00fcber Nginx in unseren Container zu proxyen. Diesen Prozess haben wir bereits im Punkt 5 der Konfiguration des Docker Registry behandelt. Dasselbe muss f\u00fcr den Backend- und Frontend-Bereich in den Dev- und Prod-Umgebungen wiederholt werden.<\/p>\n<p>Ich werde Screenshots der Einstellungen bereitstellen.<\/p>\n<h3>Backend<\/h3>\n<p>\n<img decoding=\"async\" alt=\"CI\/CD in Github Actions f\u00fcr ein Projekt mit 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 f\u00fcr ein Projekt mit Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/35d4d8ef3489608db1e173b7f214ca03.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<strong>Wichtige Klarstellung<\/strong>. Im Frontend-Container werden alle URLs au\u00dfer denjenigen, die mit <code>\/api\/<\/code> beginnen\u2014 diese werden in den Backend-Container proxyed (deshalb <strong>m\u00fcssen im Backend-Container alle Handler mit <\/strong><strong><code>\/api\/<\/code><\/strong>).<\/p>\n<h2>Ergebnisse<\/h2>\n<p>\nbeginnen. Jetzt sollte unsere Website unter den Adressen helloworld.ru und dev.helloworld.ru (Prod- und Dev-Umgebungen entsprechend) verf\u00fcgbar sein.<\/p>\n<p>Insgesamt haben wir gelernt, wie man eine einfache Anwendung mit Flask und Angular erstellt und eine Pipeline in Github Actions f\u00fcr deren Bereitstellung auf einem Server unter Plesk einrichtet.<\/p>\n<p>Ich werde die Links zu den Repositories mit dem Code wiederholen: <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 \/>Quelle: <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.1.1 - 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\/de\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\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\/de\/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 f\u00fcr ein Projekt auf Flask+Angular | ProHoster","description":"In diesem Artikel teile ich meine Erfahrungen.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","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\/de\/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\/de\/wp-json\/wp\/v2\/posts\/85646","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=85646"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/85646\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/85647"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=85646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=85646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=85646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}