sync with latest upstream source

This commit is contained in:
Petr Kubat 2020-03-24 11:16:16 +01:00
commit 23f709b601
51 changed files with 5514 additions and 590 deletions

View file

@ -0,0 +1,2 @@
Bind-mount this directory under /opt/app-root/src in container, and all the
*.conf files from postgresql-cfg/ files will be included to postgresql.conf.

View file

@ -0,0 +1 @@
shared_buffers = 111MB

View file

@ -0,0 +1,5 @@
ssl = on
ssl_cert_file = '/opt/app-root/src/server.crt' # server certificate
ssl_key_file = '/opt/app-root/src/server.key' # server private key
#ssl_ca_file # trusted certificate authorities
#ssl_crl_file # certificates revoked by certificate authorities

View file

@ -0,0 +1,4 @@
#!/bin/bash
# Postgresql server will reject key files with liberal permissions
chmod og-rwx server.key

View file

@ -0,0 +1,77 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
2c:86:f9:22:0f:0c:ed:2b:e8:a3:f1:cf:9e:2b:09:82:22:76:ec:2b
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = testing
Validity
Not Before: Sep 16 11:39:01 2019 GMT
Not After : Sep 13 11:39:01 2029 GMT
Subject: CN = testing
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:d9:0f:25:ca:d9:32:4d:db:95:f5:5f:09:5a:2b:
e2:f7:ae:6a:b3:43:ce:1c:35:60:bd:cc:01:3f:f2:
0f:eb:20:da:55:8b:42:95:da:a5:0a:c8:c5:43:54:
64:85:e7:5b:2c:77:6a:1f:db:9f:56:39:35:e4:0f:
b0:1c:2b:a6:73:46:e8:27:2b:9f:62:5c:bf:7f:48:
5a:99:e1:8d:73:fe:d6:3a:ec:25:35:07:ad:69:f3:
95:81:ea:8a:20:50:fd:fc:e9:c0:b5:ac:f7:21:af:
37:2c:8e:23:51:74:fa:75:b2:48:c4:6e:95:f1:2d:
bc:af:ff:f4:eb:da:a3:78:fe:e9:c9:c0:ef:21:b5:
46:f5:e9:8c:9a:f9:94:84:a7:63:be:d6:fe:eb:31:
fb:ca:87:2e:e6:43:53:bd:3c:09:7f:cc:7b:9d:e2:
b9:0a:49:a5:5c:61:6e:94:f9:75:85:e3:41:e7:92:
24:84:9f:61:c7:d4:cc:b5:26:8a:c1:db:bf:a5:ce:
43:72:61:04:2f:bf:21:c4:d1:73:dd:b4:f8:37:bf:
85:0d:0e:92:8d:22:33:4b:ed:6d:55:2d:0b:42:c4:
23:e8:30:f3:86:2b:99:ba:e5:ba:ef:54:b4:40:29:
2d:53:7c:d4:59:72:20:65:88:9d:68:5a:fc:25:a8:
13:0d
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
17:77:65:09:AD:ED:EE:02:01:AB:09:FE:1E:FD:AB:4E:F2:4D:0A:23
X509v3 Authority Key Identifier:
keyid:17:77:65:09:AD:ED:EE:02:01:AB:09:FE:1E:FD:AB:4E:F2:4D:0A:23
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
14:67:98:15:fa:57:88:75:89:9a:0b:f0:e1:94:dd:dc:12:ab:
a0:2a:20:6d:38:64:39:39:58:4f:4d:2d:16:1d:e2:e2:d3:56:
35:2e:3c:f5:be:7e:16:fb:87:a1:b9:27:e6:d4:52:e8:1e:c5:
c7:b7:74:b5:15:53:6d:b0:90:34:8c:ce:20:82:62:60:1e:f2:
21:f9:22:a5:cb:17:a7:a9:55:71:cb:66:f5:dd:c2:85:6a:e1:
a7:35:d0:b9:09:6a:ae:4d:a5:32:34:fa:2a:cc:10:85:6c:95:
50:50:2c:e9:59:d1:40:78:16:d3:87:c3:31:cb:33:7b:0f:3a:
ef:51:c1:2e:0c:eb:38:61:de:01:42:0e:1d:cc:7d:b1:24:4b:
ef:ce:9d:c6:b0:97:51:c9:cc:23:d6:5d:4e:cf:68:06:c2:47:
94:c5:80:df:07:bc:72:cc:79:3d:94:be:6d:c8:b3:17:e6:5e:
52:38:c4:6b:a9:ee:ad:94:f9:74:bf:8a:95:12:06:b4:4d:17:
ca:72:a5:61:90:b7:c0:0f:d0:04:e1:39:3c:75:d5:8a:5c:11:
96:f7:fe:82:5a:e6:30:2c:2f:94:4e:bb:1e:8e:d8:0b:6e:1e:
e4:5f:f6:c9:a3:4d:2f:58:ee:ad:b7:cd:53:3f:f1:dc:1e:d2:
06:a0:03:58
-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIULIb5Ig8M7Svoo/HPnisJgiJ27CswDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHdGVzdGluZzAeFw0xOTA5MTYxMTM5MDFaFw0yOTA5MTMx
MTM5MDFaMBIxEDAOBgNVBAMMB3Rlc3RpbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDZDyXK2TJN25X1XwlaK+L3rmqzQ84cNWC9zAE/8g/rINpVi0KV
2qUKyMVDVGSF51ssd2of259WOTXkD7AcK6ZzRugnK59iXL9/SFqZ4Y1z/tY67CU1
B61p85WB6oogUP386cC1rPchrzcsjiNRdPp1skjEbpXxLbyv//Tr2qN4/unJwO8h
tUb16Yya+ZSEp2O+1v7rMfvKhy7mQ1O9PAl/zHud4rkKSaVcYW6U+XWF40HnkiSE
n2HH1My1JorB27+lzkNyYQQvvyHE0XPdtPg3v4UNDpKNIjNL7W1VLQtCxCPoMPOG
K5m65brvVLRAKS1TfNRZciBliJ1oWvwlqBMNAgMBAAGjUzBRMB0GA1UdDgQWBBQX
d2UJre3uAgGrCf4e/atO8k0KIzAfBgNVHSMEGDAWgBQXd2UJre3uAgGrCf4e/atO
8k0KIzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAUZ5gV+leI
dYmaC/DhlN3cEqugKiBtOGQ5OVhPTS0WHeLi01Y1Ljz1vn4W+4ehuSfm1FLoHsXH
t3S1FVNtsJA0jM4ggmJgHvIh+SKlyxenqVVxy2b13cKFauGnNdC5CWquTaUyNPoq
zBCFbJVQUCzpWdFAeBbTh8MxyzN7DzrvUcEuDOs4Yd4BQg4dzH2xJEvvzp3GsJdR
ycwj1l1Oz2gGwkeUxYDfB7xyzHk9lL5tyLMX5l5SOMRrqe6tlPl0v4qVEga0TRfK
cqVhkLfAD9AE4Tk8ddWKXBGW9/6CWuYwLC+UTrsejtgLbh7kX/bJo00vWO6tt81T
P/HcHtIGoANY
-----END CERTIFICATE-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZDyXK2TJN25X1
XwlaK+L3rmqzQ84cNWC9zAE/8g/rINpVi0KV2qUKyMVDVGSF51ssd2of259WOTXk
D7AcK6ZzRugnK59iXL9/SFqZ4Y1z/tY67CU1B61p85WB6oogUP386cC1rPchrzcs
jiNRdPp1skjEbpXxLbyv//Tr2qN4/unJwO8htUb16Yya+ZSEp2O+1v7rMfvKhy7m
Q1O9PAl/zHud4rkKSaVcYW6U+XWF40HnkiSEn2HH1My1JorB27+lzkNyYQQvvyHE
0XPdtPg3v4UNDpKNIjNL7W1VLQtCxCPoMPOGK5m65brvVLRAKS1TfNRZciBliJ1o
WvwlqBMNAgMBAAECggEAKvM8Xy8rLQzOV4c+qoEUoD37Dw3TsvE8+1FqzeRwEe6m
RVcRDeX90mx33CLO4VAuUlYuwa8LkFwxtbcE+g4JGbZmKZoQJ76ChgUjKF/hRZqf
eXlQw3WJcvWoF9T5D/v2xhza7RgUrq2lFUPq6Stkg/WLQJNBSD/snkbfh+vzfPVW
slg2zo1o9dMe53AOzjMkQ8RljbOfd+KZE340ZzftPxcTyrE9VaQqGLNtRiehhXPJ
dB7Kmc+/Pm1OkmpblnSAIJudsMNelUYsadYFgjtEgYYXFcuqNrCWeNRmMl5I/vEp
xnVf+gQfldJ+zAbkB4+nxMCOn1tqS2nOJRGW6xu4YQKBgQDt97CAAceXnRYoraqy
1ff5K8WxxCmcD0TcX/EfYXj1Qaex979x2SpftsrBTclkOxoTPv+OfrKZ1/Eq4a8z
0onP/lRGRxQ95gPvwFzQldKzmQVsoW6odZqMPO6hYJ0SsiZTwzc82IEXPA1QJJ6E
n3OggTLs0iCW3uLyXFL8npaL4wKBgQDpgdvSU82ipiEntMpqUuQdhw1TvnYwcSaC
GTUl4Uhwfdxmb424cqHiDoBKitd6DjHo20MshA/6WhWYq/dMz7ueEBu9cCKe85En
RzX0InCV26K1zBBbwMXGJGquIaQeha1GmOkpgORHjNwdwKZkZ/DJOHYOsjwGyQ22
H8cC6MX4TwKBgQCpqQ+ApEQuN0QmKnNqX50VXHztmeLkrgo1aH3cFr2LdozeGLm4
rNFGPmfeW9w7Btw3XpILgQ9LGieKoC8urmutDDH/jQvEeerSk35ZBIidnXq9kXb4
yigu1f54tg4m1zb2P1dxnRakfx8qxYDzI0/n3lV1fPbZOf3qN6K/Ez5YawKBgHQi
rQTvvz+c5rKL3XyCG4iACeXTvY6cSC2+gcuEP3YLcxnTc6YABXmcAryQT1kaREJv
AvrZ9+Ro94LGTKn8S3DyzAktA1sRAumJJlF064/s/AD1LFGmD/dbV1+hxbGUhLiv
BpAo1eCsMzHtBhS8CWra1QS8KtSpHFOvfFh7EzNLAoGBANyGrLj6qzywdSV7ccnd
sWg5U7Jgzuonb3K8LAc9NTnGn9C093RGY5HhsS1kTew01QRfxR7IdtSI9Q0vO/6I
stLGSa9fpn4leu2P6iF8r640xx02UFhKyf8wpM2RF38hHjAWRW/BaUEfnECeDi5A
yWuW6DFMAuJ80LdwyMPGkkt2
-----END PRIVATE KEY-----

