twitter: @monobotblog
email: monobot.soft@gmail.com Backend developer en cabaana
Es el mayor proveedor de servicios cloud actualmente
Dispone de un enorme abanico de servicios, y cada año lanza varios.
Facturó alrededor de 6.300 millones de euros en 2015
Alta en aws
Hay que poner la tarjeta de credito, aunque para las primeras pruebas y durante un año hay muchos servicios que son gratis, cuando los servicios no sean gratis normalmente avisan
Tienen herramientas para consultar los costes previstos
Durante el proceso de alta descargar un .pem que deberás conservar, si lo pierdes no se puede recuperar hay que generar otro
Lo intentaremos debatir entre todos al final
AWS dispone de una herramienta para la línea de comandos que admite (CASI) TODA la funcionalidad disponible en la "consola web"
Así que en breve puedes hacer lo que quieras desde la línea de comando (si sabes como hacerlo)
aws-cli está escrito en python (obviamente)
obtener ficheros (recursivamente) de un bucket determinado
aws s3 ls s3://mybucketname --recursive
obtener el listado de servidores
aws ec2 describe-instances
Es una de las herramientas principales de aws, es donde se establecen los permisos a los roles, y éstos (los roles) se pueden aplicar a usuarios o a servidores.
S3 es el webservice para almacenaje y entrega de ficheros estáticos.
Aseguran disponibilidaddel 99,99%
Se almacenan en buckets (baldes) que están disponibles en una localización específica, no es lo mismo tener los ficheros en Irlanda que en Hong Kong
Para el caso de disponibilidad mundial también tienen un CDN que ellos llaman cloudfront
También tienen un servicio para ficheros de lento (muy muy lento) acceso glacier
Como es lógico es posible usar S3 desde django, puedes obviamente hacerlo tu a mano, con cada subida que haya una señal que lo haga por ti
Pero lo lógico es que cada vez que un usuario o servicio suba un fichero no lo haga al servidor (piensa en el caso de múltiples servidores) sino que lo haga directamente al bucket
Configurar como se suben lo ficheros media
class MediaStorage(S3BotoStorage):
location = settings.MEDIAFILES_LOCATION
Configurar el settings
AWS_STORAGE_BUCKET_NAME = 'BUCKET_NAME'
AWS_ACCESS_KEY_ID = 'xxxxxxxxxxxxxxxxxxxx'
AWS_SECRET_ACCESS_KEY = 'yyyyyyyyyyyyyyyyyyyyyyy'
MEDIAFILES_LOCATION = 'media'
MEDIA_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, MEDIAFILES_LOCATION)
DEFAULT_FILE_STORAGE = 'custom_storages.MediaStorage'
Lo mismo se puede (y debe) hacer con los estáticos ¡para que tu servidor no sirva ningún fichero!
Para una descripción paso a paso y completa ver el tutorial
Es el servicio de colas, asegura solo y al menos una entrega de la tarea, si devuelve error se volverá a enviar
Para sqs amazon puedes usar el boto de amazon, pero tendrías que hacertelo tu a mano, yo recomiendo usar sqjobs
No hay tiempo de explicarlo entero, básicamente configuras una serie de tareas, y tienes un broker que se encarga de encolar las tareas y worker/s que se encarga/n de ejecutarlas a medida que llegan.
definir el broker
def sqs_broker():
return create_sqs_broker(
access_key=settings.SQJOBS_SQS_ACCESS_KEY,
secret_key=settings.SQJOBS_SQS_SECRET_KEY,
region_name='eu-west-1'
)
el mixin de las tareas
class LogJobMixin(Job):
default_queue_name = settings.SQS_QUEUE
def pre_run(self, *args, **kwargs):
# que hacer antes de ejecutarse
def post_run(self, *args, **kwargs):
# que hacer después de ejecutarse
def on_failure(self):
# que hacer si falla
def on_retry(self):
# que hacer al reintentar
definir una tarea:
class ChangePasswordEmail(LogJobMixin):
name = 'ChangePasswordEmail'
def run(self, *args, **kwargs):
changePasswordEmail(*args, **kwargs)
encolar una tarea:
from mysqs.brokers import sqs_broker
from mysqs import ChangePasswordEmail
broker = sqs_broker()
broker.add_job(
ChangePasswordEmail,
emp.user.id,
schema_name=schema_name,
)
Los argumentos que se envían a sqs deben ser serializables directamente, no puedes por tanto enviar modelos de django
Ya solo nos queda ver como corren los workers
convenientemente tenemos una tarea de django ya configurada
manage.py sqjobs sqs worker QUEUE_NAME
Son diferentes servicios pero están estrechamente relacionados
Los los servidores a los cuales puedes acceder mediante ssh
Existe un abanico muy amplio de servidores, generalistas, cpu, memoria, ancho de banda, etc...
Los servidores deben tener asociado al menos una seccion de disco virtual (normalmente ssd)
La nube de amazon se divide en regiones (uswest, useast, euwest, eueast, etc...) y cada region se divide al menos en 3 zonas de disponibilidad (availability zones)
debes tener claro donde localizas el servidor para que esté lo mas cerca posible de tu clientes
salvo contadas excepciones los servicios de amazon están localizados
Necesita gateway (un servidor ec2 dedicado al enrutado), la red virtual da un nivel de aislamiento necesario cuando tratas mas de una máquina para que el acceso interno sea fácil y posible
Es importante hablar ahora de IAM, es muy importante asignar a los servidores los roles que les permitan acceder al resto de servicios.
Se encarga de balancear la carga entre los diferentes servidores, de tal forma que éstos estén mas o menos al mismo nivel de trabajo.
El ELB se puede asignar a un grow group, es decir una configuración de EC2 que dependiendo de una serie de requisitos establecidos por nosotros aumenta automáticamente el número de servidores.
El servicio de enrutado de amazon
Lo fantástico que tiene es que permite enrutar a una máquina específica o a un ELB, podemos hacer cosas como el 50% de la carga la pones aquí y el resto allá, o que la carga la reparta al servidor mas cercano, o por paises, etc...
Como pueden ver es un lio poder interactuar con los diferentes servidores y además a priori no sabemos las ips o cuantos servidores hay en un grow group por ejemplo, por ello nos tenemos que valer algun truco
def describe_instances(stage):
aws_instances = local('aws ec2 describe-instances', capture=True)
aws_dict = json.loads(aws_instances.stdout)
# print aws_dict
webserver_stage = {'hosts': [], 'user': ''}
websocket_stage = {'hosts': [], 'user': ''}
for res in aws_dict['Reservations']:
for inst in res['Instances']:
same_stage = False
is_webserver = False
is_websocket = False
inst_ip = inst['PublicIpAddress']
for tag in inst['Tags']:
if tag['Key'] == 'Stage':
same_stage = (
tag['Key'] == 'Stage' and tag['Value'] == stage
)
elif tag['Key'] == 'Use':
is_webserver = (
tag['Key'] == 'Use' and tag['Value'] == 'Webserver'
)
is_websocket = (
tag['Key'] == 'Use' and tag['Value'] == 'Websockets'
)
elif tag['Key'] == 'username':
username = tag['Value']
if same_stage:
if is_webserver:
webserver_stage['hosts'].append(str(inst_ip))
webserver_stage['user'] = username
elif is_websocket:
websocket_stage['hosts'].append(str(inst_ip))
websocket_stage['user'] = username
return webserver_stage, websocket_stage
def dev():
env.host_type = 'development'
env.webserver_hosts, env.websocket_host = describe_instances(env.host_type)
env.key_filename = get_pempath('mykey.pem')
env.DB_ENDPOINT = 'yourendpoint.rds.amazonaws.com'
env.DB_NAME = 'dev'
def deploy():
print(('this command will pull {red}{servername}\'s{black} '
'current branch to the latest version and restart the services ...'
).format(
red=ShellFormat.WARNING,
servername=env.host_type.upper(),
black=ShellFormat.ENDC,
)
)
resp = raw_input('Type the server\'s name to confirm: ')
if resp == env.host_type:
env.migrate = True
websocket_host = env.websocket_host != {'hosts': [], 'user': ''}
if websocket_host:
print 'WEBSOCKET SERVER: migrate the database and deploy'
execute(real_deploy,
env.websocket_host['user'],
hosts=env.websocket_host['hosts'],
)
print 'WEBSERVERS: just deploy'
env.migrate = False
execute(real_deploy,
env.webserver_hosts['user'],
hosts=env.webserver_hosts['hosts'],
)
else:
print 'WEBSERVERS: migrate the database and deploy'
execute(real_deploy,
env.webserver_hosts['user'],
hosts=env.webserver_hosts['hosts'],
)
else:
print('... exiting')
def real_deploy(user):
env.user = user
with cd(env.mydir):
run('git stash')
run('git pull')
if env.migrate:
run(
('{python} manage.py migrate_schemas '
'--settings=special.settings.{host_type}'
).format(
python=env.python_int,
host_type=env.host_type
)
)
control_service = 'supervisorctl'
if env.host_type in ['development', 'gold']:
control_service = 'circusctl'
run('sudo {} reload'.format(control_service))
sleep(3)
run('sudo {} status'.format(control_service))