<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Aristides Neto]]></title><description><![CDATA[Compartilhando conhecimento de DevOps, SRE, Kubernetes, Programação e um pouco mais...]]></description><link>https://aristides.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Apr 2026 09:39:40 GMT</lastBuildDate><atom:link href="https://aristides.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Kubernetes Scheduling - Técnicas de agendamento]]></title><description><![CDATA[Um dos principais recursos do Kubernetes é seu sistema de Scheduling, responsável por alocar recursos de maneira eficiente para os contêineres em execução. Em termos simples, o Scheduling no Kubernetes é o processo pelo qual o sistema decide em qual ...]]></description><link>https://aristides.dev/kubernetes-scheduling-tecnicas-de-agendamento</link><guid isPermaLink="true">https://aristides.dev/kubernetes-scheduling-tecnicas-de-agendamento</guid><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sat, 02 Nov 2024 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048957383/2dc51451-2a12-4292-8094-919df464d6de.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Um dos principais recursos do Kubernetes é seu sistema de Scheduling, responsável por alocar recursos de maneira eficiente para os contêineres em execução. Em termos simples, o Scheduling no Kubernetes é o processo pelo qual o sistema decide em qual nó (node) um contêiner específico deve ser executado, levando em consideração vários critérios, como recursos disponíveis, requisitos de aplicativos e políticas de alocação.</p>
<p>Ao compreender como o Scheduling funciona no Kubernetes, os desenvolvedores e administradores de sistemas podem otimizar a utilização dos recursos, garantir a escalabilidade e a confiabilidade dos aplicativos, além de facilitar a manutenção e a implantação contínua.</p>
<blockquote>
<p>Este artigo é um resumo do curso que fiz sobre Kubernetes. Decidi focar no tema de <em>Scheduling</em> por considerá-lo essencial para entender como o Kubernetes gerencia a alocação de recursos e organiza os pods nos nós do cluster, revelando aspectos fundamentais do funcionamento interno da plataforma.</p>
</blockquote>
<h2 id="heading-agendamento-manual">Agendamento manual</h2>
<p>O agendamento manual de um pod no cluster se dá quando o serviço do <code>kube-scheduler</code> não está em execução no momento, que é o serviço padrão do Kubernetes para o escalonamento, nesse caso o agendamento não será possível de forma automática.</p>
<p>Para isso, é possível informar manualmente na definição do arquivo de manifesto em qual nó o pod em questão será agendado, isso é feito durante a criação do pod.</p>
<p>Na definição do arquivo do pod, use a propriedade <code>nodeName</code> com o valor do nome do nó, por exemplo <code>node01</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># file: myapp.yaml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">labels:</span> 
    <span class="hljs-attr">app:</span> <span class="hljs-string">nginx</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
      <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8080</span>
  <span class="hljs-attr">nodeName:</span> <span class="hljs-string">node01</span>
</code></pre>
<p>Agendamento manual de um pod usando o atributo nodeName</p>
<p>Com esse manifesto, podemos aplicar ao cluster e veremos que o pod será escalado no nó <code>node01</code>, o mesmo que informamos manualmente.</p>
<pre><code class="lang-bash">kubectl apply -f myapp.yaml
<span class="hljs-string">'pod/nginx'</span> created.

kubectl get pods -o wide
NAME         READY   STATUS      RESTARTS   AGE    IP            NODE
nginx        1/1     Running     0          35s    10.244.0.4    node01
</code></pre>
<h2 id="heading-labels-e-selectors">Labels e Selectors</h2>
<h3 id="heading-labels">Labels</h3>
<p>As <strong>labels</strong> (rótulos) são pares chave/valor que são adicionados no objeto com o objetivo de agrupá-los. Você pode usar para especificar atributos de identificação dos objetos que podem ser relevantes para os usuários, por exemplo, agrupar serviços por seu ambiente com uma label <code>environment=dev</code> , ou um conjunto de aplicações que executam rotinas de processamento de vídeo, poderia conter uma label <code>tier=video-processing</code>. Labels também pode ser usado para selecionar subconjuntos de objetos.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">environment:</span> <span class="hljs-string">"dev"</span>
    <span class="hljs-attr">tier:</span> <span class="hljs-string">"video-processing"</span>
</code></pre>
<p>Definição de labels</p>
<h3 id="heading-selectors">Selectors</h3>
<p>Os <strong>selectors</strong> (seletores), são filtros que podem ser aplicados em cima das labels existentes de um objeto, através dos critérios dos filtros você teria o resultado dos grupos de pods.</p>
<p>Temos abaixo o manifesto de um pod usando a imagem do Nginx, com duas labels e um pod usando a imagem do Mysql. A seguir podemos ver como interagir com os objetos usando os seletores.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">webserver</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">environment:</span> <span class="hljs-string">dev</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">nginx</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">environment:</span> <span class="hljs-string">prod</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">mysql</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mysql</span>
    <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3306</span>
</code></pre>
<p>Definição de Pods com Nginx e Mysql</p>
<p>Podemos listar todas as labels adicionadas à um objeto com o parâmetro <code>--show-labels</code>.</p>
<pre><code class="lang-bash">kubectl get pods --show-labels
NAME               READY   STATUS             RESTARTS           AGE   LABELS
nginx              1/1     Running            0                     2m    app=nginx,enviroment=dev
</code></pre>
<p>Para listar todos os objetos filtrando por uma determinada label, pode ser usado a flag <code>--selector</code> ou <code>-l</code> que é a sua abreviação. Os seguintes operadores são suportados: <code>'='</code> , <code>'=='</code> e <code>'!='</code>.</p>
<pre><code class="lang-bash">kubectl get pods -l app=nginx
NAME        READY   STATUS    RESTARTS   AGE
webserver   1/1     Running   0          32s

kubectl get pods --selector app!=nginx
NAME    READY   STATUS    RESTARTS   AGE
mysql   1/1     Running   0          41s

kubectl get pods --selector app=nginx,environment=dev
NAME        READY   STATUS    RESTARTS   AGE
webserver   1/1     Running   0          47

kubectl get pods --selector app=mysql,environment=dev
No resources found <span class="hljs-keyword">in</span> default namespace.
</code></pre>
<p>Um ponto interessante para entendermos, é que o Kubernetes internamente usa as <code>labels</code> e <code>selector</code> para vincular um <code>replica set</code> em um <code>pod</code>, um <code>service</code> no <code>deployment</code>, e assim por diante.</p>
<h2 id="heading-taints-e-tolerations">Taints e Tolerations</h2>
<p>Os <strong>taints</strong> são uma forma de marcar (ou manchar) um nó para que esse não receba novos pods que não correspondam a um determinado critério (tolerância), ou seja, repelir os pods.</p>
<p>As <strong>tolerations</strong> seriam marcar um pod que seja tolerante a essa restrição, ou seja, para um determinado nó que foi marcado para não receber pods, um pod com tolerations irá ignorar essa restrição e será agendado nesses nós marcados.</p>
<p><strong>Exemplo:</strong></p>
<p>O nó <code>node01</code> for marcado da seguinte forma:</p>
<pre><code class="lang-bash">kubectl taint nodes node01 monitoring=prometheus:NoSchedule
</code></pre>
<p>Dessa forma, somente pods que contenham uma tolerância poderá ser agendado nesse nó. No exemplo a seguir, esse pod contém uma tolerância e será agendado no nó que foi marcado.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">nginx-toleration</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">tolerations:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"monitoring"</span>
    <span class="hljs-attr">operator:</span> <span class="hljs-string">"Equal"</span>
    <span class="hljs-attr">value:</span> <span class="hljs-string">"prometheus"</span>
    <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoSchedule"</span>
</code></pre>
<p>Definição de Pod com tolerância ao nó</p>
<h3 id="heading-vamos-entender-um-pouco-mais">Vamos entender um pouco mais...</h3>
<p>Uma tolerância corresponde a uma mancha (taint) se as chaves forem iguais e os efeitos forem os mesmos, e:</p>
<ul>
<li><p>O <code>operator</code> é <code>Exists</code> (nesse caso, o <code>value</code> não deve ser especificado), ou</p>
</li>
<li><p>O <code>operator</code> é <code>Equal</code> e os valores devem ser iguais.</p>
</li>
</ul>
<p>Para o campo <code>effect</code>, são permitidos os seguintes valores:</p>
<p><code>NoExecute</code>:</p>
<p>Isso afeta os pods que já estão em execução no nó da seguinte maneira:</p>
<ul>
<li><p>Os pods que não toleram a contaminação são despejados imediatamente.</p>
</li>
<li><p>Os pods que toleram a contaminação sem especificar <code>tolerationsSeconds</code> em sua especificação de tolerância permanecem vinculados para sempre.</p>
</li>
<li><p>Os pods que toleram a contaminação com a propriedade <code>tolerationsSeconds</code> especificada, permanecem vinculados pelo período informado, após esse tempo, os pods são despejados dos nós.</p>
</li>
</ul>
<p><code>NoSchedule</code>:</p>
<p>Nenhum pod será agendado no nó contaminado a menos que tenha uma tolerância correspondente. Os pods em execução não são removidos.</p>
<p><code>PreferNoSchedule</code>:</p>
<p>Essa é uma versão preferencial do <code>NoSchedule</code>, o plano de controle tentará <em>evitar</em> colocar o pod que não tolere a contaminação, mas não é garantido.</p>
<h3 id="heading-despejos-baseados-em-contaminacao">Despejos baseados em contaminação</h3>
<p>O controlador de nó contamina automaticamente um nó quando certas condições são verdadeiras. Exemplo:</p>
<ul>
<li><p><a target="_blank" href="http://node.kubernetes.io/not-ready"><code>node.kubernetes.io/not-ready</code></a>: o nó não está pronto, corresponde a condição/status do nó <code>Ready</code> ser <code>false</code>.</p>
</li>
<li><p><a target="_blank" href="http://node.kubernetes.io/unreachable"><code>node.kubernetes.io/unreachable</code></a>: o nó está inacessível pelo controlador de nó, corresponde a condição/status do nó <code>Ready</code> ser <code>Unkown</code>.</p>
</li>
<li><p><a target="_blank" href="http://node.kubernetes.io/memory-pressure"><code>node.kubernetes.io/memory-pressure</code></a>: o nó tem pressão de memória.</p>
</li>
<li><p><a target="_blank" href="http://node.kubernetes.io/disk-pressure"><code>node.kubernetes.io/disk-pressure</code></a>: o nó tem pressão de disco.</p>
</li>
<li><p><a target="_blank" href="http://node.kubernetes.io/pid-pressure"><code>node.kubernetes.io/pid-pressure</code></a>: o nó tem pressão PID.</p>
</li>
<li><p><a target="_blank" href="http://node.kubernetes.io/network-unvailable"><code>node.kubernetes.io/network-unvailable</code></a>: a rede do nó está indisponível.</p>
</li>
<li><p><a target="_blank" href="http://node.kubernetes.io/unschedulable"><code>node.kubernetes.io/unschedulable</code></a>: o nó não é programável.</p>
</li>
</ul>
<p>Os pods que são criados a partir de um <code>DaemonSet</code> são criados com tolerâncias para que evitem de serem removidos no caso de uma contaminação, por exemplo.</p>
<p>O pod já vem com essa duas tolerâncias:</p>
<ul>
<li><p><a target="_blank" href="http://node.kubernetes.io/unreachable"><code>node.kubernetes.io/unreachable</code></a></p>
</li>
<li><p><a target="_blank" href="http://node.kubernetes.io/no-ready"><code>node.kubernetes.io/no-ready</code></a></p>
</li>
</ul>
<h2 id="heading-nodeselector">NodeSelector</h2>
<p>O seletor de nó é a forma mais simples para determinar em qual nó o pod deve ser agendado através de seus rótulos. Você pode adicionar a propriedade <code>nodeSelector</code> à especificação do pod e informar qual o rótulo correspondente para que o pod seja agendado, se vários nós corresponderem ao rótulo informado, o scheduler do Kubernetes decidirá em qual nó o pod será colocado com base em outros fatores, como capacidade de recursos e afinidade.</p>
<p>Para visualizar todos as labels existentes em um nó, use o seguinte comando:</p>
<pre><code class="lang-bash">kubectl get nodes --show-labels

NAME      STATUS    ROLES    AGE     VERSION        LABELS
worker1   Ready     &lt;none&gt;   1d      v1.24.0        ...,kubernetes.io/hostname=worker1
</code></pre>
<p>Para adicionar uma label ao nó, execute o comando:</p>
<pre><code class="lang-bash">kubectl label nodes worker1 disktype=ssd 

kubectl get nodes --show-labels

NAME      STATUS    ROLES    AGE     VERSION        LABELS
worker1   Ready     &lt;none&gt;   1d      v1.24.0        ...,disktype=ssd
</code></pre>
<p>Agora a propriedade <code>nodeSelector</code>, pode ser usada da seguinte maneira:</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">nodeSelector:</span>
    <span class="hljs-attr">disktype:</span> <span class="hljs-string">ssd</span>
</code></pre>
<p>Definição de um Pod com a propriedade nodeSelector</p>
<p>Dessa forma, o pod será agendado em nó que corresponder a esse rótulo.</p>
<h2 id="heading-node-affinity-e-anti-affinity">Node Affinity e anti-affinity</h2>
<p>Afinidade e anti-afinidade expandem os tipos de restrições que você pode definir para o agendamento. Alguns benefícios de usar afinidade e anti-afinidade:</p>
<ul>
<li><p>Poder usar lógicas mais elaboradas para escolher o nó de destino, ao contrário do <code>nodeSelector</code> que apenas nós com as labels especificadas são escolhidas.</p>
</li>
<li><p>Poder escolher se a regra é flexível ou preferencial para que o agendador agende um pod mesmo que não consiga encontrar um nó correspondente.</p>
</li>
</ul>
<h3 id="heading-afinidade-de-no">Afinidade de nó</h3>
<p>A afinidade do nó tem o mesmo conceito da propriedade <code>nodeSelector</code>, que permite restringir em quais nós o pod dever se agendado. Existem dois tipos de afinidade de nó:</p>
<ul>
<li><p><code>requiredDuringSchedulingIgnoredDuringExecution</code>: O agendador irá agendar o pod conforme a regra definida. Caso não encontre um nó que atenda as condições, o pod não será agendado.</p>
</li>
<li><p><code>preferredDuringSchedulingIgnoredDuringExecution</code>: O agendador irá tentar encontrar um nó que atenda a regra definida. Caso não encontre um nó que corresponda as condições, o agendar ainda irá agendar o pod.</p>
</li>
</ul>
<p>Dado o seguinte manifesto, temos:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">pod-affinity</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">affinity:</span>
    <span class="hljs-attr">nodeAffinity:</span>
      <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span>
        <span class="hljs-attr">nodeSelectorTerms:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">matchExpressions:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">disktype</span>
            <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span>
            <span class="hljs-attr">values:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">ssd</span>
</code></pre>
<p>Definição de Pod com a propriedade nodeAffinity</p>
<ul>
<li><p>Este pod só será agendado em nós que tenham o rótulo <code>disktype=ssd</code>.</p>
</li>
<li><p>Se não houver nós com este rótulo, o pod ficará em estado <code>pending</code>.</p>
</li>
</ul>
<p>Para entender melhor sobre cada tipo de afinidade, vamos dividir em duas partes cada propriedade do tipo de afinidade:</p>
<ol>
<li>Primeira parte é o início de cada tipo de afinidade:</li>
</ol>
<p><code>requiredDuringScheduling</code>: Nesse trecho, estamos dizendo para o agendador que as condições definidas são obrigadas durante o agendamento, ou seja, deverá atender estritamente as regras para que o agendamento possa ser realizado.</p>
<p><code>preferredDuringScheduling</code>: Nesse trecho, estamos dizendo para o agendador que se as condições propostas ao pod não for possível atender, se por exemplo, o label <code>disktype=ssd</code> não corresponder a nenhum nó, então o agendador poderá escolher outro nó para agendar esse pod.</p>
<ol start="2">
<li>Segunda parte é o final de cada tipo de afinidade:</li>
</ol>
<p><code>ignoreDuringExecution</code>: Nesse trecho, estamos dizendo ao Kubernetes que se um pod que já está em execução em um nó e uma label é removida desse nó, esse pod não será removido. Exemplo: o pod foi agendado no nó <code>worker1</code> devido a label <code>disktype=ssd</code> que correspondeu aos critérios do pod, se essa label for removida, o agendador não irá remover esse pod do nó, pois será ignorado durante a execução.</p>
<h3 id="heading-anti-afinidade-de-no">Anti-afinidade de nó</h3>
<p>Anti-afinidade de nó funciona no mesmo conceito de afinidade de nó, só que ao invés de trabalhar com regras de <strong>"este pod deve"</strong>, será da forma de <strong>"este pod não deve"</strong> ser agendado em um determinado nó.</p>
<p>Dado o seguinte manifesto:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">affinity:</span>
    <span class="hljs-attr">nodeAffinity:</span>
      <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span>
        <span class="hljs-attr">nodeSelectorTerms:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">matchExpressions:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">kubernetes.io/os</span>
            <span class="hljs-attr">operator:</span> <span class="hljs-string">NotIn</span>
            <span class="hljs-attr">values:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">linux</span>
</code></pre>
<p>Definição de Pod com anti-affinity</p>
<p>No exemplo acima, estamos instruindo o Kubernetes a <strong>não agendar o pod em nós que estejam executando o sistema operacional Linux</strong>. Ele restringe o agendamento apenas para nós que rodam outro sistema operacional, como Windows.</p>
<p>Se não houver nós com sistema operacional diferente de Linux, o pod ficará pendente e não será agendado.</p>
<h2 id="heading-requests-e-limits">Requests e limits</h2>
<p>Os recursos de solicitações e limites são informações de consumo de CPU e memória que você pode especificar opcionalmente para um contêiner.</p>
<p>Ao usar a solicitação de recurso para os contêineres em um pod, o agendador usa essas informações para decidir em qual nó o pod deve ser agendado.</p>
<p>O agendador irá sempre buscar um nó que tenha recursos disponíveis para que o pod seja agendado. Caso não encontre nenhum nó, o pod ficará com o status <code>Pending</code> e provavelmente se visualizar o status do pod, usando o comando <code>kubectl describe pod</code> verá que houve uma falha no agendamento devido a CPU insuficiente <code>Insufficient cpu (3)</code>.</p>
<h3 id="heading-recursos-de-cpu">Recursos de CPU</h3>
<p>As solicitações e limites de CPU são medidos em unidade de CPU, no Kubernetes, 1 unidade de CPU equivale a 1 núcleo de CPU físico ou 1 núcleo virtual.</p>
<p>São permitidas usar solicitações fracionárias, por exemplo <code>0.5</code>, está solicitando metade do tempo de CPU, em relação a uma CPU de 1 core.</p>
<h3 id="heading-recursos-de-memoria">Recursos de Memória</h3>
<p>As solicitações e limites de memória são medidos em bytes, você pode informar a memória como um número inteiro simples ou como um número de ponto fixo usando os sufixos de quantidade.</p>
<p>Exemplo de uso:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">images.my-company.example/app:v4</span>
    <span class="hljs-attr">resources:</span>
      <span class="hljs-attr">requests:</span>
        <span class="hljs-attr">memory:</span> <span class="hljs-string">"64Mi"</span>
        <span class="hljs-attr">cpu:</span> <span class="hljs-string">"250m"</span>
      <span class="hljs-attr">limits:</span>
        <span class="hljs-attr">memory:</span> <span class="hljs-string">"128Mi"</span>
        <span class="hljs-attr">cpu:</span> <span class="hljs-string">"500m"</span>
</code></pre>
<p>Definição de Pod com requests e limits</p>
<p><code>resources</code>: Esta seção configura os recursos de CPU e memória que o contêiner solicita e o limite máximo que pode usar. Ela contém dois subitens:</p>
<ul>
<li><p><code>requests</code>: Define a quantidade mínima de recursos que o contêiner precisa para ser executado. Isso garante que, quando o pod for agendado, o nó terá pelo menos essa quantidade de recursos disponível.</p>
</li>
<li><p><code>limits</code>: Define a quantidade máxima de recursos que o contêiner pode usar. O contêiner pode consumir mais do que o <code>request</code>, mas nunca ultrapassará o valor do <code>limit</code>.</p>
</li>
</ul>
<h3 id="heading-analise-detalhada-dos-recursos">Análise Detalhada dos Recursos</h3>
<h4 id="heading-requests"><code>requests</code></h4>
<ul>
<li><p><code>memory: "64Mi"</code>: Especifica que o contêiner solicita 64 MiB (Mebibytes) de memória. Isso significa que o Kubernetes tentará agendar este pod apenas em nós que tenham pelo menos 64 MiB de memória disponível.</p>
</li>
<li><p><code>cpu: "250m"</code>: Especifica que o contêiner solicita 250 milicores (ou 0,25 core) de CPU. Esse é o recurso de CPU mínimo que o Kubernetes garantirá para o contêiner, mas ele pode usar mais até o limite especificado.</p>
</li>
</ul>
<h4 id="heading-limits"><code>limits</code></h4>
<ul>
<li><p><code>memory: "128Mi"</code>: Especifica que o contêiner terá um limite máximo de 128 MiB de memória. Se o contêiner tentar usar mais do que isso, o Kubernetes poderá terminá-lo com um erro de <code>OutOfMemory</code> (OOM).</p>
</li>
<li><p><code>cpu: "500m"</code>: Especifica que o contêiner terá um limite máximo de 500 milicores (ou 0,5 core) de CPU. Isso significa que o contêiner poderá consumir até metade de um núcleo de CPU, mas não mais do que isso.</p>
</li>
</ul>
<h2 id="heading-limit-range">Limit Range</h2>
<p>Por padrão os contêineres são executados com recursos de computação ilimitados no cluster. O limit range é uma política para restringir as alocações de recursos que você pode especificar para objetos como <code>Pod</code> e <code>PersistenVolumeClaim</code> em um namespace específico.</p>
<p>Um <code>LimitRange</code> fornece restrições que podem:</p>
<ul>
<li><p>Definir o uso mínimo e máximo de recursos de computação por pod ou contêiner em um namespace.</p>
</li>
<li><p>Aplicar solicitação de armazenamento mínimo e máximo por <code>PersistentVolumeClaim</code> em um namespace.</p>
</li>
<li><p>Definir uma proporção entre solicitação e limite para um recurso em um namespace.</p>
</li>
<li><p>Definir solicitação/limite padrão para recursos de computação em um namespace e injetá-los automaticamente em contêineres em tempo de execução.</p>
</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">LimitRange</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">resources-limits</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">limits:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">type:</span> <span class="hljs-string">Container</span>
    <span class="hljs-attr">max:</span>
      <span class="hljs-attr">cpu:</span> <span class="hljs-string">"2"</span>           <span class="hljs-comment"># Máximo de 2 cores de CPU por contêiner</span>
      <span class="hljs-attr">memory:</span> <span class="hljs-string">"1Gi"</span>      <span class="hljs-comment"># Máximo de 1 GiB de memória por contêiner</span>
    <span class="hljs-attr">min:</span>
      <span class="hljs-attr">cpu:</span> <span class="hljs-string">"200m"</span>        <span class="hljs-comment"># Mínimo de 200 milicores (0.2 cores) de CPU por contêiner</span>
      <span class="hljs-attr">memory:</span> <span class="hljs-string">"100Mi"</span>    <span class="hljs-comment"># Mínimo de 100 MiB de memória por contêiner</span>
    <span class="hljs-attr">default:</span>
      <span class="hljs-attr">cpu:</span> <span class="hljs-string">"500m"</span>        <span class="hljs-comment"># Solicitação padrão de CPU se não especificado</span>
      <span class="hljs-attr">memory:</span> <span class="hljs-string">"200Mi"</span>    <span class="hljs-comment"># Solicitação padrão de memória se não especificado</span>
    <span class="hljs-attr">defaultRequest:</span>
      <span class="hljs-attr">cpu:</span> <span class="hljs-string">"300m"</span>        <span class="hljs-comment"># Request padrão de CPU para contêineres que não especificarem um valor</span>
      <span class="hljs-attr">memory:</span> <span class="hljs-string">"150Mi"</span>    <span class="hljs-comment"># Request padrão de memória para contêineres que não especificarem um valor</span>
</code></pre>
<p>Definição de LimitRange para Contêiner no namespace default</p>
<h3 id="heading-limitrange-para-persistentvolumeclaim">LimitRange para PersistentVolumeClaim</h3>
<p>Outro uso comum do LimitRange é para definir limites em PersistentVolumeClaims (PVCs). Isso pode garantir que o armazenamento seja usado de forma responsável.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">LimitRange</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">storage-limits</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">limits:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">type:</span> <span class="hljs-string">PersistentVolumeClaim</span>
    <span class="hljs-attr">max:</span>
      <span class="hljs-attr">storage:</span> <span class="hljs-string">"10Gi"</span>    <span class="hljs-comment"># Máximo de 10 GiB de armazenamento por PVC</span>
    <span class="hljs-attr">min:</span>
      <span class="hljs-attr">storage:</span> <span class="hljs-string">"1Gi"</span>     <span class="hljs-comment"># Mínimo de 1 GiB de armazenamento por PVC</span>
</code></pre>
<p>Definição de LimitRange para PVCs no namespace default</p>
<h2 id="heading-resource-quotas">Resource Quotas</h2>
<p>A cota por recursos é definido por um objeto <code>ResourceQuota</code>, e fornece restrições que limitam o consumo de recursos por namespace. Ele pode limitar a quantidade de objetos que podem ser criados em um namespace por tipo.</p>
<p>Para recursos de CPU e Memória, <code>ResourceQuota</code> impõe que cada novo pod nesse namespace defina um limite para esse recurso. Se você aplicar uma cota de recursos em um namespace para CPU ou memória, o <code>requests</code> e <code>limits</code> sempre deverão ser especificados para o pod enviado. Caso contrário a admissão do pod poderá ser rejeitada.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ResourceQuota</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">resource-quota</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">hard:</span>
    <span class="hljs-attr">pods:</span> <span class="hljs-string">"10"</span>                      <span class="hljs-comment"># Limita o número máximo de pods para 10</span>
    <span class="hljs-attr">requests.cpu:</span> <span class="hljs-string">"2"</span>               <span class="hljs-comment"># Limita o total de requests de CPU para 2 cores</span>
    <span class="hljs-attr">requests.memory:</span> <span class="hljs-string">"1Gi"</span>          <span class="hljs-comment"># Limita o total de requests de memória para 1 GiB</span>
    <span class="hljs-attr">limits.cpu:</span> <span class="hljs-string">"4"</span>                 <span class="hljs-comment"># Limita o total de CPU para 4 cores</span>
    <span class="hljs-attr">limits.memory:</span> <span class="hljs-string">"2Gi"</span>            <span class="hljs-comment"># Limita o total de memória para 2 GiB</span>