View file

@ -0,0 +1,4 @@
log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql.log'

View file

@ -0,0 +1,4 @@
#!/bin/bash
# postgresql image encrypts user passwords at service start
# the functionality can be disabled by providing this file (postgresql-start-hook/set_passwords.sh) in s2i build

View file

@ -0,0 +1,10 @@
The PostgreSQL Audit Extension (or pgaudit) provides detailed session and/or object
audit logging via the standard logging facility provided by PostgreSQL.
Bind-mount this directory under /opt/app-root/src in the container, and all the
*.conf files from postgresql-cfg/ files will be included to postgresql.conf.
This config file enables the pgaudit extensions that is available in the container
image, but needs to be enabled.
More about pgaudit extension at https://www.pgaudit.org.

View file

@ -0,0 +1 @@
shared_preload_libraries = 'pgaudit'

View file

@ -0,0 +1,253 @@
{
"kind": "Template",
"apiVersion": "v1",
"metadata": {
"name": "postgresql-ephemeral",
"annotations": {
"openshift.io/display-name": "PostgreSQL (Ephemeral)",
"description": "PostgreSQL database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing",
"iconClass": "icon-postgresql",
"tags": "database,postgresql",
"openshift.io/long-description": "This template provides a standalone PostgreSQL server with a database created. The database is not stored on persistent storage, so any restart of the service will result in all data being lost. The database name, username, and password are chosen via parameters when provisioning this service.",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"openshift.io/documentation-url": "https://docs.okd.io/latest/using_images/db_images/postgresql.html",
"openshift.io/support-url": "https://access.redhat.com"
}
},
"message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Username: ${POSTGRESQL_USER}\n Password: ${POSTGRESQL_PASSWORD}\n Database Name: ${POSTGRESQL_DATABASE}\n Connection URL: postgresql://${DATABASE_SERVICE_NAME}:5432/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/.",
"labels": {
"template": "postgresql-ephemeral-template"
},
"objects": [
{
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": "${DATABASE_SERVICE_NAME}",
"annotations": {
"template.openshift.io/expose-username": "{.data['database-user']}",
"template.openshift.io/expose-password": "{.data['database-password']}",
"template.openshift.io/expose-database_name": "{.data['database-name']}"
}
},
"stringData" : {
"database-user" : "${POSTGRESQL_USER}",
"database-password" : "${POSTGRESQL_PASSWORD}",
"database-name" : "${POSTGRESQL_DATABASE}"
}
},
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "${DATABASE_SERVICE_NAME}",
"annotations": {
"template.openshift.io/expose-uri": "postgres://{.spec.clusterIP}:{.spec.ports[?(.name==\"postgresql\")].port}"
}
},
"spec": {
"ports": [
{
"name": "postgresql",
"protocol": "TCP",
"port": 5432,
"targetPort": 5432,
"nodePort": 0
}
],
"selector": {
"name": "${DATABASE_SERVICE_NAME}"
},
"type": "ClusterIP",
"sessionAffinity": "None"
},
"status": {
"loadBalancer": {}
}
},
{
"kind": "DeploymentConfig",
"apiVersion": "v1",
"metadata": {
"name": "${DATABASE_SERVICE_NAME}",
"annotations": {
"template.alpha.openshift.io/wait-for-ready": "true"
}
},
"spec": {
"strategy": {
"type": "Recreate"
},
"triggers": [
{
"type": "ImageChange",
"imageChangeParams": {
"automatic": true,
"containerNames": [
"postgresql"
],
"from": {
"kind": "ImageStreamTag",
"name": "postgresql:${POSTGRESQL_VERSION}",
"namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
},
{
"type": "ConfigChange"
}
],
"replicas": 1,
"selector": {
"name": "${DATABASE_SERVICE_NAME}"
},
"template": {
"metadata": {
"labels": {
"name": "${DATABASE_SERVICE_NAME}"
}
},
"spec": {
"containers": [
{
"name": "postgresql",
"image": " ",
"ports": [
{
"containerPort": 5432,
"protocol": "TCP"
}
],
"readinessProbe": {
"timeoutSeconds": 1,
"initialDelaySeconds": 5,
"exec": {
"command": [ "/usr/libexec/check-container" ]
}
},
"livenessProbe": {
"timeoutSeconds": 10,
"initialDelaySeconds": 120,
"exec": {
"command": [ "/usr/libexec/check-container", "--live" ]
}
},
"env": [
{
"name": "POSTGRESQL_USER",
"valueFrom": {
"secretKeyRef" : {
"name" : "${DATABASE_SERVICE_NAME}",
"key" : "database-user"
}
}
},
{
"name": "POSTGRESQL_PASSWORD",
"valueFrom": {
"secretKeyRef" : {
"name" : "${DATABASE_SERVICE_NAME}",
"key" : "database-password"
}
}
},
{
"name": "POSTGRESQL_DATABASE",
"valueFrom": {
"secretKeyRef" : {
"name" : "${DATABASE_SERVICE_NAME}",
"key" : "database-name"
}
}
}
],
"resources": {
"limits": {
"memory": "${MEMORY_LIMIT}"
}
},
"volumeMounts": [
{
"name": "${DATABASE_SERVICE_NAME}-data",
"mountPath": "/var/lib/pgsql/data"
}
],
"terminationMessagePath": "/dev/termination-log",
"imagePullPolicy": "IfNotPresent",
"capabilities": {},
"securityContext": {
"capabilities": {},
"privileged": false
}
}
],
"volumes": [
{
"name": "${DATABASE_SERVICE_NAME}-data",
"emptyDir": {
"medium": ""
}
}
],
"restartPolicy": "Always",
"dnsPolicy": "ClusterFirst"
}
}
},
"status": {}
}
],
"parameters": [
{
"name": "MEMORY_LIMIT",
"displayName": "Memory Limit",
"description": "Maximum amount of memory the container can use.",
"value": "512Mi",
"required": true
},
{
"name": "NAMESPACE",
"displayName": "Namespace",
"description": "The OpenShift Namespace where the ImageStream resides.",
"value": "openshift"
},
{
"name": "DATABASE_SERVICE_NAME",
"displayName": "Database Service Name",
"description": "The name of the OpenShift Service exposed for the database.",
"value": "postgresql",
"required": true
},
{
"name": "POSTGRESQL_USER",
"displayName": "PostgreSQL Connection Username",
"description": "Username for PostgreSQL user that will be used for accessing the database.",
"generate": "expression",
"from": "user[A-Z0-9]{3}",
"required": true
},
{
"name": "POSTGRESQL_PASSWORD",
"displayName": "PostgreSQL Connection Password",
"description": "Password for the PostgreSQL connection user.",
"generate": "expression",
"from": "[a-zA-Z0-9]{16}",
"required": true
},
{
"name": "POSTGRESQL_DATABASE",
"displayName": "PostgreSQL Database Name",
"description": "Name of the PostgreSQL database accessed.",
"value": "sampledb",
"required": true
},
{
"name": "POSTGRESQL_VERSION",
"displayName": "Version of PostgreSQL Image",
"description": "Version of PostgreSQL image to be used (10 or latest).",
"value": "10",
"required": true
}
]
}

