<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hebert Freitas</title>
    <description>The latest articles on DEV Community by Hebert Freitas (@hebertrfreitas).</description>
    <link>https://dev.to/hebertrfreitas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F354873%2F69cf4630-2b15-4077-b410-6266a0bb19b1.jpg</url>
      <title>DEV Community: Hebert Freitas</title>
      <link>https://dev.to/hebertrfreitas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hebertrfreitas"/>
    <language>en</language>
    <item>
      <title>Simulando um cluster k8s localmente com Kind</title>
      <dc:creator>Hebert Freitas</dc:creator>
      <pubDate>Tue, 22 Apr 2025 02:41:06 +0000</pubDate>
      <link>https://dev.to/hebertrfreitas/simulando-um-cluster-com-k8s-localmente-com-kind-292b</link>
      <guid>https://dev.to/hebertrfreitas/simulando-um-cluster-com-k8s-localmente-com-kind-292b</guid>
      <description>&lt;p&gt;Kubernetes é uma realidade bem difundida atualmente. Os principais cloud providers oferecem soluções built-in como o EKS na AWS ou o AKS na Azure.&lt;br&gt;
Do ponto de vista do desenvolvedor tradicional, suas aplicações devem ser construídas e preparadas para executar em containers, e em algum momento (geralmente em uma pipeline de CI/CD) o container desta aplicação será implantado em um cluster k8s.&lt;/p&gt;

&lt;p&gt;No entanto, seja por necessidade de conhecer melhor a infraestrutura do k8s (o que recomendo que todo desenvolvedor conheça profundamente se trabalha com aplicações dentro dele), ou seja porque você precisa simular ou testar algum recurso, pode ser necessário efetivamente fazer o deploy do container no ambiente com pods, deployments, services, e toda a estrutura necessária para rodar em um cluster kubernetes.&lt;br&gt;
Além disso, nem sempre existe a possibilidade de você fazer o deploy de fato em um ambiente em cloud para simular uma situação específica, ou as vezes você só quer fazer um teste de algo rápido.&lt;/p&gt;

&lt;p&gt;Já a alguns anos existem projetos que simulam um cluster k8s em sua máquina local, hoje vou apresentar um que atende muito bem os meus requisitos que é o &lt;a href="https://kind.sigs.k8s.io/" rel="noopener noreferrer"&gt;Kind&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Kind
&lt;/h2&gt;

&lt;p&gt;O kind é uma ferramenta que executa um cluster kubernetes na sua máquina com o uso de docker.&lt;br&gt;
Basicamente ela cria um cluster k8s com os nodes e control plane sendo executados em containers docker.&lt;br&gt;
Para executar o kind você precisa das seguintes ferramentas instaladas e funcionando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docker&lt;/li&gt;
&lt;li&gt;kubectl (para iteragir com o cluster)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Instalação
&lt;/h3&gt;

&lt;p&gt;Recomendo seguir as &lt;a href="https://kind.sigs.k8s.io/#installation-and-usage" rel="noopener noreferrer"&gt;instruções oficiais para instalação do kind&lt;/a&gt;.&lt;br&gt;
Ele pode ser instalado como um pacote do golang.&lt;br&gt;
Em um mac pode ser instalado via brew executando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;kind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Criando clusters
&lt;/h3&gt;

&lt;p&gt;Você pode criar um cluster k8s simplesmente executando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kind create cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por padrão, este comando criará um cluster com um único nós, mas se você quiser criar um cluster com múltiplos nós você pode usar um arquivo yaml como este:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#kind-multinode-cluster.yaml&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cluster&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;
&lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;control-plane&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;e então executar o comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kind create cluster &lt;span class="nt"&gt;--config&lt;/span&gt; kind-multinode-cluster.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando você cria um cluster no kind ele também já adiciona um contexto no seu arquivo de configuração do kubectl (geralmente em &lt;code&gt;~/.kube/config&lt;/code&gt;) apontando para as configurações do seu cluster local, portanto logo depois da criação do cluster basta executar comandos do kubectl tradicionais, por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl cluster-info &lt;span class="c"&gt;#exibe as informações do cluster&lt;/span&gt;
kubectl get nodes &lt;span class="c"&gt;#exibe as informações dos nodes do cluster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Load Balancers no Kind
&lt;/h3&gt;

&lt;p&gt;Quando você precisa expor um determinado serviço no kubernetes é comum criar um Service do tipo Load Balancer.&lt;br&gt;
Quando você está usando uma solução de kubernetes em um cloud provider como AWS ou Azure, automaticamente ao criar este Load Balancer um IP é fornecido e você consegue acessar o serviço pelo IP e porta.&lt;br&gt;
Ocorre que no kind, você está rodando localmente em sua máquina, portanto é preciso que alguma outra ferramenta faça este papel, para isso existe o &lt;a href="https://github.com/kubernetes-sigs/cloud-provider-kind?tab=readme-ov-file#install" rel="noopener noreferrer"&gt;Kubernetes Cloud Provider for KIND&lt;/a&gt;&lt;br&gt;
Recomendo fortemente que você instale ele para que seja possível expor serviços em um IP e porta para teste.&lt;br&gt;
Lembrando que após a instalação você precisa &lt;a href="https://github.com/kubernetes-sigs/cloud-provider-kind?tab=readme-ov-file#running-the-provider" rel="noopener noreferrer"&gt;executar o binário do provider&lt;/a&gt; enquanto estiver provisionando serviços no cluster k8s.&lt;/p&gt;
&lt;h3&gt;
  
  
  Fazendo o deploy de um serviço no cluster