</code></pre>
<p>Definição de ResourceQuota para o namespace default</p>
<ol>
<li><p><code>pods: "10"</code>: Limita o número total de pods que podem ser criados no namespace <code>default</code> a 10. Se um usuário tentar criar mais do que 10 pods, o Kubernetes irá bloquear a criação.</p>
</li>
<li><p><code>requests.cpu: "2"</code>: Define que a soma de todas as solicitações de CPU (<code>requests.cpu</code>) dos pods no namespace não pode ultrapassar 2 núcleos. Ou seja, se os pods no namespace já estiverem usando 2 núcleos de CPU solicitados, não será possível agendar novos pods que aumentem essa demanda.</p>
</li>
<li><p><code>requests.memory: "1Gi"</code>: Limita o total de memória solicitada pelos pods no namespace para 1 GiB. Esse é o valor máximo acumulado de <code>requests.memory</code> que todos os pods combinados podem solicitar.</p>
</li>
<li><p><code>limits.cpu: "4"</code>: Define que o consumo máximo de CPU para o namespace é de 4 núcleos. Esse limite considera o uso de <code>limits.cpu</code>, o que significa que o Kubernetes permitirá a criação de pods até que o consumo total de CPU atinja esse valor.</p>
</li>
<li><p><code>limits.memory: "2Gi"</code>: Define que o total máximo de memória, usando <code>limits.memory</code>, é de 2 GiB. Isso limita o uso total de memória dentro do namespace.</p>
</li>
</ol>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Entender as diferentes formas de agendamento de pods no Kubernetes é essencial para aproveitar ao máximo a infraestrutura e garantir que os recursos sejam utilizados de maneira inteligente e equilibrada.</p>
<p>Desde as políticas de afinidade e anti-afinidade até quotas e limites de recursos, cada técnica contribui para um ambiente mais resiliente e adaptado às necessidades da sua aplicação.</p>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Configure e gerencie alertas no Grafana: Maximize a eficiência do monitoramento]]></title><description><![CDATA[Neste artigo, explico como configurar alertas no Grafana para monitorar métricas e enviar notificações por e-mail. Após revisar as etapas básicas, como ajustar as configurações do SMTP no Grafana via docker-compose e definir pontos de contato para al...]]></description><link>https://aristides.dev/configure-e-gerencie-alertas-no-grafana-maximize-a-eficiencia-do-monitoramento</link><guid isPermaLink="true">https://aristides.dev/configure-e-gerencie-alertas-no-grafana-maximize-a-eficiencia-do-monitoramento</guid><category><![CDATA[Grafana]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Fri, 12 Apr 2024 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047850836/d75a1680-b7f1-4be9-aa4f-af0406b5c2a6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Neste artigo, explico como configurar alertas no Grafana para monitorar métricas e enviar notificações por e-mail. Após revisar as etapas básicas, como ajustar as configurações do SMTP no Grafana via docker-compose e definir pontos de contato para alertas, mostro como criar uma política de notificação e configurar alertas específicos para, por exemplo, uso de CPU ou disco. O artigo é parte de uma série sobre monitoramento com Grafana e Prometheus, voltado para usuários que desejam aprimorar suas habilidades de monitoramento e alerta.</p>
</blockquote>
<hr />
<p>Fala pessoal tudo beleza?</p>
<p>Se você está lendo este artigo pela primeira vez, saiba que ele faz parte de uma série onde explico como montar uma stack de monitoramento usando Grafana e Prometheus com Docker e Docker Compose.</p>
<p>Neste artigo, parte 4, vamos entender como funciona o envio de notificações e criar um alerta no Grafana para uma métrica específica. Quando esse alerta for acionado, um e-mail será enviado para notificar sobre o incidente.</p>
<p>O Grafana disponibiliza vários recursos que nos ajudam no gerenciamento de alertas, podendo criar rótulos, adicionar políticas de notificação e a integração com várias ferramentas de chats, como o Slack, Discord, Telegram, Microsoft Teams entre outras. Nesse <a target="_blank" href="https://grafana.com/docs/grafana/latest/alerting/fundamentals/contact-points/#supported-contact-point-integrations">link da documentação</a> contém uma lista de todas as ferramentas.</p>
<h2 id="heading-pre-requisitos">Pré requisitos</h2>
<ul>
<li><p>Ter uma instância do Grafana em execução.</p>
</li>
<li><p>Ter acompanhado os artigos anteriores onde subimos uma stack do Grafana e Prometheus.</p>
</li>
</ul>
<p>Caso queira, pode acessar os arquivos dessa stack <a target="_blank" href="https://github.com/aristidesneto/stack-de-monitoramento-grafana-e-prometheus">clicando aqui</a>.</p>
<blockquote>
<p>Obs: A versão do Grafana utilizada nesse artigo foi a v10.4.1. Versões mais antigas ou futuras podem conter diferenças.</p>
</blockquote>
<h2 id="heading-grafana-alerta">Grafana Alerta</h2>
<p>O Grafana Alerta permite que sua equipe de monitoramento seja alertada sobre as métricas e logs. Ele consolida em uma única página todos seus alertas gerenciados pelo Grafana ou por outras fonte de dados compatível com o Prometheus.</p>
<p>Uma regra de alerta pode alertar sobre vários itens de uma só vez e você pode também agrupar instâncias de alertas com base em rótulos para não receber muitas notificações de uma só vez.</p>
<h3 id="heading-como-funciona-o-grafana-alerta">Como funciona o Grafana Alerta</h3>
<p>Basicamente, temos uma regra de alerta que é criada em base de uma métrica, por exemplo, o uso de CPU ou memória de um servidor.</p>
<p><strong>Os</strong> <strong>rótulos</strong> correspondem as instâncias de alerta às políticas de notificação e podem ser usadas para agrupar seus alertas por gravidade.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048756975/4365124e-c65a-4ea9-8e16-5a70c0a30e2c.png" alt="Como funciona o Grafana Alerta - Fonte: Grafana" /></p>
<p>Como funciona o Grafana Alerta - Fonte: Grafana</p>
<p><strong>A política de notificação</strong> é o conjunto de regras para onde, quando e como os alertas são roteados.</p>
<p><strong>Os pontos de contatos</strong> definem como seus contatos são notificados quando um alerta é acionado.</p>
<h2 id="heading-conhecendo-a-pagina-de-alertas-do-grafana">Conhecendo a página de Alertas do Grafana</h2>
<p>A página de alertas é acessada no menu lateral clicando em <strong><em>Alerting ➡</em></strong> <strong><em>Alert rules</em></strong>. Nessa primeira página, irá conter os alertas que foram criados através da interface do Grafana e alertas que foram criados a partir de uma fonte de dados como o Prometheus.</p>
<p>Antes de criarmos nosso primeiro alerta, devemos configurar o SMTP no Grafana para que os e-mails possam ser enviados, e assim poder criar nosso <strong><em>Ponto de Contato</em></strong>.</p>
<h2 id="heading-configurando-um-smpt">Configurando um SMPT</h2>
<p>Para esse passo, você deve ter um SMPT em qualquer provedor de internet para poder enviar alertas, estou usando o serviço de e-mail da <a target="_blank" href="https://app.sparkpost.com/?ref=aristides.dev">SparkPost</a>.</p>
<p>Como estamos usando a stack do docker, iremos alterar o arquivo <code>docker-compose.yaml</code> adicionando algumas variáveis de configuração do SMTP.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">grafana:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">grafana/grafana:latest</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">grafana</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">grafana_data:/var/lib/grafana</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./grafana/provisioning:/etc/grafana/provisioning</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_INSTALL_PLUGINS=${GF_INSTALL_PLUGINS}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SMTP_ENABLED=${GF_SMTP_ENABLED}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SMTP_HOST=${GF_SMTP_HOST}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SMTP_USER=${GF_SMTP_USER}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SMTP_PASSWORD=${GF_SMTP_PASSWORD}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SMTP_FROM_ADDRESS=${GF_SMTP_FROM_ADDRESS}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SMTP_FROM_NAME=${GF_SMTP_FROM_NAME}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SERVER_ROOT_URL=${GF_SERVER_ROOT_URL}</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">3000</span><span class="hljs-string">:3000</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span>
</code></pre>
<p>Arquivo docker-compose.yaml</p>
<p>Entendendo as variáveis de configuração para o caso do servidor SparkPost:</p>
<ul>
<li><p><strong>GF_SMTP_ENABLED</strong>: true para ativar o serviço</p>
</li>
<li><p><strong>GF_SMTP_HOST</strong>: host do servidor SMTP, ex: smtp.sparkpostmail.com:2525</p>
</li>
<li><p><strong>GF_SMTP_USER</strong>: usuário do servidor SMTP, ex: SMTP_Injection</p>
</li>
<li><p><strong>GF_SMTP_PASSWORD</strong>: &lt;api_key&gt;</p>
</li>
<li><p><strong>GF_SMTP_FROM_ADDRESS</strong>: e-mail de um domínio válido configurado no SparkPost</p>
</li>
<li><p><strong>GF_SMTP_FROM_NAME</strong>: nome do remetente de envio</p>
</li>
<li><p><strong>GF_SERVER_ROOT_URL</strong>: url do grafana hospedado</p>
</li>
</ul>
<p>Após adicionar essas variáveis de ambiente ao arquivo do docker-compose, adicione essas mesmas variáveis com os seus valores no arquivo <code>.env</code> na raiz do projeto.</p>
<p>Suba o ambiente com o comando <code>docker-compose up -d</code>. Verifique os logs para validar se os containers subiram corretamente sem erros, também pode usar <code>docker-compose ps</code> para ver os status de cada um deles.</p>
<h2 id="heading-pontos-de-contato">Pontos de contato</h2>
<p>Os pontos de contato são as configurações que definimos como os usuários serão notificados quando um alerta é acionado, seja por um e-mail, uma mensagem no Slack, Microsoft Teams ou qualquer outra ferramenta de chat suportada pelo Grafana.</p>
<p>Para isso, clique no menu lateral em <strong><em>Alerting ➡ Contact points</em></strong> e em seguida no botão <strong><em>Add contact point</em></strong>.</p>
<p>Na página de configuração, dê um nome para o alerta, nesse caso será <strong><em>My Company Alerts - Email</em></strong>. Em <strong><em>Integration</em></strong> escolha o tipo E-mail, no campo <strong><em>Addresses</em></strong> adicione um e-mail que deseja receber as notificações.</p>
<p>As outras opções não iremos alterar, mas caso queiram podem dar uma olhada nelas. Após inserir as informações, clique no botão <strong><em>Test</em></strong> para testar o envio de e-mail e clique em <strong><em>Save contact point</em></strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048196232/660008df-7bfd-4e30-9bbb-3fa46efb6572.png" alt="Tela de criação de um ponto de contato" class="image--center mx-auto" /></p>
<p>Tela de criação de um ponto de contato</p>
<h2 id="heading-politica-de-notificacao">Política de notificação</h2>
<p>As políticas de notificação determinam como os alertas são roteados para os pontos de contato.</p>
<p>As políticas possuem uma estrutura em árvore, onde cada política pode ter uma ou mais políticas aninhadas. Cada política, exceto a política padrão, também pode corresponder a rótulos de alerta específicos.</p>
<p>Cada alerta é avaliado pela política padrão e, posteriormente, por cada política aninhada.</p>
<p>Acesse a página de criação da política clicando no menu lateral em <strong><em>Alerts ➡ Notification policies</em></strong>. Na política padrão clique no menu suspenso e em Editar. No campo de ponto de contato padrão, escolha o ponto de contato que criamos anteriormente, chamado <strong><em>My Company Alerts - Email</em></strong>.</p>
<p>As próximas informações não serão alteradas, vamos deixar as configurações padrões. Mas estarei explanando pelas configurações disponíveis.</p>
<p>Em <strong><em>Group by</em></strong>, podemos escolher rótulos para agrupar os alertas. Se vários alertas corresponderem a esta política, eles serão agrupados por esses rótulos. Uma notificação é enviada por grupo, se o campo estiver vazio, todas as notificações serão enviadas em um único grupo.</p>
<p>Em <strong><em>Timing options</em></strong>, há três opções que podem ser configuradas:</p>
<ul>
<li><p><strong>Group wait</strong>: Tempo de espera para armazenar alertas do mesmo grupo antes de enviar uma notificação inicial. Padrão é 30 segundos.</p>
</li>
<li><p><strong>Group interval</strong>: Intervalo de tempo mínimo entre duas notificações para um grupo. Padrão é 5 minutos.</p>
</li>
<li><p><strong>Repeat interval</strong>: Intervalo de tempo mínimo para reenviar uma notificação caso nenhum alerta novo tenha sido adicionado ao grupo. Padrão é de 4 horas.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048221098/3052acb2-b822-4745-9e3d-e84a35ed2932.png" alt="Editando a política de notificação padrão" class="image--center mx-auto" /></p>
<p>Editando a política de notificação padrão</p>
<h2 id="heading-criando-um-alerta">Criando um alerta</h2>
<p>Para criar nosso primeiro alerta, clique no botão <strong><em>New alert rule</em></strong> no lado direito para ser direcionado à página de configuração.</p>
<p>Essa tela é dividida em 5 sessões.</p>
<ol>
<li><p><strong>Nome do alerta:</strong> deve ser informado um nome para identificar seu alerta, por exemplo: <strong>Uso de CPU acima de 90%</strong>.</p>
</li>
<li><p><strong>Definição da query e condição do alerta:</strong> é onde definimos a query e a condição do alerta, ou seja, através da query podemos selecionar as métricas que iremos acompanhar e dizer que essa métrica deve ter alguma condição atrelada, como por exemplo: <strong><em>"Se durante 1 hora o uso de CPU tiver acima de 90%, a condição do alerta deve ser disparado"</em></strong>.</p>
</li>
<li><p><strong>Definição do comportamento de avaliação:</strong> deve determinar com que frequência uma regra de alerta deve ser avaliada e com que rapidez ela deve alterar seu estado.</p>
</li>
<li><p><strong>Configurar rótulos e notificações:</strong> anote suas regras com rótulos para facilitar uma pesquisa ou o direcionamento para uma política de notificação.</p>
</li>
<li><p><strong>Adicionar anotações:</strong> informações para complementar a mensagem de notificação enviada.</p>
</li>
</ol>
<h3 id="heading-criando-um-alerta-de-uso-de-disco">Criando um alerta de uso de disco</h3>
<p>Vamos criar um alerta e para isso, insira um nome, nesse exemplo vamos dar o nome de <strong>Uso do disco &gt; 80%</strong>.</p>
<p>Na sessão dois, iremos definir a condição que queremos que o Grafana fique observando para que qualquer anomalia que venha ocorrer, um alerta seja disparado.</p>
<p>Para isso, clique no botão <strong><em>Code</em></strong> no canto direito e cole a query abaixo no campo de métricas:</p>
<pre><code class="lang-bash">100 - ((node_filesystem_avail_bytes{job=<span class="hljs-string">"node_exporter"</span>,device!~<span class="hljs-string">'rootfs'</span>,mountpoint=<span class="hljs-string">"/"</span>} * 100) / node_filesystem_size_bytes{job=<span class="hljs-string">"node_exporter"</span>,device!~<span class="hljs-string">'rootfs'</span>,mountpoint=<span class="hljs-string">"/"</span>})
</code></pre>
<p>Para validar a query, clique no botão <strong><em>Run queries</em></strong> no canto direito. Será exibido mais abaixo, uma tabela contendo os valores encontrados em sua base de dados do Prometheus. Se você tiver seguindo esse tutorial desde o início, a seguinte tela será exibida com apenas uma entrada, caso contrário, irá aparecer as referências da sua base atual.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048251452/12d8e336-8a3e-48a8-9714-cfcafbf5cef7.png" alt="Definindo query e condição de alerta" class="image--center mx-auto" /></p>
<p>Definindo query e condição de alerta</p>
<p>Explanando sobre a query utilizada, podemos resumir da seguinte maneira:</p>
<pre><code class="lang-bash">100 - ((espaço_disponível * 100) / tamanho_total)
</code></pre>
<p>Isso é feito para representar a porcentagem de espaço livre, ou seja, 100 menos a porcentagem do espaço ocupado. Este cálculo é comum em monitoramento de sistemas para avaliar rapidamente a capacidade restante em um sistema de arquivos.</p>
<p>Mais abaixo, podemos ver duas caixas que são chamadas de Expressões (B e C). Nessas caixas podemos manipular o resultado da expressão A. Veja que na expressão B, existe um warning dizendo que a operação de <strong><em>Reduce</em></strong> não é necessária devido que a expressão A já contém os dados tratados, podemos dizer que os dados obtidos na primeira expressão já estão prontos para criação do alerta.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048273882/e11a9d81-f0ac-4484-9d3c-6f139667051b.png" alt="Caixa de expressões para configuração do alerta" class="image--center mx-auto" /></p>
<p>Caixa de expressões para configuração do alerta</p>
<p>Nesse caso, vamos remover a expressão B, clicando no ícone de lixeira no canto direito.</p>
<p>Para mantermos uma consistência de letras, renomeie a expressão C para B e altere o campo Input para A (isso é opcional, mas meu toque não me deixa ver essa sequência quebrada).</p>
<p>No campo abaixo, mantenha a opção <strong><em>Is above</em></strong> (acima de) selecionada e coloque o valor 80. Essa expressão diz ao Grafana que quando o resultado da expressão A for acima de 80% (uso de armazenamento do disco), um alerta seja disparado.</p>
<p>Na terceira sessão, iremos configurar o comportamento do alerta, como ele será avaliado. Os alertas são armazenados dentro de uma pasta, sendo necessário escolher uma existente ou podendo criar uma. Vamos criar uma pasta chamada <strong><em>Alerts</em></strong>.</p>
<p>Em <strong><em>Evaluation group</em></strong> (grupo de avaliação), deve escolher um grupo existente ou criar um para que você possa agrupar alertas que devem ser avaliados dentro de um determinado período. Vamos criar um grupo chamado de <strong><em>Usage resources</em></strong> e definir um intervalo de avaliação de 2 minutos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048299548/563adee8-6bc7-40f5-9720-c3ab159ec92d.png" alt class="image--center mx-auto" /></p>
<p>No campo <strong><em>Pending period</em></strong> (período pendente), deve ser informado o período que uma regra de alerta pode violar a condição até que a regra de alerta seja acionada, ou seja, esse será o período em que o Grafana irá aguardar até que o alerta seja disparado por definitivo, enviando uma notificação pelos canais configurados. Se dentro desse período a regra de alerta for resolvida o alerta não será disparado.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048317924/19d49cab-4ed2-4902-81c1-7178235cf28c.png" alt class="image--center mx-auto" /></p>
<p>Definindo comportamento de avaliação</p>
<p>Na quarta sessão, podemos configurar rótulos para anotar os alertas, facilitando uma pesquisa ou direcionando para uma política de notificação.</p>
<p>Os rótulos podem ser criados conforme sua necessidade e cenário. Para esse exemplo, iremos criar dois, um chamado <strong><em>company</em></strong> que terá como valor o nome da empresa, <strong><em>My Company</em></strong> e outro será <strong><em>resource_type</em></strong> que terá como o valor <strong><em>disk</em></strong>, apenas para dizer que o alerta se trata de um problema relacionado a recursos, que nesse caso é o uso do disco.</p>
<p>Clicando no botão <strong><em>Preview routing</em></strong>, será exibido qual política de notificação será usada para esse disparo, veja que a política exibida é a padrão que utilizará o ponto de contato já configurado nos passos anteriores.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048333656/2d94bb3d-33be-4c1a-8eba-ad436e14f7e8.png" alt class="image--center mx-auto" /></p>
<p>Configurando labels e notificações</p>
<p>Na quinta sessão, é possível customizar um pouco mais o conteúdo do e-mail enviado. Além dos três campos pré-definidos como o resumo e descrição, podemos adicionar mais rótulos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048348831/48288c9c-b71c-4b88-927e-9debab2e5e88.png" alt class="image--center mx-auto" /></p>
<p>Após o preenchimento de todos os campos, podemos finalmente salvar o alerta clicando no botão <strong><em>Save rule and exit</em></strong> no canto superior direito.</p>
<p>E para sabermos se tudo está funcionando como deveria? Na prática, esse alerta só será disparado quando o disco estiver acima de 80% em uso, provavelmente em nosso ambiente de desenvolvimento, nossa máquina estará abaixo disso.</p>
<p>Então para validarmos vamos diminuir o critério de avaliação de 80 para 30. Clique em editar seu alerta e altere apenas a configuração de avaliação do alerta e clique em salvar. Aguarde pelo menos 2 minutos que você verá na tela inicial de alertas que o status será alterado para <code>pending</code> e logo após para <code>firing</code>.</p>
<p>Se tudo estiver correto, você irá receber um e-mail bem parecido com a imagem abaixo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048367235/a4282067-3f87-4653-bf7e-d49bfab81031.png" alt="E-mail de alerta para uso excedido do disco" class="image--center mx-auto" /></p>
<p>E-mail de alerta para uso excedido do disco</p>
<p>Edite seu alerta novamente voltando o critério de avaliação de 30 para 80 e aguarde até que tudo seja normalizado. Assim que o Grafana identificar que as coisas voltaram ao normal, que não existe mais alertas, um novo e-mail será enviado mas com o objetivo de informar que o incidente que acabará de ser notificado, foi resolvido.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735048383380/0a13962f-cbd1-43a8-bcca-38fecc780597.png" alt="E-mail de alerta de incidente resolvido" class="image--center mx-auto" /></p>
<p>E-mail de alerta de incidente resolvido</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Com este artigo foi possível entender os principais recursos do Grafana Alerta. Passamos pelo ponto de contato, política de notificação, criação do alerta além da configuração do SMTP e o fluxo de envio de notificações.</p>
<p>Espero que de alguma maneira posso ter ajudado com esse artigo e qualquer dúvida, melhorias ou comentários, não deixe de comentar abaixo.</p>
<p>E não deixe de acompanhar a série dos artigos sobre a stack Prometheus e Grafana que estou escrevendo.</p>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Script de backup do Mysql e armazenamento no S3 da AWS]]></title><description><![CDATA[Fala pessoal tudo beleza?
Esse artigo é uma extensão de um artigo que escrevi há alguns anos, onde fiz a criação de um simples script de backup para bancos Mysql.
Hoje estarei refatorando aquele primeiro script e adicionando uma opção para poder arma...]]></description><link>https://aristides.dev/script-de-backup-do-mysql-e-armazenamento-no-s3-da-aws</link><guid isPermaLink="true">https://aristides.dev/script-de-backup-do-mysql-e-armazenamento-no-s3-da-aws</guid><category><![CDATA[MySQL]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sat, 11 Mar 2023 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047724775/64655fb3-ab4a-480e-9d7f-3900c2a6ed24.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Esse artigo é uma extensão de um artigo que escrevi há alguns anos, onde fiz a criação de um simples script de backup para bancos Mysql.</p>
<p>Hoje estarei refatorando aquele primeiro script e adicionando uma opção para poder armazenar o backup no S3 da AWS.</p>
<p>Caso queira ver o primeiro artigo que escrevi, <a target="_blank" href="https://aristides.dev/script-de-backup-de-banco-de-dados-mysql">acesse aqui</a>.</p>
<h2 id="heading-instalando-aws-s3-cli">Instalando AWS S3 CLI</h2>
<p>Primeiro vamos instalar e configurar a ferramenta de CLI que a AWS fornece para podermos interagir com os recursos na nuvem.</p>
<p>A instalação será feita em um sistema Linux, para a instalação em outros sistemas, pode ser encontrado <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html?ref=aristides.dev">nesse link</a>.</p>
<p>Execute os comandos:</p>
<pre><code class="lang-bash">$ curl <span class="hljs-string">"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"</span> -o <span class="hljs-string">"awscliv2.zip"</span>

$ unzip awscliv2.zip

$ sudo ./aws/install
</code></pre>
<p>Podemos confirmar se a instalação ocorreu com sucesso, com o comando:</p>
<pre><code class="lang-bash">$ aws --version

aws-cli/2.10.3 Python/3.9.11 Linux/5.10.0-21-amd64 exe/x86_64.debian.11 prompt/off
</code></pre>
<p>Caso já tenha o <code>aws-cli</code> instalado, poderá verificar se há alguma atualização para ser feita. No terceiro comando mostrado acima, pode usar a flag <code>--update</code> para atualizar para a versão mais recente.</p>
<pre><code class="lang-bash">$ sudo ./aws/install --update

You can now run: /usr/<span class="hljs-built_in">local</span>/bin/aws --version
</code></pre>
<h2 id="heading-criando-usuario-na-aws">Criando usuário na AWS</h2>
<p>Agora é necessário configurar o <code>aws-cli</code> com as credenciais de um usuário, para isso é necessário ter uma conta na AWS.</p>
<p>Nesse artigo irei pressupor que você já tem uma conta na AWS, não irei abordar o processo de criação de conta, mas iremos passar pela criação de um usuário <code>programático</code> e gerar as credenciais.</p>
<p>Com o foco na segurança, esse usuário terá um permissão restrita a um determinado bucket no S3, podendo apenas realizar ações nesse bucket e não tendo acesso nenhum a outros bucket que não seja o que definimos.</p>
<p>Acesse o console da AWS e navegue até a sessão do IAM.</p>
<ol>
<li><p>No menu esquerdo, clique em Usuários.</p>
</li>
<li><p>Na tela de listagem de usuários, clique em <strong>Adicionar usuários</strong>.</p>
</li>
<li><p>Informe o nome de usuário, nesse exemplo vamos criar um usuário chamado <code>backup-s3</code>. Clique em <strong>Próximo</strong>.</p>
</li>
<li><p>Nessa tela, iremos adicionar uma política ao usuário. Clique no card <strong>Anexar políticas diretamente</strong>.</p>
</li>
<li><p>Na listagem de políticas, clique em <strong>Criar política</strong>.</p>
</li>
<li><p>Na tela de criação, clique na aba <strong>JSON</strong> para informar a política manualmente.</p>
</li>
<li><p>Copie a seguinte política e cole no editor de política - <em>Altere o nome do bucket conforme sua preferência, nesse exemplo estou usando</em> <code>backups-aristides-dev</code></p>
</li>
</ol>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor0"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"s3:PutObject"</span>,
                <span class="hljs-string">"s3:GetObject"</span>,
                <span class="hljs-string">"s3:DeleteObject"</span>
            ],
            <span class="hljs-attr">"Resource"</span>: [
                <span class="hljs-string">"arn:aws:s3:::backups-aristides-dev/*"</span>
            ]
        },
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor1"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"s3:ListBucket"</span>
            ],
            <span class="hljs-attr">"Resource"</span>: [
                <span class="hljs-string">"arn:aws:s3:::backups-aristides-dev"</span>
            ]
        }
    ]
}
</code></pre>
<ol start="8">
<li><p>Clique em <strong>Próximo</strong> duas vezes até chegar na tela para informar o nome da política.</p>
</li>
<li><p>Informe o nome <code>BackupS3ReadWriteAccess</code> e clique em <strong>Criar política</strong>.</p>
</li>
<li><p>Nesse ponto, a política foi criada. Volte para a aba inicial e procure na listagem a política que acabou de ser criada, selecione ela e clique em <strong>Próximo</strong>.</p>
</li>
<li><p>Revise os dados e clique em <strong>Criar usuário</strong>.</p>
</li>
</ol>
<p>Até aqui acabamos de criar um usuário e anexamos uma política para que esse usuário tenha acesso somente a um bucket chamado <code>backups-aristides-dev</code>. Com essa política, o usuário poderá executar as ações de Leitura/Escrita/Remoção de arquivos dentro do bucket.</p>
<p>Para dar continuidade precisamos que o bucket já esteja criado. Para isso crie um bucket pela console da AWS com o mesmo nome que foi definido por você ao criar a política, no meu caso <code>backups-aristides-dev</code>. Não irei abordar a criação por se tratar de um passo simples, apenas informe o nome e deixe as opções padrões do bucket.</p>
<h2 id="heading-gerar-credenciais-de-seguranca">Gerar credenciais de segurança</h2>
<p>Após a criação do usuário e do bucket, acesse novamente a listagem de usuários do IAM e clique sobre o usuário <code>backup-s3</code> que acabamos de criar.</p>
<ol>
<li><p>Na aba Credenciais de segurança, na sessão Chaves de acesso, clique em <strong>Criar chave de acesso</strong>.</p>
</li>
<li><p>Selecione a opção <strong>Command Line Interface (CLI)</strong>, marque o checkbox <strong>Compreendo a recomendação acima e quero prosseguir para criar uma chave de acesso.</strong> e clique em <strong>Próximo</strong>.</p>
</li>
<li><p>Caso queira deixar alguma descrição (essa informação é opcional), informe na caixa de texto e clique em <strong>Criar chave de acesso</strong>.</p>
</li>
<li><p>Esse passo é muito importante, as chaves geradas serão mostradas apenas uma vez na tela. Clique em <strong>Baixar arquivo .csv</strong> e salve em um lugar seguro as credenciais.</p>
</li>
<li><p>Após baixar o arquivo .csv, clique em <strong>Concluído</strong>.</p>
</li>
</ol>
<h2 id="heading-configurando-aws-s3-cli">Configurando AWS S3 CLI</h2>
<p>Após todo os passos acima de criação de usuário e geração das chaves de acesso, devemos configurar a ferramente de CLI para podermos interagir com o S3.</p>
<p>Execute o comando abaixo para configurar o acesso e informe as credenciais do usuário criado.</p>
<pre><code class="lang-bash">$ aws configure

AWS Access Key ID [None]: &lt;access-key-id&gt;
AWS Secret Access Key [None]: &lt;secret-access-key&gt;
Default region name [None]: us-east-1
Default output format [None]: json
</code></pre>
<p>Para validarmos o acesso, vamos copiar um arquivo para o bucket e em seguida listarmos.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Cria um arquivo de teste com conteúdo</span>
$ touch teste.txt &amp;&amp; <span class="hljs-built_in">echo</span> <span class="hljs-string">"Backup Mysql"</span> &gt; teste.txt

<span class="hljs-comment"># Copia o arquivo criado para o bucket</span>
$ aws s3 cp teste.txt s3://backups-aristides-dev/
upload: ./teste.txt to s3://backups-aristides-dev/teste.txt

<span class="hljs-comment"># Lista o conteúdo do bucket</span>
$ aws s3 ls s3://backups-aristides-dev/
2023-03-11 18:45:18          13 teste.txt
</code></pre>
<p>Podemos validar também se o nosso usuário tem acesso para visualizar outros buckets:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Para listar todos os bucket no S3</span>
$ aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

<span class="hljs-comment"># Copiando um arquivo para outro bucket</span>
$ aws s3 cp teste.txt s3://meu-bucket-pessoal/       
upload failed: ./teste.txt to s3://meu-bucket-pessoal/teste.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
</code></pre>
<p>Show de bola, como visto acima o usuário criado tem acesso somente a um determinado bucket. Com isso nós garantimos uma segurança a mais para o nosso script de backup.</p>
<h2 id="heading-script-de-backup-mysql">Script de backup Mysql</h2>
<p>Nesse script, deixei toda parte do código comentado para um melhor entendimento de cada linha. Mas em resumo o script executará os seguintes passos:</p>
<ol>
<li><p>Define algumas variáveis que serão usadas durante a execução do script.</p>
</li>
<li><p>Define o nome dos bancos de dados que devem ser realizado o backup.</p>
</li>
<li><p>Verifica se o diretório de armazenamento dos arquivos e do log existem, caso não, serão criados.</p>
</li>
<li><p>Faz um loop nos bancos de dados para realizar o backup, compactar e sincronizar com o S3.</p>
</li>
<li><p>O script irá sincronizar todo o conteúdo do diretório de backup, caso um arquivo já exista no S3, esse não será enviado novamente.</p>
</li>
<li><p>Remove arquivos de backups antigos conforme a quantidade de dias determinada. Essa remoção será somente do servidor, no S3 os arquivos irão ser mantidos normalmente.</p>
</li>
</ol>
<p>Abaixo segue todo o código de backup:</p>
<pre><code class="lang-sh"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># </span>
<span class="hljs-comment"># Autor: Aristides Neto</span>
<span class="hljs-comment"># Email: falecom@aristides.dev</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Data: 11/03/2023</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Realiza o backup de bancos de dados MySQL</span>
<span class="hljs-comment"># Sincroniza com o S3 da AWS</span>
<span class="hljs-comment">#</span>

<span class="hljs-comment"># Define usuario e senha do banco</span>
USER=<span class="hljs-string">'root'</span>
PASS=<span class="hljs-string">'root'</span>

<span class="hljs-comment"># Datas</span>
DIA=`date +%d`
MES=`date +%m`
ANO=`date +%Y`
DATA_ATUAL=`date +%Y-%m-%d-%H-%M`

<span class="hljs-comment"># Data de Inicio do Backup</span>
DATA_INICIO=`date +%d/%m/%Y-%H:%M:%S`

<span class="hljs-comment"># Caminho do arquivo de log</span>
LOG_DIR=/var/<span class="hljs-built_in">log</span>/backup
LOG=<span class="hljs-variable">$LOG_DIR</span>/backup_db_<span class="hljs-variable">$ANO</span><span class="hljs-variable">$MES</span><span class="hljs-variable">$DIA</span>.<span class="hljs-built_in">log</span>

<span class="hljs-comment"># Diretorio onde serão salvos os backups</span>
DIR_BK=/backups/mysql

<span class="hljs-comment"># Lista dos bancos de dados que serão realizados o backup</span>
DATABASES=(banco01 banco02)

<span class="hljs-comment"># Verifica se existe o diretorio para armazenar os logs</span>
<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$LOG_DIR</span> ]; <span class="hljs-keyword">then</span>
    mkdir <span class="hljs-variable">$LOG_DIR</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Verifica se existe o diretorio para o backup</span>
<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$DIR_BK</span> ]; <span class="hljs-keyword">then</span>
    mkdir -p <span class="hljs-variable">$DIR_BK</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Inicio do backup</span>
<span class="hljs-built_in">echo</span> -e <span class="hljs-string">"MYSQLDUMP Iniciado em <span class="hljs-variable">$DATA_INICIO</span>\n"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>

<span class="hljs-comment"># Loop para backupear todos os bancos</span>
<span class="hljs-keyword">for</span> db <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${DATABASES[@]}</span>"</span>; <span class="hljs-keyword">do</span>
    <span class="hljs-comment"># Mysql DUMP</span>
    <span class="hljs-comment"># Para backupear procedures e functions foi adicionado o --routines</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"...Realizando backup do banco ...........................[ <span class="hljs-variable">$db</span> ]"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>
    mysqldump --routines -u<span class="hljs-variable">$USER</span> -p<span class="hljs-variable">$PASS</span> <span class="hljs-variable">$db</span> &gt; <span class="hljs-variable">$DIR_BK</span>/<span class="hljs-variable">$db</span><span class="hljs-string">'_'</span><span class="hljs-variable">$DATA_ATUAL</span>.sql

    <span class="hljs-comment"># Compacta o arquivo sql em BZ2</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"...Compactando arquivo de backup ........................[ <span class="hljs-variable">$db</span> ]"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>
    bzip2 <span class="hljs-variable">$DIR_BK</span>/<span class="hljs-variable">$db</span><span class="hljs-string">'_'</span><span class="hljs-variable">$DATA_ATUAL</span>.sql

    <span class="hljs-comment"># Sincroniza o diretório local com o bucket S3</span>
    aws s3 sync <span class="hljs-variable">$DIR_BK</span> s3://backups-aristides-dev
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"...Sincronização do diretório de backup com S3 ..........[ OK ]"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>
<span class="hljs-keyword">done</span>

DATA_FINAL=`date +%d/%m/%Y-%H:%M:%S`
<span class="hljs-built_in">echo</span> -e <span class="hljs-string">"\nMYSQLDUMP Finalizado em <span class="hljs-variable">$DATA_FINAL</span>\n"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>

<span class="hljs-comment"># Remove arquivos de backups antigos - 5 dias</span>
find <span class="hljs-variable">$DIR_BK</span> -<span class="hljs-built_in">type</span> f -mtime +5 -<span class="hljs-built_in">exec</span> rm -rf {} \;
</code></pre>
<p>Para executar esse script recomendo que seja feito com o usuário <code>root</code>, pois serão criados diretórios e arquivos em locais que serão necessárias permissões de super usuário. Caso deseja executar com um usuário não root, certifique que terá as permissões corretas.</p>
<h2 id="heading-log-de-execucao-do-script">Log de execução do script</h2>
<p>Ao executar o script, será criado um arquivo de log no caminho definido na variável <code>LOG =&gt; /var/log/backup</code> que conterá informações do banco backupeado e sincronização com o S3.</p>
<p>Esse é um arquivo bem simples, mas é uma forma de monitorar o andamento do backup.</p>
<pre><code class="lang-bash">MYSQLDUMP Iniciado em 11/03/2023-19:00:34

...Realizando backup <span class="hljs-keyword">do</span> banco ...........................[ banco01 ]
...Compactando arquivo de backup ........................[ banco01 ]
...Sincronização <span class="hljs-keyword">do</span> diretório de backup com S3 ..........[ OK ]

...Realizando backup <span class="hljs-keyword">do</span> banco ...........................[ banco02 ]
...Compactando arquivo de backup ........................[ banco02 ]
...Sincronização <span class="hljs-keyword">do</span> diretório de backup com S3 ..........[ OK ]

MYSQLDUMP Finalizado em 11/03/2023-19:04:14
</code></pre>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Esse é um script simples, que não contém validações em caso de possíveis erros nos comandos executados. É uma boa prática e recomendado adicionar validações nos comandos executados e validar se foi executado com sucesso ou não.</p>
<p>Não será o foco desse artigo essa implementação, mas recomendo que tenha validações e em casos de erros tenha um alerta, seja o envio de um e-mail ou uma notificação em um canal como o Slack, Microsoft Teams ou outro. Esses programas disponibilizam um webhook para esses casos que irá te ajudar a enviar as notificações.</p>
<p>Espero que seja útil esse script e qualquer dúvida, melhorias ou comentários, não deixe de comentar abaixo.</p>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Criando alertas com Prometheus e Alertmanager]]></title><description><![CDATA[Neste artigo, parte 3 de uma série sobre monitoramento com Grafana, Prometheus, Docker e Docker Compose, exploramos a configuração de alertas personalizados utilizando o Prometheus e o Alertmanager. Aprendemos a instalar e integrar o Alertmanager com...]]></description><link>https://aristides.dev/criando-alertas-com-prometheus-e-alertmanager</link><guid isPermaLink="true">https://aristides.dev/criando-alertas-com-prometheus-e-alertmanager</guid><category><![CDATA[Devops]]></category><category><![CDATA[Alertmanager]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Thu, 02 Feb 2023 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047273936/a3222606-6671-4802-8e92-0afd37aac9b2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Neste artigo, parte 3 de uma série sobre monitoramento com Grafana, Prometheus, Docker e Docker Compose, exploramos a configuração de alertas personalizados utilizando o Prometheus e o Alertmanager. Aprendemos a instalar e integrar o Alertmanager com o Prometheus para enviar alertas por e-mail, criamos regras de alerta no Prometheus para monitorar uptime e uso de CPU, e configuramos o Alertmanager para enviar notificações via SMTP. Além disso, validamos todo o processo simulando condições de alerta e observamos como as notificações são disparadas e resolvidas.</p>
</blockquote>
<hr />
<p>Fala pessoal tudo beleza?</p>
<p>Se você está lendo este artigo pela primeira vez, saiba que ele faz parte de uma série onde explico como montar uma stack de monitoramento usando Grafana e Prometheus com Docker e Docker Compose.</p>
<p>Neste artigo, parte 3, vou criar alertas personalizados para suas métricas no Prometheus e Alertmanager, que será responsável por enviar o evento para um e-mail.</p>
<h2 id="heading-pre-requisitos">Pré requisitos</h2>
<ul>
<li><p>Ter uma instância do Prometheus em execução.</p>
</li>
<li><p>Ter acompanhado os artigos anteriores onde subimos uma stack do Grafana e Prometheus.</p>
</li>
</ul>
<h2 id="heading-instalando-o-alertmanager">Instalando o Alertmanager</h2>
<p>O Alertmanager é um software que lida com alertas enviados por aplicativos clientes, como o servidor Prometheus. Ele é responsável em gerenciar esses alertas recebidos e encaminhar para algum cliente que esteja pré configurado, como um e-mail.</p>
<p>Com ele também é possível configurar o modo de silenciamento, ou seja, silenciar algum alerta já esperado por algumas horas ou apenas para o fim de semana.</p>
<p>Para subir uma instância do Alertmanager, iremos usar o docker. Para isso, utilize a stack que estamos criando nessa série de artigo e abra o arquivo <code>docker-compose.yaml</code>. Você pode encontrar no meu <a target="_blank" href="https://github.com/aristidesneto/stack-de-monitoramento-grafana-e-prometheus?ref=aristides.dev">Github</a>.</p>
<p>Para nosso exemplo de hoje com o Alertmanager, iremos usar o Node Exporter em uma imagem docker. No arquivo <code>docker-comose.yaml</code> já existe um serviço para ele, que está comentado. Descomente o bloco referente ao node_exporter e adicione logo abaixo o bloco do Alertmanager:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: docker-compose.yaml</span>
<span class="hljs-attr">services:</span>

  <span class="hljs-comment"># ...services</span>

  <span class="hljs-comment"># Exemplo de usar o exporter com o Docker ao invés do binário</span>
  <span class="hljs-attr">node_exporter:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">quay.io/prometheus/node-exporter:latest</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">node_exporter</span>
    <span class="hljs-attr">command:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--path.rootfs=/host'</span>
    <span class="hljs-attr">pid:</span> <span class="hljs-string">host</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'/:/host:ro,rslave'</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span>

  <span class="hljs-attr">alertmanager:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">prom/alertmanager:v0.25.0</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">alertmanager</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./alertmanager/:/etc/alertmanager/</span>
    <span class="hljs-attr">command:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--config.file=/etc/alertmanager/config.yaml'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--storage.path=/alertmanager'</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">9093</span><span class="hljs-string">:9093</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span>
