backupdb.sh 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #!/usr/bin/env bash
  2. (
  3. flock -n 9 || { logger -t PostgreSQL databases backup is already running!; exit 1; }
  4. print_elapsed_time() {
  5. time_elapsed=$(( $2-$1 ))
  6. milliseconds=$(( $time_elapsed%1000 ))
  7. seconds=$(( ($time_elapsed/1000)%60 ))
  8. minutes=$(( ($time_elapsed/1000/60)%60 ))
  9. hours=$(( ($time_elapsed/1000/60/60)%24 ))
  10. (( $hours > 0 )) && printf '%dh' $hours
  11. (( $minutes > 0 )) && printf '%dm' $minutes
  12. (( $seconds > 0 )) && printf '%ds' $seconds
  13. printf '%dms\n' $milliseconds
  14. }
  15. # ... commands executed under lock ...
  16. # Milliseconds from epoch
  17. backup_start_time=$(date +%s%3N)
  18. global_log_file="/tmp/backupdb-global.log"
  19. echo '###################################################################################################' | tee $global_log_file
  20. echo '########################### PostgreSQL databases backup has started! ##########################' | tee -a $global_log_file
  21. echo "########################### $(date '+%Y-%m-%d %H:%M:%S') #########################" | tee -a $global_log_file
  22. echo '###################################################################################################' | tee -a $global_log_file
  23. # Connect to remote storage and mount the DIR using sshfs, webdav, rclone, or samba
  24. echo "$(date '+%Y-%m-%d %H:%M:%S') - mount remote storage..." | tee -a $global_log_file
  25. # sshfs -o port=22 remote-user@remote-server:/remote-dir/ /mnt/backups/
  26. count=$(ls /mnt/backups/ | wc -l)
  27. if [[ $count > 0 ]]; then
  28. echo "$(date '+%Y-%m-%d %H:%M:%S') - remote storage has been mounted successfully..." | tee -a $global_log_file
  29. else
  30. echo "$(date '+%Y-%m-%d %H:%M:%S') - error mounting remote storage, backup process can't continue!" | tee -a $global_log_file
  31. exit 2
  32. fi
  33. # Create temporary DIR
  34. tmp_dir=$(mktemp -d)
  35. echo "$(date '+%Y-%m-%d %H:%M:%S') - $tmp_dir dir has created!" | tee -a $global_log_file
  36. last_backup_file=$tmp_dir/last-backup.sh
  37. echo "#!/usr/bin/env bash" > $last_backup_file
  38. echo "declare -A backups" >> $last_backup_file
  39. echo "$(date '+%Y-%m-%d %H:%M:%S') - $last_backup_file file has been created!" | tee -a $global_log_file
  40. # Retrieve all databases from PostgreSQL
  41. excluded_db="db.datname NOT LIKE E'\'template%\''"
  42. excluded_schema="nspname NOT LIKE E'\'pg_toast%\'' AND nspname NOT LIKE E'\'pg_temp%\'' AND nspname NOT IN (E'\'pg_catalog\'', E'\'information_schema\'')"
  43. echo "$(date '+%Y-%m-%d %H:%M:%S') - retrieve all databases" | tee -a $global_log_file
  44. databases=$(su - postgres -c "psql -Atc 'SELECT db.datname FROM pg_database db INNER JOIN pg_authid a ON a.oid = db.datdba WHERE $excluded_db ORDER BY db.datname'")
  45. #echo "|-- $databases" | cut -d'|' -f 1 | tee -a $global_log_file
  46. #i=0
  47. for db in $databases; do
  48. #(($i >= 3)) && continue
  49. #((i++))
  50. # Milliseconds from epoch
  51. #dbname=$(echo $db | cut -d'|' -f 1)
  52. start_time=$(date +%s%3N)
  53. dbname=$db
  54. log_file="$tmp_dir/${dbname}.log"
  55. echo "$(date '+%Y-%m-%d %H:%M:%S') - ===============================================================================" | tee -a $global_log_file $log_file
  56. echo "$(date '+%Y-%m-%d %H:%M:%S') - backup for $dbname database has started!" | tee -a $global_log_file $log_file
  57. echo "$(date '+%Y-%m-%d %H:%M:%S') - ===============================================================================" | tee -a $global_log_file $log_file
  58. # For later uses
  59. #username=$(echo $db | cut -d'|' -f 2)
  60. local_dir="/srv/backups/postgres/$dbname"
  61. remote_dir="/mnt/backups/postgres/$dbname"
  62. backup_dir_name=$(date '+%Y%m%d-%H%M')
  63. backup_dir="$remote_dir/$backup_dir_name"
  64. temp_file="$tmp_dir/${dbname}.gz"
  65. # Make remote and local dirs if not exits
  66. mkdir -p "$local_dir" "$backup_dir"
  67. # Dumping the sql and gzip
  68. echo "$(date '+%Y-%m-%d %H:%M:%S') - * dump sql file and compress with gzip..." | tee -a $global_log_file $log_file
  69. op_start_time=$(date +%s%3N)
  70. su - postgres -c "pg_dump -U postgres --clean --if-exists $dbname" | gzip > $temp_file
  71. op_end_time=$(date +%s%3N)
  72. echo "$(date '+%Y-%m-%d %H:%M:%S') - * dump completed after" $(print_elapsed_time $op_start_time $op_end_time) | tee -a $global_log_file $log_file
  73. # Track last backup, important for restore
  74. echo "$(date '+%Y-%m-%d %H:%M:%S') - * track backup dir: $backup_dir" | tee -a $global_log_file $log_file
  75. echo "backups['$dbname']=${backup_dir_name}" >> $last_backup_file
  76. # Copy backup to local dir
  77. cp -vf $temp_file $local_dir | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * copied: {}" | tee -a $global_log_file $log_file
  78. # Copy backup to remote dir
  79. cp -vf $temp_file $backup_dir | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * copied: {}" | tee -a $global_log_file $log_file
  80. # Remove backup temporary file
  81. rm -vf $temp_file | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * {}" | tee -a $global_log_file $log_file
  82. # Retrieve all shemas for current database
  83. schemas=$(su - postgres -c "psql -d $dbname -Atc 'SELECT nspname FROM pg_catalog.pg_namespace WHERE $excluded_schema ORDER BY nspname'")
  84. for schema in $schemas; do
  85. schema_start_time=$(date +%s%3N)
  86. echo "$(date '+%Y-%m-%d %H:%M:%S') - * :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" | tee -a $global_log_file $log_file
  87. echo "$(date '+%Y-%m-%d %H:%M:%S') - * backup for $dbname.$schema schema has started!" | tee -a $global_log_file $log_file
  88. echo "$(date '+%Y-%m-%d %H:%M:%S') - * :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" | tee -a $global_log_file $log_file
  89. temp_file="$tmp_dir/${dbname}-${schema}.gz"
  90. # Dumping the sql and gzip
  91. echo "$(date '+%Y-%m-%d %H:%M:%S') - * + dump sql file and compress with gzip..." | tee -a $global_log_file $log_file
  92. op_start_time=$(date +%s%3N)
  93. su - postgres -c "pg_dump -U postgres --clean --if-exists -n $schema $dbname" | gzip > $temp_file
  94. op_end_time=$(date +%s%3N)
  95. echo "$(date '+%Y-%m-%d %H:%M:%S') - * + dump completed after" $(print_elapsed_time $op_start_time $op_end_time) | tee -a $global_log_file $log_file
  96. # Copy backup to local dir
  97. cp -vf $temp_file $local_dir | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * + copied: {}" | tee -a $global_log_file $log_file
  98. # Copy backup to remote dir
  99. cp -vf $temp_file $backup_dir | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * + copied: {}" | tee -a $global_log_file $log_file
  100. # Remove backup temporary file
  101. rm -vf $temp_file | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * + {}" | tee -a $global_log_file $log_file
  102. schema_end_time=$(date +%s%3N)
  103. echo "$(date '+%Y-%m-%d %H:%M:%S') - * + backup for $dbname.$schema schema has completed after" $(print_elapsed_time $schema_start_time $schema_end_time) | tee -a $global_log_file $log_file
  104. done
  105. end_time=$(date +%s%3N)
  106. echo "$(date '+%Y-%m-%d %H:%M:%S') - * backup for $dbname database has completed after" $(print_elapsed_time $start_time $end_time) | tee -a $global_log_file $log_file
  107. # Copy log file
  108. cp -vf $log_file $local_dir | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * copied {}" | tee -a $global_log_file
  109. cp -vf $log_file $backup_dir | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - * copied {}" | tee -a $global_log_file
  110. done
  111. cp -fv $last_backup_file /srv/backups/postgres | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - copied {}" | tee -a $global_log_file
  112. cp -fv $last_backup_file /mnt/backups/postgres | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - copied {}" | tee -a $global_log_file
  113. # Remove backup temporary file
  114. rm -rfv $tmp_dir | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - {}" | tee -a $global_log_file
  115. # Remove old backups
  116. echo "$(date '+%Y-%m-%d %H:%M:%S') - Remove old backups (+60 days)" | tee -a $global_log_file
  117. find /mnt/backups/postgres -maxdepth 2 -mtime +60 -print0 | xargs -0 rm -rfv | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') - !! {}" | tee -a $global_log_file
  118. backup_end_time=$(date +%s%3N)
  119. echo "$(date '+%Y-%m-%d %H:%M:%S') - PostgreSQL backup has completed after" $(print_elapsed_time $backup_start_time $backup_end_time) | tee -a $global_log_file
  120. cp -fv $global_log_file /srv/backups/postgres/global.log | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') => copied {}"
  121. cp -fv $global_log_file /mnt/backups/postgres/global.log | xargs -I{} echo "$(date '+%Y-%m-%d %H:%M:%S') => copied {}"
  122. echo "$(date '+%Y-%m-%d %H:%M:%S') => unmount remote storage..."
  123. #umount -lq /mnt/backups
  124. ) 9> /var/tmp/backupdb.lock
  125. exit $?