Logical backup retention time (#1337)

* Add optional logical backup retention time
* Set defaults for potentially unbound variables, so that the script will work with older operator versions
* Document retention time parameter for logical backups
* Add retention time parameter to resources and charts

Co-authored-by: Felix Kunde <felix-kunde@gmx.de>
This commit is contained in:
A. Stoewer 2022-03-02 17:39:33 +01:00 committed by GitHub
parent ca0c27a51b
commit 695ad44caf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 78 additions and 0 deletions

View File

@ -450,6 +450,8 @@ spec:
type: string type: string
logical_backup_s3_sse: logical_backup_s3_sse:
type: string type: string
logical_backup_s3_retention_time:
type: string
logical_backup_schedule: logical_backup_schedule:
type: string type: string
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'

View File

@ -310,6 +310,8 @@ configLogicalBackup:
logical_backup_s3_secret_access_key: "" logical_backup_s3_secret_access_key: ""
# S3 server side encryption # S3 server side encryption
logical_backup_s3_sse: "AES256" logical_backup_s3_sse: "AES256"
# S3 retention time for stored backups for example "2 week" or "7 days"
logical_backup_s3_retention_time: ""
# backup schedule in the cron format # backup schedule in the cron format
logical_backup_schedule: "30 00 * * *" logical_backup_schedule: "30 00 * * *"

View File

@ -15,6 +15,9 @@ TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
K8S_API_URL=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1 K8S_API_URL=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1
CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
LOGICAL_BACKUP_PROVIDER=${LOGICAL_BACKUP_PROVIDER:="s3"}
LOGICAL_BACKUP_S3_RETENTION_TIME=${LOGICAL_BACKUP_S3_RETENTION_TIME:=""}
function estimate_size { function estimate_size {
"$PG_BIN"/psql -tqAc "${ALL_DB_SIZE_QUERY}" "$PG_BIN"/psql -tqAc "${ALL_DB_SIZE_QUERY}"
} }
@ -28,6 +31,57 @@ function compress {
pigz pigz
} }
function aws_delete_objects {
args=(
"--bucket=$LOGICAL_BACKUP_S3_BUCKET"
)
[[ ! -z "$LOGICAL_BACKUP_S3_ENDPOINT" ]] && args+=("--endpoint-url=$LOGICAL_BACKUP_S3_ENDPOINT")
[[ ! -z "$LOGICAL_BACKUP_S3_REGION" ]] && args+=("--region=$LOGICAL_BACKUP_S3_REGION")
aws s3api delete-objects "${args[@]}" --delete Objects=["$(printf {Key=%q}, "$@")"],Quiet=true
}
export -f aws_delete_objects
function aws_delete_outdated {
if [[ -z "$LOGICAL_BACKUP_S3_RETENTION_TIME" ]] ; then
echo "no retention time configured: skip cleanup of outdated backups"
return 0
fi
# define cutoff date for outdated backups (day precision)
cutoff_date=$(date -d "$LOGICAL_BACKUP_S3_RETENTION_TIME ago" +%F)
# mimic bucket setup from Spilo
prefix="spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"
args=(
"--no-paginate"
"--output=text"
"--prefix=$prefix"
"--bucket=$LOGICAL_BACKUP_S3_BUCKET"
)
[[ ! -z "$LOGICAL_BACKUP_S3_ENDPOINT" ]] && args+=("--endpoint-url=$LOGICAL_BACKUP_S3_ENDPOINT")
[[ ! -z "$LOGICAL_BACKUP_S3_REGION" ]] && args+=("--region=$LOGICAL_BACKUP_S3_REGION")
# list objects older than the cutoff date
aws s3api list-objects "${args[@]}" --query="Contents[?LastModified<='$cutoff_date'].[Key]" > /tmp/outdated-backups
# spare the last backup
sed -i '$d' /tmp/outdated-backups
count=$(wc -l < /tmp/outdated-backups)
if [[ $count == 0 ]] ; then
echo "no outdated backups to delete"
return 0
fi
echo "deleting $count outdated backups created before $cutoff_date"
# deleted outdated files in batches with 100 at a time
tr '\n' '\0' < /tmp/outdated-backups | xargs -0 -P1 -n100 bash -c 'aws_delete_objects "$@"' _
}
function aws_upload { function aws_upload {
declare -r EXPECTED_SIZE="$1" declare -r EXPECTED_SIZE="$1"
@ -59,6 +113,7 @@ function upload {
;; ;;
*) *)
aws_upload $(($(estimate_size) / DUMP_SIZE_COEFF)) aws_upload $(($(estimate_size) / DUMP_SIZE_COEFF))
aws_delete_outdated
;; ;;
esac esac
} }

View File