</code></pre>
<p>Explicando algumas linhas importantes do Alertmanager no arquivo <code>docker-compose.yaml</code>:</p>
<ol>
<li><p>No serviço <code>alertmanager</code>, informamos a versão do Alertmanager que será utilizada que é a <code>0.25.0</code> (a mais recente na data desse artigo).</p>
</li>
<li><p>Criamos um volume para mapear um arquivo de configuração para o Alertmanager, que ao iniciar o serviço essa configuração será aplicada.</p>
</li>
<li><p>Em <code>command</code>, estamos passando dois parâmetros para o Alertmanager, o primeiro <code>--config.file</code> informamos que o arquivo de configuração deve ser lido no diretório <code>/etc/alertmanager/config.yaml</code> e o segundo <code>--storage.path</code> informamos que o local de armazenamento dos dados serão armazenados no próprio host em <code>/alertmanager</code>.</p>
</li>
<li><p>Em <code>ports</code> estamos expondo a porta 9093 do host para a porta 9093 do container.</p>
</li>
<li><p>Em <code>networks</code> informamos ao docker-compose que iremos utilizar uma rede já criada por nós, que no caso é a rede <code>monitoring</code>.</p>
</li>
</ol>
<p>Agora vamos subir os containers na nossa stack. No diretório raiz do projeto, execute o comando do docker compose para subir todas as instâncias de containers:</p>
<pre><code class="lang-bash">$ docker-compose up -d
</code></pre>
<p>Verifique se todos os containers estão em execução:</p>
<pre><code class="lang-bash">$ docker-compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
alertmanager        <span class="hljs-string">"/bin/alertmanager -…"</span>   alertmanager        running             0.0.0.0:9093-&gt;9093/tcp, :::9093-&gt;9093/tcp
grafana             <span class="hljs-string">"/run.sh"</span>                grafana             running             0.0.0.0:3000-&gt;3000/tcp, :::3000-&gt;3000/tcp
node_exporter       <span class="hljs-string">"/bin/node_exporter …"</span>   node_exporter       running             9100/tcp
prometheus          <span class="hljs-string">"/bin/prometheus --c…"</span>   prometheus          running             0.0.0.0:9090-&gt;9090/tcp, :::9090-&gt;9090/tcp
</code></pre>
<p>Agora o Alertmanager está <code>running</code> e está acessível na porta 9093. Acesse o navegador com o endereço <code>localhost:9093</code> e confirme se o Alertmanager irá carregar.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047351674/65aa3398-6050-4e3a-987e-00269017684b.png" alt="Tela inicial Alertmanager" /></p>
<p>Tela inicial Alertmanager</p>
<h2 id="heading-integrando-alertmanager-com-prometheus">Integrando Alertmanager com Prometheus</h2>
<p>Já temos nossa instância do Alertmanager em execução, precisamos agora configurar o Prometheus para qualquer evento de alerta seja enviado ao Alertmanager e ele ser responsável em disparar para um cliente.</p>
<p>Abra o arquivo de configuração do Prometheus que está em <code>prometheus/prometheus.yaml</code>. Adicione a seguinte entrada no final do arquivo referenciando ao Alertmanager:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># ...</span>

<span class="hljs-attr">alerting:</span>
  <span class="hljs-attr">alertmanager:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">scheme:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">static_configs:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> 
      <span class="hljs-bullet">-</span> <span class="hljs-string">'alertmanager:9093'</span>
</code></pre>
<p>prometheus/prometheus.yaml</p>
<p>Nesse bloco de código, informamos ao Prometheus que existe agora um cliente de alerta. Na sessão <code>alerting</code> criamos um, chamado <code>alertmanager</code>. Informamos que o Alertmanager está rodando no protocolo <code>http</code> e no host <code>alertmanager:9093</code>.</p>
<p>Vamos validar se o Prometheus reconheceu essa nossa configuração. Para isso reinicie o serviço do Prometheus com o comando:</p>
<pre><code class="lang-bash">$ docker-compose restart prometheus
</code></pre>
<p>Abra o Prometheus no seu navegador em <code>localhost:9090</code> e navegue no menu <code>Status -&gt; Runtime &amp; Build Information</code>. Certifique que na sessão Alertmanagers, agora há um endpoint apontando para a instância do Alertmanager.</p>
<p>Com isso já temos a integração entre os dois serviços. Agora é necessário criar os alertas no Prometheus e quando acionado será enviado um evento de alerta para o Alertmanager.</p>
<h2 id="heading-criando-regras-de-alertas-no-prometheus">Criando regras de alertas no Prometheus</h2>
<p>Agora que já temos nossa instância em execução, vamos criar as regras de alertas no Prometheus. Para isso vamos criar um arquivo chamado <code>alert.rules</code> no diretório do <code>prometheus</code>.</p>
<p>Adicone o seguinte conteúdo nesse arquivo:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">groups:</span>

<span class="hljs-comment"># Grupo de alertas: hosts</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">hosts</span>
  <span class="hljs-attr">rules:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">Uptime</span>
    <span class="hljs-attr">expr:</span> <span class="hljs-string">up</span> <span class="hljs-string">==</span> <span class="hljs-number">0</span>
    <span class="hljs-attr">for:</span> <span class="hljs-string">15s</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-attr">severity:</span> <span class="hljs-string">critical</span>
    <span class="hljs-attr">annotations:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">"O servidor <span class="hljs-template-variable">{{ $labels.alias }}</span> está offline!"</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">"O servidor <span class="hljs-template-variable">{{ $labels.alias }}</span> parou de responder."</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">Uso</span> <span class="hljs-string">de</span> <span class="hljs-string">CPU</span>
    <span class="hljs-attr">expr:</span> <span class="hljs-number">100</span> <span class="hljs-bullet">-</span> <span class="hljs-string">(avg(rate(node_cpu_seconds_total{mode="idle"}[2m]))</span> <span class="hljs-string">by(instance)</span> <span class="hljs-string">*</span> <span class="hljs-number">100</span><span class="hljs-string">)</span> <span class="hljs-string">&gt;</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-attr">severity:</span> <span class="hljs-string">warning</span>
    <span class="hljs-attr">annotations:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Consumo</span> <span class="hljs-string">alto</span> <span class="hljs-string">de</span> <span class="hljs-string">CPU</span> <span class="hljs-string">(instance</span> {{ <span class="hljs-string">$labels.instance</span> }}<span class="hljs-string">)</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">"O servidor <span class="hljs-template-variable">{{ $labels.alias }}</span> está com o consumo de CPU acima de 80%\n  VALUE = <span class="hljs-template-variable">{{ $value }}</span>\n  LABELS = <span class="hljs-template-variable">{{ $labels }}</span>"</span>
</code></pre>
<p>prometheus/alert.rules</p>
<p>Foram criadas duas regras de alertas, onde o primeiro é o Uptime, que verifica se a instância está no ar e a segunda verifica o uso de CPU se está acima de 80% em um período de 2 minutos.</p>
<p>Lembre-se de validar as regras para seu ambiente antes de usar em produção.</p>
<p>Após criarmos o arquivo de regras, devemos informar ao Prometheus que ele deve carregar esse arquivo a aplicar suas configurações. Para isso abra novamente o arquivo <code>prometheus/prometheus.yaml</code> e adicione no inicio do arquivo o seguinte trecho:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: prometheus/prometheus.yaml</span>
<span class="hljs-attr">global:</span>
  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>

<span class="hljs-comment"># Carrega arquivo de regras de alertas</span>
<span class="hljs-attr">rule_files:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">"alert.rules"</span>
</code></pre>
<p>Nesse arquivo adicionamos a sessão de regras <code>rules_files</code>, que indica que o Prometheus deve carregar o arquivo correspondente que contém as regras de alertas.</p>
<p>Agora reinicie o Prometheus para aplicar as configurações:</p>
<pre><code class="lang-bash">$ docker-compose restart prometheus
</code></pre>
<p>Para validarmos todo esse processo, vamos abrir o Prometheus no navegador e acessar o menu <code>Status -&gt; Rules</code>. Você verá a seguinte tela com as regras criadas.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047393058/e9831bd9-2e52-4125-803d-47bb188eea1d.png" alt="Regras de alertas para Prometheus" /></p>
<p>Regras de alertas para Prometheus</p>
<p>Acesse também o menu <code>Alerts</code>, você verá as regras criadas e o status de cada uma.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047425614/f5fb66cc-04cb-460a-bbc9-c304e7f8f7dc.png" alt="Prometheus rules status Inactive" /></p>
<p>Prometheus rules status Inactive</p>
<p>Nessa tela, é possível acompanhar o status de cada evento de alerta. Podemos ver os status:</p>
<ul>
<li><p><strong>Inactive</strong>: Esse status significa que não há nenhum evento de alerta que possa ser acionado, ou seja, está tudo bem!</p>
</li>
<li><p><strong>Pending</strong>: Quando um alerta entra nesse status, quer dizer que alguma regra definida foi acionada, ficará nesse status pelo tempo determinado no arquivo de regras.</p>
</li>
<li><p><strong>Firing</strong>: Após o status de <code>Pending</code>, o status <code>Firing</code> entra em ação, após um evento ficar em espera por alguns segundos seu status muda para <code>Firing</code> e automaticamente é dispardo um alerta para o Alertmanager.</p>
</li>
</ul>
<h2 id="heading-configurando-alertmanager-com-smtp">Configurando Alertmanager com SMTP</h2>
<p>Agora é hora de configurar o Alertmanager com as informações de credenciais de e-mail usando o SMTP. Para isso, crie um arquivo <code>config.yaml</code> no diretório <code>alertmanager</code>. Cole o seguinte conteúdo no arquivo:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: alertmanager/config.yaml</span>
<span class="hljs-attr">global:</span>
  <span class="hljs-attr">smtp_smarthost:</span> <span class="hljs-string">smtp.email.com:587</span>
  <span class="hljs-attr">smtp_from:</span> <span class="hljs-string">noreply@email.com</span>
  <span class="hljs-attr">smtp_auth_username:</span> <span class="hljs-string">noreply@email.com</span>
  <span class="hljs-attr">smtp_auth_password:</span> <span class="hljs-string">suaSenhaAqui</span>

<span class="hljs-attr">route:</span>
  <span class="hljs-attr">group_by:</span> [<span class="hljs-string">'alertname'</span>]
  <span class="hljs-attr">group_interval:</span> <span class="hljs-string">5m</span>
  <span class="hljs-attr">group_wait:</span> <span class="hljs-string">30s</span>
  <span class="hljs-attr">receiver:</span> <span class="hljs-string">'email'</span>
  <span class="hljs-attr">repeat_interval:</span> <span class="hljs-string">3h</span>

<span class="hljs-attr">receivers:</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">'email'</span>
    <span class="hljs-attr">email_configs:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">to:</span> <span class="hljs-string">alerta@gmail.com</span>
      <span class="hljs-attr">send_resolved:</span> <span class="hljs-literal">true</span>
</code></pre>
<p><strong>Você deve adicionar as suas configurações SMTP do seu provedor de e-mail.</strong></p>
<p>Basicamente neste arquivo eu digo ao Alertmanager que ele deverá enviar um e-mail para <code>receivers.name.email_configs: alerta@gmail.com</code> após 30 segundos <code>route.group_wait</code> que ele receber o alerta.</p>
<p>Em <code>receivers.name.email_configs.send_resolved: true</code> informo que após a resolução de um incidente, o Alertmanager deverá enviar outro e-mail alertando que o incidente foi resolvido.</p>
<p>Defino o remetente padrão em <code>route.receiver: email</code>. Na sessão <code>global</code> informo as credenciais de e-mail SMTP.</p>
<p>Esse é um arquivo simples de configuração, é possível realizar mais configurações, realizar <code>matchers</code> dos e-mails e enviar para determinados grupos. Caso queira entender mais como funciona, acesse a <a target="_blank" href="https://prometheus.io/docs/alerting/latest/configuration/?ref=aristides.dev">documentação oficial</a>.</p>
<p>Após criarmos esse arquivo de configuração, reinicie o serviço do Prometheus e Alertmanager:</p>
<pre><code class="lang-bash">$ docker-compose restart alertmanager

$ docker-compose restart prometheus
</code></pre>
<h2 id="heading-validando-toda-a-configuracao">Validando toda a configuração</h2>
<p>Vamos validar todo esse processo de configuração que fizemos, primeiro vamos ver o que acontece quando um dos hosts que está sendo monitorado fica down, por algum motivo seu servidor Linux não está mais acessível exportando as métricas, métricas essas que o Prometheus utiliza para saber da saúde do seu servidor.</p>
<p>Para simular um servidor que esteja down, vamos parar o container do <code>node_exporter</code>. Execute o comando:</p>
<pre><code class="lang-bash">$ docker-compose stop node_exporter

$ docker-compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
alertmanager        <span class="hljs-string">"/bin/alertmanager -…"</span>   alertmanager        running             0.0.0.0:9093-&gt;9093/tcp, :::9093-&gt;9093/tcp
grafana             <span class="hljs-string">"/run.sh"</span>                grafana             running             0.0.0.0:3000-&gt;3000/tcp, :::3000-&gt;3000/tcp
node_exporter       <span class="hljs-string">"/bin/node_exporter …"</span>   node_exporter       exited (143)        
prometheus          <span class="hljs-string">"/bin/prometheus --c…"</span>   prometheus          running             0.0.0.0:9090-&gt;9090/tcp, :::9090-&gt;9090/tcp
</code></pre>
<p>Podemos ver que agora o container <code>node_exporter</code> está com o status de <code>exited</code>. Abra o Prometheus no navegador e acessa o menu <code>Alerts</code>.</p>
<p>Veja que agora o status está como <code>Pending</code> e depois de alguns segundos ficará como <code>Firing</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047470717/7cca3872-ae3d-4e62-8d73-e584db3474cb.png" alt="Prometheus rules status pending" /></p>
<p>Prometheus rules status pending</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047488726/b5608ad5-7489-411e-be9c-b397ddd1bdc9.png" alt="Prometheus rules status firing" /></p>
<p>Prometheus rules status firing</p>
<p>Se abrirmos agora o Alertmanager no navegador, veremos que foi enviado um alerta para ele.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047513909/f507c96e-c5ab-4de6-850c-648154c1cdfa.png" alt="Alertmanager alerta recebido" /></p>
<p>Alertmanager alerta recebido</p>
<p>Dentro de alguns instantes será enviado um e-mail informando que host está down, conforme imagem:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047562681/cba4d2a4-949b-471b-8799-838e792db3f2.jpeg" alt="Prometheus Alert Host Down" /></p>
<p>Prometheus Alert Host Down</p>
<p>Show de bola. Agora vamos consertar esse host que está fora, execute o comando abaixo para subir o container do <code>node_exporter</code> e vamos aguardar até que o Prometheus consiga se comunicar novamente com ele.</p>
<pre><code class="lang-bash">$ docker-compose up -d

$ docker-compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
alertmanager        <span class="hljs-string">"/bin/alertmanager -…"</span>   alertmanager        running             0.0.0.0:9093-&gt;9093/tcp, :::9093-&gt;9093/tcp
grafana             <span class="hljs-string">"/run.sh"</span>                grafana             running             0.0.0.0:3000-&gt;3000/tcp, :::3000-&gt;3000/tcp
node_exporter       <span class="hljs-string">"/bin/node_exporter …"</span>   node_exporter       running             9100/tcp
prometheus          <span class="hljs-string">"/bin/prometheus --c…"</span>   prometheus          running             0.0.0.0:9090-&gt;9090/tcp, :::9090-&gt;9090/tcp
</code></pre>
<p>Veja no Prometheus no menu <code>Alerts</code> que o evento de alerta que antes estava como <code>Firing</code> voltará ao seu estado normal e desejado que é <code>Inactive</code>. Quando tudo tiver "verdinho" o Prometheus irá enviar um novo evento ao Alertmanager, só que agora um evento que informa que o host voltou a responder.</p>
<p>O Alertmanager por sua vez, irá remover o alerta e enviará um outro e-mail informando que o problema de host down foi resolvido.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047582614/1c949abb-6cc2-47b6-9ec4-5bb90a6ac044.jpeg" alt="Prometheus Alert Host UP" /></p>
<p>Prometheus Alert Host UP</p>
<p>Mais uma vez, show de bola. Caso você queira fazer um teste também pelo alto consumo de CPU para receber o alerta, fique a vontade, pode até dimunuir o percentual de 80% para 30% apenas para validar o envio.</p>
<p>Acredito que chegamos ao fim desse artigo com o recebimento desse alerta.</p>
<h2 id="heading-download-codigo">Download código</h2>
<p>Todo o código dessa stack você pode baixar pelo link do <a target="_blank" href="https://github.com/aristidesneto/stack-de-monitoramento-grafana-e-prometheus?ref=aristides.dev">Github</a>.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Nesse artigo fizemos a instalação, configuração e integração do Alertmanager com o Prometheus. Criamos duas regras de alertas e validamos o envio de uma delas.</p>
<p>O Alertmanager em si, o uso dele é bem simples, não estarei abordando ele aqui, mas acessando a página dele é possível criar alertas por lá e silenciar um alerta por um período de tempo. Para saber mais sobre o template de notificação acesse a <a target="_blank" href="https://prometheus.io/docs/alerting/latest/notifications/?ref=aristides.dev">documentação</a>.</p>
<p>Espero que tenha ajudado até aqui.</p>
<p>Se você encontrar algum erro, sugestões de melhorias ou se tiver alguma dúvida, deixe seu comentário abaixo.</p>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Instalação e configuração do Node Exporter em servidores Linux]]></title><description><![CDATA[Neste artigo, mostramos como instalar e configurar o Node Exporter em um servidor Linux para exportar métricas que o Prometheus possa coletar e armazenar. Os passos incluem fazer download do Node Exporter, configurá-lo como um serviço do systemd, e g...]]></description><link>https://aristides.dev/instalacao-e-configuracao-do-node-exporter-em-servidores-linux-parte-2</link><guid isPermaLink="true">https://aristides.dev/instalacao-e-configuracao-do-node-exporter-em-servidores-linux-parte-2</guid><category><![CDATA[Devops]]></category><category><![CDATA[#prometheus]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Thu, 08 Dec 2022 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735047061875/6e054bef-56a1-44b6-84c8-36e6aeabccff.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Neste artigo, mostramos como instalar e configurar o Node Exporter em um servidor Linux para exportar métricas que o Prometheus possa coletar e armazenar. Os passos incluem fazer download do Node Exporter, configurá-lo como um serviço do systemd, e garantir que inicie automaticamente com o sistema. Além disso, explicamos como ajustar o arquivo de configuração do Prometheus para iniciar a coleta dessas métricas e sugerimos um dashboard no Grafana para visualizá-las. Também discutimos a opção de usar o Node Exporter com Docker e fornecemos links para mais recursos e para o código completo no GitHub.</p>
</blockquote>
<hr />
<p>Fala pessoal tudo beleza?</p>
<p>Se você está lendo este artigo pela primeira vez, saiba que esta é uma série de artigos onde explico como montar uma stack de monitoramento usando Grafana e Prometheus com Docker e Docker Compose.</p>
<p>Neste artigo, parte 2, vou configurar um servidor Linux para exportar métricas que o Prometheus pode ler e armazenar em seu banco de dados, permitindo a análise em tempo real através de dashboards do Grafana.</p>
<p>Para isso é necessário instalar e configurar o node exporter, que é um arquivo binário que irá obter várias métricas de seu servidor Linux e expor em um endpoint HTTP. Para saber mais sobre o node exporter <a target="_blank" href="https://github.com/prometheus/node_exporter">acesse a documentação</a>.</p>
<h2 id="heading-pre-requisitos">Pré requisitos</h2>
<ul>
<li><p>Ter acompanhado a parte 1 dessa série (<a target="_blank" href="https://aristides.dev/monitorando-seus-servidores-com-grafana-e-prometheus">Monitorando seus servidores com Grafana e Prometheus</a>)</p>
</li>
<li><p>Um servidor Linux instanciado em qualquer clodud ou em uma VM, por exemplo o Virtual Box</p>
</li>
<li><p>Acesso via SSH a instância do servidor</p>
</li>
</ul>
<h2 id="heading-instalando-node-exporter">Instalando Node Exporter</h2>
<p>Para a instalação do node exporter você pode <a target="_blank" href="https://github.com/prometheus/node_exporter/releases">acessar o github</a> do projeto e verificar qual a última versão disponível para download. No momento desse artigo, a última versão estável é a <code>1.5.0</code>.</p>
<p>Acesse seu servidor via SSH e vamos começar com a instalação do exporter. Crie uma variável de ambiente apenas para facilitar no processo de instalação que irá conter o valor da versão do node exporter:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> VERSION=1.5.0
</code></pre>
<p>Os comandos listados abaixo, irão realizar o download do arquivo, descompactar e mover o arquivo binário do node exporter para o diretório <code>/usr/local/bin</code>.</p>
<pre><code class="lang-bash">curl -LO https://github.com/prometheus/node_exporter/releases/download/v<span class="hljs-variable">${VERSION}</span>/node_exporter-<span class="hljs-variable">${VERSION}</span>.linux-amd64.tar.gz

tar -xvf node_exporter-<span class="hljs-variable">${VERSION}</span>.linux-amd64.tar.gz

sudo mv node_exporter-<span class="hljs-variable">${VERSION}</span>.linux-amd64/node_exporter /usr/<span class="hljs-built_in">local</span>/bin/
</code></pre>
<h2 id="heading-configurando-node-exporter">Configurando Node Exporter</h2>
<p>Até agora apenas baixamos e copiamos o binário para seu diretório, mas ainda é necessário configurar o serviço.</p>
<p>Crie um usuário no Linux para que possamos usar ele para executar o node exporter. Esse usuário só irá executar o binário, não sendo possível acessar via SSH com ele:</p>
<pre><code class="lang-bash">sudo useradd -rs /bin/<span class="hljs-literal">false</span> node_exporter
</code></pre>
<p>Crie um arquivo de inicialização no <code>systemd</code> para o binário:</p>
<pre><code class="lang-bash">sudo vim /etc/systemd/system/node_exporter.service
</code></pre>
<p>Cole o seguinte conteúdo dentro do arquivo <code>node_exporter.service</code>:</p>
<pre><code class="lang-bash">[Unit]
Description=Node Exporter
After=network.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/<span class="hljs-built_in">local</span>/bin/node_exporter

[Install]
WantedBy=multi-user.target
</code></pre>
<p>Reinicie o deamon e inicie o serviço <code>node_exporter</code> que acabamos de criar:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Reinicia o deamon e inicia o serviço </span>
sudo systemctl daemon-reload

sudo systemctl start node_exporter
</code></pre>
<p>Para gerenciar o serviço do node_exporter, você poderá usar os comandos:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Os comandos start / restart / status / stop estarão disponíveis para o serviço</span>
sudo systemctl status node_exporter
</code></pre>
<p>Após o start do serviço, se tudo ocorrer bem, o resultado deve ser o seguinte:</p>
<pre><code class="lang-bash">aristides@my-server:~$ sudo systemctl status node_exporter 
● node_exporter.service - Node Exporter
   Loaded: loaded (/etc/systemd/system/node_exporter.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2022-10-17 00:25:38 -03; 20s ago
 Main PID: 25276 (node_exporter)
    Tasks: 5 (<span class="hljs-built_in">limit</span>: 4915)
   CGroup: /system.slice/node_exporter.service
           └─25276 /usr/<span class="hljs-built_in">local</span>/bin/node_exporter

Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - sockstat"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - stat"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - textfile"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - time"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - timex"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - uname"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - vmstat"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - xfs"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">" - zfs"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:104"</span>
Oct 17 00:25:38 my-server node_exporter[25276]: time=<span class="hljs-string">"2022-10-17T00:25:38-03:00"</span> level=info msg=<span class="hljs-string">"Listening on :9100"</span> <span class="hljs-built_in">source</span>=<span class="hljs-string">"node_exporter.go:170"</span>
</code></pre>
<p>Como pode ser visto no log, o serviço está escutando na porta 9100. Se você acessar o endereço IP da sua instância <a target="_blank" href="http://ip-servidor:9100/metrics?ref=aristides.dev"><code>http://IP-SERVIDOR:9100/metrics</code></a>, terá um resultado semelhante a esse:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.</span>
<span class="hljs-comment"># TYPE go_gc_duration_seconds summary</span>
go_gc_duration_seconds{quantile=<span class="hljs-string">"0"</span>} 1.9151e-05
go_gc_duration_seconds{quantile=<span class="hljs-string">"0.25"</span>} 5.6041e-05
go_gc_duration_seconds{quantile=<span class="hljs-string">"0.5"</span>} 9.2601e-05
go_gc_duration_seconds{quantile=<span class="hljs-string">"0.75"</span>} 0.000134661
go_gc_duration_seconds{quantile=<span class="hljs-string">"1"</span>} 0.000524014
go_gc_duration_seconds_sum 0.250765764
go_gc_duration_seconds_count 1720
<span class="hljs-comment"># HELP go_goroutines Number of goroutines that currently exist.</span>
<span class="hljs-comment"># TYPE go_goroutines gauge</span>
go_goroutines 8
<span class="hljs-comment"># HELP go_info Information about the Go environment.</span>
<span class="hljs-comment"># TYPE go_info gauge</span>
go_info{version=<span class="hljs-string">"go1.19.1"</span>} 1
<span class="hljs-comment"># HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.</span>
<span class="hljs-comment"># TYPE go_memstats_alloc_bytes gauge</span>
go_memstats_alloc_bytes 2.153176e+06
...
</code></pre>
<p>Pronto, você já está expondo as métricas do seu servidor Linux para que uma instância do Prometheus possa ler e armazenar em seu banco de dados.</p>
<p>Ative o serviço do node exporter para que sempre que o servidor reiniciar, o serviço seja iniciado automaticamente:</p>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> node_exporter
</code></pre>
<p>Agora para seguir a diante, iremos usar a stack que criamos na parte 1 dessa séria, se você ainda não viu, recomendo dar uma olhada <a target="_blank" href="https://aristides.dev/monitorando-seus-servidores-com-grafana-e-prometheus">aqui nesse link</a> para que você possa acompanhar sem grandes dúvidas as alterações necessárias no arquivo de configuração do Prometheus.</p>
<h2 id="heading-configurando-o-prometheus-para-raspar-as-metricas">Configurando o Prometheus para "raspar" as métricas</h2>
<p>Já estamos com o nosso servidor expondo as métricas na porta 9100, o que precisamos agora é configurar o Prometheus para que ele <em>raspe</em> esse servidor e colete suas métricas.</p>
<p>Para isso, abra o arquivo <code>prometheus/prometheus.yaml</code>. O arquivo contém esse conteúdo atualmente:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">global:</span>
  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>  <span class="hljs-comment"># Por padrão, o alvo será 'raspado' a cada 15 segundos</span>

<span class="hljs-attr">scrape_configs:</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'prometheus'</span>

    <span class="hljs-comment"># Esse valor irá sobreescrever o valor padrão global para esse alvo</span>
    <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">5s</span>

    <span class="hljs-attr">metrics_path:</span> <span class="hljs-string">/metrics</span> <span class="hljs-comment"># Endpoint padrão para a raspagem das métricas</span>
    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:9090'</span>]
</code></pre>
<p>Adicione a seguinte entrada:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">global:</span>
  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>  <span class="hljs-comment"># Por padrão, o alvo será 'raspado' a cada 15 segundos</span>

<span class="hljs-attr">scrape_configs:</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'prometheus'</span>

    <span class="hljs-comment"># Esse valor irá sobreescrever o valor padrão global para esse alvo</span>
    <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">5s</span>

    <span class="hljs-attr">metrics_path:</span> <span class="hljs-string">/metrics</span> <span class="hljs-comment"># Endpoint padrão para a raspagem das métricas</span>
    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:9090'</span>]

  <span class="hljs-comment"># Nova entrada adicionada</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'node_exporter'</span>
    <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">5s</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'IP-SEU-SERVIDOR:9100'</span>] <span class="hljs-comment"># &lt;-- Insira o IP público do servidor</span>
        <span class="hljs-attr">labels:</span>
          <span class="hljs-attr">alias:</span> <span class="hljs-string">webserver</span>
</code></pre>
<p>Como visto acima, foi adicionado um novo JOB chamado de <code>node_exporter</code>, esse nome é sugestivo ao exporter, mas pode ser qualquer coisa.</p>
<p>Após adicionar a entrada de um novo target, reinicie o container docker caso ele esteja em execução ou inicie todos os containers:</p>
<pre><code class="lang-bash">docker-compose restart prometheus
<span class="hljs-comment"># ou</span>
docker-compose up -d
</code></pre>
<p>A partir desse momento, sua instância do Prometheus já deve estar recebendo os dados da sua instância Linux, dados esses de CPU, memória RAM, uso de disco, rede entre várias outras métricas.</p>
<h2 id="heading-dashboard-node-exporter-full">Dashboard Node Exporter Full</h2>
<p>Para visualizar métricas do node exporter, podemos utilizar um dashboard que está disponível no site do Grafana, você pode visualizar acessando esse link <a target="_blank" href="https://grafana.com/grafana/dashboards/1860-node-exporter-full">https://grafana.com/grafana/dashboards/1860-node-exporter-full</a>.</p>
<p>Para usar esse dashboard, copie esse ID <code>1860</code> e no painel do Grafana, acesse o menu Dashboard e Importar. Insira esse ID na caixa de texto e clique em Carregar.</p>
<p>Após a importação, você terá esse belo Dashboard, com métricas de vários recursos da sua máquina. Show de bola!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046909351/d094721b-3dc4-4d16-b744-32b8b846bb18.png" alt="Dashboard Node Exporter Full - Imagem 1" class="image--center mx-auto" /></p>
<p>Dashboard Node Exporter Full - Imagem 1</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046891353/048c80c6-242f-4b81-858c-1624d6c711ca.png" alt="Dashboard Node Exporter Full - Imagem 2" class="image--center mx-auto" /></p>
<p>Dashboard Node Exporter Full - Imagem 2</p>
<h2 id="heading-download-codigo">Download código</h2>
<p>Todo o código dessa stack você pode baixar pelo link do <a target="_blank" href="https://github.com/aristidesneto/stack-de-monitoramento-grafana-e-prometheus">Github</a>.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Nesse artigo fizemos a instalação e configuração do binário do node exporter, que irá expor métricas de CPU, memória RAM, uso de diso, de rede e tantas outras métricas.</p>
<p>Para seu conhecimento, caso você queira usar o node exporter através de uma imagem docker também é possível. Ao invés de baixar, instalar e configurar seu binário, é possível usar o docker para executar o exporter e expor a métricas da sua máquina Linux. Veja mais nesse <a target="_blank" href="https://github.com/prometheus/node_exporter#docker">link</a>.</p>
<p>Ao usar o docker, se atente ao arquivo de configuração <code>prometheus.yaml</code>, pois deverá ajustar o target para que acesse o endpoint do container docker.</p>
<p>Espero que tenha ajudado até aqui.</p>
<p>Se você encontrar algum erro, sugestões de melhorias ou se tiver alguma dúvida, deixe seu comentário abaixo.</p>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Monitorando seus servidores com Grafana e Prometheus]]></title><description><![CDATA[Neste artigo, você aprenderá a criar uma stack de monitoramento utilizando Grafana e Prometheus em containers com Docker e Docker-compose. Abordamos a configuração inicial do Grafana, incluindo volumes para armazenar dados e provisionar datasources a...]]></description><link>https://aristides.dev/monitorando-seus-servidores-com-grafana-e-prometheus</link><guid isPermaLink="true">https://aristides.dev/monitorando-seus-servidores-com-grafana-e-prometheus</guid><category><![CDATA[Devops]]></category><category><![CDATA[Grafana]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Wed, 05 Oct 2022 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046508676/128ca170-acd0-47c2-95c6-d39b02e0f987.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Neste artigo, você aprenderá a criar uma stack de monitoramento utilizando Grafana e Prometheus em containers com Docker e Docker-compose. Abordamos a configuração inicial do Grafana, incluindo volumes para armazenar dados e provisionar datasources automaticamente. Também mostramos como configurar o Prometheus para monitorar alvos específicos. Ao final, você terá um ambiente onde poderá acessar o Grafana e o Prometheus através de seus respectivos links para visualizar e gerenciar métricas em tempo real. Este é o ponto de partida para montar uma solução de monitoramento pronta para produção.</p>
</blockquote>
<hr />
<p>Fala pessoal tudo beleza?</p>
<p>Neste artigo, vou mostrar como configurar uma stack de monitoramento usando Grafana e Prometheus. Vou usar Docker para criar os containers com as imagens e Docker Compose para definir os requisitos dos containers.</p>
<h2 id="heading-pre-requisitos">Pré requisitos</h2>
<ul>
<li>Docker e Docker-compose instalados em sua máquina</li>
</ul>
<h2 id="heading-configurando-o-grafana">Configurando o Grafana</h2>
<p>O Grafana é um projeto Open Source que centraliza a análise e a visualização das métricas através de gráficos de aplicações, de serviços de nuvens, de serviços como o kubernetes entre outros.</p>
<p>Para saber mais acesse a <a target="_blank" href="https://grafana.com/oss/grafana/">documentação oficial</a>.</p>
<p>Para iniciar, crie uma rede no docker para podermos usar em nossa stack de monitoramento:</p>
<pre><code class="lang-bash">docker network create monitoring
</code></pre>
<p>Crie o diretório <code>grafana-prometheus</code> e em seguida crie o arquivo <code>docker-compose.yaml</code>. De início esse arquivo terá o conteúdo abaixo:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: docker-compose.yaml</span>
<span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">grafana_data:</span>

