{"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\/da\/blog\/administrirovanie\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt","title":{"rendered":"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring\" src=\"\/wp-content\/uploads\/2020\/09\/23b2db49941fa5bd5000358c8ffb991b.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nKunderne anmoder i stigende grad om l\u00f8sninger som: \u201eVi vil have noget som Amazon RDS, men billigere\u201c; \u201eVi vil have noget som RDS, men til enhver infrastruktur\u201c. For at implementere en s\u00e5dan managed-l\u00f8sning p\u00e5 Kubernetes har vi kigget p\u00e5 de nuv\u00e6rende tilstande af de mest popul\u00e6re operat\u00f8rer for PostgreSQL (Stolon, operat\u00f8rer fra Crunchy Data og Zalando) og har truffet vores valg.<\/p>\n<p>Denne artikel er baseret p\u00e5 vores erfaring, b\u00e5de fra et teoretisk perspektiv (oversigt over l\u00f8sninger) og fra en praktisk side (hvad vi valgte og hvad der kom ud af det). Men f\u00f8rst, lad os fastsl\u00e5, hvilke krav der generelt stilles til en potentiel erstatning for RDS \u2026<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Hvad er RDS?<\/h2>\n<p>\nN\u00e5r folk taler om RDS, henviser de if\u00f8lge vores erfaring til en managed database-tjeneste, der:<\/p>\n<ol>\n<li>er nem at konfigurere;<\/li>\n<li>har mulighed for at arbejde med snapshots og gendanne fra dem (helst med st\u00f8tte til <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Point-in-time_recovery\">PITR<\/a><\/noindex>);<\/li>\n<li>mulighed for at oprette master-slave topologier;<\/li>\n<li>har en bred liste af udvidelser;<\/li>\n<li>giver revisions- og brugeradgangsstyring.<\/li>\n<\/ol>\n<p>\nGenerelt set kan tilgange til implementeringen af den stillede opgave v\u00e6re meget forskellige, men metoden med betinget Ansible er ikke n\u00e6r os. (En lignende konklusion n\u00e5ede ogs\u00e5 kollegerne fra 2GIS efter <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/509926\/\">deres fors\u00f8g<\/a><\/noindex> p\u00e5 at skabe et \u201ev\u00e6rkt\u00f8j til hurtig implementering af en fejlbeh\u00e6ftet Postgres-klynge\u201c.)<\/p>\n<p>Operat\u00f8rer er den almindeligt accepterede tilgang til at l\u00f8se s\u00e5danne opgaver i Kubernetes-\u00f8kosystemet. Jeg har tidligere omtalt deres anvendelse til databaser, der k\u00f8rer inden i Kubernetes, i en af mine pr\u00e6sentationer, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/distol\/\" class=\"user_link\">distol<\/a><\/noindex>, i <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">blandt andet<\/a><\/noindex>.<\/p>\n<p><i><strong>NB<\/strong><\/i><i>: For at skabe enkle operat\u00f8rer hurtigt anbefaler vi at se p\u00e5 vores Open Source-v\u00e6rkt\u00f8j <\/i><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/shell-operator\"><i>shell-operator<\/i><\/a><\/noindex><i>. Ved at bruge det kan man g\u00f8re dette uden kendskab til Go, med metoder mere velkendte for systemadministratorer: Bash, Python osv.<\/i><\/p>\n<p>Der findes flere popul\u00e6re K8s-operat\u00f8rer til PostgreSQL:<\/p>\n<ul>\n<li>Stolon;<\/li>\n<li>Crunchy Data PostgreSQL Operator;<\/li>\n<li>Zalando Postgres Operator.<\/li>\n<\/ul>\n<p>\nLad os se n\u00e6rmere p\u00e5 dem.<\/p>\n<h2>Valg af operat\u00f8r<\/h2>\n<p>\nUdover de vigtige funktioner, der allerede er n\u00e6vnt ovenfor, forventede vi \u2014 som infrastrukturdriftsteknikere i Kubernetes \u2014 ogs\u00e5 f\u00f8lgende fra operat\u00f8rerne:<\/p>\n<ul>\n<li> deployering fra Git og med <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/extend-kubernetes\/operator\/#deploying-operators\">Custom Resources<\/a><\/noindex>;<\/li>\n<li> support til pod anti-affinity;<\/li>\n<li> installation af node affinity eller node selector;<\/li>\n<li> installation af tolerances;<\/li>\n<li> muligheder for tuning;<\/li>\n<li> forst\u00e5elige teknologier og endda kommandoer.<\/li>\n<\/ul>\n<p>\nUden at g\u00e5 i detaljer om hvert punkt (sp\u00f8rg i kommentarerne, hvis der er sp\u00f8rgsm\u00e5l tilbage efter at have l\u00e6st hele artiklen), vil jeg generelt bem\u00e6rke, at disse parametre er n\u00f8dvendige for en mere pr\u00e6cis beskrivelse af specialiseringen af klyngeknuder, s\u00e5 man kan bestille dem til specifikke applikationer. P\u00e5 denne m\u00e5de kan vi opn\u00e5 en optimal balance mellem ydeevne og omkostninger.<\/p>\n<p>Nu til selve PostgreSQL-operat\u00f8rerne.<\/p>\n<h3>1. Stolon<\/h3>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/sorintlab\/stolon\">Stolon<\/a><\/noindex> fra det italienske firma Sorint.lab i <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">den allerede n\u00e6vnte rapport<\/a><\/noindex> blev betragtet som en form for standard blandt operat\u00f8rerne for DBMS. Det er et ret gammelt projekt: den f\u00f8rste offentlige udgivelse fandt sted allerede i november 2015(!), og GitHub-repositoriet kan prale af n\u00e6sten 3000 stjerner og 40+ bidragsydere.<\/p>\n<p>Og faktisk er Stolon et fremragende eksempel p\u00e5 gennemt\u00e6nkt arkitektur:<\/p>\n<p><img decoding=\"async\" alt=\"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring\" src=\"\/wp-content\/uploads\/2020\/09\/a34625c130bfd29c5645cf2417042de2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMan kan l\u00e6se om dette operat\u00f8rs opbygning i rapporten eller <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/sorintlab\/stolon\/blob\/master\/doc\/architecture.md\">projektets dokumentation<\/a><\/noindex>. Generelt set kan det siges, at det kan g\u00f8re alt det n\u00e6vnte: failover, proxy til gennemsigtig adgang for klienter, backups\u2026 Desuden giver proxyen adgang gennem et enkelt serviceendpoint \u2014 i mods\u00e6tning til de to andre l\u00f8sninger, der vil blive gennemg\u00e5et senere (de har to tjenester til adgang til databasen).<\/p>\n<p>Dog har Stolon <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/sorintlab\/stolon\/issues\/463#issuecomment-379666733\">ingen Custom Resources<\/a><\/noindex>, hvilket g\u00f8r, at man ikke kan implementere det, s\u00e5 man nemt og hurtigt \u2014 \"som varme bolde\" \u2014 kan oprette instanser af DBMS i Kubernetes. Administration sker gennem v\u00e6rkt\u00f8jet <code>stolonctl<\/code>, implementeringen \u2014 gennem Helm-chartet, og brugerdefinerede ressourcer angives i ConfigMap.<\/p>\n<p>P\u00e5 den ene side synes det, at operat\u00f8ren ikke rigtig er en operat\u00f8r (da den ikke bruger CRD). Men p\u00e5 den anden side er det et fleksibelt system, der g\u00f8r det muligt at konfigurere ressourcer i K8s, som du \u00f8nsker.<\/p>\n<p>Afslutningsvis virker det for os ikke optimalt at have et separat chart til hver DB. Derfor begyndte vi at lede efter alternativer.<\/p>\n<h3>2. Crunchy Data PostgreSQL Operator<\/h3>\n<p>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CrunchyData\/postgres-operator\">Operat\u00f8ren fra Crunchy Data<\/a><\/noindex>, en ung amerikansk startup, s\u00e5 ud til at v\u00e6re et logisk alternativ. Dens offentlige historie begynder med den f\u00f8rste udgivelse i marts 2017, og siden da har GitHub-repositoriet f\u00e5et lidt under 1300 stjerner og 50+ bidragsydere. Den seneste udgivelse i september blev testet for arbejde med Kubernetes 1.15\u20141.18, OpenShift 3.11+ og 4.4+, GKE og VMware Enterprise PKS 1.3+.<\/p>\n<p>Arkitekturen for Crunchy Data PostgreSQL Operator opfylder ogs\u00e5 de angivne krav:<\/p>\n<p><img decoding=\"async\" alt=\"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring\" src=\"\/wp-content\/uploads\/2020\/09\/343cd0494abae42fcf5b47c5bb4eb310.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAdministration occurs via a utility <code>pgo<\/code>, but it in turn generates Custom Resources for Kubernetes. Therefore, as potential users, the operator pleased us:<\/p>\n<ul>\n<li> there's management through CRD;<\/li>\n<li> convenient user management (also through CRD);<\/li>\n<li> integration with other components <noindex><a rel=\"nofollow\" href=\"https:\/\/access.crunchydata.com\/documentation\/crunchy-postgres-containers\/4.3.1\/\">Crunchy Data Container Suite<\/a><\/noindex> \u2014 a specialized collection of container images for PostgreSQL and utilities for working with it (including pgBackRest, pgAudit, extensions from contrib, etc.).<\/li>\n<\/ul>\n<p>\nHowever, attempts to start using the operator from Crunchy Data revealed several problems:<\/p>\n<ul>\n<li>There was no possibility for tolerations \u2014 only nodeSelector was provided.<\/li>\n<li>The created pods were part of a Deployment, even though we deployed a stateful application. Unlike StatefulSets, Deployments cannot create disks.<\/li>\n<\/ul>\n<p>\nThis last drawback leads to funny moments: in the test environment, it was possible to run 3 replicas with one disk <i>local storage<\/i>, resulting in the operator reporting that 3 replicas were running (although this was not the case).<\/p>\n<p>Another feature of this operator is its built-in integration with various auxiliary systems. For example, installing pgAdmin and pgBounce is easy, and <noindex><a rel=\"nofollow\" href=\"https:\/\/access.crunchydata.com\/documentation\/postgres-operator\/4.4.0\/installation\/other\/ansible\/installing-metrics\/\">dokumentationen<\/a><\/noindex> preconfigured Grafana and Prometheus are being considered. In a recent <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> improved integration with the project <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CrunchyData\/pgmonitor\">pgMonitor<\/a><\/noindex>, allowing the operator to offer clear visualization of metrics for PgSQL \"out of the box.\"<\/p>\n<p>Nevertheless, the strange choice of generated Kubernetes resources led us to search for an alternative solution.<\/p>\n<h3>3. Zalando Postgres Operator<\/h3>\n<p>\nWe have long been familiar with Zalando products: we have experience using Zalenium and, of course, we tried <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/patroni\">Patroni<\/a><\/noindex> \u2014 their popular HA-solution for PostgreSQL. One of its authors, Alexey Klyukin, talked about the company's approach to creating <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\">Postgres Operator<\/a><\/noindex> in a broadcast of <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/479438\/\">Postgres Tuesday #5<\/a><\/noindex>, and we liked it.<\/p>\n<p>This is the youngest solution among those considered in the article: the first release took place in August 2018. However, despite the small number of formal releases, the project has come a long way, already surpassing the solution from Crunchy Data with over 1300 stars on GitHub and the highest number of contributors (70+).<\/p>\n<p>\"Under the hood,\" this operator uses time-tested solutions:<\/p>\n<ul>\n<li> Patroni and <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/spilo\">Spilo<\/a><\/noindex> for management,<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/wal-e\/wal-e\">WAL-E<\/a><\/noindex> \u2014 for backups,<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgbouncer\/pgbouncer\">PgBouncer<\/a><\/noindex> \u2014 as a connection pool.<\/li>\n<\/ul>\n<p>\nS\u00e5dan pr\u00e6senteres Zalando-operatorens arkitektur:<\/p>\n<p><img decoding=\"async\" alt=\"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring\" src=\"\/wp-content\/uploads\/2020\/09\/c66f10c1a818592ecaacd2db4ce5ace5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOperat\u00f8ren styres fuldt ud via Custom Resources, hvilket automatisk opretter et StatefulSet af containere, som derefter kan tilpasses ved at tilf\u00f8je forskellige sidecar&#8217;er til pod'en. Alt dette er et betydeligt plus i forhold til operat\u00f8ren fra Crunchy Data.<\/p>\n<p>Da vi valgte l\u00f8sningen fra Zalando blandt 3 overvejede muligheder, vil den f\u00f8lgende beskrivelse af dens funktioner blive pr\u00e6senteret nedenfor, sammen med praksis for anvendelse.<\/p>\n<h2>Praksis med Postgres Operator fra Zalando<\/h2>\n<p>\nDeployeringen af operatoren er meget enkel: det er nok at downloade den nyeste release fra GitHub og anvende YAML-filerne fra biblioteket <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/tree\/master\/manifests\">manifests<\/a><\/noindex>. Som et alternativ kan man ogs\u00e5 bruge <noindex><a rel=\"nofollow\" href=\"https:\/\/operatorhub.io\/operator\/postgres-operator\">OperatorHub<\/a><\/noindex>.<\/p>\n<p>Efter installationen b\u00f8r man tage sig af konfigurationen af <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/docs\/reference\/operator_parameters.md#aws-or-gcp-interaction\">lagre til logs og backups<\/a><\/noindex>. Det sker via ConfigMap <code>postgres-operator<\/code> i det navnerum, hvor du har installeret operatoren. N\u00e5r lagrene er konfigureret, kan du deployere dit f\u00f8rste PostgreSQL-klynge.<\/p>\n<p>For eksempel ser vores standarddeployering s\u00e5ledes ud:<\/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: &quot;12&quot;\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>\nDenne manifest deployerer en klynge med 3 instanser med en sidecar i form af <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/wrouesnel\/postgres_exporter\">postgres_exporter<\/a><\/noindex>, fra hvilken vi indsamler applikationsmetrikker. Som du kan se, er det hele meget simpelt, og hvis \u00f8nskes, kan man g\u00f8re det bogstaveligt talt ubegribelig m\u00e6ngde klynger.<\/p>\n<p>Det er v\u00e6rd at bem\u00e6rke <b>webpanelet til administration<\/b> \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/docs\/operator-ui.md\">postgres-operator-ui<\/a><\/noindex>. Det leveres sammen med operatoren og g\u00f8r det muligt at oprette og slette klynger samt arbejde med de backups, som operatoren laver.<\/p>\n<p><img decoding=\"async\" alt=\"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring\" src=\"\/wp-content\/uploads\/2020\/09\/2e9ae033a9817d101c843ce061aafdb5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Liste over PostgreSQL-klynger<\/i><\/p>\n<p><img decoding=\"async\" alt=\"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring\" src=\"\/wp-content\/uploads\/2020\/09\/74a65f7f301da14a0cb7bea6d5ef84ec.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Backupstyring<\/i><\/p>\n<p>En anden interessant funktion er underst\u00f8ttelsen af <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/docs\/user.md#teams-api-roles\">Teams API<\/a><\/noindex>. Denne mekanisme opretter automatisk <b>roller i PostgreSQL<\/b>, baseret p\u00e5 den modtagne liste over brugernavne. Derefter g\u00f8r API'et det muligt at returnere en liste over brugere, for hvilke der automatisk oprettes roller.<\/p>\n<h3>Problemer og deres l\u00f8sninger<\/h3>\n<p>\nDog brugen af operat\u00f8ren viste hurtigt, at der var flere v\u00e6sentlige ulemper:<\/p>\n<ol>\n<li> mangel p\u00e5 st\u00f8tte til nodeSelector;<\/li>\n<li> umulighed for at deaktivere backups;<\/li>\n<li> n\u00e5r man bruger funktionen til at oprette databaser, gives der ingen standardprivilegier;<\/li>\n<li> der mangler periodisk dokumentation, eller den er ikke opdateret.<\/li>\n<\/ol>\n<p>\nHeldigvis kan mange af dem l\u00f8ses. Lad os starte fra slutningen \u2014 problemer med <strong>dokumentation<\/strong>. <\/p>\n<p>Du vil sandsynligvis opleve, at det ikke altid er klart, hvordan man angiver en backup og hvordan man forbinder backup-bucket til Operator UI. Dette n\u00e6vnes kort i dokumentationen, og den reelle beskrivelse findes i <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/481\">PR<\/a><\/noindex>:<\/p>\n<ol>\n<li>det er n\u00f8dvendigt at lave en hemmelighed;<\/li>\n<li>overf\u00f8re den til operat\u00f8ren som et parameter <code>pod_environment_secret_name<\/code> i CRD med operat\u00f8rens indstillinger eller i ConfigMap (det afh\u00e6nger af, hvordan du har besluttet at installere operat\u00f8ren).<\/li>\n<\/ol>\n<p>\nMen som det viste sig, er det i \u00f8jeblikket umuligt. Det er derfor, vi har samlet <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/postgres-operator\">vores egen version af operat\u00f8ren<\/a><\/noindex> med nogle yderligere tredjepartsudviklinger. L\u00e6s mere om den nedenfor.<\/p>\n<p>Hvis du overf\u00f8rer parametre til operat\u00f8ren for backup, nemlig \u2014 <code>wal_s3_bucket<\/code> og adgangsn\u00f8gler til AWS S3, s\u00e5 <strong>vil den tage backup af alt<\/strong>: ikke kun databaser i produktion, men ogs\u00e5 staging. Det var ikke tilfredsstillende for os.<\/p>\n<p>I beskrivelsen af parametrene for Spilo, som er den grundl\u00e6ggende Docker-omslag for PgSQL, n\u00e5r man bruger operat\u00f8ren, blev det klart: man kan overf\u00f8re parameteren <code>WAL_S3_BUCKET<\/code> som tom, hvilket derved deaktiverer backups. Desuden, til stor gl\u00e6de, fandtes der ogs\u00e5 en <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/908\">f\u00e6rdig PR<\/a><\/noindex>, som vi straks accepterede i vores fork. Nu er det nok bare at tilf\u00f8je <code>enableWALArchiving: false<\/code> til ressourcen for PostgreSQL-klyngen.<\/p>\n<p>Ja, der var mulighed for at g\u00f8re det anderledes ved at k\u00f8re 2 operat\u00f8rer: en til staging (uden backups) og den anden til produktion. Men s\u00e5dan kunne vi n\u00f8jes med \u00e9n.<\/p>\n<p>Okay, vi har l\u00e6rt at give adgang til S3 i databaser, og backups begyndte at blive sendt til lageret. Hvordan f\u00e5r man backup-siderne til at fungere i Operator UI?<\/p>\n<p><img decoding=\"async\" alt=\"Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring\" src=\"\/wp-content\/uploads\/2020\/09\/46169b53707b0bbb686798c655022f40.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nI Operator UI er det n\u00f8dvendigt at tilf\u00f8je 3 variable:<\/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>\nEfter dette vil styringen af backups v\u00e6re tilg\u00e6ngelig, hvilket i vores tilf\u00e6lde vil forenkle arbejdet med staging, da vi kan levere snapshots fra produktion uden yderligere scripts.<\/p>\n<p>Som en yderligere fordel blev arbejdet med Teams API n\u00e6vnt og de brede muligheder for at oprette databaser og roller ved hj\u00e6lp af operat\u00f8ren. Men de oprettede <strong>roller havde ingen standardrettigheder.<\/strong>Derfor kunne en bruger med l\u00e6serettigheder ikke l\u00e6se de nye tabeller.<\/p>\n<p>Hvorfor er det s\u00e5dan? P\u00e5 trods af at koden <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/pkg\/cluster\/database.go#L42\">er<\/a><\/noindex> n\u00f8dvendige <code>GRANT<\/code>, anvendes de langt fra altid. Der er 2 metoder: <code>syncPreparedDatabases<\/code> og <code>syncDatabases<\/code>. I <code>syncPreparedDatabases<\/code> \u2014 p\u00e5 trods af at der i sektionen <code>preparedDatabases<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/blob\/master\/manifests\/complete-postgres-manifest.yaml#L26\">er<\/a><\/noindex> er en betingelse <code>defaultRoles<\/code> og <code>defaultUsers<\/code> til oprettelse af roller, \u2014 anvendes standardrettighederne ikke. Vi er i gang med at forberede et patch, s\u00e5 disse rettigheder automatisk anvendes.<\/p>\n<p>Og det sidste punkt i de relevante forbedringer for os \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/975\">patch<\/a><\/noindex>, der tilf\u00f8jer Node Affinity til den oprettede StatefulSet. Vore kunder foretr\u00e6kker ofte at reducere omkostningerne ved at bruge spot-instanser, og det er tydeligt, at man ikke b\u00f8r placere databasetjenester p\u00e5 dem. Dette sp\u00f8rgsm\u00e5l kunne ogs\u00e5 l\u00f8ses gennem tolerancer, men tilstedev\u00e6relsen af Node Affinity giver st\u00f8rre sikkerhed.<\/p>\n<h3>Hvad kom der ud af det?<\/h3>\n<p>\nEfter at have l\u00f8st de n\u00e6vnte problemer, forkede vi Postgres Operator fra Zalando til <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/postgres-operator\">vores eget repository<\/a><\/noindex>, hvor det bygges med s\u00e5 nyttige patches. Og for ekstra bekvemmelighed samlede vi ogs\u00e5 <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/r\/flant\/postgres-operator\">Docker-billede<\/a><\/noindex>.<\/p>\n<p>Listen over PR'er, der er accepteret i fork:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/1066\">bygning af en sikker letv\u00e6gts Docker-billede til operat\u00f8ren<\/a><\/noindex>;<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/908\">deaktivering af backups<\/a><\/noindex>;<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/1121\">opdatering af ressourceversioner til de aktuelle versioner af k8s<\/a><\/noindex>;<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/postgres-operator\/pull\/975\">implementering af Node Affinity<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nDet ville v\u00e6re fantastisk, hvis f\u00e6llesskabet st\u00f8ttede disse PR'er, s\u00e5 de kom ind i upstream med den n\u00e6ste version af operat\u00f8ren (1.6).<\/p>\n<h2>Bonus! Succeshistorie med migration af produktion<\/h2>\n<p>\nHvis du bruger Patroni, kan du migrere live produktion til operat\u00f8ren med minimal nedetid.<\/p>\n<p>Spilo g\u00f8r det muligt at lave standby-klynger via S3-lager med <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/wal-e\/wal-e\">Wal-E<\/a><\/noindex>, hvor den bin\u00e6re log PgSQL f\u00f8rst gemmes i S3, og derefter hentes af replikken. Men hvad nu hvis du <i>ikke<\/i> bruger Wal-E i en gammel infrastruktur? L\u00f8sningen p\u00e5 dette problem er allerede <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/true_engineering\/blog\/437318\/\">blevet foresl\u00e5et<\/a><\/noindex> p\u00e5 Habr.<\/p>\n<p>Logisk replikation af PostgreSQL kommer til hj\u00e6lp. Men lad os ikke g\u00e5 ind i detaljerne om, hvordan man opretter publikationer og abonnementer, fordi\u2026 vores plan fejlede.<\/p>\n<p>Sagen er, at der var flere belastede tabeller i databasen med millioner af r\u00e6kker, som desuden konstant blev opdateret og slettet. <noindex><a rel=\"nofollow\" href=\"https:\/\/postgrespro.ru\/docs\/postgrespro\/12\/sql-createsubscription\">Det enkle abonnement<\/a><\/noindex> med <code>copy_data<\/code>, hvor den nye replik kopierer alt indhold fra masteren, kunne simpelthen ikke f\u00f8lge med masteren. Indholds kopieringen fungerede i en uge, men indhentede aldrig masteren. Til sidst hjalp <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/avitotech\/recovery-use-cases-for-logical-replication-in-postgresql-10-a1e6bab03072\">artikel<\/a><\/noindex> kolleger fra Avito: man kan overf\u00f8re data ved hj\u00e6lp af <code>pg_dump<\/code>. Jeg vil beskrive vores (lidt modificerede) version af denne algoritme.<\/p>\n<p>Ideen er, at man kan oprette en deaktiveret abonnement knyttet til en specifik replikationsslot, og derefter rette transaktionsnummeret. Der var replikater til produktionen. Dette er vigtigt, fordi replikatet vil hj\u00e6lpe med at skabe en konsistent dump og forts\u00e6tte med at modtage \u00e6ndringer fra masteren.<\/p>\n<p>I de f\u00f8lgende kommandoer, der beskriver migrationsprocessen, vil f\u00f8lgende betegnelser blive brugt for v\u00e6rter:<\/p>\n<ol>\n<li><i>master<\/i> \u2014 den oprindelige server;<\/li>\n<li><i>replica1<\/i> \u2014 stream-replikat p\u00e5 den gamle produktion;<\/li>\n<li><i>replica2<\/i> \u2014 ny logisk replikat.<\/li>\n<\/ol>\n<p><\/p>\n<h3>Migrationsplan<\/h3>\n<p>\n1. Opret abonnement p\u00e5 alle tabeller i skemaet <code>public<\/code> databasen <code>dbname<\/code>:<\/p>\n<pre><code class=\"bash\">psql -h master -d dbname -c &quot;CREATE PUBLICATION dbname FOR ALL TABLES;&quot;\n<\/code><\/pre>\n<p>\n2. Opret replikationsslot p\u00e5 masteren:<\/p>\n<pre><code class=\"bash\">psql -h master -c &quot;select pg_create_logical_replication_slot('repl', 'pgoutput');&quot;\n<\/code><\/pre>\n<p>\n3. Stop replikationen p\u00e5 den gamle replikat:<\/p>\n<pre><code class=\"bash\">psql -h replica1 -c &quot;select pg_wal_replay_pause();&quot;\n<\/code><\/pre>\n<p>\n4. Hent transaktionsnummeret fra masteren:<\/p>\n<pre><code class=\"bash\">psql -h master -c &quot;select replay_lsn from pg_stat_replication where client_addr = 'replica1';&quot;\n<\/code><\/pre>\n<p>\n5. Tag dump fra den gamle replikat. Vi vil g\u00f8re dette i flere tr\u00e5de, hvilket hj\u00e6lper med at fremskynde processen:<\/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. Indl\u00e6s dumpet p\u00e5 den nye server:<\/p>\n<pre><code class=\"bash\">pg_restore -h replica2 -F d -j 8 -d dbname dump\/\n<\/code><\/pre>\n<p>\n7. Efter indl\u00e6sning af dumpet kan du starte replikationen p\u00e5 stream-replikatet:<\/p>\n<pre><code class=\"bash\">psql -h replica1 -c &quot;select pg_wal_replay_resume();&quot;\n<\/code><\/pre>\n<p>\n7. Opret abonnement p\u00e5 den nye logiske replikat:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -c &quot;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');&quot;\n<\/code><\/pre>\n<p>\n8. Hent <code>oid<\/code> af abonnementet:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c &quot;select oid, * from pg_subscription;&quot;\n<\/code><\/pre>\n<p>\n9. Antag, at vi har f\u00e5et <code>oid=1000<\/code>. Anvend transaktionsnummeret til abonnementet:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c &quot;select pg_replication_origin_advance('pg_1000', 'AA\/AAAAAAAA');&quot;\n<\/code><\/pre>\n<p>\n10. Start replikationen:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c &quot;alter subscription oldprod enable;&quot;\n<\/code><\/pre>\n<p>\n11. Kontroller abonnementets status, replikationen skal fungere:<\/p>\n<pre><code class=\"bash\">psql -h replica2 -d dbname -c &quot;select * from pg_replication_origin_status;&quot;\npsql -h master -d dbname -c &quot;select slot_name, restart_lsn, confirmed_flush_lsn from pg_replication_slots;&quot;\n<\/code><\/pre>\n<p>\n12. N\u00e5r replikationen er startet, og databaserne er synkroniseret, kan der foretages en overf\u00f8rsel.<\/p>\n<p>13. Efter at have deaktiveret replikationen, skal sekvenserne rettes. Dette er godt beskrevet <noindex><a rel=\"nofollow\" href=\"https:\/\/wiki.postgresql.org\/wiki\/Fixing_Sequences\">i artiklen p\u00e5 wiki.postgresql.org<\/a><\/noindex>.<\/p>\n<p>Takket v\u00e6re denne plan forl\u00f8b overf\u00f8rslen med minimal forsinkelse.<\/p>\n<h2>Konklusion<\/h2>\n<p>\nKubernetes-operat\u00f8rer g\u00f8r det muligt at forenkle forskellige handlinger ved at reducere dem til oprettelse af K8s-ressourcer. Men selvom man opn\u00e5r fantastisk automatisering ved hj\u00e6lp af dem, er det vigtigt at huske, at det kan medf\u00f8re en r\u00e6kke uventede nuancer, s\u00e5 v\u00e6lg operat\u00f8rer med omhu.<\/p>\n<p>Efter at have overvejet de tre mest popul\u00e6re Kubernetes-operat\u00f8rer til PostgreSQL, valgte vi projektet fra Zalando. Vi m\u00e5tte overvinde visse udfordringer, men resultatet var virkelig tilfredsstillende, s\u00e5 vi planl\u00e6gger at udvide denne erfaring til nogle andre PgSQL-installationer. Hvis du har erfaring med lignende l\u00f8sninger, vil vi gerne se detaljerne i kommentarerne!<\/p>\n<h2>P.S.<\/h2>\n<p>\nL\u00e6s ogs\u00e5 p\u00e5 vores blog:<\/p>\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">Databaser og Kubernetes (overblik og video af pr\u00e6sentationen)<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/479438\/\">Postgres-tirsdag nr. 5: PostgreSQL og Kubernetes. CI\/CD. Automatisering af testning<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/480722\/\">En historie om Redis-operat\u00f8ren i K8s og en kort oversigt over v\u00e6rkt\u00f8jer til dataanalyse for denne DB<\/a><\/noindex>\u00bb.<\/li>\n<\/ul>\n<p>Kilde: <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\/da\/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=\"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\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\/da\/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\udd47Kort oversigt over PostgreSQL-operat\u00f8rer til Kubernetes, vores valg og erfaring | ProHoster","description":"Stadig oftere fra kunder.","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/kratkij-obzor-operatorov-postgresql-dlya-kubernetes-nash-vybor-i-opyt","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\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\/da\/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\/da\/wp-json\/wp\/v2\/posts\/95322","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=95322"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/95322\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/95323"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=95322"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=95322"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=95322"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}