Este artículo presenta un script Python para subir archivos a la nube de Google de manera automática, recurriendo a la API XML de Google Cloud Storage.

Anteriormente expliqué cómo subir archivos a Google Drive utilizando el línea de comandos drive. Sin embargo, para el caso de Google Cloud Storage (solución de almacenamiento en la nube de Google Cloud) no existe una aplicación o cliente en sí, sino que se dispone de una API. Esto significa que, a fin de poder subir archivos a Google Cloud Storage desde línea de comandos, es necesario desarrollar nuestra propia aplicación o script que haga uso de dicha API. En este ejemplo, recurriendo al lenguaje Python.

Esta API utiliza un mecanismo de autenticación basado en claves públicas, el cual posibilita la interacción con GCS sin necesidad de ingresar credenciales ni autenticar de manera interactiva, y así subir archivos a la nube de manera programática.

Además de compartir el script Python para subir archivos a Google Cloud Storage utilizando la API XML, este artículo muestra paso a paso cómo crear un bucket en Google Cloud Storage, y cómo crear una cuenta de servicio y configurar las claves de autenticación.



Google Cloud Storage

Primero veamos los pasos básicos de creación de un bucket en GCS. Quienes posean una cuenta en Google Cloud y tengan un bucket ya configurado, pueden pasar a la siguiente sección.

Para comenzar es necesario crear una cuenta en Google Cloud:

cloud.google.com

Luego crear un proyecto (o utilizar uno existente) y dentro del mismo crear un nuevo bucket:

Los buckets de tipo "Nearline" y "Coldline" son de ultra bajo costo, alta disponibilidad y alta durabilidad, y están pensados para almacenar datos de manera persistente y como archivo. Coldline es ideal para almacenar datos que se espera acceder (leer, recuperar) menos de una vez al año. Nearline, en cambio, apunta a almacenar datos que se espera acceder menos de una vez al mes, pero posiblemente varias veces a lo largo de un año. Para el caso de backups (copias de seguridad) es aconsejable utilizar un bucket de tipo Nearline.

Ya tenemos nuestro nuevo bucket llamado "backup" vacío y listo para ser utilizado.

API XML

Para interactuar con un bucket de manera programática, es posible utilizar la API XML de Google Cloud Storage. Se trata de una API REST que permite gestionar datos almacenados en Google Cloud Storage de manera programática.

El acceso a esta API se autoriza a través del estándar OAuth. A tal fin es necesario crear una cuenta de servicio desde API MAnager > Credentials.

Indicar un nombre para la cuenta de servicio y otorgar el rol "Storage Admin", a fin de poder crear y eliminar archivos en el bucket.

De todos modos, desde el menú IAM & Admin es posible luego modificar el rol de la cuenta de servicio.

Es importante utilizar el formato P12 para que funcione correctamente desde Python.

Crear y guardar la clave privada. No se debe perder porque es la única copia, y es necesario protegerla adecuadamente ya que es información sensible (dicha clave autentica y da acceso a la cuenta de servicio).

La passphrase para la clave privada es siempre "notasecret".

Subir la clave al servidor mediante SCP o SFTP.

Configuración de boto

boto es una librería de Python open source utilizada como interfaz a Cloud Storage. El plugin gcs-oauth2-boto-plugin es un plugin de autenticación para el framework boto. Este provee autenticación mediante credenciales OAuth 2.0.

A fin de instalar boto es necesario contar con el gestor de paquetes de Python pip. Por ende, primero instalar el gestor de paquetes Python pip:

$ su -
# wget https://bootstrap.pypa.io/get-pip.py
# python get-pip.py
# apt-get update
# apt-get upgrade
# apt-get install gcc python-dev python-setuptools libffi-dev libssl-dev

Además, instalar virtualenv, necesario para crear un entorno aislado virtual de Python:

# sudo pip install virtualenv

Crear un directorio donde almacenar la aplicación Python y el entorno virtual:

# mkdir -p ~/scripts/backup-gcs
# cd ~/scripts/backup-gcs

Crear y activar el entorno virtual de Python:

# virtualenv venv
# source ./venv/bin/activate

Instalar las herramientas necesarias para utilizar la API XML de GCS dentro del entorno virtual:

(venv) pip install gcs-oauth2-boto-plugin

Verificar la instalación importando los módulos:

(venv)root@linuxito:~/scripts/backup-gcs# python
Python 2.7.9 (default, Jun 29 2016, 13:08:31) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import boto
>>> import gcs_oauth2_boto_plugin
>>> quit()
(venv)root@linuxito:~/scripts/backup-gcs#

Si no arroja errores, significa que la instalación de boto y gcs_oauth2_boto_plugin ha sido exitosa.

El plugin gcs_oauth2_boto_plugin utiliza la clave P12 que hemos generado previamente para autenticar contra Google Cloud. Para poder utilizar dicha clave (la cual hemos subido al servidor en pasos anteriores) es necesario crear el archivo de configuración .boto en el directorio $HOME del usuario con el que se correrá el script y activar la clave.

Para crear el archivo de configuración ~/.boto es posible recurrir a la herramienta gsutil. Esta herramienta es provista por el SDK de Google Cloud.

Instalar el SDK ejecutando los siguientes comandos:

# curl https://sdk.cloud.google.com > google_cloud_sdk.bash
# chmod +x google_cloud_sdk.bash 
# ./google_cloud_sdk.bash