<span class="hljs-attr">networks:</span>
  <span class="hljs-attr">monitoring:</span>
    <span class="hljs-attr">external:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">services:</span>

  <span class="hljs-attr">grafana:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">grafana/grafana:9.1.6</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">grafana</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">grafana_data:/var/lib/grafana</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./grafana/provisioning:/etc/grafana/provisioning/datasources</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_INSTALL_PLUGINS=${GF_INSTALL_PLUGINS}</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">3000</span><span class="hljs-string">:3000</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span>
</code></pre>
<p>Explicando algumas linhas importantes do arquivo <code>docker-compose.yaml</code>:</p>
<ol>
<li><p><code>networks:</code> Nessa linha informamos ao docker-compose que iremos utilizar uma rede já criada por nós (<code>external: true</code>), que no caso é a rede <code>monitoring</code>.</p>
</li>
<li><p>No serviço <code>grafana</code>, informamos a versão do Grafana que será utilizada que é a <code>9.1.6</code> (a mais recente na data desse artigo).</p>
</li>
<li><p>Criamos dois volumes, o primeiro para armazenar o banco de dados do Grafana, com as informações de usuários, configurações, dashboards, etc. O segundo volume é o mapeamento da pasta de provisionamento onde definimos o <code>datasource</code> padrão que será usado pelo Grafana assim que ele iniciar.</p>
</li>
<li><p>Nas variáveis de ambiente passamos alguns parâmetros do Grafana, esses valores estão sendo obtidos através de um arquivo <code>.env</code> na raiz do projeto.</p>
</li>
<li><p>Em <code>ports</code> estamos expondo a porta 3000 do host para a porta 3000 do container.</p>
</li>
</ol>
<p>Para saber mais sobre todas as possíveis configurações do Grafana, acesse a sessão de <a target="_blank" href="https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/">Configuração da documentação</a>.</p>
<h2 id="heading-provisionando-datasource">Provisionando datasource</h2>
<p>Como visto no serviço do Grafana, foi criado um volume que podemos adicionar um datasource de forma automatizada assim que o serviço do Grafana subir. Para isso, crie um arquivo chamado <code>ds-prometheus.yaml</code> dentro do diretório <code>grafana/provisioning/</code>, e cole o conteúdo abaixo:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: grafana/provisioning/ds-prometheus.yaml</span>
<span class="hljs-attr">datasources:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Prometheus</span>
  <span class="hljs-attr">access:</span> <span class="hljs-string">proxy</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">prometheus</span>
  <span class="hljs-attr">url:</span> <span class="hljs-string">http://prometheus:9090</span>
</code></pre>
<p>Nesse arquivo informamos ao Grafana que ele irá cadastrar um datasource com o nome de <code>Prometheus</code>, também é informamdo outras configurações, como a url da instância do Prometheus e o modo de acesso.</p>
<p>Esse mesmo passo pode ser feito manualmente na interface do Grafana, acessando o menu <code>Configuração</code> e <code>Datasources</code>.</p>
<h2 id="heading-arquivo-de-variaveis">Arquivo de variáveis</h2>
<p>Como visto acima, o Grafana recebe algumas variáveis que estão vindo do arquivo <code>.env</code>. Esse arquivo será responsável em armazenar todas as informações de senhas e algumas outras.</p>
<p>O uso desse arquivo torna o processo mais simples para manutenção, podendo reaproveitar variáveis quando necessário.</p>
<p>Crie o arquivo <code>.env</code> na raiz do diretório <code>grafana-prometheus</code> e cole o conteúdo abaixo:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Arquivo: .env</span>
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=password
GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource,grafana-worldmap-panel,percona-percona-app
</code></pre>
<h2 id="heading-configurando-o-prometheus">Configurando o Prometheus</h2>
<p>O Prometheus é um projeto Open Source usado para monitoramento e alertas. O Prometheus busca as métricas nos targets e grava em um banco de séries temporais, podendo visualizar essas métricas em tempo real.</p>
<p>Para saber mais acesse a <a target="_blank" href="https://prometheus.io/docs/introduction/overview/">documentação oficial</a>.</p>
<p>Vamos agora adicionar ao arquivo <code>docker-compose.yaml</code> criado, o serviço do Prometheus. Adicione após o serviço do Grafana as seguintes linhas:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: docker-compose.yaml</span>
<span class="hljs-string">...</span>
<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">grafana_data:</span>
  <span class="hljs-attr">prometheus_data:</span> <span class="hljs-string">&lt;--</span> <span class="hljs-string">volume</span> <span class="hljs-string">adicionado</span>

<span class="hljs-attr">services:</span>

  <span class="hljs-comment"># Serviço do Prometheus adicionado</span>
  <span class="hljs-attr">prometheus:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">prom/prometheus:v2.37.1</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">prometheus</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./prometheus/:/etc/prometheus/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">prometheus_data:/prometheus</span>
    <span class="hljs-attr">command:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--config.file=/etc/prometheus/prometheus.yaml'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--storage.tsdb.path=/prometheus'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.console.libraries=/etc/prometheus/console_libraries'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.console.templates=/etc/prometheus/consoles'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--storage.tsdb.retention.time=360h'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.enable-lifecycle'</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">9090</span><span class="hljs-string">:9090</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span>
</code></pre>
<p>Além do serviço do Prometheus, foi adicionado um volume para que o Prometheus possa salvar os dados dos seus alvos.</p>
<p>Explicando algumas linhas importantes do serviço do Prometheus:</p>
<ol>
<li><p>No serviço <code>prometheus</code> informamos a versão do Prometheus que será usada, que no caso é a <code>v2.37.1</code> (a mais recente no momento).</p>
</li>
<li><p>Criamos dois volumes, o primeiro é o diretório que contém o arquivo de configuração do Prometheus, o segundo volume é referente ao volume criado para salvar todas as métricas obtidas dos seus alvos.</p>
</li>
<li><p>Em <code>command</code> são passados alguns parâmetros de configuração para o Prometheus.</p>
</li>
<li><p>Em <code>ports</code> estamos expondo a porta 9000 do host para a porta 9000 do container.</p>
</li>
</ol>
<h2 id="heading-arquivo-de-configuracao-do-prometheus">Arquivo de configuração do Prometheus</h2>
<p>O Prometheus precisa de um aquivo que irá determinar quais são os alvos que ele terá que <em>raspar</em> para obter as métricas.</p>
<p>Esses alvos podem ser uma aplicação web, um servidor linux, um banco de dados, um serviço na nuvem e todos eles devem exportar essas métricas em algum endpoint HTTP para que o Prometheus possa <em>raspar</em> e gravar esses valores em seu banco.</p>
<p>Crie um arquivo chamado <code>prometheus.yaml</code> dentro do diretório prometheus. Esse arquivo terá o seguinte conteúdo:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: prometheus/prometheus.yaml</span>
<span class="hljs-attr">global:</span>
  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>  <span class="hljs-comment"># Por padrão, o alvo será 'raspado' a cada 15 segundos</span>

<span class="hljs-attr">scrape_configs:</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'prometheus'</span>

    <span class="hljs-comment"># Esse valor irá sobreescrever o valor padrão global para esse alvo</span>
    <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">5s</span>

    <span class="hljs-attr">metrics_path:</span> <span class="hljs-string">/metrics</span> <span class="hljs-comment"># Endpoint padrão para a raspagem das métricas</span>
    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:9090'</span>]
</code></pre>
<p>Nesse exemplo simples, o Prometheus irá se auto monitorar através de um endpoint, veja que o endpoint para isso é <code>localhost:9090/metrics</code>. Caso queira alterar o path padrão, basta alterar o atributo <code>metrics_path</code>.</p>
<p>Há diversas configurações possíveis para esse arquivo, você pode ver mais na <a target="_blank" href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration-file">documentação oficial</a>.</p>
<h2 id="heading-subindo-a-stack-de-monitoramento">Subindo a stack de monitoramento</h2>
<p>Agora que passamos pelos dois serviços e falamos um pouco sobre suas configurações, vamos ver isso funcionar. Mas antes, vamos ver como ficou todo o arquivo do <code>docker-compose.yaml</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Arquivo: docker-compose.yaml</span>
<span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">grafana_data:</span>
  <span class="hljs-attr">prometheus_data:</span>

<span class="hljs-attr">networks:</span>
  <span class="hljs-attr">monitoring:</span>
    <span class="hljs-attr">external:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">services:</span>

  <span class="hljs-attr">grafana:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">grafana/grafana:9.1.6</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">grafana</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">grafana_data:/var/lib/grafana</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./grafana/provisioning:/etc/grafana/provisioning/datasources</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">GF_INSTALL_PLUGINS=${GF_INSTALL_PLUGINS}</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">3000</span><span class="hljs-string">:3000</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span>

  <span class="hljs-attr">prometheus:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">prom/prometheus:v2.37.1</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">prometheus</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./prometheus/:/etc/prometheus/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">prometheus_data:/prometheus</span>
    <span class="hljs-attr">command:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--config.file=/etc/prometheus/prometheus.yaml'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--storage.tsdb.path=/prometheus'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.console.libraries=/etc/prometheus/console_libraries'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.console.templates=/etc/prometheus/consoles'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--storage.tsdb.retention.time=360h'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.enable-lifecycle'</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">9090</span><span class="hljs-string">:9090</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span>
</code></pre>
<p>E a estrutura de diretórios e arquivos ficou assim:</p>
<pre><code class="lang-bash">grafana-prometheus
├── docker-compose.yaml
├── .env
├── .env.example
├── grafana
│   └── provisioning
│       └── ds-prometheus.yaml
└── prometheus
    └── prometheus.yaml

3 directories, 5 files
</code></pre>
<p>Podemos validar nosso arquivo compose para certificar que as variáveis de ambiente estão sendo passadas corretamente para o docker-compose:</p>
<pre><code class="lang-bash">$ docker-compose config
</code></pre>
<p>Todo o conteúdo do arquivo será impresso na tela e os valores das variáveis contidas no <code>.env</code> deverá assumir as variáveis do serviço do Grafana.</p>
<p>Se tudo tiver ok, execute o comando <code>docker-compose up -d</code> para subir os containers e aguarde até que seja feito o download das imagens e estejam prontos. Assim que terminar você pode executar o comando abaixo para certificar que tudo está rodando como deveria:</p>
<pre><code class="lang-bash">$ docker-compose ps

NAME                COMMAND                  SERVICE             STATUS              PORTS
grafana             <span class="hljs-string">"/run.sh"</span>                grafana             running             0.0.0.0:3000-&gt;3000/tcp, :::3000-&gt;3000/tcp
prometheus          <span class="hljs-string">"/bin/prometheus --c…"</span>   prometheus          running             0.0.0.0:9090-&gt;9090/tcp, :::9090-&gt;9090/tcp
</code></pre>
<p>Agora você poderá acessar o Grafana pelo link <code>localhost:3000</code> e o Prometheus por esse link <code>localhost:9090</code>.</p>
<h2 id="heading-acessando-a-aplicacao">Acessando a aplicação</h2>
<h3 id="heading-grafana">Grafana</h3>
<p>Ao acessar o Grafana, você verá a tela de login, sendo necessário informar o usuário e senha. No arquivo <code>.env</code> que foi criado contém as credenciais de acesso.</p>
<p>Para acessar, entre com o usuário <code>admin</code> e senha <code>password</code>.</p>
<p>Para validarmos que foi provisionado corretamente um datasource, vá no menu lateral no ícone de engrenagens e clique em Datasources. Verá que foi criado um com o nome de Prometheus.</p>
<p>Ao clicar nele irá abrir as configurações de acesso ao Prometheus. Ao adicionar esse datasource, por padrão há disponível três opções de Dashboards para ser utilizado.</p>
<p>Clique na segunda aba <code>Dashboards</code> e clique em <code>Import</code> para criar o dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046600036/e2035c2a-7d43-40c5-9daf-db0246c3e5ac.png" alt="Datasources Prometheus" class="image--center mx-auto" /></p>
<p>Datasources Prometheus</p>
<p>Ao clicar em importar clique em cima do nome do Dashboard, por exemplo o <code>Prometheus 2.0 Stats</code>. Você verá o Dashboard apresentar os valores da própria instância do Prometheus.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046640179/7ae558e8-6099-4663-91e7-206a11ecbbaf.png" alt="Dashboard Prometheus 2.0 Stats" class="image--center mx-auto" /></p>
<p>Dashboard Prometheus 2.0 Stats</p>
<p>Esse é o início de uma configuração para usar o Grafana. Você pode procurar por mais tipos de Dashboard direto do site do <a target="_blank" href="https://grafana.com/grafana/dashboards?ref=aristides.dev">Grafana</a>.</p>
<h3 id="heading-prometheus">Prometheus</h3>
<p>Ao acessar o Prometheus, você verá a seguinte tela:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046679348/daf60082-f9de-40e8-83c2-6e8dd27afbb8.png" alt="Tela de consultas do Prometheus" class="image--center mx-auto" /></p>
<p>Tela de consultas do Prometheus</p>
<p>Nessa tela é possível visualizar as métricas que estão sendo exportadas do container do Prometheus.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046700330/a1ab5def-cf1f-454b-bca3-4a0fc007c32d.png" alt="Visualizar métricas no Prometheus" class="image--center mx-auto" /></p>
<p>Visualizar métricas no Prometheus</p>
<h2 id="heading-download-codigo">Download código</h2>
<p>Todo o código dessa stack você pode baixar pelo link do <a target="_blank" href="https://github.com/aristidesneto/stack-de-monitoramento-grafana-e-prometheus">Github</a>.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Esse é apenas o começo de uma stack de monitoramento, existem muitas outras configurações a se fazer para um ambiente de produção. Nesse artigo o objetivo principal é subir o Grafana e Prometheus com uma configuração simples mas seguindo boas práticas e com algumas pequenas customizações.</p>
<p>Pretendo e quero lançar uma série de artigos para subir uma stack pronta para produção, focando também em segurança, alertas entre outras possíveis configurações.</p>
<p>Espero que tenha ajudado até aqui.</p>
<p>Se você encontrar algum erro, sugestões de melhorias ou se tiver alguma dúvida, deixe seu comentário abaixo.</p>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Ansible - Provisionando um servidor web com Nginx, PHP e MySQL]]></title><description><![CDATA[Fala pessoal tudo beleza?
Hoje vamos subir um servidor web completo, rodando os serviços Nginx, PHP 7.4 e MySQL 5.7/MySQL 8.0. Esse script foi testado e validado para funcionar corretamente nas versões 18.04 e 20.04 do Ubuntu.
Por padrão, o script ir...]]></description><link>https://aristides.dev/ansible-provisionando-um-servidor-web-com-nginx-php-e-mysql</link><guid isPermaLink="true">https://aristides.dev/ansible-provisionando-um-servidor-web-com-nginx-php-e-mysql</guid><category><![CDATA[Devops]]></category><category><![CDATA[ansible]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Thu, 24 Dec 2020 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046386990/b61f905a-b529-4b84-ad4c-c55de067a760.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Hoje vamos subir um servidor web completo, rodando os serviços <strong>Nginx, PHP 7.4 e MySQL 5.7/MySQL 8.0</strong>. Esse script foi testado e validado para funcionar corretamente nas versões <strong>18.04 e 20.04 do Ubuntu</strong>.</p>
<p><strong>Por padrão, o script irá instalar a versão do MySQL que está no repositório da distribuição, ou seja, se você usar o Ubuntu 18.04 a versão do MySQL que será instalada será a 5.7. Se você usar o Ubuntu 20.04, a versão instalada será a 8.0.</strong></p>
<h2 id="heading-disclaimer">Disclaimer</h2>
<p>O objetivo desse artigo é mostrar um script pronto de como provisionar um servidor web com os serviços Nginx, PHP e Mysql instalados e configurados. Se eu for explicar cada passo para a instalação de cada serviço, o artigo irá ficar bem extenso e cansativo de ler.</p>
<p>Por esse motivo, achei melhor explicar somente o que foi feito para a instalação do MySQL, explicando cada arquivo necessário para criar o mesmo. Nos próximos tópicos você verá de forma automatizada como fazer:</p>
<ul>
<li><p>Instalação do MySQL</p>
</li>
<li><p>Configurar senha do usuário root</p>
</li>
<li><p>Criação de usuários</p>
</li>
<li><p>Criação dos bancos de dados</p>
</li>
<li><p>Envio de um arquivo .sql para o servidor</p>
</li>
<li><p>Importação do arquivo .sql</p>
</li>
</ul>
<blockquote>
<p>Como eu disse, meu objetivo é ter um script para provisionar um servidor web completo, por isso no final do artigo irei disponibilizar um link para você obter esse script por completo.</p>
</blockquote>
<h2 id="heading-ansible">Ansible</h2>
<p>Para nos ajudar, iremos utilizar uma ferramenta chamada Ansible. O <strong>Ansible é uma ferramenta de automação</strong> de código aberto para gerenciar, automatizar, configurar servidores e implementar serviços. O Ansible foi desenvolvido por Michael DeHaan, mas em 2015 foi adquirido pela Red Hat que o mantém até hoje.</p>
<p>O Ansible funciona de uma forma muito simples, ele não precisa de um agente instalado em seu alvo. Utiliza-se apenas de uma conexão via SSH podendo gerenciar múltiplos servidores de uma vez.</p>
<h2 id="heading-instalacao">Instalação</h2>
<p>Para a instalação do Ansible em sua máquina host, siga os passos abaixo conforme sua distribuição. Para uma lista completa para outras distribuições verifique a documentação do <a target="_blank" href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html?ref=aristides.dev">Ansible</a>.</p>
<h3 id="heading-ubuntu">Ubuntu</h3>
<pre><code class="lang-bash">sudo apt update
sudo apt install software-properties-common
sudo apt-add-repository --yes --update ppa:ansible/ansible
sudo apt install ansible
</code></pre>
<h3 id="heading-debian">Debian</h3>
<p>Adicione a linha abaixo no arquivo <code>/etc/apt/sources.list</code>:</p>
<pre><code class="lang-bash">deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main
</code></pre>
<p>Execute os seguintes comandos:</p>
<pre><code class="lang-bash">sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
sudo apt update
sudo apt install ansible
</code></pre>
<h2 id="heading-nosso-cenario">Nosso cenário</h2>
<p>Para esse tutorial, irei utilizar um droplet criado na Digital Ocean, fique a vontade para utilizar qualquer provedor de serviço como AWS, Linode, Google entre outros.</p>
<p>Antes de prosseguir para o próximo passo, crie sua máquina e obtenha o endereço IP que seu cloud irá disponibilizar.</p>
<h2 id="heading-criando-o-arquivo-de-inventario">Criando o arquivo de inventário</h2>
<p>Vamos criar um diretório onde os arquivos do Ansible serão criados. Execute o comando abaixo:</p>
<pre><code class="lang-bash">mkdir ~/ansible &amp;&amp; <span class="hljs-built_in">cd</span> ~/ansible
</code></pre>
<p>Vamos criar o nosso arquivo de inventário chamado de <code>hosts</code>.</p>
<pre><code class="lang-bash">vim hosts
</code></pre>
<p>Cole o seguinte conteúdo dentro do arquivo.</p>
<pre><code class="lang-bash">[webserver]
192.168.10.100 ansible_user=root ansible_python_interpreter=/usr/bin/python3
</code></pre>
<p>Substitue o IP <code>192.168.10.100</code> para um IP válido, disponibilizado pelo seu cloud. É através desse arquivo de inventário que o Ansible irá obter o endereço do servidor e executar os comandos necessários.</p>
<p>A estrutura e a forma que iremos trabalhar com o Ansible, será seguindo as boas práticas. Iremos trabalhar com roles, variables, files e outros recursos.</p>
<h2 id="heading-criando-uma-role">Criando uma role</h2>
<p>Uma role é usada para organizar, compartilhar a separar as responsabilidades de cada tarefa. Podemos ter várias roles, cada uma fazendo uma coisa, por exemplo: uma para instalar o MySQL, outra para criar os usuários SSH, configurar firewall, entre outras.</p>
<p>Vamos criar a nossa primeira role, para isso iremos utilizar o comando <code>ansible-galaxy</code>, digite em seu terminal:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Cria o diretório onde as roles serão armazendas</span>
mkdir roles

<span class="hljs-comment"># Cria uma role chamada mysql</span>
ansible-galaxy init roles/mysql
</code></pre>
<p>Ao executar o comando <code>ansible-galaxy init roles/mysql</code>, será criado toda a estrutura de uma role, contendo alguns diretórios e arquivos responsáveis em executar determinadas tarefas.</p>
<p>Podemos ver melhor a estrutura criada ao criarmos a role <code>mysql</code>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Diretório: ~/ansible</span>
.
├── hosts
└── roles
    └── mysql
        ├── defaults
        │   └── main.yml
        ├── files
        ├── handlers
        │   └── main.yml
        ├── meta
        │   └── main.yml
        ├── README.md
        ├── tasks
        │   └── main.yml
        ├── templates
        ├── tests
        │   ├── inventory
        │   └── test.yml
        └── vars
            └── main.yml
</code></pre>
<h2 id="heading-trabalhando-na-role-mysql">Trabalhando na role MySQL</h2>
<p>Iremos agora criar as tarefas que a role MySQL executará. Vou começar pelos arquivos de variavéis para quando chegarmos na tarefa em si, você possa entender de onde as variavéis foram obtidas.</p>
<p><strong>Os próximos tópicos estão nomeados em inglês para manter os mesmos nomes utilizados pelo Ansible.</strong></p>
<h3 id="heading-vars">Vars</h3>
<p>Podemos definir variáveis para o nosso script utilizando o arquivo <code>vars/main.yml</code> ou o <code>defaults/main.yml</code>. No diretório <code>default</code>, utilizamos quando queremos que uma determinada variável seja usada caso não for declarada em algum lugar do script, ou seja, seria onde colocaríamos as variavéis padrões para evitar algum erro durante o provisionamento, caso não declarada.</p>
<p>No diretório <code>vars</code> podemos declarar as variáveis que deverão ser usadas durante a execução do script.</p>
<p>Para a criação dos usuários e dos bancos de dados MySQL, iremos utilizar o arquivo <code>vars/main.yml</code>. Cole o seguinte conteúdo.</p>
<pre><code class="lang-yml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># vars file for roles/mysql</span>
<span class="hljs-attr">db_password:</span> <span class="hljs-string">password</span>

<span class="hljs-attr">mysql_users:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">user:</span> <span class="hljs-string">aristides</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">user@123</span>
    <span class="hljs-attr">host:</span> <span class="hljs-string">'%'</span>
    <span class="hljs-attr">priv:</span> <span class="hljs-string">'*.*:ALL,GRANT'</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">user:</span> <span class="hljs-string">example</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">example@123</span>
    <span class="hljs-attr">host:</span> <span class="hljs-string">'%'</span>
    <span class="hljs-attr">priv:</span> <span class="hljs-string">'example.*:SELECT,INSERT,UPDATE,DELETE,LOCK TABLES,EXECUTE,SHOW VIEW'</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">user:</span> <span class="hljs-string">sakila</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">sakila@123</span>
    <span class="hljs-attr">host:</span> <span class="hljs-string">'%'</span>
    <span class="hljs-attr">priv:</span> <span class="hljs-string">'sakila.*:SELECT,INSERT,UPDATE,DELETE,LOCK TABLES,EXECUTE,SHOW VIEW'</span>

<span class="hljs-attr">mysql_databases:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">database:</span> <span class="hljs-string">example</span>
    <span class="hljs-attr">encoding:</span> <span class="hljs-string">utf8mb4</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">example.sql</span>
    <span class="hljs-attr">dest:</span> <span class="hljs-string">/tmp</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">database:</span> <span class="hljs-string">sakila</span>
    <span class="hljs-attr">encoding:</span> <span class="hljs-string">utf8mb4</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">sakila.sql</span>
    <span class="hljs-attr">dest:</span> <span class="hljs-string">/tmp</span>
</code></pre>
<ul>
<li><p>Definimos a senha que será configurada para o usuário root do Mysql em <code>db_password</code>.</p>
</li>
<li><p>Definimos uma variável chamada <code>mysql_users</code> que recebe um array de usuários, nela informamos o nome e senha do usuário, o host e seus privilégios.</p>
</li>
<li><p>Na variável <code>mysql_databases</code>, informamos os bancos de dados que serão copiados da máquina host para a máquina remota (servidor).</p>
</li>
</ul>
<h3 id="heading-tasks">Tasks</h3>
<p>Vamos criar nossa primeira tarefa, para isso vamos editar o arquivo <code>tasks/main.yml</code>. Cole o seguinte conteúdo dentro do arquivo, logo abaixo explico o que será feito.</p>
<pre><code class="lang-yml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># tasks file for roles/mysql</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Instalando</span> <span class="hljs-string">MySQL</span> <span class="hljs-string">Server</span>
  <span class="hljs-attr">apt:</span> 
    <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ packages }}</span>"</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">latest</span>
    <span class="hljs-attr">update_cache:</span> <span class="hljs-literal">yes</span>
  <span class="hljs-attr">vars:</span>
    <span class="hljs-attr">packages:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mysql-server</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">python3-pymysql</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">python3-mysqldb</span>
  <span class="hljs-attr">notify:</span> <span class="hljs-string">restart</span> <span class="hljs-string">mysql</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Certifica</span> <span class="hljs-string">que</span> <span class="hljs-string">o</span> <span class="hljs-string">serviço</span> <span class="hljs-string">do</span> <span class="hljs-string">MySQL</span> <span class="hljs-string">está</span> <span class="hljs-string">rodando</span>
  <span class="hljs-attr">service:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">started</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">import_tasks:</span> <span class="hljs-string">config.yml</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">import_tasks:</span> <span class="hljs-string">import.yml</span>
</code></pre>
<ul>
<li><p>Na task do MySQL, iremos instalar o pacote <code>mysql-server</code> e extensões do python para que o Ansible possa executar comandos do MySQL.</p>
</li>
<li><p>Repare na linha <code>notify: restart mysql</code>, ela será explicada mais a diante.</p>
</li>
<li><p>Após a instalação será feita uma checagem para certificar que o serviço do MySQL está ativo.</p>
</li>
<li><p>Logo após é feito a importação de duas tasks <code>config.yml</code> e <code>import.yml</code> que foi criada para executar outros comandos. Essa é uma boa prática para dividir as responsabilidades dos arquivos.</p>
</li>
</ul>
<p>Crie o seguinte arquivo <code>mysql/tasks/config.yml</code> e cole o conteúdo abaixo:</p>
<pre><code class="lang-yml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># tasks file for roles/mysql</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Altera</span> <span class="hljs-string">plugin</span> <span class="hljs-string">de</span> <span class="hljs-string">autenticação</span> <span class="hljs-string">para</span> <span class="hljs-string">mysql_native_password</span>
  <span class="hljs-attr">shell:</span> <span class="hljs-string">mysql</span> <span class="hljs-string">-u</span> <span class="hljs-string">root</span> <span class="hljs-string">-e</span> <span class="hljs-string">'UPDATE mysql.user SET plugin="mysql_native_password" WHERE user="root" AND host="localhost"'</span>
  <span class="hljs-comment"># Caso queira executar o mesmo script na segunda vez, descomente a linha abaixo e comente a de cima.</span>
  <span class="hljs-comment"># Na primeira vez que o script é executado é criado uma senha para o usuário root, por isso a necessidade de informar a senha no comando abaixo</span>
  <span class="hljs-comment"># shell: mysql -u root -p{{ db_password }} -e 'UPDATE mysql.user SET plugin="mysql_native_password" WHERE user="root" AND host="localhost"'</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Recarrega</span> <span class="hljs-string">privilégios</span>
  <span class="hljs-attr">shell:</span> <span class="hljs-string">mysql</span> <span class="hljs-string">-u</span> <span class="hljs-string">root</span> <span class="hljs-string">-e</span> <span class="hljs-string">'FLUSH PRIVILEGES'</span>
  <span class="hljs-comment"># shell: mysql -u root -p{{ db_password }} -e 'FLUSH PRIVILEGES'</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Configura</span> <span class="hljs-string">a</span> <span class="hljs-string">senha</span> <span class="hljs-string">do</span> <span class="hljs-string">root</span>
  <span class="hljs-attr">mysql_user:</span>
    <span class="hljs-attr">login_host:</span> <span class="hljs-string">'localhost'</span>
    <span class="hljs-attr">login_user:</span> <span class="hljs-string">'root'</span>
    <span class="hljs-attr">login_password:</span> <span class="hljs-string">''</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">'root'</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ db_password }}</span>'</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Criando</span> <span class="hljs-string">usuario</span> <span class="hljs-string">Mysql</span>
  <span class="hljs-attr">mysql_user:</span>
    <span class="hljs-attr">login_user:</span> <span class="hljs-string">'root'</span>
    <span class="hljs-attr">login_password:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ db_password }}</span>'</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ item.user }}</span>'</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ item.password }}</span>'</span>
    <span class="hljs-attr">priv:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.priv }}</span>"</span>
    <span class="hljs-attr">host:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.host }}</span>"</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
  <span class="hljs-attr">with_items:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ mysql_users }}</span>"</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Criando</span> <span class="hljs-string">bancos</span> <span class="hljs-string">de</span> <span class="hljs-string">dados</span>
  <span class="hljs-attr">mysql_db:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ item.database }}</span>'</span>
    <span class="hljs-attr">login_user:</span> <span class="hljs-string">root</span>
    <span class="hljs-attr">login_password:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ db_password }}</span>"</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
    <span class="hljs-attr">encoding:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.encoding }}</span>"</span>
  <span class="hljs-attr">with_items:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ mysql_databases }}</span>"</span>
</code></pre>
<ul>
<li><p>O arquivo <code>config.yml</code> é responsável em realizar algumas configurações no banco de dados e setar uma senha para o usuário <code>root</code>.</p>
</li>
<li><p>Veja que existe algumas variáveis, por exemplo a <code>{{ db_password }}</code>, declarada no arquivo <code>vars/main.yml</code>.</p>
</li>
<li><p>Um ponto importante e crucial para entender como a funcionalidade de <code>loop</code> funciona no Ansible é analisar a linha <code>with_items: "{{ mysql_users }}"</code> e <code>with_items: "{{ mysql_databases }}"</code>. O Ansible irá percorrer esse array acessando os valores através da palavra reservada <code>item</code> e a chave do array declarado, por exemplo: <code>item.name</code>. Como informado as variáveis foram declaradas no arquivo <code>vars/main.yml</code>.</p>
</li>
</ul>
<p>Crie o seguinte arquivo <code>mysql/tasks/import.yml</code> e cole o conteúdo abaixo:</p>
<pre><code class="lang-yml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># tasks file for roles/mysql</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copiando</span> <span class="hljs-string">arquivos</span> <span class="hljs-string">SQL</span> <span class="hljs-string">para</span> <span class="hljs-string">o</span> <span class="hljs-string">servidor</span>
  <span class="hljs-attr">copy:</span>
    <span class="hljs-attr">src:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.name }}</span>"</span>
    <span class="hljs-attr">dest:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.dest }}</span>/<span class="hljs-template-variable">{{ item.name }}</span>"</span>
  <span class="hljs-attr">with_items:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ mysql_databases }}</span>"</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Import</span> <span class="hljs-string">arquivo</span> <span class="hljs-string">de</span> <span class="hljs-string">dump</span> <span class="hljs-string">do</span> <span class="hljs-string">banco</span> <span class="hljs-string">de</span> <span class="hljs-string">dados</span>
  <span class="hljs-attr">mysql_db:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ item.database }}</span>'</span>
    <span class="hljs-attr">login_user:</span> <span class="hljs-string">root</span>
    <span class="hljs-attr">login_password:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ db_password }}</span>"</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">import</span>
    <span class="hljs-attr">target:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.dest }}</span>/<span class="hljs-template-variable">{{ item.name }}</span>"</span>
  <span class="hljs-attr">with_items:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ mysql_databases }}</span>"</span>
