Pipeline de processamento de imagens sem servidor com AWS ECS e Lambda – SitePoint


Bem -vindo desenvolvedores ao mundo do desenvolvimento e automação. Hoje, estamos mergulhando em um projeto emocionante, no qual criaremos um pipeline de processamento de imagens sem servidor com os serviços da AWS.

O projeto começa com a criação de baldes S3 para armazenar imagens carregadas e miniaturas processadas e, eventualmente, usar muitos serviços como Lambda, API Gateway (para acionar a função Lambda), DynamoDB (armazenando metadados da imagem) e, finalmente, executaremos este programa no ECS Cluster criando uma imagem do Docker do projeto.

Este projeto está repleto de serviços de tecnologia em nuvem e tecnologia de desenvolvimento como o Next.js, e praticar isso aumentará ainda mais sua compreensão dos serviços em nuvem e como eles interagem entre si. Então, com mais delongas, vamos começar!

NOTA: O código e as instruções deste post são apenas para uso de demonstração e aprendizado. Um ambiente de produção exigirá uma aderência mais apertada nas configurações e segurança.

Pré -requisitos

Antes de entrarmos no projeto, precisamos garantir que os seguintes requisitos atendidos em nosso sistema:

  • Uma conta da AWS: Como usamos os serviços da AWS para o projeto, precisamos de uma conta da AWS. Um usuário do IAM configurado com o acesso de serviços necessários seria apreciado.
  • Entendimento básico dos serviços da AWS: Como estamos lidando com muitos serviços da AWS, é melhor ter um entendimento decente deles, como o S3, que é usado para armazenamento, gateway da API para desencadear a função lambda e muito mais.
  • Nó instalado: Nosso front -end é construído com o Next.js, portanto, é necessário ter um nó no seu sistema.

Para referência de código, aqui está o Repo Github.

Configuração de serviços da AWS

Vamos começar o projeto por Configurando nossos serviços da AWS. Em primeiro lugar, criaremos 2 baldes S3, ou seja, sample-image-uploads-bucket e sample-thumbnails-bucket. O motivo desse nome longo é que o nome do balde deve ser único em todo o espaço de trabalho da AWS.

Portanto, para criar o balde, acesse o painel S3 e clique em ‘Criar balde’, selecionar ‘General Purpose’ e dar um nome (amostra-imagens-uploads-bucket) e deixe o restante da configuração como padrão.

Da mesma forma, crie o outro balde denominado amostra-túmulos-bucket, mas neste balde, verifique se você desmarque o bloqueio de acesso público Porque precisaremos disso para o nosso cluster ECS.

Precisamos garantir que o bucket de amostra de túmulos tenha acesso público a leitura, para que o front-end do ECS possa exibi-los. Para isso, anexaremos a seguinte política a esse balde:

{
  "Version": "2012-10-17",
  "Statement": (
    {
      "Sid": "PublicRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::sample-thumbnails-bucket/*"
    }
  )
}

Depois de criar baldes, vamos para o nosso banco de dados para armazenar metadados da imagem. Criaremos uma tabela de dynamoDB para isso. Vá para o seu console do DynamoDB, clique na tabela Create, dê um nome (image_metadata) e, na chave primária, selecione String, nomeie -a image_id.

Os serviços da AWS se comunicarão, para que eles precisem de uma função com as permissões adequadas. Para criar uma função, vá para o painel do IAM, selecione função e clique em Criar função. Sob Tipo de identidade de confiançaselecione Serviço da AWS, e abaixo Caso de usoescolha Lambda. Anexe as seguintes políticas:

  • Amazons3fullaccess
  • Amazondynamodbfullaccess
  • CloudWatchLogsfullAccess

Dê a essa função um nome (Lambda-Image-Processor-Role) e salve-o.

Criando função lambda

Temos nosso papel de lambda, baldes e tabela de dynamoDB prontos, então agora vamos Crie a função Lambda O que processará a imagem e fará a miniatura dela, pois estamos usando a biblioteca de travesseiros para processar as imagens, o Lambda por padrão não fornece isso. Para corrigir isso, adicionaremos uma camada na função Lambda. Para fazer isso, siga as seguintes etapas:

Agora vá para o seu Lambda Painel, clique em Criar uma função. Selecione Autor do zero e escolha Python 3.9 Como linguagem de tempo de execução, dê um nome: processador de imagem e no Código guia, você tem o Upload de Opção, selecione isso, escolha Zip Arquive e envie seu arquivo zip do iMage-Processor.

Vá para Configuraçãoe sob o Permissões coluna, edite a configuração por Mudando o papel existente para o papel que criamos o Lambda-Image-Processor-Role.

