{"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\/es\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","title":{"rendered":"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>En este art\u00edculo, hablar\u00e9 sobre c\u00f3mo abordamos la cuesti\u00f3n de la alta disponibilidad en PostgreSQL, por qu\u00e9 se volvi\u00f3 importante para nosotros y qu\u00e9 resultado obtuvimos.<\/p>\n<p>Nuestro servicio es de alta carga: 2,5 millones de usuarios en todo el mundo, m\u00e1s de 50,000 usuarios activos cada d\u00eda. Los servidores est\u00e1n en Amazon en una regi\u00f3n de Irlanda: mantendemos constantemente m\u00e1s de 100 servidores diferentes, de los cuales casi 50 est\u00e1n relacionados con bases de datos.<\/p>\n<p>Todo el backend es una gran aplicaci\u00f3n monol\u00edtica stateful en Java, que mantiene una conexi\u00f3n websocket constante con el cliente. Durante el trabajo simult\u00e1neo de varios usuarios en un mismo tablero, todos ven los cambios en tiempo real, porque cada cambio lo registramos en la base de datos. Recibimos aproximadamente 10,000 solicitudes por segundo a nuestras bases de datos. En los picos de carga en Redis, registramos de 80,000 a 100,000 solicitudes por segundo.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/443f85815b0560fade7db5b639942935.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><br \/>\n<a rel=\"nofollow\" name=\"habracut\"><\/a><\/p>\n<h2>Por qu\u00e9 pasamos de Redis a PostgreSQL<\/h2>\n<p>Originalmente, nuestro servicio operaba con Redis, un almac\u00e9n key-value que mantiene todos los datos en la memoria. <a href=\"https:\/\/prohoster.info\/es\/server\/\">servidores<\/a>.<\/p>\n<p>Ventajas de Redis:<\/p>\n<ol>\n<li>Alta velocidad de respuesta, ya que todo se almacena en memoria;<\/li>\n<li>Facilidad de respaldo y replicaci\u00f3n.<\/li>\n<\/ol>\n<p>Desventajas de Redis para nosotros:<\/p>\n<ol>\n<li>No hay transacciones reales. Intentamos imitarlas a nivel de nuestra aplicaci\u00f3n. Desafortunadamente, esto no siempre funcionaba bien y requer\u00eda escribir c\u00f3digo muy complicado.<\/li>\n<li>El volumen de datos est\u00e1 limitado por la cantidad de memoria. A medida que aumenta la cantidad de datos, la memoria crecer\u00e1 y, en \u00faltima instancia, nos encontraremos con las caracter\u00edsticas de la instancia seleccionada, lo que en AWS requiere detener nuestro servicio para cambiar el tipo de instancia.<\/li>\n<li>Es necesario mantener constantemente un nivel bajo de latencia, ya que tenemos un gran n\u00famero de solicitudes. El nivel de latencia \u00f3ptimo para nosotros es de 17-20 ms. Con un nivel de 30-40 ms, recibimos respuestas lentas a las solicitudes de nuestra aplicaci\u00f3n y degradaci\u00f3n del servicio. Desafortunadamente, esto ocurri\u00f3 en septiembre de 2018, cuando una de las instancias de Redis, por alguna raz\u00f3n, experiment\u00f3 una latencia el doble de la habitual. Para solucionar el problema, detuvimos el servicio a mitad del d\u00eda para realizar un mantenimiento no programado y reemplazamos la instancia problem\u00e1tica de Redis.<\/li>\n<li>Es f\u00e1cil obtener inconsistencia de datos incluso con errores menores en el c\u00f3digo y luego gastar mucho tiempo escribiendo c\u00f3digo para corregir esos datos.<\/li>\n<\/ol>\n<p>Hemos tenido en cuenta los inconvenientes y comprendimos que necesit\u00e1bamos migrar a algo m\u00e1s conveniente, con transacciones normales y menor dependencia de la latencia. Realizamos una investigaci\u00f3n, analizamos numerosas opciones y elegimos PostgreSQL.<\/p>\n<p>Hemos estado migrando a la nueva base de datos durante 1.5 a\u00f1os y solo hemos trasladado una peque\u00f1a parte de los datos, por lo que actualmente estamos trabajando simult\u00e1neamente con Redis y PostgreSQL. M\u00e1s informaci\u00f3n sobre las etapas de la migraci\u00f3n y el cambio de datos entre bases de datos se puede encontrar en <a href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/437826\/\" rel=\"nofollow\">el art\u00edculo de mi colega<\/a>.<\/p>\n<p>Cuando comenzamos a migrar, nuestra aplicaci\u00f3n funcionaba directamente con la base de datos, accediendo al maestro de Redis y PostgreSQL. El cl\u00faster de PostgreSQL constaba de un maestro y una r\u00e9plica con replicaci\u00f3n as\u00edncrona. As\u00ed es como se ve\u00eda el esquema de trabajo con las bases de datos:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/674dd77de4c8b48a946c9f64c0b2fdd1.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<h2>Implementaci\u00f3n de PgBouncer<\/h2>\n<p>Mientras migr\u00e1bamos, el producto tambi\u00e9n evolucionaba: el n\u00famero de usuarios y servidores que trabajaban con PostgreSQL estaba aumentando, y comenzamos a quedarnos sin conexiones. PostgreSQL crea un proceso separado para cada conexi\u00f3n y consume recursos. Se puede aumentar el n\u00famero de conexiones hasta cierto punto, de lo contrario hay riesgo de que la base de datos funcione de manera sub\u00f3ptima. La opci\u00f3n ideal en esa situaci\u00f3n ser\u00e1 elegir un gestor de conexiones que se sit\u00fae frente a la base de datos.<\/p>\n<p>Ten\u00edamos dos opciones para el gestor de conexiones: Pgpool y PgBouncer. Pero el primero no soporta el modo transaccional de trabajo con la base de datos, por lo que elegimos PgBouncer.<\/p>\n<p>Configuramos el siguiente esquema de trabajo: nuestra aplicaci\u00f3n se conecta a un PgBouncer, detr\u00e1s del cual se encuentran los maestros de PostgreSQL, y detr\u00e1s de cada maestro hay una r\u00e9plica con replicaci\u00f3n as\u00edncrona.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/dabcb7f6006520c4fec85fb925e8975e.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>A su vez, no pod\u00edamos almacenar todo el volumen de datos en PostgreSQL y para nosotros era importante la velocidad de trabajo con la base de datos, por lo que comenzamos a hacer sharding de PostgreSQL a nivel de aplicaci\u00f3n. El esquema descrito anteriormente es relativamente conveniente para esto: al a\u00f1adir un nuevo shard, solo es necesario actualizar la configuraci\u00f3n de PgBouncer y la aplicaci\u00f3n puede empezar a trabajar inmediatamente con el nuevo shard.<\/p>\n<h3>Tolerancia a fallos de PgBouncer<\/h3>\n<p>Este esquema funcion\u00f3 hasta que la \u00fanica instancia de PgBouncer fall\u00f3. Estamos en AWS, donde todas las instancias se ejecutan en hardware que muere peri\u00f3dicamente. En tales casos, la instancia simplemente se traslada a nuevo hardware y vuelve a funcionar. As\u00ed ocurri\u00f3 con PgBouncer, pero se volvi\u00f3 inaccesible. Como resultado de esta ca\u00edda, nuestro servicio estuvo fuera de servicio durante 25 minutos. AWS recomienda a los usuarios implementar redundancia para estas situaciones, lo que en ese momento no hab\u00edamos implementado.<\/p>\n<p>Despu\u00e9s de esto, comenzamos a pensar seriamente en la tolerancia a fallos de PgBouncer y de los cl\u00fasteres de PostgreSQL, porque una situaci\u00f3n similar podr\u00eda repetirse con cualquier instancia en nuestra cuenta de AWS.<\/p>\n<p>Construimos el esquema de tolerancia a fallos de PgBouncer de la siguiente manera: todos los servidores de la aplicaci\u00f3n se conectan a un Network Load Balancer, detr\u00e1s del cual hay dos PgBouncer. Cada PgBouncer observa los mismos master de PostgreSQL de cada shard. En caso de una ca\u00edda de la instancia en AWS, todo el tr\u00e1fico se redirige a trav\u00e9s de otro PgBouncer. La tolerancia a fallos del Network Load Balancer es proporcionada por AWS.<\/p>\n<p>Este esquema permite agregar nuevos servidores PgBouncer sin problemas.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/2c1f1e7d9f7be4fdeb43858520d55e36.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<h2>Creaci\u00f3n de un cl\u00faster PostgreSQL tolerante a fallos<\/h2>\n<p>Al abordar esta tarea, consideramos varias opciones: failover personalizado, repmgr, AWS RDS, Patroni.<\/p>\n<h3>Scripts personalizados<\/h3>\n<p>Pueden monitorear el funcionamiento del master y, en caso de que falle, promover la r\u00e9plica a master y actualizar la configuraci\u00f3n de PgBouncer.<\/p>\n<p>Las ventajas de este enfoque radican en su m\u00e1xima simplicidad, ya que escribes los scripts t\u00fa mismo y sabes exactamente c\u00f3mo funcionan.<\/p>\n<p>Desventajas:<\/p>\n<ul>\n<li>El master puede no haber ca\u00eddo, en su lugar podr\u00eda haber ocurrido una falla de red. El failover, sin saberlo, promover\u00e1 la r\u00e9plica a master, y el antiguo master seguir\u00e1 funcionando. Como resultado, tendremos dos servidores actuando como master y no sabremos en cu\u00e1l de ellos est\u00e1n los datos m\u00e1s recientes. Esta situaci\u00f3n tambi\u00e9n se llama split-brain;<\/li>\n<li>Nos quedamos sin r\u00e9plica. En nuestra configuraci\u00f3n, hay un master y una r\u00e9plica, despu\u00e9s del failover la r\u00e9plica se promueve a master y ya no tenemos m\u00e1s r\u00e9plicas, por lo que tenemos que agregar manualmente una nueva r\u00e9plica;<\/li>\n<li>Se necesita una supervisi\u00f3n adicional del funcionamiento del failover, y tenemos 12 shards de PostgreSQL, lo que significa que debemos monitorear 12 cl\u00fasteres. Al aumentar el n\u00famero de shards, tambi\u00e9n debemos recordar actualizar el failover.<\/li>\n<\/ul>\n<p>El failover personalizado parece muy complicado y requiere soporte no trivial. Con un solo cl\u00faster de PostgreSQL, esta ser\u00eda la opci\u00f3n m\u00e1s sencilla, pero no es escalable, por lo que no es adecuada para nosotros.<\/p>\n<h3>Repmgr<\/h3>\n<p>Gestor de Replicaci\u00f3n para cl\u00fasteres de PostgreSQL que puede administrar el funcionamiento del cl\u00faster de PostgreSQL. Sin embargo, no cuenta con failover autom\u00e1tico \u201cde f\u00e1brica\u201d, por lo que ser\u00e1 necesario escribir nuestro propio \u201cwrapper\u201d sobre la soluci\u00f3n existente. Por lo tanto, puede terminar siendo incluso m\u00e1s complicado que con los scripts personalizados, as\u00ed que ni siquiera probamos Repmgr.<\/p>\n<h3>AWS RDS<\/h3>\n<p>Soporta todo lo necesario para nosotros, permite hacer copias de seguridad y soporta un pool de conexiones. Tiene conmutaci\u00f3n autom\u00e1tica: cuando el maestro falla, la r\u00e9plica se convierte en el nuevo maestro, y AWS cambia el registro DNS al nuevo maestro, manteniendo las r\u00e9plicas en diferentes AZ.<\/p>\n<p>Entre las desventajas se puede mencionar la falta de configuraciones finas. Un ejemplo de configuraciones finas: en nuestras instancias hay restricciones para las conexiones TCP, lo cual, desafortunadamente, no se puede hacer en 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>Adem\u00e1s, el precio de AWS RDS es casi el doble del precio habitual de la instancia, que fue la principal raz\u00f3n para rechazar esta soluci\u00f3n.<\/p>\n<h3>Patroni<\/h3>\n<p>Este es un template en Python para gestionar PostgreSQL con buena documentaci\u00f3n, failover autom\u00e1tico y c\u00f3digo fuente en github.<\/p>\n<p>Ventajas de Patroni:<\/p>\n<ul>\n<li>Cada par\u00e1metro de configuraci\u00f3n est\u00e1 detallado, es claro c\u00f3mo funciona cada cosa;<\/li>\n<li>El failover autom\u00e1tico funciona de f\u00e1brica;<\/li>\n<li>Est\u00e1 escrito en Python, y como nosotros tambi\u00e9n escribimos mucho en Python, nos ser\u00e1 m\u00e1s f\u00e1cil lidiar con problemas y, posiblemente, incluso ayudar en el desarrollo del proyecto;<\/li>\n<li>Administra completamente PostgreSQL, permite cambiar la configuraci\u00f3n de inmediato en todos los nodos del cl\u00faster y, si para aplicar una nueva configuraci\u00f3n se requiere reiniciar el cl\u00faster, esto tambi\u00e9n se puede hacer con Patroni.<\/li>\n<\/ul>\n<p>Desventajas:<\/p>\n<ul>\n<li>La documentaci\u00f3n no es clara sobre c\u00f3mo trabajar correctamente con PgBouncer. Aunque esto no se puede considerar un inconveniente, ya que la tarea de Patroni es gestionar PostgreSQL, y c\u00f3mo se gestionen las conexiones a Patroni ya es nuestro problema;<\/li>\n<li>Hay pocos ejemplos de implementaci\u00f3n de Patroni en grandes vol\u00famenes, pero hay muchos ejemplos de implementaci\u00f3n desde cero.<\/li>\n<\/ul>\n<p>Al final, para crear un cl\u00faster resistente a fallos, elegimos precisamente Patroni.<\/p>\n<h2>El proceso de implementaci\u00f3n de Patroni<\/h2>\n<p>Antes de Patroni, ten\u00edamos 12 shards de PostgreSQL con una configuraci\u00f3n de un maestro y una r\u00e9plica con replicaci\u00f3n as\u00edncrona. Los servidores de aplicaciones se conectaban a las bases de datos a trav\u00e9s de un Network Load Balancer, detr\u00e1s del cual hab\u00eda dos instancias de PgBouncer, y detr\u00e1s de ellas estaban todos los servidores PostgreSQL.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/8e5350d2466311593e11add30d5b1bdc.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>Para implementar Patroni, necesit\u00e1bamos seleccionar un almac\u00e9n de configuraci\u00f3n distribuido para el cl\u00faster. Patroni trabaja con sistemas de almacenamiento de configuraciones distribuidos, como etcd, Zookeeper y Consul. En producci\u00f3n, tenemos un cl\u00faster completo de Consul que funciona en conjunto con Vault y no lo utilizamos de ninguna otra manera. Es una excelente oportunidad para comenzar a utilizar Consul para su prop\u00f3sito.<\/p>\n<h3>C\u00f3mo funciona Patroni con Consul<\/h3>\n<p>Tenemos un cl\u00faster de Consul que consta de tres nodos y un cl\u00faster de Patroni que est\u00e1 compuesto por un l\u00edder y una r\u00e9plica (en Patroni, el maestro se llama l\u00edder del cl\u00faster y los esclavos son r\u00e9plicas). Cada instancia del cl\u00faster Patroni env\u00eda constantemente informaci\u00f3n sobre el estado del cl\u00faster a Consul. Por lo tanto, siempre se puede conocer la configuraci\u00f3n actual del cl\u00faster de Patroni y qui\u00e9n es el l\u00edder en ese momento desde Consul.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3234594b363904424fdce64c9d77fca5.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>Para conectar Patroni a Consul, basta con consultar la documentaci\u00f3n oficial, donde se indica que es necesario especificar el host en formato http o https, dependiendo de c\u00f3mo trabajamos con Consul, y opcionalmente el esquema de conexi\u00f3n:<\/p>\n<pre><code class=\"plaintext\">host: el host:puerto para el punto final de Consul, en formato: http(s):\/\/host:puerto\nscheme: (opcional) http o https, por defecto es http<\/code><\/pre>\n<p>Parece simple, pero aqu\u00ed comienzan los problemas ocultos. Trabajamos con Consul a trav\u00e9s de una conexi\u00f3n segura a trav\u00e9s de https, y nuestra configuraci\u00f3n de conexi\u00f3n se ver\u00e1 de la siguiente manera:<\/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>Pero as\u00ed no funciona. Al iniciar Patroni, no puede conectarse a Consul porque intenta hacer la conexi\u00f3n por http de todos modos.<\/p>\n<p>Resolver el problema ayud\u00f3 el c\u00f3digo fuente de Patroni. Afortunadamente, est\u00e1 escrito en Python. Resulta que el par\u00e1metro host no se analiza de ninguna manera, y es necesario especificar el protocolo en el esquema. As\u00ed es como se ve el bloque de configuraci\u00f3n funcional para trabajar con Consul en nuestra implementaci\u00f3n:<\/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>As\u00ed que hemos elegido el almacenamiento para la configuraci\u00f3n. Ahora necesitamos entender c\u00f3mo PgBouncer cambiar\u00e1 su configuraci\u00f3n al cambiar de l\u00edder en el cl\u00faster de Patroni. La documentaci\u00f3n no ofrece respuesta a esta pregunta, ya que no describe en principio el funcionamiento con PgBouncer.<\/p>\n<p>En busca de una soluci\u00f3n, encontramos un art\u00edculo (lamentablemente no recuerdo el nombre), donde se dec\u00eda que Consul-template ayud\u00f3 mucho en la integraci\u00f3n de PgBouncer y Patroni. Esto nos impuls\u00f3 a investigar el funcionamiento de Consul-template.<\/p>\n<p>Result\u00f3 que Consul-template monitorea constantemente la configuraci\u00f3n del cl\u00faster PostgreSQL en Consul. Al cambiar de l\u00edder, actualiza la configuraci\u00f3n de PgBouncer y env\u00eda un comando para su reinicio.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/6cf92996a127bb6637ab81dc45fdd60a.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>Una gran ventaja del template es que se almacena en forma de c\u00f3digo, por lo que al a\u00f1adir un nuevo shard, basta con hacer un nuevo commit y actualizar el template autom\u00e1ticamente, manteniendo el principio de Infraestructura como c\u00f3digo.<\/p>\n<h3>Nueva arquitectura con Patroni<\/h3>\n<p>Como resultado, obtuvimos el siguiente esquema de trabajo:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/bf9a9eff675a26039e3f76b16c6cd713.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>Todos los servidores de aplicaciones se dirigen al balanceador \u2192 detr\u00e1s de \u00e9l hay dos instancias de PgBouncer \u2192 en cada instancia se ejecuta Consul-template, que monitoriza el estado de cada cl\u00faster de Patroni y mantiene actualizada la configuraci\u00f3n de PgBouncer, que dirige las solicitudes al actual l\u00edder de cada cl\u00faster.<\/p>\n<h3>Pruebas manuales<\/h3>\n<p>Antes de poner este esquema en producci\u00f3n, lo probamos en un peque\u00f1o entorno de pruebas y verificamos el funcionamiento del cambio autom\u00e1tico. Abrimos un tablero, movimos un sticker y en ese momento 'eliminamos' al l\u00edder del cl\u00faster. En AWS, esto es suficiente con apagar la instancia a trav\u00e9s de la consola.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/d670fe373b774f1b52bfd8a8c3b53a21.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>El sticker regres\u00f3 en 10-20 segundos, y luego comenz\u00f3 a moverse normalmente nuevamente. Esto significa que el cl\u00faster de Patroni funcion\u00f3 correctamente: cambi\u00f3 de l\u00edder, envi\u00f3 la informaci\u00f3n a Consul, y Consul-template capt\u00f3 de inmediato esta informaci\u00f3n, reemplaz\u00f3 la configuraci\u00f3n de PgBouncer y envi\u00f3 el comando para recargar.<\/p>\n<h2>\u00bfC\u00f3mo sobrevivir a una alta carga y mantener un tiempo de inactividad m\u00ednimo?<\/h2>\n<p>\u00a1Todo funciona perfectamente! Pero surgen nuevas preguntas: \u00bfC\u00f3mo funcionar\u00e1 esto bajo alta carga? \u00bfC\u00f3mo desplegar todo en producci\u00f3n de manera r\u00e1pida y segura?<\/p>\n<p>Responder la primera pregunta nos ayuda un entorno de prueba, en el que realizamos pruebas de carga. Es completamente id\u00e9ntico a production en arquitectura y tiene datos de prueba generados, que en volumen son aproximadamente equivalentes a production. Simplemente decidimos \"matar\" uno de los maestros de PostgreSQL durante la prueba y ver qu\u00e9 sucede. Pero antes de esto es importante verificar el despliegue autom\u00e1tico, ya que en este entorno tenemos varios shards de PostgreSQL, as\u00ed que obtendremos un excelente test de los scripts de configuraci\u00f3n antes de la producci\u00f3n.<\/p>\n<p>Ambas tareas parecen ambiciosas, pero tenemos PostgreSQL 9.6. \u00bfQuiz\u00e1s deber\u00edamos actualizar directamente a 11.2?<\/p>\n<p>Decidimos hacer esto en 2 etapas: primero actualizar la versi\u00f3n a 11.2, luego iniciar Patroni.<\/p>\n<h3>Actualizaci\u00f3n de PostgreSQL<\/h3>\n<p>Para una actualizaci\u00f3n r\u00e1pida de la versi\u00f3n de PostgreSQL es necesario utilizar la opci\u00f3n <b>-k<\/b>, que crea enlaces duros en el disco y no requiere la copia de sus datos. En bases de datos de 300-400 GB, la actualizaci\u00f3n toma 1 segundo.<\/p>\n<p>Tenemos muchos shards, por lo que la actualizaci\u00f3n debe hacerse de forma autom\u00e1tica. Para ello, hemos escrito un playbook de Ansible que lleva a cabo todo el proceso de actualizaci\u00f3n por nosotros:<\/p>\n<pre><code class=\"plaintext\">\/usr\/lib\/postgresql\/11\/bin\/pg_upgrade \n&lt;b&gt;--enlace &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>Aqu\u00ed es importante se\u00f1alar que antes de iniciar la actualizaci\u00f3n es necesario realizarla con el par\u00e1metro <b>\u2014verificar<\/b>, para asegurarse de que la actualizaci\u00f3n sea posible. Adem\u00e1s, nuestro script realiza el intercambio de configuraciones durante la actualizaci\u00f3n. Nuestro script tard\u00f3 30 segundos en ejecutarse, lo cual es un gran resultado.<\/p>\n<h3>Iniciar Patroni<\/h3>\n<p>Para resolver el segundo problema, es suficiente mirar la configuraci\u00f3n de Patroni. En el repositorio oficial hay un ejemplo de configuraci\u00f3n con initdb, que se encarga de inicializar una nueva base de datos en el primer lanzamiento de Patroni. Pero dado que ya tenemos una base de datos lista, simplemente eliminamos esta secci\u00f3n de la configuraci\u00f3n.<\/p>\n<p>Cuando comenzamos a instalar Patroni en un cl\u00faster de PostgreSQL ya preparado y a ejecutarlo, nos encontramos con un nuevo problema: ambos servidores se iniciaban como l\u00edderes. Patroni no tiene conocimiento del estado previo del cl\u00faster y intenta iniciar ambos servidores como dos cl\u00fasteres separados con el mismo nombre. Para resolver este problema, es necesario eliminar el directorio de datos en el esclavo:<\/p>\n<pre><code class=\"plaintext\">rm -rf \/var\/lib\/postgresql\/<\/code><\/pre>\n<p><b>\u00a1Esto solo se debe hacer en el esclavo!<\/b><\/p>\n<p>Al conectar una r\u00e9plica limpia, Patroni hace un basebackup del l\u00edder y lo restaura en la r\u00e9plica, y luego alcanza el estado actual a trav\u00e9s de los registros wal.<\/p>\n<p>Otra dificultad que encontramos es que todos los cl\u00fasteres de PostgreSQL se llaman main por defecto. Esto es normal cuando cada cl\u00faster no sabe nada sobre los dem\u00e1s. Pero cuando deseas utilizar Patroni, todos los cl\u00fasteres deben tener un nombre \u00fanico. La soluci\u00f3n es cambiar el nombre del cl\u00faster en la configuraci\u00f3n de PostgreSQL.<\/p>\n<h3>Prueba de carga<\/h3>\n<p>Realizamos una prueba que simula la actividad de los usuarios en los tableros. Cuando la carga alcanz\u00f3 nuestro promedio diario, repetimos exactamente la misma prueba, apagamos una instancia con el l\u00edder de PostgreSQL. El failover autom\u00e1tico funcion\u00f3 como esper\u00e1bamos: Patroni cambi\u00f3 al l\u00edder, Consul-template actualiz\u00f3 la configuraci\u00f3n de PgBouncer y envi\u00f3 el comando para recargar. En nuestros gr\u00e1ficos de Grafana, se pod\u00edan ver retrasos de 20 a 30 segundos y un peque\u00f1o n\u00famero de errores de servidores relacionados con la conexi\u00f3n a la base. Esta es una situaci\u00f3n normal, estos valores son aceptables para nuestro failover y definitivamente son mejores que el tiempo de inactividad del servicio.<\/p>\n<h2>Salida de Patroni en producci\u00f3n<\/h2>\n<p>Al final, obtuvimos el siguiente plan:<\/p>\n<ul>\n<li>Despliegue de Consul-template en los servidores PgBouncer y puesta en marcha;<\/li>\n<li>Actualizaci\u00f3n de PostgreSQL a la versi\u00f3n 11.2;<\/li>\n<li>Cambio del nombre del cl\u00faster;<\/li>\n<li>Inicio del cl\u00faster de Patroni.<\/li>\n<\/ul>\n<p>Nuestra esquema permite realizar el primer punto pr\u00e1cticamente en cualquier momento; podemos quitar cada PgBouncer uno por uno de la operaci\u00f3n y realizar el despliegue y el inicio de consul-template. As\u00ed lo hicimos.<\/p>\n<p>Para una r\u00e1pida implementaci\u00f3n, utilizamos Ansible, ya que hab\u00edamos verificado todos los playbooks en un entorno de prueba, y el tiempo de ejecuci\u00f3n completo del escenario fue de 1,5 a 2 minutos para cada shard. Pod\u00edamos implementar todo de manera secuencial en cada shard sin detener nuestro servicio, pero tendr\u00edamos que apagar cada PostgreSQL durante unos minutos. En este caso, los usuarios cuyos datos est\u00e1n en ese shard no podr\u00edan trabajar plenamente durante ese tiempo, lo cual es inaceptable para nosotros.<\/p>\n<p>La soluci\u00f3n a esta situaci\u00f3n fue un mantenimiento programado, que realizamos cada 3 meses. Esta es una ventana para trabajos planificados, cuando apagamos completamente nuestro servicio y actualizamos las instancias de base de datos. Quedaba una semana para la pr\u00f3xima ventana, y decidimos simplemente esperar y prepararnos mejor. Durante el tiempo de espera, tomamos precauciones adicionales: para cada shard de PostgreSQL levantamos una r\u00e9plica de respaldo en caso de fallos, para conservar los datos m\u00e1s recientes, y a\u00f1adimos una nueva instancia para cada shard, que deber\u00eda convertirse en una nueva r\u00e9plica en el cl\u00faster Patroni, para no tener que ejecutar el comando de eliminaci\u00f3n de datos. Todo esto ayud\u00f3 a minimizar el riesgo de error.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/29e5c5f50dbfebfe5665fa0aeb009728.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>Reiniciamos nuestro servicio, todo funcion\u00f3 como deb\u00eda, los usuarios continuaron trabajando, pero en los gr\u00e1ficos notamos una carga anormalmente alta en los servidores de Consul.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/af0eef9ac235706c0aac62d2e1ee179d.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>\u00bfPor qu\u00e9 no vimos esto en el entorno de prueba? Este problema ilustra muy bien que debemos seguir el principio de Infraestructura como c\u00f3digo y desarrollar toda la infraestructura, comenzando desde los entornos de prueba y terminando en producci\u00f3n. De lo contrario, es muy f\u00e1cil enfrentarse a un problema como el que tuvimos. \u00bfQu\u00e9 ocurri\u00f3? Consul apareci\u00f3 primero en producci\u00f3n y luego en los entornos de prueba; como resultado, en los entornos de prueba la versi\u00f3n de Consul era m\u00e1s alta que en producci\u00f3n. Precisamente en una de las versiones se solucion\u00f3 una fuga de CPU al trabajar con consul-template. Por lo tanto, simplemente actualizamos Consul, resolviendo as\u00ed el problema.<\/p>\n<h3>Reiniciar el cl\u00faster Patroni<\/h3>\n<p>Sin embargo, nos encontramos con un nuevo problema, del que ni siquiera sospech\u00e1bamos. Al actualizar Consul, simplemente eliminamos el nodo de Consul del cl\u00faster usando el comando consul leave \u2192 Patroni se conecta a otro servidor de Consul \u2192 todo funciona. Pero cuando llegamos a la \u00faltima instancia del cl\u00faster de Consul y le enviamos el comando consul leave, todos los cl\u00fasteres de Patroni simplemente se reiniciaron, y en los registros vimos el siguiente error:<\/p>\n<pre><code class=\"plaintext\">ERROR: get_cluster\nTraceback (&uacute;ltima llamada m&aacute;s reciente):\n...\nRetryFailedError: &#039;Se super&oacute; el plazo de reintento&#039;\nERROR: Error al comunicarse con DCS\n&lt;b&gt;LOG: el sistema de base de datos est&aacute; apagado&lt;\/b&gt;<\/code><\/pre>\n<p>El cl\u00faster de Patroni no pudo obtener informaci\u00f3n sobre su cl\u00faster y se reinici\u00f3.<\/p>\n<p>Para buscar una soluci\u00f3n, nos dirigimos a los creadores de Patroni a trav\u00e9s de un issue en GitHub. Ellos propusieron mejoras a nuestros archivos de configuraci\u00f3n:<\/p>\n<pre><code class=\"python\">consul:\n consul.checks: []\nbootstrap:\n dcs:\n   retry_timeout: 8<\/code><\/pre>\n<p>Pudimos reproducir el problema en el entorno de prueba y probamos esos par\u00e1metros all\u00ed, pero, desafortunadamente, no funcionaron.<\/p>\n<p>El problema sigue sin resolverse. Planeamos probar las siguientes opciones de soluci\u00f3n:<\/p>\n<ul>\n<li>Usar el agente Consul en cada instancia del cl\u00faster Patroni;<\/li>\n<li>Corregir el problema en el c\u00f3digo.<\/li>\n<\/ul>\n<p>Entendemos el origen del error: probablemente la problem\u00e1tica se encuentra en el uso del timeout por defecto, que no se reconfigura a trav\u00e9s del archivo de configuraci\u00f3n. Al eliminar el \u00faltimo servidor Consul del cl\u00faster, todo el cl\u00faster de Consul se congela por m\u00e1s de un segundo, lo que impide que Patroni obtenga el estado del cl\u00faster y reinicia completamente todo el cl\u00faster.<\/p>\n<p>Afortunadamente, no hemos encontrado m\u00e1s errores.<\/p>\n<h2>Conclusiones sobre el uso de Patroni<\/h2>\n<p>Despu\u00e9s de iniciar con \u00e9xito Patroni, agregamos una r\u00e9plica adicional en cada cl\u00faster. Ahora en cada cl\u00faster hay una especie de qu\u00f3rum: un l\u00edder y dos r\u00e9plicas, para asegurar en caso de un split-brain durante el cambio.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3bb4c0495fea274b04edd2e99b59eacb.png\" alt=\"Cl\u00faster PostgreSQL tolerante a fallos + Patroni. Experiencia de implementaci\u00f3n\" \/><\/p>\n<p>Patroni ha estado funcionando en producci\u00f3n durante m\u00e1s de tres meses. En este tiempo, ya nos ha salvado. Recientemente, el l\u00edder de uno de los cl\u00fasteres en AWS fall\u00f3, se activ\u00f3 el failover autom\u00e1tico y los usuarios continuaron trabajando. Patroni cumpli\u00f3 su tarea principal.<\/p>\n<p><b>Un peque\u00f1o resumen sobre el uso de Patroni:<\/b><\/p>\n<ul>\n<li>Facilidad para cambiar la configuraci\u00f3n. Basta con modificar la configuraci\u00f3n en una instancia y se aplicar\u00e1 a todo el cl\u00faster. Si se requiere un reinicio para aplicar la nueva configuraci\u00f3n, Patroni lo notificar\u00e1. Patroni puede reiniciar todo el cl\u00faster con un solo comando, lo que tambi\u00e9n es muy conveniente.<\/li>\n<li>El failover autom\u00e1tico funciona y ya nos ha salvado.<\/li>\n<li>Actualizaci\u00f3n de PostgreSQL sin tiempo de inactividad para la aplicaci\u00f3n. Primero hay que actualizar las r\u00e9plicas a la nueva versi\u00f3n, luego cambiar el l\u00edder en el cl\u00faster de Patroni y actualizar al antiguo l\u00edder. En este proceso se realiza la prueba necesaria de failover autom\u00e1tico.<\/li>\n<\/ul>\n<p>Fuente: <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.2 - 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\/es\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"es_ES\" \/>\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\/es\/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\udd47 Cl\u00faster PostgreSQL + Patroni a prueba de fallos. Experiencia de implementaci\u00f3n | ProHoster","description":"En este art\u00edculo, hablar\u00e9 sobre c\u00f3mo abordamos la cuesti\u00f3n de la alta disponibilidad en PostgreSQL, por qu\u00e9 se volvi\u00f3 importante para nosotros y qu\u00e9 resultado obtuvimos.","canonical_url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/35706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=35706"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/35706\/revisions"}],"predecessor-version":[{"id":172654,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/35706\/revisions\/172654"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/26749"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=35706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=35706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=35706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}