{"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\/nl\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","title":{"rendered":"CI\/CD in Github Actions voor een project op Flask+Angular","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/26d0945a41bbf3c98fa92f15be5b8c6c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn dit artikel deel ik mijn ervaring met het instellen van CI\/CD met behulp van het Plesk-bedieningspaneel en GitHub Actions. Vandaag gaan we leren hoe we een eenvoudig project met de onopvallende naam \u00abHelloworld\u00bb kunnen implementeren. Het is geschreven in het Python-framework Flask, met workers op Celery en een frontend op Angular 8.<\/p>\n<p><b>Links naar de 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>In het eerste deel van het artikel bekijken we ons project en de onderdelen ervan. In het tweede deel zullen we onderzoeken hoe we Plesk kunnen instellen en de noodzakelijke uitbreidingen en componenten (DB, RabbitMQ, Redis, Docker, etc.) kunnen installeren.<\/p>\n<p>In het derde deel zullen we eindelijk onderzoeken hoe we een pipeline kunnen instellen voor het implementeren van ons project op de server in dev- en prod-omgevingen. En daarna starten we de site op de server.<\/p>\n<p>En ja, ik ben vergeten me voor te stellen. Ik heet Oleg Borzov, ik ben fullstack-ontwikkelaar in het CRM-team voor hypotheekbeheerder in Domklik. <br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1>Projectoverzicht<\/h1>\n<p>\nLaten we beginnen met de twee repositories van het project - de backend en de frontend - en de code doornemen.<\/p>\n<h2>Backend-gedeelte: Flask+Celery<\/h2>\n<p>\nVoor het backend-gedeelte heb ik een vrij populaire stack gekozen onder Python-ontwikkelaars: het Flask-framework (voor API) en Celery (voor de taakqueue). SQLAlchemy wordt gebruikt als ORM. Alembic wordt toegepast voor migraties. Voor JSON-validatie in de endpoints wordt Marshmallow gebruikt.<\/p>\n<p>In <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">de repository<\/a><\/noindex> Er is een bestand Readme.md met een gedetailleerde beschrijving van de structuur en instructies voor het starten van het project.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_web\">API van het webgedeelte<\/a><\/noindex> is vrij eenvoudig, bestaat uit 6 endpoints:<\/p>\n<ul>\n<li><code>\/ping<\/code> \u2014 voor de beschikbaarheidscontrole;<\/li>\n<li>endpoints voor registratie, autorisatie, de-autorisatie en het verkrijgen van de geautoriseerde gebruiker;<\/li>\n<li>een endpoint voor het versturen van e-mail dat een taak in de Celery-queue plaatst.<\/li>\n<\/ul>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_celery\">Celery-gedeelte<\/a><\/noindex> is nog eenvoudiger, er is slechts \u00e9\u00e9n taak <code>send_mail_task<\/code>.<\/p>\n<p>In de map <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/conf\/\">\/conf<\/a><\/noindex> bevat twee submappen: <\/p>\n<ul>\n<li><code>docker<\/code> met twee Docker-bestanden (<code>base.dockerfile<\/code> voor het bouwen van een zelden veranderend basisimage en <code>Dockerfile<\/code> voor de hoofdconstructies);<\/li>\n<li><code>.env_files<\/code> \u2014 met bestanden voor omgevingsvariabelen voor verschillende omgevingen. <\/li>\n<\/ul>\n<p>\nIn de hoofdmap van het project bevinden zich vier docker-compose-bestanden:<\/p>\n<ul>\n<li><code>docker-compose.local.db.yml<\/code> voor het opzetten van een lokale DB voor ontwikkeling;<\/li>\n<li><code>docker-compose.local.workers.yml<\/code> voor het lokaal opzetten van een worker, DB, Redis en RabbitMQ;<\/li>\n<li><code>docker-compose.test.yml<\/code> voor het uitvoeren van tests tijdens de implementatie;<\/li>\n<li><code>docker-compose.yml<\/code> voor de implementatie.<\/li>\n<\/ul>\n<p>\nEn de laatste interessante map voor ons \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/.ci-cd\">.ci-cd<\/a><\/noindex>. Hierin bevinden zich shell-scripts voor implementatie:<\/p>\n<ul>\n<li><code>deploy.sh<\/code> \u2014 het starten van migraties en implementatie. Wordt uitgevoerd op de server na de bouw en het uitvoeren van tests in GitHub Actions;<\/li>\n<li><code>rollback.sh<\/code> \u2014 het terugdraaien van containers naar de vorige versie van de build;<\/li>\n<li><code>curl_tg.sh<\/code> \u2014 het versturen van meldingen over de implementatie naar Telegram.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Frontend op Angular<\/h2>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\">Repository met de frontend<\/a><\/noindex> is veel eenvoudiger dan de backend. De frontend bestaat uit drie pagina's:<\/p>\n<ul>\n<li>De homepage met een formulier voor het verzenden van e-mail en een uitlogknop.<\/li>\n<li>Inlogpagina.<\/li>\n<li>Registratiepagina.<\/li>\n<\/ul>\n<p>\nDe homepage ziet er eenvoudig uit:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/4db36add2c4277397724b0d71b96ff06.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn de root liggen twee bestanden <code>Dockerfile<\/code> en <code>docker-compose.yml<\/code>, evenals de bekende map <code>.ci-cd<\/code> met iets minder scripts dan in het backend-repository (de scripts voor het uitvoeren van tests zijn verwijderd).<\/p>\n<h1>We starten het project in Plesk<\/h1>\n<p>\nLaten we beginnen met het instellen van Plesk en het cre\u00ebren van een abonnement voor onze site.<\/p>\n<h2>Installeren van extensies<\/h2>\n<p>\nIn Plesk hebben we vier extensies nodig: <\/p>\n<ul>\n<li><code>Docker<\/code> voor het beheren en visueel weergeven van de status van containers in de Plesk-admin;<\/li>\n<li><code>Git<\/code> voor het instellen van de deploy-stap op de server;<\/li>\n<li><code>Let's Encrypt<\/code> voor het genereren (en automatisch verlengen) van gratis TLS-certificaten;<\/li>\n<li><code>Firewall<\/code> voor het instellen van filtering van binnenkomend verkeer.<\/li>\n<\/ul>\n<p>\nZe kunnen worden ge\u00efnstalleerd via de Plesk-admin in de sectie Extensies:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/350cf7d7ca3ae1a965a0d3f8a73594ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWe zullen de gedetailleerde instellingen van de extensies niet bespreken; voor onze demo-doeleinden zijn de standaardinstellingen voldoende.<\/p>\n<h2>Aanmaken van een abonnement en site<\/h2>\n<p>\nVervolgens moeten we een abonnement maken voor onze site helloworld.nl en daar het subdomein dev.helloworld.nl aan toevoegen.<\/p>\n<ol>\n<li>We cre\u00ebren een abonnement voor het domein helloworld.nl en geven de login-wachtwoord op voor de systeemgebruiker:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3932dc7492aa523b6cbd540ad7a7fe6b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nOnder aan de pagina vink je het vakje aan <i>Beveilig het domein met Let's Encrypt<\/i>, als we HTTPS voor de site willen instellen:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f4d23a34764fdeb411fe2ca9cc95cb45.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Vervolgens maken we in dit abonnement het subdomein dev.helloworld.nl aan (waarvoor ook een gratis TLS-certificaat kan worden uitgegeven):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d57e6ba71454cb537917400de4275e92.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>Installeren van servercomponenten<\/h1>\n<p>\nWe hebben een server met <i>OS Debian Stretch 9.12<\/i> en een ge\u00efnstalleerde beheerpanelen <i>Plesk Obsidian 18.0.27<\/i>.<\/p>\n<p>We moeten voor ons project installeren en instellen:<\/p>\n<ul>\n<li>PostgreSQL (in ons geval zal er \u00e9\u00e9n server zijn met twee DB's voor dev- en prod-omgevingen).<\/li>\n<li>RabbitMQ (hetzelfde, \u00e9\u00e9n instantie met verschillende vhosts voor omgevingen).<\/li>\n<li>Twee instanties van Redis (voor dev- en prod-omgevingen).<\/li>\n<li>Docker Registry (voor lokale opslag van samengestelde Docker-images).<\/li>\n<li>UI-interface voor Docker registry.<\/li>\n<\/ul>\n<p><\/p>\n<h2>PostgreSQL<\/h2>\n<p>\nBij Plesk wordt al een PostgreSQL-database meegeleverd, maar niet de nieuwste versie (op het moment van schrijven ondersteunde Plesk Obsidian <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.plesk.com\/release-notes\/obsidian\/software-requirements\/\">Postgres versies 8.4\u201310.8). We willen echter de nieuwste versie voor onze applicatie (12.3 op het moment van schrijven), dus we zullen deze handmatig installeren.<\/a><\/noindex> Er zijn tal van gedetailleerde instructies voor het installeren van Postgres op Debian online (<\/p>\n<p>), dus ik zal ze niet uitgebreid beschrijven, maar gewoon de commando's geven:<noindex><a rel=\"nofollow\" href=\"https:\/\/tecadmin.net\/install-postgresql-on-debian-9-stretch\/\">bijvoorbeeld<\/a><\/noindex>), daarom zal ik ze niet in detail beschrijven, ik geef gewoon de opdrachten:<\/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>\nAangezien de standaardinstellingen in PostgreSQL nogal gemiddeld zijn, is het noodzakelijk om de configuratie aan te passen. Dit kan ons helpen <noindex><a rel=\"nofollow\" href=\"http:\/\/pgconfigurator.cybertec.at\/\">calculator<\/a><\/noindex>: je moet de parameters van je server invoeren en de instellingen in het bestand <code>\/etc\/postgresql\/12\/main\/postgresql.conf<\/code>vervangen door de voorgestelde. Het is belangrijk op te merken dat dergelijke calculators geen wondermiddel zijn, en de database verder geoptimaliseerd moet worden op basis van jouw hardware, applicaties en de complexiteit van de queries. Maar voor een start is dit voldoende.<\/p>\n<p>Naast de door de calculator voorgestelde instellingen, veranderen we in <code>postgresql.conf<\/code>de standaardpoort 5432 naar een andere (in ons voorbeeld \u2014 <strong>53983<\/strong>).<\/p>\n<p>Na het wijzigen van het configuratiebestand herstarten we postgresql-server met de volgende opdracht:<\/p>\n<pre><code class=\"bash\">service postgresql restart\n<\/code><\/pre>\n<p>\nWe hebben PostgreSQL ge\u00efnstalleerd en geconfigureerd. Nu maken we een database, gebruikers voor dev- en prod-omgevingen, en geven we de gebruikers de rechten om de database te beheren:<\/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>\nLaten we verder gaan met de installatie van RabbitMQ \u2014 de berichtenbroker voor Celery. Het wordt behoorlijk eenvoudig ge\u00efnstalleerd op Debian:<\/p>\n<pre><code class=\"bash\">wget https:\/\/packages.erlang-solutions.com\/erlang-solutions_1.0_all.deb\nsudo dpkg -i erlang-solutions_1.0_all.deb\n\nsudo apt-get update\nsudo apt-get install erlang erlang-nox\n\nsudo add-apt-repository 'deb http:\/\/www.rabbitmq.com\/debian\/ testing main'\nwget -O- https:\/\/www.rabbitmq.com\/rabbitmq-release-signing-key.asc | sudo apt-key add -\n\nsudo apt-get update\nsudo apt-get install rabbitmq-server\n<\/code><\/pre>\n<p>\nNa de installatie moeten we <i>vhosts<\/i>, gebruikers aanmaken en de benodigde rechten toekennen:<\/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>\nLaten we nu de laatste component voor onze applicatie installeren en configureren \u2014 Redis. Dit zal als backend dienen voor het opslaan van de resultaten van Celery-taken.<\/p>\n<p>We zullen twee Docker-containers met Redis opzetten voor de dev- en prod-omgevingen met behulp van de extensie <code>Docker<\/code> voor Plesk.<\/p>\n<ol>\n<li>Ga naar Plesk, navigeer naar het gedeelte Extensies, zoek de Docker-extensie en installeer deze (we hebben de gratis versie nodig):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/5638f9b40274e4f7441434279c76a426.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Ga naar de ge\u00efnstalleerde extensie, vind via de zoekfunctie de afbeelding <code>redis bitnami<\/code> en installeer de laatste versie:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/578bbbed6a66a60a4f7410f7d61ea542.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Ga naar de gedownloade container en pas de configuratie aan: geef de poort op, de maximale toegewezen hoeveelheid RAM, het wachtwoord in de omgevingsvariabelen en mount de volume:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/8bc804b794735412f5e510b3049c5667.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Voer stappen 2-3 uit voor de prod-container, wijzig alleen de parameters in de instellingen: poort, wachtwoord, RAM-grootte en het pad naar de map volume op de server:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op 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>\nNaast de basisservices zou het goed zijn om een eigen Docker-image repository op de server te plaatsen. Gelukkig is de opslagruimte op servers nu redelijk goedkoop (zeker goedkoper dan een abonnement op DockerHub), en het proces van het installeren van een priv\u00e9-repository is heel eenvoudig. <\/p>\n<p>We willen dat we hebben ge\u00efnstalleerd:<\/p>\n<ul>\n<li>een met wachtwoord beveiligde Docker-repository, toegankelijk via een subdomein <noindex><a rel=\"nofollow\" href=\"https:\/\/docker.helloworld.ru\">https:\/\/docker.helloworld.ru<\/a><\/noindex>;<\/li>\n<li>Een UI-interface om de afbeeldingen in de repository te bekijken, toegankelijk via het adres <noindex><a rel=\"nofollow\" href=\"https:\/\/docker-ui.helloworld.ru\">https:\/\/docker-ui.helloworld.ru<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nDaarvoor:<\/p>\n<ol>\n<li>Laten we in Plesk twee subdomeinen aanmaken in ons abonnement: docker.helloworld.ru en docker-ui.helloworld.ru, en laten we certificaten van Let's Encrypt voor hen instellen.<\/li>\n<li>In de map van het subdomein docker.helloworld.ru voegen we het bestand toe <code>docker-compose.yml<\/code> met de volgende inhoud:\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>Genereer met SSH het bestand .htpasswd voor Basic-authenticatie in de Docker-repository:\n<pre><code class=\"plaintext\">htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password<\/code><\/pre>\n<\/li>\n<li>Laten we de containers bouwen en opstarten:\n<pre><code class=\"plaintext\">docker-compose up -d<\/code><\/pre>\n<\/li>\n<li>En we moeten Nginx doorsturen naar onze containers. Dit kan via Plesk. <\/li>\n<\/ol>\n<p>\nDe volgende stappen moeten worden uitgevoerd voor de subdomeinen docker.helloworld.ru en docker-ui.helloworld.ru:<\/p>\n<p>In de sectie <i>Dev Tools<\/i> van onze website gaan we naar <i>Docker Proxy Rules<\/i>:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d9c05901d95d61ce5db27a1a7cfddf49.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEn we voegen een regel toe voor het proxen van het binnenkomende verkeer naar onze container:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/faa85cf6da648ef55b7c87c44040efb1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<ol>\n<li>Controleer of we kunnen inloggen op onze container vanaf de lokale machine:\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.\nLogin Succeeded<\/code><\/pre>\n<\/li>\n<li>Laten we ook de werking van het subdomein docker-ui.helloworld.ru controleren:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/fdd6e1396c27c02a88bcbd7efb12f247.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWanneer u op Browse repositories klikt, toont de browser een venster voor authenticatie waarin u de gebruikersnaam en het wachtwoord voor de repository moet invoeren. Daarna worden we doorgestuurd naar de pagina met de lijst van repositories (deze zal voorlopig leeg zijn):<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f2d75edb530e9d746095ae02d7793c1f.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h2>Poorten openen in Plesk Firewall<\/h2>\n<p>\nNa het installeren en configureren van de componenten moeten we de poorten openen, zodat de componenten toegankelijk zijn vanuit Docker-containers en het externe netwerk.<\/p>\n<p>Laten we bekijken hoe dit moet, aan de hand van de eerder ge\u00efnstalleerde Firewall-extensie voor Plesk.<\/p>\n<ol>\n<li>We gaan naar <i>Tools &amp; Settings &gt; Instellingen &gt; Firewall<\/i>:<br \/>\n<img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d54b4941944fe191574472a475cbaebe.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>We gaan naar <i>Pas Plesk Firewall-regels aan &gt; Voeg aangepaste regel toe<\/i> en open de volgende TCP-poorten voor de Docker-subnet (<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 voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3359845f9bd6dc303274da737d4b27a2.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Laten we ook een regel toevoegen die de buitenwereld toegang geeft tot de PostgreSQL-poorten en de managementpanelen van RabbitMQ:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f812cfc7127da469507538ea48b2c80d.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Pas de regels toe met de knop Wijzigingen toepassen:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op 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-configuratie in Github Actions<\/h1>\n<p>\nLaten we beginnen met het spannendste gedeelte \u2014 het configureren van de continue integratie- en leveringspipeline voor ons project op de server.<\/p>\n<p>Deze pipeline zal uit twee delen bestaan: <\/p>\n<ul>\n<li>het bouwen van een image en het uitvoeren van tests (voor de backend) \u2014 aan de Github-kant;<\/li>\n<li>migraties uitvoeren (voor de backend) en containers implementeren \u2014 op de server.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Implementatie in Plesk<\/h2>\n<p>\nLaten we eerst de tweede punt behandelen (aangezien de eerste daarvan afhankelijk is).<\/p>\n<p>We zullen het implementatieproces configureren met behulp van de Git-extensie voor Plesk.<\/p>\n<p>Laten we een voorbeeld bekijken met de Prod-omgeving voor de Backend-repository.<\/p>\n<ol>\n<li>We gaan naar het abonnement van onze Helloworld-website en navigeren naar het Git-subgedeelte:\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/b55b371560ea87a2954b6553a7cefc49.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Plak de link naar onze Github-repository in het veld 'Remote Git repository' en wijzig de standaardmap <code>httpdocs<\/code> in een andere (bijvoorbeeld, <code>\/httpdocs\/hw_back<\/code>):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/db9ba9adb6e2e1bed5da7ae8e88c76e1.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Kopieer de SSH Public key van de vorige stap en <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/settings\/ssh\/new\">toevoegen<\/a><\/noindex> voeg deze toe in de instellingen van Github.<\/li>\n<li>Klik op OK op het scherm in punt 2, waarna we worden doorgestuurd naar de repositorypagina in Plesk. Nu moeten we de repository instellen zodat deze wordt bijgewerkt bij commits naar de master branch. Ga hiervoor naar <i>Repository-instellingen<\/i> en sla de waarde op <code>Webhook URL<\/code> (deze hebben we later nodig bij de configuratie van Github Actions):\n<p><img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/14d48a357f821d734d40dc434ab3682a.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Voer in het veld Acties op het scherm van de vorige stap het script in voor het starten van de implementatie:\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>\nwaarbij: <\/p>\n<p><code>{REPOSITORY_ABSOLUTE_PATH}<\/code> \u2014 het pad naar de map van de prod backend-repository op de server;<br \/>\n<code>{ENV}<\/code> \u2014 omgeving (dev\/prod), in ons geval <code>prod<\/code>;<br \/>\n<code>{DOCKER_REGISTRY_HOST}<\/code> \u2014 de host van onze docker repository<br \/>\n<code>{TG_BOT_TOKEN}<\/code> \u2014 de token van de Telegram-bot;<br \/>\n<code>{TG_CHAT_ID}<\/code> \u2014 de ID van de chat\/kanaal voor het verzenden van meldingen.<\/p>\n<p>Voorbeeld van het script:<\/p>\n<pre><code class=\"plaintext\">cd \/var\/www\/vhosts\/helloworld.ru\/httpdocs\/hw_back\/\n.ci-cd\/deploy.sh dev docker.helloworld.ru docker_user docker_password 12345678:AAbcdEfghCH1vGbCasdfSAs0K5PALDsaw -1001234567890<\/code><\/pre>\n<\/li>\n<li>Laten we de gebruiker uit ons abonnement aan de Docker-groep toevoegen (zodat hij de containers kan beheren):\n<pre><code class=\"plaintext\">sudo usermod -aG docker helloworld_admin<\/code><\/pre>\n<\/li>\n<\/ol>\n<p>\nDe ontwikkelomgeving voor de backend-repository en frontend wordt op dezelfde manier ingesteld.<\/p>\n<h2>Pipeline voor deployment in Github Actions<\/h2>\n<p>\nLaten we beginnen met het instellen van het eerste deel van onze CI\/CD-pipeline in Github Actions.<\/p>\n<h3>Backend<\/h3>\n<p>\nDe pipeline wordt beschreven in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/blob\/master\/.github\/workflows\/deploy.yml\">het bestand deploy.yml<\/a><\/noindex>. <\/p>\n<p>Maar voordat we dat bekijken, vullen we de benodigde geheimen in Github in. Ga hiervoor naar <i>Instellingen -&gt; Geheimen<\/i>:<\/p>\n<ul>\n<li><code>DOCKER_REGISTRY<\/code> \u2014 het hostadres van onze Docker-repository (docker.helloworld.ru);<\/li>\n<li><code>DOCKER_LOGIN<\/code> \u2014 login voor de Docker-repository;<\/li>\n<li><code>DOCKER_PASSWORD<\/code> \u2014 wachtwoord hiervoor;<\/li>\n<li><code>DEPLOY_HOST<\/code> \u2014 het hostadres waar het Plesk-dashboard beschikbaar is (bijvoorbeeld: <noindex><a rel=\"nofollow\" href=\"https:\/\/helloworld.ru\">helloworld.ru<\/a><\/noindex>:8443 of <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 voor deployment naar de prod-repository op de server (we hebben dit gekregen in Deploy in Plesk punt 4);<\/li>\n<li><code>DEPLOY_BACK_DEV_TOKEN<\/code> \u2014 token voor deployment naar de dev-repository op de server.<\/li>\n<\/ul>\n<p>\nHet deploymentproces is eenvoudig en bestaat uit drie hoofd stappen: <\/p>\n<ul>\n<li>het bouwen en publiceren van de afbeelding in onze repository;<\/li>\n<li>het uitvoeren van tests in een container op basis van de vers gebouwde afbeelding;<\/li>\n<li>de deployment naar de juiste omgeving afhankelijk van de tak (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\">Het bestand deploy.yml voor de front-end repository<\/a><\/noindex> verschilt nauwelijks van de back-end. Het bevat geen stap voor het uitvoeren van tests en de namen van de tokens voor deployment zijn anders. Geheimen voor de front-end repository moeten trouwens apart worden ingevuld.<\/p>\n<h1>Website-instellingen<\/h1>\n<p><\/p>\n<h2>Traffic proxydelen via Nginx<\/h2>\n<p>\nNou, we zijn aan het einde gekomen. We moeten alleen nog het inkomende en uitgaande verkeer naar onze container via Nginx instellen. Dit proces hebben we al besproken in punt 5 van de Docker Registry-instellingen. Dit moet voor de back-end en front-end in de dev- en prod-omgevingen worden herhaald.<\/p>\n<p>Ik geef enkele screenshots van de instellingen.<\/p>\n<h3>Backend<\/h3>\n<p>\n<img decoding=\"async\" alt=\"CI\/CD in Github Actions voor een project op 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 voor een project op Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/35d4d8ef3489608db1e173b7f214ca03.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<strong>Belangrijke opmerking<\/strong>. In de frontend-container worden alle URL's geproxyd naar de backend-container, behalve die beginnen met <code>\/api\/<\/code> \u2014 deze worden naar de backend-container geproxyd (daarom <strong>moeten alle handlers in de backend-container beginnen met <\/strong><strong><code>\/api\/<\/code><\/strong>).<\/p>\n<h2>Conclusies<\/h2>\n<p>\nNu moet onze site bereikbaar zijn via de adressen helloworld.ru en dev.helloworld.ru (respectievelijk prod- en dev-omgeving).<\/p>\n<p>Samenvattend hebben we geleerd hoe we een eenvoudige applicatie op Flask en Angular kunnen voorbereiden en een pipeline in Github Actions kunnen instellen voor de deployment op een server die wordt beheerd door Plesk.<\/p>\n<p>Hier zijn de links naar de repositories met de 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 \/>Bron: <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.3 - 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\/nl\/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.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"nl_NL\" \/>\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\/nl\/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 voor een project op Flask+Angular | ProHoster","description":"In dit artikel deel ik mijn ervaring.","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nl_NL","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\/nl\/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\/nl\/wp-json\/wp\/v2\/posts\/85646","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=85646"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/85646\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media\/85647"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=85646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=85646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=85646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}