&lt;/h3&gt;

&lt;p&gt;A partir deste ponto, você já pode fazer o deploy de qualquer serviço no seu cluster k8s localmente, para exemplificar segue um arquivo yaml contendo deployment, especificação de pods e service do tipo Load Balancer para uma api de teste em python fast-api&lt;/p&gt;

&lt;p&gt;O código desta api está disponível &lt;a href="https://github.com/hebertrfreitas/fastapi-example" rel="noopener noreferrer"&gt;neste projeto&lt;/a&gt; e a mesma possui uma pipeline de CI/CD no github actions que faz o push de uma imagem docker para o docker hub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#simple-api-deployment.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pokemon-api&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pokemon-api&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pokemon-api&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pokemon-api&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hebertrfreitas/fastapi-example:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
        &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pokemon-api-deployment&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pokemon-api&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para aplicar no cluster basta executar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; simple-api-deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida para chamar e testar o serviço use os seguintes comandos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#obtem o IP do load balancer&lt;/span&gt;
&lt;span class="nv"&gt;POKEMON_API_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get svc pokemon-api-deployment &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.status.loadBalancer.ingress[0].ip}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;#faz uma chamada na rota /pokemon da aplicação, que chama uma API externa e faz um request para trazer os dados de um pokemon aleatório&lt;/span&gt;
curl http://&lt;span class="nv"&gt;$POKEMON_API_IP&lt;/span&gt;/pokemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Projeto no github
&lt;/h3&gt;

&lt;p&gt;Se quiser testar todos os comandos e códigos que usei neste post, eles estão disponíveis neste repositorio no github:&lt;br&gt;
&lt;a href="https://github.com/hebertrfreitas/kind-labs" rel="noopener noreferrer"&gt;https://github.com/hebertrfreitas/kind-labs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>k8s</category>
      <category>devops</category>
    </item>
    <item>
      <title>Montando um ambiente local para testar LLM's OpenSource</title>
      <dc:creator>Hebert Freitas</dc:creator>
      <pubDate>Wed, 19 Feb 2025 02:38:06 +0000</pubDate>
      <link>https://dev.to/hebertrfreitas/montando-um-ambiente-local-para-testar-llms-opensource-1ia3</link>
      <guid>https://dev.to/hebertrfreitas/montando-um-ambiente-local-para-testar-llms-opensource-1ia3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Estou iniciando uma série de artigos para compartilhar a minha experiência como engenheiro de software atuando focado com tecnologias de Generative AI, fique atento aos próximos posts para aprender um pouco mais sobre este tópico.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ao pesquisar um pouco sobre o mundo de Large Language Models é natural que você se depare com os grandes fornecedores de modelos como OpenAI, Anthropic, etc. Estas empresas fornecem seus modelos proprietários no formato SaaS através de um serviço que pode ser acessado por uma API desde que você tenha uma conta e pague um valor pelo seu uso.&lt;/p&gt;

&lt;p&gt;No entanto, nem só de modelos proprietários vive o universo de LLM's, muitos modelos são disponibilizados de forma open source, o que significa que você pode baixá-los e executá-los como quiser.&lt;/p&gt;

&lt;p&gt;Apesar de parece simples, detalhes como arquitetura do modelo, configurações e requisitos de hardware podem tornar a tarefa significativamente árdua para um desenvolvedor que simplesmente quer testar diferentes modelos.&lt;/p&gt;

&lt;p&gt;Para atender essa demanda, existem algumas iniciativas que tornam este trabalho mais simples, uma delas é o &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;ollama&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ollama
&lt;/h2&gt;

&lt;p&gt;O ollama é um projeto open source que tem como objetivo facilitar a nossa experiência para obter e rodar LLM's.&lt;br&gt;
Ele pode ser &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;instalado na sua máquina&lt;/a&gt; e fornece um cli através do qual baixar e executar uma LLM é tão simples quanto executar este comando&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run llama3.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imediatamente após a execução desde comando, caso você ainda não tenha este modelo localmente ele irá fazer o download do mesmo e logo em seguida fornecer uma iteração com o modelo via linha comando.&lt;/p&gt;

&lt;p&gt;Você também pode optar por executar o ollama dentro de um container docker, desde que você já tenha o docker instalado basta executar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; ollama:/root/.ollama &lt;span class="nt"&gt;-p&lt;/span&gt; 11434:11434 &lt;span class="nt"&gt;--name&lt;/span&gt; ollama ollama/ollama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida para executar o modelo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; ollama ollama run llama3.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nos exemplos acima, estamos executando o modelo &lt;code&gt;llama3.2&lt;/code&gt;, para saber a lista de modelos disponíveis, consulte o &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;catálogo do ollama&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esta solução já atende grande parte das necessidades, mas ela pode melhorar.&lt;br&gt;
O serviço do ollama também disponibiliza uma API para comunicação com o modelo, e devido a isso podemos tanto fazer requisições http como também podemos projetar um interface gráfica para facilitar nosso dia a dia, e neste ponto entra a nossa próxima ferramenta que irá nos ajudar, a &lt;a href="https://github.com/open-webui/open-webui" rel="noopener noreferrer"&gt;OpenWebUI&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  OpenWebUI
&lt;/h2&gt;