El script de instalación de gsutil solicita indicar el directorio de instalación y ruta al archivo de inicialización del perfil de usuarios del sistema de manera interactiva. Utilizar /usr/local/ como directorio de instalación, e indicar que actualice el archivo /etc/profile si se desea que gsutil esté disponible para todos los usuarios del sistema (de lo contrario dejar .bashrc):

Installation directory (this will create a google-cloud-sdk subdirectory) (/root): /usr/local/
Enter a path to an rc file to update, or leave blank to use 
[/root/.bashrc]:  /etc/profile

Recargar el perfil para verificar el acceso al ejecutable gsutil:

(venv)root@linuxito:~/scripts/backup-gcs# . /etc/profile
root@linuxito:~/scripts/backup-gcs# which gsutil 
/usr/local/google-cloud-sdk/bin/gsutil

El tutorial oficial en la documentación de Google no aclara correctamente los pasos necesarios para crear el archivo de configuración de credenciales de autenticación (.boto). El comando gsutil config -e no funciona cuando se trata de crear el archivo de configuración.

Luego de un par de días peleando con GCS descubrí que es posible generar el archivo .boto utilizando la herramienta gcloud, también incluida en el SDK.

Para activar una clave P12, utilizar el siguiente comando:

# gcloud auth activate-service-account --key-file=CLAVE.p12 ID_CUENTA_SERVICIO

Indicar correctamente la ruta a la clave privada a través del parámetro --key-file. Es posible obtener el ID de la cuenta de servicio desde la configuración de la misma (console.cloud.google.com/iam-admin/serviceaccounts/).

Por ejemplo:

root@linuxito:~/scripts/backup-gcs# gcloud auth activate-service-account --key-file=~/scripts/backup-gcs/linuxito-1234567890.p12 backup@linuxito.iam.gserviceaccount.com
WARNING: .p12 service account keys are not recomended unless it is necessary for backwards compatability. Please switch to a newer .json service account key for this account.
Activated service account credentials for: [backup@linuxito.iam.gserviceaccount.com]

Ignorar el warning ya que el tutorial oficial indica específicamente que se debe utilizar una clave P12.

Por último, copiar el archivo .boto desde el subdirectorio legacy_credentials en la configuración de gcloud al directorio $HOME:

# cp ~/.config/gcloud/legacy_credentials/backup@linuxito.iam.gserviceaccount.com/.boto ~/.boto

upload2gcs.py

Finalmente, tenemos todo preparado para autenticar contra GCS y subir archivos al bucket "backup" desde nuestro script Python.

El script, llamado upload2gcs.py toma un nombre de archivo como parámetro y lo sube al bucket indicado en la variable BUCKET, perteneciente al proyecto indicado en la variable PROJECT_ID. VENV indica la ruta al entorno virtual.

PROJECT_ID, BUCKET y VENV son las únicas variables que se necesitan modificar antes de correr el script.

Pegar el siguiente contenido dentro del archivo ~/scripts/backup-gcs/upload2gcs.py:

#!/usr/bin/python

# Your project ID can be found at https://console.cloud.google.com/
# If there is no domain for your project, then project_id = 'YOUR_PROJECT'
PROJECT_ID = 'linuxito'
BUCKET = 'backup'
VENV = '/root/scripts/backup-gcs/venv/'

# Activate virtual environment
import os
activate_this = os.path.join(VENV + 'bin/activate_this.py')
execfile(activate_this, dict(__file__=activate_this))

import boto
import gcs_oauth2_boto_plugin
import shutil
import StringIO
import tempfile
import time
import sys

# Check arguments
if len(sys.argv) < 2:
  print 'Usage: ' + sys.argv[0] + ' FILENAME'
  quit()

filename = sys.argv[1]

# URI scheme for Cloud Storage.
GOOGLE_STORAGE = 'gs'
# URI scheme for accessing local files.
LOCAL_FILE = 'file'

header_values = {"x-goog-project-id": PROJECT_ID}

# Open local file
with open(filename, 'r')  as localfile:

  dst_uri = boto.storage_uri(BUCKET + '/' + filename, GOOGLE_STORAGE)
  # The key-related functions are a consequence of boto's
  # interoperability with Amazon S3 (which employs the
  # concept of a key mapping to localfile).
  dst_uri.new_key().set_contents_from_file(localfile)

print 'Successfully created "%s/%s"' % (dst_uri.bucket_name, dst_uri.object_name)

Otorgar permisos de ejecución y subir un par de archivos de prueba:

root@linuxito:~/scripts/backup-gcs# chmod +x upload2gcs.py
root@linuxito:~/scripts/backup-gcs# ./upload2gcs.py prueba1.txt 
Successfully created "backup/prueba1.txt"
root@linuxito:~/scripts/backup-gcs# ./upload2gcs.py prueba2.tar.gz
Successfully created "backup/prueba2.tar.gz"

Luego, desde el Browser del bucket "backup", es posible comprobar que los archivos se subieron con éxito:

Recordar que la descarga de los archivos tiene un costo. Verificar el billing y los términos de uso de cada tipo de bucket cuidadosamente.

En caso de errores 403, verificar que el rol para la cuenta de servicio esté configurado adecuadamente. Ante otro tipo de errores, verificar que el acceso al archivo de configuración de boto y la clave privada, desde el script, sean correctos.

Referencias


Tal vez pueda interesarte


Compartí este artículo