# Copia continua (archiving) 

---

# Copia continua (archiving) 

* Se **archivan** los archivos WAL a otra ruta a medida que se van generando
* Podremos **restaurar los datos** de los arhivos WAL que queramos **a partir de un basebackup**
* Lo **interesante** es que los archivos WAL **se saquen del servidor**
* Opciones:
    * **Artesanal**: se copian los archivos mediante rsync o a una ruta de red
    * **[Barman](http://docs.pgbarman.org/#introduction)**: Software de servidor que maneja WALs y basebackups de multiples servidores
    * **[Wal-e](https://github.com/wal-e/wal-e)**: Software que sube los WAL y basebackups a AWS S3
    
---

# Copia continua (archiving configuración) 

    !bash
    # postgresql.conf
    archive_mode = on # activar la copia continua (archiving)
    archive_command = 'wal-push OR barman OR rsync OR ... %p' # comando para archivar el wal
    archive_timeout = 1800      # Numero de segundos tras los que forzar que un log file cambie
    
* El parámetro %p es el que recibirá el comando para saber de que logfile se trata (nombre de fichero)

---

# barman

---

# barman

* [Introducción comercial](http://www.youtube.com/embed/Ka-4R43XJFs)
* Se instala en un **servidor propio**
* Conexión entre los clientes y el servidor via **key ssh**
* El servidor barman tiene que poder ejecutar como **superusuario** en los servidores postgresql
* Hace tanto los **basebackups** como copiado de los **archives**
* Empaquetado en ubuntu 14.04

---

# barman (uso)

    !bash
    # Pruebas con barman, teniendo barman y el servidor postgresql en el mismo servidor
    # Hay que configurar el acceso del usuario barman por claves ssh
    
    # postgresql.conf
    wal_level = archive
    archive_mode = on
    archive_command = 'rsync -a %p barman@localhost:main/incoming/%f'  
    
    # /etc/barman.conf
    [main]
    description =  "Main PostgreSQL Database" 
    ssh_command = ssh postgres@localhost
    conninfo = host=localhost user=postgres password=1234
    minimum_redundancy = 1
    retention_policy = RECOVERY WINDOW OF 4 WEEKS

---

# barman (uso)
    
    !bash
    barman@apsl-edu:~$ barman check main
    Server main:
        ssh: OK
        PostgreSQL: OK
        archive_mode: OK
        archive_command: OK
        directories: OK
        retention policy settings: OK
        compression settings: OK
        minimum redundancy requirements: FAILED (have 0 backups, expected at least 1)       
    barman@apsl-edu:~$ barman list-server
    main - Main PostgreSQL Database
    barman@apsl-edu:~$ barman backup main
    Starting backup for server main in /var/lib/barman/main/base/20140723T163443
    Backup start at xlog location: 0/23000028 (000000010000000000000023, 00000028)
    Copying files.
    Copy done.
    Asking PostgreSQL server to finalize the backup.
    Backup end at xlog location: 0/230000B8 (000000010000000000000023, 000000B8)
    Backup completed
    barman@apsl-edu:~$ barman check main
    Server main:
        ssh: OK
        PostgreSQL: OK
        archive_mode: OK
        archive_command: OK
        directories: OK
        retention policy settings: OK
        compression settings: OK
        minimum redundancy requirements: OK (have 1 backups, expected at least 1)
---

# barman (uso)

    !bash
    # Ya se pueden listar los backups que haya para un servidor determinado
    barman@apsl-edu:~$ barman list-backup main
    main 20140723T163443 - Wed Jul 23 16:34:45 2014 - Size: 159.0 MiB - WAL Size: 0 B
    main 20140723T163133 - Wed Jul 23 16:31:37 2014 - Size: 159.0 MiB - WAL Size: 16.0 MiB
    
    # Se puede recuperar un backup en local, con
    barman recover main 20140723T163133 /ruta/local/para/recuperar

    # Incluso se puede hacer directamente en el server remoto con (o en otro server remoto!)
    barman@apsl-edu:~$ barman recover --remote-ssh-command='ssh postgres@localhost' \
    main 20140723T163133 /var/lib/postgresql/9.3/main
    Starting remote restore for server main using backup 20140723T163133 
    Destination directory: /var/lib/postgresql/9.3/main
    Copying the base backup.
    Copying required wal segments.
    The archive_command was set to 'false' to prevent data losses.

    Your PostgreSQL server has been successfully prepared for recovery!

    Please review network and archive related settings in the PostgreSQL
    configuration file before starting the just recovered instance.

    WARNING: Before starting up the recovered PostgreSQL server,
    please review also the settings of the following configuration
    options as they might interfere with your current recovery attempt:

        data_directory = '/var/lib/postgresql/9.3/main' # use data in another directory
        external_pid_file = '/var/run/postgresql/9.3-main.pid' # write an extra PID file
        hba_file = '/etc/postgresql/9.3/main/pg_hba.conf' # host-based authentication file
        ident_file = '/etc/postgresql/9.3/main/pg_ident.conf' # ident configuration file
        ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' # (change requires restart)
        ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' # (change requires restart)

---

# barman (uso)

    !bash
    # Está preparado para hacer recovery en un punto en el tiempo
    # pasandole uno de estos parámetros extra el comando recover
    
    --target-time TARGET_TIME: to specify a timestamp
    --target-xid TARGET_XID: to specify a transaction ID
    --target-name TARGET_NAME: to specify a named restore point -
        previously created with the pg_create_restore_point(name)

* Conclusiones:
    * Es una buena opción para no tener que manejar los archives a mano
    * Está muy bien hecho y es robusto


---

# wal-e

---

# wal-e
* [Wal-e](https://github.com/wal-e/wal-e), copyright con posibilidad de modificación y distribución.
* Originalmente desarrollado por heroku
* Programa hecho en **python** que se configura **en cada servidor** postgresql
* **Sube los archivos** a Amazon Web Services **S3** (Simple Storage Service)
* Proporciona comandos:
    * **backup-fetch**: traer un basebackup desde S3
    * **backup-push**: subir un basebackup a S3
    * **wal-fetch**: traer un archivo WAL desde S3
    * **wal-push**: subir un archivo WAL a S3
    * **backup-list**: para listar que backups hay guardados en S3
    * **delete**: para borrar backups antiguos que ya no queramos conservar
        * delete **retain**: deja el número de backups que le especifiquemos
* [Utilizado por instagram](http://instagram-engineering.tumblr.com/post/40781627982/handling-growth-with-postgres-5-tips-from-instagram)
    * *Overall, we’ve been very happy with Postgres’ performance and reliability.*

---

# wal-e (instalación y configuración)
    !bash
    # Dependecias de sistema
    aptitude install daemontools python-dev git libevent-dev pv lzop python-pip
    
    # Instalamos wal-e
    pip install six==1.7.3 # Requisito de wal-e en algunos sitemas con six antiguo
    pip install wal-e

    # Configuración de conexión
    postgres@apsl-edu:/etc/wal-e.d/env$ ls -l
    -rwxr--r-- 1 root postgres 21 oct 27 16:44 AWS_ACCESS_KEY_ID
    -rwxr--r-- 1 root postgres 41 oct 28 09:39 AWS_SECRET_ACCESS_KEY
    -rwxr--r-- 1 root postgres 33 oct 27 16:45 WALE_S3_PREFIX

    
    # postgresql.conf
    archive_mode = on
    archive_command = '/usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e wal-push %p'
    archive_timeout = 30 # Tiempo muy bajo, solo para las pruebas, nosotros configuramos 1800
    
    # Crons típicos
    postgres@apl-edu:~$ crontab -l
    # Base backup diario
    25 0 * * * /usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e backup-push \ 
    /var/lib/postgresql/9.3
    # Borramos backups antiguos, retencion de los ultimos 10 dias
    0 3 * * * /usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e delete --confirm retain 10

---

# wal-e (monitorización del basebackup)

    !python
    #!/usr/bin/python
    ### Check if the backup wal-e postgres is executed in the last day

    import subprocess
    import sys
    from datetime import datetime, timedelta
    import time

    command = "/usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e backup-list | tail -1"

    p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
    output, err = p.communicate()
    last =  output

    last_string = last.split()[1][:-5]
    last_datetime = datetime.strptime(last_string, "%Y-%m-%dT%H:%M:%S")
    #print last_datetime

    last_expected = datetime.now() - timedelta(hours=28)
    #print last_expected

    if last_datetime < last_expected:
        print "CRITICAL: Ultimo backup en wal-e es de mas de un dia, el ultimo es de: %s !" % last_datetime
    else:
        print "OK. Backup correcto. Ultimo backup es de: %s" % last_datetime

* [Script propio que revisa si existen los backups completos en wal-e](https://github.com/APSL/postgresql-wal-e-nagios/blob/master/check_wale.py)
    
---
# wal-e (monitorización de la subida de los wals)

* No he visto fallar la subida, pero **hay que controlarlo**
* Dos opciones: 
    * [Un wrapper](https://gist.github.com/mikeyk/4550560) que ejecute el comando de subida y informe de error
    * **Monitorizar el log** de postgresql para ver si hay fallos
        * [Ejemplo de log de subida correcta y errónea](https://gist.github.com/eduherraiz/8b03eb4f9d47f7cd2f25)

---

# Copia continua (planificación prueba) 

* **Configurar el cluster** para que haga archiving hacia wal-e
    * Con un archive_timeout bajo para que suba archivos cada poco tiempo
* Dejar **corriendo un elefants.py** en un cluster (5432 por ejemplo)
* Hacer un **basebackup** con wal-e
* Dejar un tiempo que se **sigan subiendo** archivos de **WAL** a S3
* **Restaurar** en otro cluster diferente con un **recovery.**conf para que traiga los archivos de S3

---

# Copia continua (prueba) 
    !bash
    # Lanzamos el checker
    edu@apsl-edu:~/Proyectos/postgresql-checker$ python elefants.py
    0 elefantes se balanceaban, como veian que no se caian, fueron a llamar a otro elefante, llamado: 0
    1 elefantes se balanceaban, como veian que no se caian, fueron a llamar a otro elefante, llamado: 1
    ...
    
    # Lanzamos el basebackup con wal-e
    postgres@apsl-edu:~$ /usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e \
    backup-push /var/lib/postgresql/9.3/main
    wal_e.operator.backup INFO     MSG: start upload postgres version metadata
            DETAIL: Uploading to s3://cursopostgres-wale/test-edu/basebackups_005/base_000000010000000000000020_00000040/extended_version.txt.
            STRUCTURED: time=2014-10-28T09:33:22.893252-00 pid=17929
    wal_e.operator.backup INFO     MSG: postgres version metadata upload complete
            STRUCTURED: time=2014-10-28T09:33:23.241488-00 pid=17929
    wal_e.worker.upload INFO     MSG: beginning volume compression
            DETAIL: Building volume 0.
            STRUCTURED: time=2014-10-28T09:33:23.269704-00 pid=17929
    wal_e.worker.upload INFO     MSG: begin uploading a base backup volume
            DETAIL: Uploading to "s3://cursopostgres-wale/test-edu/basebackups_005/base_000000010000000000000020_00000040/tar_partitions/part_00000000.tar.lzo".
            STRUCTURED: time=2014-10-28T09:33:23.557607-00 pid=17929
    wal_e.worker.upload INFO     MSG: finish uploading a base backup volume
            DETAIL: Uploading to "s3://cursopostgres-wale/test-edu/basebackups_005/base_000000010000000000000020_00000040/tar_partitions/part_00000000.tar.lzo" complete at 122.419KiB/s.
            STRUCTURED: time=2014-10-28T09:34:01.338401-00 pid=17929
    NOTICE:  pg_stop_backup complete, all required WAL segments have been archived

--- 

# Copia continua (prueba) 

    !bash
    # Podemos listar los backups existentes
    postgres@apsl-edu:~$ /usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e backup-list
    name    last_modified   expanded_size_bytes     wal_segment_backup_start        wal_segment_offset_backup_start wal_segment_backup_stop wal_segment_offset_backup_stop
    base_000000010000000000000020_00000040  2014-10-28T09:34:03.000Z                000000010000000000000020        00000040

    # Creamos un cluster para la restauración del backup
    postgres@apsl-edu:~$ pg_createcluster 9.3 wale
    Creating new cluster 9.3/wale ...
    config /etc/postgresql/9.3/wale
    data   /var/lib/postgresql/9.3/wale
    locale es_ES.UTF-8
    port   5433

    # Borramos el contenido de la carpeta de datos del nuevo cluster creado
    postgres@apsl-edu:~$ rm -R /var/lib/postgresql/9.3/wale/*
    
    # Restauramos el basebackup 
    postgres@apsl-edu:~$ /usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e backup-fetch \ 
    /var/lib/postgresql/9.3/wale/ base_000000010000000000000020_00000040
    # Donde hay que especificar la ruta
    # Y el identificador que nos ha dado el listado de backups
    # Ya tendremos el directorio preparado para configurar el recovery.conf
    
---

# Copia continua (prueba) 

    !bash
    # Si intentamos levantar el cluster sin recovery.conf nos dará error
    # Necesitamos los archivos WAL
    postgres@apsl-edu:~/9.3/wale$ pg_ctlcluster 9.3 wale start
    The PostgreSQL server failed to start. Please check the log output:
    2014-10-28 10:50:21 CET LOG:  database system was interrupted; last known up at 2014-10-28 10:33:22 CET
    2014-10-28 10:50:21 CET LOG:  creating missing WAL directory "pg_xlog/archive_status"
    2014-10-28 10:50:21 CET LOG:  invalid checkpoint record
    2014-10-28 10:50:21 CET FATAL:  could not locate required checkpoint record
    2014-10-28 10:50:21 CET HINT:  If you are not restoring from a backup, try removing the file "/var/lib/postgresql/9.3/wale/backup_label".
    2014-10-28 10:50:21 CET LOG:  startup process (PID 21139) exited with exit code 1
    2014-10-28 10:50:21 CET LOG:  aborting startup due to startup process failure

    # Preparamos el recovery.conf para que sepa donde encontrar los WAL
    postgres@apsl-edu:~/9.3/wale$ cat recovery.conf
    restore_command = '/usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e wal-fetch "%f" "%p"'

    # Levantamos el cluster con el recovery.conf
    postgres@apsl-edu:/var/log/postgresql$ pg_ctlcluster 9.3 wale start
    WARNING: connection to the database failed, disabling startup checks:
    psql: FATAL:  the database system is starting up
    
    # El error FATAL es normal, pues la restauración requiere que se bajen los WALs
    # antes de tener operativo el server

---

# Copia continua (prueba) 

    !bash
    # Veremos en los logs como va bajando los archivos wal
    tail -n 100 /var/log/postgresql/postgresql-9.3-wale.log -f
    
    # Hasta que ya ha terminado de restaurar y podemos ver
    2014-10-28 10:58:02 CET LOG:  archive recovery complete
    2014-10-28 10:58:02 CET LOG:  autovacuum launcher started
    2014-10-28 10:58:02 CET LOG:  database system is ready to accept connections

    # Podemos comprobar la base de datos de elefants para ver los cambios    
    edu@apsl-edu:~/Proyectos/postgresql-checker$ cat config.yml
    ...
    port: 5432
    ...
    
    edu@apsl-edu:~/Proyectos/postgresql-checker$ python count.py
    Hay 1633 elefantes en la tela
    
    edu@apsl-edu:~/Proyectos/postgresql-checker$ cat config.yml
    ...
    port: 5433
    ...
    
    edu@apsl-edu:~/Proyectos/postgresql-checker$ python count.py
    Hay 1594 elefantes en la tela
* [Log típico de restauración con wal-e](https://gist.githubusercontent.com/eduherraiz/feee12bc2e76a2c0e095/raw/c0bd6a6e6dea2fc5498b1b0e72272c155b6842a9/log%20restore%20wal-e)

---

# Copia continua (conclusiones) 

* Tendremos un **tiempo de perdida de datos** cómo máximo de **archive_timeout**
    * Los archivos wal que no se hayan subido no se pueden restaurar
* Pero un **archive_timeout bajo** generará **más archivos wal**
    * Perjudicando al tiempo de restauración (tiene que bajar más archivos)