<?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>Forem: Pedro Kiefer</title>
    <description>The latest articles on Forem by Pedro Kiefer (@pedrokiefer).</description>
    <link>https://forem.com/pedrokiefer</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%2F719807%2F51dd0190-1c40-4071-873e-1de9cffc2883.png</url>
      <title>Forem: Pedro Kiefer</title>
      <link>https://forem.com/pedrokiefer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pedrokiefer"/>
    <language>en</language>
    <item>
      <title>Hacking My Own AWS Account: A Tale of Legacy Systems and Modern Solutions</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Tue, 07 Jan 2025 21:00:03 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/hacking-my-own-aws-account-a-tale-of-legacy-systems-and-modern-solutions-7fc</link>
      <guid>https://forem.com/pedrokiefer/hacking-my-own-aws-account-a-tale-of-legacy-systems-and-modern-solutions-7fc</guid>
      <description>&lt;p&gt;In the shadowy world of cloud security, sometimes you have to break things to fix them. That's exactly what happened when our enterprise-grade AWS infrastructure hit a critical authentication wall. The culprit? A legacy SAML provider that was holding our KMS keys hostage.&lt;/p&gt;

&lt;p&gt;The company I work for has navigated the turbulent waters of AWS for nearly a decade. Over the years, we've transitioned from AWS user accounts (a security faux pas) to a sleek SSO solution, which was later migrated and integrated with a new identity provider and SSO solution. The journey wasn't without its challenges, and our engineering and security teams worked tirelessly to remove all user accounts — a story for another day. This week, however, we faced a new conundrum: a team discovered they couldn't use a KMS key critical to some of our data systems.&lt;/p&gt;

&lt;p&gt;Despite the hiccup, our systems continued to hum along, thanks to a role that still had access to &lt;code&gt;kms:GenerateDataKey&lt;/code&gt;, &lt;code&gt;kms:Decrypt&lt;/code&gt;, and &lt;code&gt;kms:Encrypt&lt;/code&gt;. No data was lost, and we could decrypt and use the data. However, the role lacked permission to update the key policy (&lt;code&gt;kms:PutKeyPolicy&lt;/code&gt;), effectively locking us out from assigning other roles to the key. &lt;/p&gt;

&lt;p&gt;In a stroke of luck, we found another role from our previous SSO solution with full permissions on the KMS key. This revelation came only after opening a support ticket with AWS. The role was created by AWS IAM Identity Center, and that comes with the down side that the trust policy cannot be updated. AWS IAM Identity role have the following format: &lt;code&gt;arn:aws:iam::123456789012:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_profilename_somehexdigest&lt;/code&gt; and the trust policy looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:saml-provider/AWSSSO_somehexrandomnumbers_DO_NOT_DELETE"&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;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithSAML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&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;"StringEquals"&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;"SAML:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://signin.aws.amazon.com/saml"&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;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;Here's where it got interesting. The SAML provider was still there, pointing to a digital ghost — a server that no longer existed. Can we update that to another server that we can control? And the answer is yes, we can! We will hijack our own infrastructure by spinning up a new SAML provider. This is exactly the same technique used by black-hat hackers for gaining &lt;a href="https://medium.com/@adan.alvarez/gaining-aws-persistence-by-updating-a-saml-identity-provider-ef57ebdc8db5" rel="noopener noreferrer"&gt;AWS persistence access&lt;/a&gt; on an account. You just need an IAM Role and a SAML Provider. &lt;/p&gt;

&lt;p&gt;After a quick dive into the depths of the internet, we stumbled upon some straightforward, step-by-step guides on leveraging &lt;a href="https://www.keycloak.org/" rel="noopener noreferrer"&gt;Keycloak&lt;/a&gt;, an open-source Identity and Access Management solution. This tool, running locally, would become the SAML provider we desperately needed.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://neuw.medium.com/aws-keycloak-saml-federation-in-2024-fa8eccebc497" rel="noopener noreferrer"&gt;post&lt;/a&gt; laid out all the necessary steps for setting it up. We opted to run Keycloak inside a container, eliminating the need for extensive installation and configuration.&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;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KC_BOOTSTRAP_ADMIN_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KC_BOOTSTRAP_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin quay.io/keycloak/keycloak:26.0.7 start-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To log in to AWS, you need two specific SAML attributes, and it's crucial not to include any extraneous attributes that AWS can't process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session Name&lt;/strong&gt;: This can be any random value, such as the username. The attribute name is &lt;code&gt;https://aws.amazon.com/SAML/Attributes/RoleSessionName&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Role&lt;/strong&gt;: a list of roles that the user can assume. The attribute name is &lt;code&gt;https://aws.amazon.com/SAML/Attributes/Role&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The role must follow this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:iam::&amp;lt;account-number&amp;gt;:role/&amp;lt;role-name&amp;gt;,arn:aws:iam::&amp;lt;account-number&amp;gt;:saml-provider/&amp;lt;provider-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once Keycloak was set up, the next step was updating the IAM Identity Provider to use the generated metadata file. The new Issuer URL was something like &lt;code&gt;http://localhost:8080/realms/aws&lt;/code&gt;, and the SSO Service location was updated to &lt;code&gt;http://localhost:8080/realms/aws/protocol/saml&lt;/code&gt;. Running inside Docker in development mode meant no SSL was configured, which was acceptable for initializing the SAML flow. &lt;/p&gt;

&lt;p&gt;By accessing &lt;code&gt;http://localhost:8080/realms/aws/protocol/saml/clients/keycloak-sso&lt;/code&gt; and authenticating with the user we had previously created, we were seamlessly redirected to the AWS console, assuming the role we had lost access to. With the regained access, it was a straightforward task to update the KMS Key policy, add the new principal, and remove outdated role references from the policy.&lt;/p&gt;

&lt;p&gt;Key takeaways from this experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain a concise list of IAM Identity Providers, including only the necessary SAML providers.&lt;/li&gt;
&lt;li&gt;Restrict access to updating and creating new Identity Providers.&lt;/li&gt;
&lt;li&gt;Monitor changes to SAML providers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The team gained invaluable insights from the experience, understanding the tactics hackers employ and the simplicity of executing certain maneuvers. We are eager to explore further opportunities to ethically test our systems for deeper learning.&lt;/p&gt;

</description>
      <category>whitehat</category>
      <category>aws</category>
      <category>security</category>
      <category>docker</category>
    </item>
    <item>
      <title>To AB or not AB?</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Tue, 05 Nov 2024 13:08:16 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/to-ab-or-not-ab-52ee</link>
      <guid>https://forem.com/pedrokiefer/to-ab-or-not-ab-52ee</guid>
      <description>&lt;p&gt;Outro dia me contaram sobre um teste AB que foi realizado num grande site e não melhorou a usabilidade do site. O teste trocava uma lista de cidades para selecionar uma por uma em uma caixa com autocomplete. As pessoas procuraram tudo menos cidades na caixa e o teste AB falhou. Isso me fez pensar sobre o quanto algumas decisões não fazem sentido de serem tomadas devido a média de quem acessa um produto. &lt;/p&gt;

&lt;p&gt;Otimizar o texto de um botão, para conseguir mais cliques e aumentar as vendas, um ótimo AB, com métricas claras de sucesso e fácil de fazer. Agora decidir se a cor do site vai ser azul, roxa, lilás, vai da preferência de cada um, nunca teremos um consenso. E se tivermos, talvez não seja o que os responsáveis pelo produto queiram. Teremos a média da cor esperada para um determinado tipo de serviço. Mas não queremos que cor, design, arquitetura, arte, código, sejam definidos pela média. Às vezes, precisamos – às vezes, devemos ousar. &lt;/p&gt;

&lt;p&gt;Apesar de todos termos um discurso forte sobre inovação, melhoria contínua, uso de dados para otimizar processos, se ficarmos olhando apenas para o que a média quer (e em processos estatísticos sempre haverá uma média) não faremos nada inovador, será mais do mesmo – o que todos, na média, dizem querer: ser iguais a tantos outros produtos. Ignorar um teste AB que não converteu, mas que sabemos que melhora a vida do usuário, é um jeito de inovarmos, de afirmarmos que queremos aquilo dessa forma.&lt;/p&gt;

&lt;p&gt;Precisamos aprender a interpretar os dados, ler nas entrelinhas deles, decidir o que queremos – e aqui, muitas vezes é uma decisão individual sim,  também pode ser de um pequeno time, mas nunca será um consenso entre a empresa inteira. Tomar decisões é difícil, em qualquer assunto, mas precisamos saber dizer sim para o que vemos valor e não para o que sabemos que não será proveitoso. Algumas pessoas não ficarão satisfeitas com nosso posicionamento e isso faz parte do viver em sociedade, não iremos agradar a todos. &lt;/p&gt;

&lt;p&gt;Vamos continuar com nossos ABs, mas precisamos entender que eles falham e representam a média do nosso público. Diferentes públicos terão diferentes resultados para um mesmo teste. Temos que balancear os testes com nossa  crença intrínseca de que aquilo irá funcionar melhor. Ousar para inovar.&lt;/p&gt;




&lt;h6&gt;
  
  
  Imagem de capa gerada por IA.
&lt;/h6&gt;

</description>
      <category>product</category>
      <category>mab</category>
      <category>inovation</category>
    </item>
    <item>
      <title>The Internet</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Sun, 28 Apr 2024 16:49:09 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/the-internet-5dg0</link>
      <guid>https://forem.com/pedrokiefer/the-internet-5dg0</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0lpf1fnvfgem9geta77.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0lpf1fnvfgem9geta77.jpeg" alt="The IT Crowd (FremantleMedia, 2008)" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No meu trabalho temos um programa de recompensas para pessoas que encontram problemas de segurança nos nossos sistemas. Isso é bom para a empresa porque em geral recebemos bons relatórios com os passos para reproduzirmos e a partir daí mantemos uma relação com o pesquisador até conseguirmos sanar o problema apontado. É um ótimo programa, tanto para melhorarmos os produtos da empresa quanto para aumentarmos o conhecimento dos diversos times sobre segurança — afinal, escalar segurança não é uma tarefa simples. Mas hoje não vamos falar de segurança, vamos falar sobre internet. E por que essa introdução? Bom, às vezes recebemos alguns relatórios que não são problemas de segurança, é simplesmente algo que faz parte de como a internet funciona. Como alguém confunde o funcionamento da internet com segurança? A tecnologia da informação tem tantas áreas e requer tanto conhecimento que é impossível alguém saber tudo, especialmente pessoas que estão começando, ou que mudaram de área.&lt;/p&gt;

&lt;p&gt;A imagem da capa é uma piada da série IT Crowd, no capítulo em que o time de TI diz para a nova gestora, que não sabia nada de TI, que a internet era aquela caixa preta com uma luz vermelha piscando. Se algo acontecer com a caixa será uma catástrofe mundial. A série é ótima para quem é da área, rende muitas risadas. Se alguém pode acreditar que uma caixa que pisca é a internet então tentar entender melhor como a internet funciona pode ser um conhecimento útil. &lt;/p&gt;

&lt;p&gt;O relato que me fez escrever este texto falava sobre um problema do nosso servidor contatar o servidor do cliente depois de fazer a inscrição no site. No fluxo de inscrição, logo após o cadastro de uma nova conta, enviamos um email de confirmação da criação dessa conta. Então essa conexão com o servidor deles faz parte e não é um problema de segurança. Para ser um problema de segurança seria algo mais na linha de acessar alguma página usando dados que foram enviados pelo cliente, permitindo um atacante controlar esse fluxo — mas isso não é assunto para esse artigo.&lt;/p&gt;

