<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">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>2025-01-31T19:38:49+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">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[Como realizar um ataque de dicionário para quebrar uma senha]]></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[Django queryset annotated timezone]]></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[Aprendendo docker compose]]></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[Como fazer um PR]]></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[Como fazer um PR]]></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[Deploy de aplicação hello world no Kubernetes GKE]]></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[Entendendo os parâmetros de python]]></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[VIM para limpar tags HTML]]></summary></entry><entry xml:lang="pt_br"><title type="html">rafaelhdr freelancer skills</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/rafaelhdr-freelancer-skills" rel="alternate" type="text/html" title="rafaelhdr freelancer skills" /><published>2018-05-17T11:00:00+00:00</published><updated>2018-05-17T11:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/rafaelhdr-freelancer-skills</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/rafaelhdr-freelancer-skills"><![CDATA[<h1 id="rafaelhdr-desenvolvedor-freelancer">rafaelhdr desenvolvedor freelancer</h1>

<p>Olá, meu nome é Rafael e sou desenvolvedor web desde 2003. Se você está aqui, talvez esteja interessado em me contratar como desenvolvedor, então gostaria de falar um pouco mais sobre minhas habilidades.</p>

<blockquote>
  <p>No texto, estou adicionando alguns links a projetos open-source relacionados.</p>
</blockquote>

<p><strong>Minha principal habilidade é de backend.</strong> Eu gosto das linguagens Node.js e Python. Você poderia me contratar também para desenvolver em <a href="https://gitlab.com/shereland/graphql">Golang</a>, mas ainda sou iniciante em Go. Tenho experiência desenvolvendo aplicações monolíticas com Django e também Microserviços usando <a href="https://gitlab.com/shereland/web">Express</a>, Flask e <a href="https://github.com/rafaelhdr/portfolio-wall-app">Django</a>.</p>

<p><strong>Também sei frontend.</strong> Possuo as habilidades básicas de HTML/CSS/js e também desenvolvo usando React, tanto para <a href="https://github.com/rafaelhdr/portfolio-wall-app">single-page application (<code class="language-plaintext highlighter-rouge">create-react-app my-app</code>)</a> como adicionando componentes em <a href="https://gitlab.com/shereland/web">uma aplicação já existente</a>.</p>

<p><strong>DevOps é algo que gosto bastante.</strong> Comecei aprendendo Linux alguns anos atrás (~desde 2013) e Docker desde 2016. Hoje em dia, tento sempre desenvolver minhas aplicações utilizando <a href="https://gitlab.com/shereland">GitLab CI e Docker Swarm</a>.</p>

<p><a href="/pt_br/experience">Veja minha trajetoria</a></p>

<p>Já trabalhei com Startups (Dagood, buscador de bares, baladas e restaurantes, e Helpin, para contratar prestadores de serviços) e na área de Inovação (LARC/Scopus, um laboratório da USP desenvolvendo e pesquisando para a Scopus, que é uma empresa parceira do Bradesco).</p>

<p>E-mail: <a href="&quot;mailto:contato@rafaelhdr.com.br&quot;">contato@rafaelhdr.com.br</a>.</p>]]></content><author><name></name></author><category term="rafaelhdr" /><category term="freelancer" /><summary type="html"><![CDATA[rafaelhdr desenvolvedor freelancer]]></summary></entry><entry xml:lang="pt_br"><title type="html">Editar commit antigo do git</title><link href="https://www.rafaelhdr.com.br/pt_br/blog/edit-git-previous-commit" rel="alternate" type="text/html" title="Editar commit antigo do git" /><published>2018-05-13T21:00:00+00:00</published><updated>2018-05-13T21:00:00+00:00</updated><id>https://www.rafaelhdr.com.br/blog/edit-previous-git-commit</id><content type="html" xml:base="https://www.rafaelhdr.com.br/blog/edit-git-previous-commit"><![CDATA[<h1 id="editar-commit-antigo-do-git">Editar commit antigo do git</h1>

<p>Se você cometeu algum erro em um commit anterior do git e quer editá-lo, você pode usar o git rebase para isso.</p>

