<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.rafaelhdr.com.br/pt_br/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.rafaelhdr.com.br/pt_br/" rel="alternate" type="text/html" /><updated>2026-07-24T15:23:04+00:00</updated><id>https://www.rafaelhdr.com.br/feed.xml</id><title type="html">rafaelhdr</title><subtitle>Full Stack Developer DevOps developer&lt;br /&gt; Python | Django | Docker | Postgres | Git | Linux</subtitle><entry xml:lang="pt_br"><title type="html">Claude salvando contexto com /branch</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/saving-context-using-branch" rel="alternate" type="text/html" title="Claude salvando contexto com /branch" /><published>2026-07-03T14:00:00+00:00</published><updated>2026-07-03T14:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/salvando-contexto-com-branch</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/saving-context-using-branch"><![CDATA[<h1 id="salvando-contexto-com-branch">Salvando contexto com /branch</h1>

<p>Aprendi recentemente como usar branches no Claude Code corretamente para organizar o trabalho e manter o contexto sob controle.</p>

<h2 id="o-problema-dos-contextos-grandes">O problema dos contextos grandes</h2>

<p>O contexto cresce muito conforme a conversa avança. Em algum momento, a IA tenta resumir tudo para economizar tokens e evitar o context rot.</p>

<blockquote>
  <p>Context rot is the performance degradation that happens when LLMs have to process increasingly long input contexts. Your LLM’s performance degrades when it has to search through longer contexts to find relevant information, even though that information is technically available in the context window.</p>
</blockquote>

<p>Fonte: <a href="https://redis.io/blog/context-rot/">Context Rot – Redis</a></p>

<h2 id="sugestão">Sugestão</h2>

<p>Existe o <code class="language-plaintext highlighter-rouge">/branch</code> no Claude, que basicamente separa sua conversa atual (incluindo o contexto) em uma nova thread. A nova thread tem o mesmo contexto de antes, e podemos fazer ações diferentes nela.</p>

<p>Então, você estabelece o contexto adequado (a partir da sua conversa). E em vez de executar todas as sugestões de uma vez, cada novo branch cuida de uma separadamente (assumindo que não têm relação entre si).</p>

<p>Exemplo:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Ruim

Faça um plano para melhorar meu site (context+)

[Claude gerou um plano com sugestões X, Y e Z]

Faça X (context++)

[Claude faz]

Faça Y (context+++)

[Claude faz]

Faça Z (context++++)
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Melhor

Faça um plano para melhorar meu site (context+)

[Claude gerou um plano com sugestões X, Y e Z]
</code></pre></div></div>

<p>Com isso, abra mais 2 abas e rode <code class="language-plaintext highlighter-rouge">claude --resume</code> em cada uma (com a primeira, temos 3 no total)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Aba 1
/rename X
Faça X (context++)

Aba 2
/branch Y
Faça Y (context++)

Aba 3
/branch Z
Faça Z (context++)
</code></pre></div></div>

<h2 id="exemplo-não-real">Exemplo (não) real</h2>

<p>Vou mostrar um exemplo mínimo só para explicar como funciona.</p>

<h3 id="contexto">Contexto</h3>

<p>Temos uma página simples de portfólio.</p>

<p>Adicionei 3 erros simples para serem corrigidos pela IA:</p>

