VMware Tanzu SQL with Postgres for Kubernetes 1.2をインストールしてSpring Bootアプリからアクセスするメモ
VMware Tanzu SQL with Postgres for Kubernetes 1.2.0 (以下、Tanzu Postgres) がリリースされ、
ようやくdefault namespace以外にPostgres Operatorをインストールできるようになったので試します。
cert-manager 1.0以上のインストールが必須で、TKG Extensionに含まれている古いバージョンと合わないので、TKGではなくkindを使って試します。
目次
事前準備
Kindでクラスタを作成します。
kind create cluster
cert-managerをインストールします。
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.1/cert-manager.yaml
Tanzu Postgres Operatorのインストール
Tanzu PostgresのメインであるTanzu Postgres OperatorはHelm 3でインストールできます。
Tanzu Network Registryにログインします。
export TANZUNET_USERNAME=...
export TANZUNET_PASSWORD=...
export HELM_EXPERIMENTAL_OCI=1
helm registry login registry.pivotal.io \
--username=${TANZUNET_USERNAME} \
--password=${TANZUNET_PASSWORD}
Helm chartをローカルに展開します。
helm chart pull registry.pivotal.io/tanzu-sql-postgres/postgres-operator-chart:v1.2.0
helm chart export registry.pivotal.io/tanzu-sql-postgres/postgres-operator-chart:v1.2.0 --destination=/tmp/
Tanzu Network RegistryへのimagePullSecretを作成します。今回は postgres-operator namespaceにTanzu Postgres Operatorをインストールします。
kubectl create namespace postgres-operator
kubectl create secret docker-registry regsecret \
-n postgres-operator \
--docker-server=https://registry.pivotal.io/ \
--docker-username=${TANZUNET_USERNAME} \
--docker-password=${TANZUNET_PASSWORD} \
--dry-run=client \
-o yaml \
| kubectl apply -f-
展開したチャートをインストールします。
helm install -n postgres-operator --wait postgres-operator /tmp/postgres-operator/
しばらくするとインストールが完了します。次のコマンドでリソースを確認します。
$ kubectl get pod,service,certificate,clusterissuer -n postgres-operator
NAME READY STATUS RESTARTS AGE
pod/postgres-operator-698b445bc4-wxl7n 1/1 Running 0 2m5s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/postgres-operator-webhook-service ClusterIP 10.96.69.232 <none> 443/TCP 2m5s
NAME READY SECRET AGE
certificate.cert-manager.io/postgres-operator-serving-cert True postgres-operator-webhook-server-cert 2m5s
NAME READY AGE
clusterissuer.cert-manager.io/postgres-operator-ca-certificate-cluster-issuer True 2m5s
clusterissuer.cert-manager.io/postgres-operator-selfsigned-clusterissuer True 2m5s
またPostgres Custom Resourceが利用できるようになります。
$ kubectl api-resources --api-group sql.tanzu.vmware.com
NAME SHORTNAMES APIVERSION NAMESPACED KIND
postgres pg sql.tanzu.vmware.com/v1 true Postgres
Postgres Instanceの作成
早速Postgresリソースを作成します。今回はdefault namespaceに作成します。
imagePullSecretをdefault namespaceにも作成します。
kubectl create secret docker-registry regsecret \
-n default \
--docker-server=https://registry.pivotal.io/ \
--docker-username=${TANZUNET_USERNAME} \
--docker-password=${TANZUNET_PASSWORD} \
--dry-run=client \
-o yaml \
| kubectl apply -f-
次にvehicle-dbという名前のPostgresリソースを作成します。
cat <<EOF | kubectl apply -f-
apiVersion: sql.tanzu.vmware.com/v1
kind: Postgres
metadata:
name: vehicle-db
namespace: default
spec:
storageClassName: standard
storageSize: 800M
cpu: "0.8"
memory: 800Mi
monitorStorageClassName: standard
monitorStorageSize: 1G
resources:
monitor:
limits:
cpu: 800m
memory: 800Mi
requests:
cpu: 800m
memory: 800Mi
pgConfig:
dbname: vehicle-db
username: pgadmin
serviceType: ClusterIP
highAvailability:
enabled: true
EOF
次のコマンドで作成されたリソースを確認します。PostgreSQLのStatefulSetが作成されています。
$ kubectl get all,pvc,certificate,secret -l postgres-instance=vehicle-db
NAME READY STATUS RESTARTS AGE
pod/vehicle-db-0 3/3 Running 0 118s
pod/vehicle-db-1 3/3 Running 0 118s
pod/vehicle-db-monitor-0 4/4 Running 0 2m49s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/vehicle-db ClusterIP 10.96.197.3 <none> 5432/TCP 2m49s
service/vehicle-db-agent ClusterIP None <none> <none> 2m49s
NAME READY AGE
statefulset.apps/vehicle-db 2/2 118s
statefulset.apps/vehicle-db-monitor 1/1 2m49s
NAME STATUS AGE
postgres.sql.tanzu.vmware.com/vehicle-db Running 2m50s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/vehicle-db-monitor-vehicle-db-monitor-0 Bound pvc-71d7b651-a1d3-4579-a9fb-e88fd7f75451 1G RWO standard 2m50s
persistentvolumeclaim/vehicle-db-pgdata-vehicle-db-0 Bound pvc-183816fb-9a5e-458e-978c-0b29ba77c249 800M RWO standard 119s
persistentvolumeclaim/vehicle-db-pgdata-vehicle-db-1 Bound pvc-8feeec92-3576-4935-b2fe-e7e4ba4550b3 800M RWO standard 119s
NAME READY SECRET AGE
certificate.cert-manager.io/vehicle-db-internal-ssl-certificate True vehicle-db-internal-ssl-secret 2m50s
NAME TYPE DATA AGE
secret/vehicle-db-db-secret Opaque 5 2m50s
secret/vehicle-db-empty-secret Opaque 0 2m50s
secret/vehicle-db-internal-ssl-secret kubernetes.io/tls 3 2m50s
secret/vehicle-db-monitor-secret Opaque 3 2m50s
secret/vehicle-db-pgbackrest-secret Opaque 3 2m50s
HA構成 を有効にしてあるので、次の図のようにprimaryとmirrorのPostgres及びmonitorのPodが立ち上がります。

