{"id":35706,"date":"2019-10-31T22:05:50","date_gmt":"2019-10-31T19:05:50","guid":{"rendered":"https:\/\/prohoster.info\/blog\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\/"},"modified":"2026-05-18T20:58:46","modified_gmt":"2026-05-18T18:58:46","slug":"otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","status":"publish","type":"post","link":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","title":{"rendered":"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>I artiklen vil jeg fort\u00e6lle, hvordan vi har grebet sp\u00f8rgsm\u00e5let om PostgreSQLs fejlfindingskapacitet an, hvorfor det blev vigtigt for os, og hvad vi endte med.<\/p>\n<p>Vi har en h\u00f8jbelastet tjeneste: 2,5 millioner brugere verden over, 50K+ aktive brugere hver dag. Serverne er placeret i Amazon i \u00e9n region i Irland: i drift har vi konstant 100+ forskellige servere, hvoraf n\u00e6sten 50 har databaser.<\/p>\n<p>Hele backend er en stor monolitisk stateful-applikation p\u00e5 Java, som opretholder en konstant websocket-forbindelse til klienten. N\u00e5r flere brugere arbejder samtidig p\u00e5 \u00e9n tavle, ser de alle \u00e6ndringer i realtid, fordi hver \u00e6ndring gemmes i databasen. Vi har cirka 10K foresp\u00f8rgsler pr. sekund til vores databaser. Ved maksimal belastning skriver vi 80-100K foresp\u00f8rgsler pr. sekund i Redis.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/443f85815b0560fade7db5b639942935.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><br \/>\n<a rel=\"nofollow\" name=\"habracut\"><\/a><\/p>\n<h2>Hvorfor vi gik fra Redis til PostgreSQL<\/h2>\n<p>Oprindeligt fungerede vores tjeneste med Redis, et key-value lager, der gemmer alle data i RAM. <a href=\"https:\/\/prohoster.info\/da\/server\/\">server<\/a>.<\/p>\n<p>Fordele ved Redis:<\/p>\n<ol>\n<li>H\u00f8j svartid, da alt er gemt i hukommelsen;<\/li>\n<li>Nemmere backup og replikering.<\/li>\n<\/ol>\n<p>Ulemper ved Redis for os:<\/p>\n<ol>\n<li>Ingen \u00e6gte transaktioner. Vi pr\u00f8vede at efterligne dem p\u00e5 applikationsniveau. Desv\u00e6rre fungerede det ikke altid godt og kr\u00e6vede meget kompleks kode.<\/li>\n<li>Datam\u00e6ngden er begr\u00e6nset af m\u00e6ngden af hukommelse. N\u00e5r datam\u00e6ngden stiger, vil hukommelsen vokse, og til sidst vil vi st\u00f8de p\u00e5 begr\u00e6nsningerne for den valgte instans, hvilket i AWS kr\u00e6ver, at vi stopper vores tjeneste for at \u00e6ndre instanstypen.<\/li>\n<li>Det er n\u00f8dvendigt konstant at opretholde et lavt latencyniveau, da vi har et meget stort antal foresp\u00f8rgsler. Det optimale latencyniveau for os er 17-20 ms. Ved et niveau p\u00e5 30-40 ms f\u00e5r vi lange svartider p\u00e5 vores applikationsforesp\u00f8rgsler og degradering af tjenesten. Desv\u00e6rre oplevede vi dette i september 2018, da en af instanserne med Redis pludselig fik et latency p\u00e5 det dobbelte af normalt. For at l\u00f8se problemet stoppede vi tjenesten midt p\u00e5 arbejdsdagen for planlagt vedligeholdelse og udskiftede den problematiske Redis-instans.<\/li>\n<li>Det er let at f\u00e5 inkonsistens i data, selv ved sm\u00e5 fejl i koden, og derefter bruge meget tid p\u00e5 at skrive kode for at rette disse data.<\/li>\n<\/ol>\n<p>Vi har taget hensyn til ulemperne og indset, at vi har brug for at flytte til noget mere praktisk, med normale transaktioner og mindre afh\u00e6ngighed af latency. Vi gennemf\u00f8rte en unders\u00f8gelse, analyserede mange muligheder og valgte PostgreSQL.<\/p>\n<p>Vi er allerede i gang med at flytte til den nye database i 1,5 \u00e5r og har kun overf\u00f8rt en lille del af dataene, s\u00e5 vi arbejder nu samtidig med Redis og PostgreSQL. L\u00e6s mere om faserne i flytningen og dataoverf\u00f8rslen mellem databaser i <a href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/437826\/\" rel=\"nofollow\">artiklen fra min kollega<\/a>.<\/p>\n<p>Da vi lige begyndte at flytte, arbejdede vores applikation direkte med databasen og henvendte sig til masteren af Redis og PostgreSQL. PostgreSQL-klyngen bestod af en master og en replika med asynkron replikering. S\u00e5dan s\u00e5 arbejdet med databaserne ud:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/674dd77de4c8b48a946c9f64c0b2fdd1.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<h2>Implementering af PgBouncer<\/h2>\n<p>Mens vi flyttede, udviklede produktet sig ogs\u00e5: antallet af brugere og antallet af servere, der arbejdede med PostgreSQL, voksede, og vi fik ikke nok forbindelser. PostgreSQL opretter en separat proces for hver forbindelse og bruger ressourcer. Antallet af forbindelser kan \u00f8ges op til et vist punkt, ellers er der en chance for, at databasen arbejder suboptimalt. Den ideelle l\u00f8sning i en s\u00e5dan situation ville v\u00e6re at v\u00e6lge en forbindelsesmanager, som placeres foran databasen.<\/p>\n<p>Vi havde to muligheder for forbindelsesmanageren: Pgpool og PgBouncer. Men den f\u00f8rste underst\u00f8tter ikke transaktionsdrift med databasen, s\u00e5 vi valgte PgBouncer.<\/p>\n<p>Vi har indstillet f\u00f8lgende arbejdsmetode: vores applikation henvender sig til en PgBouncer, der st\u00e5r bag masterne af PostgreSQL, og bag hver master er der en replika med asynkron replikering.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/dabcb7f6006520c4fec85fb925e8975e.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>P\u00e5 samme tid kunne vi ikke opbevare hele datam\u00e6ngden i PostgreSQL, og hastigheden i arbejdet med databasen var vigtig for os, s\u00e5 vi begyndte at shardere PostgreSQL p\u00e5 applikationsniveau. Den ovenfor beskrevne skema er relativt praktisk til dette: ved tilf\u00f8jelse af et nyt shard er det tilstr\u00e6kkeligt at opdatere PgBouncer-konfigurationen, og applikationen kan straks arbejde med det nye shard.<\/p>\n<h3>Fejltolerance for PgBouncer<\/h3>\n<p>Denne ordning fungerede indtil den eneste PgBouncer-instans gik ned. Vi er i AWS, hvor alle instanser k\u00f8rer p\u00e5 hardware, der periodisk fejler. I s\u00e5danne tilf\u00e6lde flytter instansen blot til ny hardware og fungerer igen. Det skete ogs\u00e5 med PgBouncer, men den blev utilg\u00e6ngelig. Resultatet af dette nedbrud var, at vores service var utilg\u00e6ngelig i 25 minutter. AWS anbefaler brugerredundans for s\u00e5danne situationer, hvilket ikke var implementeret hos os p\u00e5 det tidspunkt.<\/p>\n<p>Efter dette begyndte vi seri\u00f8st at overveje PgBouncers og PostgreSQL-klusters fejltolerance, da en lignende situation kunne opst\u00e5 med enhver instans i vores AWS-konto.<\/p>\n<p>Vi opbyggede PgBouncers fejltolerance-ordning p\u00e5 f\u00f8lgende m\u00e5de: alle applikationens servere henvender sig til Network Load Balancer, bag hvilken der st\u00e5r to PgBouncer. Hver PgBouncer ser p\u00e5 de samme master PostgreSQL for hver shard. I tilf\u00e6lde af at der opst\u00e5r en situation med nedbrud af instansen i AWS, omdirigeres al trafik gennem en anden PgBouncer. Network Load Balancer's fejltolerance sikres af AWS.<\/p>\n<p>Denne ordning g\u00f8r det problemfrit at tilf\u00f8je nye PgBouncer-servere.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/2c1f1e7d9f7be4fdeb43858520d55e36.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<h2>Oprettelse af et fejltolerant PostgreSQL-kluster<\/h2>\n<p>I l\u00f8sningen af denne opgave overvejede vi forskellige muligheder: egenudviklet failover, repmgr, AWS RDS, Patroni.<\/p>\n<h3>Hjemmelavede scripts<\/h3>\n<p>De kan overv\u00e5ge maestroens arbejde, og i tilf\u00e6lde af dens nedbrud, promovere en replica til maestro og opdatere konfigurationen af PgBouncer.<\/p>\n<p>Fordelen ved denne tilgang er maksimal enkelhed, fordi du selv skriver scripts og pr\u00e6cist forst\u00e5r, hvordan de fungerer.<\/p>\n<p>Ulemper:<\/p>\n<ul>\n<li>Maestroen kunne v\u00e6re blevet v\u00e6rende aktiv, i stedet kunne der v\u00e6re opst\u00e5et et netv\u00e6rksproblem. Failover, uden at vide det, ville promovere replicaen til maestro, mens den gamle maestro forts\u00e6tter med at fungere. Som et resultat ville vi have to servere i rollen som maestro og ikke vide, hvilken af dem der har de seneste opdaterede data. Denne situation kaldes ogs\u00e5 split-brain;<\/li>\n<li>Vi blev uden en replica. I vores konfiguration er der en maestro og \u00e9n replica. Efter skiftet promoveres replicaen til maestro, og vi har ikke flere replicas, s\u00e5 vi m\u00e5 manuelt tilf\u00f8je en ny replica.<\/li>\n<li>Der er behov for yderligere overv\u00e5gning af failover-arbejdet, da vi har 12 PostgreSQL-shards, hvilket betyder, at vi skal overv\u00e5ge 12 klustre. Ved en stigning i antallet af shards skal vi huske at opdatere failover ogs\u00e5.<\/li>\n<\/ul>\n<p>At skabe en failover-l\u00f8sning fra bunden virker meget komplekst og kr\u00e6ver ikke-triviel support. Med \u00e9n PostgreSQL-klynge vil dette v\u00e6re den simpleste l\u00f8sning, men den kan ikke skaleres, s\u00e5 den passer ikke til os.<\/p>\n<h3>Repmgr<\/h3>\n<p>Replication Manager til PostgreSQL-klynger, som kan styre driften af en PostgreSQL-klynge. Der er dog ikke en automatisk failover \u201cout of the box\u201d, s\u00e5 der kr\u00e6ves en egen \u201cwrapper\u201d oven p\u00e5 den eksisterende l\u00f8sning. Det kan alts\u00e5 vise sig at v\u00e6re endnu mere kompliceret end med skr\u00e6ddersyede scripts, derfor valgte vi endda ikke at afpr\u00f8ve Repmgr.<\/p>\n<h3>AWS RDS<\/h3>\n<p>Den underst\u00f8tter alt det n\u00f8dvendige for os, kan lave backups og underst\u00f8tter en connection pool. Den har automatisk switch: n\u00e5r masteren d\u00f8r, bliver replikaen den nye master, og AWS opdaterer DNS-posten til den nye master, mens replikaerne kan v\u00e6re placeret i forskellige AZ.<\/p>\n<p>En ulempe er manglen p\u00e5 fine indstillinger. Et eksempel p\u00e5 fine indstillinger: p\u00e5 vores instanser er der begr\u00e6nsninger for TCP-forbindelser, noget der desv\u00e6rre ikke kan g\u00f8res i RDS:<\/p>\n<pre><code class=\"python\">net.ipv4.tcp_keepalive_time=10\nnet.ipv4.tcp_keepalive_intvl=1\nnet.ipv4.tcp_keepalive_probes=5\nnet.ipv4.tcp_retries2=3\n<\/code><\/pre>\n<p>Desuden er prisen p\u00e5 AWS RDS n\u00e6sten dobbelt s\u00e5 h\u00f8j som den normale pris for instanser, hvilket var hoved\u00e5rsagen til at vi valgte ikke at tage denne l\u00f8sning.<\/p>\n<h3>Patroni<\/h3>\n<p>Dette er en skabelon i Python til at administrere PostgreSQL med god dokumentation, automatisk failover og kildekoden tilg\u00e6ngelig p\u00e5 GitHub.<\/p>\n<p>Fordele ved Patroni:<\/p>\n<ul>\n<li>Hver konfigurationsparameter er beskrevet, s\u00e5 det er klart, hvordan tingene fungerer;<\/li>\n<li>Automatisk failover fungerer fra starten;<\/li>\n<li>Skriver i Python, og da vi selv skriver meget i Python, vil det v\u00e6re lettere for os at finde os til rette med problemerne og m\u00e5ske endda hj\u00e6lpe med projektets udvikling;<\/li>\n<li>Styrer fuldst\u00e6ndigt PostgreSQL, tillader \u00e6ndring af konfigurationen p\u00e5 alle noder i klyngen, og hvis en genstart af klyngen kr\u00e6ves for at anvende den nye konfiguration, kan dette ogs\u00e5 g\u00f8res ved hj\u00e6lp af Patroni.<\/li>\n<\/ul>\n<p>Ulemper:<\/p>\n<ul>\n<li>Fra dokumentationen er det uklart, hvordan man korrekt arbejder med PgBouncer. Selvom det er sv\u00e6rt at betegne som en ulempe, for det er Patronis opgave at styre PostgreSQL, hvordan forbindelser til Patroni h\u00e5ndteres, er vores problem;<\/li>\n<li>Der er f\u00e5 eksempler p\u00e5 implementering af Patroni i stor skala, men mange eksempler p\u00e5 implementering fra bunden.<\/li>\n<\/ul>\n<p>I sidste ende valgte vi netop Patroni til at skabe en failover-klynge.<\/p>\n<h2>Implementeringsprocessen for Patroni<\/h2>\n<p>Inden vi brugte Patroni, havde vi 12 PostgreSQL sharder i en konfiguration med \u00e9n master og \u00e9n replika med asynkron replikation. Applikationsserverne kommunikerede med databaserne via en Network Load Balancer, som havde to instanser med PgBouncer, og bag dem var alle PostgreSQL-serverne.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/8e5350d2466311593e11add30d5b1bdc.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>For at implementere Patroni skulle vi v\u00e6lge et distribueret konfigurationslager for klyngen. Patroni arbejder med distribuerede konfigurationslagre som etcd, Zookeeper og Consul. Vi har allerede et fuldt fungerende Consul-klynge i produktion, som arbejder sammen med Vault, og ellers bruger vi det ikke. Dette er en glimrende mulighed for at begynde at anvende Consul til det, det er beregnet til.<\/p>\n<h3>Hvordan Patroni arbejder med Consul<\/h3>\n<p>Vi har en Consul-klynge, der best\u00e5r af tre noder, og en Patroni-klynge, som best\u00e5r af en leder og en replika (i Patroni kaldes masteren lederen af klyngen, mens slaverne er replikaer). Hver instans af Patroni-klyngen sender konstant oplysninger om klyngets tilstand til Consul. Derfor kan man altid f\u00e5 den aktuelle konfiguration af Patroni-klyngen fra Consul og se, hvem der er lederen lige nu.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3234594b363904424fdce64c9d77fca5.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>For at forbinde Patroni til Consul er det tilstr\u00e6kkeligt at konsultere den officielle dokumentation, hvor det er angivet, at der skal angives en v\u00e6rt i formatet http eller https afh\u00e6ngigt af, hvordan vi arbejder med Consul, samt en tilslutningsskema, som er valgfri:<\/p>\n<pre><code class=\"plaintext\">host: v\u00e6rten:port for Consul-endepunktet, i formatet: http(s):\/\/host:port\nscheme: (valgfrit) http eller https, standard er http<\/code><\/pre>\n<p>Det ser simpelt ud, men her begynder udfordringerne. Vi arbejder med Consul via en sikker forbindelse over https, og vores forbindelseskonfiguration vil se s\u00e5ledes ud:<\/p>\n<pre><code class=\"python\">consul:\n  host: https:\/\/server.production.consul:8080 \n  verify: true\n  cacert: {{ consul_cacert }}\n  cert: {{ consul_cert }}\n  key: {{ consul_key }}<\/code><\/pre>\n<p>Men det fungerer ikke. Ved opstart kan Patroni ikke forbinde til Consul, fordi det stadig fors\u00f8ger at forbinde via http.<\/p>\n<p>L\u00f8sningen p\u00e5 problemet fandt vi i koden til Patroni. Heldigvis er den skrevet i Python. Det viser sig, at parameteren host ikke parses korrekt, og protokollen skal angives i scheme. Her er et fungerende konfigurationsblok for at arbejde med Consul hos os:<\/p>\n<pre><code class=\"python\">consul:\n  host: server.production.consul:8080\n  scheme: https\n  verify: true\n  cacert: {{ consul_cacert }}\n  cert: {{ consul_cert }}\n  key: {{ consul_key }}<\/code><\/pre>\n<h3>Consul-template<\/h3>\n<p>S\u00e5, vi har valgt opbevaringen til konfigurationen. Nu skal vi forst\u00e5, hvordan PgBouncer vil skifte sin konfiguration, n\u00e5r lederen i Patroni-klusteret skifter. Der er ikke noget svar p\u00e5 dette i dokumentationen, da arbejdet med PgBouncer overhovedet ikke beskrives.<\/p>\n<p>I s\u00f8gen efter en l\u00f8sning fandt vi en artikel (titlen kan jeg desv\u00e6rre ikke huske), hvor det blev n\u00e6vnt, at Consul-template var meget nyttig i forbindelsen mellem PgBouncer og Patroni. Dette motiverede os til at unders\u00f8ge, hvordan Consul-template fungerer.<\/p>\n<p>Det viste sig, at Consul-template konstant overv\u00e5ger PostgreSQL-klusterets konfiguration i Consul. Ved skift af leder opdaterer den PgBouncer-konfigurationen og sender en kommando til at genindl\u00e6se den.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/6cf92996a127bb6637ab81dc45fdd60a.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>En stor fordel ved template er, at den opbevares som kode, s\u00e5 n\u00e5r der tilf\u00f8jes en ny shard, er det nok at lave et nyt commit og opdatere template automatisk, hvilket underst\u00f8tter princippet om Infrastructure as code.<\/p>\n<h3>Ny arkitektur med Patroni<\/h3>\n<p>Som resultat fik vi f\u00f8lgende arbejdsscheme:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/bf9a9eff675a26039e3f76b16c6cd713.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>Alle applikationsservere henvender sig til load balancer \u2192 Bag den st\u00e5r to PgBouncer-instanser \u2192 P\u00e5 hver instans k\u00f8rer Consul-template, som overv\u00e5ger status for hver Patroni-kluster og sikrer, at PgBouncer-konfigurationen, der sender anmodninger til den nuv\u00e6rende leder af hvert kluster, er opdateret.<\/p>\n<h3>Manuel testning<\/h3>\n<p>Vi k\u00f8rte dette scheme i et lille testmilj\u00f8 f\u00f8r udrulning til produktion og kontrollerede, hvordan automatisk skiftede fungerer. Vi \u00e5bnede tavlen, flyttede en klisterm\u00e6rke, og i det \u00f8jeblik 'dr\u00e6bte' vi lederen af klusteret. I AWS er det nok at slukke instansen via konsollen.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/d670fe373b774f1b52bfd8a8c3b53a21.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>Klisterm\u00e6rket vendte tilbage inden for 10-20 sekunder og begyndte derefter at flytte normalt igen. Det betyder, at Patroni-klusteret fungerede korrekt: det skiftede lederen, sendte information til Consul, og Consul-template greb straks denne information, erstattede PgBouncer-konfigurationen og sendte en kommando til reload.<\/p>\n<h2>Hvordan overlever man under h\u00f8j belastning og bevarer minimal nedetid?<\/h2>\n<p>Det fungerer glimrende! Men der opst\u00e5r nye sp\u00f8rgsm\u00e5l: Hvordan vil det fungere under h\u00f8j belastning? Hvordan udruller man det hurtigt og sikkert i produktion?<\/p>\n<p>Vi besvarer det f\u00f8rste sp\u00f8rgsm\u00e5l ved hj\u00e6lp af et testmilj\u00f8, hvor vi udf\u00f8rer belastningstest. Det er helt identisk med produktionen i sin arkitektur og har genererede testdata, der omtrent svarer til produktionen i volumen. Vi beslutter os for at \"dr\u00e6be\" en af PostgreSQL-masterne under testen og se, hvad der sker. Men f\u00f8r dette er det vigtigt at kontrollere den automatiske udrulning, da vi har flere PostgreSQL-shards i dette milj\u00f8, s\u00e5 vi f\u00e5r en fremragende test af konfigurationsskripterne inden produktionen.<\/p>\n<p>Begge opgaver ser ambiti\u00f8se ud, men vi har PostgreSQL 9.6. Skal vi m\u00e5ske straks opgradere til 11.2?<\/p>\n<p>Vi har besluttet at g\u00f8re dette i 2 faser: f\u00f8rst opgradere versionen til 11.2, derefter starte Patroni.<\/p>\n<h3>Opgradering af PostgreSQL<\/h3>\n<p>For hurtigt at opgradere versionen af PostgreSQL skal der anvendes indstillingen <b>-k<\/b>, som opretter hard links p\u00e5 disken, og der er ikke behov for at kopiere dine data. P\u00e5 databaser med 300-400 GB tager opgraderingen 1 sekund.<\/p>\n<p>Vi har mange shards, s\u00e5 opgraderingen skal udf\u00f8res automatisk. Til dette har vi skrevet en Ansible playbook, som udf\u00f8rer hele opgraderingsprocessen for os:<\/p>\n<pre><code class=\"plaintext\">\/usr\/lib\/postgresql\/11\/bin\/pg_upgrade \n&lt;b&gt;--link &lt;\/b&gt;\n--old-datadir=&#039;&#039; --new-datadir=&#039;&#039; \n --old-bindir=&#039;&#039;  --new-bindir=&#039;&#039; \n --old-options=&#039; -c config_file=&#039; \n --new-options=&#039; -c config_file=&#039;<\/code><\/pre>\n<p>Her er det vigtigt at bem\u00e6rke, at f\u00f8r opgraderingen skal den udf\u00f8res med parameteren <b>\u2014check<\/b>, for at v\u00e6re sikker p\u00e5 muligheden for opgraderingen. Vores script h\u00e5ndterer ogs\u00e5 udskiftning af konfigurationsfilerne under opgraderingen. Scriptet blev udf\u00f8rt p\u00e5 30 sekunder, hvilket er et fremragende resultat.<\/p>\n<h3>Start af Patroni<\/h3>\n<p>For at l\u00f8se det andet problem er det tilstr\u00e6kkeligt at se p\u00e5 konfigurationen af Patroni. I det officielle repository findes der et eksempel p\u00e5 en konfiguration med initdb, som er ansvarlig for at initialisere en ny database ved f\u00f8rste opstart af Patroni. Men da vi allerede har en f\u00e6rdig database, har vi blot fjernet dette afsnit fra konfigurationen.<\/p>\n<p>Da vi begyndte at installere Patroni p\u00e5 den eksisterende PostgreSQL-klynge og starte den, st\u00f8dte vi p\u00e5 et nyt problem: begge servere blev startet som leader. Patroni ved ikke noget om den tidligere tilstand af klyngen og fors\u00f8ger at starte begge servere som to separate klynger med samme navn. For at l\u00f8se dette problem er det n\u00f8dvendigt at slette data mappen p\u00e5 slaven:<\/p>\n<pre><code class=\"plaintext\">rm -rf \/var\/lib\/postgresql\/<\/code><\/pre>\n<p><b>Dette skal kun g\u00f8res p\u00e5 slaven!<\/b><\/p>\n<p>N\u00e5r en ren replika forbinder, laver Patroni en basebackup af lederen og genopretter den p\u00e5 replikken, inden den indhenter den aktuelle tilstand via wal-logs.<\/p>\n<p>Endnu en udfordring, vi stod overfor, var, at alle PostgreSQL-klynger som standard hedder main. N\u00e5r hver klynge ikke kender til de andre, er det fint. Men n\u00e5r du vil bruge Patroni, skal alle klynger have et unikt navn. L\u00f8sningen er at \u00e6ndre klynge-navnet i PostgreSQL-konfigurationen.<\/p>\n<h3>Belastningstest<\/h3>\n<p>Vi k\u00f8rte en test, der simulerer brugeraktivitet p\u00e5 tavlerne. Da belastningen n\u00e5ede vores gennemsnitlige daglige niveau, gentog vi pr\u00e6cist den samme test og slukkede en instans med leader PostgreSQL. Den automatiske failover fungerede som forventet: Patroni skiftede leder, Consul-template opdaterede konfigurationen af PgBouncer og sendte en kommando til opdatering. P\u00e5 vores grafikker i Grafana kunne vi se, at der var forsinkelser p\u00e5 20-30 sekunder og et lille antal fejl fra serverne relateret til databaseforbindelsen. Det er en normal situation, s\u00e5danne v\u00e6rdier er acceptable for vores failover og bestemt bedre end nedetid for tjenesten.<\/p>\n<h2>Patroni-output p\u00e5 produktion<\/h2>\n<p>Resultatet blev, at vi fik f\u00f8lgende plan:<\/p>\n<ul>\n<li>Deployment af Consul-template p\u00e5 PgBouncer-serverne og opstart;<\/li>\n<li>Opdatering af PostgreSQL til version 11.2;<\/li>\n<li>\u00c6ndring af klyngens navn;<\/li>\n<li>Opstart af Patroni-klyngen.<\/li>\n<\/ul>\n<p>Vores plan tillader, at vi kan udf\u00f8re det f\u00f8rste punkt p\u00e5 n\u00e6sten ethvert tidspunkt; vi kan tage hver PgBouncer ud af drift en ad gangen og udf\u00f8re deployment og opstart af consul-template p\u00e5 dem. Det gjorde vi.<\/p>\n<p>Til hurtig udrulning brugte vi Ansible, da vi allerede havde testet alle playbooks i testmilj\u00f8et, og udf\u00f8relsestiden for hele scenariet var fra 1,5 til 2 minutter for hver shard. Vi kunne rulle det ud \u00e9n ad gangen p\u00e5 hver shard uden at stoppe vores service, men det ville kr\u00e6ve, at vi slukkede hver PostgreSQL i et par minutter. I dette tilf\u00e6lde ville brugerne, hvis data findes p\u00e5 denne shard, ikke kunne arbejde ordentligt i mellemtiden, og det er uacceptabelt for os.<\/p>\n<p>Udvejen ud af denne situation var en planlagt vedligeholdelse, som finder sted hver tredje m\u00e5ned. Dette er et vindue til planlagt arbejde, hvor vi fuldst\u00e6ndig slukker for vores service og opdaterer databaseinstanserne. Der var kun en uge tilbage til det n\u00e6ste vindue, og vi besluttede at vente og forberede os yderligere. I ventetiden tog vi ogs\u00e5 ekstra forholdsregler: For hver PostgreSQL shard oprettede vi en sekund\u00e6r replika som backup i tilf\u00e6lde af fejl for at bevare de nyeste data, og vi tilf\u00f8jede en ny instans for hver shard, som skulle blive en ny replika i Patroni-klyngen for ikke at skulle udf\u00f8re kommandoen til at slette data. Alt dette hjalp med at minimere risikoen for fejl.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/29e5c5f50dbfebfe5665fa0aeb009728.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>Vi genstartede vores service, og alt fungerede som det skulle, brugerne fortsatte med at arbejde, men p\u00e5 graferne bem\u00e6rkede vi en unormalt h\u00f8j belastning p\u00e5 Consul-serverne.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/af0eef9ac235706c0aac62d2e1ee179d.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>Hvorfor s\u00e5 vi ikke dette i testmilj\u00f8et? Dette problem illustrerer meget godt, at det er n\u00f8dvendigt at f\u00f8lge princippet om Infrastructure as Code og forbedre hele infrastrukturen, fra testmilj\u00f8er til produktion. Ellers kan man nemt f\u00e5 et problem som det, vi fik. Hvad skete der? Consul blev f\u00f8rst introduceret i produktion og derefter i testmilj\u00f8erne, hvilket bet\u00f8d, at versionen af Consul i testmilj\u00f8erne var h\u00f8jere end i produktionen. Netop i en af udgivelserne blev der l\u00f8st et CPU-l\u00e6kageproblem ved brug af consul-template. Derfor opdaterede vi simpelthen Consul for at l\u00f8se problemet.<\/p>\n<h3>Genstart Patroni klynge<\/h3>\n<p>Men vi st\u00f8dte p\u00e5 et nyt problem, som vi ikke engang havde mistanke om. Ved opdateringen af Consul fjerner vi blot en Consul-node fra klyngen med kommandoen consul leave \u2192 Patroni forbinder sig til en anden Consul-server \u2192 alt fungerer. Men da vi n\u00e5ede den sidste instans i Consul-klyngen og sendte den kommandoen consul leave, genstartede alle Patroni-klynger simpelthen, og i loggene s\u00e5 vi f\u00f8lgende fejl:<\/p>\n<pre><code class=\"plaintext\">FEJL: get_cluster\nTraceback (seneste kald senest):\n...\nRetryFailedError: &#039;Overskredet fors&oslash;gsfrist&#039;\nFEJL: Fejl i kommunikationen med DCS\n&lt;b&gt;LOG: databasesystemet er lukket&lt;\/b&gt;<\/code><\/pre>\n<p>Patroni-klyngen kunne ikke f\u00e5 oplysninger om sin klynge og genstartede.<\/p>\n<p>For at finde en l\u00f8sning kontaktede vi forfatterne af Patroni via en issue p\u00e5 GitHub. De foreslog forbedringer til vores konfigurationsfiler:<\/p>\n<pre><code class=\"python\">consul:\n consul.checks: []\nbootstrap:\n dcs:\n   retry_timeout: 8<\/code><\/pre>\n<p>Vi var i stand til at gentage problemet i testmilj\u00f8et og teste disse parametre der, men desv\u00e6rre fungerede de ikke.<\/p>\n<p>Problemet er stadig uafklaret. Vi planl\u00e6gger at pr\u00f8ve f\u00f8lgende l\u00f8sningsmuligheder:<\/p>\n<ul>\n<li>Bruge Consul-agent p\u00e5 hver instans i Patroni-klyngen;<\/li>\n<li>Korrigere problemet i koden.<\/li>\n<\/ul>\n<p>Vi forst\u00e5r, hvor fejlen opst\u00e5r: sandsynligvis er problemet med brugen af standard tidsgr\u00e6nsen, som ikke bliver overskrevet i konfigurationsfilen. N\u00e5r den sidste Consul-server fjernes fra klyngen, fryser hele Consul-klyngen i mere end et sekund, hvilket g\u00f8r, at Patroni ikke kan f\u00e5 klyngens status og genstarter hele klyngen.<\/p>\n<p>Heldigvis har vi ikke st\u00f8dt p\u00e5 flere fejltagelser.<\/p>\n<h2>Resultater af brugen af Patroni<\/h2>\n<p>Efter den vellykkede opstart af Patroni tilf\u00f8jede vi en ekstra replik i hver klynge. Nu har hver klynge en form for flertalsafstemning: en leder og to replikaer, for at v\u00e6re sikker i tilf\u00e6lde af split-brain under skift.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3bb4c0495fea274b04edd2e99b59eacb.png\" alt=\"Failoverklynge PostgreSQL + Patroni. Implementeringserfaring\" \/><\/p>\n<p>Patroni har fungeret i mere end tre m\u00e5neder i produktionen. I l\u00f8bet af denne tid har det allerede hjulpet os. For nylig d\u00f8de lederen af en af klyngerne i AWS, automatisk failover blev aktiveret, og brugerne fortsatte med at arbejde. Patroni udf\u00f8rte sin hovedopgave.<\/p>\n<p><b>En lille opsummering af brugen af Patroni:<\/b><\/p>\n<ul>\n<li>Nem \u00e6ndring af konfigurationen. Det er tilstr\u00e6kkeligt at \u00e6ndre konfigurationen p\u00e5 en instans, og den vil blive anvendt p\u00e5 hele klyngen. Hvis en genstart er n\u00f8dvendig for at anvende den nye konfiguration, vil Patroni informere om det. Patroni kan genstarte hele klyngen med \u00e9n enkelt kommando, hvilket ogs\u00e5 er meget praktisk.<\/li>\n<li>Den automatiske failover fungerer og har allerede hjulpet os.<\/li>\n<li>Opdatering af PostgreSQL uden nedetid for applikationen. Det er n\u00f8dvendigt f\u00f8rst at opdatere replikaerne til den nye version, derefter skifte lederen i Patroni-klyngen og opdatere den gamle leder. I denne proces foretages den n\u00f8dvendige test af den automatiske failover.<\/li>\n<\/ul>\n<p>Kilde: <a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/457326\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c. \u0423 \u043d\u0430\u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441: 2,5 \u043c\u043b\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e \u0432\u0441\u0435\u043c\u0443 \u043c\u0438\u0440\u0443, 50\u041a+ \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c. \u0421\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 Amazone \u0432 \u043e\u0434\u043d\u043e\u043c \u0440\u0435\u0433\u0438\u043e\u043d\u0435 \u0418\u0440\u043b\u0430\u043d\u0434\u0438\u0438: \u0432 \u0440\u0430\u0431\u043e\u0442\u0435 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e 100+ \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432, \u0438\u0437 \u043d\u0438\u0445 \u043f\u043e\u0447\u0442\u0438 50 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":26749,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-35706","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\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.\" \/>\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\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\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\u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 PostgreSQL + Patroni. \u041e\u043f\u044b\u0442 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\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=\"2019-10-31T19:05:50+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-05-18T18:58:46+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\udd47Fejltolerant PostgreSQL-klynge + Patroni. Implementeringserfaring | ProHoster","description":"I artiklen vil jeg fort\u00e6lle, hvordan vi har grebet sp\u00f8rgsm\u00e5let om PostgreSQLs fejlfindingskapacitet an, hvorfor det blev vigtigt for os, og hvad vi endte med.","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","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\u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 PostgreSQL + Patroni. \u041e\u043f\u044b\u0442 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f | ProHoster","og:description":"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.","og:url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","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":"2019-10-31T19:05:50+00:00","article:modified_time":"2026-05-18T18:58:46+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35706","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":"2026-01-22 00:26:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 13:51:06","updated":"2026-01-22 00:26:19","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\/35706","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=35706"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/35706\/revisions"}],"predecessor-version":[{"id":172654,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/35706\/revisions\/172654"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/26749"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=35706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=35706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=35706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}