@ -676,6 +676,11 @@ grouped under the `logical_backup` key.
Specify server side encryption that S3 storage is using. If empty string Specify server side encryption that S3 storage is using. If empty string
is specified, no argument will be passed to `aws s3` command. Default: "AES256". is specified, no argument will be passed to `aws s3` command. Default: "AES256".
* **logical_backup_s3_retention_time**
Specify a retention time for logical backups stored in S3. Backups older than the specified retention
time will be deleted after a new backup was uploaded. If empty, all backups will be kept. Example values are
"3 days", "2 weeks", or "1 month". The default is empty.
* **logical_backup_schedule** * **logical_backup_schedule**
Backup schedule in the cron format. Please take the Backup schedule in the cron format. Please take the
[reference schedule format](https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/#schedule) [reference schedule format](https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/#schedule)

View File

@ -77,6 +77,7 @@ data:
# logical_backup_s3_endpoint: "" # logical_backup_s3_endpoint: ""
# logical_backup_s3_secret_access_key: "" # logical_backup_s3_secret_access_key: ""
logical_backup_s3_sse: "AES256" logical_backup_s3_sse: "AES256"
# logical_backup_s3_retention_time: ""
logical_backup_schedule: "30 00 * * *" logical_backup_schedule: "30 00 * * *"
major_version_upgrade_mode: "manual" major_version_upgrade_mode: "manual"
# major_version_upgrade_team_allow_list: "" # major_version_upgrade_team_allow_list: ""

View File

@ -448,6 +448,8 @@ spec:
type: string type: string
logical_backup_s3_sse: logical_backup_s3_sse:
type: string type: string
logical_backup_s3_retention_time:
type: string
logical_backup_schedule: logical_backup_schedule:
type: string type: string
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'

View File

@ -146,6 +146,7 @@ configuration:
# logical_backup_s3_region: "" # logical_backup_s3_region: ""
# logical_backup_s3_secret_access_key: "" # logical_backup_s3_secret_access_key: ""
logical_backup_s3_sse: "AES256" logical_backup_s3_sse: "AES256"
# logical_backup_s3_retention_time: ""
logical_backup_schedule: "30 00 * * *" logical_backup_schedule: "30 00 * * *"
debug: debug:
debug_logging: true debug_logging: true

View File

@ -1556,6 +1556,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
"logical_backup_s3_sse": { "logical_backup_s3_sse": {
Type: "string", Type: "string",
}, },
"logical_backup_s3_retention_time": {
Type: "string",
},
"logical_backup_schedule": { "logical_backup_schedule": {
Type: "string", Type: "string",
Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$", Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$",

View File

@ -213,6 +213,7 @@ type OperatorLogicalBackupConfiguration struct {
S3AccessKeyID string `json:"logical_backup_s3_access_key_id,omitempty"` S3AccessKeyID string `json:"logical_backup_s3_access_key_id,omitempty"`
S3SecretAccessKey string `json:"logical_backup_s3_secret_access_key,omitempty"` S3SecretAccessKey string `json:"logical_backup_s3_secret_access_key,omitempty"`
S3SSE string `json:"logical_backup_s3_sse,omitempty"` S3SSE string `json:"logical_backup_s3_sse,omitempty"`
RetentionTime string `json:"logical_backup_s3_retention_time,omitempty"`
GoogleApplicationCredentials string `json:"logical_backup_google_application_credentials,omitempty"` GoogleApplicationCredentials string `json:"logical_backup_google_application_credentials,omitempty"`
JobPrefix string `json:"logical_backup_job_prefix,omitempty"` JobPrefix string `json:"logical_backup_job_prefix,omitempty"`
} }

View File

@ -2134,6 +2134,10 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
Name: "LOGICAL_BACKUP_S3_SSE", Name: "LOGICAL_BACKUP_S3_SSE",
Value: c.OpConfig.LogicalBackup.LogicalBackupS3SSE, Value: c.OpConfig.LogicalBackup.LogicalBackupS3SSE,
}, },
{
Name: "LOGICAL_BACKUP_S3_RETENTION_TIME",
Value: c.OpConfig.LogicalBackup.LogicalBackupS3RetentionTime,
},
{ {
Name: "LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX", Name: "LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX",
Value: getBucketScopeSuffix(string(c.Postgresql.GetUID())), Value: getBucketScopeSuffix(string(c.Postgresql.GetUID())),

View File

@ -170,6 +170,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.LogicalBackupS3AccessKeyID = fromCRD.LogicalBackup.S3AccessKeyID result.LogicalBackupS3AccessKeyID = fromCRD.LogicalBackup.S3AccessKeyID
result.LogicalBackupS3SecretAccessKey = fromCRD.LogicalBackup.S3SecretAccessKey result.LogicalBackupS3SecretAccessKey = fromCRD.LogicalBackup.S3SecretAccessKey
result.LogicalBackupS3SSE = fromCRD.LogicalBackup.S3SSE result.LogicalBackupS3SSE = fromCRD.LogicalBackup.S3SSE
result.LogicalBackupS3RetentionTime = fromCRD.LogicalBackup.RetentionTime
result.LogicalBackupGoogleApplicationCredentials = fromCRD.LogicalBackup.GoogleApplicationCredentials result.LogicalBackupGoogleApplicationCredentials = fromCRD.LogicalBackup.GoogleApplicationCredentials
result.LogicalBackupJobPrefix = util.Coalesce(fromCRD.LogicalBackup.JobPrefix, "logical-backup-") result.LogicalBackupJobPrefix = util.Coalesce(fromCRD.LogicalBackup.JobPrefix, "logical-backup-")

View File

@ -128,6 +128,7 @@ type LogicalBackup struct {
LogicalBackupS3AccessKeyID string `name:"logical_backup_s3_access_key_id" default:""` LogicalBackupS3AccessKeyID string `name:"logical_backup_s3_access_key_id" default:""`
LogicalBackupS3SecretAccessKey string `name:"logical_backup_s3_secret_access_key" default:""` LogicalBackupS3SecretAccessKey string `name:"logical_backup_s3_secret_access_key" default:""`
LogicalBackupS3SSE string `name:"logical_backup_s3_sse" default:""` LogicalBackupS3SSE string `name:"logical_backup_s3_sse" default:""`
LogicalBackupS3RetentionTime string `name:"logical_backup_s3_retention_time" default:""`
LogicalBackupGoogleApplicationCredentials string `name:"logical_backup_google_application_credentials" default:""` LogicalBackupGoogleApplicationCredentials string `name:"logical_backup_google_application_credentials" default:""`
LogicalBackupJobPrefix string `name:"logical_backup_job_prefix" default:"logical-backup-"` LogicalBackupJobPrefix string `name:"logical_backup_job_prefix" default:"logical-backup-"`
} }