Postgres Podのラベルを確認するとどちらかがprimary(role=read-write)でどちらがmirror(role=read)かがわかります。
$ kubectl get pod -l postgres-instance=vehicle-db,type=data --show-labels
NAME READY STATUS RESTARTS AGE LABELS
vehicle-db-0 3/3 Running 0 70m app=postgres,controller-revision-hash=vehicle-db-7cd7587b76,headless-service=vehicle-db,postgres-instance=vehicle-db,role=read-write,statefulset.kubernetes.io/pod-name=vehicle-db-0,type=data
vehicle-db-1 3/3 Running 0 70m app=postgres,controller-revision-hash=vehicle-db-7cd7587b76,headless-service=vehicle-db,postgres-instance=vehicle-db,role=read,statefulset.kubernetes.io/pod-name=vehicle-db-1,type=data
Serviceは2つ作成されており、FQDN vehicle-db.default.svc.cluster.localへのリクエストはrole=read-writeラベルがついているPodへルーティングされ、
vehicle-db-0.vehicle-db-agent.default.svc.cluster.localとvehicle-db-1.vehicle-db-agent.default.svc.cluster.localはHeadless Serviceで個々のPodへ直接ルーティングされます。
$ kubectl get svc -l postgres-instance=vehicle-db -owide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
vehicle-db ClusterIP 10.96.196.90 <none> 5432/TCP 71m app=postgres,postgres-instance=vehicle-db,role=read-write,type=data
vehicle-db-agent ClusterIP None <none> <none> 71m headless-service=vehicle-db
次のコマンドで各ノードの状態を見ることができます。node_1がprimary、node_2がsecondaryになっています。
$ kubectl exec -ti pod/vehicle-db-1 -- pg_autoctl show state
Defaulting container name to pg-container.
Use 'kubectl describe pod/vehicle-db-1 -n default' to see all of the containers in this pod.
Name | Node | Host:Port | LSN | Reachable | Current State | Assigned State
-------+-------+--------------------------------------------------------------+-----------+-----------+---------------------+--------------------
node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | 0/D0244B8 | yes | primary | primary
node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | 0/D0244B8 | yes | secondary | secondary
次のようにPod上でpsqlを実行できます。
$ kubectl exec -it vehicle-db-1 -- bash -c 'psql -c "\l"'
Defaulting container name to pg-container.
Use 'kubectl describe pod/vehicle-db-1 -n default' to see all of the containers in this pod.
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
------------+----------+----------+---------+---------+-----------------------
postgres | postgres | UTF8 | C.UTF-8 | C.UTF-8 |
template0 | postgres | UTF8 | C.UTF-8 | C.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C.UTF-8 | C.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
vehicle-db | postgres | UTF8 | C.UTF-8 | C.UTF-8 |
(4 rows)
failoverの動作確認
failover時の動作を確認します。次のコマンドでfailoverを起こせます。
$ kubectl exec -ti pod/vehicle-db-1 -- pg_autoctl perform failover
Defaulting container name to pg-container.
Use 'kubectl describe pod/vehicle-db-1 -n default' to see all of the containers in this pod.
05:35:30 11534 INFO Targetting group 0 in formation "default"
05:35:30 11534 INFO Listening monitor notifications about state changes in formation "default" and group 0
05:35:30 11534 INFO Following table displays times when notifications are received
Time | Name | Node | Host:Port | Current State | Assigned State
---------+--------+-------+--------------------------------------------------------------+---------------------+--------------------
05:35:30 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | primary | draining
05:35:30 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | secondary | prepare_promotion
05:35:30 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | prepare_promotion | prepare_promotion
05:35:30 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | prepare_promotion | stop_replication
05:35:30 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | primary | demote_timeout
05:35:30 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | draining | demote_timeout
05:35:30 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | demote_timeout | demote_timeout
05:35:31 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | stop_replication | stop_replication
05:35:31 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | stop_replication | wait_primary
05:35:31 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | demote_timeout | demoted
05:35:31 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | demoted | demoted
05:35:31 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | wait_primary | wait_primary
05:35:31 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | demoted | catchingup
05:35:52 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | catchingup | catchingup
05:35:53 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | catchingup | secondary
05:35:53 | node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | secondary | secondary
05:35:54 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | wait_primary | primary
05:35:54 | node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | primary | primary
$ kubectl exec -ti pod/vehicle-db-1 -- pg_autoctl show state
Defaulting container name to pg-container.
Use 'kubectl describe pod/vehicle-db-1 -n default' to see all of the containers in this pod.
Name | Node | Host:Port | LSN | Reachable | Current State | Assigned State
-------+-------+--------------------------------------------------------------+-----------+-----------+---------------------+--------------------
node_1 | 1 | vehicle-db-0.vehicle-db-agent.default.svc.cluster.local:5432 | 0/F000610 | yes | secondary | secondary
node_2 | 2 | vehicle-db-1.vehicle-db-agent.default.svc.cluster.local:5432 | 0/F000610 | yes | primary | primary
Stateの変更が逐次出力され、primaryだったnode_1がsecondaryに、secondaryだったnode_2がprimaryに切り替わったことがわかります。
failover中はPodのラベルが次のように変わります。
$ kubectl get pod -l postgres-instance=vehicle-db,type=data --show-labels -w
NAME READY STATUS RESTARTS AGE LABELS
vehicle-db-0 3/3 Running 0 3m16s app=postgres,controller-revision-hash=vehicle-db-7c5bbd4d77,headless-service=vehicle-db,postgres-instance=vehicle-db,role=read-write,statefulset.kubernetes.io/pod-name=vehicle-db-0,type=data
vehicle-db-1 3/3 Running 0 3m26s app=postgres,controller-revision-hash=vehicle-db-7c5bbd4d77,headless-service=vehicle-db,postgres-instance=vehicle-db,role=read,statefulset.kubernetes.io/pod-name=vehicle-db-1,type=data
vehicle-db-0 3/3 Running 0 3m19s app=postgres,controller-revision-hash=vehicle-db-7c5bbd4d77,headless-service=vehicle-db,postgres-instance=vehicle-db,role=unavailable,statefulset.kubernetes.io/pod-name=vehicle-db-0,type=data
vehicle-db-1 3/3 Running 0 3m29s app=postgres,controller-revision-hash=vehicle-db-7c5bbd4d77,headless-service=vehicle-db,postgres-instance=vehicle-db,role=unavailable,statefulset.kubernetes.io/pod-name=vehicle-db-1,type=data
vehicle-db-1 3/3 Running 0 3m30s app=postgres,controller-revision-hash=vehicle-db-7c5bbd4d77,headless-service=vehicle-db,postgres-instance=vehicle-db,role=read-write,statefulset.kubernetes.io/pod-name=vehicle-db-1,type=data
vehicle-db-0 3/3 Running 0 3m42s app=postgres,controller-revision-hash=vehicle-db-7c5bbd4d77,headless-service=vehicle-db,postgres-instance=vehicle-db,role=read,statefulset.kubernetes.io/pod-name=vehicle-db-0,type=data
Spring Bootアプリから作成したPostgres Instanceへアクセス
vehicle-dbへアクセスするvehicle-apiアプリをデプロイします。
アプリケーションのDocker Imageは ghcr.io/making/vehicle-api です。
vehicle-apiのソースコードとDocker Imageの作成方法は Spring Boot and Cloud Native Buildpacks Hands-on Lab の"3. Getting Started"を参照してください。
vehicle-db-db-secretという名前のSecretにvehicle-dbの認証情報が格納されています。
項目は次の通りです。
$ kubectl describe secret vehicle-db-db-secret
Name: vehicle-db-db-secret
Namespace: default
Labels: app=postgres
postgres-instance=vehicle-db
Annotations: <none>
Type: Opaque
Data
====
username: 7 bytes
dbname: 10 bytes
instancename: 10 bytes
namespace: 7 bytes
password: 30 bytes
Config Treeを使ってvehicle-db-db-secretの各項目がspring.datasource.以下にバインドされるように次のmanifestをデプロイします。
URLはHeadlessではないvehicle-dbを使用します。
cat <<'EOF' > /tmp/vehicle-api.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: vehicle-api
name: vehicle-api
spec:
replicas: 1
selector:
matchLabels:
app: vehicle-api
template:
metadata:
labels:
app: vehicle-api
spec:
containers:
- image: ghcr.io/making/vehicle-api
name: vehicle-api
ports:
- containerPort: 8080
env:
- name: SPRING_CONFIG_IMPORT
value: configtree:/workspace/config/
- name: SPRING_DATASOURCE_URL
value: jdbc:postgresql://vehicle-db.${spring.datasource.namespace}.svc.cluster.local:5432/${spring.datasource.dbname}
- name: MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE
value: health,info
#! Tweak for less memory
- name: SPRING_DATASOURCE_HIKARI_MAXIMUMPOOLSIZE
value: "4"
- name: JAVA_TOOL_OPTIONS
value: -XX:ReservedCodeCacheSize=32M -Xss512k -Duser.timezone=Asia/Tokyo
- name: BPL_JVM_THREAD_COUNT
value: "20"
- name: SERVER_TOMCAT_THREADS_MAX
value: "4"
resources:
limits:
memory: 256Mi
requests:
memory: 256Mi
volumeMounts:
- name: vehicle-db
mountPath: /workspace/config/spring/datasource
readOnly: true
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
scheme: HTTP
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
scheme: HTTP
volumes:
- name: vehicle-db
secret:
secretName: vehicle-db-db-secret
---
apiVersion: v1
kind: Service
metadata:
labels:
app: vehicle-api
name: vehicle-api
spec:
ports:
- name: http
port: 80
targetPort: 8080
selector:
app: vehicle-api
EOF
kubectl apply -f /tmp/vehicle-api.yaml
デプロイできたら、kwtを使ってアクセスします。
別のターミナル上で次のコマンドを実行します。
sudo -E kwt net start
kwtが使えない場合は、代わりに次のコマンドを実行し、kubectl port-forward service/vehicle-api 8080:80以下の
vehicle-api.default.svc.cluster.localをlocalhost:8080に変更してください。
次のコマンドでvehicle-apiにアクセスします。
$ curl -s http://vehicle-api.default.svc.cluster.local/vehicles | jq .
[
{
"id": 1,
"name": "Avalon"
},
{
"id": 2,
"name": "Corolla"
},
{
"id": 3,
"name": "Crown"
},
{
"id": 4,
"name": "Levin"
},
{
"id": 5,
"name": "Yaris"
},
{
"id": 6,
"name": "Vios"
},
{
"id": 7,
"name": "Glanza"
},
{
"id": 8,
"name": "Aygo"
}
]
ここではprimaryへのアクセスはKubernetesのServiceの機能に任せましたが、JDBC Driver側に任せることもできます。 この場合はHeadless Serviceの
vehicle-db-agentを使用します。設定方法の詳細は次のドキュメントが参考になります。
- https://jdbc.postgresql.org/documentation/head/connect.html
- https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.BestPractices.html
まずsecondary(read)への接続を試行し、接続できない場合は、primary(read0write)に接続されるようにしたい場合の設定は次の通りです。
- name: SPRING_DATASOURCE_URL value: jdbc:postgresql://vehicle-db-0.vehicle-db-agent.${spring.datasource.namespace}.svc.cluster.local:5432,vehicle-db-1.vehicle-db-agent.${spring.datasource.namespace}.svc.cluster.local:5432/${spring.datasource.dbname} - name: SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_TARGETSERVERTYPE value: preferSecondary - name: SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_LOADBALANCEHOSTS value: "true"
Postgres InstanceへTLSでアクセス
Postgresリソースを作成するとPostgres ServerのTLS対応が自動で行われます。 TLS証明書はCertificateリソースとして作成されます。
このTLS証明書のCA証明書をアプリケーション側に信頼させてTLS接続するように設定を変更します。
PostgreSQL JDBC Driverのデフォルトのjavax.net.ssl.SSLSocketFactory実装であるorg.postgresql.ssl.LibPQFactoryは${HOME}/.postgresql/root.crtのPEMファイルを信頼された証明書とみなすので、
Certificateリソースが作成したSecret(vehicle-db-internal-ssl-secret)のca.crtをこのファイルにマウントします。
cat <<'EOF' > /tmp/vehicle-api.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: vehicle-api
name: vehicle-api
spec:
replicas: 1
selector:
matchLabels:
app: vehicle-api
template:
metadata:
labels:
app: vehicle-api
spec:
containers:
- image: ghcr.io/making/vehicle-api
name: vehicle-api
ports:
- containerPort: 8080
env:
- name: SPRING_CONFIG_IMPORT
value: configtree:/workspace/config/
- name: SPRING_DATASOURCE_URL
value: jdbc:postgresql://vehicle-db.${spring.datasource.namespace}.svc.cluster.local:5432/${spring.datasource.dbname}
- name: SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLMODE
value: verify-full
- name: SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLFACTORY
value: org.postgresql.ssl.LibPQFactory # Default
- name: MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE
value: health,info
#! Tweak for less memory
- name: SPRING_DATASOURCE_HIKARI_MAXIMUMPOOLSIZE
value: "4"
- name: JAVA_TOOL_OPTIONS
value: -XX:ReservedCodeCacheSize=32M -Xss512k -Duser.timezone=Asia/Tokyo
- name: BPL_JVM_THREAD_COUNT
value: "20"
- name: SERVER_TOMCAT_THREADS_MAX
value: "4"
resources:
limits:
memory: 256Mi
requests:
memory: 256Mi
volumeMounts:
- name: vehicle-db
mountPath: /workspace/config/spring/datasource
readOnly: true
- name: vehicle-db-cert
mountPath: /home/cnb/.postgresql/root.crt
subPath: ca.crt
readOnly: true
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
scheme: HTTP
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
scheme: HTTP
volumes:
- name: vehicle-db
secret:
secretName: vehicle-db-db-secret
- name: vehicle-db-cert
secret:
secretName: vehicle-db-internal-ssl-secret
---
apiVersion: v1
kind: Service
metadata:
labels:
app: vehicle-api
name: vehicle-api
spec:
ports:
- name: http
port: 80
targetPort: 8080
selector:
app: vehicle-api
EOF
kubectl apply -f /tmp/vehicle-api.yaml
次のコマンドでvehicle-apiにアクセスできることを確認します。
$ curl -s http://vehicle-api.default.svc.cluster.local/vehicles | jq .
[
{
"id": 1,
"name": "Avalon"
},
{
"id": 2,
"name": "Corolla"
},
{
"id": 3,
"name": "Crown"
},
{
"id": 4,
"name": "Levin"
},
{
"id": 5,
"name": "Yaris"
},
{
"id": 6,
"name": "Vios"
},
{
"id": 7,
"name": "Glanza"
},
{
"id": 8,
"name": "Aygo"
}
]
もしCA証明書をマウントできていなければ例外がスローされ、Stack Trace中に次のメッセージが含まれます。
Caused by: org.postgresql.util.PSQLException: Could not open SSL root certificate file /home/cnb/.postgresql/root.crt.
Postgres InstanceのデータをBackup
Tanzu PostgresはS3互換サーバーへのBackup・Restoreに対応しています。
https://postgres-kubernetes.docs.pivotal.io/1-2/backup-restore.html#backing_up_postgres
MinioにデータをBackupします。
Minioのインストール
Minioをインストールします。BackupがHTTPSにしか対応していないので、Cert Managerで証明書を作成します。
helm repo add bitnami https://charts.bitnami.com/bitnami
kubectl create namespace minio
cat <<EOF | kubectl apply -f-
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: minio-selfsigned-issuer
namespace: minio
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: minio-ca
namespace: minio
spec:
commonName: minio-ca
isCA: true
issuerRef:
kind: Issuer
name: minio-selfsigned-issuer
secretName: minio-ca
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: minio-ca-issuer
namespace: minio
spec:
ca:
secretName: minio-ca
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: minio-tls
namespace: minio
spec:
dnsNames:
- minio.minio.svc.cluster.local
- minio.minio
- 127.0.0.1
- localhost
issuerRef:
kind: Issuer
name: minio-ca-issuer
secretName: minio-tls
EOF
helm install -n minio minio bitnami/minio \
--set defaultBuckets=backup \
--set tls.enabled=true \
--set tls.existingSecret=minio-tls
次のリソースが作成されます。
$ kubectl get all,secret,issuer,certificate -n minio
NAME READY STATUS RESTARTS AGE
pod/minio-55458b8dc-cnbcc 1/1 Running 0 28m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/minio ClusterIP 10.96.120.177 <none> 9000/TCP 28m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/minio 1/1 1 1 28m
NAME DESIRED CURRENT READY AGE
replicaset.apps/minio-55458b8dc 1 1 1 28m
NAME TYPE DATA AGE
secret/default-token-62894 kubernetes.io/service-account-token 3 31m
secret/minio Opaque 3 28m
secret/minio-ca kubernetes.io/tls 3 31m
secret/minio-tls kubernetes.io/tls 3 31m
secret/minio-token-q6fg7 kubernetes.io/service-account-token 3 28m
secret/sh.helm.release.v1.minio.v1 helm.sh/release.v1 1 28m
NAME READY AGE
issuer.cert-manager.io/minio-ca-issuer True 31m
issuer.cert-manager.io/minio-selfsigned-issuer True 31m
NAME READY SECRET AGE
certificate.cert-manager.io/minio-ca True minio-ca 31m
certificate.cert-manager.io/minio-tls True minio-tls 31m
ACCESS_KEYとSECRET_KEYは次のように取得できます。
export ACCESS_KEY=$(kubectl get secret --namespace minio minio -o template='{{index .data "access-key" | base64decode}}')
export SECRET_KEY=$(kubectl get secret --namespace minio minio -o template='{{index .data "secret-key" | base64decode}}')
ブラウザで https://minio.minio.svc.cluster.local:9000 にアクセスし、ACCESS_KEYとSECRET_KEYを入力してログインします。
backupバケットが作成されていることを確認できます。