<ul>
  <li>Backend Engineer no topo e Fullstack Engineer no rodapé</li>
  <li>Erros de digitação como “passioante”</li>
  <li>Skill duplicada “HTML/CSS”</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>John Doe – Backend Engineer<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;style&gt;</span>
    <span class="nt">body</span> <span class="p">{</span> <span class="nl">font-family</span><span class="p">:</span> <span class="nb">sans-serif</span><span class="p">;</span> <span class="nl">max-width</span><span class="p">:</span> <span class="m">700px</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="p">}</span>
    <span class="nt">header</span><span class="o">,</span> <span class="nt">footer</span> <span class="p">{</span> <span class="nl">background</span><span class="p">:</span> <span class="nx">#1a1a2e</span><span class="p">;</span> <span class="nl">color</span><span class="p">:</span> <span class="nx">white</span><span class="p">;</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span> <span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span> <span class="p">}</span>
    <span class="nt">section</span> <span class="p">{</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">24px</span> <span class="m">0</span><span class="p">;</span> <span class="nl">border-bottom</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="nx">#eee</span><span class="p">;</span> <span class="p">}</span>
    <span class="nc">.skills</span> <span class="nt">span</span> <span class="p">{</span> <span class="nl">background</span><span class="p">:</span> <span class="nx">#e0e7ff</span><span class="p">;</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">4px</span> <span class="m">10px</span><span class="p">;</span> <span class="nl">border-radius</span><span class="p">:</span> <span class="m">12px</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="nl">display</span><span class="p">:</span> <span class="nb">inline-block</span><span class="p">;</span> <span class="p">}</span>
  <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>

  <span class="nt">&lt;header&gt;</span>
    <span class="nt">&lt;h1&gt;</span>John Doe<span class="nt">&lt;/h1&gt;</span>
    <span class="nt">&lt;p&gt;</span>Backend Engineer<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/header&gt;</span>

  <span class="nt">&lt;section</span> <span class="na">id=</span><span class="s">"hero"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h2&gt;</span>Building robust systems from the ground up<span class="nt">&lt;/h2&gt;</span>
    <span class="nt">&lt;p&gt;</span>Available for freelance and full-time oportunities.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/section&gt;</span>

  <span class="nt">&lt;section</span> <span class="na">id=</span><span class="s">"description"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;p&gt;</span>I am a passioante developer with 5+ years of experianced building scalable APIs and microservices. I love clean code, performance tuning, and solving hard backend challanges.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/section&gt;</span>

  <span class="nt">&lt;section</span> <span class="na">id=</span><span class="s">"skills"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h3&gt;</span>Skills<span class="nt">&lt;/h3&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"skills"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;span&gt;</span>Python<span class="nt">&lt;/span&gt;&lt;span&gt;</span>Node.js<span class="nt">&lt;/span&gt;&lt;span&gt;</span>PostgreSQL<span class="nt">&lt;/span&gt;</span>
      <span class="nt">&lt;span&gt;</span>Docker<span class="nt">&lt;/span&gt;&lt;span&gt;</span>HTML/CSS<span class="nt">&lt;/span&gt;&lt;span&gt;</span>HTML/CSS<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/section&gt;</span>

  <span class="nt">&lt;footer&gt;</span>
    <span class="nt">&lt;p&gt;</span>© 2026 John Doe – Fullstack Engineer<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/footer&gt;</span>

<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<h3 id="prompts">Prompts</h3>

<p>Plano:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ Make suggestions to improve this portfolio.html. Up to 3, as this is just an example.

(...)

● Updated plan
/plan to preview
User rejected Claude's plan:
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Plan: Improve portfolio.html                                                                                                                                                                                          │
│                                                                                                                                                                                                                       │
│ Context                                                                                                                                                                                                               │
│                                                                                                                                                                                                                       │
│ The user has a simple portfolio page (/tmp/claude/portfolio.html) for "John Doe – Backend Engineer" and wants up to 3 improvement suggestions. The page has clear, fixable issues that hurt professional credibility. │
│                                                                                                                                                                                                                       │
│ ---                                                                                                                                                                                                                   │
│ Suggestions                                                                                                                                                                                                           │
│                                                                                                                                                                                                                       │
│ 1. Fix typos in the hero and description sections                                                                                                                                                                     │
│                                                                                                                                                                                                                       │
│ The page has four spelling errors that undermine credibility for a professional portfolio:                                                                                                                            │
│ - Line 23: oportunities → opportunities                                                                                                                                                                               │
│ - Line 26: passioante → passionate                                                                                                                                                                                    │
│ - Line 26: experianced → experienced                                                                                                                                                                                  │
│ - Line 26: challanges → challenges                                                                                                                                                                                    │
│                                                                                                                                                                                                                       │
│ File: portfolio.html, lines 23 and 26                                                                                                                                                                                 │
│                                                                                                                                                                                                                       │
│ ---                                                                                                                                                                                                                   │
│ 2. Remove duplicate skill tag                                                                                                                                                                                         │
│                                                                                                                                                                                                                       │
│ HTML/CSS appears twice in the skills list (line 33). One instance should be removed.                                                                                                                                  │
│                                                                                                                                                                                                                       │
│ File: portfolio.html, line 33                                                                                                                                                                                         │
│                                                                                                                                                                                                                       │
│ ---                                                                                                                                                                                                                   │
│ 3. Fix inconsistent job title                                                                                                                                                                                         │
│                                                                                                                                                                                                                       │
│ The &lt;header&gt; calls him a "Backend Engineer" but the &lt;footer&gt; says "Fullstack Engineer". These must match — pick one and use it consistently (including in the &lt;title&gt; tag, which already says "Backend Engineer").    │
│                                                                                                                                                                                                                       │
│ File: portfolio.html, lines 5, 17, and 39                                                                                                                                                                             │
│                                                                                                                                                                                                                       │
│ ---                                                                                                                                                                                                                   │
│ Verification                                                                                                                                                                                                          │
│                                                                                                                                                                                                                       │
│ After applying changes:                                                                                                                                                                                               │
│ - Open portfolio.html in a browser and visually confirm the skills list, header, and footer                                                                                                                           │
│ - Run a spell-check or re-read lines 23 and 26 to confirm no remaining typos                                                                                                                                          │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
</code></pre></div></div>

<p>O contexto está em <code class="language-plaintext highlighter-rouge">22.3k/200k tokens (11%)</code> (obtive isso rodando <code class="language-plaintext highlighter-rouge">/context</code>)</p>

<p>Agora temos 3 abas (esta primeira, e abrimos as outras 2. Você também pode usar tmux ou algo similar).</p>

<p>Na primeira, você mantém como está (ou usa <code class="language-plaintext highlighter-rouge">/rename fix typos</code> para facilitar a leitura). Na segunda, <code class="language-plaintext highlighter-rouge">/branch remove duplicate skill tag</code>. Na terceira, <code class="language-plaintext highlighter-rouge">/branch fix inconsistent job title</code>.</p>

<p>Agora temos 3 abas, cada uma com uma ação específica.</p>

<p>E então, em cada uma você pede para resolver:</p>

<ul>
  <li>Fix typos</li>
  <li>Remove duplicate skill tag</li>
  <li>Fix inconsistent job title</li>
</ul>

<p>Com isso, você pode ter tarefas do Claude (ou outra IA) rodando de forma simultânea e otimizada.</p>

<h2 id="contexto-utilizado">Contexto utilizado</h2>

<p>Este é só um exemplo simples com prompts pequenos — não é um benchmark. Mas só para ilustrar a ideia, fiz das duas formas e registrei o contexto.</p>

<h3 id="solução-única-sem-branch">Solução única sem branch</h3>

<p>Se fizermos tudo sem branches:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Prompt: Make suggestions to improve this website page. Up to 3, as this is just an example.
/context: 22.3k/200k tokens

Prompt: Fix typos
/context: 26.3k/200k tokens

Prompt: remove duplicate skill tag
/context: 28.6k/200k tokens

Prompt: fix inconsistent job title
/context: 31k/200k tokens
</code></pre></div></div>

<p>Dá para ver que vai crescendo devagar (mesmo usando prompts bem pequenos).</p>

<h3 id="separando-em-branches">Separando em branches</h3>

<p>Quando dividimos em branches, cada um tem um contexto menor:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Aba 1:
Prompt: Make suggestions to improve this website page. Up to 3, as this is just an example.
/context: 22.3k/200k tokens
Prompt: Fix typos
/context: 26.3k/200k tokens

===========================
Aba 2:
Prompt: remove duplicate skill tag
/context: 26k/200k tokens

===========================
Aba 3:
Prompt: fix inconsistent job title
/context: 26.2k/200k tokens
</code></pre></div></div>

<h2 id="conflitos-entre-as-tarefas">Conflitos entre as tarefas</h2>

<p>Este é um exemplo com um único arquivo. Mas mesmo em projetos maiores, os branches podem tocar os mesmos arquivos.</p>

<p>Você pode evitar isso usando git worktrees (mas recomendo o worktrunk — muito amigável para devs, e cria uma pasta diferente para cada solução)</p>

<p>Worktrunk: <a href="https://worktrunk.dev/">worktrunk.dev</a></p>

<p>No meu caso, prefiro dar atenção adequada a cada tarefa. Para coisas pequenas (como corrigir um typo) não preciso disso, mas para problemas maiores eu faria X, resolveria, commitaria. Depois (no outro /branch) faria Y, e então Z.</p>

<h1 id="leia-mais">Leia mais</h1>

<p><a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents">Effective Context Engineering for AI Agents – Anthropic</a></p>

<p><a href="https://code.claude.com/docs/en/how-claude-code-works#resume-or-fork-sessions">Resume or fork sessions – Claude Code docs</a></p>]]></content><author><name></name></author><category term="ai" /><category term="claude-code" /><category term="produtividade" /><category term="contexto" /><summary type="html"><![CDATA[Como usar o /branch no Claude Code para organizar o trabalho e manter o contexto pequeno]]></summary></entry><entry xml:lang="pt_br"><title type="html">Redesign do site com IA</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/website-redesign-with-ai" rel="alternate" type="text/html" title="Redesign do site com IA" /><published>2026-06-17T14:00:00+00:00</published><updated>2026-06-17T14:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/redesign-do-site-com-ia</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/website-redesign-with-ai"><![CDATA[<h1 id="redesign-do-site-rafaelhdrcombr">Redesign do site rafaelhdr.com.br</h1>

<p>A IA chegou, e esta é a primeira vez que estou atualizando este site usando IA.</p>

<p>Essa tecnologia não é novidade para mim. Eu a uso diariamente no trabalho/vida pessoal. Já tinha experiência.</p>

<p>Mas por muito tempo quis atualizar meu portfólio, mas tinha outras prioridades.</p>

<h2 id="motivação">Motivação</h2>

<p>Estou procurando um novo emprego. Faz 8 anos que estou na The Silver Logic. Houve muito aprendizado, mas desde o ano passado estava claro para mim que queria mudar. E em janeiro comuniquei ao chefe/colegas/amigos.</p>

<p>Ultimamente tenho estado mais focado nisso. E não estava muito orgulhoso da versão atual do rafaelhdr.com.br (em termos de design).</p>

<p>Sinceramente, design não é minha habilidade mais forte, mas queria ter algo bonito para os visitantes. Algo do qual eu me orgulhasse (anteriormente, a página inicial tinha apenas um “olá” meu).</p>

<p>Estou feliz por enquanto. Definitivamente pode ser melhorado, mas posso fazer isso em outra oportunidade. Por enquanto, é suficiente.</p>

<h2 id="referências">Referências</h2>

<p>Tive alguns sites de amigos como primeira referência. Algo para começar:</p>

<ul>
  <li><a href="https://alexandreanicio.dev/">alexandreanicio.dev</a></li>
  <li><a href="https://samueljansem.dev/contact/">samueljansem.dev</a></li>
</ul>

<p>E então fui ao <a href="https://www.reddit.com/r/webdev/comments/143acrg/show_me_your_portfolios/">Reddit</a></p>

<p>Tem muitos exemplos bons. Mas alguns são exagerados (ótimos, mas eu não focaria em alcançar aquilo). Como este que roda um SO no navegador <a href="https://dustinbrett.com/">dustinbrett.com</a> ou este muito bonito <a href="https://alyssafaustino.com/">alyssafaustino.com</a>.</p>

<p>Mas então peguei 3 que foram realmente boas referências. Eu ia basear meu redesign tendo-os como referência:</p>

<ul>
  <li><a href="https://gianmarcocavallo.com/">gianmarcocavallo.com</a></li>
  <li><a href="https://www.radnaabazar.com/en">radnaabazar.com</a></li>
  <li><a href="https://m7mad.dev/">m7mad.dev</a></li>
</ul>

<p>E finalmente, quando quero explicar algo para a IA, ou até ter um bom exemplo para mim, vou ao <a href="https://dribbble.com/">Dribbble</a>.</p>

<h2 id="o-que-fiz">O que fiz</h2>

<p>Comecei fazendo a IA obter contexto, e então gerar o AGENTS.md (para prompts futuros).</p>

<p>E segundo, estava atualizando os pacotes, Docker, Ruby, Jekyll etc. E criei o docker compose para facilitar testes locais.</p>

<p>Finalmente, dividir e conquistar. Eu acessava cada página e trabalhava no que me incomodava. O que não estava claro, e o que poderia ser melhor. Esse processo incluía abrir os portfólios de outros devs para pegar ideias.</p>

<p>O desenvolvimento foi feito apenas pela IA. Ainda reviso o que está acontecendo (para ver se algo está muito errado). Por exemplo, por algum motivo ela converteu uma página para HTML (totalmente desnecessário, e eu ainda gostaria de revisar/escrever conteúdo, e markdown é muito mais fácil para isso).</p>

<p>Mas tudo foi muito rápido de fazer. É difícil comparar com o método que eu usava anos atrás.</p>

<h2 id="ferramentas-utilizadas">Ferramentas utilizadas</h2>

<p>Não usei Claude (ou ChatGPT). Esse processo também é um aprendizado para mim. A The Silver Logic adotou Claude, investindo em workshops e aprendizado. Ainda tenho muito a aprender sobre isso, mas quero testar alternativas no meu tempo pessoal.</p>

<p>A razão para as alternativas é principalmente porque não gosto muito de ficar numa solução única. Gostaria de conhecer as opções e poder escolher o que usar. E vejo muito potencial. Por exemplo, em um projeto paralelo, estou fazendo várias tarefas pequenas e fáceis de IA. Para isso, quero usar um modelo de IA barato/rápido. Para outras tarefas, gostaria de pagar mais, para economizar meu tempo.</p>

<p>Hoje em dia, estou usando estas 2 ferramentas:</p>

<h3 id="aionui">AionUI</h3>

<p>Fonte: <a href="https://aionui.com/">AionUI</a></p>

<p>Ela pode fazer muita coisa, e ainda tenho muito a aprender. Tem muitos templates, e consegue se conectar a tantos modelos (alguns deles gratuitos).</p>

<p>Foi útil para o momento de insight. Quando quero revisar algum texto, pesquisar soluções. Quando era mais relacionado ao produto, e não especificamente ao código, eu estava usando ela.</p>

<h3 id="opencode">OpenCode</h3>

<p>Fonte: <a href="https://opencode.ai/">OpenCode</a></p>

<p>Para a programação em si, estou usando no terminal.</p>

<p>Se a tarefa é pequena, apenas construo. Reviso e então é mesclado.</p>

<p>Se a tarefa é média ou grande, então explico, dou diretrizes, e peço no modo Plan. Após o plano, aconselho ou faço algum ajuste, e então uso o modo Build.</p>

<h2 id="não-totalmente-ia">Não totalmente IA</h2>

<p>Para construir, foi ótimo. Mas para o conteúdo, ainda acredito que eu deveria ser o gerador do conteúdo.</p>

<p>No final, é meu próprio site. Deve ter minha personalidade e minhas informações aqui. Mas pequenos ajustes, como adicionar URL e revisão gramatical, é para a IA.</p>]]></content><author><name></name></author><category term="web" /><category term="ia" /><category term="portfolio" /><category term="rafaelhdr" /><category term="jekyll" /><category term="docker" /><summary type="html"><![CDATA[Por que e como fiz o redesign do meu próprio site]]></summary></entry><entry xml:lang="pt_br"><title type="html">Como usar ataque de dicionário para quebrar uma senha</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/dictionary-attack" rel="alternate" type="text/html" title="Como usar ataque de dicionário para quebrar uma senha" /><published>2024-12-15T14:00:00+00:00</published><updated>2024-12-15T14:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/ataque-de-dicionario</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/dictionary-attack"><![CDATA[<h1 id="como-realizar-um-ataque-de-dicionário-para-quebrar-uma-senha">Como realizar um ataque de dicionário para quebrar uma senha</h1>

<h2 id="contexto">Contexto</h2>

<p>Ataque de dicionário consiste em usar uma lista de palavras comuns para tentar descobrir um segredo (por exemplo, a senha de um usuário).</p>

<p>Se um site não tiver proteção, ele pode ser comprometido por esse ataque.</p>

<p>Nesse post, vamos mostrar como realizar um ataque de dicionário. Ao final, teremos sugestões de como se proteger.</p>

<p>E para testar, nosso usuário terá a senha <code class="language-plaintext highlighter-rouge">1234567</code>, que é a nona senha mais comum de acordo com <a href="https://en.wikipedia.org/wiki/Wikipedia:10,000_most_common_passwords">Wikipedia</a>.</p>

<h2 id="atenção">Atenção</h2>

<p><strong>Aviso de ética:</strong> Este guia é destinado apenas a fins educacionais e para testes autorizados de segurança. Certifique-se de ter permissão explícita antes de realizar ataques em qualquer sistema.</p>

<h2 id="ambiente">Ambiente</h2>

<p>Para simular o ataque, vamos rodar localmente uma página simples web usando Django.</p>

<p>Arquivo Dockerfile</p>

<div class="language-Dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> python:3.12-slim</span>

<span class="k">WORKDIR</span><span class="s"> /app</span>

<span class="k">RUN </span>pip <span class="nb">install </span><span class="nv">Django</span><span class="o">==</span>5.1.4 <span class="o">&amp;&amp;</span> <span class="se">\
</span>    django-admin startproject myproject <span class="nb">.</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span>    <span class="nb">echo</span> <span class="s2">"STATIC_ROOT = '/app/static/'"</span> <span class="o">&gt;&gt;</span> myproject/settings.py <span class="o">&amp;&amp;</span> <span class="se">\
</span>    python manage.py migrate <span class="o">&amp;&amp;</span> python manage.py collectstatic <span class="nt">--noinput</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span>    <span class="nb">echo</span> <span class="s2">"from django.contrib.auth.models import User; User.objects.create_superuser('admin', 'admin@example.com', '1234567')"</span> | python manage.py shell

<span class="k">EXPOSE</span><span class="s"> 8000</span>

<span class="k">CMD</span><span class="s"> ["python", "manage.py", "runserver", "0.0.0.0:8000"]</span>
</code></pre></div></div>

<p>E para rodar, basta executar o comando:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build -t unsafe-django .
docker run -p 8000:8000 unsafe-django`
</code></pre></div></div>

<p>E pronto. Você pode acessar a página em <code class="language-plaintext highlighter-rouge">http://localhost:8000/admin/</code>.</p>

<p><img src="/assets/posts/2024-12-20-dictionary-attack/django-login.png" alt="Login do Django" /></p>

<h2 id="burp-suite">Burp Suite</h2>

<p>O Burp Suite é uma das ferramentas mais populares para testes de segurança de aplicações web, amplamente utilizada por analistas de segurança e desenvolvedores para identificar vulnerabilidades como injeção de código, falhas de autenticação e exposição de dados sensíveis.</p>

<p>Para o ataque de dicionário, vamos usar o <strong>Intruder</strong>. Vamos fazer isso passo a passo.</p>

<p>Você pode instalar seguindo as intruções do link <a href="https://portswigger.net/burp/documentation/desktop/getting-started/download-and-install">Download and Install</a>.</p>

<h3 id="usando-intruder">Usando Intruder</h3>

<p>Abra seu Burp Suite.</p>

<p><img src="/assets/posts/2024-12-20-dictionary-attack/burp-initial.png" alt="Página inicial do Burp" /></p>

<p>Estou usando a Community Edition, e com isso preciso apenas clicar em <strong>Next</strong>. Na janela seguinte <strong>Start Burp</strong>.</p>

<p>E agora temos ele pronto para começar a usar o Burp. Precisamos primeiro capturar nossa request. Vá na aba <strong>Proxy</strong> e clique em <strong>Open Browser</strong>.</p>

<p>Vamos usar esse Browser para detectar a request que queremos fazer o ataque. No caso, vamos fazer login.</p>

<p>Dentro do Browser, acesse <code class="language-plaintext highlighter-rouge">http://localhost:8000/admin/</code>. E no seu Burp Suite, clique em <strong>Intercept off</strong> (para começar a interceptar).</p>

<p>De volta ao browser, tente acessar com o usuário <code class="language-plaintext highlighter-rouge">admin</code> e uma senha qualquer como <code class="language-plaintext highlighter-rouge">123456</code> (supondo que não sabemos a senha do admin ainda). De volta ao Burp, você verá a request esperando. Queremos replicar ela, então clique com o botão direito e <strong>Send to Intruder</strong>.</p>

<p><img src="/assets/posts/2024-12-20-dictionary-attack/burp-btn-send-to-intruder.png" alt="Burp proxy" /></p>

<p>E podemos ir para a aba <strong>Intruder</strong>.</p>

<p>Nessa aba nova, você verá a request que interceptamos. Selecione a senha que você utilizou e clique em <strong>Add</strong>.</p>

<p><img src="/assets/posts/2024-12-20-dictionary-attack/burp-sniper-attack.png" alt="Burp Sniper Attack" /></p>

<p>Vamos usar <strong>Simple List</strong> como nosso Payload type. Com isso, iremos passar um arquivo com o nosso dicionário de senhas. Para o nosso exemplo, vou usar um dicionarário com apenas 20 senhas.</p>

<pre><code class="language-txt">123456
password
12345678
qwerty
123456789
12345
1234
111111
1234567
dragon
123123
baseball
abc123
football
monkey
letmein
696969
shadow
master
666666
</code></pre>

<p>Eu peguei essas senhas do <a href="https://en.wikipedia.org/wiki/Wikipedia:10,000_most_common_passwords">Wikipedia</a>. Basta salvar em um arquivo e selecionar ele no Burp (clicar em <code class="language-plaintext highlighter-rouge">Load...</code> e selecionar seu arquivo).</p>

<p>Com tudo isso pronto, você terá algo parecido com isso:</p>

<p><img src="/assets/posts/2024-12-20-dictionary-attack/burp-ready-to-start.png" alt="Burp pronto para começar o ataque" /></p>

<p>Agora basta clicar em Start Attack. E você verá o Burp tentando todas as senhas do dicionário.</p>

<p>Após alguns instantes, você verá que a senha <code class="language-plaintext highlighter-rouge">1234567</code> foi encontrada.</p>

<p><img src="/assets/posts/2024-12-20-dictionary-attack/burp-results.png" alt="Resultados do Burp" /></p>

<p>Nosso resultado é esse diferente em que temos o response code 302, e podemos ver qual foi a senha que deu certo (no caso, <code class="language-plaintext highlighter-rouge">1234567</code>).</p>

<h2 id="como-se-proteger">Como se proteger</h2>

<p>Para se proteger de ataques de dicionário, você pode:</p>

<p><strong>Limitar o número de tentativas de login:</strong> Usar ferramentas que limitem o número de tentativas de login, como lockouts, para evitar que um atacante tente várias senhas.</p>

<p><strong>Usar autenticação de dois fatores:</strong> É uma boa solução também, pois mesmo que o atacante descubra a senha, ele ainda precisará de outro fator para acessar a conta. Por outro lado, é interessante ter outras formas também, caso contrário seu site poderá ser utilizado para descobrir senhas de outros serviços - por exemplo, caso seus usuários usem a mesma senha em vários sites.</p>

<p><strong>Usar senhas fortes:</strong> É comum mostrar um indicador de quão forte é a senha para que o usuário possa saber se está usando senha muito fraca e evitar que seja descoberta facilmente.</p>

<p><strong>Monitorar tentativas de login:</strong> Usar um serviço de monitoramento de segurança para identificar tentativas de login suspeitas.</p>

<p><strong>Login sem senha:</strong> Outra forma de se proteger é não usar senha. Existem outras formas de autenticação, como fazer o login usando o Google, Facebook, ou envio de token por e-mail.</p>

<h2 id="conclusão">Conclusão</h2>

<p>Esse post foi focado em mostrar como um ataque de dicionário pode ser feito e trazer indicações de como se proteger.</p>

<p>Há diversas formas de se proteger, mas quis apenas explicá-las superficialmente, pois a proteção vai depender bastante de qual linguagem/frameworks você usa.</p>]]></content><author><name></name></author><category term="segurança" /><category term="owasp" /><summary type="html"><![CDATA[Demonstra como realizar um ataque de dicionário contra uma página Django para fins educacionais de segurança.]]></summary></entry><entry xml:lang="pt_br"><title type="html">Django queryset annotated timezone</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/django-queryset-with-timezones" rel="alternate" type="text/html" title="Django queryset annotated timezone" /><published>2024-07-15T11:00:00+00:00</published><updated>2024-07-15T11:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/django-queryset-com-timezones</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/django-queryset-with-timezones"><![CDATA[<h1 id="django-queryset-annotated-timezone">Django queryset annotated timezone</h1>

<h2 id="tldr">TL;DR</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    def annotate_tz_aware(self, dt):
        sql_date = dt.strftime("%Y-%m-%d")
        return (
            self
            .annotate(
                dt=RawSQL("(%s || ' ' || time || ' ')::timestamp AT TIME ZONE timezone", (sql_date,)),
            )
        )
</code></pre></div></div>

<p>Annotate dessa linha passando a data (<code class="language-plaintext highlighter-rouge">sql_date</code>)</p>

<h2 id="cenário">Cenário</h2>

<p>Vamos imaginar o seguinte cenário. Você possui seu model com date e time naive com um campo separado para timezone. E na queryset você precisa ordenar ou filtrar. Você pode usar um annotation para poder ordenar o que vem antes variando com o timezone.</p>

<blockquote>
  <p>Por que não usar datetime com timezone convertendo para UTC em todos? No meu caso em específico, eu precisava de eventos recorrentes, preferindo deixar date e time naive, e fazendo as conversões pela própria queryset ou ao retornar ao usuário.</p>
</blockquote>

<h2 id="código-base">Código base</h2>

<p>Tendo como base o seguinte repositório <a href="https://gitlab.com/rafaelhdr/dj_queryset_timezone">GitHub</a>.</p>

<p>Vamos olhar o models.py:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Event(models.Model):
    name = models.CharField(max_length=100)
    weekdays = models.CharField(max_length=100)
    time = models.TimeField()
    timezone = models.CharField(max_length=100)
</code></pre></div></div>

<p>E o seguinte teste:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    def test_sort_different_tz_events(self):
        london_event = Event.objects.create(
            name='Event 1',
            weekdays='mon,tue,wed,thu,fri',
            time='12:00',
            timezone='Europe/London',
        )
        paris_event = Event.objects.create(
            name='Event 2',
            weekdays='mon,tue,wed,thu,fri',
            time='12:01',
            timezone='Europe/Paris',
        )

        dt = date(2024, 7, 15)
        events = Event.objects.annotate_tz_aware(dt).order_by('dt')
        assert events[0] == paris_event
        assert events[1] == london_event
</code></pre></div></div>

<p>Nesse teste, o evento em Londres acontece às 12:00 de Londres, que seria 11:00 UTC. Mas o event de Paris deve acontecer antes, pois é 12:01 de Paris, mas 10:01 UTC.</p>

<p>Consegui alcançar o objetivo com RawQuery fazendo o seguinte annotation:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class EventManager(models.Manager):

    def annotate_tz_aware(self, dt):
        sql_date = dt.strftime("%Y-%m-%d")
        return (
            self
            .annotate(
                dt=RawSQL("(%s || ' ' || time || ' ')::timestamp AT TIME ZONE timezone", (sql_date,)),
            )
        )


class Event(models.Model):
    name = models.CharField(max_length=100)
    weekdays = models.CharField(max_length=100)
    time = models.TimeField()
    timezone = models.CharField(max_length=100)

    objects = EventManager()
</code></pre></div></div>

<p>Vou explicar cada passo do <code class="language-plaintext highlighter-rouge">annotate_tz_aware</code>.</p>

<ul>
  <li>Usamos o <code class="language-plaintext highlighter-rouge">dt</code> para passar qual data está sendo feita a comparação. Isso é necessário pois fazemos a comparação no mesmo timezone (UTC no exemplo) e precisamos da data caso seja convertido para o dia seguinte.
** Exemplo: 2:00 em Sydney (Austrália) seriam 15h do dia anterior em UTC. Precisamos levar isso em consideração na hora da conversão.
** Por que não usar uma data aleatória? Pois dependendo da data do ano, temos o horário de verão, então essa conversão será diferente.</li>
  <li><code class="language-plaintext highlighter-rouge">"(%s || ' ' || time || ' ')::timestamp</code> esse trecho é apenas para concatenar como um datetime sem a noção de timezone.</li>
  <li><code class="language-plaintext highlighter-rouge">AT TIME ZONE timezone</code> vamos forçar que o resultado seja no timezone do evento. E dessa forma fazemos a comparação com o datetime ciente do timezone na hora de ordenar.</li>
</ul>

<h2 id="simplificação">Simplificação</h2>

<p>Esse foi um jeito bem simplificado do que fiz. No caso, não verifiquei se dt tem o weekday do evento. Mas espero ter passado a ideia principal.</p>]]></content><author><name></name></author><category term="django" /><category term="queryset" /><summary type="html"><![CDATA[Mostra como usar RawSQL annotation no Django para trabalhar com timezones em querysets com date e time naive.]]></summary></entry><entry xml:lang="pt_br"><title type="html">Learning docker compose</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/learning-docker-compose" rel="alternate" type="text/html" title="Learning docker compose" /><published>2019-09-09T11:00:00+00:00</published><updated>2019-09-09T11:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/learning-docker-compose</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/learning-docker-compose"><![CDATA[<h1 id="aprendendo-docker-compose">Aprendendo docker compose</h1>

<p>Nesse post, vou explicar um pouco como fazer uma aplicação simples usando docker-compose. Farei uma aplicação web com Flask utilizando banco de dados MariaDB.</p>

<p>Ela será simples, sem dados ou algum migration com o banco de dados. O foco do post será entender o docker-compose.</p>

<h2 id="aplicação-flask">Aplicação Flask</h2>

<p>Usarei o Flask para essa aplicação simples. Ela terá apenas 2 endpoints. Um será apenas uma mensagem estática e o outro fará uma requisição para o banco de dados.</p>

<p>A estrutura será a seguinte:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├── application
│   ├── Dockerfile
│   └── src
│       ├── app.py
│       └── requirements.txt
└── docker-compose.yml
</code></pre></div></div>

<ul>
  <li><em>Dockerfile</em> contém a nossa imagem Docker.</li>
  <li><em>app.py</em> é a nossa aplicação.</li>
  <li><em>requirements.txt</em> contém as dependências do nosso projeto.</li>
  <li><em>docker-compose.yml</em> é para orquestrar os nossos serviços (a aplicação e o banco de dados).</li>
</ul>

<p>O código de exemplo está no <a href="https://gitlab.com/rafaelhdr/docker-compose-example">GitLab</a></p>

<h3 id="apppy">app.py</h3>

<p>Nossa aplicação possui esses 2 endpoints: <code class="language-plaintext highlighter-rouge">/</code> e <code class="language-plaintext highlighter-rouge">/db</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">os</span>
<span class="kn">import</span> <span class="n">pymysql.cursors</span>
<span class="kn">from</span> <span class="n">flask</span> <span class="kn">import</span> <span class="n">Flask</span>


<span class="n">connection</span> <span class="o">=</span> <span class="n">pymysql</span><span class="p">.</span><span class="nf">connect</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="sh">'</span><span class="s">database</span><span class="sh">'</span><span class="p">,</span>
                             <span class="n">user</span><span class="o">=</span><span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">MYSQL_USER</span><span class="sh">'</span><span class="p">),</span>
                             <span class="n">password</span><span class="o">=</span><span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">MYSQL_PASSWORD</span><span class="sh">'</span><span class="p">),</span>
                             <span class="n">db</span><span class="o">=</span><span class="sh">'</span><span class="s">application_db</span><span class="sh">'</span><span class="p">,</span>
                             <span class="n">charset</span><span class="o">=</span><span class="sh">'</span><span class="s">utf8mb4</span><span class="sh">'</span><span class="p">,</span>
                             <span class="n">cursorclass</span><span class="o">=</span><span class="n">pymysql</span><span class="p">.</span><span class="n">cursors</span><span class="p">.</span><span class="n">DictCursor</span><span class="p">)</span>

<span class="n">app</span> <span class="o">=</span> <span class="nc">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>


<span class="nd">@app.route</span><span class="p">(</span><span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
    <span class="k">return</span> <span class="sa">f</span><span class="sh">'</span><span class="s">Application /</span><span class="sh">'</span>


<span class="nd">@app.route</span><span class="p">(</span><span class="sh">'</span><span class="s">/db</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">db</span><span class="p">():</span>
    <span class="n">cur</span> <span class="o">=</span> <span class="n">connection</span><span class="p">.</span><span class="nf">cursor</span><span class="p">()</span>
    <span class="n">cur</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="sh">"</span><span class="s">SELECT VERSION() AS db_version</span><span class="sh">"</span><span class="p">)</span>

    <span class="n">version</span> <span class="o">=</span> <span class="n">cur</span><span class="p">.</span><span class="nf">fetchone</span><span class="p">()</span>

    <span class="nf">return </span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">Database /db&lt;br /&gt;</span><span class="se">\n</span><span class="sh">'</span>
            <span class="sa">f</span><span class="sh">'</span><span class="s">Version of database: </span><span class="si">{</span><span class="n">version</span><span class="p">[</span><span class="sh">"</span><span class="s">db_version</span><span class="sh">"</span><span class="p">]</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>

<p>A aplicação usa os primeiros exemplos que encontrei para flaks e pymysql.</p>

<p>O endpoint <code class="language-plaintext highlighter-rouge">/</code> é apenas para imprimir um texto simples e <code class="language-plaintext highlighter-rouge">/db</code> se conecta ao banco de dados e escreve a versão.</p>

<h3 id="requirementstxt">requirements.txt</h3>

<pre><code class="language-txt">Flask==1.1.1
PyMySQL==0.9.3
</code></pre>

<p>Esse arquivo contém as dependências do projeto.</p>

<h3 id="dockerfile">Dockerfile</h3>

<p>O Dockerfile tem comentários para ajudar a entender o que ele está fazendo. Ele está copiando a pasta <code class="language-plaintext highlighter-rouge">src</code> para a imagem, instalando os requerimentos e definindo o comando para inicializar.</p>

<div class="language-Dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> python:3.7</span>

<span class="c"># Copy full application to the image</span>
<span class="c"># For real environments, consider customize .dockerignore</span>
<span class="k">COPY</span><span class="s"> src /opt/application</span>

<span class="c"># This is to make it easier. The defaul PWD where you</span>
<span class="c"># start when using your application</span>
<span class="k">WORKDIR</span><span class="s"> /opt/application</span>

<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt

<span class="c"># Run default flask, but allow remote access</span>
<span class="k">CMD</span><span class="s"> flask run --host=0.0.0.0</span>
</code></pre></div></div>

<h3 id="docker-composeyml">docker-compose.yml</h3>

<p>Esse arquivo vai listar os serviços e suas variáveis.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.7"</span>

<span class="na">services</span><span class="pi">:</span>
  <span class="na">application</span><span class="pi">:</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">application</span>
    <span class="na">links</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">database</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">5000:5000"</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">FLASK_DEBUG</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1"</span>
      <span class="na">MYSQL_USER</span><span class="pi">:</span> <span class="s2">"</span><span class="s">application"</span>
      <span class="na">MYSQL_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">password"</span>
      <span class="na">MYSQL_DATABASE</span><span class="pi">:</span> <span class="s2">"</span><span class="s">application_db"</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">./application/src:/opt/application</span>

  <span class="na">database</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">mariadb:10.4</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">MYSQL_RANDOM_ROOT_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">yes"</span>
      <span class="na">MYSQL_DATABASE</span><span class="pi">:</span> <span class="s2">"</span><span class="s">application_db"</span>
      <span class="na">MYSQL_USER</span><span class="pi">:</span> <span class="s2">"</span><span class="s">application"</span>
      <span class="na">MYSQL_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">password"</span>
</code></pre></div></div>

<p>O serviço de banco de dados está definindo a imagem a ser utilizada e as variáveis de ambiente. Nós não fazemos modificações na imagem.</p>

<p>Mas nossa aplicação precisa de customização (definida em nosso Dockerfile).</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">build: application</code> define onde o nosso Dockerfile está. Nossa imagem será criada com base nele.</li>
  <li><code class="language-plaintext highlighter-rouge">links:</code> é a lista de conexões entre nossos serviços. Nossa aplicação precisa se conectar ao banco de dados.</li>
  <li><code class="language-plaintext highlighter-rouge">ports:</code> é a lista de portas conectadas com o host (seu computador). Nesse exemplo, se acessar <code class="language-plaintext highlighter-rouge">localhost:5000</code> no meu navegador local, ele irá responder com o <code class="language-plaintext highlighter-rouge">localhost:5000</code> do container.</li>
  <li><code class="language-plaintext highlighter-rouge">environment:</code> é a lista com a variáveis de ambiente passadas para o container.</li>
  <li><code class="language-plaintext highlighter-rouge">volumes:</code> é a lista de volumes utilizados. Aqui, estou montando o código-fonte na imagem. A razão para isso é que se eu realizar mudanças no host, elas serão atualizadas no container. Também porque configurei no flask <code class="language-plaintext highlighter-rouge">FLASK_DEBUG: "1"</code>.</li>
</ul>

<h2 id="rodando">Rodando</h2>

<p>Agora você precisa apenas rodar com o comando <code class="language-plaintext highlighter-rouge">docker-compose up</code>.</p>

<p>Página estática</p>

<p><img src="/assets/posts/2019-09-05-learning-docker-compose/application.png" alt="application" /></p>

<p>Página fazendo conexão com o banco de dados</p>

<p><img src="/assets/posts/2019-09-05-learning-docker-compose/database.png" alt="database" /></p>

<h2 id="alguns-truques">Alguns truques</h2>

<h3 id="rodar-em-background">Rodar em background</h3>

<p>Para rodar em background basta adicionar uma flag <code class="language-plaintext highlighter-rouge">docker-compose up -d</code>. Seu terminal não estará preso ao docker. Se você quiser ver os logs, rode <code class="language-plaintext highlighter-rouge">docker-compose logs</code> (para ver todos os logs), <code class="language-plaintext highlighter-rouge">docker-compose logs -f</code> (para ver todos os logs e seguir os seguintes) ou <code class="language-plaintext highlighter-rouge">docker-compose logs application</code> (para filtrar os logs de um determinado serviço).</p>

<h3 id="compose-network">Compose network</h3>

<p>Não sei se você percebeu, mas não precisei mapear o hostname do banco de dados. O Docker já o criou para mim.</p>

<p>Isso aconteceu porque ligamos os serviços:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    links:
      - database
</code></pre></div></div>

<p>No caso, usamos o mesmo nome do serviço como db hostname da aplicação.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>host='database',
</code></pre></div></div>

<h3 id="debug-container">Debug container</h3>

<p>Você também pode inspecionar o container roando o comando <code class="language-plaintext highlighter-rouge">docker-compose exec application bash</code>.</p>

<h3 id="mudar-portas">Mudar portas</h3>

<p>Você não precisa usar a porta 5000 de seu host. Você pode escoolher qualquer outra. (exemplo: no arquivo <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>, você poderia mudar para <code class="language-plaintext highlighter-rouge">8000:5000</code>)</p>

<h3 id="mudando-o-dockerfile">Mudando o Dockerfile</h3>

<p>Se você fizer mudanças que alterem a imagem gerada, será necessário build da imagem.</p>

<p>Por exemplo, se você começar seu projeto (<code class="language-plaintext highlighter-rouge">docker-compose up</code>) e depois atualizar o <code class="language-plaintext highlighter-rouge">requirements.txt</code>.</p>

<p>Você pode fazer isso rodando <code class="language-plaintext highlighter-rouge">docker-compose build</code>.</p>]]></content><author><name></name></author><category term="docker" /><category term="compose" /><summary type="html"><![CDATA[Ensina docker-compose criando do zero uma aplicação web Flask com banco de dados MariaDB.]]></summary></entry><entry xml:lang="pt_br"><title type="html">Começando com Docker</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/starting-with-docker" rel="alternate" type="text/html" title="Começando com Docker" /><published>2019-08-28T11:00:00+00:00</published><updated>2019-08-28T11:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/starting-with-docker</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/starting-with-docker"><![CDATA[<h1 id="como-fazer-um-pr">Como fazer um PR</h1>

<p>Nesse post, explicarei um pouco como usar o Docker. Basicamente, vou abordar o comando <code class="language-plaintext highlighter-rouge">run</code> a alguns truques.</p>

<h2 id="run">Run</h2>

<p>Esse comando vai rodar um container com uma imagem já existente.</p>

<p>Se você quiser rodar o Ubuntu localmente, deve rodar apenas <code class="language-plaintext highlighter-rouge">docker run ubuntu</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run -it ubuntu
root@aacdec8ea1e8:/# echo test
test
root@aacdec8ea1e8:/#
</code></pre></div></div>

<p>A primeira linha (<code class="language-plaintext highlighter-rouge">docker run -it ubuntu</code>) rodou no meu host (meu Sistema Operacional, não no container docker).
A segunda linha (<code class="language-plaintext highlighter-rouge">echo test</code>) rodou no container Ubuntu.</p>

<p>P.S.: A flag <code class="language-plaintext highlighter-rouge">-it</code> é para conectar o seu terminao com o container (para ver o que acontece dentro do container).</p>

<p>Poderiamos user outras imagens (debian, centos, python…). No <a href="https://hub.docker.com/">Docker Hub</a> você pode encontrar diversas outras imagens.</p>

<h3 id="usar-outra-versão">Usar outra versão</h3>

<p>Também é possível usar diferentes versões do Ubuntu. Nós precisamos apenas usar uma tag diferente para a versão.</p>

<p>Podemos usar o Ubuntu 19.04 usando o comando <code class="language-plaintext highlighter-rouge">docker run ubuntu:19.04</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run -it ubuntu:19.04
root@bc620ad9745b:/# cat /etc/*release | grep VERSION_ID
VERSION_ID="19.04"
root@bc620ad9745b:/# exit
exit
$ docker run -it ubuntu
root@6acfdb1bff1d:/# cat /etc/*release | grep VERSION_ID
VERSION_ID="18.04"
</code></pre></div></div>

<p>Por padrão, usa-se a tag <code class="language-plaintext highlighter-rouge">latest</code>, ou seja, rodar o comando <code class="language-plaintext highlighter-rouge">docker run ubuntu</code> é o mesmo que rodar <code class="language-plaintext highlighter-rouge">docker run ubuntu:latest</code>. Hoje, o <code class="language-plaintext highlighter-rouge">latest</code> é o mesmo que <code class="language-plaintext highlighter-rouge">18.04</code>. No lançamento do próximo Ubuntu LTS a versão latest será a <code class="language-plaintext highlighter-rouge">20.04</code>.</p>

<h3 id="mudar-o-comando-padrão">Mudar o comando padrão</h3>

<p>Cada imagem tem um comando padrão para rodar. Quando usamos <code class="language-plaintext highlighter-rouge">docker run -it ubuntu</code>, ele automaticamente roda <code class="language-plaintext highlighter-rouge">bash</code>. Mas quando usamos uma imagem python, ele roda o comando <code class="language-plaintext highlighter-rouge">python</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run --rm -it python
Python 3.7.4 (default, Aug 14 2019, 12:09:51)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; print('hello')
hello
</code></pre></div></div>

<p>Você pode mudar o comando padrão. Por exemplo, caso queira instalar algo antes do comando padrão.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run --rm -it python bash
root@1db1de2e11cc:/# pip install awscli
Collecting awscli
  Downloading https://files.pythonhosted.org/packages/97/72/bafa0c932416fb91c87f172cd4b0fdff5be32e4d01a3c89c8ae03549d7c3/awscli-1.16.229-py2.py3-none-any.whl (2.0MB)
     |████████████████████████████████| 2.0MB 2.2MB/s
...
Building wheels for collected packages: PyYAML
  Building wheel for PyYAML (setup.py) ... done
  Created wheel for PyYAML: filename=PyYAML-5.1.2-cp37-cp37m-linux_x86_64.whl size=468680 sha256=df0859d47787aaf4e7b2b2e1f5720ab061297d9b3c2519b32f5254217b77fd51
  Stored in directory: /root/.cache/pip/wheels/d9/45/dd/65f0b38450c47cf7e5312883deb97d065e030c5cca0a365030
Successfully built PyYAML
Installing collected packages: pyasn1, rsa, docutils, six, python-dateutil, urllib3, jmespath, botocore, s3transfer, colorama, PyYAML, awscli
Successfully installed PyYAML-5.1.2 awscli-1.16.229 botocore-1.12.219 colorama-0.3.9 docutils-0.15.2 jmespath-0.9.4 pyasn1-0.4.6 python-dateutil-2.8.0 rsa-3.4.2 s3transfer-0.2.1 six-1.12.0 urllib3-1.25.3
root@1db1de2e11cc:/# python
Python 3.7.4 (default, Aug 14 2019, 12:09:51)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; import awscli
&gt;&gt;&gt; # Do things with awscli
</code></pre></div></div>

<h3 id="conectar-de-um-terminal-diferente">Conectar de um terminal diferente</h3>

<p>Vamos rodar o servidor em um terminal e criar arquivos em outro.</p>

<p>Terminal 1</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run -it -p 8000:8000 python bash
root@1e171a55d01f:/# python -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
</code></pre></div></div>

<p>Terminal 2</p>

<p>Execute o comando <code class="language-plaintext highlighter-rouge">ps</code> para obter o container ID e se conectar ao container usando o comando <code class="language-plaintext highlighter-rouge">exec</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                                              NAMES
1e171a55d01f        python                 "bash"                   42 seconds ago      Up 41 seconds       0.0.0.0:8000-&gt;8000/tcp                             stupefied_heyrovsky

$ docker exec -it 1e171a55d01f bash
root@1e171a55d01f:/# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var
root@1e171a55d01f:/# echo 'My site' &gt; index.html
</code></pre></div></div>

<p>Se você acessar <a href="http://localhost:8000/">localhost:8000</a> verá a página simples que acabou de ser criada.</p>

<p><img src="/assets/posts/2019-08-28-starting-with-docker/my-site.png" alt="My site example" /></p>

<h2 id="dicas">Dicas</h2>

<h3 id="usar-a-flag-rm">Usar a flag –rm</h3>

<p>Quando você roda esses comandos os containers são criados mas não apagados depois de utilizá-los. No fim, você terá diversos containers antigos ocupando espaço.</p>

<p>Você pode encontrar esses containers rodando <code class="language-plaintext highlighter-rouge">docker ps -a</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker ps -a
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                      PORTS                                              NAMES
1e171a55d01f        python                 "bash"                   10 minutes ago      Up 10 minutes               0.0.0.0:8000-&gt;8000/tcp                             stupefied_heyrovsky
6acfdb1bff1d        ubuntu                 "/bin/bash"              2 hours ago         Exited (0) 22 minutes ago                                                      cranky_booth
bc620ad9745b        ubuntu:19.04           "/bin/bash"              2 hours ago         Exited (0) 2 hours ago                                                         gracious_hopper
</code></pre></div></div>

<p>Se você rodar a flag <code class="language-plaintext highlighter-rouge">--rm</code> isso não acontecerá. Por exemplo: <code class="language-plaintext highlighter-rouge">docker run -it --rm ubuntu</code>.</p>

<h3 id="limpar-imagens">Limpar Imagens</h3>

<p>Você também pode limpar espaço rodando o comando <code class="language-plaintext highlighter-rouge">prune</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker system prune -a
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all images without at least one container associated to them
  - all build cache

Are you sure you want to continue? [y/N] y
...
</code></pre></div></div>

<p>Por agora, esse comando não tempo problema pois não criamos muitas coisas. Mas precisa tomar cuidado para não apagar nada importante (se por acaso algo importante estiver rodando, ele não será apagado).</p>

<h2 id="resumo">Resumo</h2>

<p>Esse é apenas o básico de tarefas com o docker. Espero que você tenha entendido um pouco mais sobre ele.</p>

<p>Outra possibilidade é rodar 2 containers (por exemplo, um site PHP e um banco de dados). Eu expliquei no <a href="https://stackoverflow.com/questions/43127599/connect-to-mysql-server-from-php-with-docker/43128428#43128428">StackOverflow</a> como fazer isso.</p>

<p>Isso é interessante para o aprendizado, mas eu prefiro usar o docker-compose para isso.</p>

<p>Veja o post sobre <a href="/pt_br/blog/learning-docker-compose">docker-compose</a>.</p>]]></content><author><name></name></author><category term="docker" /><summary type="html"><![CDATA[Explica os fundamentos do Docker, focando no comando run e truques básicos para começar a usar containers.]]></summary></entry><entry xml:lang="pt_br"><title type="html">How to make a PR</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/how-to-make-a-pr" rel="alternate" type="text/html" title="How to make a PR" /><published>2018-12-23T11:00:00+00:00</published><updated>2018-12-23T11:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/how-to-make-a-pr</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/how-to-make-a-pr"><![CDATA[<h1 id="como-fazer-um-pr">Como fazer um PR</h1>

<p>Esse tutotial ensina como fazer um PR (Pull Request, que é integrar seu código git)</p>

<h2 id="pegue-o-código">Pegue o código</h2>

<p>Usarei o meu repositório <a href="https://gitlab.com/rafaelhdr/pr-example">pr tutorial repository</a> como examplo. Você pode usá-lo, mas eu recomendo que você use o seu próprio.</p>

<p><code class="language-plaintext highlighter-rouge">git clone git@gitlab.com:rafaelhdr/pr-example.git</code></p>

<h3 id="use-código-recente">Use código recente</h3>

<p><em>(você pode pular esse passo, se clonou o repositório)</em></p>

<p>A maioria do tempo você não estará clonando um repositório, mas usando um já existente. Nesse caso, vá para a branch master e atualize o código.</p>

<p><code class="language-plaintext highlighter-rouge">git checkout master</code> para ir a branch master</p>

<p><code class="language-plaintext highlighter-rouge">git pull origin master</code> para atualizar com o código mais recente</p>

<h2 id="branch">Branch</h2>

<p>Vamos criar um novo branch com as mudanças. Criaremos o novo branch, e faremos um Pull Reqquest para integrar o código com suas mudanças à branch master (normalmente master, mas pode ser outra).</p>

<p><code class="language-plaintext highlighter-rouge">git branch make-chapter-2</code> para criar a nova branch make-chapter-2</p>

<p><code class="language-plaintext highlighter-rouge">git checkout make-chapter-2</code> para ir a essa nova branch</p>

<blockquote>
  <p>Dica: Os dois comandos acima poderiam ser substituídos por <code class="language-plaintext highlighter-rouge">git branch -b make-chapter-2</code></p>
</blockquote>

<p>Mude algo no código (examplo: adicione um parágrapho lorem ipsum a content.txt).</p>

<p><code class="language-plaintext highlighter-rouge">git add content.txt</code> para adicionar o código ao commit</p>

<p><code class="language-plaintext highlighter-rouge">git commit -m "make chapter 2"</code> para criar um commit com essas mudanças</p>

<p>Agora, seu código local está atualizado. We precisamos apenas colocar no servidor (no meu caso, GitLab).</p>

<p><code class="language-plaintext highlighter-rouge">git push origin make-chapter-2</code></p>

<h2 id="pronto">Pronto</h2>

<p>O código está pronto. Você pode vê-lo no GitLab (e integrar de lá).</p>]]></content><author><name></name></author><category term="git" /><category term="gitlab" /><summary type="html"><![CDATA[Ensina como fazer um Pull Request (PR) no GitLab, desde clonar o repositório até submeter o merge.]]></summary></entry><entry xml:lang="pt_br"><title type="html">Deploy de aplicação hello world no Kubernetes GKE</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/deploying-hello-world-application-to-gke" rel="alternate" type="text/html" title="Deploy de aplicação hello world no Kubernetes GKE" /><published>2018-06-08T11:00:00+00:00</published><updated>2018-06-08T11:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/deploying-hello-world-application-to-gke</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/deploying-hello-world-application-to-gke"><![CDATA[<h1 id="deploy-de-aplicação-hello-world-no-kubernetes-gke">Deploy de aplicação hello world no Kubernetes GKE</h1>

<p>Esse tutorial é para deploy de uma aplicação simple no <a href="https://cloud.google.com/kubernetes-engine/">Google Kubernetes Engine</a>.</p>

<p>Mas para começar, você precisa de:</p>

<ul>
  <li><a href="https://cloud.google.com/sdk/">gcloud</a> linha de comando instalada e configurada</li>
  <li><a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/">kubectl</a> linha de comando instalada</li>
</ul>

<p>É melhor se você já souber os básico de Docker. Eu criei uma imagem <code class="language-plaintext highlighter-rouge">rafaelhdr/server-hello</code> para isso. Essa imagem apenas responde <code class="language-plaintext highlighter-rouge">Hello, World!</code> para <code class="language-plaintext highlighter-rouge">GET /</code>.</p>

<h2 id="crie-o-cluster">Crie o cluster</h2>

<p><code class="language-plaintext highlighter-rouge">gcloud container clusters create hello-cluster</code></p>

<p>Esse comando irá criar seu cluster kubernetes. Depois de um tempo irá aparecer:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
NAME           LOCATION       MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
hello-cluster  us-central1-a  1.8.10-gke.0    35.226.200.163  n1-standard-1  1.8.10-gke.0  3          RUNNING
</code></pre></div></div>

<h2 id="deploy-da-aplicação">Deploy da aplicação</h2>

<p>Nós temos nosso cluster mas nenhuma aplicação rodando. Veja com os comandos abaixo.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl get deployment
No resources found.

<span class="nv">$ </span>kubectl get pods
No resources found.
</code></pre></div></div>

<p>Então vamos colocar nossa aplicação. Crie o arquivo de configuração <code class="language-plaintext highlighter-rouge">sh-service-lb.yaml</code>:</p>

<blockquote>
  <p>Se quiser usar sua própria aplicação, mude <strong>image</strong> e <strong>cointanerPort</strong>.</p>
</blockquote>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">extensions/v1beta1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">sh-deployment</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">sh-deployment</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">sh-deployment</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">sh-deployment</span>
          <span class="na">image</span><span class="pi">:</span> <span class="s">rafaelhdr/server-hello</span>
          <span class="na">ports</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">5000</span>
</code></pre></div></div>

<p>Para deploy em nosso cluster, rode:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl apply <span class="nt">-f</span> sh-deployment.yaml 
deployment.extensions <span class="s2">"sh-deployment"</span> created
</code></pre></div></div>

<p>Fizémos o deploy de nossa aplicação. Você pode checar rodando:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl get deployment
NAME            DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
sh-deployment   2         2         2            2           39s

<span class="nv">$ </span>kubectl get pods
NAME                             READY     STATUS    RESTARTS   AGE
sh-deployment-6db6b8856d-q9j95   1/1       Running   0          53s
sh-deployment-6db6b8856d-tgmmh   1/1       Running   0          53s
</code></pre></div></div>

<p><strong>O que fizémos?</strong> - Criamos duas replicas de nossa aplicação em nosso cluster (<code class="language-plaintext highlighter-rouge">replicas: 2</code>). É por isso que temos 2 pods e um deployment.</p>

<blockquote>
  <p><strong>O que é um Pod?</strong> É a menos unidade que colocamos no Kubernetes. Nós poderíamos ter dois containers rodando juntos, de forma que compartilhariam o mesmo volume.</p>
</blockquote>

<p>O problema é que ele ainda não está exposto. Vamos criar o Load Balancer.</p>

<h2 id="serviço-load-balancer">Serviço Load Balancer</h2>

<p>Nós vamos seguir os mesmos passos (criar arquivo de configuração e <code class="language-plaintext highlighter-rouge">apply</code> usando <code class="language-plaintext highlighter-rouge">kubectl</code>). Crie o arquivo <code class="language-plaintext highlighter-rouge">sh-service-lb.yaml</code>.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">sh-service</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">sh-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">LoadBalancer</span>
  <span class="na">ports</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">80</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">5000</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">sh-deployment</span>
</code></pre></div></div>

<p>Agora nós rodamos o mesmo comando para esse arquivo:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl apply <span class="nt">-f</span> sh-service-lb.yaml
service <span class="s2">"sh-service"</span> created
</code></pre></div></div>

<p><strong>O que fizémos?</strong> Criamos nosso Load Balancer redirecionando requests de acesso externo (porta 80) para nosso container (porta 5000).</p>

<p>Certo. Nosso serviço foi criado, mas precisa de um tempo para iniciar. Espere o EXTERNAL-IP mudar de PENDING para um valor real:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl get services
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>        AGE
kubernetes   ClusterIP      10.15.240.1     &lt;none&gt;        443/TCP        25m
sh-service   LoadBalancer   10.15.241.155   &lt;pending&gt;     80:30138/TCP   1s

<span class="c"># Wait some time</span>

<span class="nv">$ </span>kubectl get services
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT<span class="o">(</span>S<span class="o">)</span>        AGE
kubernetes   ClusterIP      10.15.240.1     &lt;none&gt;          443/TCP        27m
sh-service   LoadBalancer   10.15.241.155   35.192.86.102   80:30138/TCP   1m
</code></pre></div></div>

<p>Meu IP externo é 35.192.86.102. Pegue o seu IP externo e acesse de seu navegador (http://YOUR_EXTERNAL_IP/).</p>

<h2 id="acessando-a-interface-web">Acessando a interface web</h2>

<p>Pegue o token de acesso rodando <code class="language-plaintext highlighter-rouge">kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | awk '/^deployment-controller-token-/{print $1}') | awk '$1=="token:"{print $2}'</code>.</p>

<p>Acesse a interface localmente por um proxy rodando <code class="language-plaintext highlighter-rouge">kubectl config view</code> e abrindo <a href="http://127.0.0.1:8001/ui/">http://127.0.0.1:8001/ui/</a>. Para Sign In, use o token obtido anteriormente.</p>

<h2 id="apague-tudo">Apague tudo</h2>

<p>Certo, fizémos nossos testes. Podemos remover tudo apagando nosso cluster.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gcloud container clusters delete hello-cluster
The following clusters will be deleted.
 - <span class="o">[</span>hello-cluster] <span class="k">in</span> <span class="o">[</span>us-central1-a]

Do you want to <span class="k">continue</span> <span class="o">(</span>Y/n<span class="o">)</span>?  Y

...
</code></pre></div></div>

<h2 id="resumo">Resumo</h2>

<p>Nesse tutorial, criamos um cluster, colocamos nossa aplicação e um load balancer nele, acessamos a interface web e então deletamos esse cluster.</p>

<p>Esse é apenas o começo para o Kubernetes, mas fizémos apenas o básico. Tentarei fazer mais posts sobre Kubernetes. Enquanto isso, você poderia ver <a href="https://medium.freecodecamp.org/learn-kubernetes-in-under-3-hours-a-detailed-guide-to-orchestrating-containers-114ff420e882">Learn Kubernetes in Under 3 Hours: A Detailed Guide to Orchestrating Containers</a>. É um tutorial mais completo que esse, aprendendo os conceitos que testamos aqui mais profundamente, mas em um cluster local.</p>

<h2 id="referências">Referências:</h2>

<ul>
  <li><a href="https://stackoverflow.com/questions/46664104/how-to-sign-in-kubernetes-dashboard">How to sign in kubernetes dashboard?</a></li>
  <li><a href="https://cloud.google.com/kubernetes-engine/docs/quickstart">Quickstart - GKE</a></li>
  <li><a href="https://kubernetes-v1-4.github.io/docs/hellonode/">Hello World Walkthrough</a></li>
</ul>]]></content><author><name></name></author><category term="docker" /><category term="kubernetes" /><category term="gke" /><summary type="html"><![CDATA[Tutorial de deploy de uma aplicação hello world no Google Kubernetes Engine (GKE) usando Docker e kubectl.]]></summary></entry><entry xml:lang="pt_br"><title type="html">Entendendo os parâmetros de python</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/understanding-python-parameters" rel="alternate" type="text/html" title="Entendendo os parâmetros de python" /><published>2018-06-05T11:00:00+00:00</published><updated>2018-06-05T11:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/understanding-python-parameters</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/understanding-python-parameters"><![CDATA[<h1 id="entendendo-os-parâmetros-de-python">Entendendo os parâmetros de python</h1>

<p>Nesse post, vou escrever uma função simples <code class="language-plaintext highlighter-rouge">full_profile</code> explicando alguns conceitos sobre os parâmetros de Python. Con isso, poderemos entender como a famosa função <code class="language-plaintext highlighter-rouge">my_function(*args, **kwargs)</code> funciona.</p>

<h2 id="argumento-de-posição-positional-argument">Argumento de posição (Positional argument)</h2>

<p>Esse é o primeiro tipo de parâmetro que vemos. A sequência de argumentos dada é usada:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">full_profile</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Id: </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Name: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Age: </span><span class="si">{</span><span class="n">age</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">,</span> <span class="mi">30</span><span class="p">)</span>
</code></pre></div></div>

<p>Resultado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Id: 1
Name: rafael
Age: 30
</code></pre></div></div>

<h2 id="argumento-de-palavra-chave-keyword-argument">Argumento de palavra-chave (Keyword argument)</h2>

<p>Algumas vezes é melhor usarmos argumento de palavra-chave para ficar mais legível.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">full_profile</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Id: </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Name: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Age: </span><span class="si">{</span><span class="n">age</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
</code></pre></div></div>

<p>Resultado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Id: 1
Name: rafael
Age: 30
</code></pre></div></div>

<p>Em minha opinião, prefiro argumento de palavra chave pois é mais explícito (mas nem sempre, como por exemplo em <code class="language-plaintext highlighter-rouge">soma(1, 2)</code> por não fazer diferença a ordem).</p>

<h2 id="forçar-argumento-de-palavra-chave">Forçar argumento de palavra-chave</h2>

<p>Se você trocar a ordem dos argumentos, pode ter o seguinte problema:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">full_profile</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Id: </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Name: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Age: </span><span class="si">{</span><span class="n">age</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="c1"># If you change the order for some reason, the result will be wrong
</span><span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Resultado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Id: 1
Name: 30
Age: rafael
</code></pre></div></div>

<p>Você pode forçar o argumento ser de palavra chave usando <code class="language-plaintext highlighter-rouge">*</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">full_profile</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Id: </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Name: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Age: </span><span class="si">{</span><span class="n">age</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="c1"># This will not work anymore
</span><span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">)</span>

<span class="c1"># This will work
</span><span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>

<span class="c1"># This result is the same
</span><span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Resultado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
  File <span class="s2">"&lt;stdin&gt;"</span>, line 2, <span class="k">in</span> &lt;module&gt;
TypeError: full_profile<span class="o">()</span> takes 1 positional argument but 3 were given

Id: 1
Name: rafael
Age: 30

Id: 1
Name: rafael
Age: 30
</code></pre></div></div>

<h2 id="adicionar-argumentos-de-posição-não-definidos">Adicionar argumentos de posição não definidos</h2>

<p>Se quisermos adicionar mais informações passadas pelo usuário, mas não definidas na função, podemos usar args.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">full_profile</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Id: </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Name: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Age: </span><span class="si">{</span><span class="n">age</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span>

<span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="sh">"</span><span class="s">I am a developer</span><span class="sh">"</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
</code></pre></div></div>

<p>Resultado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Id: 1
Name: rafael
Age: 30
I am a developer
</code></pre></div></div>

<p>Agora entendemos os <code class="language-plaintext highlighter-rouge">*args</code> do padrão <code class="language-plaintext highlighter-rouge">my_function(*args, **kwargs)</code>.</p>

<blockquote>
  <p>Nós não precisamos usar o nome <code class="language-plaintext highlighter-rouge">args</code>. Poderia ser algum outro nome, como <code class="language-plaintext highlighter-rouge">extra</code>. É apenas umas convenção.</p>
</blockquote>

<h2 id="adicionando-argumentos-de-palavras-chave-não-definidos">Adicionando argumentos de palavras-chave não definidos</h2>

<p>Se precisarmos de mais informações passadas pelo usuário, mas não definidos pela função, podemos usar kwargs.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">full_profile</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Id: </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Name: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Age: </span><span class="si">{</span><span class="n">age</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="p">.</span><span class="nf">items</span><span class="p">():</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">key</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="sh">"</span><span class="s">I am a developer</span><span class="sh">"</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span> <span class="n">Company</span><span class="o">=</span><span class="sh">"</span><span class="s">rafaelhdr</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Resultado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Id: 1
Name: rafael
Age: 30
I am a developer
Company: rafaelhdr
</code></pre></div></div>

<p>E agora entendemos o <code class="language-plaintext highlighter-rouge">**kwargs</code> do padrão <code class="language-plaintext highlighter-rouge">my_function(*args, **kwargs)</code>.</p>

<blockquote>
  <p>Novamente, <code class="language-plaintext highlighter-rouge">kwargs</code> é a convenção, mas você pode escolher outro nome.</p>
</blockquote>

<p>Se usarmos o padrão <code class="language-plaintext highlighter-rouge">my_function(*args, **kwargs)</code> ele aceitará todos os casos. Mas os argumentos são todos opcionais. Se definirmos ele na função e o usuário não fornecê-lo, será levantado um erro:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">full_profile</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Id: </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Name: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Age: </span><span class="si">{</span><span class="n">age</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="p">.</span><span class="nf">items</span><span class="p">():</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">key</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="c1"># age is removed of the arguments
</span><span class="nf">full_profile</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="sh">"</span><span class="s">I am a developer</span><span class="sh">"</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">"</span><span class="s">rafael</span><span class="sh">"</span><span class="p">,</span> <span class="n">Company</span><span class="o">=</span><span class="sh">"</span><span class="s">rafaelhdr</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Resultado:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
TypeError: full_profile() missing 1 required keyword-only argument: 'age'
</code></pre></div></div>

<h1 id="args-e-kwargs">args e kwargs</h1>

<p>Podemos pensar em <code class="language-plaintext highlighter-rouge">*args</code> como uma lista de <strong>argumentos de posição</strong> não definidos e <code class="language-plaintext highlighter-rouge">**kwargs</code> é um Dicionário de <strong>argumentos de palavra-chave</strong> não definidos.</p>

<h2 id="referência">Referência</h2>

<p><a href="https://docs.python.org/3/glossary.html#term-parameter">Python Glossary - term parameter</a></p>]]></content><author><name></name></author><category term="python" /><summary type="html"><![CDATA[Explica os tipos de parâmetros em Python (posicionais, keyword, *args, **kwargs) com exemplos progressivos.]]></summary></entry><entry xml:lang="pt_br"><title type="html">vim para limpar tags HTML</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/vim-regex-for-cleaning-html-tags" rel="alternate" type="text/html" title="vim para limpar tags HTML" /><published>2018-05-21T21:00:00+00:00</published><updated>2018-05-21T21:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/vim-regex-for-cleaning-html-tags</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/vim-regex-for-cleaning-html-tags"><![CDATA[<h1 id="vim-para-limpar-tags-html">VIM para limpar tags HTML</h1>

<p>Você tem um arquivo muito grande, e quer extrair algum valor em específico. Você pode usar substituição com expressão regular.</p>

<blockquote>
  <p>Certo, não é apenas com VIM. Você poderia usar o grep também. Estou usando VIM pela facilidade no post.</p>
</blockquote>

<blockquote>
  <p>Sim, você poderia usar um crawler para isso. No meu caso, eu só queria extrair esses valores de um único arquivo.</p>
</blockquote>

<p><strong>TL;DR</strong></p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:%g!/useful-class/d
:%s/&lt;a.*href="\([^"]*\).*\n/\1\r/g
</code></pre></div></div>

<h2 id="arquivo-de-teste">Arquivo de teste</h2>

<p>Para testar, você pode abrir o seguinte arquivo no VIM:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SOME BIG HTML
<span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"useful-class"</span> <span class="na">href=</span><span class="s">"http://example/1"</span><span class="nt">&gt;</span>Example 1<span class="nt">&lt;/a&gt;</span>
NOT USEFUL LINE
<span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"useful-class"</span> <span class="na">href=</span><span class="s">"http://example/2"</span><span class="nt">&gt;</span>Example 2<span class="nt">&lt;/a&gt;</span>
END OF BIG HTML
</code></pre></div></div>

<p>Em nosso exemplo, queremos remover as linhas inúteis e deixar apenas a URL.</p>

<h2 id="comandos">Comandos</h2>

<p>Limpar as linhas inúteis:</p>

<p><code class="language-plaintext highlighter-rouge">:%g!/useful-class/d</code></p>

<ul>
  <li><strong>%g!</strong> Fazer algo em todas as linhas que não contenham certa expressão</li>
  <li><strong>useful-class</strong> A expressão que estamos procurando. No caso, queremos encontrar useful-class</li>
  <li><strong>d</strong> Definimos que queremos deletar quando não encontrar a expressão</li>
</ul>

<p>Remover conteúdo inútil:</p>

<p><code class="language-plaintext highlighter-rouge">:%s/&lt;a.*href="\([^"]*\).*\n/\1\r/g</code></p>

<ul>
  <li><strong>%s</strong> Substituir todas as linhas que possuam certa expressão</li>
  <li>Expressão (esse é longo):
    <ul>
      <li><strong>&lt;a</strong> Que começa com <code class="language-plaintext highlighter-rouge">&lt;a</code></li>
      <li><strong>.*</strong> Com qualquer caracter até…</li>
      <li><strong>href=”</strong> …até que encontre <code class="language-plaintext highlighter-rouge">href="</code></li>
      <li><strong>\([^”]*\)</strong> e a primeira parte (chamada de “atom”) é qualquer coisa que não contenha aspas(<code class="language-plaintext highlighter-rouge">[^"]*</code> sem aspas). O “atom” é o conteúdo entre os parênteses(<code class="language-plaintext highlighter-rouge">(...)</code>)</li>
      <li><strong>.*</strong> e qualquer caracter até…</li>
      <li><strong>\n</strong> …até que encontre uma nova linha <code class="language-plaintext highlighter-rouge">\n</code></li>
    </ul>
  </li>
  <li><strong>\1\r</strong> e substituir pelo primeiro “atom” e uma nova linha</li>
  <li><strong>g</strong> rodar o comando</li>
</ul>

<h2 id="done">Done</h2>

<p>O resultado é esse:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http://example/1
http://example/2
</code></pre></div></div>

<p>Não é simples, mas da segunda vez é mais fácil de lembrar.</p>

<p>Eu usei hoje para limpar um simples arquivo longo (copiei o DOM <code class="language-plaintext highlighter-rouge">&lt;ul&gt;&lt;li&gt;&lt;a&gt;...&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</code>) e rodei os comandos (ao mesmo tempo que ia aprendendo também).</p>

<h2 id="referências">Referências</h2>

<ul>
  <li><a href="http://vim.wikia.com/wiki/Delete_all_lines_containing_a_pattern">Delete all lines containing a pattern</a></li>
  <li><a href="https://stackoverflow.com/questions/11624166/replace-while-keeping-certain-words-in-vi-vim">Replace while keeping certain “words” in vi/vim</a></li>
  <li><a href="https://stackoverflow.com/questions/71323/how-to-replace-a-character-by-a-newline-in-vim">How to replace a character by a newline in Vim?</a></li>
</ul>]]></content><author><name></name></author><category term="vim" /><category term="regex" /><category term="html" /><category term="tag" /><summary type="html"><![CDATA[Demonstra como usar substituições com regex no VIM para extrair valores específicos de tags HTML em arquivos grandes.]]></summary></entry></feed>