&lt;p&gt;A internet é uma grande rede com diversos tipos de equipamentos, computadores, servidores, dispositivos móveis, etc. Todos falam a mesma língua entre si: IP (Internet Protocol!) — ok, não é uma língua, é um protocolo de comunicação, mas fiquem com a poesia de pensar que as máquinas conversam entre si. Como o IP exige que se decore diversos números para chegar a um determinado servidor e nós humanos não gostamos de decorar números (alguns sim, mas não é a regra), foi inventado outro sistema para nos ajudar nisso e podermos usar nomes: DNS (Domain Name System). &lt;/p&gt;

&lt;p&gt;DNS é o sistema que nos permite decorar “google.com” ou “nytimes.com” ou “g1.globo.com” e não nos preocuparmos em saber que google pode ser 142.251.132.46 ou 142.251.129.142 ou … — sistemas grandes como o google podem responder por diversos IPs diferentes, impossível de decorar. O DNS resolve esse problema muito bem com diversos servidores espalhados pelo mundo, basta mandar uma pergunta para ele e receber de volta o IP correspondente. São diversos tipos de perguntas possíveis, uma delas é para descobrir qual o servidor de email de um determinado domínio. Sabendo qual o endereço do servidor de email podemos usar um outro protocolo em cima de IP para mandar uma mensagem — nossa confirmação da criação da conta.&lt;/p&gt;

&lt;p&gt;O que um sistema de envio de email faz é basicamente descobrir qual o IP do servidor de destino, perguntando para o DNS essa informação, e depois é feita uma conexão entre os servidores e o email é transferido para o servidor de destino. Falando assim parece super simples e realmente o conceito é simples — depois foram adicionadas camadas de segurança, criptografia, assinaturas para garantir que só um servidor possa enviar email para um dado domínio, etc.&lt;/p&gt;

&lt;p&gt;Outros sistemas que formam a internet são muito similares quando focamos no essencial, como por exemplo como a Web (http) funciona. O que acontece quando abrimos um navegador e digitamos &lt;a href="http://www.amazon.com"&gt;www.amazon.com&lt;/a&gt;? Inclusive essa é uma pergunta muito usada em entrevistas, dá para responder de muitas formas, cada pessoa vai acabar focando no que sabe mais. Um desenvolvedor de navegador pode falar sobre como conexões são abertas, memórias que são alocadas, interações com o sistema operacional, bibliotecas para renderizar, etc. Um engenheiro de redes pode falar sobre roteadores, como as rotas são distribuídas e quão dinâmica uma rede é. Alguém que trabalhe com frontend pode falar sobre como o navegador exibe a página, como o estilo da página é interpretado, como os scripts são executados. Mas respondendo a pergunta de forma muito concisa e abstraindo que vivemos cercados de estilo, scripts e imagens: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O navegador descobre para qual IP o endereço digitado aponta. (Olha o DNS aparecendo aqui). &lt;/li&gt;
&lt;li&gt;O navegador abre uma conexão para o IP na porta 80 (sim, estou ignorando criptografia).&lt;/li&gt;
&lt;li&gt;Navegador manda alguns dados para o servidor (página, identificação do navegador, versão do protocolo, etc)&lt;/li&gt;
&lt;li&gt;Servidor verifica o que foi recebido e procura se o arquivo existe. Se existir, manda o arquivo de volta. Caso contrário retorna erro.&lt;/li&gt;
&lt;li&gt;Navegador recebe o arquivo e exibe na tela.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simples, não? Claro que para realmente funcionar temos um mundo de outras coisas acontecendo. Milhares de detalhes estão escondidos e são, em geral, irrelevantes para a maioria das pessoas. Dependendo da curiosidade ou da necessidade do trabalho podemos explorar cada uma das partes. Mas é necessário termos os conceitos básicos de forma clara, pois a partir deles conseguimos entender o resto ou até mesmo criar novas tecnologias.&lt;/p&gt;

&lt;p&gt;A internet é formada por várias caixinhas pretas que piscam, mas não é uma única que controla tudo. Pode-se dizer que os conceitos fundamentais são simples. A soma de muitos conceitos simples é que tornam o todo extremamente complexo. Isolando e removendo as partes não essenciais conseguimos entender muito melhor como qualquer coisa funciona, não só a internet.&lt;/p&gt;

</description>
      <category>internet</category>
      <category>fundamentos</category>
    </item>
    <item>
      <title>Custos nas Nuvens</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Mon, 24 Apr 2023 20:01:45 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/custos-nas-nuvens-453l</link>
      <guid>https://forem.com/pedrokiefer/custos-nas-nuvens-453l</guid>
      <description>&lt;p&gt;Como membro do time de segurança, parte do meu trabalho é criar e manter regras de conformidade na nuvem, que garantem um padrão mínimo de qualidade e segurança para os diversos serviços que uma nuvem oferece. Obviamente as regras não saem de graça e são um custo que os times de segurança acabam arcando com – entretanto essa é a forma mais fácil de escalar boas práticas de segurança no ambiente. Uma das formas de implementar essas regras é utilizando o serviço AWS Config, um serviço da AWS capaz de verificar, auditar e avaliar configurações e relacionamentos entre recursos na nuvem. ​​Com o AWS Config pagamos por evento de configuração gerado e também pela execução das regras.&lt;/p&gt;

&lt;p&gt;Outro dia, enquanto olhava os custos das contas, notei que o AWS Config era o segundo maior gasto de uma conta, logo abaixo dos custos de EC2 – o normal é encontrar custos de rede, EBS, S3, RDS nessa posição. Algo não estava certo e fui investigar o que estava causando esse comportamento, afinal uma conta de testes não deveria gastar mais em AWS Config do que uma conta de produção. &lt;/p&gt;

&lt;p&gt;Depois de encontrar alguns textos sobre o assunto e alguns exemplos de query para utilizar no AWS Athena, comecei a investigação. O primeiro achado foi que gerávamos entre 28 e 36 mil itens de configuração por dia, em um ambiente muito pequeno e com baixo nível de mudanças – as maiores mudanças vêm da utilização de um agendador para ligar e desligar o sistema durante as horas de trabalho, que gera uma ótima economia de EC2 e RDS.&lt;/p&gt;

&lt;p&gt;Escolhi um dia qualquer e olhei o agregado de eventos agrupados por tipo de evento que foi gerado. Outro espanto: o primeiro colocado eram alterações de subnet! O que estava alterando a rede tantas vezes em um dia? Na sequência tinham alterações em interfaces de rede e grupos de segurança, o que faz sentido se o primeiro colocado for alterações de subnet. Mais algumas conversas com colegas, outras queries rodadas e identificamos que a maioria das alterações vinham de uma subnet só. Para nossa sorte, essa subnet só tinha 12 EC2s rodando, o que permitiria até uma exploração manual olhando cada uma das 12 máquinas, mas não foi necessário. Uma das EC2s tinha o mesmo nome de um dos grupos de segurança que aparecia no topo da lista de eventos gerados por dia – uma ótima correlação!&lt;/p&gt;

&lt;p&gt;Investigando a EC2 logo chegamos a conclusão que ela fazia parte de um grupo de autoscale, e olhando os eventos deste grupo vimos que havia uma falha sistemática na verificação da saúde da instância criada. Resumindo, o grupo de autoscale estava criando uma instância, tentava verificar a saúde, falhava, destruía a instância, criava uma nova… eternamente preso num ciclo de falhas. Em um ambiente sem AWS Config habilitado isso teria um custo mínimo associado ao valor da EC2, de um balanceador de carga ocioso e de armazenamento não utilizado. Mas, quando habilitamos o Config passou a gerar diversos itens de configuração, quase um ataque de amplificação de custos. Cada ligar / desligar de uma máquina, criar / destruir interfaces de rede, vincular / desvincular grupos de segurança, etc. geraram novos itens de configuração.&lt;/p&gt;

&lt;p&gt;Com esse achado, resolvemos investigar os outros grupos de autoscale para ver se existia o mesmo comportamento errôneo e encontramos mais alguns. Fazendo os ajustes necessários, seja desligando o sistema que claramente não estava em uso pois falhava sempre ou resolvendo a saúde do sistema, o custo da conta caiu rapidamente. &lt;/p&gt;

&lt;p&gt;Se os custos estão altos, ou fora de um padrão esperado, invista um tempo na investigação da causa, converse com os times que utilizam a conta, proponha novas arquiteturas e veja quais trocas são possíveis em cada sistema. Gerir custos na nuvem não é simples, requer um trabalho constante de todos – e não somente do time de FinOps. Não deixe os custos da nuvem ficarem nas nuvens. &lt;/p&gt;

</description>
      <category>cloud</category>
      <category>costs</category>
      <category>finops</category>
      <category>devops</category>
    </item>
    <item>
      <title>Mergulhos profundos ou investigando sistemas</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Tue, 10 Jan 2023 17:29:28 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/mergulhos-profundos-ou-investigando-sistemas-314m</link>
      <guid>https://forem.com/pedrokiefer/mergulhos-profundos-ou-investigando-sistemas-314m</guid>
      <description>&lt;p&gt;Eu sempre gostei de investigar coisas, qualquer coisa. Quando criança queria saber como os brinquedos funcionavam, desmontava e montava os carrinhos, bicicletas, aparelhos de som, computadores... Vivia tentando entender as partes mecânicas, depois os circuitos e eventualmente os softwares. Cresci com isso, algo muito pessoal e sempre uma grande diversão para mim. Nem todas as pessoas curtem, inclusive talvez a exceção sejam as pessoas que gostam de entender tudo, então não se sinta culpado ou mal por não gostar ou não querer fazer esse tipo de investigação; chame a pessoa que você conhece e sabe que gosta disso para te ajudar, vai ser um prazer para ela. Pra mim, fazer esse tipo de trabalho é tão natural e divertido que às vezes esqueço que também é trabalho!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iMhh2pDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6ip14pyozd0dbe5bsvk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iMhh2pDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6ip14pyozd0dbe5bsvk.png" alt="Mergulhos profundos" width="662" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toda essa introdução para chegarmos no assunto que quero contar: uma investigação para otimizar custos na AWS. Mas não é só sobre isso, é uma tentativa minha de mostrar que investigar coisas não é linear e não tem fórmula mágica. Bom, vamos ao causo!&lt;/p&gt;

&lt;p&gt;Temos uma conta na AWS que rodam alguns sistemas e o custo de NAT Gateway estava um tanto elevado. NAT Gateway é a peça mágica que traduz as conexões que vem de uma rede privada para uma rede pública (ie. a internet). Toda a rede privada tem isso, na sua casa é o roteador que faz esse papel, na AWS temos o NAT Gateway como serviço gerenciado, pagamos e a AWS resolve os problemas de escala, mantém o sistema atualizado, etc — também dá pra fazer direto numa EC2 mas não é o objetivo aqui. Outra peça legal que a AWS fornece são os VPC Endpoints, que permite conectar serviços de múltiplas contas sem integrar a rede ou conectar diretamente com serviços da AWS (que não deixam de ser outras contas, só que gerenciadas pela própria AWS).&lt;/p&gt;

