{"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\/da\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","title":{"rendered":"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/26d0945a41bbf3c98fa92f15be5b8c6c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nI denne artikel vil jeg dele min erfaring med at indstille CI\/CD ved hj\u00e6lp af Plesk kontrolpanelet og Github Actions. I dag skal vi l\u00e6re at deployere et simpelt projekt med det uformelle navn \u00abHelloworld\u00bb. Det er skrevet med Python-rammen Flask, med arbejdere p\u00e5 Celery og frontend p\u00e5 Angular 8.<\/p>\n<p><b>Links til 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>I den f\u00f8rste del af artiklen vil vi se p\u00e5 vores projekt og dets dele. I den anden vil vi finde ud af, hvordan man konfigurerer Plesk og installerer de n\u00f8dvendige udvidelser og komponenter (DB, RabbitMQ, Redis, Docker osv.).<\/p>\n<p>I den tredje del vil vi endelig unders\u00f8ge, hvordan man konfigurerer en pipeline til at deployere vores projekt p\u00e5 serveren i dev- og prod-milj\u00f8. Og s\u00e5 vil vi k\u00f8re hjemmesiden p\u00e5 serveren.<\/p>\n<p>Og ja, jeg glemte at pr\u00e6sentere mig selv. Jeg hedder Oleg Borzov, jeg er fullstack-udvikler i CRM-teamet for boligl\u00e5nsmanagere hos Domklik. <br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1>Projektoversigt<\/h1>\n<p>\nLad os f\u00f8rst se p\u00e5 de to repositories for projektet \u2014 backend og frontend \u2014 og f\u00e5 et indblik i koden.<\/p>\n<h2>Backend-delen: Flask+Celery<\/h2>\n<p>\nTil backend-delen har jeg valgt en ret popul\u00e6r kombination blandt Python-udviklere: Flask framework (til API) og Celery (til opgavek\u00f8). SQLAlchemy bruges som ORM. Alembic anvendes til migrationer. Til validering af JSON i h\u00e6nderne \u2014 Marshmallow.<\/p>\n<p>I <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\">repository<\/a><\/noindex> Der er en Readme.md-fil med en detaljeret beskrivelse af strukturen og instruktioner til at starte projektet.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_web\">API-webdelen<\/a><\/noindex> er forholdsvis simpel og best\u00e5r af 6 endpoints:<\/p>\n<ul>\n<li><code>\/ping<\/code> \u2014 til at kontrollere tilg\u00e6ngelighed;<\/li>\n<li>endpoints til registrering, autorisering, deautorisering og til at f\u00e5 den autoriserede bruger;<\/li>\n<li>et endpoint til at sende email, der placerer en opgave i Celery-k\u00f8en.<\/li>\n<\/ul>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/app_celery\">Celery-delen<\/a><\/noindex> er endnu enklere, der er kun \u00e9n opgave <code>send_mail_task<\/code>.<\/p>\n<p>I mappen <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/conf\/\">\/conf<\/a><\/noindex> der er to undermapper: <\/p>\n<ul>\n<li><code>docker<\/code> med to Docker-filer (<code>base.dockerfile<\/code> til at bygge et grundl\u00e6ggende billede, der sj\u00e6ldent \u00e6ndres, og <code>Dockerfile<\/code> til de vigtigste builds);<\/li>\n<li><code>.env_files<\/code> \u2014 med filer til milj\u00f8variabler for forskellige milj\u00f8er. <\/li>\n<\/ul>\n<p>\nI rodmappen af projektet findes der fire docker-compose-filer:<\/p>\n<ul>\n<li><code>docker-compose.local.db.yml<\/code> til at starte en lokal database til udvikling;<\/li>\n<li><code>docker-compose.local.workers.yml<\/code> til lokalt at starte arbejdere, database, Redis og RabbitMQ;<\/li>\n<li><code>docker-compose.test.yml<\/code> til at k\u00f8re tests under implementering;<\/li>\n<li><code>docker-compose.yml<\/code> til deployment.<\/li>\n<\/ul>\n<p>\nOg den sidste interessante mappe for os \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/tree\/master\/.ci-cd\">.ci-cd<\/a><\/noindex>. I den ligger shell-scripts til deployment:<\/p>\n<ul>\n<li><code>deploy.sh<\/code> \u2014 k\u00f8rsel af migrationer og deployment. K\u00f8rer p\u00e5 serveren efter build og test i Github Actions;<\/li>\n<li><code>rollback.sh<\/code> \u2014 tilbagef\u00f8rsel af containere til den tidligere version af build;<\/li>\n<li><code>curl_tg.sh<\/code> \u2014 sending notifications about deployment on Telegram.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Frontend p\u00e5 Angular<\/h2>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld-front\">Repository med front<\/a><\/noindex> er meget enklere end back. Fronten best\u00e5r af tre sider:<\/p>\n<ul>\n<li>Forsiden med en formular til at sende e-mail og en logout-knap.<\/li>\n<li>Login-side.<\/li>\n<li>Registreringsside.<\/li>\n<\/ul>\n<p>\nForsiden ser undselig ud:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/4db36add2c4277397724b0d71b96ff06.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nI roden ligger der to filer <code>Dockerfile<\/code> og <code>docker-compose.yml<\/code>, samt en velkendt mappe <code>.ci-cd<\/code> med et lidt mindre antal scripts end i backrepositoryt (scripts til testk\u00f8rsel er fjernet).<\/p>\n<h1>Opret projekt i Plesk<\/h1>\n<p>\nLad os begynde med at konfigurere Plesk og oprette et abonnement til vores website.<\/p>\n<h2>Installation af udvidelser<\/h2>\n<p>\nI Plesk har vi brug for fire udvidelser: <\/p>\n<ul>\n<li><code>Docker<\/code> til at administrere og visuelt vise tilstanden af containere i Plesk adminpanelet;<\/li>\n<li><code>Git<\/code> til at konfigurere deployment-trinene p\u00e5 serveren;<\/li>\n<li><code>Let's Encrypt<\/code> til generering (og automatisk forl\u00e6ngelse) af gratis TLS-certifikater;<\/li>\n<li><code>Firewall<\/code> til konfiguration af filter for indkommende trafik.<\/li>\n<\/ul>\n<p>\nDe kan installeres gennem Plesk adminpanelet under Udvidelser:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/350cf7d7ca3ae1a965a0d3f8a73594ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nVi vil ikke gennemg\u00e5 detaljerne ved installationen af udvidelserne; de standardindstillinger, vi f\u00e5r, er tilstr\u00e6kkelige til vores demoform\u00e5l.<\/p>\n<h2>Oprettelse af abonnement og hjemmeside<\/h2>\n<p>\nDern\u00e6st skal vi oprette et abonnement til vores hjemmeside helloworld.ru og tilf\u00f8je underdom\u00e6net dev.helloworld.ru.<\/p>\n<ol>\n<li>Vi opretter abonnementet for dom\u00e6net helloworld.ru og angiver login-adgangskode for systembrugeren:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3932dc7492aa523b6cbd540ad7a7fe6b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNederst p\u00e5 siden s\u00e6tter vi et flueben <i>Sikre dom\u00e6net med Let's Encrypt<\/i>, hvis vi \u00f8nsker at konfigurere HTTPS til vores hjemmeside:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f4d23a34764fdeb411fe2ca9cc95cb45.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Dern\u00e6st opretter vi underdom\u00e6net dev.helloworld.ru i dette abonnement (for hvilket der ogs\u00e5 kan udstedes et gratis TLS-certifikat):\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 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 af serverkomponenter<\/h1>\n<p>\nVi har en server med <i>OS Debian Stretch 9.12<\/i> og den installerede kontrolpanel <i>Plesk Obsidian 18.0.27<\/i>.<\/p>\n<p>Vi skal installere og konfigurere f\u00f8lgende til vores projekt:<\/p>\n<ul>\n<li>PostgreSQL (i vores tilf\u00e6lde vil der v\u00e6re \u00e9n server med to databaser til dev- og prod-milj\u00f8et).<\/li>\n<li>RabbitMQ (samme princip, en instans med forskellige vhosts til milj\u00f8er).<\/li>\n<li>To instanser af Redis (til dev- og prod-milj\u00f8).<\/li>\n<li>Docker Registry (til lokal lagring af bygget Docker-images).<\/li>\n<li>UI-interface til Docker registry.<\/li>\n<\/ul>\n<p><\/p>\n<h2>PostgreSQL<\/h2>\n<p>\nMed Plesk f\u00f8lger en PostgreSQL-database, men ikke den nyeste version (p\u00e5 tidspunktet for skrivning underst\u00f8ttede Plesk Obsidian <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.plesk.com\/release-notes\/obsidian\/software-requirements\/\">Postgres versioner 8.4\u201310.8). Vi \u00f8nsker dog den nyeste version (12.3 p\u00e5 tidspunktet for skrivningen), s\u00e5 vi vil installere den manuelt.<\/a><\/noindex> Der er mange detaljerede instruktioner til installation af Postgres p\u00e5 Debian online (<\/p>\n<p>), s\u00e5 jeg vil ikke beskrive dem i detaljer, men blot pr\u00e6senterer kommandoerne:<noindex><a rel=\"nofollow\" href=\"https:\/\/tecadmin.net\/install-postgresql-on-debian-9-stretch\/\">eksempel<\/a><\/noindex>), s\u00e5 jeg vil ikke g\u00e5 i detaljer om dem, jeg vil blot give kommandoerne:<\/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>\nI betragtning af, at PostgreSQL har ret middelm\u00e5dige standardindstillinger, er det vigtigt at justere konfigurationen. Dette vil hj\u00e6lpe os med <noindex><a rel=\"nofollow\" href=\"http:\/\/pgconfigurator.cybertec.at\/\">beregner<\/a><\/noindex>: du skal indtaste dine servers parametre og erstatte indstillingerne i filen <code>\/etc\/postgresql\/12\/main\/postgresql.conf<\/code>med de foresl\u00e5ede. Her skal det n\u00e6vnes, at s\u00e5danne kalkulatorer ikke er en magisk l\u00f8sning, og databasen b\u00f8r justeres mere pr\u00e6cist baseret p\u00e5 dit hardware, applikationer og kompleksiteten af foresp\u00f8rgsler. Men til en start er dette tilstr\u00e6kkeligt.<\/p>\n<p>Udover de foresl\u00e5ede indstillinger fra kalkulatoren \u00e6ndrer vi ogs\u00e5 i <code>postgresql.conf<\/code>den standardport 5432 til en anden (i vores eksempel \u2014 <strong>53983<\/strong>).<\/p>\n<p>Efter \u00e6ndringen af konfigurationsfilen genstarter vi postgresql-serveren med kommandoen:<\/p>\n<pre><code class=\"bash\">service postgresql restart\n<\/code><\/pre>\n<p>\nVi har installeret og konfigureret PostgreSQL. Nu opretter vi databaser, brugere til dev- og prod-milj\u00f8er, og giver brugerne rettigheder til at administrere databasen:<\/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>\nLad os g\u00e5 videre til installationen af RabbitMQ \u2014 beskedbroker til Celery. Det installeres temmelig let p\u00e5 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>\nEfter installationen skal vi oprette <i>vhosts<\/i>, brugere og tildele de n\u00f8dvendige rettigheder:<\/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>\nNu skal vi installere og konfigurere den sidste komponent til vores applikation \u2014 Redis. Den vil blive brugt som backend til opbevaring af resultaterne fra Celery-opgaver.<\/p>\n<p>Vi vil ops\u00e6tte to Docker-containere med Redis til dev- og prod-milj\u00f8er ved hj\u00e6lp af udvidelsen <code>Docker<\/code> til Plesk.<\/p>\n<ol>\n<li>Vi g\u00e5r ind i Plesk, navigerer til sektionen Udvidelser, s\u00f8ger efter udvidelsen Docker og installerer den (vi har brug for den gratis version):\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/5638f9b40274e4f7441434279c76a426.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Vi g\u00e5r til den installerede udvidelse, finder via s\u00f8gning billedet <code>redis bitnami<\/code> og installerer den nyeste version:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/578bbbed6a66a60a4f7410f7d61ea542.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Vi g\u00e5r ind i den downloadede container og justerer konfigurationen: angiver port, maksimal tildelt RAM, adgangskoden i milj\u00f8variablerne, og monterer volumen:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/8bc804b794735412f5e510b3049c5667.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Udf\u00f8r trin 2-3 for prod-containeren, i indstillingerne \u00e6ndrer vi kun parametrene: port, adgangskode, ram st\u00f8rrelse og stien til mappingsmappen p\u00e5 serveren:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 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>\nUdover de grundl\u00e6ggende tjenester ville det v\u00e6re godt at have et eget Docker-billede repository p\u00e5 serveren. Heldigvis er pladsen p\u00e5 servere nu rimeligt billig (meget billigere end et abonnement p\u00e5 DockerHub), og processen med at installere et privat repository er meget enkel. <\/p>\n<p>Vi \u00f8nsker, at vi har installeret:<\/p>\n<ul>\n<li>et beskyttet Docker-repository med adgangskode, tilg\u00e6ngeligt via subdom\u00e6net <noindex><a rel=\"nofollow\" href=\"https:\/\/docker.helloworld.ru\">https:\/\/docker.helloworld.ru<\/a><\/noindex>;<\/li>\n<li>UI-gr\u00e6nseflade til at se billeder i repository, tilg\u00e6ngelig p\u00e5 adressen <noindex><a rel=\"nofollow\" href=\"https:\/\/docker-ui.helloworld.ru\">https:\/\/docker-ui.helloworld.ru<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nFor dette:<\/p>\n<ol>\n<li>Vi opretter to subdom\u00e6ner i vores Plesk-abonnement: docker.helloworld.ru og docker-ui.helloworld.ru, og konfigurerer certifikaterne fra Let\u2019s Encrypt for dem.<\/li>\n<li>I mappen for subdom\u00e6net docker.helloworld.ru tilf\u00f8jer vi filen <code>docker-compose.yml<\/code> med f\u00f8lgende indhold:\n<pre><code class=\"plaintext\">version: \"3\"\n\nservices:\n  docker-registry:\n    image: \"registry:2\"\n    restart: always\n    ports:\n      - \"53985:5000\"\n    environment:\n      REGISTRY_AUTH: htpasswd\n      REGISTRY_AUTH_HTPASSWD_REALM: basic-realm\n      REGISTRY_AUTH_HTPASSWD_PATH: \/auth\/.htpasswd\n      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: \/data\n    volumes:\n      - .\/.docker-registry.htpasswd:\/auth\/.htpasswd\n      - .\/data:\/data\n\n  docker-registry-ui:\n    image: konradkleine\/docker-registry-frontend:v2\n    restart: always\n    ports:\n      - \"53986:80\"\n    environment:\n      VIRTUAL_HOST: '*, https:\/\/*'\n      ENV_DOCKER_REGISTRY_HOST: 'docker-registry'\n      ENV_DOCKER_REGISTRY_PORT: 5000\n    links:\n      - 'docker-registry'\n<\/code><\/pre>\n<p>\n<\/li>\n<li>Via SSH genererer vi .htpasswd-filen til Basic-godkendelse i Docker-repositoryet:\n<pre><code class=\"plaintext\">htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password<\/code><\/pre>\n<\/li>\n<li>Vi bygger og starter containerne:\n<pre><code class=\"plaintext\">docker-compose up -d<\/code><\/pre>\n<\/li>\n<li>Og vi skal omdirigere Nginx til vores containere. Dette kan g\u00f8res via Plesk. <\/li>\n<\/ol>\n<p>\nDe f\u00f8lgende handlinger skal udf\u00f8res for subdom\u00e6nerne docker.helloworld.ru og docker-ui.helloworld.ru:<\/p>\n<p>I sektionen <i>Dev Tools<\/i> g\u00e5r ind under vores websted p\u00e5 <i>Docker Proxy Rules<\/i>:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d9c05901d95d61ce5db27a1a7cfddf49.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nOg vi tilf\u00f8jer en regel for at proxy indg\u00e5ende trafik til vores container:<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/faa85cf6da648ef55b7c87c44040efb1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<ol>\n<li>Vi tjekker, at vi kan logge ind i vores container fra den lokale maskine:\n<pre><code class=\"plaintext\">$ docker login docker.helloworld.ru -u hw_docker_admin -p hw_docker_password\nADVARSEL! Brug af --password via CLI er usikkert. Brug --password-stdin.\nLogin lykkedes<\/code><\/pre>\n<\/li>\n<li>Vi tjekker ogs\u00e5 funktionen af subdom\u00e6net docker-ui.helloworld.ru:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/fdd6e1396c27c02a88bcbd7efb12f247.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nN\u00e5r du klikker p\u00e5 Browse repositories, viser browseren et vindue til godkendelse, hvor du skal indtaste brugernavn og adgangskode til repository'et. Derefter bliver du omdirigeret til siden med listen over repositories (den vil v\u00e6re tom for dig indtil videre):<\/p>\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f2d75edb530e9d746095ae02d7793c1f.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h2>\u00c5bn porte i Plesk Firewall<\/h2>\n<p>\nEfter installation og ops\u00e6tning af komponenter skal vi \u00e5bne porte, s\u00e5 komponenterne kan tilg\u00e5s fra Docker-containere og det eksterne netv\u00e6rk.<\/p>\n<p>Lad os se, hvordan det g\u00f8res med eksemplet p\u00e5 den firewall-udvidelse, vi tidligere installerede til Plesk.<\/p>\n<ol>\n<li>Vi g\u00e5r til <i>Tools &amp; Settings &gt; Settings &gt; Firewall<\/i>:<br \/>\n<img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/d54b4941944fe191574472a475cbaebe.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Vi g\u00e5r til <i>Modificer Plesk Firewall-regler &gt; Tilf\u00f8j brugerdefineret regel<\/i> og \u00e5bner de f\u00f8lgende TCP-porte for Docker-subnettet (<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 i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/3359845f9bd6dc303274da737d4b27a2.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Vi tilf\u00f8jer ogs\u00e5 en regel, der \u00e5bner PostgreSQL-porte og managementpanelet for RabbitMQ for verden:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/f812cfc7127da469507538ea48b2c80d.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Anvend reglerne ved at klikke p\u00e5 knappen Apply Changes:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/e85444ae717aaefa796664003fc86578.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<\/ol>\n<p><\/p>\n<h1>Ops\u00e6tning af CI\/CD i Github Actions<\/h1>\n<p>\nLad os g\u00e5 til den mest interessante del - ops\u00e6tningen af pipeline for kontinuerlig integration og deployment af vores projekt til serveren.<\/p>\n<p>Denne pipeline vil best\u00e5 af to dele: <\/p>\n<ul>\n<li>opbygning af billedet og k\u00f8rsel af tests (for backend) - p\u00e5 GitHub-siden;<\/li>\n<li>k\u00f8rsel af migrationer (for backend) og deployment af containere - p\u00e5 serveren.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Deployment i Plesk<\/h2>\n<p>\nLad os f\u00f8rst kigge p\u00e5 den anden del (da den afh\u00e6nger af den f\u00f8rste).<\/p>\n<p>Vi vil ops\u00e6tte deployments gennem Git-udvidelsen til Plesk.<\/p>\n<p>Lad os tage et eksempel med Prod-milj\u00f8et for Backend repository.<\/p>\n<ol>\n<li>G\u00e5 ind i vores Helloworld-websites abonnement og klik p\u00e5 Git-delen:\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/b55b371560ea87a2954b6553a7cefc49.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Inds\u00e6t linket til vores GitHub-repository i feltet \"Remote Git repository\" og \u00e6ndr standardmappen <code>httpdocs<\/code> til en anden (for eksempel, <code>\/httpdocs\/hw_back<\/code>):\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/db9ba9adb6e2e1bed5da7ae8e88c76e1.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>Kopier SSH Public key fra det forrige trin og <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/settings\/ssh\/new\">tilf\u00f8j<\/a><\/noindex> den i indstillingerne for GitHub.<\/li>\n<li>Klik p\u00e5 OK p\u00e5 sk\u00e6rmen i punkt 2, hvorefter du bliver sendt til repository-siden i Plesk. Nu skal vi ops\u00e6tte opdateringen af repository'et ved commits til master-grenen. For dette skal vi g\u00e5 til <i>Repository Settings<\/i> og gemme v\u00e6rdien <code>Webhook URL<\/code> (vi f\u00e5r brug for den senere ved ops\u00e6tningen af Github Actions):\n<p><img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/14d48a357f821d734d40dc434ab3682a.png\" style=\"display:block;margin: 0 auto;\" \/><\/li>\n<li>I Actions-feltet p\u00e5 sk\u00e6rmen fra det forrige punkt indtaster vi scriptet til at starte deployment:\n<pre><code class=\"plaintext\">cd {REPOSITORY_ABSOLUTE_PATH}\n.ci-cd\/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID} <\/code><\/pre>\n<p>\nhvor: <\/p>\n<p><code>{REPOSITORY_ABSOLUTE_PATH}<\/code> \u2014 stien til mappen for prod backend-repository p\u00e5 serveren;<br \/>\n<code>{ENV}<\/code> \u2014 milj\u00f8 (dev\/prod), i vores tilf\u00e6lde <code>prod<\/code>;<br \/>\n<code>{DOCKER_REGISTRY_HOST}<\/code> \u2014 host til vores docker-repository<br \/>\n<code>{TG_BOT_TOKEN}<\/code> \u2014 token for Telegram-bot;<br \/>\n<code>{TG_CHAT_ID}<\/code> \u2014 ID for chat\/kanal til at sende meddelelser.<\/p>\n<p>Eksempel p\u00e5 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>Vi tilf\u00f8jer brugeren fra vores abonnement til Docker-gruppen (s\u00e5 han kan administrere containere):\n<pre><code class=\"plaintext\">sudo usermod -aG docker helloworld_admin<\/code><\/pre>\n<\/li>\n<\/ol>\n<p>\nUdviklingsmilj\u00f8et for backend-repositoriet og frontend konfigureres p\u00e5 samme m\u00e5de.<\/p>\n<h2>Deploy-pipeline i Github Actions<\/h2>\n<p>\nLad os g\u00e5 videre til indstillingen af den f\u00f8rste del af vores CI\/CD-pipeline i Github Actions.<\/p>\n<h3>Backend<\/h3>\n<p>\nPipelinen beskrives i <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/olegborzov\/helloworld_back\/blob\/master\/.github\/workflows\/deploy.yml\">filen deploy.yml<\/a><\/noindex>. <\/p>\n<p>Men f\u00f8r vi g\u00e5r i gang med det, skal vi udfylde de n\u00f8dvendige Secret-variabler i Github. G\u00e5 til <i>Indstillinger -&gt; Hemmeligheder<\/i>:<\/p>\n<ul>\n<li><code>DOCKER_REGISTRY<\/code> \u2014 v\u00e6rten for vores Docker-repository (docker.helloworld.ru);<\/li>\n<li><code>DOCKER_LOGIN<\/code> \u2014 login til Docker-repository;<\/li>\n<li><code>DOCKER_PASSWORD<\/code> \u2014 password til det;<\/li>\n<li><code>DEPLOY_HOST<\/code> \u2014 v\u00e6rten, hvor Plesk-administrationen er tilg\u00e6ngelig (f.eks.: <noindex><a rel=\"nofollow\" href=\"https:\/\/helloworld.ru\">helloworld.ru<\/a><\/noindex>:8443 eller <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 til deployment i prod-repositoriet p\u00e5 serveren (vi fik det i Udrulning i Plesk punkt 4);<\/li>\n<li><code>DEPLOY_BACK_DEV_TOKEN<\/code> \u2014 token til deployment i dev-repositoriet p\u00e5 serveren.<\/li>\n<\/ul>\n<p>\nDeploy-processen er enkel og best\u00e5r af tre hovedtrin: <\/p>\n<ul>\n<li>opbygning og publicering af billedet i vores repository;<\/li>\n<li>k\u00f8rsel af tests i containeren baseret p\u00e5 det nyopbyggede billede;<\/li>\n<li>udrulning til det relevante milj\u00f8 afh\u00e6ngigt af grenen (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\">Filen deploy.yml for frontend-repositoriet<\/a><\/noindex> adskiller sig ikke meget fra backend-versionen. Den mangler trin til k\u00f8rsel af tests, og navne\u00e6ndringerne for tokens til deployment. Hemmelighederne for frontend-repositoriet skal desuden udfyldes separat.<\/p>\n<h1>Indstilling af webstedet<\/h1>\n<p><\/p>\n<h2>Proxiering af trafik gennem Nginx<\/h2>\n<p>\nS\u00e5 er vi ved vejs ende. Det er kun tilbage at indstille proxiering af indkommende og udg\u00e5ende trafik i vores container gennem Nginx. Denne proces har vi allerede set p\u00e5 i punkt 5 i konfigurationen af Docker Registry. Det samme skal gentages for backend- og frontend-delen i dev- og prod-milj\u00f8erne.<\/p>\n<p>Jeg viser sk\u00e6rmbilleder af indstillingerne.<\/p>\n<h3>Backend<\/h3>\n<p>\n<img decoding=\"async\" alt=\"CI\/CD i Github Actions til et projekt p\u00e5 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 i Github Actions til et projekt p\u00e5 Flask+Angular\" src=\"\/wp-content\/uploads\/2020\/06\/35d4d8ef3489608db1e173b7f214ca03.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<strong>Vigtigt pr\u00e6ciseret<\/strong>. I frontend-containeren vil alle URL'er, undtagen dem der starter med <code>\/api\/<\/code> \u2014 blive proxieret til backend-containeren (derfor <strong>skal alle h\u00e5ndterere i backend-containeren begynde med <\/strong><strong><code>\/api\/<\/code><\/strong>).<\/p>\n<h2>Resum\u00e9<\/h2>\n<p>\nNu skal vores websted v\u00e6re tilg\u00e6ngeligt p\u00e5 adresserne helloworld.ru og dev.helloworld.ru (henholdsvis prod- og dev-milj\u00f8).<\/p>\n<p>Vi har l\u00e6rt, hvordan man forbereder en simpel applikation p\u00e5 Flask og Angular, og hvordan man indstiller en pipeline i Github Actions til at udrulle den p\u00e5 en server, der styres af Plesk.<\/p>\n<p>Jeg gentager links til repositoryerne med koden: <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 \/>Kilde: <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\/da\/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=\"da_DK\" \/>\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\/da\/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 i Github Actions for et projekt p\u00e5 Flask+Angular | ProHoster","description":"I denne artikel vil jeg dele min erfaring.","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/ci-cd-v-github-actions-dlya-proekta-na-flask-angular","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"da_DK","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\/da\/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\/da\/wp-json\/wp\/v2\/posts\/85646","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=85646"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/85646\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/85647"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=85646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=85646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=85646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}