</code></pre>
<ul>
<li><p>O arquivo <code>import.yml</code> é responsável em copiar para o servidor os arquivos <code>.sql</code> de backup e realizar a importação do mesmo.</p>
</li>
<li><p>Veja que a importação é feita utilizando o recurso de <code>loop</code> e pode ser utilizado para importar vários bancos de dados de maneira rápida e eficiente.</p>
</li>
</ul>
<blockquote>
<p>Veja que iremos importar dois bancos de dados, você pode fazer o download <a target="_blank" href="https://assets-aristides-dev.s3.amazonaws.com/downloads/sakila.zip?ref=aristides.dev">clicando aqui</a>. Descompacte o arquivo .zip e salve os dois arquivos <code>.sql</code> no diretório <code>roles/mysql/files</code>. Os dois são os mesmos bancos só que com nomes diferentes, isso apenas para entender que podemos importar vários bancos de dados.</p>
</blockquote>
<h3 id="heading-handlers">Handlers</h3>
<p>Os handlers são gatilhos que irá acionar uma task. No arquivo <code>vars/main.yml</code> durante a instalação do MySQL, existe uma chamada para o handler <code>restart mysql</code> através da linha <code>notify: restart mysql</code>.</p>
<p>Os handlers quando acionados são executados somente no final do script, independentemente de onde são chamados, sendo essa a diferença de uma task normal.</p>
<p>O arquivo que será utilizado é o <code>handlers/main.yml</code>. Copie e cole o conteúdo abaixo:</p>
<pre><code class="lang-yml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># handlers file for roles/mysql</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">restart</span> <span class="hljs-string">mysql</span>
  <span class="hljs-attr">service:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">restarted</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">yes</span>
</code></pre>
<ul>
<li>Nesse arquivo criamos um handler chamado de <code>restart mysql</code> que como o próprio nome diz, ao finalizar a execução do script o Ansible irá restartar o serviço do MySQL para que entre em vigor as alterações realizadas.</li>
</ul>
<h3 id="heading-playbook">Playbook</h3>
<p>Para que tudo isso funcione, temos que criar um arquivo responsável em executar nossas tarefas, esse arquivo é chamado de playbook.</p>
<p>Crie um arquivo chamado <code>my-server.yml</code> na raiz do seu projeto e cole o seguinte conteúdo nele.</p>
<pre><code class="lang-yml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">webserver</span>
  <span class="hljs-attr">gather_facts:</span> <span class="hljs-literal">no</span>

  <span class="hljs-attr">roles:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">mysql</span>
</code></pre>
<ul>
<li><p>Informamos ao Ansible que ao ler esse arquivo utilize o host informado no arquivo <code>hosts</code> que contenha a chave <code>webserver</code>. Esse arquivo <code>hosts</code> criamos lá no início, o arquivo de inventário.</p>
</li>
<li><p>Na linha <code>gather_facts</code> deixei como <code>no</code> para ganhar um tempo na execução do script. Caso for <code>yes</code>, o Ansible irá obter várias informações do servidor como por exemplo, sistema operacional, memória ram, hd e várias outras. Essas informações podem ser usadas para outros fins, mas para o nosso caso não é necessário.</p>
</li>
<li><p>Em <code>roles</code> definimos que será executada a role <code>mysql</code>. Você pode adicionar quantas roles que desejar.</p>
</li>
</ul>
<h3 id="heading-arquivo-de-configuracao-opcional">Arquivo de configuração (opcional)</h3>
<p>A criação de um arquivo de configuração na raiz do projeto é opcional, pois o arquivo principal se encontra em <code>/etc/ansible/ansible.cfg</code> e você pode realizar qualquer configuração necessária.</p>
<p>Existe uma configuração que eu tenho preferência em alterar que se chama <code>host_key_checking</code>. Setando essa chave para <code>false</code> quer dizer que no primeiro acesso ao servidor não será feita a verificação da chave do host, ou seja, aquela pergunta se o host é confiável e sendo necessário digitar <code>yes</code> para continuar.</p>
<p>Por padrão o Ansible deixa essa opção como <code>true</code> por motivos de segurança, ele protege contra spoofing de servidor. Caso queira saber mais <a target="_blank" href="https://docs.ansible.com/ansible/latest/user_guide/connection_details.html?ref=aristides.dev#managing-host-key-checking">clique aqui</a>.</p>
<p>Para desativar, crie na raiz do projeto o arquivo <code>ansible.cfg</code> e cole o seguinte contéudo:</p>
<pre><code class="lang-bash">[defaults]
host_key_checking = False
</code></pre>
<p>Vamos ver como está nossa estrutura de diretórios depois da criação dos nossos arquivos:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Diretório: ~/ansible</span>
.
├── ansible.cfg
├── hosts
├── my-server.yml
└── roles
    ├── mysql
        ├── defaults
        │   └── main.yml
        ├── files
        │   ├── example.sql
        │   └── sakila.sql
        ├── handlers
        │   └── main.yml
        ├── meta
        │   └── main.yml
        ├── README.md
        ├── tasks
        │   ├── config.yml
        │   ├── import.yml
        │   └── main.yml
        ├── templates
        ├── tests
        │   ├── inventory
        │   └── test.yml
        └── vars
            └── main.yml
</code></pre>
<h2 id="heading-execucao-do-script">Execução do script</h2>
<p>Vamos entender o que será feito com esse script:</p>
<ul>
<li><p>Será feito a instalação do MySQL</p>
</li>
<li><p>A senha do root será alterada conforme definimos no arquivo <code>vars/main.yml</code></p>
</li>
<li><p>Serão criados usuários para o acesso ao MySQL</p>
</li>
<li><p>Serão criados bando de dados</p>
</li>
<li><p>Serão importados dois arquivos .sql para os bancos recém criados</p>
</li>
</ul>
<p>Agora precisamos executar o nosso playbook, para isso digite o comando abaixo informando o arquivo de inventário utilizado e o nosso playbook. O comando <code>time</code> no início do comando para a execução do playbook, é um comando do Linux, utilizo ele para que no final ele informe o tempo que levou na execução do comando.</p>
<pre><code class="lang-bash">time ansible-playbook -i hosts my-server.yml
</code></pre>
<p>Após a finalização do script, você verá a informação:</p>
<pre><code class="lang-bash">PLAY RECAP ******************************************************************************************
192.168.10.100  : ok=10   changed=9    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


real    2m46,517s     &lt;-- tempo de execução <span class="hljs-keyword">do</span> script
user    0m20,500s
sys 0m2,520s
</code></pre>
<p>A partir desse momento você terá o MySQL rodando com todas as configurações devidas de uma forma automatizada, lindo né? E isso tudo levou apenas 2 min e 46 segundos. Se fosse fazer isso na mão, quanto tempo seria?</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Para finalizarmos esse artigo, como disse, meu objetivo é mostrar como subir um servidor web completo com Nginx, PHP e MySQL, se você seguir exatamente os passos aqui citados você terá seu servidor MySQL pronto.</p>
<p>Você pode encontrar o código no meu repositório no <a target="_blank" href="https://github.com/aristidesneto/ansible-webserver?ref=aristides.dev">github</a>.</p>
<p>Se você encontrar algum erro, sugestões de melhorias ou se tiver alguma dúvida, deixe seu comentário abaixo.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Matomo Analytics - Alternativa para uma ferramenta de análise web]]></title><description><![CDATA[Fala pessoal tudo beleza?
Acredito que todos conheçam o Google Analytics, uma ferramenta de análise web que cria estatísticas de visitas, você precisa apenas adicionar um script no seu site e esperar que esses dados coletados pelo Google vire relatór...]]></description><link>https://aristides.dev/matomo-analytics-alternativa-para-uma-ferramenta-de-analise-web</link><guid isPermaLink="true">https://aristides.dev/matomo-analytics-alternativa-para-uma-ferramenta-de-analise-web</guid><category><![CDATA[dicas]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Thu, 01 Oct 2020 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046156542/d3975a11-50bd-4224-a593-4272be5e65ac.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Acredito que todos conheçam o Google Analytics, uma ferramenta de análise web que cria estatísticas de visitas, você precisa apenas adicionar um script no seu site e esperar que esses dados coletados pelo Google vire relatórios, gráficos e números para você!</p>
<p>Como descrito no título desse artigo, não será sobre o Google Analytics que irei falar, mas sim, do Matomo Analytics.</p>
<h2 id="heading-disclaimer">Disclaimer</h2>
<p>Esse não é um artigo técnico, não estarei ensinando como instalar e/ou configurar a ferramenta, mas estarei compartilhando mais a respeito sobre os recursos, comunidade e contribuições.</p>
<p>Logo estarei publicando um artigo mais técnico ;)</p>
<h2 id="heading-sobre-matomo-analytics">Sobre Matomo Analytics</h2>
<p>Matomo Analytics é uma ferramenta de análise web Open Source, responsável em coletar dados de visitantes para ajudar em tomadas de decisões.</p>
<p>Matomo tem como filosofia dar às pessoas a liberdade e o direito à sua própria privacidade e dados. E em 2007 nasce a base atual do Matomo que originalmente se chamava Piwiki.</p>
<p>O fundador do Matomo, Matthieu Aubry tinha como objetivo ter uma alternativa de código livre para o Google Anaytics, que deveria ser tão poderoso quanto e que também respeitasse os dados, propriedade e privacidade do usuário.</p>
<p>Com a ajuda da comunidade o projeto cresceu e se tornou a plataforma de análise web de código aberto nº 1 do mundo. Hoje, é usado em mais de 1,4 milhão de sites, em mais de 190 países, e acessível em mais de 50 idiomas.</p>
<p>E Piwik evoluiu para Matomo.</p>
<h2 id="heading-o-que-o-matomo-oferece">O que o Matomo oferece</h2>
<ul>
<li><p>100% de propriedade dos dados - seus dados em suas mãos</p>
</li>
<li><p>Flexibilidade</p>
</li>
<li><p>Confiabilidade e Segurança</p>
</li>
<li><p>Uma ferramenta fácil de usar</p>
</li>
<li><p>Proteção de privacidade do usuário</p>
</li>
<li><p>Opções de hospedagem On-Premise ou em Cloud</p>
</li>
<li><p>Open Source</p>
</li>
<li><p>Conformidade com GDPR</p>
</li>
<li><p>Mais de 100 integrações</p>
</li>
</ul>
<p>Quando se diz 100% de propriedade dos dados, quer dizer que você pode simplesmente baixar o código fonte da ferramenta e instalar em seu próprio servidor, tendo todo o controle dos dados coletados pela ferramenta, e essa é uma grande vantagem, pois você sabe que você não precisará concordar com os Termos e Políticas onde esses dados serão compartilhados com outros serviços.</p>
<h2 id="heading-a-comunidade-matomo">A comunidade Matomo</h2>
<p>Por ser um projeto Open Source, a ferramenta é vista com bons olhos por devs e por amantes do código livre. Você também pode contribuir com o projeto, veja como:</p>
<ol>
<li><p><a target="_blank" href="https://forum.matomo.org/?ref=aristides.dev">Ajude a comunidade através dos fóruns de discussões</a>.</p>
</li>
<li><p><a target="_blank" href="https://developer.matomo.org/?ref=aristides.dev">Se você é um desenvolvedor</a>.</p>
</li>
<li><p><a target="_blank" href="https://matomo.org/translations/?ref=aristides.dev">Ajude na tradução da ferramenta</a>.</p>
</li>
</ol>
<p>Outros links úteis:</p>
<ul>
<li><p><a target="_blank" href="https://matomo.org/?ref=aristides.dev">https://matomo.org/</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/matomo-org/?ref=aristides.dev">https://github.com/matomo-org/</a></p>
</li>
<li><p><a target="_blank" href="https://matomo.org/docs/installation/?ref=aristides.dev">https://matomo.org/docs/installation/</a></p>
</li>
</ul>
<h2 id="heading-minha-contribuicao">Minha contribuição</h2>
<p>Gostaria de compartilhar aqui a minha pequena parte onde ajudo na tradução da ferramenta. Iniciei nessa comunidade em Maio de 2018, e vi no Matomo uma oportunidade de ajudar e de certa forma aprender.</p>
<p>Enviei um e-mail para o suporte para entender mais sobre como eu poderia ajudar, e de uma forma bem atenciosa, foi me passado todas as informações para que eu pudesse começar.</p>
<p>Depois de um tempo contribuindo com o projeto, recebi um e-mail agradecendo pelas contribuições e que gostariam de retribuir. Assim, fui presenteado com uma bela blusa e uma carta escrita a mão por @matthieuaubry.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045922036/1b0da4f5-1ce7-4f30-be25-7282ad5222c7.png" alt="Presenteado por Matomo" /></p>
<p>Presenteado por Matomo</p>
<blockquote>
<p>Quando começamos a fazer algo para a comunidade, o primeiro objetivo é sempre compartilhar com outras pessoas nossas experiências, nossos problemas e como resolvemos esse problema, e comigo não é diferente. Você sempre terá algo para compartilhar, então, comece agora mesmo!</p>
</blockquote>
<h2 id="heading-dashboard-matomo">Dashboard Matomo</h2>
<p>Para mostrar mais sobre essa ferramenta incrível, irei aqui mostrar algumas imagens dos dados que são coletados pelo Matomo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045951179/0367aa75-2e25-4777-9e28-f104998e7b2f.png" alt="Matomo dashboard" /></p>
<p>Matomo dashboard</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045982989/89e7371c-66bc-40cd-88fe-1031112cc322.png" alt="Matomo visitas" /></p>
<p>Matomo visitas</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046034252/48c4b822-1203-4019-bfeb-7a11acc91225.png" alt="Matomo navegadores" /></p>
<p>Matomo navegadores</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046052514/c3848f24-832e-45a9-861d-272c79b24405.png" alt="Matomo sistemas operacionais" /></p>
<p>Matomo sistemas operacionais</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735046072089/2212a80b-dbe4-4d84-bdfa-17ab5e65f086.png" alt="Matomo visão geral de visitas" /></p>
<p>Matomo visão geral de visitas</p>
<p>Bom pessoal, espero que tenha atiçado a vontade de conhecer mais essa ferramenta.</p>
<p>Esse artigo foi mais para apresentar a ferramenta para vocês, irei publicar outro artigo mais técnico onde mostrarei como instalar e configurar a ferramenta para a coleta de dados.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Instalando Let's Encrypt no Ubuntu e Debian com Nginx]]></title><description><![CDATA[Fala pessoal tudo beleza?
Nesse artigo vamos ver como é fácil criar certificados SSL para o seu site usando o Let's Encrypt e como servidor web usaremos o Nginx.
Let's Encrypt é uma Autoridade de Certificação (CA), que fornece certificados gratuitame...]]></description><link>https://aristides.dev/instalando-lets-encrypt-no-ubuntu-e-debian-com-nginx</link><guid isPermaLink="true">https://aristides.dev/instalando-lets-encrypt-no-ubuntu-e-debian-com-nginx</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Mon, 21 Sep 2020 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045797540/b11e17f5-776a-43b7-8951-75bd08309cb5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Nesse artigo vamos ver como é fácil criar certificados SSL para o seu site usando o Let's Encrypt e como servidor web usaremos o Nginx.</p>
<p><a target="_blank" href="https://letsencrypt.org/getting-started">Let's Encrypt</a> é uma Autoridade de Certificação (CA), que fornece certificados gratuitamente e gerencia seus certificados de uma forma automatizada, facilita todo o processo de criação, validação, instalação e renovação de certificados.</p>
<p>Para instalar e utilizar o Let's Encrypt é necessário que tenha acesso ao shell do seu servidor com um usuário root ou que tenha privilégios de sudo, para que possamos executar os comandos de instalação dos pacotes e para a geração dos certificados.</p>
<h2 id="heading-disclaimer">Disclaimer</h2>
<p>Para esse tutorial, utilizei a distribuição <strong>Ubuntu 18.04</strong>, mas se você estiver usando o <strong>Ubuntu 20.04</strong>, <strong>Debian 9</strong> ou <strong>Debian 10</strong>, continue, pois irá funcionar sem problemas.</p>
<p>Para serviço de VPS, estou usando um droplet na Digital Ocean.</p>
<blockquote>
<p>A Digital Ocean oferece um serviço de hospedagem de VPS excelente, estável e com vários recursos disponíveis em seu painel. Caso queira conhecer mais, utilize meu <a target="_blank" href="https://m.do.co/c/31d5a38be024">link de referência</a> para se cadastrar e ganhe créditos assim que começar a utilizar seus serviços. Você ganha e eu também ;)</p>
</blockquote>
<h2 id="heading-instalando-o-lets-encrypt">Instalando o Let's Encrypt</h2>
<p>O Let's Encrypt recomenda o uso do cliente <a target="_blank" href="https://certbot.eff.org/">Certbot ACME</a> para a maioria das pessoas, pois possuem modos avançados de configurações, e para nós que gostamos de fazer as coisas sem esforço, é uma boa!</p>
<p>Primeiro vamos instalar o pacote Certbot com o plugin do Nginx, digite o comando abaixo:</p>
<pre><code class="lang-bash">sudo apt install python-certbot-nginx
</code></pre>
<p>Esse comando irá instalar o pacote do Certbot e o plugin para Nginx, caso você ainda não tenha o Nginx instalado, será instalado junto.</p>
<p>Após a instalação do certbot e do plugin, vamos gerar o nosso certificado.</p>
<blockquote>
<p>Para que seja possível gerar o certificado SSL seu site deve estar acessível na web, com o DNS já configurado. Caso queira configurar um servidor Nginx veja esse arquivo <a target="_blank" href="https://aristides.dev/configurando-virtual-host-no-nginx">Aprenda a instalação e a configuração básica de como criar um server block com o servidor web Nginx</a></p>
</blockquote>
<p>Se você usa um firewall em seu servidor, certifique-se que a porta <code>443</code> esteja liberada. Para quem usa o utilitário UFW, pode verificar com os comandos:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Para verificar se a porta está liberada</span>
sudo ufw status

<span class="hljs-comment"># Para liberar a porta caso não esteja</span>
sudo ufw allow 443
</code></pre>
<h2 id="heading-gerando-certificado-ssl">Gerando certificado SSL</h2>
<p>Para o nosso exemplo, criei o subdomínio <code>meublog.aristides.dev</code>.</p>
<p>Para gerar o certificado, utiliza-se o utilitário <code>certbot</code> com o plugin do <code>nginx</code>, em seguida o parâmetro <code>-d</code> seguido do seu domínio.</p>
<pre><code class="lang-bash">sudo certbot --nginx -d meublog.aristides.dev
</code></pre>
<p>Caso queira criar certificados para mais de um domínio ao mesmo tempo, basta informar os mesmos com o parâmetro <code>-d</code>.</p>
<pre><code class="lang-bash">sudo certbot --nginx -d meusite.com.br -d meusite2.com.br
</code></pre>
<p>Ao executar o comando acima, algumas perguntas serão feitas e devem ser respondidas, a primeira é sobre os Termos de Serviço. Informe <code>A</code> para concordar e continuar.</p>
<p>A segunda é se gostaria de compartihar seu e-mail e receber notícias, campanhas entre outras informações sobre Let's Encrypt.</p>
<p>A terceira pergunta é se deseja que todo o tráfego para seu site seja redirecionado para HTTPS, digite <code>2</code> para Sim.</p>
<pre><code class="lang-bash">Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this <span class="hljs-keyword">for</span>
new sites, or <span class="hljs-keyword">if</span> you<span class="hljs-string">'re confident your site works on HTTPS. You can undo this
change by editing your web server'</span>s configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] <span class="hljs-keyword">then</span> [enter] (press <span class="hljs-string">'c'</span> to cancel): 2 
Redirecting all traffic on port 80 to ssl <span class="hljs-keyword">in</span> /etc/nginx/sites-enabled/default
</code></pre>
<p><em>Pronto. Certificado gerado com sucesso. Congratulations!</em></p>
<pre><code class="lang-bash">- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://meublog.aristides.dev

You should <span class="hljs-built_in">test</span> your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=meublog.aristides.dev
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/meublog.aristides.dev/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/meublog.aristides.dev/privkey.pem
   Your cert will expire on 2020-12-20. To obtain a new or tweaked
   version of this certificate <span class="hljs-keyword">in</span> the future, simply run certbot again
   with the <span class="hljs-string">"certonly"</span> option. To non-interactively renew *all* of
   your certificates, run <span class="hljs-string">"certbot renew"</span>
 - Your account credentials have been saved <span class="hljs-keyword">in</span> your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let<span class="hljs-string">'s Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le</span>
</code></pre>
<p>Muito bom! Seu certificado foi gerado com sucesso e nesse momento seu site já deve estar funcionando com HTTPS.</p>
<h2 id="heading-validando-a-configuracao-do-ssl">Validando a configuração do SSL</h2>
<p>Veja que após a conclusão do processo, ele informe que é possível testar a "força" que sua configuração possui. Para isso é necessário acessar o site <a target="_blank" href="https://www.ssllabs.com/ssltest">https://www.ssllabs.com/ssltest</a> e digitar o endereço do seu site.</p>
<blockquote>
<p><strong>Nota</strong>: Se você testar seu site agora, é muito provável que receba a letra <code>B</code>, que se refere à algumas variavéis de segurança que é testado. Na instalação que usamos aqui, não geramos uma chave RSA e nem a chave DHPARAM, a geração dessas chaves são um passo a mais para aumentar a segurança.  </p>
<p>Utilizamos aqui o plugin do Nginx para a instalação, por isso que automaticamente nosso arquivo de configuração foi atualizado com as informações dos certificados e redirecionamento para HTTPS.  </p>
<p>Para ter um certificado mais seguro, geralmente utilizamos o <code>certbot certonly</code>, para apenas gerar os certificados e manualmente as configurações e a geração de chaves RSA e DHPARAM deve ser feitas pelo administrador.  </p>
<p>Estarei escrevendo um outro post a respeito dessa configuração!</p>
</blockquote>
<h2 id="heading-renovacao-automatica">Renovação automática</h2>
<p>O certificado gerado tem validade por 90 dias. Para que o certificado seja renovado e você não tenha problemas com seu site, o Let's Encrypt cria uma entrada no cron do Linux para que automaticamente seja renovado o certificado quando necessário.</p>
<p>Veja o arquivo criado em <code>/etc/cron.d/certbot</code>, o mesmo é executado a cada 12 horas.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># /etc/cron.d/certbot: crontab entries for the certbot package</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Upstream recommends attempting renewal twice a day</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Eventually, this will be an opportunity to validate certificates</span>
<span class="hljs-comment"># haven't been revoked, etc.  Renewal will only occur if expiration</span>
<span class="hljs-comment"># is within 30 days.</span>
SHELL=/bin/sh
PATH=/usr/<span class="hljs-built_in">local</span>/sbin:/usr/<span class="hljs-built_in">local</span>/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root <span class="hljs-built_in">test</span> -x /usr/bin/certbot -a \! -d /run/systemd/system &amp;&amp; perl -e <span class="hljs-string">'sleep int(rand(43200))'</span> &amp;&amp; certbot -q renew
</code></pre>
<p>Caso queira simular manualmente a renovação dos certificados, execute o comando <code>certbot renew</code> com o parâmetro <code>--dry-run</code>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Para simular</span>
sudo certbot renew --dry-run

<span class="hljs-comment"># Para renovar</span>
sudo certbot renew
</code></pre>
<p>Se você executar o comando para renovar e se ainda não for necessário, será retornada uma mensagem que não existe certificado para renovação.</p>
<h2 id="heading-listar-informacoes-sobre-certificados">Listar informações sobre certificados</h2>
<p>Para saber maiores informações sobre todos os certificados que foram gerados pelo certbot, execute o comando <code>certbot certificates</code>:</p>
<pre><code class="lang-bash">sudo certbot certificates

Saving debug <span class="hljs-built_in">log</span> to /var/<span class="hljs-built_in">log</span>/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: meublog.aristides.dev
    Domains: meublog.aristides.dev
    Expiry Date: 2020-12-20 00:12:05+00:00 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/meublog.aristides.dev/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/meublog.aristides.dev/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
</code></pre>
<h2 id="heading-remover-um-certificado">Remover um certificado</h2>
<p>Caso você não precise mais de um certificado criado para um domínio, seja por qual motivo for, a maneira mais simples e eficiente de fazer isso é utilizar os comandos que o certbot nos fornece.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Remover através de uma lista fornecida pelo Certbot</span>
sudo certbot delete

<span class="hljs-comment"># Remover pelo domínio</span>
<span class="hljs-comment"># Esse comando não solicita confirmação, ao executar o certificado será removido</span>
sudo certbot delete --cert-name meublog.aristides.dev
</code></pre>
<h2 id="heading-diretorio-dos-certificados-gerados">Diretório dos certificados gerados</h2>
<p>Veja que no diretório <code>/etc/letsencrypt</code> é onde todos os arquivos referente aos certificados são salvos. Dentro dele existe um diretório chamado <code>live</code>.</p>
<pre><code class="lang-bash">root@ubuntu-letsencrypt:/etc/letsencrypt<span class="hljs-comment"># ls -l</span>
total 40
drwx------ 3 root root 4096 Sep 21 01:11 accounts
drwx------ 3 root root 4096 Sep 21 01:12 archive
-rw-r--r-- 1 root root  121 Oct 10  2019 cli.ini
drwxr-xr-x 2 root root 4096 Sep 21 01:11 csr
drwx------ 2 root root 4096 Sep 21 01:11 keys
drwx------ 3 root root 4096 Sep 21 01:12 live
-rw-r--r-- 1 root root 1143 Sep 21 01:11 options-ssl-nginx.conf
drwxr-xr-x 2 root root 4096 Sep 21 01:12 renewal
drwxr-xr-x 5 root root 4096 Sep 21 01:11 renewal-hooks
-rw-r--r-- 1 root root  424 Sep 21 01:11 ssl-dhparams.pem
</code></pre>
<p>Dentro do diretório <code>live</code> estão salvos os arquivos referente ao seu site, veja que existe um diretório com o nome do meu domínio e dentro existe 4 arquivos com a extensão <code>.pem</code> que são as chaves e certificados gerados, e que são necessários para a validação do SSL.</p>
<pre><code class="lang-bash">root@ubuntu-letsencrypt:/etc/letsencrypt/live<span class="hljs-comment"># ls -l</span>
total 4
drwxr-xr-x 2 root root 4096 Sep 21 01:12 meublog.aristides.dev

root@ubuntu-letsencrypt:/etc/letsencrypt/live<span class="hljs-comment"># ls -l meublog.aristides.dev/</span>
total 4
-rw-r--r-- 1 root root 682 Sep 21 01:12 README
lrwxrwxrwx 1 root root  45 Sep 21 01:12 cert.pem -&gt; ../../archive/meublog.aristides.dev/cert1.pem
lrwxrwxrwx 1 root root  46 Sep 21 01:12 chain.pem -&gt; ../../archive/meublog.aristides.dev/chain1.pem
lrwxrwxrwx 1 root root  50 Sep 21 01:12 fullchain.pem -&gt; ../../archive/meublog.aristides.dev/fullchain1.pem
lrwxrwxrwx 1 root root  48 Sep 21 01:12 privkey.pem -&gt; ../../archive/meublog.aristides.dev/privkey1.pem
</code></pre>
<p>Os arquivos <code>fullchain.pem</code> e <code>privkey.pem</code> são usados dentro do arquivo de configuração do Nginx do seu site em <code>/etc/nginx/sites-available</code>.</p>
<p>Veja como ficou o arquivo <code>/etc/nginx/sites-available/default.conf</code> do Nginx após o certificado ser gerado:</p>
<pre><code class="lang-nginx"><span class="hljs-comment"># Arquivo: /etc/nginx/sites-available/default.conf</span>
<span class="hljs-comment"># partes das linhas foram ocultadas </span>

<span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">443</span> ssl ipv6only=<span class="hljs-literal">on</span>; <span class="hljs-comment"># managed by Certbot</span>
<span class="hljs-attribute">listen</span> <span class="hljs-number">443</span> ssl; <span class="hljs-comment"># managed by Certbot</span>
<span class="hljs-attribute">ssl_certificate</span> /etc/letsencrypt/live/meublog.aristides.dev/fullchain.pem; <span class="hljs-comment"># managed by Certbot</span>
<span class="hljs-attribute">ssl_certificate_key</span> /etc/letsencrypt/live/meublog.aristides.dev/privkey.pem; <span class="hljs-comment"># managed by Certbot</span>
<span class="hljs-attribute">include</span> /etc/letsencrypt/options-ssl-nginx.conf; <span class="hljs-comment"># managed by Certbot</span>
<span class="hljs-attribute">ssl_dhparam</span> /etc/letsencrypt/ssl-dhparams.pem; <span class="hljs-comment"># managed by Certbot</span>

<span class="hljs-section">server</span> {
    <span class="hljs-attribute">if</span> (<span class="hljs-variable">$host</span> = meublog.aristides.dev) {
        <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> https://<span class="hljs-variable">$host</span><span class="hljs-variable">$request_uri</span>;
    } <span class="hljs-comment"># managed by Certbot</span>


    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span> default_server;
    <span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">80</span> default_server;

    <span class="hljs-attribute">server_name</span> meublog.aristides.dev;
    <span class="hljs-attribute">return</span> <span class="hljs-number">404</span>; <span class="hljs-comment"># managed by Certbot</span>
}
</code></pre>
<p>Bom pessoal é isso aí. O Let's Encrypt nos ajuda a criar e gerenciar certificados automaticamente, sem esforço nenhum, do jeito que gostamos :)</p>
<p>Se ficou com alguma dúvida fique a vontade para perguntar.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Redimensionar imagens no PHP com a biblioteca Imagine]]></title><description><![CDATA[Fala pessoal tudo beleza?
Hoje o assunto é manipulação de imagens com PHP.
Imagine é uma biblioteca OOP (Programação Orientada a Objetos) para manipulação de imagens criada no PHP 5.3. Usa as melhores práticas de desenvolvimento com um design cuidado...]]></description><link>https://aristides.dev/redimensionar-imagens-no-php-com-a-biblioteca-imagine</link><guid isPermaLink="true">https://aristides.dev/redimensionar-imagens-no-php-com-a-biblioteca-imagine</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sun, 22 Mar 2020 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045670904/e6de82a8-6878-499d-899d-02b73c00a565.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Hoje o assunto é manipulação de imagens com PHP.</p>
<p><a target="_blank" href="https://imagine.readthedocs.io/en/stable/index.html?ref=aristides.dev">Imagine</a> é uma biblioteca OOP (Programação Orientada a Objetos) para manipulação de imagens criada no PHP 5.3. Usa as melhores práticas de desenvolvimento com um design cuidadoso permitindo um código desacoplado e testável.</p>
<p>É um projeto open source, caso queira contribuir veja mais <a target="_blank" href="https://github.com/avalanche123/Imagine?ref=aristides.dev">clicando aqui</a>.</p>
<p><strong>Imagine</strong> unifica as bibliotecas <a target="_blank" href="https://imagemagick.org/?ref=aristides.dev">ImageMagick</a>, <a target="_blank" href="http://www.graphicsmagick.org/?ref=aristides.dev">GraphicsMagick</a> e <a target="_blank" href="https://www.php.net/manual/pt_BR/book.image.php?ref=aristides.dev">GD</a>, de forma orientada a objetos e de uma maneira muito simplista. Isso significa que você deve ter pelo menos uma dessas bibliotecas instaladas no seu ambiente.</p>
<p>Com ela você pode criar miniaturas de imagens, cortar, girar, redimensionar, inverter e até criar uma imagem composta.</p>
<h2 id="heading-requisitos">Requisitos</h2>
<p>Em nosso exemplo vamos usar a biblioteca GD, então certifique-se se possui instalada. Para instalar execute o comando abaixo e substitua a versão do seu PHP:</p>
<pre><code class="lang-bash">sudo apt install php7.4-gd
</code></pre>
<h2 id="heading-instalacao-da-imagine">Instalação da Imagine</h2>
<p>Para a instalação vamos utilizar o composer, em seu terminal digite o comando:</p>
<pre><code class="lang-bash">composer require imagine/imagine
</code></pre>
<h2 id="heading-iniciando-com-imagine">Iniciando com Imagine</h2>
<p>Precisamos importar a classe ou as classes para começar a manipular as imagens. Vamos utilizar o autoload do composer para importar as classes:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">require</span> <span class="hljs-string">'vendor/autoload.php'</span>;

$imagine = <span class="hljs-keyword">new</span> Imagine\Gd\Imagine();
</code></pre>
<p>Caso queira baixar os arquivos desse exemplo, deixei no meu GitHub, acesse <a target="_blank" href="https://github.com/aristidesneto/php-imagine?ref=aristides.dev">esse repositório</a> e clone na sua máquina. Os exemplos abaixo estão no arquivo index.php.</p>
<p><em>No próprio repositório há uma descrição melhor para que possa executar os exemplos.</em></p>
<h2 id="heading-resize">Resize</h2>
<p>O redimensionamento de imagens é o mais comum entre aplicações web, muito utilizado para gerar Thumbnails. Veja como é fácil:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span> 

$imagine = <span class="hljs-keyword">new</span> Imagine\GD\Imagine;

