Maarten van Vliet Blocked Desbloquear Seguir Seguindo 4 de janeiro
No último AWS ReInvent, foi anunciado que o AWS Lambda suportaria o Ruby como uma linguagem de tempo de execução. Eu estava ansioso para testar isso, a poderosa sintaxe e os recursos do Ruby são uma alegria para trabalhar e acoplar isso com o AWS Lambda. Achei que poderia ser aproveitado para um redimensionamento fácil do Lambda.
Comecei com a estrutura sem servidor, pois é uma maneira fácil de provisionar o Lambda. O objetivo é que, quando uma imagem é carregada em um bucket do S3, um Lambda seja iniciado, ele redimensione a imagem e, em seguida, carregue-a em outro bucket.
O primeiro passo é instalar serverless
npm install -g serverless
serverless create -t aws-ruby -p image-resizer
Isso cria o clichê básico de uma função Lambda framework ruby sem servidor. Ele vai criar dois arquivos, um handler.rb
, o arquivo com a função real do Lambda vai chamar, e uma serverless.yml
um arquivo que o quadro sem servidor usa para configurar e provisionar o lambda e serviços afiliados, tais como AWS S3 e AWS API Gateway
Primeiro, alteraremos a configuração serverless.yml
. Adicionamos uma função handle_resize
que chama a função ImageHandler.process
em handler.rb
sempre que um objeto é criado no bucket S3 your-images
. Além disso, sob a chave do provedor, concedemos à função acesso ao S3 e usamos o tempo de execução do ruby2.5
.
Também adicionamos um recurso S3 resized-your-images
, esse será o repositório para o qual movemos as imagens redimensionadas.
service: image-resizer
provider:
name: aws
runtime: ruby2.5
iamRoleStatements:
- Effect: Allow Action:
- s3:* Resource: "*"
functions:
handle_resize:
handler: handler.ImageHandler.process
events:
- s3:
bucket: your-images
event: s3:ObjectCreated:*
resources:
Resources:
ResizedImages:
Type: AWS::S3::Bucket
Properties:
BucketName: resized-your-images
Agora podemos implementar a função ImageHandler.process
.
Escrevendo o lambda Ruby
Primeiro vamos adicionar um Gemfile por causa de duas dependências que precisamos usar. O AWS S3 SDK para recuperar e fazer upload de arquivos para o S3 e o mini_magick
gem, um wrapper para o imagemagick.
#Gemfile
source 'https://rubygems.org'
gem 'aws-sdk-s3', '~> 1.30.0'
gem "mini_magick", '~> 4.9.0'
Em seguida, atualizamos o arquivo handler.rb
.
#handler.rb
require 'uploaded_file'
class ImageHandler
def self.process(event:, context:)
event = event["Records"].first
bucket_name = event["s3"]["bucket"]["name"]
object_name = event["s3"]["object"]["key"]
file = UploadedFile.from_s3(bucket_name, object_name)
file.resize "100x100"
file.upload_file("resized-your-images", "resized_" + event["s3"]["object"]["key"] )
end
end
O ImageHandler.process
é a função chamada pelo lambda e possui dois argumentos, o event
, no nosso caso, um evento enviado pelo S3 sobre arquivos recém-criados, e o context
, basicamente metadados sobre a função e seu ambiente.
A função de process
recupera o bloco e a chave do objeto recém-criado do objeto de evento. Em seguida, ele chama a classe UploadedFile
ainda não implementada. Essa classe recupera o objeto s3, redimensiona e envia para outro bucket. Esta classe é independente do lambda e, em geral, toda a lógica de negócios não deve ser vinculada ao Lambda. Isso facilita o uso do código em outros contextos e também permite testes mais fáceis. Por exemplo, posso testar o UploadedFile.from_s3(bucket_name, object_name)
sem ter que simular todo o evento S3 Lambda.
Agora vamos implementar a classe UploadedFile.
# uploaded_file.rb
require "aws-sdk-s3"
require "mini_magick"
class UploadedFile
def self.from_s3(bucket_name, object_name)
s3 = Aws::S3::Resource.new()
object = s3.bucket(bucket_name).object(object_name)
tmp_file_name = "/tmp/#{object_name}"
object.get(response_target: tmp_file_name)
UploadedFile.new(tmp_file_name)
end
def initialize(tmp_file)
@tmp_file = tmp_file
end
def resize(params)
image = MiniMagick::Image.open(@tmp_file)
image.resize params
@resized_tmp_file = "/tmp/resized.jpg"
image.write @resized_tmp_file
end
def upload_file(target_bucket, target_object)
s3 = Aws::S3::Resource.new()
object = s3.bucket(target_bucket).object(target_object).upload_file(@resized_tmp_file)
end
end
O uploaded_file.rb
é um rubi típico. Ele recupera o arquivo S3, armazena-o em um arquivo tmp. Chamamos mini_magick
para redimensionar a imagem e, em seguida, fazer o upload do arquivo novamente.
Desdobramento, desenvolvimento
Ao implantar, precisamos ter certeza de que a função Lambda tenha acesso às dependências. A estrutura sem servidor criará um arquivo zip com o código e todas as dependências localmente e implantará isso na AWS. Portanto, é importante que executemos a mesma versão do ruby localmente no AWS, de modo que seria o ruby 2.5. Se você usar o rbenv
, você pode usar o rbenv install 2.5.0
e o rbenv local 2.5.0
para definir a versão do Ruby para este projeto.
Em seguida, precisamos vender as dependências para que elas sejam incluídas no pacote. Executar o bundle install --path vendor/bundle
, agora as dependências estão no diretório do vendor
.
Antes de podermos implantar, ainda precisamos definir as credenciais para a AWS. Veja a documentação do Serverless Framework para mais informações.
Agora que isso é feito, você pode chamar sls deploy
e a função lambda será implementada e os buckets S3 serão criados. Faça o upload de uma imagem para o intervalo " your-images
e, momentos depois, uma versão redimensionada aparecerá no intervalo " resized-your-images
. Se algo der errado, você pode seguir isso nos arquivos de log no AWS Cloudwatch. Haverá um fluxo de log para a função lambda. Você também pode chamar sls logs -f handle_resize
para completar os logs.
Encontre o código no github