Agora vá para o seu balde S3 (Bucket) e vá para o seu Propriedades seção e role para baixo para Notificação de eventon, aqui clique em Crie notificação de eventosDê um nome (processador de gatilho-imagem) e, no tipo de evento, selecione Put e selecione a função Lambda que criamos (Processador Image).

Agora, como o travesseiro não vem embutido com a biblioteca Lambda, faremos as seguintes etapas para corrigir isso:

  1. Vá para sua função lambda (euMage-Processor) e role para baixo até o Camada Seção, clique aqui Adicione a camada.
  1. No Adicione a camada Seção, selecione Especifique um ARN e forneça este ARN arn:aws:lambda:us-east-1:770693421928:layer:Klayers-p39-pillow:1 . Mudar a região de acordo; Estou usando os EUA-East-1. Adicione a camada.

Agora, na guia Código da sua função lambda, você estaria tendo um lambda-function.py, coloque o seguinte conteúdo dentro do lambda_function.py:

import boto3
import uuid
import os
from PIL import Image
from io import BytesIO
import datetime

s3 = boto3.client('s3')
dynamodb = boto3.client('dynamodb')

UPLOAD_BUCKET = ''  
THUMBNAIL_BUCKET = '' 
DDB_TABLE = 'image_metadata'

def lambda_handler(event, context):
    
    record = event('Records')(0)
    bucket = record('s3')('bucket')('name')
    key = record('s3')('object')('key')

    
    response = s3.get_object(Bucket=bucket, Key=key)
    image = Image.open(BytesIO(response('Body').read()))

    
    image.thumbnail((200, 200))

    
    thumbnail_buffer = BytesIO()
    image.save(thumbnail_buffer, 'JPEG')
    thumbnail_buffer.seek(0)

    
    thumbnail_key = f"thumb_{key}"
    s3.put_object(
        Bucket=THUMBNAIL_BUCKET,
        Key=thumbnail_key,
        Body=thumbnail_buffer,
        ContentType='image/jpeg'
    )

    
    image_id = str(uuid.uuid4())
    original_url = f"https://{UPLOAD_BUCKET}.s3.amazonaws.com/{key}"
    thumbnail_url = f"https://{THUMBNAIL_BUCKET}.s3.amazonaws.com/{thumbnail_key}"
    uploaded_at = datetime.datetime.now().isoformat()

    dynamodb.put_item(
        TableName=DDB_TABLE,
        Item={
            'image_id': {'S': image_id},
            'original_url': {'S': original_url},
            'thumbnail_url': {'S': thumbnail_url},
            'uploaded_at': {'S': uploaded_at}
        }
    )

    return {
        'statusCode': 200,
        'body': f"Thumbnail created: {thumbnail_url}"
    }

Agora, precisaremos de outra função Lambda para o API Gateway, pois isso atuará como o ponto de entrada para o nosso aplicativo ECS front -end buscar dados de imagem do DynamoDB.

Para criar a função Lambda, vá para o seu Painel Lambdaclique em criar funçãoselecione Autor do zero e Python 3.9 Como tempo de execução, dê um nome, get-image-metadata e, na configuração, selecione a mesma função que atribuímos a outras funções Lambda (Lambda-Image-Processor-Role)

Agora, na seção de código da função, coloque o seguinte conteúdo:

import boto3
import json

dynamodb = boto3.client('dynamodb')
TABLE_NAME = 'image_metadata'

def lambda_handler(event, context):
    try:
        
        response = dynamodb.scan(TableName=TABLE_NAME)

        
        images = ()
        for item in response('Items'):
            images.append({
                'image_id': item('image_id')('S'),
                'original_url': item('original_url')('S'),
                'thumbnail_url': item('thumbnail_url')('S'),
                'uploaded_at': item('uploaded_at')('S')
            })

        return {
            'statusCode': 200,
            'headers': {
                "Content-Type": "application/json"
            },
            'body': json.dumps(images)
        }

    except Exception as e:
        return {
            'statusCode': 500,
            'body': f"Error: {str(e)}"
        }

Criando o gateway da API

O gateway da API atuará como o ponto de entrada do seu aplicativo de front -end do ECS para buscar dados de imagem do DynamoDB. Ele se conectará à função Lambda que consulta o DynamoDB e retorna os metadados da imagem. O URL do gateway é usado em nosso aplicativo de front -end para exibir imagens. Para criar o gateway da API, faça as seguintes etapas:

  • Vá para o Console de gerenciamento da AWS → Pesquise API Gateway → Clique Crie API.
  • Selecione API HTTP.
  • Clique em Construir.
  • Nome da API: Image-Gallery-api
  • Adicione integrações: Selecione Lambda e selecione a função get_image_metadata
  • Selecione Método: Obtenha e Caminho: /imagens
  • Tipo de endpoint: Regional
  • Clique em Próximo e crie o URL da API Gateway.