<span class="hljs-comment">// Abre a imagem original</span>
$image = $imagine-&gt;open(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/email.png'</span>);

<span class="hljs-comment">// Obtém o tamanho real da imagem</span>
$size = $image&gt;getSize();

<span class="hljs-comment">// Resize da imagem definindo 450px de largura</span>
$image-&gt;resize($size-&gt;widen(<span class="hljs-number">450</span>))
    -&gt;save(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/images/email_resize.png'</span>);
</code></pre>
<h2 id="heading-rotacao">Rotação</h2>
<p>Rotacione a imagem de uma forma simples. Veja para rotacionar em 90 graus.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span> 

$imagine = <span class="hljs-keyword">new</span> Imagine\GD\Imagine;

<span class="hljs-comment">// Abre a imagem original</span>
$image = $imagine-&gt;open(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/email.png'</span>);

<span class="hljs-comment">// Rotaciona em 90 graus</span>
$origin-&gt;rotate(<span class="hljs-number">90</span>)
    -&gt;save(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/images/email_rotate.png'</span>);
</code></pre>
<h2 id="heading-cortar">Cortar</h2>
<p>Corte a imagem baseado em um ponto inicial e seu tamanho. O método <code>crop()</code> deve receber dois parâmetros indicando posição e tamanho da imagem.</p>
<p>Nesse caso, será utilizada a classe <code>Point</code> para informar a posição e a classe <code>Box</code> para informar o tamanho da “caixa” que será cortada.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

$imagine = <span class="hljs-keyword">new</span> Imagine\GD\Imagine;

<span class="hljs-comment">// Abre a imagem original</span>
$image = $imagine-&gt;open(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/email.png'</span>);

<span class="hljs-comment">// Corta a imagem</span>
$origin-&gt;crop(<span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>,<span class="hljs-number">90</span>), <span class="hljs-keyword">new</span> Box(<span class="hljs-number">150</span>, <span class="hljs-number">150</span>))
    -&gt;save(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/images/email_crop.png'</span>);
</code></pre>
<p>Este artigo foi bem ao ponto!</p>
<p>Essas são algumas das opções que podemos utilizar com a biblioteca Imagine. Acesse a documentação para conhecer mais e veja o quanto é simples trabalhar com manipulação de imagens no PHP.</p>
<p>Até próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Deploy Laravel 6 utilizando a ferramenta Deployer]]></title><description><![CDATA[Fala pessoal tudo beleza?
Mais um artigo hoje, no formato tutorial. Nesse artigo aprenderemos como fazer o deploy de uma aplicação desenvolvida com o Laravel, usando uma ferramenta de deploy.
Deployer
O Deployer é uma ferramenta desenvolvida em PHP e...]]></description><link>https://aristides.dev/deploy-laravel-6-utilizando-a-ferramenta-deployer</link><guid isPermaLink="true">https://aristides.dev/deploy-laravel-6-utilizando-a-ferramenta-deployer</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sat, 28 Dec 2019 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045433744/7ed85ca7-8e23-4c17-bbaa-0f8a01cce37b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Mais um artigo hoje, no formato tutorial. Nesse artigo aprenderemos como fazer o deploy de uma aplicação desenvolvida com o Laravel, usando uma ferramenta de deploy.</p>
<h2 id="heading-deployer">Deployer</h2>
<p>O <a target="_blank" href="https://deployer.org/?ref=aristides.dev">Deployer</a> é uma ferramenta desenvolvida em PHP e que faz todo o trabalho pesado em realizar o deploy da sua aplicação PHP, seja usando um framework ou não.</p>
<p>Todos os passos a seguir, foram executados usando o sistema operacional Linux.</p>
<p>Pré requisitos:</p>
<ul>
<li><p>Conta no Bitbucket (ou em outro repositório git)</p>
</li>
<li><p>Uma VPS (estou usando a Digital Ocean)</p>
</li>
<li><p>Servidor com Nginx, PHP e Mysql instalados</p>
</li>
<li><p>Node e NPM instalados no servidor</p>
</li>
<li><p>Acesso root (ou sudo) ao servidor via SSH</p>
</li>
</ul>
<p>Te indico a leitura de 3 artigos do meu blog que pode te ajudar em caso de alguma dúvida de alguns tópicos desse tutorial, são eles:</p>
<ol>
<li><p><a target="_blank" href="https://aristides.dev/configurando-um-servidor-lemp-debian-9-parte-1">Configuração de um servidor web usando LEMP no Debian 9. Criando um usuário e configurando o SSH</a></p>
</li>
<li><p><a target="_blank" href="https://aristides.dev/configurando-um-servidor-lemp-debian-9-parte-2">Aprenda a configurar um servidor web usando LEMP no Debian 9. Iremos instalar o Nginx, MariaDB e PHP 7.2</a></p>
</li>
<li><p><a target="_blank" href="https://aristides.dev/configurando-virtual-host-no-nginx">Aprenda a instalação e a configuração básica de como criar um server block com o servidor web Nginx</a></p>
</li>
</ol>
<p>Serão criados dois ambientes, de produção e homologação. Para acessarmos a aplicação, nesse tutorial iremos utilizar o IP do servidor, mas caso queira pode ser usado um domínio válido e configurado. Por exemplo, caso esteja usando um domínio, crie um subdomínio com o prefixo homolog.seusite.com.br. Esse será o endereço para o ambiente de homologação, e o seusite.com.br o endereço de produção.</p>
<p>Para o meu caso, irei usar o IP e definir as portas para diferenciar os ambientes, por exemplo, a porta 80 para produção e 81 para homologação.</p>
<h2 id="heading-instalando-o-deployer">Instalando o Deployer</h2>
<p>A instalação deve ser feita na máquina de desenvolvimento. Para isso digite os 3 comandos abaixo em seu terminal.</p>
<pre><code class="lang-bash">curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/<span class="hljs-built_in">local</span>/bin/dep
chmod +x /usr/<span class="hljs-built_in">local</span>/bin/dep
</code></pre>
<p>No comando acima, é feito o download do arquivo <code>deployer.phar</code> e em seguinda movemos o arquivo para o diretório <code>/usr/local/bin</code> renomeando para <code>dep</code>, e para finalizar concedemos permissão de execução para o arquivo.</p>
<p>Para testar o deployer, execute no seu terminal o comando:</p>
<pre><code class="lang-bash">dep --version
</code></pre>
<p>Se aparecer a versão do Deployer, sucesso!</p>
<h2 id="heading-instalando-o-laravel">Instalando o Laravel</h2>
<p>Nesse exemplo iremos baixar uma aplicação Laravel usando o composer. Não terá nenhuma regra de negócio na aplicação, apenas irei criar a autenticação padrão do Laravel e uma implementação a mais para que o deploy funcione corretamente.</p>
<pre><code class="lang-bash">mkdir -p ~/projetos/deployer/
<span class="hljs-built_in">cd</span> ~/projetos/deployer
composer create-project --prefer-dist laravel/laravel blog
</code></pre>
<p>Com o projeto criado, entre no diretório da aplicação e execute os comandos abaixo para criar a autenticação padrão do Laravel 6 (Esse passo é somente para o Laravel 6+).</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> blog
composer require laravel/ui --dev
php artisan ui vue --auth
npm install &amp;&amp; npm run dev
</code></pre>
<p>Para que esse exemplo funcione, é necessário criar um controller e alterar a rota barra <code>/</code> do Laravel. Durante o deploy é executado o comando <code>artisan config:cache</code>, e esse comando não funciona se existir uma rota que esteja usando uma função anônima, conhecida também como <em>closure</em>, ou seja, a rota inicial do Laravel utiliza uma função anônima.</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> view(<span class="hljs-string">'welcome'</span>);
});
</code></pre>
<p>Para resolver, crie um controller chamado <code>WelcomeController</code> e altere a rota para:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-string">'WelcomeController@index'</span>);
</code></pre>
<p>No arquivo <code>WelcomeController</code>, adicione no método <code>index()</code>, o return para a view welcome.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WelcomeController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'welcome'</span>);
    }
}
</code></pre>
<p>No arquivo de rotas da API <code>routes/api.php</code> também existe uma função anônima, nesse caso basta remover a rota que o problema é resolvido.</p>
<p>Até esse momento temos nossa aplicação Laravel funcionando e com a autenticação padrão gerada, apenas faltando a conexão com o banco de dados, mas agora não será preciso se preocupar com isso. Vamos fazer isso mais para frente.</p>
<h2 id="heading-conhecendo-o-deployer">Conhecendo o Deployer</h2>
<p>Antes de começar com o deployer, vamos conhecer alguns de seus comandos. Digite no terminal:</p>
<pre><code class="lang-bash">dep list
</code></pre>
<p>Você verá os comandos disponíveis e dentre eles existe o comando <code>init</code>. Essa opção criará o template inicial de uma configuração, é possível também passar um parâmetro informando qual template gostaria de usar. Vamos ver na prática o comando <code>dep init</code>.</p>
<p>Dentro do diretório do seu projeto, digite o comando abaixo:</p>
<pre><code class="lang-bash">dep init
</code></pre>
<p>Veja o retorno do comando:</p>
<pre><code class="lang-bash">Welcome to the Deployer config generator
This utility will walk you through creating a deploy.php file.
It only covers the most common items, and tries to guess sensible defaults.

Press ^C at any time to quit.

Please select your project <span class="hljs-built_in">type</span> [Common]:
[0 ] Common
[1 ] Laravel
[2 ] Symfony
[3 ] Yii
[4 ] Yii2 Basic App
[5 ] Yii2 Advanced App
[6 ] Zend Framework
[7 ] CakePHP
[8 ] CodeIgniter
[9 ] Drupal
[10] TYPO3
&gt;
</code></pre>
<p>O resultado desse comando é uma lista de <strong>Tipos de Projeto</strong> disponíveis para o uso, o padrão é uma aplicação Common, que seria uma aplicação comum em PHP, sem o uso de frameworks.</p>
<p>Selecione a opção 1 para escolher o template Laravel. Em seguida será solicitado que informe o repositório de onde está seu projeto, apenas dê enter, iremos configurar depois. Em seguida ele pergunta se deseja contribuir com a ferramenta enviando informações anônimas, escolha sim ou não.</p>
<p>Finalizado esses passos, veja que no diretório raiz da sua aplicação foi criado um arquivo chamado <code>deploy.php</code>.</p>
<p><em>Dica: Podemos criar o mesmo arquivo deploy.php informando o parâmetro -t seguido do nome do template. Veja como ficaria para nosso exemplo:</em> <code>dep init -t Laravel</code></p>
<h2 id="heading-configurando-o-deployer">Configurando o Deployer</h2>
<p>Abra o arquivo <code>deploy.php</code> criado na raiz do seu projeto e vamos brincar um pouco.</p>
<p>No trecho abaixo, informamos o endereço do repositório onde a aplicação se encontra. Caso você não tenha essa url ainda, no tópico Versionando a aplicação eu falo sobre isso, deixe como está nesse momento, depois basta atualizar essa informação.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Project repository</span>
set(<span class="hljs-string">'repository'</span>, <span class="hljs-string">'git@bitbucket.org:username/deployer-laravel.git'</span>);
</code></pre>
<p>Dica importante: O repositório fica disponível através de dois endereços: <code>git@bitbucket.org</code> e <code>https://username@bitbucket</code>. Existem diferenças entre eles, para que a primeira opção funcione é necessário ter cadastrado a chave pública do SSH no Bitbucket. Na segunda opção é necessário informar a senha para cada requisição realizada.</p>
<p>Na próxima configuração, definimos o host da aplicação, podemos ter um, dois ou mais hosts configurados. Nesse exemplo criaremos dois, de produção e homologação.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Hosts</span>
host(<span class="hljs-string">'production'</span>)
    -&gt;hostname(<span class="hljs-string">'deploy@174.138.55.236'</span>)
    -&gt;port(<span class="hljs-number">33458</span>)
    -&gt;stage(<span class="hljs-string">'prod'</span>)
    -&gt;set(<span class="hljs-string">'keep_releases'</span>, <span class="hljs-number">6</span>)
    -&gt;set(<span class="hljs-string">'branch'</span>, <span class="hljs-string">'master'</span>)
    -&gt;set(<span class="hljs-string">'deploy_path'</span>, <span class="hljs-string">'/var/www/html/production-my-app'</span>);

host(<span class="hljs-string">'homolog'</span>)
    -&gt;hostname(<span class="hljs-string">'deploy@174.138.55.236'</span>)
    -&gt;port(<span class="hljs-number">33458</span>)
    -&gt;stage(<span class="hljs-string">'hml'</span>)
    -&gt;set(<span class="hljs-string">'keep_releases'</span>, <span class="hljs-number">3</span>)
    -&gt;set(<span class="hljs-string">'branch'</span>, <span class="hljs-string">'develop'</span>)
    -&gt;set(<span class="hljs-string">'deploy_path'</span>, <span class="hljs-string">'/var/www/html/homolog-my-app'</span>);
</code></pre>
<p>O código em si é bem simples de entender, mas vou explicar o que cada linha significa:</p>
<ol>
<li><p><strong>host</strong>: é o nome que identifica aquela conexão</p>
</li>
<li><p><strong>hostname</strong>: usuário que será utilizado para fazer o deploy e o IP do servidor</p>
</li>
<li><p><strong>port</strong>: porta SSH utilizada (se a porta for a padrão 22, essa opção pode ser omitida)</p>
</li>
<li><p><strong>stage</strong>: um alias para o host (sempre utilize)</p>
</li>
<li><p><strong>keep_releases</strong>: número de versões que será mantido no servidor</p>
</li>
<li><p><strong>branch</strong>: branch que será utilizado ao realizar o deploy</p>
</li>
<li><p><strong>deploy_path</strong>: diretório onde se encontra a aplicação</p>
</li>
</ol>
<p>O parâmetro stage eu recomendo utilizar porque utilizando o stage com a opção abaixo, o <code>default_stage</code>, quer dizer que sempre ao realizar um deploy (sem informar o host como parâmetro) será realizado em homologação, assim evitando que seja feito deploy para produção sem querer.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Stage default</span>
set(<span class="hljs-string">'default_stage'</span>, <span class="hljs-string">'hml'</span>);
</code></pre>
<p>O restante das informações são bem auto explicativas, caso tiver com dúvidas deixe sua pergunta nos comentários, e não deixe de olhar a documentação do deployer, vale a pena.</p>
<h2 id="heading-versionando-a-aplicacao">Versionando a aplicação</h2>
<p>Crie um repositório no bitbucket e copie a url do seu repositório, com essa url você poderá atualizar o arquivo deploy.php conforme mencionei no tópico anterior.</p>
<p>Dentro do diretório do seu projeto, vamos utilizar o git para versionar e enviar para o bitbucket.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ~/projetos/deployer/blog
git init
git add .
git commit -m “Primeiro commit”
git remote add origin https://username@bitbucket.org/username/deployer-laravel.git
git push -u origin master
</code></pre>
<p>Acabamos de enviar para o repositório nossa aplicação.</p>
<h2 id="heading-branch-develop">Branch develop</h2>
<p>Vamos criar um branch chamado <code>develop</code>, que será nosso branch de homologação. Também iremos enviar para o bitbucket.</p>
<pre><code class="lang-bash">git checkout -b develop
git push -u origin develop
</code></pre>
<p>Pronto! Até o momento temos nossa aplicação Laravel versionada no Bitbucket com dois branchs (master e develop).</p>
<h2 id="heading-criando-banco-de-dados-e-usuario-mysql">Criando banco de dados e usuário Mysql</h2>
<p>Agora os passos a seguir serão executados no servidor.</p>
<p>Acesse o servidor via SSH para criarmos o banco de dados da nossa aplicação. Digite no terminal os comandos abaixo para criar o banco:</p>
<pre><code class="lang-bash">mysql -uroot -psenha
CREATE DATABASE laravel;
CREATE USER <span class="hljs-string">'deployer'</span>@<span class="hljs-string">'localhost'</span> IDENTIFIED BY <span class="hljs-string">'senhaforteaqui'</span>;
GRANT ALL PRIVILEGES ON laravel . * TO <span class="hljs-string">'deployer'</span>@<span class="hljs-string">'localhost'</span>;
FLUSH PRIVILEGES;
</code></pre>
<p>Pronto, criamos nosso banco de dados chamado laravel e criamos também um usuário com todos os privilégios para que possamos conectar no banco.</p>
<h2 id="heading-configurando-usuario-deploy">Configurando usuário deploy</h2>
<p>Ainda logado no servidor, crie um usuário shell que será responsável em realizar o deploy da sua aplicação:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Informe uma senha forte</span>
adduser deploy

<span class="hljs-comment"># Adicionando o deploy no grupo www-data</span>
usermod -aG www-data deploy
</code></pre>
<h2 id="heading-diretorio-web">Diretório WEB</h2>
<p>Ainda no servidor, vamos criar dois diretórios que irão hospedar nossa aplicação web e alterar o dono para o usuário deploy. Digite os seguintes comandos:</p>
<pre><code class="lang-bash">mkdir -p /var/www/html/production-my-app
mkdir -p /var/www/html/homolog-my-app
chown deploy:deploy /var/www/html/production-my-app
chown deploy:deploy /var/www/html/homolog-my-app
</code></pre>
<h2 id="heading-configurando-arquivo-host-do-nginx">Configurando arquivo host do Nginx</h2>
<p>Entre no diretório de configuração de sites do nginx e crie dois arquivos de configuração.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /etc/nginx/sites-available
touch production.conf homolog.conf
</code></pre>
<p>Agora copie e cole o conteúdo abaixo dentro do arquivo <code>production.conf</code>.</p>
<p>Lembre-se de alterar as informações referente ao seu host, por exemplo o IP e a porta caso necessário.</p>
<pre><code class="lang-nginx"><span class="hljs-comment"># Arquivo production.conf</span>
<span class="hljs-section">server</span> {

    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
    <span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">80</span>;       

    <span class="hljs-attribute">server_name</span> <span class="hljs-number">174.138.55.236</span>;

    <span class="hljs-comment"># Log</span>
    <span class="hljs-attribute">access_log</span> /var/log/nginx/access.log;
    <span class="hljs-attribute">error_log</span> /var/log/nginx/error.log;

    <span class="hljs-comment"># Directory root</span>
    <span class="hljs-attribute">root</span> /var/www/html/production-my-app/current/public;
    <span class="hljs-attribute">index</span> index.php index.html;

    <span class="hljs-attribute">client_max_body_size</span> <span class="hljs-number">128M</span>;

    <span class="hljs-attribute">location</span> / {
        <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php?<span class="hljs-variable">$query_string</span>;
    }

    <span class="hljs-comment"># pass PHP scripts to FastCGI server</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
        <span class="hljs-comment"># Note que aqui definimos a versao que iremos utilizar do PHP</span>
        <span class="hljs-attribute">fastcgi_pass</span> unix:/var/run/php/php7.2-fpm.sock;
        <span class="hljs-attribute">fastcgi_index</span> index.php;
        <span class="hljs-attribute">fastcgi_split_path_info</span><span class="hljs-regexp"> ^(.+\.php)(.*)$</span>;
        <span class="hljs-attribute">include</span> /etc/nginx/fastcgi_params;
        <span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$realpath_root</span><span class="hljs-variable">$fastcgi_script_name</span>;
        <span class="hljs-attribute">fastcgi_param</span> DOCUMENT_ROOT <span class="hljs-variable">$realpath_root</span>;
    }

    <span class="hljs-comment"># deny access to .htaccess files</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.ht</span> {
        <span class="hljs-attribute">deny</span> all;
    }

    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.well-known</span> {
        <span class="hljs-attribute">allow</span> all;
    }

    <span class="hljs-comment"># Set header expirations on per-project basis</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$</span> {
        <span class="hljs-attribute">expires</span> <span class="hljs-number">365d</span>;
    }
}
</code></pre>
<p>Abra o arquivo <code>homolog.conf</code> e cole o conteúdo abaixo:</p>
<pre><code class="lang-nginx"><span class="hljs-comment"># Arquivo homolog.conf</span>
<span class="hljs-section">server</span> {

    <span class="hljs-attribute">listen</span> <span class="hljs-number">81</span>;
    <span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">81</span>;       

    <span class="hljs-attribute">server_name</span> <span class="hljs-number">174.138.55.236</span>;

    <span class="hljs-comment"># Log</span>
    <span class="hljs-attribute">access_log</span> /var/log/nginx/access.log;
    <span class="hljs-attribute">error_log</span> /var/log/nginx/error.log;

    <span class="hljs-comment"># Directory root</span>
    <span class="hljs-attribute">root</span> /var/www/html/homolog-my-app/current/public;
    <span class="hljs-attribute">index</span> index.php index.html;

    <span class="hljs-attribute">client_max_body_size</span> <span class="hljs-number">128M</span>;

    <span class="hljs-attribute">location</span> / {
        <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php?<span class="hljs-variable">$query_string</span>;
    }

    <span class="hljs-comment"># pass PHP scripts to FastCGI server</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
        <span class="hljs-comment"># Note que aqui definimos a versao que iremos utilizar do PHP</span>
        <span class="hljs-attribute">fastcgi_pass</span> unix:/var/run/php/php7.2-fpm.sock;
        <span class="hljs-attribute">fastcgi_index</span> index.php;
        <span class="hljs-attribute">fastcgi_split_path_info</span><span class="hljs-regexp"> ^(.+\.php)(.*)$</span>;
        <span class="hljs-attribute">include</span> /etc/nginx/fastcgi_params;
        <span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$realpath_root</span><span class="hljs-variable">$fastcgi_script_name</span>;
        <span class="hljs-attribute">fastcgi_param</span> DOCUMENT_ROOT <span class="hljs-variable">$realpath_root</span>;
    }

    <span class="hljs-comment"># deny access to .htaccess files</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.ht</span> {
        <span class="hljs-attribute">deny</span> all;
    }

    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.well-known</span> {
        <span class="hljs-attribute">allow</span> all;
    }

    <span class="hljs-comment"># Set header expirations on per-project basis</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$</span> {
        <span class="hljs-attribute">expires</span> <span class="hljs-number">365d</span>;
    }
}
</code></pre>
<p>Ative os dois sites que acabamos de configurar:</p>
<pre><code class="lang-bash">ln -s /etc/nginx/sites-available/production.conf /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/homolog.conf /etc/nginx/sites-enabled/
service nginx restart
</code></pre>
<p>Pronto. Arquivos vhost do nginx configurados. Se você tentar acessar agora, não funcionará, pois ainda falta realizar o deploy da aplicação, calma que vamos chegar lá.</p>
<h2 id="heading-configurando-chaves-ssh-no-bitbucket">Configurando chaves SSH no Bitbucket</h2>
<p>Para que o deploy funcione sem a necessidade de informar a senha, vamos criar uma chave SSH para o usuário deploy.</p>
<p>Logue no servidor com o usuário deploy criado neste tutorial e digite o comando:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Acessando o servidor com o usuário deploy</span>
ssh deploy@174.138.55.236

<span class="hljs-comment"># Gerar chaves SSH</span>
ssh-keygen
</code></pre>
<p>Confirme as informações, quando solicitar uma senha, não é necessário informar, apenas dê enter até finalizar.</p>
<p>Será gerado dois arquivos em <code>/home/deploy/.ssh</code> chamado <code>id_rsa</code> (chave privada) e <code>id_rsa.pub</code> (chave pública).</p>
<p>Copie todo o conteúdo do arquivo <code>id_rsa.pub</code> e siga os passos:</p>
<ol>
<li><p>Abra sua conta do bitbucket</p>
</li>
<li><p>Clique sobre sua foto de perfil e em Bitbucket settings</p>
</li>
<li><p>No painel da esquerda clique em SSH Keys</p>
</li>
<li><p>Clique no botão Add key e cole o conteúdo no campo key</p>
</li>
<li><p>Dê um nome para identificar essa chave</p>
</li>
<li><p>Clique em Add key para salvar as informações</p>
</li>
</ol>
<p>Nesse ponto a comunicação do servidor e bitbucket já pode ser feita através das chaves SSH de maneira segura e sem a necessidade de informar uma senha.</p>
<p>Apenas para efeito de teste, vamos clonar nosso repositório no servidor. Copie a url do repositório que pode ser obtida no próprio repositório, e dentro do diretório /home/deploy (no servidor) cole a url do repositório e dê enter:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> git@bitbucket.org:username/deployer-laravel.git
</code></pre>
<p>Irá aparecer uma mensagem para que confirme o host e que seja adicionado na lista de hosts confiáveis, informe yes para continuar.</p>
<p>Sucesso. Seu repositório foi clonado.</p>
<h2 id="heading-ufa-chegamos-no-deploy">Ufa, chegamos no deploy!</h2>
<p>Bom, vamos ver como ficou o nosso arquivo de deploy? Após nossas configurações o arquivo deve estar parecido com esse:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Deployer</span>;

<span class="hljs-keyword">require</span> <span class="hljs-string">'recipe/laravel.php'</span>;

<span class="hljs-comment">// Project name</span>
set(<span class="hljs-string">'application'</span>, <span class="hljs-string">'My APP'</span>);

<span class="hljs-comment">// Project repository</span>
set(<span class="hljs-string">'repository'</span>, <span class="hljs-string">'git@bitbucket.org:username/deployer-laravel.git'</span>);

<span class="hljs-comment">// [Optional] Allocate tty for git clone. Default value is false.</span>
set(<span class="hljs-string">'git_tty'</span>, <span class="hljs-literal">true</span>);

<span class="hljs-comment">// Shared files/dirs between deploys</span>
add(<span class="hljs-string">'shared_files'</span>, []);
add(<span class="hljs-string">'shared_dirs'</span>, []);

<span class="hljs-comment">// Writable dirs by web server</span>
add(<span class="hljs-string">'writable_dirs'</span>, []);

<span class="hljs-comment">// Stage default</span>
set(<span class="hljs-string">'default_stage'</span>, <span class="hljs-string">'hml'</span>);

<span class="hljs-comment">// Hosts</span>
host(<span class="hljs-string">'production'</span>)
    -&gt;hostname(<span class="hljs-string">'deploy@174.138.55.236'</span>)
    -&gt;port(<span class="hljs-number">33458</span>)
    -&gt;stage(<span class="hljs-string">'prod'</span>)
    -&gt;set(<span class="hljs-string">'keep_releases'</span>, <span class="hljs-number">6</span>)
    -&gt;set(<span class="hljs-string">'branch'</span>, <span class="hljs-string">'master'</span>)
    -&gt;set(<span class="hljs-string">'deploy_path'</span>, <span class="hljs-string">'/var/www/html/production-my-app'</span>);

host(<span class="hljs-string">'homolog'</span>)
    -&gt;hostname(<span class="hljs-string">'deploy@174.138.55.236'</span>)
    -&gt;port(<span class="hljs-number">33458</span>)
    -&gt;stage(<span class="hljs-string">'hml'</span>)
    -&gt;set(<span class="hljs-string">'keep_releases'</span>, <span class="hljs-number">3</span>)
    -&gt;set(<span class="hljs-string">'branch'</span>, <span class="hljs-string">'develop'</span>)
    -&gt;set(<span class="hljs-string">'deploy_path'</span>, <span class="hljs-string">'/var/www/html/homolog-my-app'</span>);

<span class="hljs-comment">// Tasks</span>

task(<span class="hljs-string">'npm_run'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Descomente esse if após o primeiro deploy</span>
    <span class="hljs-comment">//if (has('previous_release')) {</span>
    <span class="hljs-comment">//    run('cp -R {{previous_release}}/node_modules {{release_path}}/node_modules');</span>
    <span class="hljs-comment">//}</span>

    run(<span class="hljs-string">'cd {{release_path}} &amp;&amp; npm install'</span>);
    run(<span class="hljs-string">'cd {{release_path}} &amp;&amp; npm run prod'</span>);
});

<span class="hljs-comment">// [Optional] if deploy fails automatically unlock.</span>
after(<span class="hljs-string">'deploy:failed'</span>, <span class="hljs-string">'deploy:unlock'</span>);

<span class="hljs-comment">// Migrate database before symlink new release.</span>

<span class="hljs-comment">// Descomente essa linha após o primeiro deploy</span>
<span class="hljs-comment">// before('deploy:symlink', 'artisan:migrate');</span>
</code></pre>
<p>Um detalhe muito importante para fazermos antes do primeiro deploy é comentar a última linha, ela é responsável em executar a migrate, se fizermos o deploy com a migrate sem antes configurar o arquivo .env no servidor, irá dar erro de conexão com o banco de dados e isso é fato.</p>
<p>Mais um teste rápido antes de iniciar, dentro do seu projeto local, digite no terminal o comando dep ssh:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045549579/036f6826-6951-4f65-bbc2-ac49dada1fb8.png" alt="Executar login SSH no servidor" /></p>
<p>Executar login SSH no servidor</p>
<p>Viu que bacana? O deployer identificou que foi configurado dois hosts e ele te deu a opção de escolher em qual host deseja conectar via SSH. Informei o host de homologação e ele realizou o login no servidor.</p>
<p>A mensagem de erro dizendo que o diretório <code>current</code> não existe é normal, pois ainda não realizamos o deploy e a estrutura de diretórios que a ferramenta de deploy cria, não existe no momento.</p>
<p>Chega de enrolação, bora fazer esse deploy. Se ainda continua logado no servidor, digite <code>exit</code> para sair e voltar para sua máquina. Dentro do diretório do seu projeto digite o comando <code>dep deploy</code>. Veja que não passamos nenhum parâmetro, nesse caso o deploy será realizado no host de homologação.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045528004/fc5b78f0-20cf-4f4f-9f5c-46be9a787de1.png" alt="Realizando o deploy da aplicação" /></p>
<p>Realizando o deploy da aplicação</p>
<p>Agora precisamos configurar o arquivo <code>.env</code>, se tentar acessar o endereço IP na porta 81 irá receber um erro 500.</p>
<p>Logando no servidor através do comando <code>dep ssh</code>, dentro do diretório current de homologação (diretório atual quando se faz login usando o <code>dep ssh</code>), se abrirmos o arquivo <code>.env</code> verá que está em branco. Copie o conteúdo do arquivo <code>.env.example</code> para <code>.env</code>:</p>
<pre><code class="lang-bash">cp .env.example .env
</code></pre>
<p>Agora abra o .env e adicione as informações de acesso ao Mysql que criamos no início do tutorial. Salve as alterações e saia do arquivo.</p>
<pre><code class="lang-bash">DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=deployer
DB_PASSWORD=senhaforteaqui
</code></pre>
<p>É necessário gerar a chave de segurança do artisan para que a aplicação funcione corretamente, execute os comandos:</p>
<pre><code class="lang-bash">php artisan config:clear
php artisan key:generate
</code></pre>
<p>Nesse momento sua aplicação deve estar funcionando, acesse seu endereço IP na porta 81 e verifique se a página inicial do Laravel irá aparecer. Se não deu certo, verifique o log do Laravel para identificar o problema.</p>
<p>Vamos continuar pois ainda não acabou. Agora que configuramos nosso arquivo .env com as informações de acesso ao Mysql, vamos executar o deploy novamente agora com a opção de executar a migrate. Lembra que comentamos a linha responsável em executar a migrate, vamos descomentar esse linha e aproveite para descomentar a linha da tarefa do <code>npm_run</code>, ficando assim:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Descomente o if da tarefa npm_run</span>
<span class="hljs-keyword">if</span> (has(<span class="hljs-string">'previous_release'</span>)) {
    run(<span class="hljs-string">'cp -R {{previous_release}}/node_modules {{release_path}}/node_modules'</span>);
}

<span class="hljs-comment">// Descomente o artisan migrate</span>
before(<span class="hljs-string">'deploy:symlink'</span>, <span class="hljs-string">'artisan:migrate'</span>);
</code></pre>
<p>Ou seja, antes da tarefa symlink, será executado as migrations. Vamos executar o deploy novamente:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045496533/6f054aee-5ce9-4c26-91a3-6644d3e3604a.png" alt="Deploy da migration realizado com sucesso" /></p>
<p>Deploy da migration realizado com sucesso</p>
<p>Veja que lindo que foi o resultado. Foi executada uma tarefa nova agora, a tarefa responsável em criar as migrations, veja a linha <code>artisan:migrate</code> no log acima.</p>
<p>Bom, agora se você acessar sua aplicação e realizar o cadastro de um usuário verá que funcionará lindamente.</p>
<h2 id="heading-deploy-em-producao">Deploy em produção</h2>
<p>Configuramos o deployer para que seja feito em dois ambientes, em homologação e produção. Ao executar o comando dep deploy é feito o deploy em homologação. Para que seja feito em produção é necessário informar um parâmetro a mais no comando, veja:</p>
<pre><code class="lang-bash">dep deploy prod
</code></pre>
<p>O prod na frente do comando vem do stage definido no arquivo deploy.php. Agora que você já sabe como fazer o deploy em homologação que tal fazer a mesma coisa para produção?</p>
<p>Execute os mesmos passos que você fez para homolog, crie um banco de dados para produção e após o primeiro deploy configure seu .env com os dados de acesso ao Mysql.</p>
<p>A implantação de uma ferramenta de deploy apesar de parecer simples, se torna um pouco complicado se o seu ambiente não está preparado ainda, por exemplo, as configurações essenciais do servidor como o Mysql, Nginx, PHP, etc...</p>
<p>Fico a disposição caso tenham alguma dúvida ou algum problema.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Script de backup de banco de dados Mysql]]></title><description><![CDATA[Fala pessoal tudo beleza?
Nesse artigo vou mostrar um script bem bacana para fazer backup de banco de dados Mysql.
O script irá gerar um arquivo de log para futuras consultas e também terá a opção de remover os backups antigos. Uma dica interessante ...]]></description><link>https://aristides.dev/script-de-backup-de-banco-de-dados-mysql</link><guid isPermaLink="true">https://aristides.dev/script-de-backup-de-banco-de-dados-mysql</guid><category><![CDATA[dicas]]></category><category><![CDATA[MySQL]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Mon, 10 Jun 2019 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735045147112/fdb8d9ba-2af0-45a1-a6eb-af5c4a249550.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Nesse artigo vou mostrar um script bem bacana para fazer backup de banco de dados Mysql.</p>
<p>O script irá gerar um arquivo de log para futuras consultas e também terá a opção de remover os backups antigos. Uma dica interessante é utilizar notificações para que quando ocorrer algum erro seja enviado um email ou uma notificação.</p>
<blockquote>
<p><strong>Atualização (11/03/2023): Backup na AWS S3 - Escrevi um novo artigo onde adicionei a opção de sincronizar o backup do Mysql no S3 da AWS. Acesse o novo artigo</strong> <a target="_blank" href="https://aristides.dev/script-de-backup-do-mysql-e-armazenamento-no-s3-da-aws"><strong>nesse link.</strong></a></p>
</blockquote>
<p>Bora então ver esse script?</p>
<p>Irei colocar trechos do código do script separadamente e com explicações. No final deixarei o script completo para vocês, ok?</p>
<p>Aqui iremos definir o caminho de onde serão salvos os arquivos de logs.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Caminho do arquivo de log</span>
LOG_DIR=/var/<span class="hljs-built_in">log</span>/backup
LOG=<span class="hljs-variable">$LOG_DIR</span>/backup_db_<span class="hljs-variable">$ANO</span><span class="hljs-variable">$MES</span><span class="hljs-variable">$DIA</span>.<span class="hljs-built_in">log</span>
</code></pre>
<p>Nesse trecho definimos o diretório que os backups serão salvos.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Diretorio onde serão salvos os backups</span>
DIR_BK=/var/backups/database
</code></pre>
<p>Para evitar erros durante o processo, antes de salvarmos os arquivos de log e backup iremos verificar se o diretório existe, caso não, iremos criar.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Verifica se existe o diretorio para armazenar os logs</span>
<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$LOG</span> ]; <span class="hljs-keyword">then</span>
    mkdir <span class="hljs-variable">$LOG</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Verifica se existe o diretorio para o backup</span>
