From 3a57f31fbaef4afe02631e2de8ceb1e62a62e42d Mon Sep 17 00:00:00 2001 From: dimka Date: Mon, 31 May 2021 11:02:32 +0300 Subject: [PATCH 01/65] fix baseUrl --- README.md | 2 +- addons/auto-cluster.yaml | 2 +- addons/cluster.json | 2 +- manifest.yaml | 2 +- scripts/jcm | 44 ---------------------------------------- tests/status.yaml | 2 +- 6 files changed, 5 insertions(+), 49 deletions(-) delete mode 100644 scripts/jcm diff --git a/README.md b/README.md index 9035b51..c1b14a0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Within the package, each database container receives the [vertical scaling](http In order to get PostgreSQL Database Replication solution instantly deployed, click the **Deploy to Jelastic** button below and specify your email address within the opened widget. Then choose one of the [Jelastic Public Cloud](https://jelastic.cloud) providers (in case you don’t have an account at the appropriate platform, it will be created automatically) and press **Install**. -[![Deploy](images/deploy-to-jelastic.png)](https://jelastic.com/install-application/?manifest=https://raw.githubusercontent.com/jelastic-jps/postgres/master/manifest.yaml) +[![Deploy](images/deploy-to-jelastic.png)](https://jelastic.com/install-application/?manifest=https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0/manifest.yaml) To install the package manually, log in to the Jelastic dashboard with your credentials and [import](https://docs.jelastic.com/environment-import) link to the [**_manifest.yaml_**](https://github.com/jelastic-jps/postgres/blob/master/manifest.yaml) file (alternatively, you can locate this package via [Jelastic Marketplace](https://docs.jelastic.com/marketplace), *Clusters* section) diff --git a/addons/auto-cluster.yaml b/addons/auto-cluster.yaml index ca2f84c..7103047 100644 --- a/addons/auto-cluster.yaml +++ b/addons/auto-cluster.yaml @@ -1,6 +1,6 @@ type: update id: postgres-master-slave-auto-cluster -baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/master +baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Auto-Cluster diff --git a/addons/cluster.json b/addons/cluster.json index 946b1d8..50793b7 100644 --- a/addons/cluster.json +++ b/addons/cluster.json @@ -1,5 +1,5 @@ { - "jps": "https://raw.githubusercontent.com/jelastic-jps/postgres/master/addons/auto-cluster.yaml", + "jps": "https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0/addons/auto-cluster.yaml", "defaultState": false, "skipOnEnvInstall": true, "nodeGroupData": { diff --git a/manifest.yaml b/manifest.yaml index 872a393..d1607aa 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,7 +1,7 @@ type: install version: 1.7 id: postgres-master-slave -baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/master +baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 homepage: http://docs.jelastic.com/postgresql-database-replication logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Cluster diff --git a/scripts/jcm b/scripts/jcm deleted file mode 100644 index a84e6a7..0000000 --- a/scripts/jcm +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -MASTER_IP=$4 -SLAVE_IP=$2 -DB_PASSWORD=$3 -PGSQL_DATA="/var/lib/pgsql/data" - - -if [ "${1}" == "master" ]; then - #set up master - PGPASSWORD=${DB_PASSWORD} psql -Uwebadmin postgres -c "CREATE USER replication REPLICATION LOGIN CONNECTION LIMIT -1 ENCRYPTED PASSWORD '${DB_PASSWORD}';"; - sudo /etc/init.d/postgresql stop - sed -i "1i host replication replication ${SLAVE_IP}/32 trust" ${PGSQL_DATA}/pg_hba.conf; - sed -i "s|.*wal_level.*|wal_level = hot_standby|g" ${PGSQL_DATA}/postgresql.conf; - sed -i "s|.*max_wal_senders.*|max_wal_senders = 8|g" ${PGSQL_DATA}/postgresql.conf; - sed -i "s|.*wal_keep_segments.*|wal_keep_segments = 32|g" ${PGSQL_DATA}/postgresql.conf; - sed -i "s|.*archive_mode.*|archive_mode = on|g" ${PGSQL_DATA}/postgresql.conf; - sed -i "s|.*archive_command.*| archive_command = 'cd .'|g" ${PGSQL_DATA}/postgresql.conf; - sudo /etc/init.d/postgresql start -fi - -if [ "${1}" == "slave" ]; then - #set up slave - sudo /etc/init.d/postgresql stop - rm -rf ${PGSQL_DATA}; - PGPASSWORD=${DB_PASSWORD} pg_basebackup -h ${MASTER_IP} -D ${PGSQL_DATA} -U replication -v -P; - sed -i "1i host replication replication ${MASTER_IP}/32 trust" ${PGSQL_DATA}/pg_hba.conf; - sed -i "s|.*hot_standby.*|hot_standby = on|g" ${PGSQL_DATA}/postgresql.conf; - sed -i "153 a wal_level = hot_standby" ${PGSQL_DATA}/postgresql.conf; - sed -i "s|.*archive_mode.*|archive_mode = on|g" ${PGSQL_DATA}/postgresql.conf; - sed -i "s|.*archive_command.*| archive_command = 'cd .'|g" ${PGSQL_DATA}/postgresql.conf; - major=$(psql --version | cut -d' ' -f3 | cut -d'.' -f1) - [ "$major" -lt "12" ] && { - sed -i "s|.*max_wal_senders.*|max_wal_senders = 1|g" ${PGSQL_DATA}/postgresql.conf; - echo "standby_mode = on" > ${PGSQL_DATA}/recovery.conf; - echo "primary_conninfo = 'host=${MASTER_IP} port=5432 user=replication password=${DB_PASSWORD}'" >> ${PGSQL_DATA}/recovery.conf; - echo "trigger_file = '/tmp/postgresql.trigger.5432'" >> ${PGSQL_DATA}/recovery.conf; - } || { - sed -i "s|.*max_wal_senders.*|max_wal_senders = 8|g" ${PGSQL_DATA}/postgresql.conf; - echo "primary_conninfo = 'host=${MASTER_IP} port=5432 user=replication password=${DB_PASSWORD}'" >> ${PGSQL_DATA}/postgresql.conf; - echo "promote_trigger_file = '/tmp/postgresql.trigger.5432'" >> ${PGSQL_DATA}/postgresql.conf; - touch ${PGSQL_DATA}/standby.signal - } - sudo /etc/init.d/postgresql start -fi diff --git a/tests/status.yaml b/tests/status.yaml index 1ae5d88..eeb3244 100644 --- a/tests/status.yaml +++ b/tests/status.yaml @@ -2,7 +2,7 @@ type: update id: postgres-master-slave-test name: PostreSQL Primary-Secondary Test homepage: https://www.postgresql.org/ -baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/master +baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 logo: /images/postgres-70x70.png description: Test of Primary-Secondary cluster status From 20b55c4012ef28c76e8453894fa14d0bb4622f00 Mon Sep 17 00:00:00 2001 From: dimka Date: Mon, 31 May 2021 11:23:18 +0300 Subject: [PATCH 02/65] JE-57674 JE-58401 --- addons/auto-cluster.yaml | 70 +++++++++++++++++++++++++++++----------- manifest.yaml | 4 ++- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/addons/auto-cluster.yaml b/addons/auto-cluster.yaml index 7103047..9782cfb 100644 --- a/addons/auto-cluster.yaml +++ b/addons/auto-cluster.yaml @@ -12,14 +12,19 @@ onInstall: init onAfterScaleOut[sqldb]: - getPswd + - getNodes - forEach(event.response.nodes): - initSlave: - id: ${@i.id} + initSecondary: + id: ${@i.id} ip: ${@i.address} + - forEach(nodes.sqldb): + if (${@i.id} != ${nodes.sqldb.master.id}): + cmd[${@i.id}]: jcm updateHbaConf ${globals.nodes_address} ${@i.address} -onBeforeScaleIn[sqldb]: - - forEach(event.response.nodes): - cmd[${nodes.sqldb.master.id}]: jcm removeReplicaHost ${@i.address} +onAfterScaleIn[sqldb]: + - getNodes + - forEach(nodes.sqldb): + cmd[${@i.id}]: jcm updateHbaConf ${globals.nodes_address} ${@i.address} onAfterClone: - script: delete MANIFEST.id; return {result:0, jps:MANIFEST}; @@ -27,7 +32,13 @@ onAfterClone: envName: ${event.response.env.envName} settings: nodeGroup: ${settings.nodeGroup} - clone: true + clone: true + +onBeforeMigrate: + if (${env.status} != 1): + stopEvent: + type: warning + message: Migration of stopped PostgreSQL Primary-Secondary Auto-Cluster is not supported. onAfterMigrate: init: @@ -39,6 +50,7 @@ actions: # vars: {"KEY_PASS":"${fn.password}"} - if (${settings.clone:false} || ${this.update:false}): - cmd[${nodes.sqldb.master.id}]: jcm removeAllReplicaHosts + - getNodes - forEach(nodes.sqldb): - if (${@i.id} != ${nodes.sqldb.master.id}): - cmd[${nodes.sqldb.master.id}]: |- @@ -47,32 +59,54 @@ actions: - cmd[${@i.id}]: |- #jcm updateReplicaHost ${nodes.sqldb.master.address} &>> /var/log/run.log jcm updatePrimaryConnInfo &>> /var/log/run.log + jcm updateHbaConf ${globals.nodes_address} ${@i.address} sudo jem service restart - else: - setNodeDisplayName[${nodes.sqldb.master.id}]: Primary - - getPswd + - initPrimary + - getNodes - forEach(nodes.sqldb): - if (${@i.id} != ${nodes.sqldb.master.id}): - initSlave: + - if (${@i.id} != ${nodes.sqldb.master.id}): + - initSecondary: id: ${@i.id} ip: ${@i.address} - + - cmd[${@i.id}]: |- + jcm updateHbaConf ${globals.nodes_address} ${@i.address} + sudo jem service reload + + initPrimary: + - cmd[${nodes.sqldb.master.id}]: jcm initPrimary &>> /var/log/run.log + - getPswd + getPswd: - - cmd[${nodes.sqldb.master.id}]: |- - jcm initMaster &>> /var/log/run.log - jcm getPswd + - cmd[${nodes.sqldb.master.id}]: jcm getPswd - setGlobals: - pswd: ${response.out} - - initSlave: + pswd: ${response.out} + + initSecondary: - setNodeDisplayName[${this.id}]: Secondary - cmd[${nodes.sqldb.master.id}]: |- jcm addReplicaHost ${this.ip} &>> /var/log/run.log sudo jem service reload - cmd[${this.id}]: |- jcm setPswd ${globals.pswd} - jcm initSlave ${nodes.sqldb.master.address} &>> /var/log/run.log - + jcm initSecondary &>> /var/log/run.log + + getNodes: + - script: | + var resp = jelastic.env.control.GetEnvInfo('${env.envName}', session); + if (resp.result != 0) return resp; + var nodes_address = []; + for (var l = 0, k = resp.nodes; l < k.length; l++) { + if (k[l].nodeGroup == 'sqldb') { + nodes_address.push(k[l].address); + } + } + return { result: 0, nodes_address: nodes_address.join(",") } + - setGlobals: + nodes_address: ${response.nodes_address} + + startPage: ${nodes.sqldb.master.url} success: | **Admin Panel**: [${nodes.sqldb.master.url}](${nodes.sqldb.master.url}) diff --git a/manifest.yaml b/manifest.yaml index d1607aa..af4d2f8 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -29,7 +29,9 @@ settings: caption: PostgreSQL 11 - value: postgres12 caption: PostgreSQL 12 - default: postgres12 + - value: postgres13 + caption: PostgreSQL 13 + default: postgres13 nodes: cloudlets: 32 From 2b8f9f56852befc6e77b573e9f3685668424bef5 Mon Sep 17 00:00:00 2001 From: dimka Date: Fri, 5 Nov 2021 11:29:38 +0200 Subject: [PATCH 03/65] change baseUrl --- addons/auto-cluster.yaml | 2 +- manifest.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/auto-cluster.yaml b/addons/auto-cluster.yaml index 9782cfb..6b1f6fb 100644 --- a/addons/auto-cluster.yaml +++ b/addons/auto-cluster.yaml @@ -1,6 +1,6 @@ type: update id: postgres-master-slave-auto-cluster -baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 +baseUrl: https://raw.githubusercontent.com/dimkadt/postgres/v2.0.0 logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Auto-Cluster diff --git a/manifest.yaml b/manifest.yaml index af4d2f8..8cd59f9 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,7 +1,7 @@ type: install version: 1.7 id: postgres-master-slave -baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 +baseUrl: https://raw.githubusercontent.com/dimkadt/postgres/v2.0.0 homepage: http://docs.jelastic.com/postgresql-database-replication logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Cluster From e7b3e20a11b20b50a6a2526f3f6202cd602f143e Mon Sep 17 00:00:00 2001 From: dimka Date: Fri, 5 Nov 2021 11:31:17 +0200 Subject: [PATCH 04/65] JE-60446 [PostgreSQL cluster] - no password in email after clone --- addons/auto-cluster.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/auto-cluster.yaml b/addons/auto-cluster.yaml index 6b1f6fb..967936e 100644 --- a/addons/auto-cluster.yaml +++ b/addons/auto-cluster.yaml @@ -50,6 +50,7 @@ actions: # vars: {"KEY_PASS":"${fn.password}"} - if (${settings.clone:false} || ${this.update:false}): - cmd[${nodes.sqldb.master.id}]: jcm removeAllReplicaHosts + - getPswd - getNodes - forEach(nodes.sqldb): - if (${@i.id} != ${nodes.sqldb.master.id}): From 544e05682d5bbc6dbabce9a6cf4a60b6219e3af4 Mon Sep 17 00:00:00 2001 From: dimka Date: Fri, 5 Nov 2021 14:45:07 +0200 Subject: [PATCH 05/65] Add PostgreSQL 14 --- manifest.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manifest.yaml b/manifest.yaml index 8cd59f9..a0c0963 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -31,7 +31,9 @@ settings: caption: PostgreSQL 12 - value: postgres13 caption: PostgreSQL 13 - default: postgres13 + - value: postgres14 + caption: PostgreSQL 14 + default: postgres14 nodes: cloudlets: 32 From 3733e67cae30132c87a0286d2dc9547acbf029e9 Mon Sep 17 00:00:00 2001 From: "alexey.lazarenko" Date: Thu, 20 Jan 2022 14:49:45 +0200 Subject: [PATCH 06/65] JE-61147 Deprecate PostgreSQL 9 --- manifest.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/manifest.yaml b/manifest.yaml index af4d2f8..7e50b39 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -21,8 +21,6 @@ settings: name: nodeType type: list values: - - value: postgres9 - caption: PostgreSQL 9 - value: postgres10 caption: PostgreSQL 10 - value: postgres11 From 90b933f917a0ccb4fa45609697c0690abce739d8 Mon Sep 17 00:00:00 2001 From: Dmitry Tsurko Date: Wed, 9 Feb 2022 12:31:21 +0200 Subject: [PATCH 07/65] Update addons/auto-cluster.yaml Co-authored-by: Slava Katiukha --- addons/auto-cluster.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/auto-cluster.yaml b/addons/auto-cluster.yaml index 967936e..c4adf74 100644 --- a/addons/auto-cluster.yaml +++ b/addons/auto-cluster.yaml @@ -1,6 +1,6 @@ type: update id: postgres-master-slave-auto-cluster -baseUrl: https://raw.githubusercontent.com/dimkadt/postgres/v2.0.0 +baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Auto-Cluster From 5eeea0fc57bf0be6e1ce9c654b408dc6b3fa0926 Mon Sep 17 00:00:00 2001 From: Dmitry Tsurko Date: Wed, 9 Feb 2022 12:31:29 +0200 Subject: [PATCH 08/65] Update manifest.yaml Co-authored-by: Slava Katiukha --- manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.yaml b/manifest.yaml index a0c0963..4865765 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,7 +1,7 @@ type: install version: 1.7 id: postgres-master-slave -baseUrl: https://raw.githubusercontent.com/dimkadt/postgres/v2.0.0 +baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 homepage: http://docs.jelastic.com/postgresql-database-replication logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Cluster From c3ebb903673d1b69cbcea79d834d766abcd617b2 Mon Sep 17 00:00:00 2001 From: "alexey.lazarenko" Date: Mon, 21 Nov 2022 10:09:14 +0200 Subject: [PATCH 09/65] deprecated 10 version --- manifest.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/manifest.yaml b/manifest.yaml index 933b2d9..9d6930f 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -21,8 +21,6 @@ settings: name: nodeType type: list values: - - value: postgres10 - caption: PostgreSQL 10 - value: postgres11 caption: PostgreSQL 11 - value: postgres12 From dfac37833a0e9e67941278f6ed9115bab524ee67 Mon Sep 17 00:00:00 2001 From: DmytroZubelevych <31444413+DmytroZubelevych@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:43:22 +0200 Subject: [PATCH 10/65] Update manifest.yaml --- manifest.yaml | 62 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/manifest.yaml b/manifest.yaml index 9d6930f..129e026 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,7 +1,7 @@ type: install version: 1.7 id: postgres-master-slave -baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 +baseUrl: https://raw.githubusercontent.com/DmytroZubelevych/postgres/v2.0.0 homepage: http://docs.jelastic.com/postgresql-database-replication logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Cluster @@ -17,19 +17,25 @@ categories: settings: fields: - caption: Version - name: nodeType - type: list - values: - - value: postgres11 - caption: PostgreSQL 11 - - value: postgres12 - caption: PostgreSQL 12 - - value: postgres13 - caption: PostgreSQL 13 - - value: postgres14 - caption: PostgreSQL 14 - default: postgres14 + - caption: Version + name: nodeType + type: list + values: + - value: postgres10 + caption: PostgreSQL 10 + - value: postgres11 + caption: PostgreSQL 11 + - value: postgres12 + caption: PostgreSQL 12 + - value: postgres13 + caption: PostgreSQL 13 + - value: postgres14 + caption: PostgreSQL 14 + default: postgres14 + - type: toggle + caption: Pgpool-II enabled + name: is_pgpool2 + value: true nodes: cloudlets: 32 @@ -37,17 +43,23 @@ nodes: scalingMode: STATELESS nodeType: ${settings.nodeType} password: ${fn.password} - cluster: true - skipNodeEmails: true - + cluster: + is_pgpool2: ${settings.is_pgpool2} + +onInstall: + - cmd[${nodes.sqldb.master.id}]: jcm getPswd + - setGlobals: + pswd: ${response.out} + - if ('${settings.is_pgpool2}' == 'true'): + - cmd[${nodes.pgpool.master.id}]: cat /var/lib/pgsql/.pcppass |awk -F ':' '{print $4}' + - setGlobals: + pgpoolPasswd: '${response.out}' + successPath: /text/success-pgpool.md?_r=${fn.random} + - else: + - setGlobals: + successPath: /text/success.md?_r=${fn.random} + startPage: ${nodes.sqldb.master.url} success: email: false - text: | - **Admin Panel**: [${nodes.sqldb.master.url}](${nodes.sqldb.master.url}) - **User**: webadmin - **Password**: ${nodes.sqldb.password} - - * [Database Replication with PostgreSQL](https://docs.jelastic.com/postgresql-database-replication/) - * [Remote Access to PostgreSQL](https://docs.jelastic.com/remote-access-postgres/) - * [Import and Export Dump to PostgreSQL](https://docs.jelastic.com/dump-postgres/) + text: ${globals.successPath} From 0fa45d91360329fc796c8658869c9080720243af Mon Sep 17 00:00:00 2001 From: DmytroZubelevych <31444413+DmytroZubelevych@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:43:38 +0200 Subject: [PATCH 11/65] Update manifest.yaml --- manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.yaml b/manifest.yaml index 129e026..6d4746a 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,7 +1,7 @@ type: install version: 1.7 id: postgres-master-slave -baseUrl: https://raw.githubusercontent.com/DmytroZubelevych/postgres/v2.0.0 +baseUrl: https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0 homepage: http://docs.jelastic.com/postgresql-database-replication logo: /images/postgres-70x70.png name: PostgreSQL Primary-Secondary Cluster From 8090f6b4ef7eae36810b69ec1ff9e9af23944cbf Mon Sep 17 00:00:00 2001 From: DmytroZubelevych <31444413+DmytroZubelevych@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:54:50 +0200 Subject: [PATCH 12/65] Update auto-cluster.yaml --- addons/auto-cluster.yaml | 184 +++++++++++++++++++++++++++++++++++---- 1 file changed, 167 insertions(+), 17 deletions(-) diff --git a/addons/auto-cluster.yaml b/addons/auto-cluster.yaml index c4adf74..98df234 100644 --- a/addons/auto-cluster.yaml +++ b/addons/auto-cluster.yaml @@ -10,7 +10,20 @@ nodeGroupAlias: onInstall: init +globals: + pgpoolintpass: ${settings.pgpoolintpass:[fn.password]} + postgresqlConf: '/var/lib/pgsql/data/postgresql.conf' + +onAfterResetServicePassword[sqldb]: + - copyPcpassFile + +onAfterResetNodePassword[sqldb]: + - copyPcpassFile + onAfterScaleOut[sqldb]: + - copyPcpassFile + - if ('${settings.is_pgpool2}' == 'true'): + - adjustConfigs4PgpoolUser - getPswd - getNodes - forEach(event.response.nodes): @@ -20,18 +33,66 @@ onAfterScaleOut[sqldb]: - forEach(nodes.sqldb): if (${@i.id} != ${nodes.sqldb.master.id}): cmd[${@i.id}]: jcm updateHbaConf ${globals.nodes_address} ${@i.address} + - if ('${settings.is_pgpool2}' == 'true'): + - forEach(event.response.nodes): + - addPgNodesToPgPool: + pgaddress: ${@i.address} + - forEach(pgpoolnode:nodes.pgpool): + - generateAndTransferSSHKeys: + id: ${@pgpoolnode.id} + - addToKnownHosts + +onBeforeScaleIn[pgpool]: + - forEach(pgpoolnode:event.response.nodes): + - removeWatchdogConfig: + pgpoolAddress: ${@pgpoolnode.address} + - cmd[sqldb]: sed -ci -e '/${@pgpoolnode.address}/d' /var/lib/pgsql/data/pg_hba.conf; + - cmd[sqldb,pgpool]: jem service restart + +onAfterScaleOut[pgpool]: + - preparePgpoolNodes + - cmd[${event.response.nodes.join(id,)}]: rm -f ~/.ssh/id_rsa ~/.ssh/id_rsa.pub + - forEach(pgpoolnode:event.response.nodes): + - generateAndTransferSSHKeys: + id: ${@pgpoolnode.id} + - cmd[pgpool]: jcm enableWatchdog + user: root + - forEach(pgpoolnode:nodes.pgpool): + - cmd[sqldb]: grep -q '${@pgpoolnode.address}' /var/lib/pgsql/data/pg_hba.conf || sed -ci -e '1i host all pgpool ${@pgpoolnode.address}/32 trust' /var/lib/pgsql/data/pg_hba.conf; + - getPgPoolNodesCount + - setPgpoolNodeId: + pgPoolNode: ${@pgpoolnode.id} + - addWatchdogConfig: + pgpoolAddress: ${@pgpoolnode.address} + - addToKnownHosts onAfterScaleIn[sqldb]: - getNodes - forEach(nodes.sqldb): cmd[${@i.id}]: jcm updateHbaConf ${globals.nodes_address} ${@i.address} + - if ('${settings.is_pgpool2}' == 'true'): + - forEach(event.response.nodes): + - removePgNodesFromPgPool: + pgaddress: ${@i.address} + +onAfterScaleIn[pgpool]: + - if (nodes.pgpool.length == 1): + - cmd[pgpool]: jcm disableWatchdog + - removeWatchdogConfig: + pgpoolAddress: ${nodes.pgpool.master.address} + - cmd[pgpool]: |- + rm -f /etc/pgpool-II/pgpool_node_id + sed -ci -e 's/use_watchdog/#use_watchdog/' /etc/pgpool-II/pgpool.conf + jem service restart onAfterClone: - script: delete MANIFEST.id; return {result:0, jps:MANIFEST}; - install: ${response.jps} envName: ${event.response.env.envName} - settings: - nodeGroup: ${settings.nodeGroup} + settings: + pgpoolintpass: ${settings.pgpoolintpass} + nodeGroup: ${settings.nodeGroup} + is_pgpool2: ${settings.is_pgpool2} clone: true onBeforeMigrate: @@ -46,8 +107,6 @@ onAfterMigrate: actions: init: - #- env.control.AddContainerEnvVars[sqldb]: - # vars: {"KEY_PASS":"${fn.password}"} - if (${settings.clone:false} || ${this.update:false}): - cmd[${nodes.sqldb.master.id}]: jcm removeAllReplicaHosts - getPswd @@ -58,10 +117,12 @@ actions: jcm addReplicaHost ${@i.address} &>> /var/log/run.log sudo jem service reload - cmd[${@i.id}]: |- - #jcm updateReplicaHost ${nodes.sqldb.master.address} &>> /var/log/run.log - jcm updatePrimaryConnInfo &>> /var/log/run.log + jcm updatePrimaryConnInfo &>>/var/log/run.log jcm updateHbaConf ${globals.nodes_address} ${@i.address} sudo jem service restart + - if ('${settings.is_pgpool2}' == 'true'): + - cmd[pgpool]: jcm cleanupNodesFromPgpool2Conf &>>/var/log/run.log + user: root - else: - setNodeDisplayName[${nodes.sqldb.master.id}]: Primary - initPrimary @@ -74,6 +135,77 @@ actions: - cmd[${@i.id}]: |- jcm updateHbaConf ${globals.nodes_address} ${@i.address} sudo jem service reload + - copyPcpassFile + - if ('${settings.is_pgpool2}' == 'true'): + - adjustConfigs4PgpoolUser + - cmd[${nodes.sqldb.master.id}]: |- + psql -U webadmin -d postgres -c "CREATE USER pgpool REPLICATION LOGIN CONNECTION LIMIT -1 ENCRYPTED PASSWORD '${globals.pgpoolintpass}';" + - preparePgpoolNodes + - forEach(nodes.sqldb): + - addPgNodesToPgPool: + pgaddress: ${@i.address} + - forEach(pgpoolnode:nodes.pgpool): + - generateAndTransferSSHKeys: + id: ${@pgpoolnode.id} + - cmd[sqldb]: sed -ci -e '1i host all pgpool ${@pgpoolnode.address}/32 trust' /var/lib/pgsql/data/pg_hba.conf; + - if (nodes.pgpool.length > 1): + - cmd[pgpool]: jcm enableWatchdog + user: root + - forEach(pgpoolnode:nodes.pgpool): + - getPgPoolNodesCount + - setPgpoolNodeId: + pgPoolNode: ${@pgpoolnode.id} + - addWatchdogConfig: + pgpoolAddress: ${@pgpoolnode.address} + - cmd[sqldb]: jem service restart + - cmd[pgpool]: jem service restart + - cmd[${nodes.pgpool.master.id}]: cat /var/lib/pgsql/.pcppass |awk -F ':' '{print $4}' + - setGlobals: + pgpoolPasswd: '${response.out}' + successPath: /text/success-pgpool.md?_r=${fn.random} + - else: + - setGlobals: + successPath: /text/success.md?_r=${fn.random} + + addToKnownHosts: + - forEach(pgnode:nodes.sqldb): + - cmd[pgpool]: |- + ssh-keygen -R ${@pgnode.address} + ssh-keyscan ${@pgnode.address} 2>&1 1>> ~/.ssh/known_hosts + + copyPcpassFile: + - cmd[${nodes.sqldb.master.id}]: cat /var/lib/pgsql/.pgpass + - cmd[sqldb]: echo '${response.out}' > /var/lib/pgsql/.pgpass + + generateAndTransferSSHKeys: + - cmd[${this.id}]: |- + [ -f "/var/lib/pgsql/.ssh/id_rsa.pub" ] || ssh-keygen -q -t rsa -N '' <<< $'\ny' + - cmd[${this.id}]: |- + cat /var/lib/pgsql/.ssh/id_rsa.pub + - cmd[sqldb]: + grep -q '${response.out}' /var/lib/pgsql/.ssh/authorized_keys || echo '${response.out}' >> /var/lib/pgsql/.ssh/authorized_keys + + preparePgpoolNodes: + - cmd[pgpool]: |- + pg_md5 --md5auth --username=pgpool ${globals.pgpoolintpass} + chown -R postgres:postgres /etc/pgpool-II + user: root + + adjustConfigs4PgpoolUser: + - cmd[sqldb]: |- + jem service stop; + source /etc/jelastic/metainf.conf; + if [ "$COMPUTE_TYPE_VERSION" -ge "14" ] ; then + sed -ci -e 's|#password_encryption.*|password_encryption = md5|' ${globals.postgresqlConf} + fi + if [ "$COMPUTE_TYPE_VERSION" -ge "13" ] ; then + echo 'wal_keep_size = 1024' >> ${globals.postgresqlConf} + else + echo "wal_keep_segments = 128" >> ${globals.postgresqlConf} + fi + grep -q "^wal_log_hints" ${globals.postgresqlConf} || echo 'wal_log_hints = on' >> ${globals.postgresqlConf}; + jem service start; + user: root initPrimary: - cmd[${nodes.sqldb.master.id}]: jcm initPrimary &>> /var/log/run.log @@ -107,15 +239,33 @@ actions: - setGlobals: nodes_address: ${response.nodes_address} - + addPgNodesToPgPool: + - forEach(pgpoolnode:nodes.pgpool): + - cmd[${@pgpoolnode.id}]: jcm addPgNodeToPgpool2Conf ${this.pgaddress} &>>/var/log/run.log + + removePgNodesFromPgPool: + - forEach(pgpoolnode:nodes.pgpool): + - cmd[${@pgpoolnode.id}]: jcm removePgNodeFromPgpool2Conf ${this.pgaddress} &>>/var/log/run.log + user: root + + getPgPoolNodesCount: + - cmd[${nodes.pgpool.master.id}]: grep '^wd_port[0-9]*' /etc/pgpool-II/pgpool.conf|wc -l + - setGlobals: + pgPoolCount: ${response.out} + + setPgpoolNodeId: + - cmd[${this.pgPoolNode}]: let PGPOOLCOUNT='${globals.pgPoolCount}+1'; [ -f "/etc/pgpool-II/pgpool_node_id" ] || jcm setPgpoolNodeId $PGPOOLCOUNT + user: root + + addWatchdogConfig: + - cmd[pgpool]: let PGPOOLCOUNT='${globals.pgPoolCount}+1'; jcm addWatchdogConfig $PGPOOLCOUNT ${this.pgpoolAddress} + user: root + + removeWatchdogConfig: + - cmd[pgpool]: jcm removeWatchdogConfig ${this.pgpoolAddress} + user: root + startPage: ${nodes.sqldb.master.url} -success: | - **Admin Panel**: [${nodes.sqldb.master.url}](${nodes.sqldb.master.url}) - **User**: webadmin - **Password**: ${globals.pswd} - - * [Database Replication with PostgreSQL](https://docs.jelastic.com/postgresql-database-replication/) - * [Remote Access to PostgreSQL](https://docs.jelastic.com/remote-access-postgres/) - * [Import and Export Dump to PostgreSQL](https://docs.jelastic.com/dump-postgres/) - - +success: + email: ${globals.successPath} + text: ${globals.successPath} From 7e0190ce33d01301e0d147bf700b1d0ce997b26c Mon Sep 17 00:00:00 2001 From: DmytroZubelevych <31444413+DmytroZubelevych@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:55:27 +0200 Subject: [PATCH 13/65] Update cluster.json --- addons/cluster.json | 76 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/addons/cluster.json b/addons/cluster.json index 50793b7..cd8fa0f 100644 --- a/addons/cluster.json +++ b/addons/cluster.json @@ -1,20 +1,62 @@ { - "jps": "https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0/addons/auto-cluster.yaml", - "defaultState": false, - "skipOnEnvInstall": true, - "nodeGroupData": { - "scalingMode": "STATELESS", - "skipNodeEmails": true, - "isRedeploySupport": true, - "isResetServicePassword": "NODEGROUP" + "convertable": false, + "skipOnEnvInstall": true, + "jps": "https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0/addons/auto-cluster.yaml", + "description": "

Primary-Secondary with Scalable Secondaries

Pre-configured PostgreSQL database cluster with primary-secondary replication. New LB and DB nodes are automatically added into the cluster upon scaling.
", + "defaultState": false, + "nodeGroupData": { + "scalingMode": "STATELESS", + "skipNodeEmails": true, + "isRedeploySupport": true, + "isResetServicePassword": "NODEGROUP" + }, + "settings": { + "data": { + "is_pgpool2": false }, - "validation": { - "minCount": 2, - "minCloudlets": 4, - "scalingMode": "STATELESS" - }, - "recommended": { - "cloudlets": 32 - }, - "description": "

Primary-Secondary with Scalable Secondaries

Pre-configured PostgreSQL database cluster with primary-secondary replication. New nodes are automatically added into the cluster as secondaries. Learn More" + "fields": [ + { + "type": "toggle", + "caption": "Pgpool-II", + "name": "is_pgpool2", + "value": false + } + ] + }, + "validation": { + "minCount": 2, + "minCloudlets": 4, + "scalingMode": "STATELESS", + "rules": [ + { + "is_pgpool2": { + "true": { + "setGlobals": { + "pgpool2Count": 1 + } + } + } + } + ] + }, + "requires": [ + "pgpool2" + ], + "extraNodes": [ + { + "nodeGroup": "pgpool", + "nodeType": "pgpool2", + "count": "${globals.pgpool2Count:0}", + "isClusterSupport": false, + "isDeploySupport": false, + "isClusterDependency": false, + "isResetServicePassword": "NODEGROUP", + "applyQuotas": true, + "validation": { + "minCount": 1, + "maxCount": 3, + "scalingMode": "STATEFUL" + } + } + ] } From d63bfa25f78e76520e1ac7c13e7db110e91fa74c Mon Sep 17 00:00:00 2001 From: DmytroZubelevych <31444413+DmytroZubelevych@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:56:16 +0200 Subject: [PATCH 14/65] Create success.md --- text/success.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 text/success.md diff --git a/text/success.md b/text/success.md new file mode 100644 index 0000000..1b93e65 --- /dev/null +++ b/text/success.md @@ -0,0 +1,6 @@ +**Admin Panel**: [${nodes.sqldb.master.url}](${nodes.sqldb.master.url}) +**User**: webadmin +**Password**: ${nodes.sqldb.password} +* [Database Replication with PostgreSQL](https://docs.jelastic.com/postgresql-database-replication/) +* [Remote Access to PostgreSQL](https://docs.jelastic.com/remote-access-postgres/) +* [Import and Export Dump to PostgreSQL](https://docs.jelastic.com/dump-postgres/) From 2cf9cbdfaffb567c38733ac28d7acb2fabaefbe8 Mon Sep 17 00:00:00 2001 From: DmytroZubelevych <31444413+DmytroZubelevych@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:56:58 +0200 Subject: [PATCH 15/65] Create success-pgpool.md --- text/success-pgpool.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 text/success-pgpool.md diff --git a/text/success-pgpool.md b/text/success-pgpool.md new file mode 100644 index 0000000..b919d88 --- /dev/null +++ b/text/success-pgpool.md @@ -0,0 +1,14 @@ +**Admin Panel**: [${nodes.sqldb.master.url}](${nodes.sqldb.master.url}) +**User**: webadmin +**Password**: ${globals.pswd} + +**You can connect to PostgreSQL cluster through the Pgpool-II leader node using the data below**: + +**Pgpool-II Leader Node**: node${nodes.pgpool.master.id}-${env.domain}:5432 +**PgpoolAdmin Url**: [${nodes.pgpool.master.url}](${nodes.pgpool.master.url}) +**PgpoolAdmin User**: postgres +**PgpoolAdmin Password**: ${globals.pgpoolPasswd} + +* [Database Replication with PostgreSQL](https://docs.jelastic.com/postgresql-database-replication/) +* [Remote Access to PostgreSQL](https://docs.jelastic.com/remote-access-postgres/) +* [Import and Export Dump to PostgreSQL](https://docs.jelastic.com/dump-postgres/) From afa79de71e0ad86b9d7664dbea768590ae517782 Mon Sep 17 00:00:00 2001 From: DmytroZubelevych <31444413+DmytroZubelevych@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:26:25 +0200 Subject: [PATCH 16/65] Update manifest.yaml --- manifest.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/manifest.yaml b/manifest.yaml index 6d4746a..04c4da8 100755 --- a/manifest.yaml +++ b/manifest.yaml @@ -35,6 +35,7 @@ settings: - type: toggle caption: Pgpool-II enabled name: is_pgpool2 + tooltip:

Pgpool-II Load Balancer

Scalable and Highly Available load balancer layer to distribute requests and manage PostgreSQL replication topology. New LB and DB nodes are automatically added into the cluster upon scaling.
value: true nodes: From 7e758080b5414ba63bb04c30da353458752c6dff Mon Sep 17 00:00:00 2001 From: vlobzakov Date: Tue, 13 Dec 2022 15:13:44 +0200 Subject: [PATCH 17/65] JE-64848 Readme created --- README.md | 64 +- README.md.backup | 79 + images/deploy-to-cloud.png | Bin 0 -> 2701 bytes images/pgadmin.png | Bin 0 -> 86470 bytes images/pgpool-admin.png | Bin 0 -> 63032 bytes ...tgres-single-region-big-tip-black-font.svg | 1900 +++++++++++++++++ images/phppgadmin.png | Bin 0 -> 29614 bytes .../postgresql-replication-installation.png | Bin 104641 -> 62304 bytes ...postgresql-replication-success-message.png | Bin 67656 -> 60391 bytes 9 files changed, 2034 insertions(+), 9 deletions(-) create mode 100644 README.md.backup create mode 100644 images/deploy-to-cloud.png create mode 100644 images/pgadmin.png create mode 100644 images/pgpool-admin.png create mode 100644 images/pgpool-postgres-single-region-big-tip-black-font.svg create mode 100644 images/phppgadmin.png diff --git a/README.md b/README.md index c1b14a0..bf52ae9 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,34 @@ # PostgreSQL Database Replication -Basic ready-to-use PostgreSQL cluster, which implements asynchronous primary-secondary data replication within a pair of preconfigured database containers. +Basic ready-to-use PostgreSQL cluster, which implements asynchronous primary-secondary data replication within a pair of preconfigured database containers and load balancing. ## Package Implementation Specifics -The presented PostgreSQL Replication solution is built upon Jelastic certified stack template for **PostgreSQL**. It operates two database containers (primary and secondary, one per role) and makes data from primary DB server to be asynchronously replicated to a standby one. +The presented PostgreSQL Replication solution is built upon Virtuozzo Application Platform certified stack templates: + - for **[PostgreSQL](https://www.postgresql.org/)** database + - for **[Pgpool-II](https://www.pgpool.net/mediawiki/index.php/Main_Page)** load balancer + + By default, package operates two database containers (primary and secondary, one per role) and makes data from primary DB server to be asynchronously replicated to a standby one. + In front of the cluster a scalable load balancer layer of Pgpool-II node can be added which provides load-balancing, monitoring and management of database cluster.

- +

-Within the package, each database container receives the [vertical scaling](https://docs.jelastic.com/automatic-vertical-scaling) up to **32 dynamic cloudlets** (or 4 GiB of RAM and 12.8 GHz of CPU) that are provided dynamically based on the incoming load. Subsequently, you can change the resource allocation limit by following the above-linked guide. +Within the package, each database container receives the [vertical scaling](https://www.virtuozzo.com/application-platform-docs/automatic-vertical-scaling/) up to **32 dynamic cloudlets** (or 4 GiB of RAM and 12.8 GHz of CPU) that are provided dynamically based on the incoming load. And for the load balancer node the **6 dynamic cloudlets** provided by default. Subsequently, you can change the resource allocation limit by following the above-linked guide. ## How to Install PostgreSQL Database Replication Package -In order to get PostgreSQL Database Replication solution instantly deployed, click the **Deploy to Jelastic** button below and specify your email address within the opened widget. Then choose one of the [Jelastic Public Cloud](https://jelastic.cloud) providers (in case you don’t have an account at the appropriate platform, it will be created automatically) and press **Install**. +In order to get PostgreSQL Database Replication solution instantly deployed, click the **Deploy to Cloud** button below and specify your email address within the opened widget. Then choose one of the [Virtuozzo Public Cloud](https://www.virtuozzo.com/application-platform-partners/) providers (in case you don’t have an account at the appropriate platform, it will be created automatically) and press **Install**. -[![Deploy](images/deploy-to-jelastic.png)](https://jelastic.com/install-application/?manifest=https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0/manifest.yaml) +[![Deploy](images/deploy-to-cloud.png)](https://jelastic.com/install-application/?manifest=https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0/manifest.yaml) -To install the package manually, log in to the Jelastic dashboard with your credentials and [import](https://docs.jelastic.com/environment-import) link to the [**_manifest.yaml_**](https://github.com/jelastic-jps/postgres/blob/master/manifest.yaml) file (alternatively, you can locate this package via [Jelastic Marketplace](https://docs.jelastic.com/marketplace), *Clusters* section) +To install the package manually, log in to the Jelastic dashboard with your credentials and [import](https://www.virtuozzo.com/application-platform-docs/environment-import/) link to the [**_manifest.yaml_**](https://github.com/jelastic-jps/postgres/blob/master/manifest.yaml) file (alternatively, you can locate this package via [Jelastic Marketplace](https://www.virtuozzo.com/application-platform-docs/marketplace/), *Clusters* section) ![postgresql-replication-installation](images/postgresql-replication-installation.png) -Within the opened installation window, type *Environment* name and optional *Display Name* ([environment alias](https://docs.jelastic.com/environment-aliases)). Also, select the preferable [*Region*](https://docs.jelastic.com/environment-regions) (if several ones are available) and click **Install**. +Within the opened installation window, choose the PostgreSQL database version among available ones, type *Environment* name and optional *Display Name* ([environment alias](https://www.virtuozzo.com/application-platform-docs/environment-aliases/). Also, select the preferable [*Region*](https://www.virtuozzo.com/application-platform-docs/environment-regions/) (if several ones are available) and click **Install**. +If required you may disable Pgpool-II load balancer layer with respective toggle. Wait a few minutes for Jelastic to prepare your environment and set up the required replication configurations. When finished, you’ll be shown the appropriate notification with data for PostgreSQL administration interface access. @@ -30,4 +36,44 @@ Wait a few minutes for Jelastic to prepare your environment and set up the requi This information will be also duplicated to you via email. -To find more details on PostgreSQL Replication package installation and use, refer to the [article](http://blog.jelastic.com/2017/05/25/master-slave-postgresql-replication-automatic-installation/). +### Cluster Entry Point + +In case of no Pgpool-II nodes were added to cluster topology, use Primary node to access the cluster. And if the load balancing layer was deployed in front of the db cluster you may use any of Pgpool-II nodes as the entry point. + +### Cluster Management + +In VAP the PostgreSQL cluster components can be either managed via [CLI](https://www.virtuozzo.com/application-platform-docs/ssh-access/) or UI. + +#### Database Management + +Database nodes have a built-in management administration panel phpPgAdmin. Use the only one on Primary node. + +

+ +

+ +If required, the separate node can be installed with more advanced PostgreSQL database management software [pgAdmin4](https://www.pgadmin.org/) via importing [manifest](https://github.com/jelastic-jps/pgadmin/blob/master/manifest.yaml) from VAP collection. + +

+ +

+ +#### Pgpool-II Management + +Pgpool-II nodes can be also managed via user-friendly built-in Administration Panel [pgpoolAdmin](https://www.pgpool.net/docs/pgpoolAdmin/index_en.html). + +

+ +

+ +Pgpool-II admin panel provides an ability to tune: + - load balancing and even at database level. It means that you can specify how the requests to every database should be processed and balanced + - connnection pools + - logging + - replication + - debugging + - failover and failback + - etc. + + +To find more details on PostgreSQL Replication package installation and use, refer to the [article](https://www.virtuozzo.com/company/blog/postgresql-auto-clustering-master-slave-replication/). diff --git a/README.md.backup b/README.md.backup new file mode 100644 index 0000000..bf52ae9 --- /dev/null +++ b/README.md.backup @@ -0,0 +1,79 @@ +# PostgreSQL Database Replication + +Basic ready-to-use PostgreSQL cluster, which implements asynchronous primary-secondary data replication within a pair of preconfigured database containers and load balancing. + +## Package Implementation Specifics + +The presented PostgreSQL Replication solution is built upon Virtuozzo Application Platform certified stack templates: + - for **[PostgreSQL](https://www.postgresql.org/)** database + - for **[Pgpool-II](https://www.pgpool.net/mediawiki/index.php/Main_Page)** load balancer + + By default, package operates two database containers (primary and secondary, one per role) and makes data from primary DB server to be asynchronously replicated to a standby one. + In front of the cluster a scalable load balancer layer of Pgpool-II node can be added which provides load-balancing, monitoring and management of database cluster. + +

+ +

+ +Within the package, each database container receives the [vertical scaling](https://www.virtuozzo.com/application-platform-docs/automatic-vertical-scaling/) up to **32 dynamic cloudlets** (or 4 GiB of RAM and 12.8 GHz of CPU) that are provided dynamically based on the incoming load. And for the load balancer node the **6 dynamic cloudlets** provided by default. Subsequently, you can change the resource allocation limit by following the above-linked guide. + +## How to Install PostgreSQL Database Replication Package + +In order to get PostgreSQL Database Replication solution instantly deployed, click the **Deploy to Cloud** button below and specify your email address within the opened widget. Then choose one of the [Virtuozzo Public Cloud](https://www.virtuozzo.com/application-platform-partners/) providers (in case you don’t have an account at the appropriate platform, it will be created automatically) and press **Install**. + +[![Deploy](images/deploy-to-cloud.png)](https://jelastic.com/install-application/?manifest=https://raw.githubusercontent.com/jelastic-jps/postgres/v2.0.0/manifest.yaml) + +To install the package manually, log in to the Jelastic dashboard with your credentials and [import](https://www.virtuozzo.com/application-platform-docs/environment-import/) link to the [**_manifest.yaml_**](https://github.com/jelastic-jps/postgres/blob/master/manifest.yaml) file (alternatively, you can locate this package via [Jelastic Marketplace](https://www.virtuozzo.com/application-platform-docs/marketplace/), *Clusters* section) + +![postgresql-replication-installation](images/postgresql-replication-installation.png) + +Within the opened installation window, choose the PostgreSQL database version among available ones, type *Environment* name and optional *Display Name* ([environment alias](https://www.virtuozzo.com/application-platform-docs/environment-aliases/). Also, select the preferable [*Region*](https://www.virtuozzo.com/application-platform-docs/environment-regions/) (if several ones are available) and click **Install**. +If required you may disable Pgpool-II load balancer layer with respective toggle. + +Wait a few minutes for Jelastic to prepare your environment and set up the required replication configurations. When finished, you’ll be shown the appropriate notification with data for PostgreSQL administration interface access. + +![postgresql-replication-success-message](images/postgresql-replication-success-message.png) + +This information will be also duplicated to you via email. + +### Cluster Entry Point + +In case of no Pgpool-II nodes were added to cluster topology, use Primary node to access the cluster. And if the load balancing layer was deployed in front of the db cluster you may use any of Pgpool-II nodes as the entry point. + +### Cluster Management + +In VAP the PostgreSQL cluster components can be either managed via [CLI](https://www.virtuozzo.com/application-platform-docs/ssh-access/) or UI. + +#### Database Management + +Database nodes have a built-in management administration panel phpPgAdmin. Use the only one on Primary node. + +

+ +

+ +If required, the separate node can be installed with more advanced PostgreSQL database management software [pgAdmin4](https://www.pgadmin.org/) via importing [manifest](https://github.com/jelastic-jps/pgadmin/blob/master/manifest.yaml) from VAP collection. + +

+ +

+ +#### Pgpool-II Management + +Pgpool-II nodes can be also managed via user-friendly built-in Administration Panel [pgpoolAdmin](https://www.pgpool.net/docs/pgpoolAdmin/index_en.html). + +

+ +

+ +Pgpool-II admin panel provides an ability to tune: + - load balancing and even at database level. It means that you can specify how the requests to every database should be processed and balanced + - connnection pools + - logging + - replication + - debugging + - failover and failback + - etc. + + +To find more details on PostgreSQL Replication package installation and use, refer to the [article](https://www.virtuozzo.com/company/blog/postgresql-auto-clustering-master-slave-replication/). diff --git a/images/deploy-to-cloud.png b/images/deploy-to-cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..70c6554688d20fe91291785592464db792e0571f GIT binary patch literal 2701 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 Hu0mjf!a5Lf literal 0 HcmV?d00001 diff --git a/images/pgadmin.png b/images/pgadmin.png new file mode 100644 index 0000000000000000000000000000000000000000..db9c97ea595af1f7446ba69cf5b9e2bc1cc38937 GIT binary patch literal 86470 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