&lt;p&gt;A rede em questão só tinha NAT Gateway e nenhum VPC Endpoint, então todos os serviços da AWS que estavam em uso passavam pelo gateway para conseguir chegar na internet e no serviço. Funciona, mas tem um custo elevado perto do custo de um VPC Endpoint (0.045 usd / GB&lt;sup id="fnref1"&gt;1&lt;/sup&gt; contra 0.01 usd / GB&lt;sup id="fnref2"&gt;2&lt;/sup&gt; do VPC Endpoint). Investiguei rapidamente quais os serviços estavam em uso na conta: SQS, S3, SNS, EC2... talvez os serviços mais comuns da AWS. Criei o VPC Endpoint para o SQS e magicamente o tráfego da NAT Gateway despencou — bateu aquele momento de pânico, será que fiz algo tão errado assim? Mas não, foi só olhar as métricas do endpoint que o tráfego estava todo lá, ufa!&lt;/p&gt;

&lt;p&gt;Só isso já deu uma ótima economia, mas não tava feliz e tinha sido fácil demais, zero aprendizados. Resolvi investigar um pouco mais, pra isso fui atrás de outro serviço da AWS: VPC Flow Logs. Habilitando isso dentro de um VPC temos acesso a todas as conexões que existem na rede, de onde elas surgem, para onde vão, quando começaram, quantos bytes trafegaram em cada conexão. Uma ferramenta ótima para investigar redes, porém pode ter um custo elevado dependendo da rede. Para evitar surpresas liguei o serviço, coletei dados por alguns minutos e desliguei. Cada arquivo gerado tinha cerca de 10Mb, compactados, com algo em torno de 1 milhão de entradas. Hora de trazer a ferramenta de análise de dados (que sempre esqueço como usar, obrigado Google por me salvar): pandas.&lt;/p&gt;

&lt;p&gt;Primeiro passo era carregar um tanto dos dados e olhar o formato deles, temos várias colunas: IP de origem, IP de destino, porta de origem, instância, serviço da AWS, IP de origem do pacote. Todas as informações necessárias para identificar os fluxos de dados. A documentação da AWS tem alguns exemplos de fluxos e seus significados: &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-records-examples.html"&gt;https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-records-examples.html&lt;/a&gt;, com isso já sabia qual seria o primeiro passo: como não tinha interesse em saber de onde tinha vindo o pacote, só que ele tinha saído de um NAT Gateway e estava indo para a internet, então filtrei os dados com o IP interno do gateway. Agora fiquei com bem menos linhas para trabalhar, mas ainda assim um volume grande para olhar manualmente.&lt;/p&gt;