View file

@ -0,0 +1,277 @@
{
"kind": "Template",
"apiVersion": "v1",
"metadata": {
"name": "postgresql-persistent",
"annotations": {
"openshift.io/display-name": "PostgreSQL",
"description": "PostgreSQL database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/.\n\nNOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.",
"iconClass": "icon-postgresql",
"tags": "database,postgresql",
"openshift.io/long-description": "This template provides a standalone PostgreSQL server with a database created. The database is stored on persistent storage. The database name, username, and password are chosen via parameters when provisioning this service.",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"openshift.io/documentation-url": "https://docs.okd.io/latest/using_images/db_images/postgresql.html",
"openshift.io/support-url": "https://access.redhat.com"
}
},
"message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Username: ${POSTGRESQL_USER}\n Password: ${POSTGRESQL_PASSWORD}\n Database Name: ${POSTGRESQL_DATABASE}\n Connection URL: postgresql://${DATABASE_SERVICE_NAME}:5432/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/postgresql-container/.",
"labels": {
"template": "postgresql-persistent-template"
},
"objects": [
{
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": "${DATABASE_SERVICE_NAME}",
"annotations": {
"template.openshift.io/expose-username": "{.data['database-user']}",
"template.openshift.io/expose-password": "{.data['database-password']}",
"template.openshift.io/expose-database_name": "{.data['database-name']}"
}
},
"stringData" : {
"database-user" : "${POSTGRESQL_USER}",
"database-password" : "${POSTGRESQL_PASSWORD}",
"database-name" : "${POSTGRESQL_DATABASE}"
}
},
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "${DATABASE_SERVICE_NAME}",
"annotations": {
"template.openshift.io/expose-uri": "postgres://{.spec.clusterIP}:{.spec.ports[?(.name==\"postgresql\")].port}"
}
},
"spec": {
"ports": [
{
"name": "postgresql",
"protocol": "TCP",
"port": 5432,
"targetPort": 5432,
"nodePort": 0
}
],
"selector": {
"name": "${DATABASE_SERVICE_NAME}"
},
"type": "ClusterIP",
"sessionAffinity": "None"
},
"status": {
"loadBalancer": {}
}
},
{
"kind": "PersistentVolumeClaim",
"apiVersion": "v1",
"metadata": {
"name": "${DATABASE_SERVICE_NAME}"
},
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"resources": {
"requests": {
"storage": "${VOLUME_CAPACITY}"
}
}
}
},
{
"kind": "DeploymentConfig",
"apiVersion": "v1",
"metadata": {
"name": "${DATABASE_SERVICE_NAME}",
"annotations": {
"template.alpha.openshift.io/wait-for-ready": "true"
}
},
"spec": {
"strategy": {
"type": "Recreate"
},
"triggers": [
{
"type": "ImageChange",
"imageChangeParams": {
"automatic": true,
"containerNames": [
"postgresql"
],
"from": {
"kind": "ImageStreamTag",
"name": "postgresql:${POSTGRESQL_VERSION}",
"namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
},
{
"type": "ConfigChange"
}
],
"replicas": 1,
"selector": {
"name": "${DATABASE_SERVICE_NAME}"
},
"template": {
"metadata": {
"labels": {
"name": "${DATABASE_SERVICE_NAME}"
}
},
"spec": {
"containers": [
{
"name": "postgresql",
"image": " ",
"ports": [
{
"containerPort": 5432,
"protocol": "TCP"
}
],
"readinessProbe": {
"timeoutSeconds": 1,
"initialDelaySeconds": 5,
"exec": {
"command": [ "/usr/libexec/check-container" ]
}
},
"livenessProbe": {
"timeoutSeconds": 10,
"initialDelaySeconds": 120,
"exec": {
"command": [ "/usr/libexec/check-container", "--live" ]
}
},
"env": [
{
"name": "POSTGRESQL_USER",
"valueFrom": {
"secretKeyRef" : {
"name" : "${DATABASE_SERVICE_NAME}",
"key" : "database-user"
}
}
},
{
"name": "POSTGRESQL_PASSWORD",
"valueFrom": {
"secretKeyRef" : {
"name" : "${DATABASE_SERVICE_NAME}",
"key" : "database-password"
}
}
},
{
"name": "POSTGRESQL_DATABASE",
"valueFrom": {
"secretKeyRef" : {
"name" : "${DATABASE_SERVICE_NAME}",
"key" : "database-name"
}
}
}
],
"resources": {
"limits": {
"memory": "${MEMORY_LIMIT}"
}
},
"volumeMounts": [
{
"name": "${DATABASE_SERVICE_NAME}-data",
"mountPath": "/var/lib/pgsql/data"
}
],
"terminationMessagePath": "/dev/termination-log",
"imagePullPolicy": "IfNotPresent",
"capabilities": {},
"securityContext": {
"capabilities": {},
"privileged": false
}
}
],
"volumes": [
{
"name": "${DATABASE_SERVICE_NAME}-data",
"persistentVolumeClaim": {
"claimName": "${DATABASE_SERVICE_NAME}"
}
}
],
"restartPolicy": "Always",
"dnsPolicy": "ClusterFirst"
}
}
},
"status": {}
}
],
"parameters": [
{
"name": "MEMORY_LIMIT",
"displayName": "Memory Limit",
"description": "Maximum amount of memory the container can use.",
"value": "512Mi",
"required": true
},
{
"name": "NAMESPACE",
"displayName": "Namespace",
"description": "The OpenShift Namespace where the ImageStream resides.",
"value": "openshift"
},
{
"name": "DATABASE_SERVICE_NAME",
"displayName": "Database Service Name",
"description": "The name of the OpenShift Service exposed for the database.",
"value": "postgresql",
"required": true
},
{
"name": "POSTGRESQL_USER",
"displayName": "PostgreSQL Connection Username",
"description": "Username for PostgreSQL user that will be used for accessing the database.",
"generate": "expression",
"from": "user[A-Z0-9]{3}",
"required": true
},
{
"name": "POSTGRESQL_PASSWORD",
"displayName": "PostgreSQL Connection Password",
"description": "Password for the PostgreSQL connection user.",
"generate": "expression",
"from": "[a-zA-Z0-9]{16}",
"required": true
},
{
"name": "POSTGRESQL_DATABASE",
"displayName": "PostgreSQL Database Name",
"description": "Name of the PostgreSQL database accessed.",
"value": "sampledb",
"required": true
},
{
"name": "VOLUME_CAPACITY",
"displayName": "Volume Capacity",
"description": "Volume space available for data, e.g. 512Mi, 2Gi.",
"value": "1Gi",
"required": true
},
{
"name": "POSTGRESQL_VERSION",
"displayName": "Version of PostgreSQL Image",
"description": "Version of PostgreSQL image to be used (10 or latest).",
"value": "10",
"required": true
}
]
}