<p><em>Não é recomendado se você já subiu com</em> <code class="language-plaintext highlighter-rouge">git push</code></p>

<p><strong>tl;dr</strong> <code class="language-plaintext highlighter-rouge">git rebase -i COMMITHASH</code></p>

<p>Vou explicar como fazer com um exemplo completo:</p>

<h2 id="criando-o-cenário">Criando o cenário</h2>

<p>Com os comandos abaixo, você terá três commits.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git init
<span class="nb">echo</span> <span class="s2">"first commit"</span> <span class="o">&gt;</span> file0.txt
git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"first commit"</span>
<span class="nb">echo</span> <span class="s2">"some code
password forgotten
more code"</span> <span class="o">&gt;</span> file1.txt
git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"commit 1"</span>
<span class="nb">echo</span> <span class="s2">"more code"</span> <span class="o">&gt;</span> file2.txt
git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"commit 2"</span>
</code></pre></div></div>

<p>Temos três arquivos (file0.txt, file1.txt e file2.txt), mas um deles tem a senha escrita que foi adicionado no “commit 1”.</p>

<h2 id="git-rebase">git rebase</h2>

<p>Podemos usar o git rebase com hash ou então há 2 commits atrás.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git rebase <span class="nt">-i</span> HEAD^2
<span class="c"># OR</span>
git rebase <span class="nt">-i</span> COMMIT_1_HASH
</code></pre></div></div>

<p>Você verá algo assim (com hash diferente);</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pick 3aa4948 commit 1
pick 9e5aadf commit 2

<span class="c"># More code, even explaining the options</span>
</code></pre></div></div>

<p>Nós queremos editar o “commit 1”, então vamos mudar de <strong>pick</strong> para <strong>edit</strong> no “commit 1”:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>edit 3aa4948 commit 1
pick 9e5aadf commit 2
</code></pre></div></div>

<blockquote>
  <p>Poderíamos ter utilizado apenas <code class="language-plaintext highlighter-rouge">e</code> ao invés de <code class="language-plaintext highlighter-rouge">edit</code>.</p>
</blockquote>

<p>E agora estamos no “commit 1”. Vamos remover a linha <code class="language-plaintext highlighter-rouge">password forgotten</code> e refazer o commit.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"some code
more code"</span> <span class="o">&gt;</span> file1.txt
git add <span class="nb">.</span>
git commit <span class="nt">--amend</span> <span class="c"># We are editing an existent commit</span>
</code></pre></div></div>

<p>E está pronto. Nós precisamos deixar o git continuar o processo com o comando <code class="language-plaintext highlighter-rouge">git rebase --continue</code>.</p>

<p>Você verá a seguinte mensagem: <code class="language-plaintext highlighter-rouge">Successfully rebased and updated refs/heads/master.</code></p>

<p>E agora está pronto para o <code class="language-plaintext highlighter-rouge">push</code>.</p>

<h2 id="cuidado">Cuidado</h2>

<p>Se você já fez <code class="language-plaintext highlighter-rouge">push</code> antes, provevalmente terá problemas. É melhor editar o arquivo (e mudar sua própria senha).</p>

<h2 id="nota-sobre-o-primeiro-commit">Nota sobre o primeiro commit</h2>

<p>O primeiro commit não é importante. Por que eu o adicionei? Porque o git rebase seria um pouco diferente.</p>

<p>Se você tentar <code class="language-plaintext highlighter-rouge">git rebase -i HEAD~3</code> terá o seguinte erro:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fatal: Needed a single revision
invalid upstream <span class="s1">'HEAD~3'</span>
</code></pre></div></div>

<p>Precisaria no caso usar <code class="language-plaintext highlighter-rouge">git rebase -i --root</code>.</p>

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

<p><a href="https://git-scm.com/book/en/v2/Git-Branching-Rebasing">Git Rebasing</a></p>]]></content><author><name></name></author><category term="git" /><category term="rebase" /><summary type="html"><![CDATA[Editar commit antigo do git]]></summary></entry></feed>