&lt;p&gt;O OpenWebUI é um projeto opensource que fornece uma interface padronizada de chat conversacional para testar LLM's, pode ser usada em conjunto com o ollama que assume o papel de servir as LLM's localmente.&lt;/p&gt;

&lt;p&gt;Você pode instalar o OpenWebUI via &lt;code&gt;pip&lt;/code&gt; com o comando (recomendamos que você crie um ambiente virtual antes de instalar&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;open-webui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida rodar o servidor web com&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open-webui serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Acessando o endereço &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; você verá uma tela parecida com esta.&lt;/p&gt;

&lt;p&gt;O OpenWebUI também pode ser executado via docker e tem compatibilidade total com o ollama, caso queira consumir os modelos do ollama, execute o comando abaixo (considerando que você já tem um container docker de nome ollama sendo executado)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:8080 &lt;span class="nt"&gt;--add-host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host.docker.internal:host-gateway &lt;span class="nt"&gt;-v&lt;/span&gt; open-webui:/app/backend/data &lt;span class="nt"&gt;--name&lt;/span&gt; open-webui &lt;span class="nt"&gt;--restart&lt;/span&gt; always ghcr.io/open-webui/open-webui:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neste caso, após a execução, basta esperar alguns segundos (o servidor do OpenWebUI pode demorar um pouco para subir) e acessar o endereço &lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Os modelos servidos pelo ollama estarão disponíveis e você poderá conversar com eles, semelhante ao exemplo à seguir:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7nemvg10btie6lfyfpl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7nemvg10btie6lfyfpl.png" alt="print da tela mostrando a interface web do serviço openwebui em um chat com o modelo llama 3.2" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus, executando tudo com um comando
&lt;/h2&gt;

&lt;p&gt;Para quem achar muito trabalhoso, eu disponibilizei um projeto no github por onde você pode subir todo este ambiente e os serviços com apenas um comando.&lt;br&gt;
O projeto está disponível aqui: &lt;a href="https://github.com/hebertrfreitas/generative-ai-labs/tree/main/examples/ollama" rel="noopener noreferrer"&gt;https://github.com/hebertrfreitas/generative-ai-labs/tree/main/examples/ollama&lt;/a&gt;&lt;/p&gt;

</description>
      <category>genai</category>
      <category>ollama</category>
      <category>openwebui</category>
      <category>docker</category>
    </item>
    <item>
      <title>Murmurhash -criando um rollout progressivo via backend</title>
      <dc:creator>Hebert Freitas</dc:creator>
      <pubDate>Fri, 21 Apr 2023 21:32:15 +0000</pubDate>
      <link>https://dev.to/hebertrfreitas/murmurhash-criando-um-rollout-progressivo-via-backend-cba</link>
      <guid>https://dev.to/hebertrfreitas/murmurhash-criando-um-rollout-progressivo-via-backend-cba</guid>
      <description>&lt;p&gt;Ao ler o título deste post o desenvolvedor mais experiente provavelmente vai se perguntar: "Mas já não existem várias formas de se liberar uma nova funcionalidade para usuários progressivamente ?"&lt;/p&gt;

&lt;p&gt;E a resposta é sim, de fato existem algumas maneiras.&lt;br&gt;
Por exemplo, se você estiver em um ambiente com k8s e istio pode por exemplo usar um &lt;a href="https://istio.io/latest/docs/reference/config/networking/virtual-service/"&gt;Virtual Service para isso&lt;/a&gt;, você ainda pode fazer isso fora mesmo do k8s com um simples proxy reverso na frente de instâncias diferentes da sua aplicação (uma com a nova feature, e outra sem) além de várias outras.&lt;/p&gt;

&lt;p&gt;Porem aqui neste artigo, vou lhe apresentar uma forma de atingir o mesmo objetivo usando um algoritmo de hash bem conhecido, o &lt;a href="https://en.wikipedia.org/wiki/MurmurHash"&gt;Murmurhash&lt;/a&gt; que pode ser bem útil dependendo do seu cenário.&lt;/p&gt;
&lt;h3&gt;
  
  
  Murmurhash
&lt;/h3&gt;

&lt;p&gt;O murmurhash é um algoritmo de hash não criptográfico.&lt;/p&gt;

&lt;p&gt;Ser um algoritmo de hash significa que dado um input ele converte isso para um output encriptado. &lt;br&gt;
Quer um exemplo simples no nosso dia a dia ?&lt;br&gt;
Se você estiver em um linux ou mac pode usar o sha256 da seguinte maneira:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; teste de mensagem sha256 | shasum &lt;span class="nt"&gt;-a&lt;/span&gt; 256
8785c54a5c506d0c4f031a76b7170c35b2bde862a2bbd7ab2d0485570b75bc06
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Já a parte do não criptográfico se refere ao fato de muitas funções hash terem como principal característica tornar impossível a reversão da saída de volta no texto original, o que é especialmente útil se você estiver trabalhando com segurança onde isto é uma premissa. Este não é o caso do murmurhash que não se preocupa tanto com esta característica em detrimento de outras.&lt;/p&gt;

&lt;p&gt;Ao contrário da família de algoritmos criptográficos, o murmurhash foi criado para ser rápido devido a sua otimização, com uma boa resistência a colisões e outras características interessantes.&lt;br&gt;
O algoritmo Murmurhash foi criado por Austin Appleby e a implementação de referência em C++ pode ser encontrada no &lt;a href="https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para demonstrar um pouco do que o algoritmo pode fazer, vamos olhar um simples código em java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hebert freitas"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;inputBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Charset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTF8"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="nc"&gt;HashCode&lt;/span&gt; &lt;span class="n"&gt;hashCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Hashing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;murmur3_128&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputBytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;outputBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asBytes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neste simples código estamos aplicando o murmurhash e fazendo o hash de um array de bytes que foi gerado a partir de uma simples string com o meu nome.&lt;br&gt;
Optei por usar uma implementação do &lt;a href="https://github.com/google/guava/blob/v31.1/guava/src/com/google/common/hash/Murmur3_128HashFunction.java"&gt;murmurhash disponível dentro da lib guava&lt;/a&gt; mas nada lhe impede de usar outra implementação.&lt;/p&gt;

&lt;p&gt;O algoritmo do murmurhash é também determinístico, de maneira que para uma mesma entrada sempre haverá uma mesma saída.&lt;/p&gt;

&lt;p&gt;Neste exemplo, estamos optando por pegar o resultado do hash também como um array de bytes, mas seria possível também pegar o resultado como um inteiro ou long.&lt;/p&gt;

&lt;p&gt;Todas estas características fazem com que o murmurhash seja usado em cenários interessantes, vamos ver a seguir um possível cenário.&lt;/p&gt;
&lt;h3&gt;
  
  
  Aplicabilidade
&lt;/h3&gt;

&lt;p&gt;Você sabia que quando usa a lib para interagir com o &lt;a href="https://pt.wikipedia.org/wiki/Apache_Kafka"&gt;kafka&lt;/a&gt; na sua aplicação o murmurhash é usado por debaixo dos panos para definir em qual partição de um tópico a mensagem será postada com base na sua key ? &lt;br&gt;
A referencia pode ser vista &lt;a href="https://github.com/apache/kafka/blob/6cf1010a6bf3bd6a9e0495c4787eb5b3aa01d5e8/clients/src/main/java/org/apache/kafka/clients/producer/internals/BuiltInPartitioner.java#L328"&gt;aqui&lt;/a&gt; e basicamente o que é feito é usar o murmurhash aplicado a key da mensagem e busca o resto da divisão sobre o total de partições.&lt;br&gt;
Lembram-se também do fato de que o kafka consegue garantir a ordenão de mensagem apenas dentro de uma partição de um tópico mas não dentro de todas as partições ?&lt;br&gt;
Eis outro benefício do murmurhash neste cenário, como sempre teremos o mesmo valor de saída para a mesma entrada mensagens com a mesma key sempre serão direcionadas para a mesma partição.&lt;br&gt;
O murmurhash também é muito eficaz no chamado &lt;a href="https://en.wikipedia.org/wiki/Avalanche_effect"&gt;efeito avalanche&lt;/a&gt; que nada mais é do que uma característica onde caso modifiquemos minimamente o input teremos um resultado totalmente diferente,  e devido a isso, também é eficaz em distribuir uniformemente entre as partições de um tópico mensagens com keys distintas.&lt;/p&gt;

&lt;p&gt;A seguir vamos ver como algumas destas características podem ser aplicadas em um cenário real.&lt;/p&gt;
&lt;h3&gt;
  
  
  Criando um rollout progressivo com o murmurhash
&lt;/h3&gt;

&lt;p&gt;Imagine que você está diante do seguinte cenário: &lt;br&gt;
"Uma determinada aplicação possui uma base de usuários relativamente grande e você deseja liberar uma nova funcionalidade que foi implementada de forma progressiva para estes usuários, primeiro para 10% da base, depois 20%, e assim sucessivamente até atingir todos os usuários"&lt;/p&gt;

&lt;p&gt;Uma das melhores formas de fazer isso é implementar uma feature toogle que permite ligar ou desligar a nova feature na sua aplicação, mas neste caso, não pode ser uma toogle estática que representa o mesmo valor para todos os usuário da aplicação, ela teria que identificar se um determinado cliente entraria ou não nela naquele determinado momento baseado em alguma lógica.&lt;br&gt;
Uma outra alternativa que você pode pensar é marcar os seus usuários na base de dados dizendo quem entra e quem não, atualizando isso aos poucos conforme você vai expandindo o fluxo, mas esta solução não parece ser muito eficiente e de difícil manutenção.&lt;/p&gt;

&lt;p&gt;Para atender essa demanda podemos aplicar o murmurhash, usando como input alguma coisa que sempre seja única e nunca mude para um usuário ( id, cpf, etc)&lt;br&gt;
Vamos ver um simples exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;calculatePercentage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="nc"&gt;HashCode&lt;/span&gt; &lt;span class="n"&gt;hashCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Hashing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;murmur3_32_fixed&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Charset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTF8"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;hashedValueInLong&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUnsignedLong&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asInt&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;percentual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashedValueInLong&lt;/span&gt;  &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Percentual: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;percentual&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;percentual&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Detalhando cada um dos passos:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Aplicamos o murmurhash a uma string de input da mesma maneira que fizemos ateriormente.&lt;/li&gt;
&lt;li&gt;Pegamos o resultado do murmurhash como um inteiro e convertermos para um unsigned long (vamos aprofundar este detalhe a seguir)&lt;/li&gt;
&lt;li&gt;Dividimos o numero resultado do murmurhash por 2 elevado a 32 porque este é o valor máximo que pode ser atingido.&lt;/li&gt;
&lt;li&gt;O resultado é um percentual, um valor em 0.1 e 1.0 aplicado a um usuário.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neste exemplo, estamos usando a versão de 32 bits do murmurhash, que pode gerar um hash de até 32 bits.&lt;br&gt;
Na linha 3 do código convertermos um int para um long usando porque valores inteiros podem ser negativos e esta é uma forma simples de se obter o valor sem sinal, para mais detalhes veja a &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#toUnsignedLong-int-"&gt;documentação do método &lt;code&gt;Integer.toUnsignedLong&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
Se você executar este método com o input &lt;code&gt;836d473d-56be-4ef9-9b13-1fc8f36ef98e&lt;/code&gt; terá como resultado o valor &lt;code&gt;0.913...&lt;/code&gt;. &lt;br&gt;
Com este valor basta comparar com o percentual máximo que você deseja disponibilizar a nova feature. Neste exemplo, se estivermos liberando para 20% da base com certeza este usuário não entraria, mas se já estivermos liberando para 95% da base este usuário entraria porque seu percentual é 91%.&lt;/p&gt;

&lt;h3&gt;
  
  
  É possível ter exatidão máxima com este método ?
&lt;/h3&gt;

&lt;p&gt;Considerando as características do algoritmo murmurhash ele funciona melhor conforme o publico alvo vai aumentando, ou neste caso, conforme maior é sua base de usuários.&lt;/p&gt;

&lt;h3&gt;
  
  
  Projeto de exemplo
&lt;/h3&gt;

&lt;p&gt;Para exemplificar tudo o que discutimos criei um projeto de exemplo, onde a partir de uma lista fictícia de 10.000 usuários e um valor predeterminado de 10% classificamos esta lista para demonstrar que o algoritmo consegue distribuir uniformemente os hashs e filtrar um valor muito próximo de 10% de usuários mesmo sem conhecer toda a base.&lt;/p&gt;

&lt;p&gt;O projeto está disponível neste link = &lt;a href="https://github.com/hebertrfreitas/murmurhash-example"&gt;https://github.com/hebertrfreitas/murmurhash-example&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>backend</category>
      <category>java</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>DynamoDB local - Um mock funcional para o DynamoDB</title>
      <dc:creator>Hebert Freitas</dc:creator>
      <pubDate>Sun, 13 Feb 2022 19:11:38 +0000</pubDate>
      <link>https://dev.to/hebertrfreitas/dynamodb-local-um-mock-funcional-para-o-dynamodb-c94</link>
      <guid>https://dev.to/hebertrfreitas/dynamodb-local-um-mock-funcional-para-o-dynamodb-c94</guid>
      <description>&lt;p&gt;Recentemente escrevi um post sobre o localstack, que fornece um mock funcional para os principais serviços da aws.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/hebertrfreitas" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F354873%2F69cf4630-2b15-4077-b410-6266a0bb19b1.jpg" alt="hebertrfreitas"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/hebertrfreitas/localstack-um-mock-funcional-para-os-servicos-da-aws-1jkd" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Localstack - Um mock funcional para os serviços da aws&lt;/h2&gt;
      &lt;h3&gt;Hebert Freitas ・ Nov 20 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#localstack&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#braziliandevs&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Porém, caso você esteja trabalhando especificamente com o &lt;a href="https://aws.amazon.com/pt/dynamodb/" rel="noopener noreferrer"&gt;DynamoDB&lt;/a&gt; a aws fornece oficialmente uma ferramenta chamada &lt;a href="https://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/DynamoDBLocal.html" rel="noopener noreferrer"&gt;DynamoDB local&lt;/a&gt; que é recomendada para testes e desenvolvimento da aplicação sem se conectar aos serviços reais da aws. &lt;br&gt;
Neste artigo vamos entender um pouco como funciona esta ferramenta 😀&lt;/p&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Como Usar&lt;/li&gt;
&lt;li&gt;Usando via Docker&lt;/li&gt;
&lt;li&gt;Aplicação de exemplo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Como usar &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;O DynamoDB local convenientemente é fornecido de três formas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Como um programa java executável (arquivo .jar)&lt;/li&gt;
&lt;li&gt;Através de uma imagem docker&lt;/li&gt;
&lt;li&gt;Através de uma dependência do maven&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A página de &lt;a href="https://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html" rel="noopener noreferrer"&gt;documentação&lt;/a&gt; dá detalhes sobre o uso de cada uma destas opções. Neste post utilizaremos a opção via imagem docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Utilizando o DynamoDB local via docker &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Se você apenas executar o comando à seguir no seu terminal(assumindo que você tem o docker instalado corretamente) já terá o dynamodb local rodando na porta 8000&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker run -p 8000:8000 amazon/dynamodb-local


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;E sim, você já consegue apontar a sua aplicação para ele 😁, vamos demonstrar isso no no tópico em que construímos uma aplicação de exemplo.&lt;/p&gt;

&lt;p&gt;Por padrão o entrypoint executado pela imagem docker oficial irá utilizar a propriedade &lt;code&gt;-inMemory&lt;/code&gt; que significa que o dynamo irá manter todos os dados em memória, logo ao finalizar o container automaticamente os dados são perdidos.&lt;/p&gt;

&lt;p&gt;Eventualmente vamos querer manter os arquivos e dados do banco de dados criados via container docker mesmo após a finalização do mesmo, para isso segue o conteúdo de um arquivo docker compose que explora um pouco mais as opções disponíveis.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dynamodb-local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;working_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/dynamodblocal&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;DynamoDBLocal.jar&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-sharedDb&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-dbPath&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;./data"&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amazon/dynamodb-local:latest"&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dynamodb-local&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.dynamodbdata:/home/dynamodblocal/data"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Para utilizar estas instruções, salve o conteúdo em um arquivo chamado &lt;code&gt;docker-compose.yaml&lt;/code&gt; e em seguida execute o comando &lt;code&gt;docker-compose up&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Algumas observações sobre a configuração realizar no docker compose:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;sobrescrevemos o comando inicial executado ao subir o container na linha &lt;code&gt;command:&lt;/code&gt;, observe que não colocamos o comando &lt;code&gt;java&lt;/code&gt; porque ele já é especificado no entrypoint default da imagem docker fornecida pela aws. &lt;/li&gt;
&lt;li&gt;utilizamos o parâmetro &lt;code&gt;-sharedDb&lt;/code&gt; para informar ao dynamo que desejamos criar um único arquivo para armazenar os bancos de dados.&lt;/li&gt;
&lt;li&gt;o parâmetros &lt;code&gt;-dbPath ./data&lt;/code&gt; informa onde queremos que o arquivo de banco de dados seja salvo. Como o workdir foi definido para &lt;code&gt;/home/dynamodblocal&lt;/code&gt; o banco de dados será salvo no caminho absoluto &lt;code&gt;/home/dynamodblocal/data&lt;/code&gt; dentro do container.&lt;/li&gt;
&lt;li&gt;Criamos um volume apontando o caminho &lt;code&gt;.dynamodbdata&lt;/code&gt; da máquina host para &lt;code&gt;/home/dynamodblocal/data&lt;/code&gt; dentro do container, logo todos os arquivos de banco de dados gerados pelo dynamo serão mapeados para lá.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ao executar o comando &lt;code&gt;docker-compose up&lt;/code&gt; na pasta com o arquivo de conteúdo exemplificado acima podemos observar que será criada a pasta &lt;code&gt;.dynamodbdata&lt;/code&gt; a princípio vazia.&lt;/p&gt;

&lt;p&gt;Caso você tenha o &lt;a href="https://aws.amazon.com/pt/cli/" rel="noopener noreferrer"&gt;aws-cli&lt;/a&gt; instalado na sua máquina, é perfeitamente possível executar comandos apontando para o dynamodb local, basta inserir o parâmetro &lt;code&gt;--endpoint-url=http://localhost:8000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Por exemplo, para listar as tabelas existentes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:8000 dynamodb list-tables


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Aplicação de exemplo &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Para demonstrar o uso do dynamodb local construi uma pequena api usando spring boot e kotlin, o código está disponível no &lt;a href="https://github.com/hebertrfreitas/dynamodb-example" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nesta aplicação estamos usando a biblioteca &lt;a href="https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/dynamodb-enhanced-client.html" rel="noopener noreferrer"&gt;dynamodb-enhanced&lt;/a&gt; que faz parte do AWS sdk for java (v2) e fornece uma maneira bem intuitiva de mapear as tabelas dynamo na sua aplicação via anotações. &lt;/p&gt;

&lt;p&gt;Para que a aplicação possa se comunicar com o dynamo é necessário criar um objeto do tipo &lt;code&gt;software.amazon.awssdk.services.dynamodb.DynamoDbClient&lt;/code&gt; na aplicação fizemos isso provisionando um &lt;code&gt;bean&lt;/code&gt; do spring da seguinte maneira:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;dynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="nc"&gt;DynamoDbClient&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DynamoDbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;region&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;regionAsAwsEnum&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(!&lt;/span&gt; &lt;span class="n"&gt;awsProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
                    &lt;span class="nf"&gt;endpointOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;//awsProperties.endpoint = http://localhost:8000 &lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;credentialsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DefaultCredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Observe que chamamos no builder o método &lt;code&gt;endpointOverride&lt;/code&gt; apenas se &lt;code&gt;awsProperties.endpoint&lt;/code&gt; for diferente de nulo.&lt;br&gt;
A ideia aqui é passar nessa propriedade o valor &lt;code&gt;http://localhost:8000&lt;/code&gt; exatamente como fizemos no aws-cli, apontando para onde está rodando o DynamoDB local na sua máquina.&lt;br&gt;
Se você não passar essa propriedade o builder não executara este ponto e apontará para o serviço real da aws.&lt;/p&gt;

&lt;p&gt;Nesta api estamos fazendo um simples crud de um cenário hipotético onde queremos cadastrar pintores famosos e suas pinturas.&lt;/p&gt;

&lt;p&gt;A estrutura básica pensada para o modelo de dados pode ser representada da seguinte maneira em json&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Leonardo da Vinci"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"birthdate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1452-04-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paintings"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mona Lisa (La Gioconda)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Louvre, Paris"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Last Supper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Santa Maria delle Grazie, Milan"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esta estrutura pode ser facilmente mapeada para classes através da anotações, segue exemplo de como fizemos isso no código&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * Important note: using `var` because the AWS SDK needs default `setters` in order to detect an attribute
 */&lt;/span&gt;
&lt;span class="nd"&gt;@DynamoDbBean&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Painter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDbPartitionKey&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;JsonFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;birthdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;paintings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Paint&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="nd"&gt;@DynamoDbBean&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?){&lt;/span&gt;
    &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;OBS: Os contrutores default aceitando nulo e o uso de var são devido a requisitos do AWS SDK que necessita de classes com getters e setters padrão para que seja possível mapear as mesmas para o dynamo&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para executar o projeto baixe o mesmo do github, em seguida na raiz execute &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Isso irá subir um docker-compose&lt;br&gt;
contendo o dynamodb-local e também um container com o aws-cli que fará a criação da tabela inicial.&lt;/p&gt;

&lt;p&gt;Em seguida execute &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

./gradlew bootRun


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Este segundo comando irá subir a aplicação na porta 8080.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OBS: O java 11 instalado é requisito para que você consiga executar o projeto.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A api está documentada com swagger então é possível acessar o mesmo no endereço &lt;code&gt;http://localhost:8080/swagger-ui.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkwf2mhhkc93vml7yapx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkwf2mhhkc93vml7yapx.png" alt="Imagem do swagger-ui acessível no navegador"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para testar, seguem alguns comandos que podem ser executados via &lt;code&gt;curl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Criando um pintor&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s1"&gt;'localhost:8080/painters'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
    "id": "123",
    "name": "Leonardo da Vinci",
    "birthdate": "1452-04-15",
    "paintings":[
        {
            "name": "Mona Lisa (La Gioconda)",
            "location": "Louvre, Paris"
        },
        {
            "name": "The Last Supper",
            "location": "Santa Maria delle Grazie, Milan"
        }
    ]
}'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Bucando um pintor pelo id&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="s1"&gt;'localhost:8080/painters/123'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Deletando um pintor&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; DELETE &lt;span class="s1"&gt;'localhost:8080/painters/123'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>aws</category>
      <category>dynamodb</category>
      <category>docker</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Localstack - Um mock funcional para os serviços da aws</title>
      <dc:creator>Hebert Freitas</dc:creator>
      <pubDate>Sat, 20 Nov 2021 19:36:10 +0000</pubDate>
      <link>https://dev.to/hebertrfreitas/localstack-um-mock-funcional-para-os-servicos-da-aws-1jkd</link>
      <guid>https://dev.to/hebertrfreitas/localstack-um-mock-funcional-para-os-servicos-da-aws-1jkd</guid>
      <description>&lt;p&gt;Atualmente é bem comum trabalhar em projetos que se comuniquem com alguns serviços da aws, seja uma fila SQS, um bucket S3 para armazenar arquivos ou até mesmo um banco de dados DynamoDB.&lt;/p&gt;

&lt;p&gt;Durante o desenvolvimento deste software você irá precisar fazer a comunicação com estes serviços em nuvem da aws para testar o seu produto, simular cenários ou até mesmo executar alguns testes de integração.&lt;/p&gt;

&lt;p&gt;No entanto, nem sempre você terá disponível um ambiente na aws onde você possa alterar livremente de maneira que atenda os requisitos do desenvolvimento, ou mesmo que tenha, pode ser complexo fazer a comunicação em um primeiro momento. &lt;/p&gt;

&lt;p&gt;Para resolver estes problemas, existe o projeto &lt;a href="https://github.com/localstack/localstack" rel="noopener noreferrer"&gt;localstack&lt;/a&gt;, que fornece um mock dos principais servicos da AWS como SQS, SNS, S3 entre outros.&lt;/p&gt;

&lt;h3&gt;
  
  
  Como usar o localstack
&lt;/h3&gt;

&lt;p&gt;A forma mais conveniente de executar o projeto é através de uma imagem docker oficial disponível neste link:  &lt;a href="https://hub.docker.com/r/localstack/localstack" rel="noopener noreferrer"&gt;dockerhub localstack&lt;/a&gt;, segue um exemplo de um arquivo do docker-compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;localstack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localstack"&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localstack/localstack:0.12.11&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4566:4566"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4571:4571"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SERVICES=sqs,s3&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para executar este arquivo crie um arquivo yaml chamado docker-compose.yaml com o conteúdo acima e execute o comando &lt;code&gt;docker-compose up&lt;/code&gt; na mesma pasta do arquivo criado. &lt;/p&gt;

&lt;p&gt;Alguns pontos importantes a se destacar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;neste exemplo estamos usando a versão 0.12.11 mas você pode e deve checar qual versão mais atualizada está disponível no docker hub do projeto.&lt;/li&gt;
&lt;li&gt;a variável de ambiente &lt;code&gt;SERVICES&lt;/code&gt; aceita uma lista de nomes dos serviços que desejamos provisionar, neste exemplo estamos pedindo ao localstack para disponibilizar uma api do SQS e uma do S3.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Interagindo com os serviços da aws no localstack
&lt;/h3&gt;

&lt;p&gt;Uma das formas oficiais de se comunicar com os serviços da aws é o &lt;a href="https://hub.docker.com/r/localstack/localstack" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você pode ter o awscli instalado na sua máquina e apontar para os serviços da aws mockados no localstack.&lt;br&gt;
Para fazer isso basta inserir em todos os comando o seguinte parâmetro: &lt;code&gt;--endpoint-url=http://localstack:4566&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Exemplo de criação de uma queue no SQS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 sqs create-queue &lt;span class="nt"&gt;--queue-name&lt;/span&gt; teste
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No entanto, caso você não queria ou não precise ter o &lt;code&gt;awscli&lt;/code&gt; instalado no seu computador, o localstack já disponibiliza um wrapper do &lt;code&gt;awscli&lt;/code&gt; chamado &lt;code&gt;awslocal&lt;/code&gt; que já vem pré-configurado para interagir localmente com os serviços mockados da aws.&lt;/p&gt;

&lt;p&gt;Considerando que você esteja executando o projeto via docker é possível fazer o mesmo exemplo citado acima da seguinte maneira:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#para entrar no container (considerando que o nome do seu container é localstack)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; localstack sh
&lt;span class="c"&gt;#comando equivalente ao apresentado acima para criar uma fila &lt;/span&gt;
awslocal sqs create-queue &lt;span class="nt"&gt;--queue-name&lt;/span&gt; teste 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe que não precisamos adicionar o parâmetro &lt;code&gt;--endpoint-url&lt;/code&gt; pois o &lt;code&gt;awslocal&lt;/code&gt; já está configurado assumindo que desejo me comunicar com os serviços localmente.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠ Porque localhost:4566 ?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
O projeto localstack disponibiliza o mock dos serviços da aws na porta 4566.&lt;/p&gt;

&lt;p&gt;Considerando que você está executando o projeto via docker é importante que você mapeie a porta 4566 do container para a 4566(ou outra a seu critério) da sua máquina local, foi isso que fizemos no arquivo de exemplo do docker-compose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Como executar comandos no startup do container
&lt;/h3&gt;

&lt;p&gt;Caso você queira executar comandos após o startup do container como criar um bucket no s3 ou inicializar uma fila sqs basta criar um arquivo .sh com a lista de comandos e montá-lo como volume na pasta &lt;code&gt;/docker-entrypoint-initaws.d&lt;/code&gt; dentro do container.&lt;/p&gt;

&lt;p&gt;Ao iniciar, o localstack irá executar todos os scripts existentes neste caminho.&lt;br&gt;
Sendo assim nosso docker-compose ficaria com a seguinte configuração:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;localstack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localstack"&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localstack/localstack:0.12.11&lt;/span&gt;
      &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4566:4566"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4571:4571"&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SERVICES=sqs,s3&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./init-scripts:/docker-entrypoint-initaws.d&lt;/span&gt; &lt;span class="c1"&gt;#mapeamento do volume&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora podemos criar a pasta &lt;code&gt;init-scripts&lt;/code&gt; na raiz do projeto e um arquivo shell script &lt;code&gt;startup.sh&lt;/code&gt; dentro dela com alguns comandos por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'creating test bucket'&lt;/span&gt;
awslocal s3api create-bucket &lt;span class="nt"&gt;--bucket&lt;/span&gt; test-bucket

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'listing buckets'&lt;/span&gt;
awslocal s3api list-buckets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A estrutura do projeto deverá ficar da seguinte maneira:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; .
├── docker-compose.yaml
└── init-scripts
    └── startup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se executar o comando &lt;code&gt;docker-compose up&lt;/code&gt; após o startup inicial você verá as mensagens do script sendo executado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1627764858619%2F9HfzKFprK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1627764858619%2F9HfzKFprK.png" alt="Screen Shot 2021-07-31 at 17.53.36.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O conteúdo deste post está disponível no github no endereço: &lt;a href="https://github.com/hebertrfreitas/localstack-docker" rel="noopener noreferrer"&gt;https://github.com/hebertrfreitas/localstack-docker&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>aws</category>
      <category>localstack</category>
      <category>braziliandevs</category>
    </item>
  </channel>
</rss>