View file

@ -0,0 +1,240 @@
# PostgreSQL Replication Example
**WARNING: This is only a Proof-Of-Concept example and it is not meant to be used in
production. Use at your own risk.**
## What is PostgreSQL replication?
Replication enables data from one database server (master, or primary) to be
replicated to one or more servers (slaves, or standby servers).
PostgreSQL has [different replication solutions](http://www.postgresql.org/docs/9.2/static/different-replication-solutions.html),
each with its own pros and cons.
This example uses PostgreSQL's native support for [streaming replication](http://www.postgresql.org/docs/9.2/static/warm-standby.html).
In this configuration, the primary server operates in continuous archiving mode,
while each standby server operates in continuous recovery mode, streaming over
the network the write-ahead log (WAL) records from the primary as they're
generated.
This configuration can be used to create a high availability (HA) cluster
configuration and has relatively low performance impact on the primary server.
A standby server can also be used for read-only queries.
## Deployment
This example uses a [PersistentVolumeClaim](https://docs.okd.io/latest/architecture/additional_concepts/storage.html#persistent-volume-claims)
to request persistent storage for the primary PostgreSQL server.
You need to have persistent volumes configured and available in your project in
order to continue. For trying out this example in a single node testing
environment, you can create a temporary volume with:
```
$ oc create -f - <<EOF
{
"kind": "PersistentVolume",
"apiVersion": "v1",
"metadata": {
"name": "postgres-data-volume"
},
"spec": {
"capacity": {
"storage": "512Mi"
},
"hostPath": {
"path": "`mktemp -d --tmpdir pg-data.XXXXX | tee >(xargs chmod a+rwx)`"
},
"accessModes": [
"ReadWriteOnce"
]
}
}
EOF
```
It is recommended, however, that you use other [type of PersistentVolume](https://docs.okd.io/latest/architecture/additional_concepts/storage.html#types-of-persistent-volumes)
such as NFS.
Now you can create a new database deployment:
```
$ oc new-app examples/replica/postgresql_replica.json
```
## How does this example work?
### Services 'postgresql-master' and 'postgresql-slave'
These services are the entry point for connecting to, respectively, the primary
database server and any of the standby servers.
In your application, connect to the `postgresql-master` service for write operations, and to `postgresql-master` or `postgresql-slave` for reads.
Keep in mind that reading from a slave might return slightly outdated data.
To get a list of endpoints for the read-only standby servers, you can do a DNS
query. From a container in the same OpenShift project:
```
$ dig postgresql-slave A +search +short
```
### DeploymentConfig 'postgresql-master'
This resource defines a [deployment configuration](https://docs.okd.io/latest/architecture/core_concepts/deployments.html#deployments-and-deployment-configurations)
to spawn the PostgreSQL primary database server, or master.
Once the master is started, it works as a standalone database server, fully
independent of the slaves.
### DeploymentConfig 'postgresql-slave'
This resource defines a [deployment configuration](https://docs.okd.io/latest/architecture/core_concepts/deployments.html#deployments-and-deployment-configurations)
to spawn PostgreSQL standby servers, the slaves.
Upon startup, each slave waits for the master server to become available (via
DNS lookup). Once that happens, the slave connects to the master and starts
streaming the WAL.
To check that the slave is connected and streaming changes from the master,
you can issue the following commands:
```
$ master_name=`oc get pods -l name=postgresql-master -t '{{ (index .items 0).metadata.name }}'`
$ oc exec $master_name -- bash -c 'psql -c "select client_addr, state from pg_stat_replication;"'
```
After a successful deployment, you should get an output similar to:
```
client_addr | state
--------------+-----------
172.17.0.227 | streaming
(1 row)
```
## Scaling
By default, the provided template creates one primary and one standby server.
Scaling in this setup means increasing the number of standby servers,
consequently increasing data redundancy and concurrent read throughput (if
reading from slaves).
You can add more slaves using `oc scale`:
```
$ oc scale dc postgresql-slave --replicas=2
```
Using `oc scale` with `postgresql-master` is not supported.
After scaling, you can verify that all slaves are streaming changes from the
master with:
```
$ oc exec $master_name -- bash -c 'psql -c "select client_addr, state from pg_stat_replication;"'
client_addr | state
--------------+-----------
172.17.0.227 | streaming
172.17.0.229 | streaming
(2 rows)
```
There should be one row per slave (number of replicas defined via `oc scale`).
## Changing passwords
You can change the passwords for the database user and admin, as well as the
password used for replication, by changing the appropriate environment variables
in the deployment configurations described earlier.
No other method is supported.
On every deploy, passwords are reset to match the values in the environment
variables of the DeploymentConfig 'postgresql-master'.
### POSTGRESQL_PASSWORD and POSTGRESQL_ADMIN_PASSWORD
These are, respectively, the passwords for the regular database user defined
by `POSTGRESQL_USER` and the admin user 'postgres'.
You can change these passwords with:
```
$ oc env dc postgresql-master POSTGRESQL_PASSWORD=NewPassword POSTGRESQL_ADMIN_PASSWORD=NewAdminPassword
deploymentconfigs/postgresql-master
```
This will trigger the redeployment of the primary server.
Note that you can change one password but not the other by simply omitting one
of the arguments to `oc env` above.
You can verify that the new password is in effect with:
```
$ oc exec $master_name -- bash -c 'PGPASSWORD=NewPassword psql -h postgresql-master $POSTGRESQL_DATABASE $POSTGRESQL_USER -c "select * from (select inet_server_addr()) ra cross join (select current_database()) cdb cross join (select current_user) cu"'
inet_server_addr | current_database | current_user
------------------+------------------+--------------
172.17.1.38 | userdb | user
(1 row)
```
You should also be able to connect to a slave using the new password:
```
$ oc exec $master_name -- bash -c 'PGPASSWORD=NewPassword psql -h postgresql-slave $POSTGRESQL_DATABASE $POSTGRESQL_USER -c "select * from (select inet_server_addr()) ra cross join (select current_database()) cdb cross join (select current_user) cu"'
inet_server_addr | current_database | current_user
------------------+------------------+--------------
172.17.1.35 | userdb | user
(1 row)
```
For completeness, here's how to verify the new admin password:
```
$ oc exec $master_name -- bash -c 'PGPASSWORD=NewAdminPassword psql -h postgresql-master $POSTGRESQL_DATABASE -c "select * from (select inet_server_addr()) ra cross join (select current_database()) cdb cross join (select current_user) cu"'
inet_server_addr | current_database | current_user
------------------+------------------+--------------
172.17.1.38 | userdb | postgres
(1 row)
```
```
$ oc exec $master_name -- bash -c 'PGPASSWORD=NewAdminPassword psql -h postgresql-slave $POSTGRESQL_DATABASE -c "select * from (select inet_server_addr()) ra cross join (select current_database()) cdb cross join (select current_user) cu"'
inet_server_addr | current_database | current_user
------------------+------------------+--------------
172.17.1.35 | userdb | postgres
(1 row)
```
### POSTGRESQL_MASTER_PASSWORD
This password is used by standby servers to connect to the primary. Both
deployment configurations in this example setup need to agree on the value of
this password to have replication working correctly.
You can change the environment variable with the password on both deployment
configurations at once:
```
$ oc env dc postgresql-master postgresql-slave POSTGRESQL_MASTER_PASSWORD=NewReplicationPassword
deploymentconfigs/postgresql-master
deploymentconfigs/postgresql-slave
```
This will trigger the redeployment of both primary and standby servers.
Note that, as a current limitation in this example, the standby servers store
replicated data in an an ephemeral [emptyDir](https://docs.okd.io/latest/dev_guide/volumes.html).
This means that redeploying a standby server will cause it to start replicating
again from scratch.
After the primary and standby servers are ready, you can verify that the standby
servers are successfully connected to the primary:
```
$ oc exec $master_name -- bash -c 'psql -c "select client_addr, state from pg_stat_replication;"'
client_addr | state
-------------+-----------
172.17.1.35 | streaming
(1 row)
```

View file

@ -0,0 +1,370 @@
{
"kind": "Template",
"apiVersion": "v1",
"metadata": {
"name": "pg-replica-example",
"annotations": {
"description": "PostgreSQL Replication Example",
"iconClass": "icon-database",
"tags": "database,postgresql,replication"
}
},
"parameters": [
{
"name": "POSTGRESQL_MASTER_USER",
"description": "The username used for master-slave replication",
"value": "master",
"required": true
},
{
"name": "POSTGRESQL_MASTER_PASSWORD",
"description": "The password for the PostgreSQL replication user",
"generate": "expression",
"from": "[a-zA-Z0-9]{12}",
"required": true
},
{
"name": "POSTGRESQL_USER",
"description": "The username that clients will use to connect to PostgreSQL server",
"value": "user",
"required": true
},
{
"name": "POSTGRESQL_PASSWORD",
"description": "The password for the PostgreSQL master user",
"generate": "expression",
"from": "[a-zA-Z0-9]{12}",
"required": true
},
{
"name": "POSTGRESQL_DATABASE",
"description": "The name of the database that will be created",
"value": "userdb",
"required": true
},
{
"name": "POSTGRESQL_ADMIN_PASSWORD",
"description": "The password for the PostgreSQL administrator",
"generate": "expression",
"from": "[a-zA-Z0-9]{12}",
"required": false
},
{
"name": "POSTGRESQL_MASTER_SERVICE_NAME",
"description": "The name of the PostgreSQL Service (used to DNS lookup, default: 'postgresql-master')",
"value": "postgresql-master",
"required": true
},
{
"name": "POSTGRESQL_SLAVE_SERVICE_NAME",
"description": "The name of the PostgreSQL Service (used to DNS lookup, default: 'postgresql-slave')",
"value": "postgresql-slave",
"required": true
},
{
"name": "VOLUME_CAPACITY",
"description": "Volume space available for data, e.g. 512Mi, 2Gi",
"value": "512Mi",
"required": true
},
{
"name": "IMAGESTREAMTAG",
"displayName": "ImageStreamTag",
"description": "The OpenShift ImageStreamTag to use for PostgreSQL.",
"value": "postgresql:9.6"
},
{
"name": "NAMESPACE",
"displayName": "Namespace",
"description": "The OpenShift Namespace where the ImageStream resides.",
"value": "openshift"
}
],
"objects": [
{
"kind": "PersistentVolumeClaim",
"apiVersion": "v1",
"metadata": {
"name": "postgresql-data-claim"
},
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"resources": {
"requests": {
"storage": "${VOLUME_CAPACITY}"
}
}
}
},
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "${POSTGRESQL_MASTER_SERVICE_NAME}",
"labels": {
"name": "${POSTGRESQL_MASTER_SERVICE_NAME}"
}
},
"spec": {
"ports": [
{
"port": 5432,
"targetPort": 5432
}
],
"selector": {
"name": "${POSTGRESQL_MASTER_SERVICE_NAME}"
},
"clusterIP": "None"
}
},
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "${POSTGRESQL_SLAVE_SERVICE_NAME}",
"labels": {
"name": "${POSTGRESQL_SLAVE_SERVICE_NAME}"
}
},
"spec": {
"ports": [
{
"port": 5432,
"targetPort": 5432
}
],
"selector": {
"name": "${POSTGRESQL_SLAVE_SERVICE_NAME}"
},
"clusterIP": "None"
}
},
{
"kind": "DeploymentConfig",
"apiVersion": "v1",
"metadata": {
"name": "${POSTGRESQL_MASTER_SERVICE_NAME}"
},
"spec": {
"strategy": {
"type": "Recreate"
},
"triggers": [
{
"type": "ImageChange",
"imageChangeParams": {
"automatic": true,
"containerNames": [
"postgresql-master"
],
"from": {
"kind": "ImageStreamTag",
"name": "${IMAGESTREAMTAG}",
"namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
},
{
"type": "ConfigChange"
}
],
"replicas": 1,
"selector": {
"name": "${POSTGRESQL_MASTER_SERVICE_NAME}"
},
"template": {
"metadata": {
"labels": {
"name": "${POSTGRESQL_MASTER_SERVICE_NAME}"
}
},
"spec": {
"volumes": [
{
"name": "postgresql-data",
"persistentVolumeClaim": {
"claimName": "postgresql-data-claim"
}
}
],
"containers": [
{
"name": "postgresql-master",
"image": " ",
"args": [
"run-postgresql-master"
],
"ports": [
{
"containerPort": 5432
}
],
"readinessProbe": {
"timeoutSeconds": 1,
"initialDelaySeconds": 5,
"exec": {
"command": [ "/usr/libexec/check-container" ]
}
},
"livenessProbe": {
"timeoutSeconds": 10,
"initialDelaySeconds": 120,
"exec": {
"command": [ "/usr/libexec/check-container", "--live" ]
}
},
"env": [
{
"name": "POSTGRESQL_MASTER_USER",
"value": "${POSTGRESQL_MASTER_USER}"
},
{
"name": "POSTGRESQL_MASTER_PASSWORD",
"value": "${POSTGRESQL_MASTER_PASSWORD}"
},
{
"name": "POSTGRESQL_USER",
"value": "${POSTGRESQL_USER}"
},
{
"name": "POSTGRESQL_PASSWORD",
"value": "${POSTGRESQL_PASSWORD}"
},
{
"name": "POSTGRESQL_DATABASE",
"value": "${POSTGRESQL_DATABASE}"
},
{
"name": "POSTGRESQL_ADMIN_PASSWORD",
"value": "${POSTGRESQL_ADMIN_PASSWORD}"
}
],
"volumeMounts": [
{
"name": "postgresql-data",
"mountPath": "/var/lib/pgsql/data"
}
]
}
]
}
}
}
},
{
"kind": "DeploymentConfig",
"apiVersion": "v1",
"metadata": {
"name": "${POSTGRESQL_SLAVE_SERVICE_NAME}"
},
"spec": {
"strategy": {
"type": "Recreate"
},
"triggers": [
{
"type": "ImageChange",
"imageChangeParams": {
"automatic": true,
"containerNames": [
"postgresql-slave"
],
"from": {
"kind": "ImageStreamTag",
"name": "${IMAGESTREAMTAG}",
"namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
},
{
"type": "ConfigChange"
}
],
"replicas": 1,
"selector": {
"name": "${POSTGRESQL_SLAVE_SERVICE_NAME}"
},
"template": {
"metadata": {
"labels": {
"name": "${POSTGRESQL_SLAVE_SERVICE_NAME}"
}
},
"spec": {
"volumes": [
{
"name": "postgresql-data",
"emptyDir": {}
}
],
"containers": [
{
"name": "postgresql-slave",
"image": " ",
"args": [
"run-postgresql-slave"
],
"ports": [
{
"containerPort": 5432
}
],
"readinessProbe": {
"timeoutSeconds": 1,
"initialDelaySeconds": 5,
"exec": {
"command": [ "/usr/libexec/check-container" ]
}
},
"livenessProbe": {
"timeoutSeconds": 10,
"initialDelaySeconds": 120,
"exec": {
"command": [ "/usr/libexec/check-container", "--live" ]
}
},
"env": [
{
"name": "POSTGRESQL_MASTER_SERVICE_NAME",
"value": "${POSTGRESQL_MASTER_SERVICE_NAME}"
},
{
"name": "POSTGRESQL_MASTER_USER",
"value": "${POSTGRESQL_MASTER_USER}"
},
{
"name": "POSTGRESQL_MASTER_PASSWORD",
"value": "${POSTGRESQL_MASTER_PASSWORD}"
},
{
"name": "POSTGRESQL_USER",
"value": "${POSTGRESQL_USER}"
},
{
"name": "POSTGRESQL_PASSWORD",
"value": "${POSTGRESQL_PASSWORD}"
},
{
"name": "POSTGRESQL_DATABASE",
"value": "${POSTGRESQL_DATABASE}"
}
],
"volumeMounts": [
{
"name": "postgresql-data",
"mountPath": "/var/lib/pgsql/data"
}
]
}
]
}
}
}
}
]
}

View file

@ -0,0 +1,36 @@
#! /usr/bin/bash -x
# fail early
set -e
# source the convenience tooling
source "${CONTAINER_SCRIPTS_PATH}/common.sh"
# set $PGDATA variable
set_pgdata
# assert uninitialized data
test ! -f "$PGDATA/postgresql.conf"
# empty config file is needed after 'initialize_database' call
touch "$POSTGRESQL_CONFIG_FILE"
initialize_database
# start local PostgreSQL server (wait with '-w')
pg_ctl -w start -o "-h ''"
# load all sql files
shopt -s nullglob
for file in /tmp/src/init/*.sql; do
psql -f "$file"
done
pg_ctl stop
# dump the data into $PWD (in-image storage)
tar caf data.tar.xz -C "$PGDATA" .
rm -rf "$PGDATA"
# install pre-start hook
cp -r /tmp/src/postgresql-pre-start .

View file

@ -0,0 +1,2 @@
CREATE TABLE test (sth TEXT);
INSERT INTO test VALUES ('hello world');

View file

@ -0,0 +1,3 @@
if test ! -f "$PGDATA/postgresql.conf"; then
tar xf "$APP_DATA"/src/data.tar.xz -C "$PGDATA"
fi