Antes de criar o front -end, vamos testar o aplicativo manualmente. Primeiro vá para o seu balde de upload S3 (Bucket) e carregar um JPG/JPEG imagem; Outra imagem não funcionará, pois sua função apenas processa esses dois tipos:


Na foto acima, enviei uma imagem intitulada “ghibil-art.jpg”O arquivo, e uma vez enviado, ele desencadeará a função Lambda, que criará a miniatura a partir dele denominada“Miniatura-Ghibil-Art.jpg”E guarde -o em Bucket de amostra-túmulos e as informações sobre a imagem serão armazenadas em Image-metadata Tabela no DynamoDB.

Na imagem acima, você pode ver o item dentro da seção Explore Item da nossa tabela DynamoDB “Image-metadata.”Para testar a Api-Gateway, verificaremos o URL de invocar nosso API da Gallery-Gallery seguido de /imagens. Ele mostrará a seguinte saída, com o comando CURL:

Agora, nosso aplicativo está funcionando bem, podemos implantar um front -end para visualizar o projeto.

Criando o aplicativo de front -end

Por uma questão de simplicidade, criaremos um front -end mínimo e simples da galeria usando o Next.js, Dockerize -o e implantá -lo no ECS. Para criar o aplicativo, faça as seguintes etapas:

Inicialização

npx create-next-app@latest image-gallery
cd image-gallery
npm install
npm install axios

Crie um novo arquivo componentes/galeria.js:

'use client';

import { useState, useEffect } from 'react';
import axios from 'axios';
import styles from './Gallery.module.css';

const Gallery = () => {
  const (images, setImages) = useState(());
  const (loading, setLoading) = useState(true);

  useEffect(() => {
    const fetchImages = async () => {
      try {
        const response = await axios.get('https:///images');
        setImages(response.data);
        setLoading(false);
      } catch (error) {
        console.error('Error fetching images:', error);
        setLoading(false);
      }
    };

    fetchImages();
  }, ());

  if (loading) {
    return <div className={styles.loading}>Loading...</div>;
  }

  return (
    <div className={styles.gallery}>
      {images.map((image) => (
        <div key={image.image_id} className={styles.imageCard}>
          <img
            src={image.thumbnail_url}
            alt="Gallery thumbnail"
            width={200}
            height={150}
            className={styles.thumbnail}
          />
          <p className={styles.date}>
            {new Date(image.uploaded_at).toLocaleDateString()}
          </p>
        </div>
      ))}
    </div>
  );
};

export default Gallery;

Certifique-se de alterar o gateway-url para o seu api_gateway_url

Adicione o módulo CSS

Crie componentes/Gallery.module.css:

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 20px;
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}
.imageCard {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
  overflow: hidden;
  transition: transform 0.2s;
}
.imageCard:hover {
  transform: scale(1.05);
}
.thumbnail {
  width: 100%;
  height: 150px;
  object-fit: cover;
}
.date {
  text-align: center;
  padding: 10px;
  margin: 0;
  font-size: 0.9em;
  color: #666;
}
.loading {
  text-align: center;
  padding: 50px;
  font-size: 1.2em;
}

Atualize a página inicial

Modificar app/page.js:

import Gallery from '../components/Gallery';

export default function Home() {
  return (
    <main>
      <h1 style={{ textAlign: 'center', padding: '20px' }}>Image Gallery</h1>
      <Gallery />
    </main>
  );
}

Componente de imagem interno do próximo.JS

Para usar o componente de imagem interno do Next.JS para melhor otimização, atualização Next.config.mjs:


const nextConfig = {
  images: {
    domains: ('sample-thumbnails-bucket.s3.amazonaws.com'),
  },
};

export default nextConfig;

Execute o aplicativo

Visite http: // localhost: 3000 no seu navegador e você verá o aplicativo em execução com todas as miniaturas enviadas.

Para fins de demonstração, coloquei quatro imagens (JPEG/JPG) no meu amostra-imagens-upload-bucket. Através da função, eles são transformados em miniaturas e armazenados no bucket de amostra-túmulos.

O aplicativo se parece com o seguinte:

Contaiuntar e criar o cluster ECS

Agora, quase terminamos o projeto, por isso continuaremos criando um DockerFile do projeto da seguinte maneira:

# Use the official Node.js image as a base
FROM node:18-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files and install dependencies
COPY package.json package-lock.json ./
RUN npm install

# Copy the rest of the application code
COPY . .

# Build the Next.js app
RUN npm run build

# Use a lightweight Node.js image for production
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy built files from the builder stage
COPY --from=builder /app ./

# Expose port
EXPOSE 3000

# Run the application
CMD ("npm", "start")

