{"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 mit 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 mit 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 Erfahrungen bei der Einrichtung von CI\/CD mit dem Plesk-Panel und GitHub Actions. Heute lernen wir, ein einfaches Projekt mit dem unauff\u00e4lligen Namen \u201eHelloworld\u201c bereitzustellen. Es ist mit dem Python-Framework Flask geschrieben, verwendet Celery-Worker und hat ein Frontend in Angular 8.<\/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 Komponenten. Im zweiten Teil kl\u00e4ren wir, wie man Plesk einrichtet und die notwendigen Erweiterungen und Komponenten (DB, RabbitMQ, Redis, Docker usw.) installiert.<\/p>\n<p>Im dritten Teil werden wir schlie\u00dflich die Pipeline f\u00fcr die Bereitstellung unseres Projekts auf den Server in die Entwicklungs- und Produktionsumgebung konfigurieren. Anschlie\u00dfend starten wir die Website auf dem Server.<\/p>\n<p>Und ja, ich habe vergessen, mich vorzustellen. Mein Name ist Oleg Borzov, ich bin Fullstack-Entwickler im CRM-Team f\u00fcr Hypothekenmanager bei Domclick. <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 Backend und Frontend \u2013 und gehen den Code durch.<\/p>\n<h2>Backend-Teil: Flask+Celery<\/h2>\n<p>\nF\u00fcr das Backend habe ich ein recht popul\u00e4res Setup unter Python-Entwicklern gew\u00e4hlt: das Flask-Framework (f\u00fcr die API) und Celery (f\u00fcr die Aufgabenwarteschlange). Als ORM kommt SQLAlchemy zum Einsatz. F\u00fcr Migrationen wird Alembic verwendet. F\u00fcr die JSON-Validierung in den Endpunkten nutzen wir Marshmallow.<\/p>\n<p>In <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">Repository<\/a><\/noindex> Es gibt eine Datei Readme.md mit einer detaillierten 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> ist ziemlich einfach und besteht aus 6 Endpunkten:<\/p>\n<ul>\n<li><code>\/ping<\/code> \u2014 zur \u00dcberpr\u00fcfung der Erreichbarkeit;<\/li>\n<li>Endpunkte f\u00fcr Registrierung, Authentifizierung, Deauthentifizierung und um den autorisierten Benutzer abzurufen;<\/li>\n<li>ein Endpunkt zum Versenden von E-Mails, der eine Aufgabe in die Celery-Warteschlange einreiht.<\/li>\n<\/ul>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_celery\">Der Celery-Teil<\/a><\/noindex> ist noch einfacher, dort gibt es nur eine Aufgabe <code>send_mail_task<\/code>.<\/p>\n<p>Im Ordner <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> f\u00fcr den Aufbau eines selten \u00e4ndernden Basisimages und <code>Dockerfile<\/code> f\u00fcr die Haupt-Bauten);<\/li>\n<li><code>.env_files<\/code> \u2014 mit Dateien f\u00fcr Umgebungsvariablen f\u00fcr verschiedene Umgebungen. <\/li>\n<\/ul>\n<p>\nIm Stammverzeichnis des Projekts befinden sich vier docker-compose-Dateien:<\/p>\n<ul>\n<li><code>docker-compose.local.db.yml<\/code> zum Hochfahren einer lokalen Datenbank f\u00fcr die Entwicklung;<\/li>\n<li><code>docker-compose.local.workers.yml<\/code> zum Hochfahren eines lokalen Workers, einer Datenbank, Redis und RabbitMQ;<\/li>\n<li><code>docker-compose.test.yml<\/code> zum Ausf\u00fchren von Tests bei der Bereitstellung;<\/li>\n<li><code>docker-compose.yml<\/code> f\u00fcr das Deployment.<\/li>\n<\/ul>\n<p>\nUnd der letzte interessante Ordner f\u00fcr uns \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/.ci-cd\">.ci-cd<\/a><\/noindex>. Es enth\u00e4lt Shell-Skripte f\u00fcr die Bereitstellung:<\/p>\n<ul>\n<li><code>deploy.sh<\/code> \u2014 Start der Migration und des Deployments. Wird auf dem Server nach dem Zusammenstellen und dem Ausf\u00fchren der Tests in Github Actions gestartet;<\/li>\n<li><code>rollback.sh<\/code> \u2014 R\u00fccksetzung der Container auf die vorherige Version der Bereitstellung;<\/li>\n<li><code>curl_tg.sh<\/code> \u2014 Versand von Benachrichtigungen \u00fcber die Bereitstellung an Telegram.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Frontend auf Angular<\/h2>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\">Repository mit dem Frontend<\/a><\/noindex> ist deutlich einfacher als das Backend. Das Frontend besteht aus drei Seiten:<\/p>\n<ul>\n<li>Hauptseite mit einem Formular zum Senden von E-Mails und einem Ausloggen-Button.<\/li>\n<li>Login-Seite.<\/li>\n<li>Registrierungsseite.<\/li>\n<\/ul>\n<p>\nDie Hauptseite wirkt minimalistisch:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 Stammverzeichnis sind zwei Dateien enthalten <code>Dockerfile<\/code> und <code>docker-compose.yml<\/code>, sowie ein uns vertrauter Ordner <code>.ci-cd<\/code> mit etwas weniger Skripten als im Backend-Repository (Skripte zum Testen wurden entfernt).<\/p>\n<h1>Projekt in Plesk anlegen<\/h1>\n<p>\nWir beginnen mit der Konfiguration von Plesk und der Erstellung 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 zur visuellen Darstellung des Zustands der Container in der Plesk-Administration;<\/li>\n<li><code>Git<\/code> zur Konfiguration des Deploy-Schrittes auf dem Server;<\/li>\n<li><code>Let's Encrypt<\/code> zur Generierung (und automatischen Verl\u00e4ngerung) von kostenlosen TLS-Zertifikaten;<\/li>\n<li><code>Firewall<\/code> zur Konfiguration der Filterung eingehender Daten.<\/li>\n<\/ul>\n<p>\nDiese k\u00f6nnen \u00fcber die Plesk-Administration im Abschnitt Erweiterungen installiert werden:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 von Erweiterungen nicht behandeln; f\u00fcr unsere Demo-Zwecke sind die Standardeinstellungen 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 das Subdomain dev.helloworld.ru hinzuf\u00fcgen.<\/p>\n<ol>\n<li>Wir erstellen ein Abonnement f\u00fcr die Domain helloworld.ru und geben den Benutzername und das Passwort f\u00fcr den Systembenutzer an:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 mit 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 das Subdomain dev.helloworld.ru (f\u00fcr das ebenfalls ein kostenloses TLS-Zertifikat ausgestellt werden kann):\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 von Serverkomponenten<\/h1>\n<p>\nWir haben einen Server mit <i>OS Debian Stretch 9.12<\/i> und installiertem Control Panel <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 DBs f\u00fcr die dev- und prod-Umgebung geben).<\/li>\n<li>RabbitMQ (dasselbe, eine Instanz mit verschiedenen vhosts f\u00fcr die Umgebungen).<\/li>\n<li>Zwei Instanzen 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-Interface f\u00fcr 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 in der neuesten Version (zum Zeitpunkt der Erstellung dieses Artikels unterst\u00fctzte Plesk Obsidian <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.plesk.com\/release-notes\/obsidian\/software-requirements\/\">die<\/a><\/noindex> Postgres-Versionen 8.4\u201310.8. F\u00fcr unsere Anwendung m\u00f6chten wir jedoch die neueste Version (12.3 zum Zeitpunkt des Schreibens dieses Artikels), weshalb wir sie manuell installieren werden.<\/p>\n<p>Es gibt viele detaillierte Anleitungen zur Installation von Postgres auf Debian im Internet (<noindex><a rel=\"nofollow\" href=\"https:\/\/tecadmin.net\/install-postgresql-on-debian-9-stretch\/\">Nummer 00 oder<\/a><\/noindex>), daher werde ich sie nicht im Detail beschreiben, sondern einfach die Befehle angeben:<\/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 eher durchschnittlich ist, sollten wir die Konfiguration unbedingt anpassen. Dabei hilft uns <noindex><a rel=\"nofollow\" href=\"http:\/\/pgconfigurator.cybertec.at\/\">Rechner<\/a><\/noindex>: Geben Sie die Parameter Ihres Servers ein und ersetzen Sie die Einstellungen in der Datei <code>\/etc\/postgresql\/12\/main\/postgresql.conf<\/code>durch die vorgeschlagenen. Hier sollte angemerkt werden, dass solche Kalkulatoren kein Allheilmittel sind und die Datenbank gezielt angepasst werden sollte, basierend auf Ihrer Hardware, Anwendung und Komplexit\u00e4t der Abfragen. F\u00fcr den Anfang reicht das jedoch aus.<\/p>\n<p>Neben den vom Kalkulator vorgeschlagenen Einstellungen \u00e4ndern wir auch in <code>postgresql.conf<\/code>den standardm\u00e4\u00dfig angegebenen Port 5432 auf einen anderen (in unserem Beispiel \u2014 <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 Datenbanken, Benutzer f\u00fcr die Entwicklungs- und Produktionsumgebungen und weisen den Benutzern die n\u00f6tigen Berechtigungen zur Verwaltung der Datenbanken zu:<\/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>\nNun kommen wir zur Installation von RabbitMQ \u2014 dem Nachrichtenbroker f\u00fcr Celery. Die Installation auf Debian gestaltet sich 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 anlegen und die entsprechenden Berechtigungen vergeben:<\/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>\nLassen Sie uns nun die letzte Komponente f\u00fcr unsere Anwendung \u2013 Redis \u2013 installieren und konfigurieren. Es wird als Backend zur Speicherung der Ergebnisse von Celery-Tasks verwendet.<\/p>\n<p>Wir werden zwei Docker-Container mit Redis f\u00fcr die Entwicklungs- und Produktionsumgebungen mithilfe des <code>Docker<\/code> Plesk-Extensions.<\/p>\n<ol>\n<li>Gehen Sie zu Plesk, navigieren Sie zu den Erweiterungen, suchen Sie nach der Docker-Erweiterung und installieren Sie diese (wir ben\u00f6tigen die kostenlose Version):\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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>Navigieren Sie zur installierten Erweiterung, suchen Sie nach dem Image <code>redis bitnami<\/code> und installieren Sie die neueste Version:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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>Gehen Sie in den heruntergeladenen Container und passen Sie die Konfiguration an: Geben Sie den Port, die maximale zugewiesene Gr\u00f6\u00dfe des RAM, das Passwort in den Umgebungsvariablen an und montieren Sie das Volume:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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\u00fchren Sie die Schritte 2-3 f\u00fcr den Produktionscontainer aus, \u00e4ndern Sie dabei nur die Einstellungen: Port, Passwort, RAM-Gr\u00f6\u00dfe und den Pfad zum Volume-Ordner auf dem Server:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 Docker-Image-Repository zu installieren. Gl\u00fccklicherweise sind Speicherplatz auf Servern derzeit recht g\u00fcnstig (sicherlich g\u00fcnstiger als ein Abonnement bei DockerHub), und der Prozess der Installation eines privaten Repositories ist sehr einfach. <\/p>\n<p>Wir m\u00f6chten, dass folgende Komponenten installiert sind:<\/p>\n<ul>\n<li>ein passwortgesch\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>UI-Schnittstelle zum Anzeigen von Images im Repository, erreichbar unter <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>Wir erstellen in Plesk zwei Subdomains in unserem Abonnement: docker.helloworld.de und docker-ui.helloworld.de, und richten f\u00fcr sie Let's Encrypt-Zertifikate ein.<\/li>\n<li>In den Ordner der Subdomain docker.helloworld.de f\u00fcgen wir 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>Unter SSH generieren wir 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 die Container auf und starten sie:\n<pre><code class=\"plaintext\">docker-compose up -d<\/code><\/pre>\n<\/li>\n<li>Und wir m\u00fcssen Nginx auf unsere Container umleiten. Dies kann \u00fcber Plesk erfolgen. <\/li>\n<\/ol>\n<p>\nDie folgenden Schritte sind f\u00fcr die Subdomains docker.helloworld.de und docker-ui.helloworld.de durchzuf\u00fchren:<\/p>\n<p>Im Abschnitt <i>Entwicklertools<\/i> unserer Website gehen wir zu <i>Docker-Proxy-Regeln<\/i>:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 f\u00fcgen eine Regel f\u00fcr die Proxyweiterleitung des eingehenden Traffics zu unserem Container hinzu:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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, ob wir uns von unserem lokalen Computer aus in unseren Container anmelden 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>Wir testen au\u00dferdem die Funktion des Subdomains docker-ui.helloworld.ru:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 Browse repositories klicken, wird ein Anmeldefenster angezeigt, in das Sie Ihren Benutzernamen und Ihr Passwort f\u00fcr das Repository eingeben m\u00fcssen. Danach werden Sie auf eine Seite mit der Liste der Repositories weitergeleitet (diese wird vorerst leer sein):<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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>Ports im Plesk Firewall \u00f6ffnen<\/h2>\n<p>\nNach der Installation und Konfiguration der Komponenten m\u00fcssen wir Ports \u00f6ffnen, damit die Komponenten aus Docker-Containern und dem externen Netzwerk zug\u00e4nglich sind.<\/p>\n<p>Lassen Sie uns am Beispiel der zuvor installierten Firewall-Erweiterung f\u00fcr Plesk sehen, wie das geht.<\/p>\n<ol>\n<li>Gehen wir zu <i>Tools &amp; Settings &gt; Settings &gt; Firewall<\/i>:<br \/>\n<img decoding=\"async\" alt=\"CI\/CD mit 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 wir zu <i>Plesk Firewall-Regeln \u00e4ndern &gt; Benutzerdefinierte Regel hinzuf\u00fcgen<\/i> und \u00f6ffnen 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 mit 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 f\u00fcgen au\u00dferdem eine Regel hinzu, die dem externen Zugriff die Ports f\u00fcr PostgreSQL und die Verwaltungsoberfl\u00e4che von RabbitMQ \u00f6ffnet:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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>Wir wenden die Regeln mit der Schaltfl\u00e4che \u00c4nderungen anwenden an:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 in GitHub Actions einrichten<\/h1>\n<p>\nKommen wir zum spannendsten Teil \u2014 der Einrichtung der Pipeline f\u00fcr kontinuierliche Integration und Bereitstellung unseres Projekts auf dem Server.<\/p>\n<p>Dieser Pipeline wird aus zwei Teilen bestehen: <\/p>\n<ul>\n<li>dem Erstellen des Images und dem Ausf\u00fchren von Tests (f\u00fcr das Backend) \u2013 auf der Github-Seite;<\/li>\n<li>dem Starten von Migrationsprozessen (f\u00fcr das Backend) und dem Deployen von Containern \u2013 auf dem Server.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Deployment in Plesk<\/h2>\n<p>\nBeginnen wir zun\u00e4chst mit dem zweiten Punkt (da dieser den ersten beeinflusst).<\/p>\n<p>Den Deployment-Prozess richten wir mit Hilfe der Git-Erweiterung f\u00fcr Plesk ein.<\/p>\n<p>Betrachten wir ein Beispiel mit der Produktionsumgebung f\u00fcr das Backend-Repository.<\/p>\n<ol>\n<li>Wir gehen in das Abonnement unserer Website Helloworld und navigieren zum Unterabschnitt Git:\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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 mit 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 Public Key vom vorherigen Schritt und <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/settings\/ssh\/new\">f\u00fcgen Sie ihn<\/a><\/noindex> in die Einstellungen von Github ein.<\/li>\n<li>Klicken Sie auf OK auf dem Bildschirm im Punkt 2, danach werden Sie zur Repository-Seite in Plesk weitergeleitet. Jetzt m\u00fcssen wir das Aktualisieren des Repositories bei Commits in den Master-Branch einrichten. Dazu gehen wir zu <i>Repository-Einstellungen<\/i> und speichern den Wert <code>Webhook-URL<\/code> (diese ben\u00f6tigen wir sp\u00e4ter bei der Einrichtung von Github Actions):\n<p><img decoding=\"async\" alt=\"CI\/CD mit 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>Im Feld Action auf dem Bildschirm des vorherigen Punktes geben wir das Skript zum Starten des Deployments ein:\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>\nwo: <\/p>\n<p><code>{REPOSITORY_ABSOLUTE_PATH}<\/code> \u2014 Pfad zum Verzeichnis des Produktions-Backend-Repositories auf dem Server;<br \/>\n<code>{ENV}<\/code> \u2014 Umgebung (dev\/prod), in unserem Fall <code>prod<\/code>;<br \/>\n<code>{DOCKER_REGISTRY_HOST}<\/code> \u2014 Host unseres Docker-Repositories<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 Benachrichtigungen.<\/p>\n<p>Beispielscript:<\/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 den 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>\nNun richten wir den ersten Teil unseres CI\/CD-Pipelines in Github Actions ein.<\/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>beschrieben. Aber bevor wir sie durchgehen, f\u00fcgen wir die ben\u00f6tigten Secret-Variablen in Github hinzu. Gehen Sie dazu zu <i>Einstellungen -&gt; Geheimnisse<\/i>:<\/p>\n<ul>\n<li><code>DOCKER_REGISTRY<\/code> \u2014 Host unseres Docker-Repositories (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 Host, auf dem das Plesk-Admin-Panel verf\u00fcgbar ist (z. B. <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 es in Deployment in Plesk Punkt 4 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 Deploy-Prozess ist einfach und besteht aus drei Hauptschritten: <\/p>\n<ul>\n<li>Erstellung und Ver\u00f6ffentlichung des Images in unserem Repository;<\/li>\n<li>Ausf\u00fchrung von Tests in einem Container auf Basis des neu erstellten Images;<\/li>\n<li>Bereitstellung in die erforderliche Umgebung je nach 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\">Die Datei deploy.yml f\u00fcr das Frontend-Repository<\/a><\/noindex> unterscheidet sich kaum von der des Backend-Repositories. Sie enth\u00e4lt keinen Schritt zur Ausf\u00fchrung von Tests und die Namen der Deploy-Tokens werden ge\u00e4ndert. \u00dcbrigens m\u00fcssen die Secrets f\u00fcr das Frontend-Repository separat ausgef\u00fcllt werden.<\/p>\n<h1>Website-Einrichtung<\/h1>\n<p><\/p>\n<h2>Traffic-Proxying \u00fcber Nginx<\/h2>\n<p>\nNun sind wir am Ende angekommen. Es bleibt nur noch, das Proxying des eingehenden und ausgehenden Traffics in unseren Container \u00fcber Nginx einzurichten. Diesen Prozess haben wir bereits im Punkt 5 der Docker Registry-Einstellungen behandelt. Dasselbe muss f\u00fcr die Front- und Backend-Seite 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 mit 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 mit 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>Wichtiger Hinweis<\/strong>. Im Frontend-Container werden alle URLs proxied, au\u00dfer denen, die mit <code>\/api\/<\/code> beginnen \u2014 sie werden an den Backend-Container weitergeleitet (deshalb <strong>m\u00fcssen alle Handler im Backend-Container 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-Umgebung entsprechend) verf\u00fcgbar sein.<\/p>\n<p>Zusammenfassend haben wir gelernt, wie man eine einfache Anwendung mit Flask und Angular erstellt und eine Pipeline in Github Actions einrichtet, um sie auf einem Server mit Plesk bereitzustellen.<\/p>\n<p>Hier sind die Links zu den Repositories mit dem Code: <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 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\/de\/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=\"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 \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\/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 mit Flask+Angular | ProHoster","description":"In diesem Artikel teile ich meine Erfahrungen mit der Einrichtung von CI\/CD unter Verwendung des Plesk-Control-Panels und Github Actions. Heute lernen wir, ein einfaches Projekt mit dem schlichten Namen \u201eHelloworld\u201c bereitzustellen. Es ist mit dem Python-Framework Flask geschrieben, verwendet Celery f\u00fcr die Arbeiter und Angular 8 f\u00fcr das Frontend. Hier sind die Links zu den Repositories: Backend, Frontend. Im ersten Teil des Artikels werden wir unser Projekt betrachten.","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 \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\/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"},"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}]}}