Capítulo 8: CaaS e FaaS
8.4 Functions
Oracle Functions, ou simplesmente Functions, é uma plataforma serverless que possibilita a criação e execução de códigos na infraestrutura do OCI, sem a necessidade de provisionar, configurar ou gerenciar servidores.
Serverless é um termo que se refere a um modelo de computação em nuvem onde o gerenciamento da infraestrutura do servidor é abstraído do desenvolvedor. Embora o nome possa sugerir a ausência de servidores, na verdade, eles estão presentes, mas a sua administração é completamente gerenciada pelo provedor de nuvem. Isso permite que os desenvolvedores se concentrem na lógica de suas aplicações, sem se preocupar com a infraestrutura subjacente.
Em vez de precisar criar e configurar servidores, você simplesmente faz o deployment do seu código e o OCI se encarrega de provisionar a infraestrutura necessária para executar a sua aplicação. Isso inclui também a escalabilidade automática dos recursos utilizados, garantindo que sua aplicação possa se adaptar às variações de demanda sem a necessidade de intervenção manual.
O serviço OCI Functions é baseado no projeto de código aberto Fn Project, que oferece uma plataforma de Função como Serviço (FaaS - Function as a Service) para o desenvolvimento e execução de aplicações serverless. A escolha em usar um projeto de código aberto é evitar o vendor lock-in, comum em outras plataformas serverless. Aplicações desenvolvidas usando o Fn Project não estão sujeitas a esse lock-in, permitindo que sejam executadas localmente ou em qualquer outro provedor de nuvem.

NOTA
Os termos BaaS (Backend as a Service) ou FaaS (Function as a Service), também são usados para descrever este modelo computacional.
Embora o conceito de serverless possa parecer a solução mágica e definitiva para a construção de aplicações, ele não é uma "bala de prata" que se aplica a todos os casos. Veremos que existem algumas limitações e seu uso exige uma nova abordagem na forma como as aplicações são desenvolvidas.
8.4.1 Funções em Contêineres
Uma função é, essencialmente, um contêiner que é executado na infraestrutura do OCI, seja por meio de uma chamada direta ou em resposta a um evento específico. O processo de construção da imagem do contêiner e seu envio para o OCIR permanece o mesmo; no entanto, é realizado de maneira diferente, utilizando a ferramenta de linha de comando fornecida pelo Fn Project.
NOTA
Por ser um contêiner, é possível criar seu próprio Dockerfile do zero em um processo conhecido como Bring-Your-Own-Dockerfile (BYOD). Para mais informações, consulte "Using Custom Dockerfiles".
As funções que você cria devem ser projetadas para executar uma única tarefa de forma simples e eficiente. O serviço não é destinado à execução de grandes aplicações e sim, pequenas tarefas, como, por exemplo, realizar um processamento de dados simples e, ao final, enviar um e-mail.
Aqui, já encontramos uma limitação em relação ao código que será executado como uma função: o tempo total de execução e a quantidade de memória disponível para que a função realize a sua tarefa.
Ao criar uma função, é obrigatório informar o "tempo máximo de vida do contêiner" como também, a quantidade máxima de memória que ele pode utilizar. O tempo máximo de execução pode ser configurado entre 30 segundos até no máximo 300 segundos (5 minutos). Em relação à memória, é possível alocar no minímo 128 MB e no máximo 3072 MB (3 GB).
Isso significa que, se a execução da função ultrapassar o tempo máximo especificado, o OCI encerrará a função. Da mesma forma, se a função exigir mais memória do que a alocada, o OCI também encerrará a função.
Outra limitação importante é a quantidade máxima de dados que podem ser enviados para a função, bem como a quantidade máxima de dados que ela pode retornar. Esses limites são fixos em 6 MB e não podem ser alterados.
Essas limitações também se refletem na forma como o serviço é cobrado. A cobrança é baseada no tempo em que a função permanece ativa e na quantidade de memória alocada. Por exemplo, ao calcular rapidamente o custo do serviço para 200 chamadas por dia, totalizando 6.200 chamadas por mês (200 * 31 = 6.200), com um tempo de execução de 300 segundos e utilização de 256 MB de memória, o custo mensal seria de R$ 5,08.