<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$DIR_BK</span> ]; <span class="hljs-keyword">then</span>
    mkdir -p <span class="hljs-variable">$DIR_BK</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<p>Aqui vai uma dica bastante útil, você pode definir os bancos de dados que serão backupeados durante o processo. Para isso informe o nome dos bancos separados por espaços.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Lista dos bancos de dados que serão realizados o backup</span>
DATABASES=(banco01 banco02 banco03)
</code></pre>
<p>Agora nesse trecho, iremos fazer um loop percorrendo a lista de bancos que foram informados na linha acima. Após o término do backup iremos compactar o arquivo sql em bz2.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Loop para backupear todos os bancos</span>
<span class="hljs-keyword">for</span> db <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${DATABASES[@]}</span>"</span>; <span class="hljs-keyword">do</span>
    <span class="hljs-comment"># Mysql DUMP</span>
    mysqldump -u<span class="hljs-variable">$USER</span> -p<span class="hljs-variable">$PASS</span> <span class="hljs-variable">$db</span> &gt; <span class="hljs-variable">$DIR_BK</span>/<span class="hljs-variable">$db</span><span class="hljs-string">'_'</span><span class="hljs-variable">$DATA_ATUAL</span>.sql

    <span class="hljs-comment"># Compacta o arquivo sql em BZ2</span>
    bzip2 <span class="hljs-variable">$DIR_BK</span>/<span class="hljs-variable">$db</span><span class="hljs-string">'_'</span><span class="hljs-variable">$DATA_ATUAL</span>.sql
<span class="hljs-keyword">done</span>
</code></pre>
<p>Nessa linha, informamos que os backups com mais de 5 dias serão removidos, assim economizando espaço. Você pode alterar o número de dias conforme sua necessidade.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Remove arquivos de backups antigos</span>
find <span class="hljs-variable">$DIR_BK</span> -<span class="hljs-built_in">type</span> f -mtime +5 -<span class="hljs-built_in">exec</span> rm -rf {} \;
</code></pre>
<p>Bom, até aqui vimos alguns trechos do script de backup, que somente eles não fazem sentido.</p>
<p>Esse é um script simples, mas que pode ser melhorado e já resolve e muito nas rotinas de backups.</p>
<p>Veja abaixo o script completo.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># </span>
<span class="hljs-comment"># Autor: Aristides Neto</span>
<span class="hljs-comment"># Email: falecom@aristides.dev</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Data: 09/06/2019</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Realiza o backup de bancos de dados MySQL</span>
<span class="hljs-comment">#</span>

<span class="hljs-comment"># Define usuario e senha do banco</span>
USER=<span class="hljs-string">'root'</span>
PASS=<span class="hljs-string">'root'</span>

<span class="hljs-comment"># Datas</span>
DIA=`date +%d`
MES=`date +%m`
ANO=`date +%Y`
DATA_ATUAL=`date +%Y-%m-%d-%H-%M`

<span class="hljs-comment"># Data de Inicio do Backup</span>
DATA_INICIO=`date +%d/%m/%Y-%H:%M:%S`

<span class="hljs-comment"># Caminho do arquivo de log</span>
LOG_DIR=/var/<span class="hljs-built_in">log</span>/backup
LOG=<span class="hljs-variable">$LOG_DIR</span>/backup_db_<span class="hljs-variable">$ANO</span><span class="hljs-variable">$MES</span><span class="hljs-variable">$DIA</span>.<span class="hljs-built_in">log</span>

<span class="hljs-comment"># Diretorio onde serão salvos os backups</span>
DIR_BK=/var/backups/database

<span class="hljs-comment"># Lista dos bancos de dados que serão realizados o backup</span>
DATABASES=(banco01 banco02)

<span class="hljs-comment"># Verifica se existe o diretorio para armazenar os logs</span>
<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$LOG_DIR</span> ]; <span class="hljs-keyword">then</span>
    mkdir <span class="hljs-variable">$LOG_DIR</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Verifica se existe o diretorio para o backup</span>
<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$DIR_BK</span> ]; <span class="hljs-keyword">then</span>
    mkdir -p <span class="hljs-variable">$DIR_BK</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Inicio do backup</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"MYSQLDUMP Iniciado em <span class="hljs-variable">$DATA_INICIO</span>"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>

<span class="hljs-comment"># Loop para backupear todos os bancos</span>
<span class="hljs-keyword">for</span> db <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${DATABASES[@]}</span>"</span>; <span class="hljs-keyword">do</span>
    <span class="hljs-comment"># Mysql DUMP</span>
    <span class="hljs-comment"># Para backupear procedures e functions foi adicionado o --routines</span>
    mysqldump --routines -u<span class="hljs-variable">$USER</span> -p<span class="hljs-variable">$PASS</span> <span class="hljs-variable">$db</span> &gt; <span class="hljs-variable">$DIR_BK</span>/<span class="hljs-variable">$db</span><span class="hljs-string">'_'</span><span class="hljs-variable">$DATA_ATUAL</span>.sql

    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Realizando backup do banco ...............[ <span class="hljs-variable">$db</span> ]"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>

    <span class="hljs-comment"># Compacta o arquivo sql em BZ2</span>
    bzip2 <span class="hljs-variable">$DIR_BK</span>/<span class="hljs-variable">$db</span><span class="hljs-string">'_'</span><span class="hljs-variable">$DATA_ATUAL</span>.sql
<span class="hljs-keyword">done</span>

DATA_FINAL=`date +%d/%m/%Y-%H:%M:%S`
<span class="hljs-built_in">echo</span> <span class="hljs-string">"MYSQLDUMP Finalizado em <span class="hljs-variable">$DATA_FINAL</span>"</span> &gt;&gt; <span class="hljs-variable">$LOG</span>

<span class="hljs-comment"># Remove arquivos de backups antigos - 5 dias</span>
find <span class="hljs-variable">$DIR_BK</span> -<span class="hljs-built_in">type</span> f -mtime +5 -<span class="hljs-built_in">exec</span> rm -rf {} \;
</code></pre>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Lembre-se, sempre faça backup e restore dos seus backups... Tenha certeza que quando for precisar restaurar um banco, você tenha um backup confiável!</p>
<p>O que achou desse script? Se tiver alguma dúvida deixa um comentário que faço questão em ajudar!</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Configurar DNS na CloudFlare e ativar SSL]]></title><description><![CDATA[Fala pessoal tudo beleza?
Vejo muito gente perguntando nos grupos se vale a pena usar CloudFlare e como configurar um DNS.
Se você usa Linode, DigitalOcean, AWS ou qualquer outro serviço e até uma hospedagem compartilhada veja como é fácil configurar...]]></description><link>https://aristides.dev/configurar-dns-na-cloudflare-e-ativar-ssl</link><guid isPermaLink="true">https://aristides.dev/configurar-dns-na-cloudflare-e-ativar-ssl</guid><category><![CDATA[dicas]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sun, 29 Jul 2018 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044925619/bbd57f61-9248-4902-9c52-e4bfb100e906.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Vejo muito gente perguntando nos grupos se vale a pena usar CloudFlare e como configurar um DNS.</p>
<p>Se você usa Linode, DigitalOcean, AWS ou qualquer outro serviço e até uma hospedagem compartilhada veja como é fácil configurar.</p>
<p>Se vale a pena? Claro que sim. A CloudFlare é um serviço de distribuição de conteúdo, ao usar seus serviços você terá várias “cópias” dos seus sites em diversos servidores da CloudFlare espalhados pelo mundo.</p>
<p>Assim quando os usuários acessarem seu site ele terá acesso a uma cópia do seu site em cache, e o download será muito mais rápido comparando a um site que não usa a CloudFlare.</p>
<p>Além disso fornece um certificado SSL totalmente gratuito. Proteção contra ataques e muito mais...</p>
<p>Bom, bora configurar então!</p>
<p>Antes de começarmos vamos fazer um pequeno teste. Execute no seu terminal o comando de ping para seu site:</p>
<pre><code class="lang-bash">ping aristides.dev
</code></pre>
<p>Preste atenção ao tempo de resposta do seu ping. Somente guarde esses valores ok? Iremos usar lá em baixo.</p>
<h2 id="heading-adicionando-o-site">Adicionando o Site</h2>
<p>Acesse o site da CloudFlare <a target="_blank" href="https://www.cloudflare.com/?ref=aristides.dev">clicando aqui</a> e realize seu cadastro.</p>
<p>Após o login, a tela que você verá será o dashboard, onde os sites já adicionados estarão disponíveis para configurar. No nosso caso iremos adicionar um novo site. Então clique no botão <strong>Add Site</strong>.</p>
<p>Você irá digitar o domínio que deseja adicionar. Vou usar um domínio fictício para esse exemplo: <code>dev-linkinside.com.br</code>. Clique no botão Add Site.</p>
<p>A próxima tela diz que a Cloudflare irá consultar os registros de DNS do seu domínio para agilizar o nosso trabalho para que não precisamos digitar todas as informações. Apenas clique em <strong>Next</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044732088/61db97d5-6c31-410d-a97b-23256b57640c.png" alt="Tela de informação sobre o processo de consulta de DNS" /></p>
<p>Tela de informação sobre o processo de consulta de DNS</p>
<p>Após clicar em Next será necessário escolher o plano que deseja. Escolha o plano Free e clique em Confirm Plan. Para saber mais sobre cada plano clique no link ao lado Learn More.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044754703/3e8334ad-c8e0-40c0-8b94-7f3dd3b73aac.png" alt="Listagem de planos" /></p>
<p>Listagem de planos</p>
<p>Na seguinte tela será exibido todos os registros DNS que a CloudFlare encontrou. Na figura abaixo existem somente 3 registros do tipo A (por ser um exemplo). Caso você já tenha um site funcionando há algum tempo, pode ser que tenha mais registros. Por exemplo: Tipo A, TXT, CNAME, etc.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044813546/3f09f28f-0a1a-4f12-a6a6-bb196674e67a.png" alt="Listagem de registros DNS encontrados" /></p>
<p>Listagem de registros DNS encontrados</p>
<p>Após clicar em continuar, virá a informação mais importante que é alterar o DNS para a CloudFlare. Para isso entre no registro.br e altere o DNS para o informado abaixo. No meu caso, já esta com o DNS da CloudFlare por isso a tabela de <strong>From</strong> e <strong>To</strong> são iguais.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044837534/a2f9dfee-bbe3-4b90-828b-5fac05b87bbc.png" alt="Alterar registros DNS" /></p>
<p>Alterar registros DNS</p>
<p>Após você alterar seu registro de DNS no registro.br, espere alguns minutos para que a CloudFlare reconheça essa alteração e seu domínio fique ativado como a imagem abaixo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044853928/e1d069a3-c401-4a2c-880b-c159f281eeec.png" alt="Status de alteração do serviço" /></p>
<p>Status de alteração do serviço</p>
<p>As configurações básicas já estão prontas. Agora você já pode configurar adicionando SSL, cache de arquivos, entre outras opções. Irei citar aqui algumas.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044869331/0a917c71-48c8-4e89-993a-ae7ae1d57f8f.png" alt="Opções de serviços na Cloudflare" /></p>
<p>Opções de serviços na Cloudflare</p>
<p>No menu acima existem várias opções para você alterar que deixam seu site mais otimizado, vou citar algumas e deixar você fazer o resto ok?</p>
<ul>
<li><p><strong>Overview</strong>: Mostra o status do seu domínio e o resumo de algumas informações.</p>
</li>
<li><p><strong>Analytics</strong>: Estatísticas de tráfego do seu site.</p>
</li>
<li><p><strong>DNS</strong>: A mais importante, onde você irá configurar o IP do seu atual servidor, criar registro para contasde emails, para subdominios e ativar o SSL do seu site deixando a nuvem amarela ligada.</p>
</li>
<li><p><strong>Speed</strong>: Otimizar a performance do seu site minificando os arquivos JS, CSS e HTML.</p>
</li>
</ul>
<p>Deixo para vocês consultarem cada item do menu e ver o que cada um oferece de configuração. Se tiver alguma dúvida deixe sua pergunta nos comentários.</p>
<p><em>Ah! Lembra quando você executou o comando ping no seu site para ver o tempo de resposta? Então, faça novamente após estiver tudo certo na CloudFlare e veja como estará seu ping agora.</em></p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Instalando LogViewer no Laravel 5.6]]></title><description><![CDATA[Fala pessoal tudo beleza?
Hoje vai uma dica de como visualizar os logs do Laravel de uma maneira mais amigável. Quem nunca abriu aquele arquivo de log do Laravel e se deparou com um monte de linhas e se perdeu com tantas informações? Normal.
O LogVie...]]></description><link>https://aristides.dev/instalando-logviewer-no-laravel-5-6</link><guid isPermaLink="true">https://aristides.dev/instalando-logviewer-no-laravel-5-6</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sat, 23 Jun 2018 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044381737/3a48c038-9b6c-4e58-80b9-908a37dc243f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Hoje vai uma dica de como visualizar os logs do Laravel de uma maneira mais amigável. Quem nunca abriu aquele arquivo de log do Laravel e se deparou com um monte de linhas e se perdeu com tantas informações? Normal.</p>
<p>O LogViewer é um pacote desenvolvido por Arcanedev e está disponível em seu repositório no GitHub. Para mais detalhes sobre o pacote <a target="_blank" href="https://github.com/ARCANEDEV/LogViewer?ref=aristides.dev">clique aqui</a>.</p>
<h2 id="heading-instalando-logviewer">Instalando LogViewer</h2>
<p>Podemos usar o comando abaixo para instalar o pacote no Laravel 5.6:</p>
<pre><code class="lang-bash">composer require arcanedev/log-viewer
</code></pre>
<p>Para instalação do pacote na versão 5.5 do Laravel execute o comando:</p>
<pre><code class="lang-bash">composer require arcanedev/log-viewer:4.4.*
</code></pre>
<p><em>Nota: Para versões anteriores ao 5.5 verifique esse</em> <a target="_blank" href="https://github.com/ARCANEDEV/LogViewer/blob/master/_docs/1.Installation-and-Setup.md?ref=aristides.dev#version-compatibility"><em>link</em></a> <em>e altere o comando conforme sua aplicação.</em></p>
<p>Para quem estiver usando uma versão do Laravel anterior ao 5.5 será necessário registrar o Service Provider em <code>config/app.php</code> no array providers. Caso não seja seu caso pule esse passo.</p>
<pre><code class="lang-php"><span class="hljs-string">'providers'</span> =&gt; [
    ...
    Arcanedev\LogViewer\LogViewerServiceProvider::class,
],
</code></pre>
<h2 id="heading-configuracao">Configuração</h2>
<p>Antes de começar, devemos fazer uma pequena alteração em nosso arquivo <code>.env</code>.</p>
<p>O LogViewer suporta apenas o canal de log diário. Então se sua versão é a 5.6 vamos alterar a variável <code>LOG_CHANNEL</code> que deve estar como <code>slack</code>, alterando seu valor para <code>daily</code>.</p>
<pre><code class="lang-bash">LOG_CHANNEL=daily
</code></pre>
<p>Para instalação do Laravel versão 5.5 ou inferior adicione a linha abaixo no seu arquivo .env.</p>
<pre><code class="lang-bash">APP_LOG=daily
</code></pre>
<h2 id="heading-comandos-artisan">Comandos Artisan</h2>
<p>O pacote disponibiliza alguns comandos que facilitam o gerenciamento da aplicação. São eles:</p>
<ul>
<li>Para publicar os arquivos de configuração e traduções</li>
</ul>
<pre><code class="lang-bash">php artisan log-viewer:publish
</code></pre>
<ul>
<li>Para forçar a publicação</li>
</ul>
<pre><code class="lang-bash">php artisan log-viewer:publish --force
</code></pre>
<ul>
<li>Publicar somente o arquivo de configuração</li>
</ul>
<pre><code class="lang-bash">php artisan log-viewer:publish --tag=config
</code></pre>
<ul>
<li>Publicar somente o arquivo de tradução</li>
</ul>
<pre><code class="lang-bash">php artisan log-viewer:publish --tag=lang
</code></pre>
<ul>
<li>Requisitos da aplicação e a verificação do arquivo de log</li>
</ul>
<pre><code class="lang-bash">php artisan log-viewer:check
</code></pre>
<p>É isso ai. A partir de agora sua aplicação estará rodando no seguinte endereço: <code>http://example.com/log-viewer</code>.</p>
<p>Veja algumas imagens do LogViewer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044495119/cda203fa-9943-4acf-8c92-6ce97221feb3.jpeg" alt="Dashboard Logviewer" /></p>
<p>Dashboard Logviewer</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044521212/8e140c4b-6341-499d-baaa-fcf1d56f2a2d.jpeg" alt="Listagem de logs" /></p>
<p>Listagem de logs</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735044536775/93b84929-98b9-4e1b-a26d-62929fa6e7ab.jpeg" alt="Detalhes do log" /></p>
<p>Detalhes do log</p>
<h2 id="heading-dicas-adicionais">Dicas adicionais</h2>
<p>Bom até aqui esta rodando tudo perfeitamente correto? Mas essa url é manjada, qualquer pessoa com conhecimento no framework pode acessar minha url não é? Então bora lá para resolver isso.</p>
<p>Vamos executar um comando que citei acima, irá publicar os arquivos de configurações e traduções do LogViewer.</p>
<pre><code class="lang-bash">php artisan log-viewer:publish
</code></pre>
<p>Agora temos o arquivo de configuração no diretório <code>config/log-viewer.php</code>. Nesse arquivo podemos destacar as seguintes opções:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">/* -----------------------------------------------------------------
    |  Diretório do arquivo de Log do Laravel
    | -----------------------------------------------------------------
    */</span>

   <span class="hljs-string">'storage-path'</span>  =&gt; storage_path(<span class="hljs-string">'logs'</span>),

<span class="hljs-comment">/* -----------------------------------------------------------------
    |  Configuração da Rota
    | -----------------------------------------------------------------
    */</span>

   <span class="hljs-string">'route'</span>         =&gt; [
       <span class="hljs-string">'enabled'</span>    =&gt; <span class="hljs-literal">true</span>,

       <span class="hljs-string">'attributes'</span> =&gt; [
           <span class="hljs-string">'prefix'</span>     =&gt; <span class="hljs-string">'log-viewer'</span>,

           <span class="hljs-string">'middleware'</span> =&gt; env(<span class="hljs-string">'ARCANEDEV_LOGVIEWER_MIDDLEWARE'</span>) ? explode(<span class="hljs-string">','</span>, env(<span class="hljs-string">'ARCANEDEV_LOGVIEWER_MIDDLEWARE'</span>)) : <span class="hljs-literal">null</span>,
       ],
   ],
</code></pre>
<p>No array route temos o attributes e a chave prefix que é a URL atual para acessarmos o LogViewer. Podemos alterar para <code>log-laravel</code> por exemplo. A URL ficaria <code>http://example.com/log-laravel</code>. Bem fácil né?</p>
<p>Ainda assim falta alguma coisa... Dessa maneira qualquer pessoa pode acessar essa página, até mesmo aquele usuário que não esteja autenticado no site. Para alteramos isso reparem que logo abaixo do prefix temos middleware. Ou seja, você pode definir um middleware de autenticação e somente pessoas autorizadas terão acesso a esse link.</p>
<p>Para isso abra seu arquivo <code>.env</code> e adicione a seguinte linha:</p>
<pre><code class="lang-bash">ARCANEDEV_LOGVIEWER_MIDDLEWARE=web,auth,custom
</code></pre>
<p>Dessa maneira definimos que os middlewares web, auth e custom terão acesso ao LogViewer. Altere conforme sua aplicação.</p>
<p>É isso aí pessoal. Espero que tenha gostado dessa dica e desse pacote que é uma mão na roda para decifrarmos melhor o log do Laravel.</p>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Instalando Laravel no painel ISPConfig 3]]></title><description><![CDATA[Fala pessoal tudo beleza?
Conheci o painel ISPConfig 3 buscando uma solução para meu droplet na Digital Ocean, onde eu tinha meu site e gerenciava ele apenas via SSH. Fazer tudo via SSH muitas das vezes não é produtivo (mas é muito insano), então um ...]]></description><link>https://aristides.dev/instalando-laravel-no-painel-ispconfig-3</link><guid isPermaLink="true">https://aristides.dev/instalando-laravel-no-painel-ispconfig-3</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sat, 09 Jun 2018 15:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043743968/c503c370-8bd8-414f-8ba1-1727ce69830e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Conheci o painel ISPConfig 3 buscando uma solução para meu droplet na Digital Ocean, onde eu tinha meu site e gerenciava ele apenas via SSH. Fazer tudo via SSH muitas das vezes não é produtivo (mas é muito insano), então um painel é uma ótima opção.</p>
<p>Encontrei um site ótimo onde aprendi mais sobre o ISP. Hoje gerencio meu site (este mesmo que você está acessando) através desse painel que otimiza e muito nosso trabalho.</p>
<p>ISPConfig é um painel para gerenciar sites, banco de dados, e-mails, relatórios e muito mais. Para mais informações sobre o painel visite o site <a target="_blank" href="https://www.ispconfig.org/?ref=aristides.dev">https://www.ispconfig.org/</a>.</p>
<p>Só que hoje não será dele que irei falar, mas de como instalar e configurar as diretivas do servidor web para que o Laravel funcione corretamente em seu ISPConfig.</p>
<p>No final do artigo deixarei um link muito bom para quem quiser aprender sobre o ISPConfig.</p>
<h2 id="heading-criando-um-site">Criando um site</h2>
<p>Para criar um site no ISPConfig vá em Site -&gt; Adicionar novo site. Preencha os campos necessários na aba Domínio e clique em Salvar.</p>
<p>Nota: Para que o site responda pelo seu domínio não esqueça de apontar na sua tabela de DNS o IP do servidor ISPConfig.</p>
<p>Após criar o site entre nele novamente e vá na aba Opções. No final da página você verá a caixa de Diretivas do Nginx. Será aí que iremos adicionar nossas diretivas para que o Laravel funcione.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043860947/86baf15a-a63b-47d8-b8dd-064243881b26.png" alt /></p>
<p>Caixa de diretivas Nginx</p>
<h2 id="heading-diretivas-do-nginx">Diretivas do Nginx</h2>
<p>Para que o Laravel funcione corretamente, a configuração abaixo é essencial. Copie e cole na caixa de diretivas.</p>
<pre><code class="lang-nginx"><span class="hljs-attribute">location</span> = /robots.txt {
   <span class="hljs-section">root</span> {DOCROOT}public;
   <span class="hljs-attribute">access_log</span> <span class="hljs-literal">off</span>;
   <span class="hljs-attribute">log_not_found</span> <span class="hljs-literal">off</span>;
   <span class="hljs-attribute">allow</span> all;          
}

<span class="hljs-attribute">location</span> / {
   <span class="hljs-section">root</span> {DOCROOT}public;
   <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php?q=<span class="hljs-variable">$uri</span>&amp;<span class="hljs-variable">$args</span>;
}