Agora vamos construir a imagem do Docker usando:

docker build -t sample-nextjs-app .

Agora que temos nossa imagem do Docker, iremos para o AWS ECR Repo, para isso, faça as seguintes etapas:

Etapa 1: empurre a imagem do Docker para a Amazon ECR

  1. Vá para o console de gerenciamento da AWS → Pesquise ECR (Registro de contêineres elásticos) → Abrir ECR.
  2. Crie um novo repositório:
    • Clique Criar repositório.
    • Definir Nome do repositório (por exemplo, amostra-nextjs-app).
    • Escolher Privado (ou Público se necessário).
    • Clique Criar repositório.
  3. Empurre sua imagem do Docker para o ECR:
    • No repositório recém -criado, clique Ver comandos push.
    • Siga os comandos para:
      • Autentique o docker com ECR.
      • Construa, marque e empurre sua imagem.
      • Você precisa ter a AWS CLI configurada para esta etapa.

Etapa 2: Crie um cluster ECS

aws ecs create-cluster --cluster-name sample-ecs-cluster

Etapa 3: Crie uma definição de tarefa

  1. No console do ECSVá para Definições de tarefas.
  2. Clique Crie nova definição de tarefa.
  3. Escolher Fargate → Clique Próximo passo.
  4. Definir detalhes da definição de tarefa:
    • Nome: amostra-nextJs-Task
    • Função de tarefas: EcstaskexecutionRole (Crie um se estiver ausente).
{
  "Version": "2012-10-17",
  "Statement": (
    {
      "Sid": "Statement1",
      "Effect": "Allow",
      "Action": (
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability"
      ),
      "Resource": "arn:aws:ecr:us-east-1:624448302051:repository/sample-nextjs-app"
    }
  )
}
  • Memória de tarefas e CPU: Escolha valores apropriados (por exemplo, 512MB e 256 CPU).
  1. Defina o contêiner:
    • Clique Adicione o contêiner.
    • Nome do contêiner: amostra-nextJs-container.
    • Imagem URL: Cole o ECR Image URI da etapa 1.
    • Mapeamentos de porta: Definir 3000 para portas de contêiner e host.
    • Clique Adicionar.
  2. Clique Criar.

Etapa 4: Crie um serviço ECS

  1. Vá para “ECS” → Clique Clusters → Selecione seu cluster (sample-ecs-cluster).
  2. Clique Criar serviço.
  3. Escolha Fargate → Clique Próximo passo.
  4. Configure o serviço:
    • Definição da tarefa: selecione Sample-nextJs-Task.
    • Conjunto: Amostra-ECS-cluster.
    • Nome do serviço: Sample-NextJs-Service.
    • Número de tarefas: 1 (Pode escalar mais tarde).
  5. Configurações de rede:
    • Selecione um VPC existente.
    • Escolher Sub -redes públicas.
    • Habilitar IP público-atribuído automático.
  6. Clique Próximo passoCriar serviço.

Etapa 5: acesse o aplicativo

  1. Vá para ECS> Clusters> Amostra-ECS-Cluster.
  2. Clique no Guia Tarefas.
  3. Clique na tarefa em execução.
  4. Encontre o IP público sob Rede.

Abra um navegador e vá para:

http: //: 3000

Seu Next.js O aplicativo deve estar ao vivo! 🚀

Conclusão

Isso marca o fim do blog. Hoje, dividimos em muitos serviços da AWS: S3, IAM, ECR, função lambda, ECS, Fargate e API Gateway. Iniciamos o projeto criando baldes S3 e eventualmente implantamos nosso aplicativo em um cluster ECS.

Ao longo deste guia, cobrimos Containdo o aplicativo Next.js, empurrando -o para o ECR, configurando as definições de tarefas do ECS e implantando através do console da AWS. Essa configuração permite escala automatizada, atualizações fáceis e acesso seguro da API-todos os principais benefícios de uma implantação nativa em nuvem.

As configurações de produção em potencial podem incluir alterações como abaixo:

  • Implementando permissões mais restritivas do IAM, melhorando o controle sobre o acesso do público a baldes S3 (usando o CloudFront, URLs pré-assinados ou um proxy de back-end em vez de fazer o público-samas-túmulos-bucket)
  • Adicionando tratamento de erros e paginação (especialmente para consultas de dynamoDB)
  • Utilizando configurações seguras de VPC/rede para CEs (como usar um balanceador de carga de aplicativos e sub -redes privadas em vez de IPs públicos diretos)
  • Abordando preocupações de escala substituindo a operação DynamoDB.Scan dentro do Lambda, que recebe os metadados pelo DynamoDB.Query
  • Usando variáveis ​​de ambiente em vez de um URL de gateway de API codificado no Código Next.js



Source link