NOTA
Sempre verifique o custo do serviço atráves do link OCI Cost Estimator para obter valores atualizados.
É fundamental entender a tecnologia para projetar de forma eficaz o que pode ser transformado em uma função ou o que é mais apropriado para essa finalidade. Neste capítulo, utilizaremos como exemplo duas funcionalidades da aplicação OCI Pizza que utilizam o OCI Functions para enviar e-mails aos usuários finais.
8.4.2 Cold Start e Hot Start
Como já mencionado, uma função é essencialmente um contêiner que é executado pelo OCI. Essa execução pode ser realizada por meio do utilitário de linha de comando fornecido pelo Fn Project, OCI CLI, SDKs, chamadas HTTP diretas ou em resposta a eventos configuráveis.
Toda execução de aplicações conteinerizadas segue o mesmo processo de deployment. Primeiramente, a imagem do contêiner é baixada do registro de imagens (OCIR) e, em seguida, é executada por uma infraestrutura que suporte a execução de contêineres por exemplo, Container Instances ou OKE.
O tempo total do processo de deployment, desde a inicialização até a disponibilização da função para processar a requisição é denominado Cold Start.
Cold Start é o termo utilizado em ambientes de computação serverless que se refere ao atraso inicial que ocorre quando uma função é invocado pela primeira vez ou após um período de inatividade (idle time). Em outras palavras, é o tempo total que o provedor de nuvem leva para disponibilizar a função e, em seguida, executar o seu código.

Uma função que foi criada e permanece inativa por um certo período, é encerrada pelo OCI. Se ela for invocada novamente, o período de cold start se reinicia para essa função.
O período de cold start não pode ser ajustado e é imprevisível, o que significa que não é possível estimar com precisão o tempo que o OCI levará para disponibilizar a função para uso. Essa é uma das características do serviço que deve ser levada em conta ao se projetar funções.
Já o termo Hot Start tem um significado oposto. Quando uma função ainda está ativa ou, se a infraestrutura de execução estiver "de pé", as requisições para essa função geralmente apresentam um tempo de resposta inferior a um segundo, pois não é necessário realizar todo o processo de deploy nesse caso.
É importante dizer que, requisições subsequentes são direcionadas ao mesmo contêiner. Se necessário, o OCI escala automaticamente a infraestrutura de forma horizontal para atender a um maior volume de requisições, até um limite máximo de 60 GB de memória para execução de todas as funções. Esse é o limite do tenancy, e, se necessário, é possível solicitar uma alteração para aumentar a capacidade máxima de memória.
8.4.3 Provisioned Concurrency
Uma maneira de garantir que as funções estejam prontas para uso e evitar o cold start é utilizar o recurso Provisioned Concurrency. Essa funcionalidade assegura que a infraestrutura de execução permaneça disponível para um número mínimo de invocações simultâneas, permitindo que as funções sejam acionadas rapidamente.
Provisioned Concurrency é medido em "provisioned concurrency units" (PCUs). Você determina a quantidade de PCUs com base no número de invocações concorrentes que você espera para a função. Ao especificar PCUs, a infraestrutura de execução da função se torna dedicada a você e está sempre disponível. No entanto, essa disponibilidade contínua resulta em um custo mais elevado.