&lt;p&gt;Agrupei os IPs de destino, filtrei alguns que eu já conhecia e não tinha interesse. Mas ainda estava com um monte de IPs que não diziam muita coisa para mim. Duas ferramentas vieram a mente: &lt;code&gt;curl&lt;/code&gt; e &lt;code&gt;host&lt;/code&gt;. A primeira ferramenta faz chamadas HTTP e a segunda resolve DNS e DNS Reverso. Tentei a segunda ferramente primeiro e não tive muito sucesso, só indicava que era uma máquina da AWS (isso eu já sabia, AWS é dona do bloco &lt;code&gt;3.128.0.0/9&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host 3.239.232.234
234.232.239.3.in-addr.arpa domain name pointer ec2-3-239-232-234.compute-1.amazonaws.com.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso não me ajuda a descobrir qual o sistema, então vamos para o &lt;code&gt;curl&lt;/code&gt;. Mas como HTTP vai me ajudar a identificar um IP? Bom, estamos em 2023, maioria dos sistemas tem um certificado e trabalham com HTTPS. No certicado sempre temos o nome comum que aquele sistema responde.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v https://3.239.232.234
...
* Server certificate:
*  subject: CN=queue.amazonaws.com
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ótimo, agora eu sei que o IP &lt;code&gt;3.239.232.234&lt;/code&gt; é o SQS. Opa! Como assim? Eu criei um VPC Endpoint para ele, não deveria passar mais pelo NAT Gateway. Fiz algo errado? o que está acontecendo? Muitas perguntas... mas tinha mais serviços para identificar e usar &lt;code&gt;host&lt;/code&gt; e &lt;code&gt;curl&lt;/code&gt; manualmente não escala. Hora de escrever um código&lt;sup id="fnref3"&gt;3&lt;/sup&gt; que imite o funcionamento das ferramentas em python para passar a coluna do DataFrame e deixar trabalhando. Algumas tentativas depois tenho o que eu preciso, rodo em cima dos IPs de destino de tabela, agrupo pelo nome encontrado e tá lá em primeiro lugar o SQS, mas também tem outros serviços da AWS. Vejo quais fazem sentido adicionar VPC Endpoints, crio eles e vou ser feliz na próxima tarefa? Não consigo, preciso saber por que o SQS continua passando no Gateway. Antes disso dou uma olhada nos gráficos e os VPC Endpoints novos fizeram sentido e vão realmente diminuir os custos.&lt;/p&gt;

&lt;p&gt;Posso voltar a tentar entender o que aconteceu com o SQS. Olho o nome &lt;code&gt;queue.amazonaws.com&lt;/code&gt; e vejo que é diferente do &lt;code&gt;sqs.&amp;lt;region&amp;gt;.amazonaws.com&lt;/code&gt; que estou acostumado a ver, olho a documentação da AWS: &lt;a href="https://docs.aws.amazon.com/general/latest/gr/sqs-service.html"&gt;https://docs.aws.amazon.com/general/latest/gr/sqs-service.html&lt;/a&gt; e entendo o problema, temos aplicações usando o endereço legado do serviço. Uma chuva de perguntas passa pela minha cabeça: Quais aplicações? Onde elas estão? Quais linguagens? Será que são sistemas legados que queremos desligar?&lt;/p&gt;

&lt;p&gt;Primeiro passo é investigar se temos algum repositório com código chamando direto esse endereço. Rápida pesquisa no sistema de versionamento e nada relevante, então hora de fazer uma análise mais profunda. Enquanto levantava a relação de serviços acessados já tinha gerado uma lista de nome e IPs, então só filtrei a lista e obtive todos os IPs que atendem &lt;code&gt;queue.amazonaws.com&lt;/code&gt;; sim, são vários, é um serviço da AWS com altíssima disponibilidade. Agora posso filtrar os flows procurando os endereços de origem que chegam em algum dos IPs dessa lista, o que resultou numa pequena lista de IPs internos — algo em torno de 25 endereços.&lt;/p&gt;

&lt;p&gt;Filtro as instâncias EC2 que temos rodando com essa lista de IPs internos, e surpresa, são todos nós de um cluster kubernetes. "É, não vai ser tão fácil encontrar a aplicação" penso eu enquanto procuro como listar os pods de um dado nó do cluster. Mais uma linha de comando gigante e obtenho a lista de aplicações e são muitas! Tento alguns filtros na linha de comando mesmo &lt;code&gt;cat | cut | sort | unique -c&lt;/code&gt;, vejo alguns sistemas que rodam em todos os nós: coisas padrão do kube, umas duas aplicações grandes e uma lista enorme de aplicações pequenas. Vou atrás do código fonte das maiores aplicações e nada fora do comum e nem usam SQS! Vou seguindo a lista, mas sem muita esperança de encontrar uma aplicação só. Um padrão começa a surgir: muitas aplicações são escritas em python, mas isso não me diz muito ainda. Nesse momento, quase desisto da investigação, versão do &lt;code&gt;boto3&lt;/code&gt; (biblioteca python para acesso aos serviços da AWS) era relativamente nova em todos os projetos; nada com cara de sistemas legados e sem manutenção; parecia um beco sem saída, então voltei atrás mas retive algumas informações: "python", "endereços legados SQS".&lt;/p&gt;

&lt;p&gt;Peguei as poucas informações que tinha e voltei pro Google, mesmo sem esperanças de achar algo. Eis que encontro uma issue no Github do &lt;code&gt;botocore&lt;/code&gt; (biblioteca que faz o grosso das coisas para a &lt;code&gt;boto3&lt;/code&gt;): &lt;a href="https://github.com/boto/botocore/issues/1418"&gt;https://github.com/boto/botocore/issues/1418&lt;/a&gt; e tá ali o problema, reportado em 2018! A biblioteca gera e usa os endereços antigos por causa de alguma incompatibilidade do python 2.6, que está há muitos anos descontinuado. Cheguei na solução, não tem solução! Mas não fiquei feliz com isso e fui olhar issues relacionadas, algumas duplicadas, outras com mais informações e encontrei outra: &lt;a href="https://github.com/boto/botocore/issues/2705"&gt;https://github.com/boto/botocore/issues/2705&lt;/a&gt; essa tinha um pull request de 1º de novembro e falava que o problema estava resolvido na &lt;code&gt;botocore &amp;gt;= 1.29.0&lt;/code&gt;. Vou direto pro terminal, instalo a biblioteca e testo. Problema resolvido! Provavelmente eu não precisava ter olhado para todas as aplicações que estavam rodando — mesmo que por amostragem como eu fiz, uma pesquisa mais certeira poderia ter resolvido meu problema de forma mais rápida. Mas como eu poderia ser mais certeiro se não sabia o que estava procurando?&lt;/p&gt;

&lt;p&gt;Depois de tantas idas e vindas, tudo o que me restava fazer era comunicar os times que as bibliotecas &lt;code&gt;botocore&lt;/code&gt; e &lt;code&gt;boto3&lt;/code&gt; deveriam ser atualizadas para a última versão e que isso ajudaria na redução de custos. Foi uma grande montanha russa, cheia de voltas, mas no final deu tudo certo. Em outros casos simplesmente não conseguimos achar a causa, seja por falta de tempo (ie. a causa raiz não vale o esforço da investigação) ou por falta de conhecimento do problema que estamos lidando. Sempre peça ajuda! Um outro par de olhos (ou orelhas) ajudam muito. Às vezes só de explicar em qual parede chegamos e como chegamos lá já nos ajuda a pensarmos em outras soluções para o problema.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/vpc/pricing/"&gt;https://aws.amazon.com/vpc/pricing/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/privatelink/pricing/"&gt;https://aws.amazon.com/privatelink/pricing/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Parte do código python utilizado na análise &lt;a href="https://gist.github.com/pedrokiefer/3e8f4103f1094de6018256e0088cf8d8"&gt;https://gist.github.com/pedrokiefer/3e8f4103f1094de6018256e0088cf8d8&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>programming</category>
      <category>debugging</category>
      <category>infrastructure</category>
      <category>aws</category>
    </item>
    <item>
      <title>Logging, o quê, quando e como?</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Fri, 18 Mar 2022 20:32:31 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/logging-o-que-quando-e-como-13lh</link>
      <guid>https://forem.com/pedrokiefer/logging-o-que-quando-e-como-13lh</guid>
      <description>&lt;p&gt;"Vou logar tudo, ligar debug em produção" - quem nunca teve vontade de fazer isso? ou que já fez isso? Eu já fiz, me arrependi; me arrependi porque o sistema ficou muito mais lento, criando impacto nos usuários finais e o volume de logs foi tão grande que ficou impossível achar o que eu queria. Durante o desenvolvimento muitas vezes criamos diversas mensagens de log, elas ajudam muito a entender como o sistema está funcionando. Mas todos esses logs quando em produção fazem pouco sentido e só geram pressão no sistema de logs. Precisamos de mensagens de log que façam sentido numa investigação, por exemplo um stacktrace faz sentido ser logado, mas todos os If's que tomamos no meio do caminho não fazem.&lt;/p&gt;

&lt;p&gt;Os logs tem que fazer sentido para o negócio também. Alguns logs podem ser em função de uma exigência legal. Outros para auditoria interna. Logue coisas que não façam sentido ter uma métrica, mas que são importantes para entender o que aconteceu na aplicação. Lembre-se: logs não substituem métricas, podem até auxiliar em alguns momentos quando estamos voando às cegas, mas adicione as métricas necessárias assim que possível.&lt;/p&gt;

&lt;p&gt;Com leis em vigor como LGPD (Lei Geral de Proteção de Dados) no Brasil e GDPR (General Data Protection Regulation) na Europa, precisamos ter um cuidado extra com os dados que são logados. Um dos artigos da lei menciona o fato de um usuário poder pedir a remoção de seus dados pessoais dos sistemas que a empresa gerencia. Caso tenhamos logado esses dados, vamos ter que percorrer um enorme volume de logs para encontrar e remover os dados pessoais. &lt;strong&gt;Em geral, logs não devem ser alterados depois de escritos por requisitos jurídicos, portanto temos um grande problema do ponto de vista legal caso precisarmos alterar algum valor neles.&lt;/strong&gt; Evite escrever qualquer dado pessoal em mensagens de log.&lt;/p&gt;

&lt;p&gt;Dependendo da importância dos logs, eles podem (devem) inclusive ficar armazenados em infraestruturas diferentes, com acesso muito restrito, evitando ao máximo que eles sejam alterados.&lt;/p&gt;

&lt;p&gt;Garanta que todos os sistemas adicionem e repassem um header de &lt;strong&gt;Request-Id&lt;/strong&gt;. O seu sistema não trabalha sozinho, existem diversas peças de software envolvidas pare ele funcionar. Com um RequestId passando entre sistemas, fica fácil isolar na busca dos logs onde foi a falha. É um sistema de tracing bastante rudimentar, mas que dá muita informação para o caso de problemas. Se puderes, tente adicionar e &lt;strong&gt;instrumentar&lt;/strong&gt; as aplicações para um sistema de tracing completo. Instrumentar é fundamental.&lt;/p&gt;

&lt;p&gt;E o mais importante de tudo: &lt;strong&gt;SEMPRE&lt;/strong&gt; leia os logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mensagens de log
&lt;/h2&gt;

&lt;p&gt;Outro aspecto importante é como escrever as mensagens de log. Quais informações são pertinentes e qual a ordem que devemos apresentá-las? &lt;a href="https://www.usenix.org/system/files/login/articles/login_summer19_07_legaza.pdf" rel="noopener noreferrer"&gt;Neste trabalho&lt;/a&gt; os autores exploram esses aspectos e propõe o seguinte modelo do que uma mensagem de log deve conter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quando?&lt;/li&gt;
&lt;li&gt;Onde?&lt;/li&gt;
&lt;li&gt;Severidade&lt;/li&gt;
&lt;li&gt;O quê?&lt;/li&gt;
&lt;li&gt;Por quê?&lt;/li&gt;
&lt;li&gt;O quê irá acontecer?&lt;/li&gt;
&lt;li&gt;Outro detalhes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Os três primeiros são metadados sobre o log: data, servidor, arquivo e linha de código, severidade. Os outros trazem informações concretas do que aconteceu. Os autores ainda definem quais campos são necessários dependendo da severidade.&lt;/p&gt;

&lt;p&gt;Não precisamos seguir à risca o que diz o artigo, mas precisamos ter uma noção de que estruturar as mensagens em volta de um padrão torna mais fácil o nosso trabalho. Queremos as informações necessárias para uma boa investigação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formato de logs
&lt;/h2&gt;

&lt;p&gt;Dê preferência para logs estruturados, isto é não logue texto puro, mas algo que o sistema de logging consiga entender e simplificar a busca. Em um log estruturado fica fácil definir campos customizados, dados que são de interesse do time na hora de realizar uma pesquisa. Usando o exemplo abaixo, temos os campos referentes ao comportamento do &lt;code&gt;upstream&lt;/code&gt; claramente definidos no log estruturado. Na versão somente texto não temos todas essas informações.&lt;/p&gt;

&lt;p&gt;Exemplo de log em formato de texto&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;173.234.238.142 - - [04/Oct/2021:17:18:35 +0000] "GET /?param=a HTTP/2.0" 200 5316 "https://mysite.com/?param=a" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36" "2.75"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exemplo de log estruturado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "@timestamp": "2021-10-04T17:18:35+0000",
    "host": "nginx01.mysite.com",
    "remote_addr": "173.234.238.142",
    "vhost": "mysite.com",
    "request_method": "GET",
    "request_uri": "/?param=a",
    "server_protocol": "HTTP/2.0",
    "http_referer": "",
    "status": "200",
    "body_bytes_sent": "5316",
    "request_time": "2.75",
    "upstream_addr": "internal.mysite.cloud",
    "upstream_status": "200",
    "upstream_response_length": "5316",
    "upstream_response_time": "2.73",
    "upstream_cache_status": "MISS",
    "uri": "https://mysite.com/?param=a",
    "http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36",
    "http_x_forwarded_for": "mysite.com"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sistemas de logs
&lt;/h2&gt;

&lt;p&gt;Outro aspecto muito importante de logs é o sistema que faz o gerenciamento deles — faz a coleta, prepara os dados, armazena e disponibiliza para busca. Normalmente só olhamos para nossas aplicações e os logs que elas geram, mas entender o que está do outro lado nos ajuda a escrever logs melhores. Abaixo temos um desenho esquemático de como funcionam a maioria dos sistemas de logs.  Vamos seguir o fluxo de um log, da aplicação até a busca que foi realizada.&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%2F1n0iycluav4scwd9qesi.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%2F1n0iycluav4scwd9qesi.png" alt="Sistema de logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As aplicações geram seus logs durante seu ciclo de vida. Estes logs podem ir diretamente para um sistema de ingestão ou podem ser escritos na saída padrão. Quando vão para a saída padrão tem mais uma peça no desenho que faz o envio para o sistema de ingestão. O sistema de ingestão vai fazer um pré-processamento dos logs, transformar texto em json e possivelmente ajustar a data de recebimento.&lt;/p&gt;

&lt;p&gt;Para sistemas que geram uma quantidade massiva de logs, normalmente, temos uma fila, que serve de armazenamento temporário e não deixa saturar o sistema de indexação, evitando perdas de logs. Aqui podemos ver qual o impacto que uma aplicação gerando uma quantidade excessiva de logs pode ter em outras aplicações. Se tivermos uma aplicação com muitos logs e outra com poucos logs, a fila irá encher e ambas demorarão para chegar na indexação. Por isso o conselho de gerar uma quantidade menor de logs quando em produção.&lt;/p&gt;

&lt;p&gt;Do outro lado da fila, temos um sistema de indexação que fará o processamento necessário no log para adicioná-lo a um índice. É nesse momento que um campo de data é reconhecido como tal e que todos os outros campos ganham significado&lt;sup id="fnref1"&gt;1&lt;/sup&gt; para que possamos realizar buscas. O log agora está dentro de um conjunto enorme de outros logs, todos devidamente identificados e preparados para pesquisas.&lt;/p&gt;

&lt;p&gt;A pessoa desenvolvedora normalmente utiliza uma interface gráfica para fazer as buscas, limitando os logs para serem de uma só aplicação, no espaço de tempo de interesse, etc. A maioria dos sistemas têm uma linguagem de busca extremamente poderosa, permitindo acharmos a agulha no meio do palheiro. Esse sistema de query busca os dados que foram previamente indexados e retorna todas as linhas de log relacionadas. A sugestão de repassar um &lt;strong&gt;Request-Id&lt;/strong&gt; entre as aplicações vem justamente para facilitar no momento de busca.&lt;/p&gt;

&lt;p&gt;O principal sistema de logs open-source é a combinação de ElasticSearch, Logstash e Kibana, conhecido comumente por &lt;a href="https://www.elastic.co/what-is/elk-stack" rel="noopener noreferrer"&gt;ELK&lt;/a&gt;. Seu funcionamento é bastante similar ao que acabei de descrever.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Liberdade poética, não estou falando de semântica. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>logging</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Segurança</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Tue, 16 Nov 2021 12:21:32 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/seguranca-4no8</link>
      <guid>https://forem.com/pedrokiefer/seguranca-4no8</guid>
      <description>&lt;p&gt;Segurança é um dos temas mais em voga no momento, especialmente depois dos grandes ataques de ransomware que aconteceram no Brasil e no mundo. Segurança deve permear todos os passos do desenvolvimento de software e não ser uma camada que jogam por cima. Claro, em sistema legados talvez seja a única solução possível e mesmo assim será um cobertor curto — algum pedaço vai ficar desprotegido.&lt;/p&gt;

&lt;p&gt;Quando estamos começando um projeto novo, devemos olhar para a segurança desde o princípio. Algumas escolhas simples podem fazer toda a diferença. Isso é parte do príncipio de shift left, isto é, trazer segurança para dentro dos times e deixar de ser uma responsabilidade de um time só de segurança.&lt;/p&gt;

&lt;p&gt;Todos são responsáveis pela segurança — incluíndo as pessoas não técnicas, ou talvez especialmente elas. Imaginem se a portaria deixa entrar alguém que não deveria? De nada adianta dez camadas de segurança no software se qualquer um entra no prédio e tem acesso a rede local, ou se deixamos as senhas anotadas na mesa. Temos que balancear o nível paranóia com o bom senso. Nenhum dos extremos funciona bem.&lt;/p&gt;

&lt;p&gt;Olhando para o compilado de vulnerabilidades feito pela &lt;a href="https://owasp.org/www-project-top-ten/"&gt;OWASP&lt;/a&gt; temos um ótimo panorama de quais sãos os problemas mais comuns. A maioria é evitável simplesmente se prestarmos atenção e entendermos que vulnerabilidades existem. Por exemplo, ataques de injeção são &lt;a href="https://www.vice.com/en/article/aekzez/the-history-of-sql-injection-the-hack-that-will-never-go-away"&gt;conhecidos desde 1998&lt;/a&gt; e praticamente todas as bibliotecas para acesso de banco de dados possuem funções que evitam esse tipo de ataque. Basta uma simples escolha para resolver uma classe inteira de problemas. Outros são um pouco mais complexos, mas nada de outro planeta. Com a ajuda de uma equipe de segurança qualquer time está apto a sanar todos.&lt;/p&gt;

&lt;p&gt;Existem diversas ferramentas que prometem milagres, fazem análise de código, análise em tempo de execução, filtram requisições maliciosas, etc., mas o mais importante é ter uma cultura que entenda que segurança vem primeiro e faz parte da qualidade do software entregue. As ferramentas podem auxiliar, indicando onde devemos olhar, mas são só ferramentas. Um time com conhecimento vai sempre se sair melhor que ferramentas — especialmente se as ferramentas não forem totalmente compreendidas pelo time. Criar uma cultura é bem mais demorado, envolve treinamentos e participação de todos, mas os benefícios são incrivelmente maiores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Código e Linguagens
&lt;/h2&gt;

&lt;p&gt;Quando olhamos para segurança sob a ótica do código precisamos ter em mente que aquele software deve atender algum usuário. Se criarmos algo super seguro, mas de difícil uso, poucas pessoas irão usar ou encontrarão formas de burlar a burocracia da segurança — especialmente se forem desenvolvedores. Então é necessário encontrar um meio termo entre segurança e uso. Aceitar que o risco sempre existirá, mas estamos minimizando ele ao máximo. Todo o software é inseguro por natureza. O único software totalmente seguro é aquele que ainda não foi escrito — depois de escrito ele será inseguro, ou rodará num ambiente inseguro. Encontre o balanço de segurança / utilidade ideal para o contexto do negócio.&lt;/p&gt;

&lt;p&gt;Nunca é demais repetir, mas &lt;strong&gt;valide&lt;/strong&gt; todas as entradas — inclusive aqueles headers HTTP que parecem inofensivos.&lt;/p&gt;

&lt;p&gt;Não deixe chaves de acesso, senhas, ou qualquer dado sensível no meio do código fonte ou configuração. Gerencie as senhas com um sistema próprio para isso. Tenha cuidado redobrado se o projeto for público, uma senha vazada pode ser a porta de entrada para diversos tipos de ataques.&lt;/p&gt;

&lt;p&gt;As linguagens de programação evoluíram muito nos últimos 20 anos e surgiram algumas linguagens com modelos de memória mais seguros — &lt;a href="https://en.wikipedia.org/wiki/Rust_(programming_language)"&gt;&lt;code&gt;Rust&lt;/code&gt;&lt;/a&gt; sendo o melhor exemplo. Escolher uma linguagem com um modelo de memória seguro dá diversos benefícios e barra muitos tipos de ataques. Um modelo de memória seguro é tão importante que diversos projetos opensource estão reescrevendo partes em Rust para justamente não terem mais problemas com o comportamento indefinido do &lt;code&gt;C&lt;/code&gt; — a biblioteca de criptografia do &lt;a href="https://github.com/pyca/cryptography"&gt;python&lt;/a&gt;, ou mais recentemente o próprio &lt;a href="https://github.com/Rust-for-Linux"&gt;kernel do linux&lt;/a&gt;. &lt;code&gt;Go&lt;/code&gt; adicionou suporte nativo a fuzzing para justamente facilitar testes e aumentar a segurança das aplicações que adotam essa prática — fuzzing consiste em testar diversos tipos de dados para uma entrada qualquer, se o teste falha temos uma potencial falha de segurança ou somente um bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criptografia
&lt;/h2&gt;

&lt;p&gt;Use e abuse de soluções de criptografia, mas não tente inventar a sua. Todos os algoritmos envolvidos na geração de chaves criptográficas fortes são difícies de implementar. Um pequeno erro pode fazer que um gerador de números aleatórios vire uma máquina de repetição, um atacante facilmente criaria uma chave igual. Existem ótimas bibliotecas prontas e seguras para criptografia.&lt;/p&gt;

&lt;p&gt;Use criptografia no trânsito de dados, mesmo que seja entre sistemas internos — a maioria dos protocolos já possuem uma versão com TLS. Garanta que os dados estejam cifrados quando em repouso, todos os sistemas operacionais e banco de dados como serviço suportam.&lt;/p&gt;

&lt;h1&gt;
  
  
  Infraestrutura
&lt;/h1&gt;

&lt;p&gt;Uma das camadas fundamentais para um sistema seguro é a infraestrutura onde rodamos nossos sistemas. Se utilizamos um provedor de nuvem, eles garantem a segurança física dos servidores, mas cabe a nós garantir que o sistema está seguro. Assuma sempre que o ambiente está comprometido — mesmo não estando —, defina o mínimo de privilégios necessários para cada parte da infraestrutura. Não é simples aplicar esse tipo de pensamento em um ambiente já existente, então começe pequeno, garanta que novos sistemas já surjam com os acessos mínimos. Aqui estamos aplicando os princípios de &lt;a href="https://en.wikipedia.org/wiki/Zero_trust_security_model"&gt;Zero Trust&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para quem trabalha mais com frontend, ou mobile, troquem nuvem por navegador ou dispositivo móvel. Os mesmos conceitos se aplicam e alguns casos de uso podem inclusive fazer parte das ferramentas de desenvolvimento.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Debugging; Deu ruim, e agora?</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Fri, 12 Nov 2021 10:20:12 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/debugging-deu-ruim-e-agora-2lhf</link>
      <guid>https://forem.com/pedrokiefer/debugging-deu-ruim-e-agora-2lhf</guid>
      <description>&lt;p&gt;Debugar sistemas distribuídos não é uma tarefa simples. Uma das primeiras ferramentas que precisamos é um modelo mental de como o sistema se comporta (ou como desejamos que ele se comporte). Tente desenhar num esquema simples o fluxo das interações, onde chegam as requisições, quais bancos de dados estão envolvidos, quais subsistemas são consultados. Não precisa ser um modelo extremamente detalhado, mas um que fique fácil identificar as partes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Spoiler&lt;/strong&gt;: Se você está com dificuldades de fazer um modelo, ou se a interações estão ficando extremamente complexas e acopladas, talvez seja um sinal de que está na hora de simplificar o sistema. Repensar subsistemas, agrupá-los quando possível, mantendo domínios de informações similares nesses grupos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X4Ys4XF6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jjuv4nr1jcc8igtvolid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X4Ys4XF6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jjuv4nr1jcc8igtvolid.png" alt="Exemplo de modelo mental" width="513" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No modelo acima conseguimos facilmente ver as partes envolvidas sem entrar nos detalhes do que seriam os subsistemas. Será que temos OpenId Connect para autenticação? Será que é um ES com muitos nós atendendo a busca? Do nosso ponto de vista tanto faz, só precisamos entender que os sistemas existem e que podem ser eventuais pontos de falha — com sorte existem outros times cuidando dessas partes. Com o modelo em mãos, temos que ver se temos métricas que possam indicar falhas em qualquer uma das relações, depois verificamos se há logs interessantes ou, melhor ainda, se temos um sistema de tracing com instrumentação adequada. Agora, com sorte, já temos informações suficientes para fazer qualquer investigação no nosso sistema.&lt;/p&gt;

&lt;p&gt;Começamos olhando as métricas e vendo se algo fugiu do padrão. Por exemplo, se o sistema de busca ficar lento, vamos notar latências aumentando no nosso sistema. Se o sistema de vídeos sair do ar, vamos encontrar erros 500 nas métricas e logs. Será que o nosso sistema saiu do ar por alguma dessas dependências? Se for uma dependência forte, temos que lidar adequadamente, retornar uma informação de erro para nossos usuários. Talvez possamos degradar a qualidade do serviço pelo período que o subsistema ficou fora do ar. Se fizermos isso, precisamos de uma métrica indicando quantas vezes estamos degradando a nossa qualidade — para eventualmente conversar com o time responsável e mostrar o impacto aos usuários.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Métricas&lt;/strong&gt;: leia o &lt;a href="https://dev.to/pedrokiefer/metricas-the-good-the-bad-and-the-ugly-4b7e"&gt;texto de métricas&lt;/a&gt; para uma discussão maior sobre o assunto.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Os logs terão informações mais detalhadas sobre o problema, um stacktrace pode permitir que você ache a linha exata onde o sistema quebrou. Correlacionando os logs de outros subsistemas ajudará a entender qual foi o dado que gerou o erro. Tente isolar ao máximo a fonte causadora do problema, isso vai tornar o processo de correção muito mais fácil pois você já terá um caso de uso para implementar um novo teste!&lt;/p&gt;

&lt;p&gt;Não culpe a infraestrutura antes de verificar os logs e entender o que está acontecendo. Se é um problema de infraestrutura, provavelmente outras aplicações também serão afetadas, não somente a sua. Investigue e aprenda sobre as peças de infraestrutura, pergunte aos colegas sobre as escolhas que foram feitas e proponha outras caso a atual não atenda mais o produto.&lt;/p&gt;

&lt;p&gt;Passada a turbulência do incidente é hora de reunir os times envolvidos e fazer um postmortem. Aqui não queremos só ver causa-efeito, mas queremos uma análise ampla e profunda do que gerou o problema. A partir disso conseguimos traçar ações de melhoria contínua, e não só resolver o problema pontualmente. Tente aplicar a técnica dos cinco porquês. Essa técnica consiste em ficar perguntando repetidas vezes "Por quê?", sempre tentando buscar a causa raiz, que pode não ser um problema de código, mas uma falta de conhecimento do time, ou um problema de comunicação entre times. Nos aprofundando na causa conseguimos crescer como time e evitar que uma classe de problemas similares se repita.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Deploy, release the kraken!</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Tue, 09 Nov 2021 14:33:48 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/deploy-release-the-kraken-1ae9</link>
      <guid>https://forem.com/pedrokiefer/deploy-release-the-kraken-1ae9</guid>
      <description>&lt;p&gt;Chegou a hora de mandar para produção o trabalho dos últimos dias ou do dia! É só jogar para cima do time de operações e deu, né? Claro que não, é hora de vermos e ajustarmos uma esteira de entrega que atenda os requisitos do produto. É um trabalho em conjunto entre operação e desenvolvimento, ambos precisam conhecer sobre o sistema, a infraestrutura, a escalabilidade, o código. Essa troca de informações é essencial para um crescimento escalável do sistema.&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%2Fmh69i2gdz7kkoncd2qsl.gif" 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%2Fmh69i2gdz7kkoncd2qsl.gif" alt="Deploy!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hoje em dia as opções de deploy são praticamente infinitas: kubernetes, diferentes PaaS, máquinas virtuais, docker, infraestrutura serverless, etc. Escolha a que tem melhor custo-benefício para o produto. Evite fazer escolhas da moda ou só por quê você quer aprender algo. Entenda os pontos de falha da infra escolhida – é impossível entender todos, mas saiba que eles existem e que vão eventualmente acontecer. Redes vão se particionar, pacotes vão se perder, DNS vai demorar para atualizar, VMs vão morrer, armazenamento será corrompido, configurações ficarão fora de sincronismo. O software e a esteira de entrega devem levar todos esses pontos em consideração.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Não faça deploys manualmente.&lt;/strong&gt; Entenda manualmente como qualquer prática que dependa do ambiente local de desenvolvimento ou conhecimento que só existe na cabeça do desenvolvedor. Garanta uma esteira de entrega completa que permita reprodutibilidade do build e testes do sistema. Queremos evitar o clássico problema "funciona na minha máquina" então reprodutibilidade é uma peça chave de qualquer esteira de entrega.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estratégias
&lt;/h2&gt;

&lt;p&gt;Discuta e planeje estratégias de entrega do software, levando em conta pelo menos os seguintes pontos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Os modelos de dados são compatíveis?&lt;/li&gt;
&lt;li&gt;Como iremos atuar em caso de problemas?&lt;/li&gt;
&lt;li&gt;É simples voltar para a versão anterior?&lt;/li&gt;
&lt;li&gt;Temos uma linha base das métricas pra conseguir detectar anomalias?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Durante o desenvolvimento, pense nas alterações nos modelos de dados e modificações nas assinaturas de funções. A maioria dos sistemas atualmente são distribuídos e na maioria das estratégias de deploy as instâncias novas começam a trabalhar antes de desligarmos todas as antigas. Será que remover um atributo vai quebrar as units antigas? Se quebrar, como iremos voltar para a versão anterior em caso de falha? Tente manter a retrocompatibilidade dos modelos de dados. No caso de APIs que são consumidas por outros sistemas, não quebre o modelo até que todos consigam atualizar. Dê preferência para criar novas rotas com os modelos novos se a atualização for totalmente incompatível — isso acontece, é normal.&lt;/p&gt;

&lt;p&gt;Defina um plano de ação simples, que todos no time saibam executar, para quando tivermos problemas não gastarmos tempo tentando lembrar aquele comando mágico ou a sequência correta de passos. Se possível, tenha um ambiente que permita exercitar essas ações. Um plano de ação simples é voltar para a versão anterior, basta garantir retrocompatibilidade nas alterações que fizermos ao sistema.&lt;/p&gt;

&lt;p&gt;As métricas do sistema vão definir uma linha base do seu comportamento ao longo do tempo. Se familiarize com isso para entender melhor quando algo foge do padrão. As métricas vão fugir do padrão quando temos algum evento especial, por exemplo Black Friday ou a final de um campeonato. Elas também podem fugir da linha base quando subirmos uma alteração com problema, e às vezes só conseguimos ver um problema quando está em produção com uma carga bem mais alta do que no sistema de teste.&lt;/p&gt;

&lt;p&gt;Existem diversas estratégias que ajudam nos pontos citados: blue-green, canary deployment, feature flags, rolling update, etc. Estude com o time qual faz mais sentido para o sistema. Particularmente, gosto muito de canários e feature flags. Abaixo irei exemplificar algumas dessas estratégias, são apenas esboços sem detalhes de implementação.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blue-Green
&lt;/h3&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%2Fb2oeb8dteunex9hbnybh.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%2Fb2oeb8dteunex9hbnybh.png" alt="Blue-Green"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Uma estratégia simples de deploy é usar Blue-Green, onde criamos em paralelo uma nova infraestrutura completa com a nova versão do sistema. Na figura acima estamos apontando os acessos para o lado green. No próximo deploy iremos recriar o lado blue, daí podemos testar a aplicação utilizando o endereço interno antes de trocarmos o apontamento do endereço principal. Caso tenhamos algum problema, basta voltar o apontamento para o lado green.&lt;/p&gt;

&lt;p&gt;Normalmente as bases de dados são as mesmas para ambos os lados, então tome cuidado com migrações nos modelos de dados.&lt;/p&gt;

&lt;p&gt;Prós:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facilidade de voltar a aplicação&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custo de manter duas infraestruturas em paralelo por um tempo. Em ambientes não cloud isso pode ter um custo considerável.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rolling Update
&lt;/h3&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%2F1u4h5e999khobmvymbei.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%2F1u4h5e999khobmvymbei.png" alt="Rolling Update"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As instâncias da aplicação vão sendo substituídas aos poucos, em um número fixo de instâncias por vez. Somente depois que as novas instâncias estão respondendo corretamente é feita a remoção das instâncias antigas. Na figura temos a instância A como sendo a nova, e a instância 1 marcada para remoção. É a estratégia padrão do &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Prós:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resiliente caso a aplicação falhe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pode ser demorado para fazer um rollback&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Canary Deployment
&lt;/h3&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%2Farflzfr27m5fiw9a94oa.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%2Farflzfr27m5fiw9a94oa.png" alt="Canary Deployment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A ideia por trás de canários é subir uma aplicação que possa ser sacrificada caso tenha problemas. O nome vem justamente dos canários que os mineiros utilizavam no passado para indicar a qualidade do ar. Quando o canário para de cantar é porque o ar não está respirável e os mineiros devem sair daquele túnel. Aqui é a mesma coisa, colocamos uma pequena carga na nova versão da aplicação e olhamos o seu comportamento. Conforme temos confiança que o comportamento está dentro do esperado vamos aumentando a quantidade de instâncias e o volume servido. Em caso de problemas, basta tirar a nova versão do ar. Minimizamos qualquer tempo de indisponibilidade e sensação de problemas por parte dos usuários. Existem ferramentas que automatizam esse processo de deployment. O livro &lt;a href="https://sre.google/workbook/table-of-contents/" rel="noopener noreferrer"&gt;The Site Reliability Workbook&lt;/a&gt; tem um &lt;a href="https://sre.google/workbook/canarying-releases/" rel="noopener noreferrer"&gt;capítulo inteiro&lt;/a&gt; dedicado sobre a prática.&lt;/p&gt;

&lt;p&gt;Para aplicações que processam dados, deve-se usar &lt;a href="https://sre.google/workbook/data-processing/#canarying" rel="noopener noreferrer"&gt;outras formas&lt;/a&gt;, mas o princípio é sempre o mesmo: olhar o comportamento das métricas e os resultados gerados e ir trocando de forma incremental as instâncias.&lt;/p&gt;

&lt;p&gt;Um amigo gosta de dizer que a única forma de deploy deveria ser canários. A grande maioria dos problemas em sistemas poderiam ser evitados se canários fossem utilizados.&lt;/p&gt;

&lt;p&gt;Prós:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fácil de detectar problemas&lt;/li&gt;
&lt;li&gt;Minimiza o impacto com os usuários&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complexidade para criar o ambiente de deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Feature Flags
&lt;/h3&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%2F22p9ayj2y2x76dde94xx.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%2F22p9ayj2y2x76dde94xx.png" alt="Feature Flags"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O princípio por trás de feature flags é permitir ligar ou desligar comportamentos do sistema de forma dinâmica. Podemos utilizar alguma das outras estratégias descritas para atualizar as instâncias, e depois controlamos quando vamos habilitar a funcionalidade. O controle pode ser só ligado ou desligado, ou ser probabilístico — 5% das execuções chamam a funcionalidade nova. Normalmente temos métricas associadas à flag para podermos acompanhar o comportamento novo. Também podemos usar esse sistema para controles dinâmicos, por exemplo: desligar um subsistema durante um momento de pico de acessos.&lt;/p&gt;

&lt;p&gt;Prós:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facilidade de uso&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requer um sistema para gerenciar as flags&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Métricas - the good, the bad, and the ugly</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Sun, 07 Nov 2021 23:52:00 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/metricas-the-good-the-bad-and-the-ugly-4b7e</link>
      <guid>https://forem.com/pedrokiefer/metricas-the-good-the-bad-and-the-ugly-4b7e</guid>
      <description>&lt;p&gt;Métricas são a forma que você consegue observar o estado atual do sistema. Existem infinitas coisas para se medir num sistema, então o difícil é separar o que eu realmente devo medir das coisas que eu posso medir — lembrando que elas são a janela para um início de debug.&lt;/p&gt;

&lt;p&gt;No livro de SRE do Google são citadas 4 métricas básicas (golden signals): latência, trâfego, erros e saturação. Em cada sistema, essas métricas são coisas distintas. O importante é pensar no que importa para os usuários: latência de 20s é ruim para servir uma página, mas pode ser ótima para o processamento de um vídeo.&lt;/p&gt;

&lt;p&gt;Pense no seu sistema e no que importa para ele, meça isso. Adicione outras métricas para facilitar o debug e entender o comportamento: métricas de caches, métricas para acessos a outros sistemas, métricas de banco de dados, métricas de conexões. Evite usar essas métricas específicas para alertas, elas servem para o debug. A saúde do sistema deve ser vista nas quatro principais, crie alertas em cima delas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conheça&lt;/strong&gt; as métricas de sua aplicação: como ela se comporta ao longo do dia; qual o pico de acessos que já serviu; qual o crescimento dos acessos/dos dados. Saber o comportamento padrão permite identificar de forma rápida um problema. Às vezes o desvio da linha base é pequeno, talvez não gere um alarme, mas pode despertar a curiosidade da pessoa que está acostumada a olhar aqueles valores.&lt;/p&gt;

&lt;p&gt;Se você adicionar métricas demais pode acabar sobrecarregando a aplicação e gastar mais tempo de CPU com elas do que com o serviço em si.&lt;/p&gt;

&lt;p&gt;Observabilidade tem que fazer parte da cultura dos times, assim como segurança e qualidade. A thread do tweet abaixo fala justamente sobre isso.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1448373809631817734-213" src="https://platform.twitter.com/embed/Tweet.html?id=1448373809631817734"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1448373809631817734-213');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1448373809631817734&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  The Good
&lt;/h2&gt;

&lt;p&gt;Existem diversos tipos de sistemas, de métricas de aplicação à métricas de comportamento dos usuários. Cada um tem caraterísticas diferentes, volumes de dados diferentes, e muitos usos distintos (às vezes de uma mesma métrica), mas todos os sistemas podem ser classificados em duas grandes categorias: push ou pull. Isto é, alguns sistemas vão até as aplicações buscar métricas (pull), outros recebem a métrica (push).&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%2Fv54j4wz25d08nvagwq8g.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%2Fv54j4wz25d08nvagwq8g.png" alt="Sistemas de métricas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exemplo mais comum de sistema que utiliza pull: Prometheus&lt;br&gt;
Exemplo mais comum de sistema que utiliza push: Newrelic, Datadog&lt;/p&gt;

&lt;p&gt;Um sistema de métricas nada mais é do que um banco de dados com facilidade de gerar agregações temporais, afinal, queremos ver como as coisas se comportam ao longo do tempo. Um modelo mental bastante simples é o da figura abaixo, onde temos várias entradas para cada momento de tempo e dentro desta estrutura estão guardados metadados sobre a métrica (qual a instância, qual a rota, etc) e o valor que foi lido (ou recebido). Com isso podemos fazer agregações como "qual foi a taxa de requisições por minuto da rota '/'" e apresentar um gráfico com esses valores.&lt;br&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%2Fdb5novypbhdfm571j2lx.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%2Fdb5novypbhdfm571j2lx.png" alt="Funcionamento interno"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cardinalidade merece um tópico à parte, mas vou tentar ser breve sobre. Cardinalidade fala da quantidade de variações que uma mesma métrica pode ter. Usando uma requisição http como exemplo podemos ter: rota, status, user agent, headers, hosts, etc. Cada termo pode receber diversos valores, alguns podem receber valores que não controlamos — isso dá margem para ataques de negação de serviço, não da aplicação que recebe as requisições mas do sistema de métricas. Se temos tantas variações, e fazemos a combinação (na real é o produto cartesiano) entre todas as variações possíveis temos uma cardinalidade enorme. Alguns sistemas de métrica não foram planejados para suportar isso. Portanto é necessário sempre saber se o sistema em questão atende aos requisitos de cardinalidade que a sua métrica precisa. Aqui vale resaltar que você também deve pensar se todas as variações são necessárias para a sua aplicação.&lt;/p&gt;

&lt;p&gt;Alertas são essenciais em qualquer sistema de monitoração. É através deles que vamos mobilizar os times para atuarem. Como não queremos mobilizar pessoas por alarmes falsos, temos que entender porque um alerta está gerando falsos positivos, isto é, alarmando quando não deveria. Quando temos muitos falsos positivos temos a tendência de ignorar todos os alertas, pois assumimos que são sempre os mesmos, mas eventualmente será um alerta real que ficará esquecido por mais tempo do que deveria. Mais tempo com problemas, gera impacto com os usuários e definitivamente não queremos que o nosso sistema de alerta vire trending topics no Twitter.&lt;/p&gt;

&lt;p&gt;O time precisa ter conhecimento de como medir e o que medir, afinal são as pessoas mais capazes de entender o que o sistema faz. Um time de observabilidade vai auxiliar mantendo o sistema de métricas e dando dicas de como utilizar as ferramentas. Em um time de frontend é bastante provável que a stack de observabilidade não atenda aos requisitos de volume de métricas e de cardinalidade, mas um sistema de injestão de métricas para Big Data consiga absorver sem problemas o volume necessário. Por isso, a necessidade do time saber o que quer medir e qual a finalidade dessa medição.&lt;/p&gt;

&lt;p&gt;Para quem quiser se aprofundar em alertas recomendo fortemente o livro &lt;a href="https://sre.google/books/" rel="noopener noreferrer"&gt;Site Reliability Engineering&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bad
&lt;/h2&gt;

&lt;p&gt;Métricas geram muitas discussões e observabilidade é um campo de constante melhorias. Ficar atento ao que as empresas grandes estão fazendo e o que surge na comunidade é fundamental. Dito isso, é comum nos depararmos com as seguintes frases e vou dar meus 5 centavos sobre elas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Ah, mas aqui nós usamos o XPTO"&lt;/strong&gt; — troque XPTO por Newrelic, Datadog, Prometheus, etc. As métricas não dependem do sistemas escolhido, entenda o que cada um desses sistemas oferece. Newrelic e Datadog oferecem instrumentação automágica, adicionam vários hooks em várias partes do teu software para conseguir medir. Verifique se as métricas realmente te atendem, não é por ser automágico que você terá as melhores métricas para o sistema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Adotamos o apdex para todas nossas apps"&lt;/strong&gt; se todas as aplicações possuem um volume alto de acesso, ótimo. Apdex vai dar um cheiro do que está acontecendo. Mas aquela aplicação que recebe pouco tráfego vai estar sempre alarmando, ou aquela que demora para responder porque realmente está processando um grande volume de dados. Métricas são dos times e eles que vão saber o que importa, se for para normalizar algum métrica, normalize as quatro básicas e deixe os times definirem seus SLOs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Correlation does not imply causation"&lt;/strong&gt; - Correlação não implica em causalidade. Leve isso para vida! Só por quê você viu uma métrica não infira que ela é a causa. Para fazer essa inferência levante mais dados, outras métricas, logs, etc. com tudo isso, crie uma hipótese da causa e teste! Sim, nós usamos o método científico para entender o que acontece nos sistemas.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ugly ou The good?
&lt;/h2&gt;

&lt;p&gt;Se você gosta de matemática essa parte será &lt;strong&gt;The Good&lt;/strong&gt; novamente, caso contrário provavelmente &lt;strong&gt;The Ugly&lt;/strong&gt;. Mas vou tentar deixar da forma mais simples possível, afinal métricas são pura matemática e estatística. E aqui não quero dizer que é requisito saber muita matemática para trabalhar com TI, mas ter algumas noções básicas ajuda bastante.&lt;/p&gt;

&lt;p&gt;Métricas &lt;strong&gt;precisam&lt;/strong&gt; ser agregáveis, isto é, posso reuni-las dentro de uma janela de tempo e conseguir fazer operações matemáticas. Por exemplo, um contador de chamadas de uma rota HTTP é agregável, podemos somar todos os valores das diversas instâncias dentro de uma janela de tempo e dizer que atendemos 300 req/min.&lt;/p&gt;

&lt;p&gt;Agora imagine que a aplicação entregue uma métrica que é o tempo médio de requisição dos últimos 30s. Dá para agregar essa métrica? Até dá e tu poderias ter uma média do tempo médio entre instâncias, mas não faz sentido tentar agregar o tempo máximo; afinal será o tempo máximo do tempo médio das requisições, não é um valor que tenha utilidade prática. Fazer média de médias é aceitável, inferir outras coisas em cima de médias começa a ser uma zona perigosa. Por isso, para tempos o ideal é utilizarmos histogramas, que são agregáveis e explicam melhor o que acontece com as requisições do que uma média.&lt;/p&gt;

&lt;p&gt;Histogramas nada mais são do que diversos contadores, cada um responsável por um pequeno intervalo de tempo (ou do que você quiser, usei tempo por que é fácil de imaginar). Mas para entender um histograma vamos falar sobre probabilidade e estatística, começando com a curva normal, ou gaussiana, aquela que tem formato de um sino. Vou usar os exemplos como variáveis contínuas, ao invés das discretas que temos nos sistemas de métricas, só para ficar mais fácil de desenhar. Na imagem abaixo vemos a curva normal, com as marcas da média e dos desvios padrão. Ao lado temos alguns outros exemplos de distribuições que lembram uma curva normal — o tipo de análise que precisamos entender se aplica a toda a família de curvas.&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%2F3lpqpokg96t6et9yj7l5.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%2F3lpqpokg96t6et9yj7l5.png" alt="Curva normal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O que precisamos entender como funciona, e levar para todas as métricas de latência, é que se só olharmos para a média provavelmente estamos errando feio. Nenhum sistema se comporta exatamente igual à uma gaussiana, então a média não vai estar ali no centro da figura, mas pode estar mais no início, ou final da curva. Para termos um entendimento comum entre todos, medimos a área que está abaixo da curva em alguns intervalos de interesse como 50% da área, ou 75%, ou 90%, 99%. O ponto onde termina essa área é o valor do percentil 50, percentil 75, percentil 90 e percentil 99.&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%2F6riqmh0tqct3vv1j9pb9.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%2F6riqmh0tqct3vv1j9pb9.png" alt="Percentis"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E o que isso significa para a latência? Significa que quando pegamos a latência 90% de todas as requisições que o sistema recebeu, tivemos uma latência de 100ms. Só 10% dos usuários tiveram uma latência maior, o que é ótimo. Se quisermos ser mais exigentes podemos olhar o P99 onde vemos a latência que levou para atender 99% das requisições, isto é só 1% dos usuários pode ter sofrido com latências maiores do que o valor P99. Evite entrar na paranoia de querer garantir algo para 99,9999% das requisições, os custos para adicionar um 9 a mais vão se tornando proibitivos e não existe chegar a 100%.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Cachear ou não cachear, eis a questão</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Mon, 25 Oct 2021 18:09:23 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/cachear-ou-nao-cachear-eis-a-questao-2084</link>
      <guid>https://forem.com/pedrokiefer/cachear-ou-nao-cachear-eis-a-questao-2084</guid>
      <description>&lt;p&gt;Você realmente precisa de um cache? A operação do sistema é tão demorada assim? Ou o problema são as dependências do sistema? Existem muitas justificativas para se usar um cache e com ele vem diversos benefícios e &lt;em&gt;malefícios&lt;/em&gt;, então antes de dizer "Coloca um cache aí que resolve" analise a situação.&lt;/p&gt;

&lt;p&gt;Benefícios&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entrega rápida para as consultas realizadas (milisegundos)&lt;/li&gt;
&lt;li&gt;Diminui uso de CPU: por que recalcular a mesma coisa se posso servir o que já calculei.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Malefícios&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invalidação de cache&lt;/li&gt;
&lt;li&gt;Invalidação de cache&lt;/li&gt;
&lt;li&gt;Invalidação de cache&lt;/li&gt;
&lt;li&gt;Arquitetura começa a ficar complexa&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sim, eu repeti "Invalidação de cache" três vezes, e poderia repetir mais umas vezes. Invalidar cache é &lt;strong&gt;difícil&lt;/strong&gt;. Se eu invalido muito seguido, o cache deixa de ser efetivo. Se eu não invalido nunca, estou servindo dados ruins. Como isso depende do contexto do sistema não existe receita de bolo pra saber se o cache é bom ou ruim. Então o jeito é medir.&lt;/p&gt;

&lt;p&gt;Se você tem um cache, você &lt;strong&gt;deve&lt;/strong&gt; medir pelo menos a taxa de cache hit e cache miss. Se a taxa de cache miss for maior do que a de hits, então provavelmente esse cache não serve para muita coisa. Se o cache mistura diversos tipos de dados diferentes, meça por tipo de chave e não globalmente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Básico
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nmnfgIQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f45i9zxuuta41twu11zj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nmnfgIQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f45i9zxuuta41twu11zj.png" alt="Cache com alguns elementos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Talvez isso seja básico, mas não custa revisar. Na figura acima temos um cache com poucos objetos guardados. Cada objeto tem uma etiqueta &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt;, e um tempo de vida &lt;code&gt;ttl&lt;/code&gt; — nem todo o cache tem um &lt;code&gt;ttl&lt;/code&gt; associado, por exemplo caches de cpu. Podemos recuperar um objeto de forma rápida utilizando a etiqueta adequada. Conforme a aplicação roda, outros objetos vão sendo adicionados até o momento em que o cache está cheio. E agora? o que e eu faço para adicionar outro objeto? E se o objeto for muito maior do que os outros?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w_HU8N41--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6h7hrmwfjec5dsgzn3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w_HU8N41--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6h7hrmwfjec5dsgzn3m.png" alt="Cache cheio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nesse momento o comportamento do cache vai depender da implementação, existem diversas formas de lidar com esse momento — a Wikipedia lista &lt;a href="https://en.wikipedia.org/wiki/Cache_replacement_policies"&gt;19 políticas&lt;/a&gt; diferentes. Saber como a biblioteca ou sistema de cache vai funcionar nessas situação é extremamente importante, pode ser o diferencial entre um cache sendo bem utilizado de um que não serve para nada.&lt;/p&gt;

&lt;p&gt;Ainda há outras classes de problemas se o cache for distribuído, como o modelo de consistência que o cache utiliza, ou o fato de que duas instâncias da aplicação tentem atualizar uma mesma chave. Não tenho a pretensão de tentar explicar isso, mas é importante saber que esses problemas existem. O livro &lt;a href="https://www.amazon.com.br/Designing-Data-Intensive-Applications-Martin-Kleppmann/dp/1449373321"&gt;Designing Data-Intensive Applications&lt;/a&gt; explica todos esses detalhes de uma forma muito clara. Vale a leitura para os que querem conhecer mais sobre dados em sistemas distribuídos.&lt;/p&gt;

&lt;p&gt;Ter ideia desses conceitos facilita muito na hora de discutirmos estratégias e melhorias nos sistemas. Evitamos que uma melhoria na verdade tenha um impacto negativo por uma má escolha de cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usando caches
&lt;/h2&gt;

&lt;p&gt;Seu time já entendeu as vantagens, desvantagens, diferenças da arquitetura e é hora de usar um cache, mas não conversamos sobre os dados que vamos armazenar lá. Será que existe alguma relação entre dados e cache? Será que o ciclo de vida dos dados tem alguma relação com o cache? Quais os impactos ao produto se servimos um conteúdo antigo?&lt;/p&gt;

&lt;p&gt;Diferentes tipos de dados têm requisitos de caches diferentes. Para dados com um ciclo de vida muito curto não faz sentido colocar um tempo de vida extremamente alto. Agora, se o dado só é alterado poucas vezes na semana, um tempo de vida de um dia pode ser interessante —  caso exista um volume de acesso que justifique isso, obviamente. Aqui temos que levar em conta os requisitos do produto. Em um sistema de bolsa de valores, exibir um conteúdo antigo pode significar uma perda de muito dinheiro. Entender a dinâmica do conteúdo servido é importante, até para definir se é necessário um mecanismo (bom o suficiente) de invalidação de cache.&lt;/p&gt;

&lt;p&gt;Numa página web, com baixa taxa de alterações no HTML servido, você quer cachear o máximo possível nos servidores de borda e nos navegadores dos clientes. Por máximo possível entende-se o maior tempo que não gere impacto negativo ao negócio — seja por uma falha encontrada, ou porque às alterações tem que ser vistas de forma mais rápida. O negócio vai dizer o que fazer. E aqui, obviamente, entram custos: menos cache na borda significam mais requests em todo o backend e mais tráfego nos subsistemas — o impacto pode ser brutal dependendo da arquitetura.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onde fica o cache?
&lt;/h2&gt;

&lt;p&gt;Podemos ter diversos tipos de cache: dentro da aplicação, num sistema separado, ou ainda remoto no browser ou aplicativos dos clientes. Os caches ainda podem ser persistidos ou em memória. São muitas escolhas e muitos sistemas usam mais de um tipo. Mas independente de onde vamos deixar o cache, temos que lembrar de qual será o comportamento do sistema quando o cache estiver zerado, isto é o que acontece quando temos um &lt;em&gt;cold cache&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Uma aplicação que utiliza um cache em memória dentro de cada instância vai perder essas informações quando uma reiniciar, ou fizermos um novo deploy. Será que isso afetará o sistema? Bem provável que sim. Se for um impacto pequeno, dentro do esperado pelo negócio, estamos bem. Caso contrário devemos estudar um outro método, ou rever a necessidade desse cache. Outro aspecto que surge num cache em memória dentro da aplicação é o sincronismo de dados: todas as instâncias possuem o mesmo valor para um determinada chave? Qual a percepção do usuário se apresentarmos diferentes dados? Um cache rodando num sistema separado pode resolver esse problema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Não há bala de prata
&lt;/h2&gt;

&lt;p&gt;Caches não resolvem tudo.&lt;br&gt;
São uma ótima ferramenta, ajudam bastante a melhorar o desempenho do sistema. Te levam até um certo patamar de escalabilidade — que pode estar bem além da sua escala atual. Para ultrapassar essa barreira temos que repensar os sistemas, o modelo de dados, o consumo desses dados, os algoritmos utilizados. Só pense nessa barreira quando a hora chegar, não crie um sistema mais complexo do que a sua escala atual.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Arquitetura Escalável</title>
      <dc:creator>Pedro Kiefer</dc:creator>
      <pubDate>Wed, 20 Oct 2021 14:15:20 +0000</pubDate>
      <link>https://forem.com/pedrokiefer/arquitetura-escalavel-335g</link>
      <guid>https://forem.com/pedrokiefer/arquitetura-escalavel-335g</guid>
      <description>&lt;p&gt;Atualmente todos falam em arquiteturas e software escaláveis: "it webscales!"; mas você realmente precisa de tudo isso no dia a dia? Seu sistema recebe 1M req/s para justificar complexidades e abstrações desnecessárias? Provavelmente a resposta é não. Então começe do básico, garanta ótima qualidade desde o príncipio e se um dia for necessário atender 1M req/s será muito mais fácil refatorar o sistema.&lt;/p&gt;

&lt;p&gt;"Ah, mas eu preciso fazer micro serviços, porque todo mundo faz e isso escala!" Beleza, faça micro serviços mas não faça femto-serviços (minha definição para um serviço que é absurdamente pequeno; femto é 10^-15, enquanto micro é só 10^-6.). Um serviço de processamento de fotos não precisa ser 10 serviços diferentes com 10 filas separadas. Faça um serviço que englobe todo o processamento e escale esse serviço. Fica mais fácil de manter, dá para manter na cabeça todo o sistema, o deploy fica mais simples. "Ah, mas daí é um monólito", não, não é, só é um conjunto mínimo de funcionalidades reunidas em um local.&lt;/p&gt;

&lt;p&gt;Se vocês tem muitos micro serviços para compor uma funcionalidade fica muito díficil coordernar uma atualização no payload usado entre os serviços. Será que todos vão entender a mensagem nova? Será que preciso atualizar tudo ao mesmo tempo? Se isso for necessário, agrupe tudo sob um sistema só.&lt;/p&gt;

&lt;h2&gt;
  
  
  Código
&lt;/h2&gt;

&lt;p&gt;Abstraia e crie interfaces somente do que faz sentido no momento, não gaste tempo e energia criando uma arquitetura mega flexível que nunca será usada. Se o código estiver simples e bem testado fica simples refatorar para adicionar mais possibilidades.&lt;/p&gt;

&lt;p&gt;Algumas linguagens em nome de "arquiteturas enterprise" &lt;del&gt;(algo para C-level achar bacana, eu acho?)&lt;/del&gt; acabam criando diversos padrões de projeto que geram só níveis de indireção e abstrações que são pouco úteis para a entrega de valor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurações
&lt;/h2&gt;

&lt;p&gt;Evite juntar configurações do sistema com regras de negócio. Por exemplo, se você tem um sistema dinâmico para facilitar que outros times desenvolvam serviços em cima, deixe em arquivos separados as configurações que fazem o sistema funcionar e as configurações que são do negócio. O intuito aqui é diminuir os problemas em caso de uma configuração errada. Se temos um arquivo só e quebramos a configuração podemos tirar do ar todo o sistema. Quando separamos podemos continuar servindo conteúdo stale até arrumarmos a regra de negócio. Pense sempre em dois planos: controle e dados.&lt;/p&gt;

&lt;p&gt;Versione todas as configurações do sistema - exceto senhas e dados sensíveis - junto com o código fonte. Configurações são tão importantes quanto o código. Evite alterar configurações manualmente, crie pipelines de entrega adequados para fazer as mudanças necessárias a partir do repositório.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependências
&lt;/h2&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%2Fox3cjjcbxhxuprd6m52h.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%2Fox3cjjcbxhxuprd6m52h.png" alt="Exemplo de caos de dependências"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Evite dependências externas, especialmente as que você tem zero controle. Se você precisa usar serviços externos entenda do princípio que eles vão falhar e sua aplicação provavelmente não deveria falhar junto – claro, se for algo essencial da aplicação não dá para ficar sem. Mas um sistema de métricas ou de logging não deveria tirar a aplicação do ar. Nem um deploy em outro sistema deveria ter um impacto enorme na sua aplicação.&lt;/p&gt;

&lt;p&gt;Use retentativas, circuit-breakers ou ainda service mesh, para facilitar a gestão das dependências. Se as aplicações estão muito acopladas, então não há benefícios de ter micro serviços e um grande monólito faria um trabalho muito melhor. Pense em micro serviços como peças que possam ser trocadas quando necessário — e talvez dê para continuar voando sem elas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exemplo
&lt;/h2&gt;

&lt;p&gt;Para exercitar as ideias apresentadas vamos criar um caso de uso real: um sistema de vendas de ingressos para um cinema. O sistema consiste em usuários podendo escolher qual filme querem assistir, em qual dia e horário, e todo o fluxo de compra e emissão do ingresso. A arquitetura inicial é conforme a figura abaixo.&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%2Filmsafyhj4btgi09cb8c.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%2Filmsafyhj4btgi09cb8c.png" alt="Arquitetura inicial"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Essa arquitetura pode ser considerada um monólito. Uma única aplicação é responsável por todos os comportamentos do sistema: a autenticação dos usuários que desejam comprar ingressos, o sistema de pagamentos, a gestão de quais filmes estão sendo exibidos em quais salas, entre outras funcionalidades que desejarmos para um sistema como esse.&lt;/p&gt;

&lt;p&gt;Você pode se perguntar: se isso é um monólito, como podemos afirmar que essa aplicação é escalável? Ninguém especificou qual o volume de acessos, quantas salas de cinema o sistema gerencia, nem quantos filmes diferentes estarão disponíveis e onde eles estarão.&lt;/p&gt;

&lt;p&gt;Do ponto de vista de escalabilidade da aplicação, é perfeitamente aceitável começarmos com uma arquitetura dessas. No entanto, existe um pulo do gato para que o código não pareça um novelo de lã depois de um encontro com unhas afiadas: criarmos o sistema levando em conta os domínios necessários para seu funcionamento, garantindo que eles são independentes entre si e se comunicam atráves de interfaces bem definidas.&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%2F1t8pc33l4gqfo3l8n24q.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%2F1t8pc33l4gqfo3l8n24q.png" alt="Arquitetura inicial interna"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nessa figura mostramos os domínios existentes, deixando claro o que está agrupado em cada um deles: autenticação, filmes, salas, ingressos, pagamentos. Se a arquitetura começa com uma boa separação de conceitos, fica fácil escalar. E, dependendo do contexto, essa arquitetura é a única necessária! Se formos pensar em uma cidade com poucos habitantes, que possui um único cinema com 4 salas que exibem apenas 4 filmes, temos quase certeza de que nunca teremos um volume de acessos maior do que esse sistema consegue aguentar.&lt;/p&gt;

&lt;p&gt;No entanto, vamos exercitar nosso raciocínio para o outro lado. O sistema foi um sucesso, revolucionou a gestão de ingressos na cidade. A empresa, obviamente, quer estender o lucro e o sucesso obtido com o software. Para tanto, decidiu criar outro sistema para vender artigos relacionados a cinema.&lt;/p&gt;

&lt;p&gt;De modo a facilitar o uso para os atuais usuários, resolveram ter uma solução única de autenticação. Como essa responsabilidade já estava totalmente separada na estrutura do código, bastou um refactor para tirar a gestão de usuários do sistema de ingressos e criar um sistema separado. Agora esse sistema pode atender os fluxos de venda de ingressos e de souveniers. Qualquer melhoria na gestão de usuários é propagada para todos os sistemas que o utilizam, e também conseguimos escalar só essa parte do sistema se precisarmos.&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%2F7gjep3j7ya2hdmlgjeh0.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%2F7gjep3j7ya2hdmlgjeh0.png" alt="Arquitetura com autenticação separada"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O sucesso foi estrondoso! A empresa continuou faturando e, logo em seguida, surgiu uma grande oportunidade de negócio: comprar outras salas na cidade vizinha. Além disso, em uma pesquisa de satisfação com os seus clientes, a empresa viu que a grande dor de seus usuários era uma falta de lugares marcados nos ingressos.&lt;/p&gt;

&lt;p&gt;O sistema atual não dava conta - era preciso escalar melhor suas partes internas. Como os domínios não mudaram, basta uma reorganização e criação de novos subsistemas responsáveis por uma dada área. A gestão de ingressos ganha seu próprio subsistema, que escala independemente da gestão de salas e filmes. Lá também temos toda a lógica necessária para gerir a escolha de assentos, o tempo máximo de uma reserva, etc.&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%2Fh4ub96avdi59ve9r3x7g.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%2Fh4ub96avdi59ve9r3x7g.png" alt="Arquitetura final para a história presente"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos ver como o sistema cresceu, outra fontes de dados surgiram, pequenas partes tornaram-se escaláveis. O sistema está pronto? Provavelmente não, sempre haverá novas oportunidades de negócio: aumentar o alcance de cidades, incorporar a gestão de teatros; depende da evolução do negócio. Mas tendo os domínios bem separados, conseguimos escalar na medida certa para não gerar sistemas super complexos. Internamente alguns domínios podem ainda se desdobrar em mais partes, mas o ponto principal é conseguirmos ver a arquitetura como um todo. Qualquer pessoa consegue manter um modelo mental conforme a última figura.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"Ah, mas esse exemplo é todo de backend, não dá pra aplicar em front."&lt;/code&gt; É possível sim, basta imaginar que temos todo o código de front-end como uma &lt;code&gt;Single Page Application&lt;/code&gt; (SPA). Ter uma SPA é perfeitamente aceitável e permite o compartilhamento de componentes entre páginas, poupando o retrabalho! No entanto, imagine que essa SPA faz o roteamento para todas as páginas e componentes da aplicação - autenticação, pagamentos, visualização dos filmes disponíveis, escolha de sala, entre outras.&lt;/p&gt;

&lt;p&gt;À medida em que a necessidade do sistema vai evoluindo, o número de páginas, componentes e comportamentos complexos vai crescendo. O desempenho e agilidade da página ficam comprometidos; a experiência é degradada para o usuário, que fica esperando até que todo o programa seja executado pelo navegador. Podemos pensar em separar em partes a aplicação, só carregando o necessários conforme a necessidade. Se o usuário nunca entrar na parte de pagamentos, por que gastar tempo deixando ela disponível?&lt;/p&gt;

&lt;p&gt;O mesmo conceito e ideia de micro serviços pode ser aplicado a micro frontends, que entregarão pequenos comportamentos ou componentes que serão adicionados apenas quando estritamente necessário para a página, melhorando o desempenho e a experiência do usuário. Esses componentes podem ter a atenção devida de UXs e desenvolvedores dedicados que cuidarão e melhorarão a experiência, o que faz com que todos ganhem - usuários e empresa.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
