{"id":95322,"date":"2020-09-28T07:42:39","date_gmt":"2020-09-28T05:42:39","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt"},"modified":"2020-09-28T07:42:39","modified_gmt":"2020-09-28T05:42:39","slug":"kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt","status":"publish","type":"post","link":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt","title":{"rendered":"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring\" src=\"\/wp-content\/uploads\/2020\/09\/23b2db49941fa5bd5000358c8ffb991b.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSteeds vaker ontvangen we verzoeken van klanten zoals: \"We willen iets als Amazon RDS, maar goedkoper\"; \"We willen iets als RDS, maar overal, in elke infrastructuur\". Om een soortgelijk managed-oplossing op Kubernetes te realiseren, hebben we gekeken naar de huidige staat van de meest populaire operators voor PostgreSQL (Stolon, operators van Crunchy Data en Zalando) en hebben we onze keuze gemaakt.<\/p>\n<p>Dit artikel bevat onze ervaringen, zowel vanuit theoretisch perspectief (een overzicht van oplossingen) als vanuit praktisch perspectief (wat is gekozen en wat is daarvan het resultaat). Maar laten we eerst bepalen welke eisen er \u00fcberhaupt worden gesteld aan een mogelijke vervanging van RDS\u2026<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Wat is RDS?<\/h2>\n<p>\nWanneer mensen het over RDS hebben, bedoelen ze uit onze ervaring meestal een managed database service die:<\/p>\n<ol>\n<li>gemakkelijk te configureren is;<\/li>\n<li>de mogelijkheid heeft om met snapshots te werken en hieruit te herstellen (bij voorkeur met ondersteuning voor <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Point-in-time_recovery\">PITR<\/a><\/noindex>);<\/li>\n<li>de mogelijkheid biedt om master-slave topologie\u00ebn te cre\u00ebren;<\/li>\n<li>een rijke lijst van extensies biedt;<\/li>\n<li>audit en gebruikers-\/toegangsbewaking verzorgt.<\/li>\n<\/ol>\n<p>\nIn het algemeen kunnen de benaderingen voor de uit te voeren taak behoorlijk verschillend zijn, maar de weg met een soortgelijke Ansible spreekt ons niet aan. (Collega's van 2GIS kwamen ook tot een vergelijkbare conclusie tijdens <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/509926\/\">hun poging<\/a><\/noindex> om \"een hulpmiddel voor het snel implementeren van een failover-cluster op basis van Postgres\" te cre\u00ebren.)<\/p>\n<p>Operators zijn de algemeen aanvaarde benadering voor het oplossen van dergelijke taken in het Kubernetes-ecosysteem. Meer over hen in relatie tot databases die binnen Kubernetes draaien, heeft technische directeur \"Flanta\" al verteld, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/distol\/\" class=\"user_link\">distol<\/a><\/noindex>, in <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">in een van zijn presentaties.<\/a><\/noindex>.<\/p>\n<p><i><strong>NB<\/strong><\/i><i>: Voor het snel cre\u00ebren van eenvoudige operators raden we aan om aandacht te besteden aan onze Open Source-tool <\/i><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/shell-operator\"><i>shell-operator<\/i><\/a><\/noindex><i>. Hiermee kun je dit doen zonder kennis van Go, op manieren die meer vertrouwd zijn voor sysadmins: in Bash, Python, enz.<\/i><\/p>\n<p>Voor PostgreSQL zijn er verschillende populaire K8s-operators:<\/p>\n<ul>\n<li>Stolon;<\/li>\n<li>Crunchy Data PostgreSQL Operator;<\/li>\n<li>Zalando Postgres Operator.<\/li>\n<\/ul>\n<p>\nLaten we ze eens nader bekijken.<\/p>\n<h2>De keuze van de operator<\/h2>\n<p>\nNaast de belangrijke mogelijkheden die hierboven zijn genoemd, hebben we - als infrastructuur engineers in Kubernetes - ook de volgende verwachtingen van operators gehad:<\/p>\n<ul>\n<li> deployment vanuit Git en met <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/extend-kubernetes\/operator\/#deploying-operators\">Custom Resources;<\/a><\/noindex>;<\/li>\n<li> ondersteuning voor pod anti-affinity;<\/li>\n<li> installatie van node affinity of node selector;<\/li>\n<li> installatie van tolerations;<\/li>\n<li> aanwezigheid van tuningmogelijkheden;<\/li>\n<li> begrijpelijke technologie\u00ebn en zelfs commando's.<\/li>\n<\/ul>\n<p>\nZonder in detail op elk van de punten in te gaan (vraag in de opmerkingen als er na het lezen van het artikel nog vragen zijn), wil ik in het algemeen opmerken dat deze parameters nodig zijn voor een fijnere beschrijving van de specialisatie van de clusterknopen, zodat we ze kunnen aanvragen voor specifieke applicaties. Zo kunnen we een optimale balans bereiken tussen prestaties en kosten.<\/p>\n<p>Nu, naar de PostgreSQL-operators zelf.<\/p>\n<h3>1. Stolon<\/h3>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/sorintlab\/stolon\">Stolon<\/a><\/noindex> van het Italiaanse bedrijf Sorint.lab in <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">het al genoemde rapport<\/a><\/noindex> werd beschouwd als een soort standaard onder operators voor databases. Dit is een vrij oud project: de eerste publieke release vond plaats in november 2015(!), en de GitHub-repo kan bogen op bijna 3000 sterren en 40+ bijdragers.<\/p>\n<p>En inderdaad, Stolon is een uitstekend voorbeeld van doordachte architectuur:<\/p>\n<p><img decoding=\"async\" alt=\"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring\" src=\"\/wp-content\/uploads\/2020\/09\/a34625c130bfd29c5645cf2417042de2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDe details van deze operator zijn te vinden in het rapport of <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/sorintlab\/stolon\/blob\/master\/doc\/architecture.md\">projectdocumentatie<\/a><\/noindex>. In het algemeen is het voldoende om te zeggen dat hij alles kan wat beschreven is: failover, proxy voor transparante toegang voor klanten, back-ups... Bovendien bieden de proxies toegang via \u00e9\u00e9n service-eindpunt - in tegenstelling tot de twee andere oplossingen die verder worden besproken (die hebben twee services voor toegang tot de database).<\/p>\n<p>Echter, Stolon <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/sorintlab\/stolon\/issues\/463#issuecomment-379666733\">heeft geen Custom Resources<\/a><\/noindex>, waardoor het niet mogelijk is om het zo te implementeren dat je eenvoudig en snel - \"zoals warme broodjes\" - database-exemplaren in Kubernetes kunt aanmaken. Beheer gebeurt via de tool <code>stolonctl<\/code>, implementatie - via een Helm-chart, en gebruikersdefinities worden vastgelegd in ConfigMap.<\/p>\n<p>Aan de ene kant lijkt de operator niet echt een operator te zijn (want hij gebruikt geen CRD). Maar aan de andere kant is het een flexibel systeem dat je in staat stelt om bronnen in K8s zo in te stellen zoals jij dat wilt.<\/p>\n<p>Samenvattend leek het voor ons niet optimaal om voor elke database een aparte chart aan te maken. Daarom gingen we op zoek naar alternatieven.<\/p>\n<h3>2. Crunchy Data PostgreSQL Operator<\/h3>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CrunchyData\/postgres-operator\">De operator van Crunchy Data<\/a><\/noindex>, een jonge Amerikaanse start-up, leek een logische alternatieve keuze. Zijn publieke geschiedenis begint met de eerste release in maart 2017, sindsdien heeft de GitHub-repo iets minder dan 1300 sterren en 50+ bijdragers gekregen. De laatste release van september is getest voor gebruik met Kubernetes 1.15\u20141.18, OpenShift 3.11+ en 4.4+, GKE en VMware Enterprise PKS 1.3+.<\/p>\n<p>De architectuur van Crunchy Data PostgreSQL Operator voldoet ook aan de opgegeven eisen:<\/p>\n<p><img decoding=\"async\" alt=\"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring\" src=\"\/wp-content\/uploads\/2020\/09\/343cd0494abae42fcf5b47c5bb4eb310.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nBeheer gebeurt via de tool <code>pgo<\/code>, maar het genereert op zijn beurt Custom Resources voor Kubernetes. Daarom was er goed nieuws voor ons als potenti\u00eble gebruikers van de operator:<\/p>\n<ul>\n<li> er is beheer via CRD;<\/li>\n<li> gemakkelijk gebruikersbeheer (ook via CRD);<\/li>\n<li> integratie met andere componenten <noindex><a rel=\"nofollow\" href=\"https:\/\/access.crunchydata.com\/documentation\/crunchy-postgres-containers\/4.3.1\/\">Crunchy Data Container Suite<\/a><\/noindex> \u2014 een gespecialiseerde verzameling containerafbeeldingen voor PostgreSQL en tools voor het werken ermee (inclusief pgBackRest, pgAudit, extensies uit contrib, enz.).<\/li>\n<\/ul>\n<p>\nEchter, de pogingen om de operator van Crunchy Data te gebruiken hebben enkele problemen aan het licht gebracht:<\/p>\n<ul>\n<li>Er was geen mogelijkheid voor tolerances \u2014 alleen nodeSelector was beschikbaar.<\/li>\n<li>De gemaakte pod's waren deel van een Deployment, hoewel we een stateful-toepassing deponeerden. In tegenstelling tot StatefulSets kunnen Deployments geen schijven aanmaken.<\/li>\n<\/ul>\n<p>\nDit laatste gebrek leidt tot amusante momenten: op de testomgeving lukte het om 3 replica's met \u00e9\u00e9n schijf te draaien <i>lokale opslag<\/i>, waardoor de operator meldde dat 3 replica's actief waren (terwijl dat niet het geval was).<\/p>\n<p>Een andere eigenschap van deze operator is de kant-en-klare integratie met verschillende ondersteunende systemen. Het is bijvoorbeeld eenvoudig om pgAdmin en pgBounce te installeren, en in <noindex><a rel=\"nofollow\" href=\"https:\/\/access.crunchydata.com\/documentation\/postgres-operator\/4.4.0\/installation\/other\/ansible\/installing-metrics\/\">de documentatie<\/a><\/noindex> overweegde voorgeconfigureerde Grafana en Prometheus. In de recente <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CrunchyData\/postgres-operator\/releases\/tag\/v4.5.0-beta.1\">release 4.5.0-beta1<\/a><\/noindex> wordt de verbeterde integratie met het project <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CrunchyData\/pgmonitor\">pgMonitor<\/a><\/noindex>, waardoor de operator een visuele weergave van de PgSQL-metrics 'out of the box' biedt.<\/p>\n<p>Desondanks heeft de vreemde keuze van gegenereerde Kubernetes-resources ons gedwongen een andere oplossing te zoeken.<\/p>\n<h3>3. Zalando Postgres Operator<\/h3>\n<p>\nWe zijn al lang bekend met de producten van Zalando: we hebben ervaring met Zalenium en natuurlijk hebben we geprobeerd <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/patroni\">Patroni<\/a><\/noindex> \u2014 hun populaire HA-oplossing voor PostgreSQL. Over de aanpak van het bedrijf bij het cre\u00ebren van <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\">Postgres Operator<\/a><\/noindex> vertelde een van de auteurs \u2014 Alexey Klyukin \u2014 tijdens de uitzending van <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/479438\/\">Postgres-dinsdag nummer 5<\/a><\/noindex>, en we waren er meteen enthousiast over.<\/p>\n<p>Dit is de meest recente oplossing in het artikel: de eerste release vond plaats in augustus 2018. Ondanks het geringe aantal formele releases is het project steeds verder gevorderd, en het heeft inmiddels het Crunchy Data-oplossing overtroffen met meer dan 1300 sterren op GitHub en het hoogste aantal bijdragers (meer dan 70).<\/p>\n<p>\u2018Onder de motorkap\u2019 van deze operator worden bewezen oplossingen gebruikt:<\/p>\n<ul>\n<li> Patroni en <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/spilo\">Spilo<\/a><\/noindex> voor beheer,<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/wal-e\/wal-e\">WAL-E<\/a><\/noindex> \u2014 voor back-ups,<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgbouncer\/pgbouncer\">PgBouncer<\/a><\/noindex> \u2014 als verbindingenpool.<\/li>\n<\/ul>\n<p>\nZo is de architectuur van de operator van Zalando gepresenteerd:<\/p>\n<p><img decoding=\"async\" alt=\"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring\" src=\"\/wp-content\/uploads\/2020\/09\/c66f10c1a818592ecaacd2db4ce5ace5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDe operator wordt volledig beheerd via Custom Resources, cre\u00ebert automatisch een StatefulSet uit containers die vervolgens kunnen worden gepersonaliseerd door verschillende sidecars aan de pod toe te voegen. Dit is een aanzienlijk voordeel vergeleken met de operator van Crunchy Data.<\/p>\n<p>Aangezien we de oplossing van Zalando hebben gekozen uit de 3 overwogen opties, volgt hieronder een beschrijving van de mogelijkheden, samen met de praktische toepassing.<\/p>\n<h2>Praktijk met Postgres Operator van Zalando<\/h2>\n<p>\nDe deployment van de operator gebeurt heel eenvoudig: het enige wat je hoeft te doen is de actuele release van GitHub te downloaden en de YAML-bestanden uit de directory toe te passen. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/tree\/master\/manifests\">manifests<\/a><\/noindex>. Als alternatief kun je ook gebruikmaken van <noindex><a rel=\"nofollow\" href=\"https:\/\/operatorhub.io\/operator\/postgres-operator\">OperatorHub<\/a><\/noindex>.<\/p>\n<p>Na de installatie is het belangrijk om de <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/docs\/reference\/operator_parameters.md#aws-or-gcp-interaction\">opslag voor logs en back-ups<\/a><\/noindex>. Dit gebeurt via ConfigMap <code>postgres-operator<\/code> in de namespace waar je de operator hebt ge\u00efnstalleerd. Zodra de opslag is ingesteld, kun je de eerste PostgreSQL-cluster implementeren.<\/p>\n<p>Bijvoorbeeld, de standaard deployment ziet eruit als volgt:<\/p>\n<pre><code class=\"plaintext\">apiVersion: acid.zalan.do\/v1\nkind: postgresql\nmetadata:\n name: staging-db\nspec:\n numberOfInstances: 3\n patroni:\n   synchronous_mode: true\n postgresql:\n   version: \"12\"\n resources:\n   limits:\n     cpu: 100m\n     memory: 1Gi\n   requests:\n     cpu: 100m\n     memory: 1Gi\n sidecars:\n - env:\n   - name: DATA_SOURCE_URI\n     value: 127.0.0.1:5432\n   - name: DATA_SOURCE_PASS\n     valueFrom:\n       secretKeyRef:\n         key: password\n         name: postgres.staging-db.credentials\n   - name: DATA_SOURCE_USER\n     value: postgres\n   image: wrouesnel\/postgres_exporter\n   name: prometheus-exporter\n   resources:\n     limits:\n       cpu: 500m\n       memory: 100Mi\n     requests:\n       cpu: 100m\n       memory: 100Mi\n teamId: staging\n volume:\n   size: 2Gi\n<\/code><\/pre>\n<p>\nDit manifest implementeert een cluster van 3 exemplaren met een sidecar in de vorm van <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/wrouesnel\/postgres_exporter\">postgres_exporter<\/a><\/noindex>, waarvan we de statistieken van de applicatie ophalen. Zoals je ziet, is het heel eenvoudig, en als je wilt, kun je letterlijk een onbeperkt aantal clusters maken.<\/p>\n<p>Het is ook de moeite waard om te wijzen op <b>de webinterface voor administratie<\/b> \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/docs\/operator-ui.md\">postgres-operator-ui<\/a><\/noindex>. Deze wordt meegeleverd met de operator en stelt je in staat om clusters te cre\u00ebren en te verwijderen, evenals te werken met de back-ups die de operator maakt.<\/p>\n<p><img decoding=\"async\" alt=\"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring\" src=\"\/wp-content\/uploads\/2020\/09\/2e9ae033a9817d101c843ce061aafdb5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Lijst van PostgreSQL-clusters<\/i><\/p>\n<p><img decoding=\"async\" alt=\"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring\" src=\"\/wp-content\/uploads\/2020\/09\/74a65f7f301da14a0cb7bea6d5ef84ec.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Beheer van back-ups<\/i><\/p>\n<p>Een andere interessante functie is de ondersteuning van <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/docs\/user.md#teams-api-roles\">Teams API<\/a><\/noindex>. Dit mechanisme cre\u00ebert automatisch <b>rollen in PostgreSQL<\/b>, op basis van de ontvangen lijst van gebruikersnamen. Vervolgens kan de API een lijst van gebruikers teruggeven waarvoor automatisch rollen worden aangemaakt.<\/p>\n<h3>Problemen en oplossingen<\/h3>\n<p>\nEchter, het gebruik van de operator heeft al snel enkele significante nadelen aan het licht gebracht:<\/p>\n<ol>\n<li> gebrek aan ondersteuning voor nodeSelector;<\/li>\n<li> ongemak om back-ups uit te schakelen;<\/li>\n<li> bij het gebruik van de functie voor het aanmaken van databases verschijnen er geen standaardrechten;<\/li>\n<li> af en toe ontbreekt documentatie of is deze verouderd.<\/li>\n<\/ol>\n<p>\nGelukkig kunnen veel van deze problemen worden opgelost. Laten we beginnen met het einde \u2014 problemen met <strong>de documentatie<\/strong>. <\/p>\n<p>Waarschijnlijk krijg je te maken met het feit dat het niet altijd duidelijk is hoe je een back-up opzet en hoe je de back-up bucket aan de Operator UI koppelt. Dit wordt slechts terloops in de documentatie genoemd, maar een echte beschrijving is te vinden in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/481\">PR<\/a><\/noindex>:<\/p>\n<ol>\n<li>het moet een geheim worden gemaakt;<\/li>\n<li>het aan de operator doorgeven als parameter <code>pod_environment_secret_name<\/code> in de CRD met de instellingen van de operator of in de ConfigMap (afhankelijk van hoe je hebt besloten de operator te installeren).<\/li>\n<\/ol>\n<p>\nEchter, zoals het blijkt, is het op dit moment onmogelijk. Daarom hebben we <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/postgres-operator\">onze versie van de operator<\/a><\/noindex> met enkele extra externe toevoegingen. Meer hierover \u2014 zie hieronder.<\/p>\n<p>Als je parameters voor de back-up aan de operator doorgeeft, zoals <code>wal_s3_bucket<\/code> en toegangssleutels voor AWS S3, dan <strong>zal het alles back-uppen<\/strong>: niet alleen databases in productie, maar ook staging. Dit was niet acceptabel voor ons.<\/p>\n<p>In de beschrijving van de parameters voor Spilo, dat de basis Docker-wrapping voor PgSQL is bij gebruik van de operator, bleek dat je de parameter <code>WAL_S3_BUCKET<\/code> leeg kunt doorgeven, waarmee de back-ups zijn uitgeschakeld. Bovendien, tot grote vreugde, is er ook <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/908\">een kant-en-klare PR<\/a><\/noindex>, die we onmiddellijk hebben geaccepteerd in onze fork. Nu is het voldoende om eenvoudig <code>enableWALArchiving: false<\/code> toe te voegen aan de resource van de PostgreSQL-cluster.<\/p>\n<p>Ja, het was mogelijk om het anders te doen door 2 operators te draaien: \u00e9\u00e9n voor staging (zonder back-ups), en de tweede voor productie. Maar zo konden we met \u00e9\u00e9n operator toe.<\/p>\n<p>Ok\u00e9, we hebben geleerd toegang tot databases voor S3 door te geven en de back-ups begonnen in de opslag terecht te komen. Hoe laten we de back-up pagina's in de Operator UI werken?<\/p>\n<p><img decoding=\"async\" alt=\"Korte overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring\" src=\"\/wp-content\/uploads\/2020\/09\/46169b53707b0bbb686798c655022f40.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIn de Operator UI moeten 3 variabelen worden toegevoegd:<\/p>\n<ul>\n<li> <code>SPILO_S3_BACKUP_BUCKET<\/code><\/li>\n<li> <code>AWS_ACCESS_KEY_ID<\/code><\/li>\n<li> <code>AWS_SECRET_ACCESS_KEY<\/code><\/li>\n<\/ul>\n<p>\nNa dit alles zal het beheer van back-ups beschikbaar zijn, wat in ons geval het werken met staging vereenvoudigt, waardoor we snapshots van productie zonder extra scripts kunnen leveren.<\/p>\n<p>Als nog een pluspunt werd het werken met de Teams API en de uitgebreide mogelijkheden voor het aanmaken van databases en rollen met behulp van de operator genoemd. Echter, de gemaakte <strong>rollen hadden geen standaardrechten<\/strong>. Dienovereenkomstig kon een gebruiker met leesrechten nieuwe tabellen niet lezen.<\/p>\n<p>Waarom is dat zo? Ondanks het feit dat in de code <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/pkg\/cluster\/database.go#L42\">bestaande<\/a><\/noindex> de nodige <code>GRANT<\/code>, ze worden lang niet altijd toegepast. Er zijn 2 methoden: <code>syncPreparedDatabases<\/code> en <code>syncDatabases<\/code>wordt een tabel met wijzigingen en instructies voor de overgang naar de nieuwe configuratie gegeven. Voor meer informatie, zie <code>syncPreparedDatabases<\/code> \u2014 ondanks dat in de sectie <code>preparedDatabases<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/manifests\/complete-postgres-manifest.yaml#L26\">bestaande<\/a><\/noindex> er een voorwaarde is <code>defaultRoles<\/code> en <code>defaultUsers<\/code> voor het aanmaken van rollen, \u2014 de standaardrechten worden niet toegepast. We zijn bezig met het voorbereiden van een patch zodat deze rechten automatisch worden toegepast.<\/p>\n<p>En het laatste punt in de relevante verbeteringen voor ons is \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/975\">patch<\/a><\/noindex>, die Node Affinity toevoegt aan de aangemaakte StatefulSet. Onze klanten geven er vaak de voorkeur aan om kosten te verlagen door spot-instanties te gebruiken, en het is duidelijk niet verstandig om databaseservices daarop te plaatsen. Dit probleem zou ook met tolerations opgelost kunnen worden, maar het hebben van Node Affinity biedt veel meer zekerheid.<\/p>\n<h3>Wat is het resultaat?<\/h3>\n<p>\nAls resultaat van de oplossing van de genoemde problemen hebben we de Postgres Operator van Zalando geforkt naar <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/postgres-operator\">onze eigen repository<\/a><\/noindex>, waar hij wordt opgebouwd met deze nuttige patches. En voor extra gemak hebben we ook een <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/r\/flant\/postgres-operator\">Docker-image<\/a><\/noindex>.<\/p>\n<p>Lijst van PR's die in de fork zijn geaccepteerd:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/1066\">opbouw van een veilige, lichte Docker-image voor de operator<\/a><\/noindex>;<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/908\">deactivering van backups<\/a><\/noindex>;<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/1121\">bijwerken van versies van resources voor de actuele versies van k8s<\/a><\/noindex>;<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/975\">implementatie van Node Affinity<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nHet zou geweldig zijn als de community deze PR's steunt, zodat ze in de upstream komen met de volgende versie van de operator (1.6).<\/p>\n<h2>Bonus! Succesverhaal over de migratie van productie<\/h2>\n<p>\nAls je Patroni gebruikt, kun je live productie met minimale downtime naar de operator migreren.<\/p>\n<p>Spilo maakt het mogelijk om standby-clusters via S3-opslag te cre\u00ebren met <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/wal-e\/wal-e\">Wal-E<\/a><\/noindex>, wanneer de binaire log van PgSQL eerst in S3 wordt opgeslagen en vervolgens door een replica wordt opgehaald. Maar wat te doen als je <i>niet<\/i> Wal-E in de oude infrastructuur gebruikt? De oplossing voor dit probleem is al <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/true_engineering\/blog\/437318\/\">aangeboden<\/a><\/noindex> op Habr\u00e9.<\/p>\n<p>Logische replicatie PostgreSQL komt te hulp. Laten we echter niet ingaan op de details van hoe publicaties en abonnementen te maken, omdat\u2026 ons plan is mislukt.<\/p>\n<p>Het probleem is dat er in de database verschillende zwaar belaste tabellen waren met miljoenen rijen, die bovendien voortdurend werden toegevoegd en verwijderd. <noindex><a rel=\"nofollow\" href=\"https:\/\/postgrespro.ru\/docs\/postgrespro\/12\/sql-createsubscription\">Een eenvoudige abonnement<\/a><\/noindex> met <code>copy_data<\/code>, wanneer een nieuwe replica alles kopieert van de master, kon simpelweg niet bijhouden met de master. Het kopi\u00ebren van de inhoud werkte een week, maar heeft de master nooit ingehaald. Uiteindelijk hielp een oplossing voor het probleem van <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/avitotech\/recovery-use-cases-for-logical-replication-in-postgresql-10-a1e6bab03072\">artikel<\/a><\/noindex> collega's van Avito: je kunt de gegevens verplaatsen met <code>pg_dump<\/code>. Ik zal onze (licht aangepaste) versie van dit algoritme beschrijven.<\/p>\n<p>Het idee is dat je een inactieve abonnement kunt maken, gekoppeld aan een specifieke replicatieslot, en vervolgens het transactienummer kunt aanpassen. Er waren replicas beschikbaar voor de productieomgeving. Dit is belangrijk omdat de replica zal helpen een consistente dump te cre\u00ebren en doorgaan met het ontvangen van wijzigingen van de master.<\/p>\n<p>In de volgende opdrachten, die het migratieproces beschrijven, zullen de volgende aanduidingen voor hosts worden gebruikt:<\/p>\n<ol>\n<li><i>master<\/i> \u2014 de oorspronkelijke server;<\/li>\n<li><i>replica1<\/i> \u2014 streaming replica op de oude productie;<\/li>\n<li><i>replica2<\/i> \u2014 nieuwe logische replica.<\/li>\n<\/ol>\n<p><\/p>\n<h3>Migratieplan<\/h3>\n<p>\n1. We zullen een abonnement maken op de master voor alle tabellen in het schema <code>public<\/code> van de database <code>dbname<\/code>:<\/p>\n<pre><code class=\"bash\">psql -h master -d dbname -c \"CREATE PUBLICATION dbname FOR ALL TABLES;\"\n<\/code><\/pre>\n<p>\n2. We cre\u00ebren een replicatieslot op de master:<\/p>\n<pre><code class=\"bash\">psql -h master -c \"select pg_create_logical_replication_slot('repl', 'pgoutput');\"\n<\/code><\/pre>\n<p>\n3. We stoppen de replicatie op de oude replica:<\/p>\n<pre><code class=\"bash\">psql -h replica1 -c \"select pg_wal_replay_pause();\"\n<\/code><\/pre>\n<p>\n4. We krijgen het transactienummer van de master:<\/p>\n<pre><code class=\"bash\">psql -h master -c \"select replay_lsn from pg_stat_replication where client_addr = 'replica1';\"\n<\/code><\/pre>\n<p>\n5. We maken een dump van de oude replica. We zullen dit in meerdere streams doen, wat het proces zal versnellen:<\/p>\n<pre><code class=\"bash\">pg_dump -h replica1 --no-publications --no-subscriptions -O -C -F d -j 8 -f dump\/ dbname\n<\/code><\/pre>\n<p>\n6. We laden de dump op de nieuwe server:<\/p>\n<pre><code class=\"bash\">pg_restore -h replica2 -F d -j 8 -d dbname dump\/\n<\/code><\/pre>\n<p>\n7. Na het laden van de dump kan de replicatie op de streaming replica worden gestart:<\/p>\n<pre><code class=\"bash\">psql -h replica1 -c \"select pg_wal_replay_resume();\"\n<\/code><\/pre>\n<p>\n7. We cre\u00ebren een abonnement op de nieuwe logische replica:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -c \"create subscription oldprod connection 'host=replica1 port=5432 user=postgres password=secret dbname=dbname' publication dbname with (enabled = false, create_slot = false, copy_data = false, slot_name='repl');\"\n<\/code><\/pre>\n<p>\n8. We krijgen <code>oid<\/code> van het abonnement:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c \"select oid, * from pg_subscription;\"\n<\/code><\/pre>\n<p>\n9. Stel dat we hebben gekregen <code>oid=1000<\/code>. We passen het transactienummer toe op het abonnement:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c \"select pg_replication_origin_advance('pg_1000', 'AA\/AAAAAAAA');\"\n<\/code><\/pre>\n<p>\n10. We starten de replicatie:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c \"alter subscription oldprod enable;\"\n<\/code><\/pre>\n<p>\n11. We controleren de status van het abonnement, de replicatie zou moeten werken:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c \"select * from pg_replication_origin_status;\"\npsql -h master -d dbname -c \"select slot_name, restart_lsn, confirmed_flush_lsn from pg_replication_slots;\"\n<\/code><\/pre>\n<p>\n12. Nadat de replicatie is gestart en de databases zijn gesynchroniseerd, kan de overstap worden gemaakt.<\/p>\n<p>13. Na het uitschakelen van de replicatie moeten de sequences worden aangepast. Dit is goed beschreven <noindex><a rel=\"nofollow\" href=\"https:\/\/wiki.postgresql.org\/wiki\/Fixing_Sequences\">in een artikel op wiki.postgresql.org<\/a><\/noindex>.<\/p>\n<p>Dankzij dit plan is de overstap met minimale vertragingen verlopen.<\/p>\n<h2>Conclusie<\/h2>\n<p>\nKubernetes-operators maken verschillende acties eenvoudiger door ze te reduceren tot het cre\u00ebren van K8s-resources. Echter, nadat mooie automatisering is bereikt, is het belangrijk te onthouden dat dit ook onverwachte nuances met zich mee kan brengen, dus kies wijs bij de selectie van operators.<\/p>\n<p>Na het bekijken van de drie meest populaire Kubernetes-operators voor PostgreSQL, hebben we gekozen voor het project van Zalando. We zijn op bepaalde moeilijkheden gestuit, maar het resultaat was echt bevredigend, dus we zijn van plan deze ervaring ook op andere PgSQL-installaties uit te breiden. Als je ervaring hebt met vergelijkbare oplossingen, zouden we het leuk vinden om de details in de reacties te zien!<\/p>\n<h2>P.S.<\/h2>\n<p>\nLees ook op onze blog:<\/p>\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">Databases en Kubernetes (overzicht en video van de presentatie)<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/479438\/\">Postgres-dinsdag nr. 5: PostgreSQL en Kubernetes. CI\/CD. Automatisering van testen.<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/480722\/\">Een verhaal over de Redis-operator in K8s en een mini-overzicht van tools voor gegevensanalyse van deze DB.<\/a><\/noindex>\u00bb.<\/li>\n<\/ul>\n<p>Bron: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/520616\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412\u0441\u0451 \u0447\u0430\u0449\u0435 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u044e\u0442 \u0442\u0430\u043a\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b: \u00ab\u0425\u043e\u0442\u0438\u043c \u043a\u0430\u043a Amazon RDS, \u043d\u043e \u0434\u0435\u0448\u0435\u0432\u043b\u0435\u00bb; \u00ab\u0425\u043e\u0442\u0438\u043c \u043a\u0430\u043a RDS, \u043d\u043e \u0432\u0435\u0437\u0434\u0435, \u0432 \u043b\u044e\u0431\u043e\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435\u00bb. \u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435 managed-\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0430 Kubernetes, \u043c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0434\u043b\u044f PostgreSQL (Stolon, \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u043e\u0442 Crunchy Data \u0438 Zalando) \u0438 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0432\u044b\u0431\u043e\u0440. \u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043d\u0430\u043c\u0438 \u043e\u043f\u044b\u0442 \u0438 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":95323,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-95322","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\u0441\u0451 \u0447\u0430\u0449\u0435 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432.\" \/>\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\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt\" \/>\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\udd47\u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u043e\u0431\u0437\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432 PostgreSQL \u0434\u043b\u044f Kubernetes, \u043d\u0430\u0448 \u0432\u044b\u0431\u043e\u0440 \u0438 \u043e\u043f\u044b\u0442 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412\u0441\u0451 \u0447\u0430\u0449\u0435 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt\" \/>\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-09-28T05:42:39+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-09-28T05:42:39+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\udd47Een kort overzicht van PostgreSQL-operators voor Kubernetes, onze keuze en ervaring | ProHoster","description":"Steeds vaker van klanten.","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt","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\udd47\u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u043e\u0431\u0437\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432 PostgreSQL \u0434\u043b\u044f Kubernetes, \u043d\u0430\u0448 \u0432\u044b\u0431\u043e\u0440 \u0438 \u043e\u043f\u044b\u0442 | ProHoster","og:description":"\u0412\u0441\u0451 \u0447\u0430\u0449\u0435 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432.","og:url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt","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-09-28T05:42:39+00:00","article:modified_time":"2020-09-28T05:42:39+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"95322","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 11:08:22","updated":"2022-09-30 21:12:08","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\/95322","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=95322"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/95322\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media\/95323"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=95322"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=95322"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=95322"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}