Para a aplicação OCI Pizza, não será definido nenhum valor para Provisioned Concurrency, uma vez que as funções da aplicação podem tolerar o cold start. Para mais informações sobre o assunto, consulte "Reducing Initial Latency Using Provisioned Concurrency".
8.4.4 Criando Funções
Agora que a teoria e as limitações do uso de funções foram abordadas, é hora de aplicar esse conhecimento na prática e preparar o ambiente para o desenvolvimento de funções.
NOTA
O passo a passo para a instalação do Fn Project e a preparação do ambiente é baseado no documento "Functions QuickStart on Local Host".
Instalação do Fn Project
Primeiramente, na sua máquina local, deve-se instalar o Fn Project através do comando abaixo:
Uma forma de verificar se a instalação foi concluída com sucesso é através do comando fn que acaba de ser instalado:
NOTA
Para mais informações sobre a instalação do Fn Project, consulte Fn Installation.
Function Application
Toda função antes de ser criada, deve fazer parte de um Application, que serve como um meio de agrupar funções com configurações comuns no OCI.
As funções dentro de um mesmo Application compartilham a mesma sub-rede, utilizam as mesmas variáveis de ambiente e a mesma arquitetura de processador, possuem configurações de log em comum e são executadas de forma isolada em relação a outras Applications.
A aplicação OCI Pizza utiliza um Application por região para agrupar suas funções, que será criada especificando a sub-rede privada (subnprv) e algumas variáveis de ambiente necessárias.
O comando abaixo cria a Application na região Brazil East (São Paulo):
NOTA
A sub-rede utilizada deve ter pelo menos um determinado número mínimo de endereços IP livres para uso do OCI Functions. Consulte CIDR Blocks and OCI Functions para maiores detalhes.
Habilitando a captura dos Logs
Toda função, ao ser executada, gera logs. Para facilitar a análise de possíveis problemas que possam impedir a execução correta da função, é uma boa prática habilitar o registro desses logs.
Para habilitar os logs de execução das funções, primeiro é necessário obter o OCID do Log Group que foi criado na seção do Capítulo 1
Em seguida, obtenha o OCID do Application:
Com essas informações, já é possível habilitar os logs para a execução das funções:
Configurações iniciais do Fn Project
Antes de construir funções é necessário configurar o Fn Project de acordo com alguns parametrôs do OCI.
- Criar um contexto de utilização especificando o parâmetro --provider oracle:
- Após, utilize o contexto que foi recentemente criado:
- Atualize o contexto com as informações referentes ao OCID do compartimento onde as funções serão implantadas, ao OCID do compartimento que armazena as imagens de contêiner (serviço OCIR), à URL da região para o endpoint das funções e à URL do repositório de imagens para onde as imagens serão enviadas (push) e baixadas (pull):
- Por fim, é necessário fazer login no serviço OCIR da região:
NOTA
Para obter mais informações sobre os comandos utilizados, consulte Functions QuickStart on Local Host.
Funções da aplicação OCI Pizza
Existem duas funções localizadas no diretório "services/" que são utilizadas pela aplicação OCI Pizza:
-
fn-user-registry-email
- Função destinada a registrar um novo usuário.
- Após o usuário submeter seus dados de cadastro, a função insere essas informações no banco de dados e, em seguida, envia um e-mail ao usuário para confirmar e efetivar o seu cadastro.
-
fn-password-recovery-email
- Função destinada para recuperação de senha do usuário.
- Essa função será executada para gerar um link de recuperação de senha, que será enviado ao usuário por e-mail.
Ambas as fuções, após o seu devido processamento, utilizam o serviço Email Delivery para enviar um e-mail ao usuário.
NOTA
Aqui, apresentarei os detalhes sobre como criar uma função, utilizando como exemplo a função "fn-password-recovery-email". Os mesmos comandos devem ser aplicados à função "fn-user-registry-email" para que a aplicação OCI Pizza funcione por completo.
Depois de todo o ambiente local do Fn Project estar configurado, a função é criada utilizando o comando fn init:
Note que, no comando acima, além de definir a memória alocada (--memory) e o tempo máximo de execução (--timeout), é obrigatório especificar o ambiente de execução responsável por executar o código da função (--runtime). Para a aplicação OCI Pizza, todo o código foi desenvolvido na linguagem de programação Python, versão 3.8 (python3.8).
NOTA
OCI Functions oferece suporte a diversas linguagens de programação. Para verificar se a sua linguagem de programação é suportada, consulte o link "Languages Supported by OCI Functions".
O comando fn init cria o diretório "fn-password-recovery-email/" com três arquivos:
-
func.py
- Arquivo principal e o primeiro a ser chamado quando a função é invocada.
-
func.yaml
- Arquivo de configuração da função que contém informações essenciais para sua execução.
-
requirements.txt
- Arquito que contém a lista de depêndencias Python necessárias para executar a função.
Por ser um contêiner, a função permite a criação de diretórios, a adição de pacotes extras e o desenvolvimento de bibliotecas específicas, entre outras personalizações. No entanto, é importante notar que quanto maior o tamanho da função, mais tempo será necessário para o seu deployment. Além disso, quanto mais funcionalidades forem incorporadas, maior será a demanda de memória durante a execução da função.
A função fn-password-recovery-email apresenta a seguinte estrutura de arquivos e diretórios:
Build e Push
Para construir a imagem da função, dentro do diretório "fn-password-recovery-email/" e execute o comando fn build:
Por fim, é necessário enviar a imagem para o OCIR utilizando o comando fn push:
NOTA
O comando fn deploy combina a execução dos comandos fn build e fn push. Além de realizar essas operações simultaneamente, ele também atualiza a versão da função para cada nova execução.
Criando a função no OCI
Uma vez que a imagem da função está disponível no OCIR, o próximo passo é criar a função no OCI. Para isso, é necessário especificar a quantidade de memória a ser alocada, o tempo máximo de execução e a URL completa da imagem correspondente à região onde a função será executada:
Executando a Função
Existem diferentes maneiras de executar uma função que já foi criada e configurada no OCI. São elas:
-
Utilizando o comando fn invoke.
-
Por meio do OCI CLI.
-
Por meio dos SDKs do OCI.
-
Por meio de uma chamada HTTP assinada.
Os métodos fn invoke, OCI CLI e SDKs constroem automaticamente a requisição para se comunicar com as APIs do OCI. Por outro lado, ao utilizar o método "HTTP assinado", você precisará criar manualmente a requisição que será enviada às APIs do OCI. Isso inclui a geração de uma assinatura válida e a inclusão do respectivo OCID do compartimento onde a função está localizada. Essas informações devem ser inseridas no cabeçalho HTTP que será utilizado para chamar a função.
NOTA
Consulte "Invoking Functions" para obter mais informações sobre os diferentes métodos de execução de uma função.
Aqui, será usado o comando fn invoke para chamar a função "fn-password-recovery-email":
NOTA
O comando jq, usado no final da linha de comando, não é necessário. Ele foi utilizado apenas para formatar a resposta retornada da função, tornando-a mais legível.
Um último detalhe a ser observado sobre o tempo de cold start: utilizando o utilitário de linha de comando time, é possível verificar que a primeira execução da função levou quase 16 segundos para completar. Esse foi o tempo total que o OCI levou para preparar a função, incluindo o tempo necessário para a execução do código e o tempo do fn invoke.
Na segunda execução, com a infraestrutura ainda ativa (hot start), o tempo total foi de aproximadamente 1 segundo:
É importante lembrar que o período de cold start é imprevisível, e esses valores podem variar, podendo ser maiores ou menores.
Exibindo os logs de execução
Para verificar os logs relacionados à execução da função, recomendo consultar o Console da Web. Essa opção facilita a aplicação de filtros e proporciona uma visualização mais intuitiva das informações.

A sintaxe para visualizar os logs por meio do OCI CLI é mais complexa e geralmente é utilizada quando se deseja enviar os logs para processamento em outra aplicação. De qualquer forma, aqui está um exemplo de comando:
Lembre-se de que o horário em que o OCI registra as informações é em UTC. No exemplo acima, o comando fn invoke foi executado às 10:20:52 no meu horário local (GMT-3).
NOTA
Consulte Logging Query Language Specification para obter mais informações sobre a sintaxe utilizada pelo OCI para consultar logs.