MinioにBackupするための設定
PostgresインスタンスにBackup先の設定を行います。
まず、次のSecretを作成します。
kubectl create secret generic s3-secret \
--from-literal=bucket=backup \
--from-literal=region=minio \
--from-literal=endpoint=minio.minio.svc.cluster.local \
--from-literal=key=${ACCESS_KEY} \
--from-literal=keySecret=${SECRET_KEY} \
--from-literal=port=9000 \
--from-literal=uriStyle=path \
--from-literal=verifyTLS=false \
--dry-run=client \
-oyaml \
| kubectl apply -f-
このSecret名をPostgresリソースのspec.backupLocationSecret.nameに設定します。
cat <<EOF | kubectl apply -f-
apiVersion: sql.tanzu.vmware.com/v1
kind: Postgres
metadata:
name: vehicle-db
namespace: default
spec:
storageClassName: standard
storageSize: 800M
cpu: "0.8"
memory: 800Mi
monitorStorageClassName: standard
monitorStorageSize: 1G
resources:
monitor:
limits:
cpu: 800m
memory: 800Mi
requests:
cpu: 800m
memory: 800Mi
pgConfig:
dbname: vehicle-db
username: pgadmin
serviceType: ClusterIP
highAvailability:
enabled: true
backupLocationSecret:
name: s3-secret
EOF
次のコマンドでS3互換サーバーとの疎通チェックができます。
kubectl exec -it $(kubectl get pod -l postgres-instance=vehicle-db,role=read-write -o jsonpath='{.items[0].metadata.name}') -c pg-container -- bash -c 'pgbackrest check --stanza=${BACKUP_STANZA_NAME}'
次のようなログが出力されればOKです。
2021-07-30 03:50:51.679 P00 INFO: check command begin 2.31: --config=/pgsql/custom/pgbackrest.conf --exec-id=1358-7b6d37bb --log-level-console=info --log-level-file=debug --log-path=/pgsql/logs --pg1-path=/pgsql/data --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-s3-bucket=backup --repo1-s3-endpoint=minio.minio.svc.cluster.local --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-port=9000 --repo1-s3-region=minio --repo1-s3-uri-style=path --no-repo1-s3-verify-tls --repo1-type=s3 --stanza=default-vehicle-db
2021-07-30 03:50:52.808 P00 INFO: WAL segment 00000010000000000000002C successfully archived to '/var/lib/pgbackrest/archive/default-vehicle-db/11-1/0000001000000000/00000010000000000000002C-37381308f2f91d19e651c6e2e45d46a01c09b7ac.gz'
2021-07-30 03:50:52.808 P00 INFO: check command end: completed successfully (1130ms)
チェックがうまくいかない場合は
--log-level-console=debugオプションを追加して原因を調べてください。
Backupの実行
次のコマンドでBackupを実行できます。
kubectl exec -it $(kubectl get pod -l postgres-instance=vehicle-db,role=read-write -o jsonpath='{.items[0].metadata.name}') -c pg-container -- bash -c 'pgbackrest backup --stanza=${BACKUP_STANZA_NAME}'
次のようなログが出力されればOKです。
2021-07-30 03:53:00.765 P00 INFO: backup command begin 2.31: --config=/pgsql/custom/pgbackrest.conf --exec-id=4192-4a8f18a8 --log-level-console=info --log-level-file=debug --log-path=/pgsql/logs --pg1-path=/pgsql/data --process-max=2 --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-retention-diff=1 --repo1-retention-full=2 --repo1-s3-bucket=backup --repo1-s3-endpoint=minio.minio.svc.cluster.local --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-port=9000 --repo1-s3-region=minio --repo1-s3-uri-style=path --no-repo1-s3-verify-tls --repo1-type=s3 --stanza=default-vehicle-db --start-fast
WARN: no prior backup exists, incr backup has been changed to full
2021-07-30 03:53:01.482 P00 INFO: execute non-exclusive pg_start_backup(): backup begins after the requested immediate checkpoint completes
2021-07-30 03:53:01.984 P00 INFO: backup start archive = 00000010000000000000002F, lsn = 0/2F000028
2021-07-30 03:53:03.303 P01 INFO: backup file /pgsql/data/base/16393/1255 (608KB, 1%) checksum 03522dc03523eff79db1ece7e075dcb9413e487a
...
2021-07-30 03:53:06.432 P02 INFO: backup file /pgsql/data/base/1/PG_VERSION (3B, 100%) checksum dd71038f3463f511ee7403dbcbc87195302d891c
2021-07-30 03:53:06.433 P01 INFO: backup file /pgsql/data/global/6100_vm (0B, 100%)
...
2021-07-30 03:53:07.209 P01 INFO: backup file /pgsql/data/base/1/13574 (0B, 100%)
2021-07-30 03:53:07.209 P00 INFO: full backup size = 31.3MB
2021-07-30 03:53:07.210 P00 INFO: execute non-exclusive pg_stop_backup() and wait for all WAL segments to archive
2021-07-30 03:53:07.415 P00 INFO: backup stop archive = 00000010000000000000002F, lsn = 0/2F000130
2021-07-30 03:53:07.423 P00 INFO: check archive for segment(s) 00000010000000000000002F:00000010000000000000002F
2021-07-30 03:53:07.561 P00 INFO: new backup label = 20210730-035301F
2021-07-30 03:53:07.602 P00 INFO: backup command end: completed successfully (6837ms)
2021-07-30 03:53:07.605 P00 INFO: expire command begin 2.31: --config=/pgsql/custom/pgbackrest.conf --exec-id=4192-4a8f18a8 --log-level-console=info --log-level-file=debug --log-path=/pgsql/logs --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-retention-diff=1 --repo1-retention-full=2 --repo1-s3-bucket=backup --repo1-s3-endpoint=minio.minio.svc.cluster.local --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-port=9000 --repo1-s3-region=minio --repo1-s3-uri-style=path --no-repo1-s3-verify-tls --repo1-type=s3 --stanza=default-vehicle-db
2021-07-30 03:53:07.621 P00 INFO: expire command end: completed successfully (17ms)
Minio上にもデータが作成されていることを確認できます。

デフォルトでは初回はfullBackup、2回目移行はincrementalなBackupになります。--type=fullオプションをつけてfullBackupを強制することもできます。
次のコマンドでBackup情報を確認できます。
kubectl exec -it $(kubectl get pod -l postgres-instance=vehicle-db,role=read-write -o jsonpath='{.items[0].metadata.name}') -c pg-container -- bash -c 'pgbackrest info --stanza=${BACKUP_STANZA_NAME}'
次のようなログが出力されます。
stanza: default-vehicle-db
status: ok
cipher: aes-256-cbc
db (current)
wal archive min/max (11-1): 00000010000000000000002B/00000010000000000000002F
full backup: 20210730-035301F
timestamp start/stop: 2021-07-30 03:53:01 / 2021-07-30 03:53:07
wal start/stop: 00000010000000000000002F / 00000010000000000000002F
database size: 31.3MB, backup size: 31.3MB
repository size: 3.7MB, repository backup size: 3.7MB
TBD: Postgres InstanceのデータをRestore
https://postgres-kubernetes.docs.pivotal.io/1-2/backup-restore.html#restore