<span class="hljs-attribute">location</span> <span class="hljs-variable">@php</span> {
   <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> =<span class="hljs-number">404</span>;
   <span class="hljs-section">root</span> {DOCROOT}public;
   <span class="hljs-attribute">include</span> /etc/nginx/fastcgi_params;
   <span class="hljs-attribute">fastcgi_pass</span> unix:/var/lib/php7.1-fpm/web1.sock;
   <span class="hljs-attribute">fastcgi_index</span> index.php;
   <span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$document_root</span><span class="hljs-variable">$fastcgi_script_name</span>;
   <span class="hljs-attribute">fastcgi_intercept_errors</span> <span class="hljs-literal">off</span>;
}
</code></pre>
<p>Nota: a diretiva acima está definida para o usuário <strong>web1</strong> conforme a linha <code>fastcgi_pass unix:/var/lib/php7.1-fpm/web1.sock</code>. Altere essa diretiva para o usuário que você estará usando em seu Painel.</p>
<p>Se vocês notarem nas 3 diretivas acima todos possuem a linha:</p>
<pre><code class="lang-nginx"><span class="hljs-section">root</span> {DOCROOT}public
</code></pre>
<p>Essa variável docroot é o próprio painel que facilita nossa vida. Então ficaria algo como:</p>
<pre><code class="lang-nginx"><span class="hljs-attribute">root</span> /var/www/example.com/web/public
</code></pre>
<p>Nessa linha indicamos ao Nginx que ele irá buscar os arquivos nesse diretório, ou seja, quando usamos um Framework que é o caso do Laravel, o diretório onde ficam os arquivos é a pasta public e não a raiz do site. Portanto devemos indicar isso ao nosso servidor web.</p>
<p>Note que até o arquivo robots.txt é necessário essa diretiva, pois caso contrário o servidor retornará um erro 404 caso tentarmos acessar o endereço <code>http://example.com/robots.txt</code>.</p>
<h2 id="heading-instalando-o-laravel">Instalando o Laravel</h2>
<p>Primeiro vamos criar um usuário shell pelo painel ISP. Vá em <strong>Sites</strong> -&gt; <strong>Usuários shell</strong> e clique em adicionar novo usuário. Preencha todas as informações necessárias.</p>
<p>Na opção Shell enjaulado (chroot) sempre escolha Jailkit. Isso deixará o usuário shell sempre em Jail (enjaulado), evitando que caso alguém consiga quebrar sua senha e ter acesso ao servidor fique enjaulado dentro de um espaço do site e não tenha acesso ao resto do servidor.</p>
<p>Acesse seu servidor via SSH com o usuário que foi criado agora (em Jail) e vamos fazer uma instalação limpa do Laravel utilizando o composer no diretório /web do usuário. Será necessário remover todo o conteúdo do diretório /web para que o composer consiga baixar a aplicação. Execute os comandos:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /web
rm -rf *
composer create-project --prefer-dist laravel/laravel .
</code></pre>
<p>O ponto no final quer dizer que a instalação será feita no diretório atual, ou seja, no <code>/web</code> do usuário. Após o composer finalizar toda a instalação acesse o endereço da sua aplicação e verá a tela inicial do Laravel.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043919948/8e05eab0-a7e7-401e-8f24-6a1664689c4b.png" alt="Tela inicial do Laravel" /></p>
<p>Agora basta criar um banco de dados, usuário para o banco e configurar seu arquivo .env para conectá-lo ao seu banco e começar a brincar.</p>
<p>Bom, vou deixar aqui dois links de sites que eu acompanho bastante e que me ajuda muito com o painel ISPConfig.</p>
<ul>
<li><p>HowtoForge: <a target="_blank" href="https://www.howtoforge.com/?ref=aristides.dev">https://www.howtoforge.com</a></p>
</li>
<li><p>Fator Binário: <a target="_blank" href="https://fatorbinario.com/?ref=aristides.dev">https://fatorbinario.com</a></p>
</li>
</ul>
<p>Até a próxima.</p>
]]></content:encoded></item><item><title><![CDATA[Configurando um servidor LEMP Debian 9 - Parte 2]]></title><description><![CDATA[Fala pessoal tudo beleza?
Esse é o segundo artigo da série Configurando um servidor web com Debian 9. Você pode ver o primeiro artigo clicando aqui.
Nesse artigo irei instalar e configurar o Nginx, MariaDB e o PHP 7.2. E vamos também fazer algumas co...]]></description><link>https://aristides.dev/configurando-um-servidor-lemp-debian-9-parte-2</link><guid isPermaLink="true">https://aristides.dev/configurando-um-servidor-lemp-debian-9-parte-2</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Sat, 17 Feb 2018 14:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043620325/fe7d2920-81ee-4879-a778-501c2e5ba52a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Esse é o segundo artigo da série Configurando um servidor web com Debian 9. Você pode ver o primeiro artigo <a target="_blank" href="https://aristides.dev/configurando-um-servidor-lemp-debian-9-parte-1">clicando aqui</a>.</p>
<p>Nesse artigo irei instalar e configurar o Nginx, MariaDB e o PHP 7.2. E vamos também fazer algumas configurações para deixar nosso servidor web bem bacana.</p>
<h2 id="heading-atualizando-os-pacotes">Atualizando os pacotes</h2>
<p>Primeiro vamos atualizar a lista de pacotes do sistema com o comando abaixo:</p>
<pre><code class="lang-bash">sudo apt update &amp;&amp; sudo apt -y upgrade &amp;&amp; sudo apt -y dist-upgrade
</code></pre>
<h2 id="heading-instalar-nginx">Instalar Nginx</h2>
<p>O Nginx é um servidor web, proxy reverso e proxy balanceador de carga e pode ser usado também junto com Apache. Ele tem uma performance muito boa comparando com um servidor web rodando o Apache. O interessante que os dois podem ser configurados juntos e fazem um belo serviço.</p>
<p>Execute o comando abaixo para instalar o Nginx:</p>
<pre><code class="lang-bash">sudo apt install nginx
</code></pre>
<p>Se tudo ocorrer bem, seu servidor web já estará rodando. Verifique no link <code>http://localhost</code> e confira a página de boas vindas.</p>
<h2 id="heading-instalar-mariadb">Instalar Mariadb</h2>
<p>MariaDB é baseado no MySQL, uma banco de dados muito robusto e muito utilizado hoje em dia para projetos pequenos e grandes portes. É também o mais utilizado para quem esta começando com a programação.</p>
<p>Execute o comando abaixo para instalar o MariaDB:</p>
<pre><code class="lang-bash">sudo apt install mariadb-server
</code></pre>
<p>Após a instalação é altamente recomendado executar um script para configurar algumas informações.</p>
<p>Execute o comando:</p>
<pre><code class="lang-bash">sudo mysql_secure_installation
</code></pre>
<ul>
<li><p>A primeira pergunta é se você já tem uma senha para o usuário root. Caso sim entre com ela e caso contrário apenas tecle enter sem preencher nada.</p>
</li>
<li><p>Segunda pergunta é se deseja alterar a senha de root.</p>
</li>
<li><p>Terceira, é se você deseja remover os usuários anônimos do mysql: <strong>Y</strong></p>
</li>
<li><p>Quarta, desabilitar o login remoto do root? <strong>Y (Sempre)</strong></p>
</li>
<li><p>Quinta, remover a base de dados teste e o acesso a ela? <strong>Y</strong></p>
</li>
<li><p>Sexta, recarregar os privilégios? <strong>Y</strong></p>
</li>
</ul>
<pre><code class="lang-bash">All <span class="hljs-keyword">done</span>! If you<span class="hljs-string">'ve completed all of the above steps, your MariaDB
installation should now be secure.
Thanks for using MariaDB!</span>
</code></pre>
<p>Conforme a mensagem acima, as alterações foram feitas com sucesso. Agora você esta com o banco configurado e pronto para usar.</p>
<h2 id="heading-instalar-o-php-72">Instalar o PHP 7.2</h2>
<p>Para instalarmos o PHP7.2 no Debian precisaremos baixar de outro repositório, pois essa versão não é nativa do Debian 8 (Jessie) e 9 (Stretch).</p>
<p>Para instalar execute os seguintes comandos:</p>
<pre><code class="lang-bash">sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
<span class="hljs-built_in">echo</span> <span class="hljs-string">"deb https://packages.sury.org/php/ <span class="hljs-subst">$(lsb_release -sc)</span> main"</span> | sudo tee /etc/apt/sources.list.d/php.list
sudo apt-get update &amp;&amp; sudo apt-get install php7.2-cli php7.2-fpm php7.2-mysql php7.2-curl php-memcached  php7.2-dev php7.2-sqlite3 php7.2-mbstring php7.2-gd php7.2-json php7.2-xmlrpc php7.2-xml php7.2-zip
</code></pre>
<p>Caso o comando acima apresenta algum erro ao atualizar, verifique no arquivo <code>/etc/apt/sources.list.d/php.list</code> se o nome da distribuição foi inserida corretamente conforme a linha abaixo.</p>
<ul>
<li>Para Debian 8 Jessie</li>
</ul>
<pre><code class="lang-bash">deb https://packages.sury.org/php/ jessie main
</code></pre>
<ul>
<li>Para Debian 9 Stretch</li>
</ul>
<pre><code class="lang-bash">deb https://packages.sury.org/php/ stretch main
</code></pre>
<p>Caso não tenha gravado corretamente, copie a linha correspondente a sua distribuição e altere no arquivo. E tente atualizar a lista de pacotes novamente.</p>
<h2 id="heading-dica">Dica</h2>
<p>Vamos alterar uma configuração no PHP para deixarmos ele mais seguro. Navegue até o diretório <code>/etc/php/7.2/fpm/</code> e abra o arquivo <code>php.ini</code>.</p>
<p>Localize a linha onde existe a diretiva cgi.fix_pathinfo. Deve estar comentado com o sinal # no início da linha. Remova o comentário e caso tiver o valor 1 altere para 0.</p>
<pre><code class="lang-bash">cgi.fix_pathinfo=0
</code></pre>
<p>Reinicie o PHP:</p>
<pre><code class="lang-bash">sudo service php7.2-fpm restart
</code></pre>
<h2 id="heading-configurando-o-nginx-com-o-php-72">Configurando o Nginx com o PHP 7.2</h2>
<p>Temos agora que configurar um site no Nginx utilizando a nova versão do PHP. Entre no diretório /etc/nginx/sites-available e altere o arquivo default para a configuração abaixo:</p>
<pre><code class="lang-nginx"><span class="hljs-section">server</span> {

    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
    <span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">80</span>;       

    <span class="hljs-attribute">server_name</span> example.com.br www.example.com.br;

    <span class="hljs-comment"># Log</span>
    <span class="hljs-attribute">access_log</span> <span class="hljs-literal">off</span>;
    <span class="hljs-attribute">error_log</span> /var/log/nginx/error.log;

    <span class="hljs-comment"># Directory root</span>
    <span class="hljs-attribute">root</span> /var/www/html/example;
    <span class="hljs-attribute">index</span> index.php index.html;

    <span class="hljs-attribute">client_max_body_size</span> <span class="hljs-number">128M</span>;

    <span class="hljs-attribute">location</span> / {
        <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php?<span class="hljs-variable">$query_string</span>;
    }

    <span class="hljs-comment"># pass PHP scripts to FastCGI server</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
        <span class="hljs-comment"># Note que aqui definimos a versao que iremos utilizar do PHP</span>
        <span class="hljs-attribute">fastcgi_pass</span> unix:/var/run/php/php7.2-fpm.sock;
        <span class="hljs-attribute">fastcgi_index</span> index.php;
        <span class="hljs-attribute">fastcgi_split_path_info</span><span class="hljs-regexp"> ^(.+\.php)(.*)$</span>;
        <span class="hljs-attribute">include</span> /etc/nginx/fastcgi_params;
        <span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$realpath_root</span><span class="hljs-variable">$fastcgi_script_name</span>;
        <span class="hljs-attribute">fastcgi_param</span> DOCUMENT_ROOT <span class="hljs-variable">$realpath_root</span>;
    }

    <span class="hljs-comment"># deny access to .htaccess files</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.ht</span> {
        <span class="hljs-attribute">deny</span> all;
    }

    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.well-known</span> {
        <span class="hljs-attribute">allow</span> all;
    }

    <span class="hljs-comment"># Set header expirations on per-project basis</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$</span> {
        <span class="hljs-attribute">expires</span> <span class="hljs-number">365d</span>;
    }
}
</code></pre>
<p>Salve o arquivo e execute o comando abaixo para verificar se as alterações estão corretas.</p>
<pre><code class="lang-bash">nginx -t
</code></pre>
<p>E reinicie o serviço do Nginx:</p>
<pre><code class="lang-bash">sudo service nginx restart
</code></pre>
<p>Feito! Seu servidor já estará rodando com sucesso. Caso tenha algum problema ou alguma dúvida não deixe de perguntar.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Configurando Virtual Host no Nginx]]></title><description><![CDATA[Fala pessoal tudo beleza?
Hoje vou mostrar como é fácil instalar e configurar o Nginx que é um servidor web, proxy reverso, proxy balanceador de carga e tem um desempenho muito bom.
Existem muitas configurações que podemos fazer para melhorar nosso s...]]></description><link>https://aristides.dev/configurando-virtual-host-no-nginx</link><guid isPermaLink="true">https://aristides.dev/configurando-virtual-host-no-nginx</guid><category><![CDATA[Linux]]></category><category><![CDATA[nginx]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Fri, 15 Dec 2017 14:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043441264/0ccdec02-6b8a-4698-8a4f-6014456d0eb3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Hoje vou mostrar como é fácil instalar e configurar o Nginx que é um servidor web, proxy reverso, proxy balanceador de carga e tem um desempenho muito bom.</p>
<p>Existem muitas configurações que podemos fazer para melhorar nosso servidor em termos de desempenho e segurança, quero mostrar aqui algumas delas. Irei dividir esse artigo em algumas partes para não ficar muito extenso. Aqui irei começar com a instalação e configuração básica.</p>
<blockquote>
<p>Após a leitura desse artigo, acesse meu outro artigo <a target="_blank" href="https://aristides.dev/instalando-lets-encrypt-no-ubuntu-e-debian-com-nginx">Instalando Let's Encrypt no Ubuntu e Debian com Nginx</a> e complemente a configuração do seu servidor web com certificado SSL gratuito</p>
</blockquote>
<h2 id="heading-instalacao">Instalação</h2>
<p>Estou utilizando a distribuição GNU/Linux Debian 9 para esse exemplo. Nada impede que use uma diferente, apenas adapte o comando para o seu sistema.</p>
<pre><code class="lang-bash">apt update &amp;&amp; apt install -y nginx
</code></pre>
<p>Após finalizar a instalação você já pode conferir se seu servidor local esta rodando corretamente. Entre com o endereço <code>http://localhost</code> e veja se a página de boas vindas do Nginx apareceu.</p>
<h2 id="heading-comandos-basicos">Comandos básicos</h2>
<p>Para saber sobre o status, para reiniciar o serviço e checar se as configurações alteradas foram bem sucedidas utilizamos os comandos abaixo:</p>
<p>Verificar o status do serviço Nginx.</p>
<pre><code class="lang-bash">service nginx status
</code></pre>
<p>Iniciar, parar ou reiniciar o serviço.</p>
<pre><code class="lang-bash">service nginx start / stop / restart
</code></pre>
<p>Checar se alterações feitas estão corretas.</p>
<pre><code class="lang-bash">nginx -t
</code></pre>
<h2 id="heading-configurando-nginx">Configurando Nginx</h2>
<p>Os arquivos de configuração ficam em <code>/etc/nginx</code> por padrão. No diretório <code>sites-available</code> ficam os arquivos de configuração dos sites e no diretório <code>sites-enabled</code> ficam os sites que estão ativos no servidor, que na verdade é um link simbólico para o diretório <code>sites-available</code>.</p>
<p>No diretório sites-available temos um arquivo chamado default que é o arquivo padrão do Nginx. Essa é a configuração usada quando acessamos a página de boas vindas após a instalação.</p>
<p>Vamos criar uma configuração nova, para isso entre no diretório <code>/etc/nginx/sites-available</code> e crie um arquivo vazio para o nosso site:</p>
<pre><code class="lang-bash">touch example.com
</code></pre>
<p>Abra o arquivo com seu editor e cole todo o conteúdo abaixo nele.</p>
<pre><code class="lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-comment"># Porta WEB</span>
    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span> default_server;
    <span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">80</span> default_server;

    <span class="hljs-comment"># Nome do servidor</span>
    <span class="hljs-attribute">server_name</span> example.com www.example.com;     

    <span class="hljs-comment"># Diretorio de Log</span>
    <span class="hljs-attribute">access_log</span> /var/log/nginx/access.log;
    <span class="hljs-attribute">error_log</span> /var/log/nginx/error.log;
    <span class="hljs-attribute">rewrite_log</span> <span class="hljs-literal">on</span>;

    <span class="hljs-comment"># Diretorio dos arquivos web</span>
    <span class="hljs-attribute">root</span> /usr/share/nginx/html/example.com;

    <span class="hljs-comment"># Extensões de arquivos que serão lidos</span>
    <span class="hljs-attribute">index</span> index.php index.html;

    <span class="hljs-attribute">client_max_body_size</span> <span class="hljs-number">128M</span>;

    <span class="hljs-comment"># URL amigáveis</span>
    <span class="hljs-attribute">location</span> / {
        <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php?<span class="hljs-variable">$query_string</span>;
    }

    <span class="hljs-comment"># Configurações PHP FPM.</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~* \.php$</span> {
        <span class="hljs-attribute">fastcgi_pass</span> unix:/run/php/php7.1-fpm.sock;
        <span class="hljs-attribute">fastcgi_index</span> index.php;
        <span class="hljs-attribute">fastcgi_split_path_info</span><span class="hljs-regexp"> ^(.+\.php)(.*)$</span>;
        <span class="hljs-attribute">include</span> /etc/nginx/fastcgi_params;
        <span class="hljs-attribute">fastcgi_param</span> SCRIPT_FILENAME <span class="hljs-variable">$realpath_root</span><span class="hljs-variable">$fastcgi_script_name</span>;
        <span class="hljs-attribute">fastcgi_param</span> DOCUMENT_ROOT <span class="hljs-variable">$realpath_root</span>;
    }

    <span class="hljs-comment"># Bloqueia arquivo com .ht (Nginx não utiliza o .htaccess como o Apache)</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~ /\.ht</span> {
        <span class="hljs-attribute">deny</span> all;
    }

    <span class="hljs-comment"># Configura cache das extensões abaixo para expirar em 365 dias</span>
    <span class="hljs-attribute">location</span> <span class="hljs-regexp">~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$</span> {
        <span class="hljs-attribute">expires</span> <span class="hljs-number">365d</span>;
    }
}
</code></pre>
<p>No arquivo acima definimos algumas configurações essenciais para o funcionamento do um site. Vou ressaltar as principais.</p>
<ol>
<li><p>Na linha <code>listen</code> devemos colocar a porta que o Nginx escutará que é a porta padrão 80. A opção <code>default_server</code> deverá ser usada em somente uma configuração, pois essa será a principal do servidor.</p>
</li>
<li><p>Na linha <code>server_name</code> deverá ser inserido o nome do seu domínio.</p>
</li>
<li><p>Na directiva <code>root</code> deverá ser o caminho da sua aplicação, dos seus arquivos em PHP por exemplo.</p>
</li>
<li><p>No bloco de configurações do PHP é usado o PHP 7.1, caso esteja usando outra versão do PHP deve ser alterado a opção <code>fastcgi_pass unix:/run/php/php7.1-fpm.sock;</code> alterando o php7.1 para a versão instalada no seu servidor.</p>
</li>
</ol>
<h2 id="heading-testando-nossa-configuracao">Testando nossa configuração</h2>
<p>Precisamos testar nossa configuração após essas alterações, para isso execute o comando abaixo:</p>
<pre><code class="lang-bash">nginx -t

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf <span class="hljs-built_in">test</span> is successful
</code></pre>
<p>Se o retorno for igual a resposta acima quer dizer que deu tudo certo. Agora devemos ativar esse site para termos acesso a ele no navegador e reiniciar o serviço Nginx. Entre no diretório <code>/etc/nginx/sites-enabled</code> e digite o comando:</p>
<pre><code class="lang-bash">ln -s /etc/nginx/sites-available/example.com
service nginx restart
</code></pre>
<p>Pronto, agora seu site deve estar no ar.</p>
<p>Nesse artigo falei sobre uma configuração mais básica do Nginx. Irei fazer uma segunda parte onde irei dar algumas dicas para melhorar o desempenho ativando o cache do Nginx e como configurar para utilizar um certificado SSL grátis.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Criar sitemap automaticamente em PHP]]></title><description><![CDATA[Fala pessoal tudo beleza?
Hoje quero compartilhar um código bem bacana para gerar o sitemap do seu blog automaticamente sem que você precise se esforçar para isso. Parece bom né?!
Se você tem um blog que semanalmente você posta artigos novos e depois...]]></description><link>https://aristides.dev/criar-sitemap-automaticamente-em-php</link><guid isPermaLink="true">https://aristides.dev/criar-sitemap-automaticamente-em-php</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Thu, 30 Nov 2017 14:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043327883/c1258bd8-f9b1-4e73-997c-67f65ca31846.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Hoje quero compartilhar um código bem bacana para gerar o sitemap do seu blog automaticamente sem que você precise se esforçar para isso. Parece bom né?!</p>
<p>Se você tem um blog que semanalmente você posta artigos novos e depois precisa manualmente ir no arquivo sitemap e atualizar na mão e até mesmo ir no Google e enviar seu sitemap seu problema irá acabar.</p>
<p>Esse código busca todos os seus posts no banco de dados e cria o sitemap atualizado e sem esforço nenhum envia para o Google! Bom vamos ver como funciona.</p>
<h2 id="heading-conexao-com-o-banco">Conexão com o banco</h2>
<p>Primeiro vamos conectar com o nosso banco.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Dados de conexao com o banco</span>
define(<span class="hljs-string">'MYSQL_HOST'</span>, <span class="hljs-string">'localhost'</span>);
define(<span class="hljs-string">'MYSQL_USER'</span>, <span class="hljs-string">'db_user'</span>);
define(<span class="hljs-string">'MYSQL_PASSWORD'</span>, <span class="hljs-string">'db_password'</span>);
define(<span class="hljs-string">'MYSQL_DB_NAME'</span>, <span class="hljs-string">'db_name'</span>);
define(<span class="hljs-string">'HOME'</span>, <span class="hljs-string">'http://example.com'</span>);
</code></pre>
<p>Vamos realizar a conexão dentro de um <code>try catch</code>, podendo assim tratar um possível erro de conexão.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">try</span>
{
    <span class="hljs-comment">// Realiza a conexao com o banco - PDO</span>
    $PDO = <span class="hljs-keyword">new</span> PDO(<span class="hljs-string">'mysql:host='</span> . MYSQL_HOST . <span class="hljs-string">';dbname='</span> . MYSQL_DB_NAME, MYSQL_USER, MYSQL_PASSWORD);
    $PDO-&gt;exec(<span class="hljs-string">"set names utf8"</span>);
}
<span class="hljs-keyword">catch</span> (PDOException $e)
{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">'Erro ao conectar com o banco: '</span> . $e-&gt;getMessage();
}
</code></pre>
<p>Conexão realizada. Vamos agora buscar os posts no banco de dados.</p>
<h2 id="heading-realizando-a-busca">Realizando a busca</h2>
<p>Agora vamos buscar no banco de dados todos os posts ativos e definir em uma variável a data e hora atual do sistema em formato <code>ISO8601 (2017-11-22T00:06:23-02:00)</code>.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Realiza a consulta no banco de todas as postagens ativas</span>
$sql = <span class="hljs-string">"SELECT * FROM posts WHERE status = 'PUBLISHED'"</span>;
$result = $PDO-&gt;query($sql);
$rows = $result-&gt;fetchAll(PDO::FETCH_ASSOC);

<span class="hljs-comment">// Data e hora atual</span>
$datetime = <span class="hljs-keyword">new</span> DateTime(date(<span class="hljs-string">'Y-m-d H:i:s'</span>));
<span class="hljs-comment">// A linha abaixo me retornará uma data no seguinte formato: 2017-11-22T00:06:23-02:00</span>
$date = $datetime-&gt;format(DateTime::ATOM); <span class="hljs-comment">// ISO8601</span>
</code></pre>
<h2 id="heading-gerando-o-xml">Gerando o XML</h2>
<p>Agora aqui que é a sacada. Iremos criar o XML e nos posts iremos colocar um foreach para que ele percorra todo nosso array e gere uma lista com os posts atualizados.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Gera o arquivo XML do sitemap</span>
$xml = <span class="hljs-string">'&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;urlset
    xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
    http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"&gt;
    &lt;url&gt;
        &lt;loc&gt;'</span>.HOME.<span class="hljs-string">'&lt;/loc&gt;
        &lt;lastmod&gt;'</span>.$date.<span class="hljs-string">'&lt;/lastmod&gt;
        &lt;changefreq&gt;weekly&lt;/changefreq&gt;
        &lt;priority&gt;1.00&lt;/priority&gt;
    &lt;/url&gt;'</span>;
    <span class="hljs-keyword">foreach</span>($rows <span class="hljs-keyword">as</span> $v){
        $datetime = <span class="hljs-keyword">new</span> DateTime($v[<span class="hljs-string">'updated_at'</span>]);
        $date = $datetime-&gt;format(DateTime::ATOM);
        $xml .=<span class="hljs-string">'
        &lt;url&gt;
            &lt;loc&gt;'</span>.HOME.<span class="hljs-string">'/'</span>.$v[<span class="hljs-string">'slug'</span>].<span class="hljs-string">'&lt;/loc&gt;
            &lt;lastmod&gt;'</span>.$date.<span class="hljs-string">'&lt;/lastmod&gt;
            &lt;changefreq&gt;weekly&lt;/changefreq&gt;
            &lt;priority&gt;0.85&lt;/priority&gt;
        &lt;/url&gt;'</span>;
    }
$xml .= <span class="hljs-string">'
&lt;/urlset&gt;'</span>;
</code></pre>
<p>Agora vamos criar o arquivo sitemap propriamente dito.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Abre o arquivo ou tenta cria-lo se ele não exixtir</span>
$arquivo = fopen(<span class="hljs-string">'sitemap.xml'</span>, <span class="hljs-string">'w'</span>);
<span class="hljs-keyword">if</span> (fwrite($arquivo, $xml)) {
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Arquivo sitemap.xml criado com sucesso"</span>;
} <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Não foi possível criar o arquivo. Verifique as permissões do diretório."</span>;
}
fclose($arquivo);
</code></pre>
<p>Usando a função <a target="_blank" href="https://php.net/manual/pt_BR/function.fopen.php">fopen</a> do PHP iremos abrir o arquivo e caso ele não exista o PHP irá criar um. Depois escreveremos o conteúdo da variável <code>$xml</code> no arquivo que acabamos de criar.</p>
<h2 id="heading-compactando-o-arquivo-sitemap">Compactando o arquivo sitemap</h2>
<p>Após criarmos vamos compactar o arquito <code>sitemap.xml</code> para <code>sitemap.xml.gz</code>. Para isso usaremos a função do PHP <a target="_blank" href="https://php.net/manual/pt_BR/function.gzencode.php">gzencode</a>.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Compactar arquivo sitemap para GZIP</span>
$data = implode(<span class="hljs-string">""</span>, file(<span class="hljs-string">"sitemap.xml"</span>));
$gzdata = gzencode($data, <span class="hljs-number">9</span>);
$fp = fopen(<span class="hljs-string">"sitemap.xml.gz"</span>, <span class="hljs-string">"w"</span>);
fwrite($fp, $gzdata);
fclose($fp);
</code></pre>
<p>Maravilha! Até agora vimos como criar o arquivo sitemap automaticamente buscando seus posts e gerando o XML e compactando no formato .gz. Esses dois arquivos são os que enviamos para o Google para que ele reconheça a estrutura do nosso site e melhore nosso SEO.</p>
<h2 id="heading-enviando-sitemap-para-o-google">Enviando sitemap para o Google</h2>
<p>Que tal fazer o envio automaticamente para o Google? Bora lá!</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Envia para o Google o novo sitemap gerado</span>
$urlSitemap = <span class="hljs-string">"http://www.google.com/webmasters/sitemaps/ping?sitemap="</span> . HOME . <span class="hljs-string">"/"</span>;
<span class="hljs-comment">// Arquivos a serem enviados</span>
$Files = [<span class="hljs-string">'sitemap.xml'</span>, <span class="hljs-string">'sitemap.xml.gz'</span>];

<span class="hljs-comment">// Envia os dois arquivos sitemap gerados para a URL do Google</span>
<span class="hljs-keyword">foreach</span> ($Files <span class="hljs-keyword">as</span> $file) {
    $url = $urlSitemap . $file;
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, <span class="hljs-number">0</span>);
    curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
}
</code></pre>
<p>Nesse script definimos a URL do Google Webmaster e os arquivos a serem enviados.</p>
<p>No <code>foreach</code> verificamos os arquivos e enviamos um por vez para a URL utilizando a biblioteca <code>cURL</code>.</p>
<p>Para enviar ao Google você pode configurar o cron do Linux para executar semanalmente por exemplo, ou se adaptar às suas necessidades.</p>
<p>Bom galera espero que seja útil para vocês como é para mim. Caso queiram contribuir com melhorias e ou correções podem deixar um comentário abaixo.</p>
<p>Veja abaixo o código completo:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// Dados de conexao com o banco</span>
define(<span class="hljs-string">'MYSQL_HOST'</span>, <span class="hljs-string">'localhost'</span>);
define(<span class="hljs-string">'MYSQL_USER'</span>, <span class="hljs-string">'user_db'</span>);
define(<span class="hljs-string">'MYSQL_PASSWORD'</span>, <span class="hljs-string">'pass_db'</span>);
define(<span class="hljs-string">'MYSQL_DB_NAME'</span>, <span class="hljs-string">'name_db'</span>);
define(<span class="hljs-string">'HOME'</span>, <span class="hljs-string">'http://example.com'</span>);

<span class="hljs-keyword">try</span>
{
    <span class="hljs-comment">// Realiza a conexao com o banco - PDO</span>
    $PDO = <span class="hljs-keyword">new</span> PDO(<span class="hljs-string">'mysql:host='</span> . MYSQL_HOST . <span class="hljs-string">';dbname='</span> . MYSQL_DB_NAME, MYSQL_USER, MYSQL_PASSWORD);
    $PDO-&gt;exec(<span class="hljs-string">"set names utf8"</span>);

    <span class="hljs-comment">// Realiza a consulta no banco de todas as postagens ativas</span>
    $sql = <span class="hljs-string">"SELECT * FROM posts WHERE status = 'PUBLISHED'"</span>;
    $result = $PDO-&gt;query($sql);
    $rows = $result-&gt;fetchAll(PDO::FETCH_ASSOC);

    <span class="hljs-comment">// Data e hora atual</span>
    $datetime = <span class="hljs-keyword">new</span> DateTime(date(<span class="hljs-string">'Y-m-d H:i:s'</span>));
    <span class="hljs-comment">// A linha abaixo me retornará uma data no seguinte formato: 2017-11-22T00:06:23-02:00</span>
    $date = $datetime-&gt;format(DateTime::ATOM); <span class="hljs-comment">// ISO8601   </span>

    <span class="hljs-comment">// Gera o arquivo XML do sitemap</span>
    $xml = <span class="hljs-string">'&lt;?xml version="1.0" encoding="UTF-8"?&gt;
    &lt;urlset
        xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"&gt;

        &lt;url&gt;
            &lt;loc&gt;'</span>.HOME.<span class="hljs-string">'&lt;/loc&gt;
            &lt;lastmod&gt;'</span>.$date.<span class="hljs-string">'&lt;/lastmod&gt;
            &lt;changefreq&gt;weekly&lt;/changefreq&gt;
            &lt;priority&gt;1.00&lt;/priority&gt;
        &lt;/url&gt;'</span>;

        <span class="hljs-keyword">foreach</span>($rows <span class="hljs-keyword">as</span> $v){
            $datetime = <span class="hljs-keyword">new</span> DateTime($v[<span class="hljs-string">'updated_at'</span>]);
            $date = $datetime-&gt;format(DateTime::ATOM);
            $xml .=<span class="hljs-string">'
                &lt;url&gt;
                    &lt;loc&gt;'</span>.HOME.<span class="hljs-string">'/'</span>.$v[<span class="hljs-string">'slug'</span>].<span class="hljs-string">'&lt;/loc&gt;
                    &lt;lastmod&gt;'</span>.$date.<span class="hljs-string">'&lt;/lastmod&gt;
                    &lt;changefreq&gt;weekly&lt;/changefreq&gt;
                    &lt;priority&gt;0.85&lt;/priority&gt;
                &lt;/url&gt;    '</span>;
        }

    $xml .= <span class="hljs-string">'
    &lt;/urlset&gt;'</span>;

    <span class="hljs-comment">// Abre o arquivo ou tenta cria-lo se ele não exixtir</span>
    $arquivo = fopen(<span class="hljs-string">'sitemap.xml'</span>, <span class="hljs-string">'w'</span>);
    <span class="hljs-keyword">if</span> (fwrite($arquivo, $xml)) {
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Arquivo sitemap.xml criado com sucesso"</span>;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Não foi possível criar o arquivo. Verifique as permissões do diretório."</span>;
    }
    fclose($arquivo);

    <span class="hljs-comment">// Compactar arquivo sitemap para GZIP</span>
    $data = implode(<span class="hljs-string">""</span>, file(<span class="hljs-string">"sitemap.xml"</span>));
    $gzdata = gzencode($data, <span class="hljs-number">9</span>);
    $fp = fopen(<span class="hljs-string">"sitemap.xml.gz"</span>, <span class="hljs-string">"w"</span>);
    fwrite($fp, $gzdata);
    fclose($fp);

    <span class="hljs-comment">// Envia para o Google o novo sitemap gerado</span>
    $urlSitemap = <span class="hljs-string">"http://www.google.com/webmasters/sitemaps/ping?sitemap="</span> . HOME . <span class="hljs-string">"/"</span>;
    <span class="hljs-comment">// Arquivos a serem enviados</span>
    $Files = [<span class="hljs-string">'sitemap.xml'</span>, <span class="hljs-string">'sitemap.xml.gz'</span>];

    <span class="hljs-comment">// Envia os dois arquivos sitemap gerados para a URL do Google</span>
    <span class="hljs-keyword">foreach</span> ($Files <span class="hljs-keyword">as</span> $file) {
        $url = $urlSitemap . $file;
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HEADER, <span class="hljs-number">0</span>);
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
    }

}
<span class="hljs-keyword">catch</span> (PDOException $e)
{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">'Erro ao conectar com o banco: '</span> . $e-&gt;getMessage();
}
</code></pre>
<p>Se preferir, acesso o <a target="_blank" href="https://github.com/aristidesneto/sitemap-generation">repositório no Github</a>.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Configurando um servidor LEMP Debian 9 - Parte 1]]></title><description><![CDATA[Fala pessoal tudo beleza?
Esse é o primeiro artigo de alguns que irei publicar com o objetivo de ajudar a configurar um servidor web do zero. Mostrarei como configurar os principais serviços e instalar um servidor web para rodar aplicações e sites.
E...]]></description><link>https://aristides.dev/configurando-um-servidor-lemp-debian-9-parte-1</link><guid isPermaLink="true">https://aristides.dev/configurando-um-servidor-lemp-debian-9-parte-1</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Aristides Neto]]></dc:creator><pubDate>Fri, 17 Nov 2017 14:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043092019/dbf023ff-0de0-4d8c-bc94-394428a04881.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fala pessoal tudo beleza?</p>
<p>Esse é o primeiro artigo de alguns que irei publicar com o objetivo de ajudar a configurar um servidor web do zero. Mostrarei como configurar os principais serviços e instalar um servidor web para rodar aplicações e sites.</p>
<p>Essa série será sempre com exemplos práticos sem muito entrar em detalhes, irei simplificar o máximo para não ficar maçante. Chega de enrolação e vamos começar...</p>
<p>Nessa série estou utilizando uma instalação limpa do <a target="_blank" href="https://debian.org/">Debian 9 - Stretch</a>.</p>
<h2 id="heading-criando-um-novo-usuario">Criando um novo usuário</h2>
<p>Sabemos que por motivos de segurança não devemos usar o usuário root para tarefas rotineiras. Para isso devemos criar um usuário para nós.</p>
<p>O comando abaixo irá criar um usuário com o nome john. Após confirmar com o Enter, você terá que digitar uma senha para o usuário, insira uma senha forte!</p>
<p>Entre com o seu nome e preencha as informações seguintes caso desejar. Não é obrigatório preencher tudo e você pode apenas confirmar com o Enter para pular as perguntas.</p>
<pre><code class="lang-bash">adduser john
</code></pre>
<p>Agora devemos adicionar seu usuário para podermos executar alguns comandos com super poderes. Para isso execute o comando abaixo:</p>
<pre><code class="lang-bash">usermod -aG sudo john
</code></pre>
<p>Assim você já poderá executar comandos com privilégios de root utilizando o sudo.</p>
<h2 id="heading-aumentando-a-seguranca-com-autenticacao-de-chaves">Aumentando a segurança com autenticação de chaves</h2>
<p>Podemos melhorar a segurança do nosso servidor inserindo uma chave pública para autenticação. Vamos criar um par de chaves e adicionar ao nosso servidor.</p>
<p>Caso ainda não tenha uma chave execute o comando abaixo. Caso já tenha pode pular para a parte de Instalar a chave manualmente.</p>
<p>Execute o comando abaixo no seu computador local, assumindo que você esteja usando Linux como sistema operacional. Caso esteja usando Windows busque por Criar chave ssh windows no Google.</p>
<pre><code class="lang-bash">$ ssh-keygen
</code></pre>
<p>Você poderá cadastrar uma senha caso queira é uma segurança a mais para você. Eu particularmente não utilizo. Com uma senha ou sem, a autenticação com a chave privada é uma maneira mais segura de efetuar o login do que uma autenticação básica com apenas a senha.</p>
<p>O comando acima irá gerar uma chave privada <code>id_rsa</code> e uma chave pública <code>id_rsa.pub</code> no diretório local do usuário na pasta <code>~/.ssh</code>.</p>
<h3 id="heading-instalar-a-chave-manualmente">Instalar a chave manualmente</h3>
<p>Agora vamos instalar essa chave pública que acabamos de criar no servidor. O comando abaixo irá exibir o conteúdo do arquivo id_rsa.pub. Apenas copie o conteúdo do arquivo.</p>
<pre><code class="lang-bash">$ cat ~/.ssh/id_rsa.pub
</code></pre>
<p>Logado no servidor como root dê o comando abaixo para trocar de usuário assumindo o usuário que criamos nesse tutorial.</p>
<pre><code class="lang-bash">su - john
</code></pre>
<p>Crie um diretório chamado .ssh dentro do seu diretório home e restrinja suas permissões com os comandos:</p>
<pre><code class="lang-bash">$ mkdir ~/.ssh
$ chmod 700 ~/.ssh
</code></pre>
<p>Vamos criar agora um arquivo dentro desse diretório chamado <code>authorized_keys</code>. Nesse exemplo vamos utilizar o editor nano.</p>
<pre><code class="lang-bash">$ nano ~/.ssh/authorized_keys
</code></pre>
<p>Cole todo o conteúdo do arquivo que você copiou no passo acima para este que acabamos de criar. Para salvar pressione CTRL+O e CTRL+X para sair do arquivo.</p>
<p>Agora vamos restringir o acesso a esse arquivo para que apenas seu usuário tenha acesso com o comando abaixo:</p>
<pre><code class="lang-bash">$ chmod 600 ~/.ssh/authorized_keys
</code></pre>
<p>Pronto. Chave instalada no servidor e agora poderá acessar seu servidor com mais segurança. Para sair do seu usuário digite exit que ele irá voltar para o usuário root.</p>
<h2 id="heading-configurando-o-ssh">Configurando o SSH</h2>
<p>Podemos realizar algumas alterações no arquivo de configuração do SSH para proteger nosso servidor um pouco mais.</p>
<p>Abra o arquivo abaixo utilizando o usuário root com seu editor de texto:</p>
<pre><code class="lang-bash">nano /etc/ssh/sshd_config
</code></pre>
<p>Agora vamos fazer as alterações.</p>
<h3 id="heading-desabilitar-login-com-usuario-root">Desabilitar login com usuário root</h3>
<p>Vamos bloquear o acesso via SSH para o usuário root, este é um procedimento altamente recomendado para servidores. Iremos deixar que apenas o usuário que criamos tenha acesso ao servidor.</p>
<p>Para isso altere a linha abaixo de <strong>yes</strong> para <strong>no</strong>. Ficando conforme exemplo:</p>
<pre><code class="lang-bash">PermitRootLogin no
</code></pre>
<p>Isso irá restringir o login de root. Pessoas má intencionadas sempre utilizam ataques de força bruta em cima desse usuário e se a senha não for forte o suficiente é certeza que seu sistema estará vulnerável à esse ataque.</p>
<h3 id="heading-alterando-a-porta-padrao">Alterando a porta padrão</h3>
<p>Agora vamos deixar nosso servidor um pouco mais seguro alterando a porta padrão do SSH que é a 22.  Procure a linha abaixo no arquivo de configuração.</p>
<pre><code class="lang-bash">Port 22
</code></pre>
<p>Nesse exemplo vamos utilizar a porta 54122. Altere a linha deixando a dessa forma:</p>
<pre><code class="lang-bash">Port 54122
</code></pre>
<p>Nesses dois exemplos que fizemos, nós melhoramos a segurança do nosso servidor. A partir de agora para efeturar login no servidor será necessário especificar a porta SSH nova. Veremos um exemplo mais abaixo.</p>
<h3 id="heading-quer-melhorar-a-seguranca-um-pouco-mais">Quer melhorar a segurança um pouco mais?</h3>
<p>Vamos especificar no arquivo de configuração do SSH que apenas o nosso usuário poderá acessar via SSH. Restringindo qualquer outro usuário do sistema.</p>
<p>Abra o arquivo de configuração caso já tenha fechado.</p>
<pre><code class="lang-bash">nano /etc/ssh/sshd_conf
</code></pre>
<p>Podemos inserir a linha abaixo em qualquer lugar do arquivo, mas para ficar mais organizado vamos inserir após a linha do PermitRootLogin.</p>
<pre><code class="lang-bash">AllowUsers john
</code></pre>
<p>Para salvar pressione CTRL+O e CTRL+X para sair do arquivo. Para que todas essas alterações surjam efeitos devemos reiniciar o serviço SSH com o comando.</p>
<pre><code class="lang-bash">service ssh restart
</code></pre>
<p>E como saber se tudo que fizemos deu certo? Vamos testar antes de fechar nossa conexão com o servidor.</p>
<p>Abra uma nova janela do seu terminal e efetue uma nova conexão com o servidor via SSH utilizando seu novo usuário. Agora especificando a porta que escolhemos para o serviço.</p>
<pre><code class="lang-bash">ssh john@ip_servidor -p 54122
</code></pre>
<p>Se tudo correr bem você estará logado no servidor sem utilizar uma senha. Ao logar o sistema verifica sua chave privada com a chave pública no servidor e autoriza sua entrada caso esteja tudo certo.</p>
<p>Para executar comando com privilégios de root informe o sudo antes do comando.</p>
<pre><code class="lang-bash">$ sudo apt update
</code></pre>
<p>Maravilha. Agora elevamos o nível de segurança do nosso servidor. Lembre-se que só isso não deixará seu servidor com segurança nível hard, é necessário um conjunto de configurações e serviços executando para ter uma proteção maior.</p>
<p>Até a próxima!</p>
]]></content:encoded></item></channel></rss>