Post

Construindo um Blog com Jekyll e CI/CD - Parte 1: Configuração Inicial e Ambiente

Construindo um Blog com Jekyll e CI/CD - Parte 1: Configuração Inicial e Ambiente

Introdução

Este tutorial abrangente guiará você através do processo de configuração de um blog estático utilizando o Jekyll, um gerador de sites estáticos popular, e o elegante tema Chirpy. O diferencial deste guia é a integração de um pipeline de CI/CD (Integração Contínua/Entrega Contínua) robusto no GitLab, permitindo a automação completa do processo de construção, teste e implantação do seu blog. Ao final desta série, você terá um blog dinâmico e de fácil manutenção, com um fluxo de trabalho otimizado para publicações e atualizações.

Nesta primeira parte, focaremos na preparação do ambiente, na configuração inicial do projeto no GitLab e na criação dos elementos essenciais para o nosso pipeline de CI/CD, incluindo o Dockerfile e as variáveis de ambiente necessárias. Entenderemos a arquitetura proposta e os pré-requisitos fundamentais para garantir uma jornada tranquila.

Pré-requisitos Essenciais

Para seguir este tutorial com sucesso, é fundamental que você tenha acesso e familiaridade com as seguintes ferramentas e conceitos. Eles formam a base para a construção e automação do seu blog:

  • Conta no GitLab: O GitLab será a plataforma central para hospedar o código-fonte do seu blog e orquestrar o pipeline de CI/CD. Certifique-se de ter uma conta ativa e acesso ao seu ambiente. Você pode se registrar gratuitamente em https://gitlab.com.

  • Conta no Docker Hub: Utilizaremos o Docker para empacotar nosso blog em uma imagem portátil. O Docker Hub servirá como o registro para armazenar essas imagens, facilitando a implantação. Crie sua conta em https://hub.docker.com caso ainda não tenha uma.

  • VPS (Virtual Private Server): Seu blog precisará de um servidor para ser hospedado e acessível publicamente. Uma VPS oferece a flexibilidade e o controle necessários. Este tutorial assume que você possui um servidor virtual com acesso root ou sudo. O sistema operacional de referência é o Debian 12, mas as instruções podem ser adaptadas para outras distribuições Linux com pequenas modificações.

  • Domínio e DNS Configurados: Para que seu blog seja acessível através de um nome amigável (ex: meublog.com), você precisará de um nome de domínio registrado. Além disso, as configurações de DNS (Domain Name System) do seu domínio devem estar corretamente configuradas para apontar para os endereços IPv4 e IPv6 da sua VPS. Isso garante que, ao digitar o nome do seu domínio no navegador, os usuários sejam direcionados ao seu servidor.

  • Conhecimentos Básicos: Uma compreensão fundamental de conceitos como terminal Linux (comandos básicos de navegação e manipulação de arquivos), Git (controle de versão, comandos como clone, add, commit, push), Docker (criação e execução de contêineres) e os princípios de CI/CD (Integração Contínua, Entrega Contínua, automação de builds e deploys) será extremamente útil. Embora o tutorial seja detalhado, essa base facilitará o entendimento e a resolução de possíveis problemas.

Arquitetura do Projeto e Fluxo do Pipeline CI/CD

Antes de mergulharmos na implementação, é crucial visualizar a arquitetura do nosso projeto e entender como o pipeline de CI/CD funcionará. Isso nos dará uma visão clara de como as diferentes peças se encaixam para automatizar a publicação do seu blog.

Visão Geral da Arquitetura

Nosso projeto será estruturado em torno de um repositório GitLab que conterá o código-fonte do blog Jekyll. O GitLab CI/CD será o motor que orquestrará o processo, desde a construção da imagem Docker do blog até a sua implantação na VPS. O Docker Hub atuará como um repositório central para nossas imagens Docker, garantindo que elas possam ser facilmente puxadas e executadas em qualquer ambiente.

Visão da Arquitetura

graph TD
    A[GitLab] -->|Push de código| B[Pipeline CI/CD]
    B --> C{Branch}
    C -->|main| D[Produção]
    C -->|homolog| E[Homologação]
    D --> F[Docker Hub]
    E --> F
    F --> G[VPS]
    G --> H[Proxy Reverso Nginx]
    H --> I[Contêiner Produção]
    H --> J[Contêiner Homologação]
    I --> K[Usuários]
    J --> L[Equipe de Teste]

Fluxo do Pipeline CI/CD

O pipeline de CI/CD será acionado automaticamente a cada alteração no código do seu blog no GitLab. Ele consistirá em estágios bem definidos, garantindo que cada nova versão do seu blog seja construída, testada e implantada de forma consistente e confiável. O fluxo básico será:

  1. Build (Construção): O código do blog será empacotado em uma imagem Docker. Esta etapa inclui a instalação de dependências, a compilação do site Jekyll e a criação da imagem final.
  2. Deploy (Implantação): A imagem Docker construída será implantada em um ambiente específico (homologação ou produção) na sua VPS. Isso envolve parar o contêiner anterior, puxar a nova imagem e iniciar um novo contêiner.
  3. Cleanup (Limpeza): Após a implantação bem-sucedida, imagens Docker antigas e desnecessárias serão removidas para otimizar o espaço e a organização.

Componentes Chave

Os principais componentes envolvidos neste processo são:

  • Jekyll: O gerador de sites estáticos que transforma seu conteúdo Markdown em páginas HTML prontas para serem servidas.
  • Tema Chirpy: Um tema moderno e responsivo para Jekyll, que oferece uma experiência de usuário agradável e diversas funcionalidades.
  • GitLab: A plataforma de DevOps que hospeda o repositório do código, gerencia o controle de versão e executa o pipeline de CI/CD.
  • GitLab CI/CD: O serviço de integração e entrega contínua do GitLab, responsável por automatizar as etapas de build, deploy e cleanup.
  • Docker: A tecnologia de contêineres que empacota o blog e suas dependências em uma unidade isolada e portátil.
  • Docker Hub: O registro de imagens Docker onde as imagens do seu blog serão armazenadas.
  • VPS: O servidor virtual onde o contêiner Docker do seu blog será executado, tornando-o acessível na internet.

Configurando o Projeto no GitLab

O primeiro passo prático é configurar o repositório do seu blog no GitLab. Este repositório será o coração do seu projeto, onde todo o código-fonte será armazenado e versionado.

Criando o Repositório no GitLab

Siga estes passos para criar um novo projeto (repositório) no GitLab:

  1. Faça login na sua conta do GitLab.
  2. No menu superior, navegue até Menu > Projects > New Project.
  3. Na página de criação de projeto, selecione a opção Create Blank Project. Esta opção nos permite iniciar um repositório vazio, onde importaremos o tema Chirpy.
  4. Preencha os campos obrigatórios:
    • Project name: Sugerimos jekyll-chirpy para manter a consistência com o tutorial, mas você pode escolher um nome que melhor se adapte ao seu projeto.
    • Visibility Level: Escolha a visibilidade do seu repositório. Private significa que apenas você e os membros que você convidar terão acesso. Public tornará o código visível para qualquer pessoa na internet. Para a maioria dos blogs pessoais, Public é uma boa opção, mas Private oferece mais controle.
  5. Importante: Desmarque a opção Initialize repository with a README. Não precisamos de um README inicial, pois importaremos os arquivos do tema Chirpy em breve.
  6. Clique em Create Project para finalizar a criação do repositório.

Configurando o Tema Chirpy

Com o repositório vazio criado, o próximo passo é popular o projeto com os arquivos do tema Chirpy. Faremos isso clonando o repositório oficial do tema e copiando seus conteúdos para o nosso novo repositório.

  1. Abra seu terminal e crie um diretório para o seu projeto. Em seguida, navegue até ele:
    1
    2
    
    mkdir ~/jekyll-chirpy
    cd ~/jekyll-chirpy
    
  2. Clone o repositório oficial do tema Chirpy para um diretório temporário e, em seguida, copie todos os seus conteúdos para o diretório raiz do seu projeto. Após a cópia, o diretório temporário pode ser removido.
    1
    2
    3
    
    git clone https://github.com/cotes2020/jekyll-theme-chirpy.git temp
    cp -r temp/* .
    rm -rf temp
    

    Para verificar se os arquivos foram copiados corretamente, você pode usar o comando ls -la ou exa -l (se tiver o exa instalado) para listar o conteúdo do diretório:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    exa -l
    # Saída esperada (pode variar ligeiramente):
    # .rw-rw-r-- 6.7k gean 23 May 15:48 _config.yml
    # drwxrwxr-x    - gean 23 May 15:48 _data
    # drwxrwxr-x    - gean 23 May 15:48 _includes
    # drwxrwxr-x    - gean 23 May 15:48 _javascript
    # drwxrwxr-x    - gean 23 May 15:48 _layouts
    # drwxrwxr-x    - gean 23 May 15:48 _plugins
    # drwxrwxr-x    - gean 23 May 15:48 _posts
    # drwxrwxr-x    - gean 23 May 15:48 _sass
    # drwxrwxr-x    - gean 23 May 15:48 _tabs
    # drwxrwxr-x    - gean 23 May 15:48 assets
    # drwxrwxr-x    - gean 23 May 15:48 docs
    # .rw-rw-r--   63 gean 23 May 15:48 eslint.config.js
    # .rw-rw-r--  284 gean 23 May 15:48 Gemfile
    # .rw-rw-r--   34 gean 23 May 15:48 index.html
    # .rw-rw-r-- 1.5k gean 23 May 15:48 jekyll-theme-chirpy.gemspec
    # .rw-rw-r-- 1.1k gean 23 May 15:48 LICENSE
    # .rw-rw-r-- 3.7k gean 23 May 15:48 package.json
    # .rw-rw-r--  886 gean 23 May 15:48 purgecss.js
    # .rw-rw-r-- 3.6k gean 23 May 15:48 README.md
    # .rw-rw-r-- 2.2k gean 23 May 15:48 rollup.config.js
    # drwxrwxr-x    - gean 23 May 15:48 tools
    
  3. Agora, inicialize o Git no seu diretório local, configure suas credenciais e adicione o repositório remoto do GitLab. Em seguida, faça o commit inicial e envie os arquivos para o GitLab.
    1
    2
    3
    4
    5
    6
    7
    
    git init --initial-branch=main
    git config --local user.name "Seu Nome"
    git config --local user.email "seu.email@example.com"
    git remote add origin git@gitlab.com:seu-usuario/jekyll-chirpy.git # ATENÇÃO: Altere 'seu-usuario' para o seu nome de usuário do GitLab
    git add .
    git commit -m "Initial commit: Setup Jekyll Chirpy theme"
    git push --set-upstream origin main
    

    Observação: Certifique-se de substituir seu-usuario pelo seu nome de usuário real do GitLab no comando git remote add origin. Além disso, configure seu nome e e-mail no git config --local para que seus commits sejam atribuídos corretamente.

  4. Para implementar um fluxo de trabalho de CI/CD com ambientes separados (homologação e produção), criaremos uma branch homolog. Esta branch será usada para testar as alterações antes de serem promovidas para a branch main (produção).
    1
    2
    3
    
    git branch # Verifica as branches existentes
    git checkout -b homolog # Cria e muda para a branch 'homolog'
    git push -u origin homolog # Envia a branch 'homolog' para o GitLab
    

Verificando os Arquivos no Repositório GitLab

Após enviar os arquivos, é uma boa prática verificar se tudo foi carregado corretamente no GitLab:

  1. Acesse o repositório jekyll-chirpy no GitLab através do seu navegador.
  2. Confirme se todos os arquivos e diretórios do tema Chirpy estão presentes na branch main.
  3. Verifique se as branches main e homolog estão disponíveis e contêm os arquivos esperados.

Configurando Credenciais para o Docker Hub no GitLab

Para que o GitLab CI/CD possa construir e enviar imagens Docker para o Docker Hub, ele precisará de credenciais de acesso. Faremos isso criando um Personal Access Token no Docker Hub e configurando-o como variáveis protegidas no GitLab.

Criando um Token de Acesso no Docker Hub

Um Personal Access Token (PAT) é uma alternativa segura à sua senha para autenticação programática. Siga estes passos para gerar um:

  1. Acesse sua conta no Docker Hub.
  2. Faça login com seu nome de usuário e senha.
  3. No canto superior direito, clique no seu avatar ou nome de usuário para abrir o menu de usuário.
  4. Navegue até Account Settings > Security > Personal Access Tokens.
  5. Na página de tokens, clique em New Access Token.
  6. Preencha os detalhes do token:
    • Token Description: Insira um nome descritivo para o token, como gitlab-ci-jekyll-blog. Isso ajuda a identificar a finalidade do token posteriormente.
    • Access Permissions: Selecione Read & Write. Isso concede ao GitLab CI/CD as permissões necessárias para puxar (read) e enviar (write) imagens para seus repositórios no Docker Hub.
    • Expires: Recomenda-se deixar como Never para tokens usados em CI/CD, a menos que sua política de segurança exija rotação regular. Se você definir uma data de expiração, lembre-se de renová-lo antes que expire para evitar interrupções no pipeline.
  7. Clique em Generate.
  8. Muito Importante: Copie o token gerado imediatamente. Este token será exibido apenas uma vez e não poderá ser recuperado posteriormente. Guarde-o em um local seguro (por exemplo, um gerenciador de senhas) e não o compartilhe publicamente.

Nota: O GitLab oferece um registro de contêineres integrado que pode substituir o Docker Hub, GitLab Container Registry.

Criando Variáveis de Acesso do Docker Hub no Projeto do GitLab

Com o token de acesso em mãos, vamos configurá-lo no GitLab como variáveis de CI/CD. Variáveis protegidas são essenciais para armazenar informações sensíveis, como senhas e tokens, de forma segura, impedindo que sejam expostas nos logs do pipeline.

  1. Acesse o repositório jekyll-chirpy no GitLab.
  2. No menu lateral esquerdo, navegue até Settings > CI/CD.
  3. Expanda a seção Variables.
  4. Clique em Add variable para adicionar as duas variáveis necessárias:

    • Variável 1: DOCKER_USERNAME
      • Key: DOCKER_USERNAME
      • Value: Seu nome de usuário do Docker Hub.
      • Type: Variable (padrão)
      • Environment scope: All (padrão)
      • Protect variable: Marque esta caixa. Isso garante que a variável não seja exposta em logs de jobs não protegidos e só esteja disponível para branches e tags protegidas.
      • Expand variable reference: Marque esta caixa.
      • Description: (Opcional) Uma breve descrição, como Nome de usuário para autenticação no Docker Hub.
      • Clique em Add variable.
    • Variável 2: DOCKER_PASSWORD
      • Key: DOCKER_PASSWORD
      • Value: O Personal Access Token que você gerou e copiou do Docker Hub.
      • Type: Variable (padrão)
      • Environment scope: All (padrão)
      • Protect variable: Marque esta caixa. É crucial proteger esta variável, pois ela contém sua credencial de acesso.
      • Expand variable reference: Marque esta caixa.
      • Description: (Opcional) Uma breve descrição, como Token de acesso para autenticação no Docker Hub.
      • Clique em Add variable.

    Após adicionar ambas as variáveis, elas aparecerão na lista de variáveis do seu projeto, mas seus valores estarão ocultos por segurança.

Criando o Dockerfile e Preparando o Ambiente de Build

O Dockerfile é a receita para construir a imagem Docker do seu blog. Ele define todas as etapas necessárias para configurar o ambiente, instalar dependências e compilar o site Jekyll dentro de um contêiner isolado. Utilizaremos uma abordagem de multi-stage build para criar uma imagem final otimizada e leve.

Estrutura do Dockerfile

No diretório raiz do seu projeto jekyll-chirpy, crie um novo arquivo chamado Dockerfile:

1
touch Dockerfile

Em seguida, edite o Dockerfile e adicione o seguinte conteúdo. Cada seção será explicada em detalhes a seguir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
FROM ruby:3.2-bullseye AS builder

# 1. Instala dependências do sistema
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    ca-certificates \
    gnupg \
    build-essential \
    ruby-dev \
    gcc \
    g++ \
    make \
    libffi-dev \
    libyaml-dev \
    zlib1g-dev \
    git

# 2. Instala Node.js 18 (obrigatório para o Chirpy)
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
    && apt-get install -y nodejs

WORKDIR /app

# 3. Copia arquivos de dependências primeiro para cache
COPY Gemfile jekyll-theme-chirpy.gemspec package.json package-lock.json* ./

# 4. Instala dependências
RUN bundle install --jobs=$(nproc) --retry 3 \
    && npm install --force

# 5. Copia todo o código fonte
COPY . .

# 6. Build dos assets (etapa crítica)
RUN npm run build

# 7. Build do Jekyll
RUN JEKYLL_ENV=production bundle exec jekyll build --destination /app/_site

# --- Estágio final ---
FROM nginx:1.27-alpine

COPY --from=builder /app/_site /usr/share/nginx/html

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Explicando o Dockerfile: Uma Análise Detalhada

Este Dockerfile utiliza uma técnica chamada multi-stage build, que é uma prática recomendada para criar imagens Docker eficientes e pequenas. Ele divide o processo de construção em duas fases principais:

Estágio builder (Construção)

Este é o primeiro estágio, responsável por compilar o site Jekyll e gerar os arquivos estáticos. Ele é baseado na imagem ruby:3.2-bullseye, que fornece um ambiente Ruby necessário para o Jekyll.

  • FROM ruby:3.2-bullseye AS builder: Define a imagem base para este estágio e a nomeia como builder. Usamos uma versão específica do Ruby (3.2) em uma distribuição Debian (bullseye) para garantir consistência.

  • RUN apt-get update && apt-get install -y --no-install-recommends ...: Este comando instala todas as dependências do sistema operacional necessárias para compilar o Jekyll e seus plugins. Inclui ferramentas de build (build-essential, gcc, g++, make), bibliotecas de desenvolvimento (libffi-dev, libyaml-dev, zlib1g-dev), git para operações de repositório e curl e ca-certificates para download seguro.

  • RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && apt-get install -y nodejs: O tema Chirpy, como muitos temas modernos, depende do Node.js para gerenciar assets (como JavaScript e CSS) e executar scripts de build. Esta linha instala o Node.js versão 18, que é um requisito específico do tema Chirpy.

  • WORKDIR /app: Define o diretório de trabalho dentro do contêiner para /app. Todos os comandos subsequentes serão executados a partir deste diretório.

  • COPY Gemfile jekyll-theme-chirpy.gemspec package.json package-lock.json* ./: Esta é uma otimização importante para o cache do Docker. Copiamos apenas os arquivos de dependência (Gemfile, gemspec, package.json, package-lock.json) primeiro. Se esses arquivos não mudarem entre as builds, o Docker pode reutilizar a camada de cache para a instalação de dependências, acelerando o processo.

  • RUN bundle install --jobs=$(nproc) --retry 3 \ && npm install --force: Instala as dependências do Ruby (via bundle install) e do Node.js (via npm install).
    • bundle install --jobs=$(nproc) --retry 3: Instala as gems do Ruby definidas no Gemfile. --jobs=$(nproc) utiliza todos os núcleos da CPU disponíveis para acelerar a instalação, e --retry 3 tenta novamente em caso de falha de rede.
    • npm install --force: Instala as dependências do Node.js definidas no package.json. O --force é usado para resolver possíveis conflitos de dependência, comum em ambientes de build.
  • COPY . .: Copia todo o restante do código-fonte do seu projeto para o diretório de trabalho /app dentro do contêiner. Isso inclui seus posts, configurações, layouts, etc.

  • RUN npm run build: Executa o script de build definido no package.json do tema Chirpy. Este script geralmente compila assets como CSS (via Sass) e JavaScript, otimizando-os para produção.

  • RUN JEKYLL_ENV=production bundle exec jekyll build --destination /app/_site: Este é o comando principal que constrói o site Jekyll. JEKYLL_ENV=production garante que o Jekyll seja construído no modo de produção, o que pode ativar otimizações e desativar funcionalidades de desenvolvimento. --destination /app/_site especifica que os arquivos HTML estáticos gerados devem ser colocados no diretório /app/_site dentro do contêiner.

Estágio Final (Serviço)

Este é o segundo e último estágio. Ele pega os artefatos gerados pelo estágio builder e os empacota em uma imagem final muito menor, que será usada para servir o blog.

  • FROM nginx:1.27-alpine: Define a imagem base para o estágio final. Usamos nginx:1.27-alpine porque o Nginx é um servidor web leve e eficiente, ideal para servir arquivos estáticos. A versão alpine é baseada na distribuição Alpine Linux, que é extremamente pequena, resultando em imagens Docker menores e mais seguras.

  • COPY --from=builder /app/_site /usr/share/nginx/html: Esta é a parte crucial do multi-stage build. Copiamos apenas os arquivos estáticos gerados pelo Jekyll (que estão em /app/_site no estágio builder) para o diretório padrão de serviço do Nginx (/usr/share/nginx/html) no estágio final. Isso significa que a imagem final não contém todas as ferramentas de build e dependências do estágio builder, resultando em uma imagem muito mais leve.

  • EXPOSE 80: Informa ao Docker que o contêiner expõe a porta 80 em tempo de execução. Esta é a porta padrão para tráfego HTTP.

  • CMD ["nginx", "-g", "daemon off;"]: Define o comando que será executado quando o contêiner for iniciado. Neste caso, ele inicia o servidor Nginx em primeiro plano (daemon off;), garantindo que o contêiner permaneça em execução enquanto o Nginx estiver ativo.

Testando o Dockerfile Localmente

Antes de integrar o Dockerfile ao pipeline de CI/CD, é fundamental testá-lo localmente para garantir que a imagem seja construída corretamente e que o blog funcione como esperado. Isso economiza tempo e evita problemas no pipeline.

  1. Construa a imagem Docker: No diretório raiz do seu projeto (onde o Dockerfile está localizado), execute o seguinte comando. O . no final indica que o contexto da build é o diretório atual.
    1
    
    docker build -t img-jekyll-chirpy .
    

    Este comando lerá o Dockerfile e construirá a imagem, nomeando-a como img-jekyll-chirpy. O processo pode levar alguns minutos na primeira vez, pois todas as dependências precisam ser baixadas e instaladas.

  2. Execute o contêiner para testar: Após a construção bem-sucedida da imagem, execute um contêiner a partir dela. Mapearemos a porta 80 do contêiner para a porta 8080 da sua máquina local, permitindo que você acesse o blog através do seu navegador.
    1
    
    docker run -d --name jekyll-chirpy-test -p 8080:80 img-jekyll-chirpy
    
    • -d: Executa o contêiner em modo detached (em segundo plano).
    • --name jekyll-chirpy-test: Atribui um nome amigável ao contêiner para facilitar a identificação.
    • -p 8080:80: Mapeia a porta 8080 da sua máquina host para a porta 80 do contêiner (onde o Nginx está servindo o blog).
    • img-jekyll-chirpy: O nome da imagem Docker que acabamos de construir.
  3. Acesse o site: Abra seu navegador web e navegue para http://localhost:8080. Você deverá ver seu blog Jekyll funcionando localmente. Se tudo estiver correto, parabéns! Você construiu e executou seu blog em um contêiner Docker.

  4. Verifique os logs do contêiner (opcional): Se o blog não carregar ou se você encontrar algum problema, os logs do contêiner podem fornecer informações valiosas para depuração. Substitua jekyll-chirpy-test pelo nome do seu contêiner.
    1
    
    docker logs jekyll-chirpy-test
    
  5. Pare e remova o contêiner de teste (limpeza): Após o teste, é uma boa prática parar e remover o contêiner para liberar recursos.
    1
    2
    
    docker stop jekyll-chirpy-test
    docker rm jekyll-chirpy-test
    

Configurando o Pipeline CI/CD no GitLab com Ambientes Separados

Agora que temos o Dockerfile funcionando, vamos configurar o pipeline de CI/CD no GitLab. O GitLab CI/CD utiliza um arquivo .gitlab-ci.yml para definir as etapas, jobs e condições para a execução do pipeline. Nosso objetivo é criar um pipeline que suporte ambientes de homologação e produção, permitindo testes antes da implantação final.

Criando o Arquivo .gitlab-ci.yml

No diretório raiz do seu projeto, crie um arquivo chamado .gitlab-ci.yml:

1
touch .gitlab-ci.yml

Em seguida, adicione o seguinte conteúdo ao arquivo. Este arquivo define os estágios do pipeline, as variáveis globais e os jobs específicos para cada ambiente (produção e homologação).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
stages:
  - build
  - deploy
  - cleanup

variables:
  IMAGE_NAME: $DOCKER_USERNAME/jekyll-chirpy

build:production:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
    - docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
    - docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:production
    - docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
    - docker push $IMAGE_NAME:$CI_COMMIT_SHA
    - docker push $IMAGE_NAME:production
    - docker push $IMAGE_NAME:latest
  only:
    - main

build:homolog:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
    - docker build -t $IMAGE_NAME:$CI_COMMIT_SHA-homolog .
    - docker tag $IMAGE_NAME:$CI_COMMIT_SHA-homolog $IMAGE_NAME:homolog
    - docker push $IMAGE_NAME:$CI_COMMIT_SHA-homolog
    - docker push $IMAGE_NAME:homolog
  only:
    - homolog

deploy:production:
  stage: deploy
  tags:
    - jekyll-chirpy
  script:
    - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
    - docker pull $IMAGE_NAME:$CI_COMMIT_SHA
    - docker stop jekyll-chirpy-prd || true
    - docker rm jekyll-chirpy-prd || true
    - docker run -d --network proxy-net --name jekyll-chirpy-prd --restart unless-stopped $IMAGE_NAME:$CI_COMMIT_SHA
  dependencies:
    - build:production
  only:
    - main

deploy:homolog:
  stage: deploy
  tags:
    - jekyll-chirpy
  script:
    - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
    - docker pull $IMAGE_NAME:$CI_COMMIT_SHA-homolog
    - docker stop jekyll-chirpy-hml || true
    - docker rm jekyll-chirpy-hml || true
    - docker run -d --network proxy-net --name jekyll-chirpy-hml --restart unless-stopped $IMAGE_NAME:$CI_COMMIT_SHA-homolog
  dependencies:
    - build:homolog
  only:
    - homolog

cleanup:production:
  stage: cleanup
  tags:
    - jekyll-chirpy
  script:
    - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
    - docker rmi $IMAGE_NAME:$CI_COMMIT_SHA 2>/dev/null || true
    - docker rmi $IMAGE_NAME:production 2>/dev/null || true
    - docker rmi $IMAGE_NAME:latest 2>/dev/null || true
    - docker image prune -af
  dependencies:
    - deploy:production 
  only:
    - main

cleanup:homolog:
  stage: cleanup
  tags:
    - jekyll-chirpy
  script:
    - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
    - docker rmi $IMAGE_NAME:$CI_COMMIT_SHA-homolog 2>/dev/null || true
    - docker rmi $IMAGE_NAME:homolog 2>/dev/null || true
    - docker image prune -af
  dependencies:
    - deploy:homolog  
  only:
    - homolog

cleanup:registry:production:
  stage: cleanup
  image: alpine
  script:
    - apk add curl jq
    - |
      TOKEN=$(curl -s -X POST -H "Content-Type: application/json" -d '{"username": "'$DOCKER_USERNAME'", "password": "'$DOCKER_PASSWORD'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
      curl -s -H "Authorization: JWT ${TOKEN}" "https://hub.docker.com/v2/repositories/${IMAGE_NAME}/tags/?page_size=100" | \
      jq -r '.results[] | select(.name != "latest" and .name != "production").name' | \
      xargs -I {} curl -s -X DELETE -H "Authorization: JWT ${TOKEN}" "https://hub.docker.com/v2/repositories/${IMAGE_NAME}/tags/{}/"
  only:
    - main

cleanup:registry:homolog:
  stage: cleanup
  image: alpine
  script:
    - apk add curl jq
    - |
      TOKEN=$(curl -s -X POST -H "Content-Type: application/json" -d '{"username": "'$DOCKER_USERNAME'", "password": "'$DOCKER_PASSWORD'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
      curl -s -H "Authorization: JWT ${TOKEN}" "https://hub.docker.com/v2/repositories/${IMAGE_NAME}/tags/?page_size=100" | \
      jq -r '.results[] | select(.name != "homolog").name' | \
      xargs -I {} curl -s -X DELETE -H "Authorization: JWT ${TOKEN}" "https://hub.docker.com/v2/repositories/${IMAGE_NAME}/tags/{}/"
  only:
    - homolog

Explicação Detalhada do Pipeline CI/CD com Ambientes Separados

O arquivo .gitlab-ci.yml que acabamos de criar é o coração da nossa automação. Ele define uma série de estágios e jobs que serão executados sequencialmente para construir, implantar e limpar nosso blog. A grande vantagem aqui é a separação de ambientes, permitindo que alterações sejam testadas em um ambiente de homologação antes de serem promovidas para produção.

Estágios (stages)

Os estágios definem a ordem de execução dos jobs. Um estágio só começa quando todos os jobs do estágio anterior são concluídos com sucesso. Nosso pipeline tem três estágios principais:

  • build: Responsável por construir a imagem Docker do blog.
  • deploy: Responsável por implantar a imagem Docker em um servidor.
  • cleanup: Responsável por remover imagens Docker antigas e liberar espaço.

Variáveis Globais (variables)

  • IMAGE_NAME: $DOCKER_USERNAME/jekyll-chirpy: Define uma variável global IMAGE_NAME que será usada em todos os jobs. Ela combina o nome de usuário do Docker Hub (obtido da variável protegida DOCKER_USERNAME) com o nome do repositório da imagem (jekyll-chirpy). Isso garante que as imagens sejam enviadas para o local correto no Docker Hub.

Jobs de build

Existem dois jobs de build, um para cada ambiente:

  • build:production:
    • stage: build: Indica que este job pertence ao estágio build.
    • image: docker:latest: O job será executado em um contêiner Docker que já possui o cliente Docker instalado.
    • services: - docker:dind: docker:dind (Docker in Docker) é um serviço que permite que o contêiner do job execute comandos Docker, como docker build e docker push. Isso é essencial para construir e enviar imagens.
    • script: Contém os comandos shell que serão executados.
      • echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin: Autentica o cliente Docker no Docker Hub usando as variáveis protegidas que configuramos no GitLab. O echo e o pipe (|) são usados para passar a senha de forma segura para o docker login.
      • docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .: Constrói a imagem Docker a partir do Dockerfile no diretório atual. A imagem é taggeada com o nome da imagem ($IMAGE_NAME) e o SHA do commit atual ($CI_COMMIT_SHA). Isso garante que cada build tenha uma tag única e rastreável.
      • docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:production: Cria uma tag adicional production para a imagem recém-construída. Esta tag sempre apontará para a última versão de produção.
      • docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest: Cria uma tag latest que também aponta para a última versão de produção. latest é uma tag comum para a versão mais recente de uma imagem.
      • docker push ...: Envia as imagens taggeadas para o Docker Hub.
    • only: - main: Este job só será executado quando houver um push ou merge para a branch main.
  • build:homolog:
    • Similar ao build:production, mas com tags específicas para homologação ($CI_COMMIT_SHA-homolog e homolog).
    • only: - homolog: Este job só será executado quando houver um push ou merge para a branch homolog.

Jobs de deploy

Também temos dois jobs de deploy, um para cada ambiente:

  • deploy:production:
    • stage: deploy: Indica que este job pertence ao estágio deploy.
    • tags: - jekyll-chirpy: Esta linha é crucial. Ela especifica que este job deve ser executado por um GitLab Runner que tenha a tag jekyll-chirpy. Você precisará configurar um GitLab Runner na sua VPS com esta tag para que a implantação funcione. (A configuração do Runner será abordada na Parte 2 do tutorial).
    • script:
      • echo "$DOCKER_PASSWORD" | docker login ...: Autentica no Docker Hub.
      • docker pull $IMAGE_NAME:$CI_COMMIT_SHA: Puxa a imagem Docker específica do commit que foi construída no estágio build:production.
      • docker stop jekyll-chirpy-prd || true: Tenta parar o contêiner de produção existente. O || true garante que o pipeline não falhe se o contêiner não estiver em execução (por exemplo, na primeira implantação).
      • docker rm jekyll-chirpy-prd || true: Tenta remover o contêiner de produção existente.
      • docker run -d --network proxy-net --name jekyll-chirpy-prd --restart unless-stopped $IMAGE_NAME:$CI_COMMIT_SHA: Inicia um novo contêiner de produção.
        • -d: Modo detached.
        • --network proxy-net: Conecta o contêiner a uma rede Docker chamada proxy-net. Esta rede será usada para integração com um proxy reverso (como Nginx Proxy Manager ou Traefik), que será configurado na Parte 3 para rotear o tráfego externo para o seu blog.
        • --name jekyll-chirpy-prd: Nomeia o contêiner de produção.
        • --restart unless-stopped: Garante que o contêiner reinicie automaticamente, a menos que seja explicitamente parado.
    • dependencies: - build:production: Garante que este job só será executado após o sucesso do job build:production.
    • only: - main: Este job só será executado para a branch main.
  • deploy:homolog:
    • Similar ao deploy:production, mas com nomes de contêiner e tags de imagem específicos para homologação (jekyll-chirpy-hml e $CI_COMMIT_SHA-homolog).
    • only: - homolog: Este job só será executado para a branch homolog.

Jobs de cleanup (Limpeza Local)

Estes jobs são executados no GitLab Runner (na sua VPS) após a implantação para remover imagens Docker locais que não são mais necessárias, economizando espaço em disco.

  • cleanup:production e cleanup:homolog:
    • stage: cleanup: Pertencem ao estágio cleanup.
    • tags: - jekyll-chirpy: Executados no GitLab Runner.
    • script:
      • docker rmi ... || true: Tenta remover as imagens Docker locais com as tags de commit e de ambiente. O || true evita falhas se a imagem já tiver sido removida ou não existir.
      • docker image prune -af: Remove todas as imagens Docker não utilizadas (dangling images) e as que não estão associadas a nenhum contêiner em execução. O -a remove todas as imagens não utilizadas, e -f força a remoção sem confirmação.
    • dependencies: - deploy:production / deploy:homolog: Garante que a limpeza só ocorra após a implantação bem-sucedida.
    • only: - main / only: - homolog: Executados apenas para suas respectivas branches.

Jobs de cleanup:registry (Limpeza Remota no Docker Hub)

Estes jobs são mais avançados e visam limpar tags de imagens antigas no Docker Hub, evitando que seu registro fique sobrecarregado com muitas versões de imagens. Eles usam curl e jq para interagir com a API do Docker Hub.

  • cleanup:registry:production e cleanup:registry:homolog:
    • stage: cleanup: Pertencem ao estágio cleanup.
    • image: alpine: Usam uma imagem Alpine mínima que inclui curl e jq para interações HTTP e parsing JSON.
    • script:
      • apk add curl jq: Instala curl e jq no contêiner Alpine.
      • O bloco TOKEN=$(curl ...) e curl -s -H ... | jq ... | xargs ... é um script complexo que:
        1. Autentica no Docker Hub para obter um token JWT.
        2. Lista todas as tags do seu repositório de imagens no Docker Hub.
        3. Filtra as tags, excluindo latest e production (ou homolog).
        4. Para cada tag filtrada, envia uma requisição DELETE para a API do Docker Hub para removê-la.
    • only: - main / only: - homolog: Executados apenas para suas respectivas branches.

Fluxo de Trabalho com Branches e Proteção

Para garantir a estabilidade e a segurança do seu blog, é fundamental adotar um fluxo de trabalho com branches bem definido e proteger as branches críticas no GitLab.

Fluxo de Trabalho com Branches

Recomendamos o seguinte fluxo de trabalho:

  • main (Produção): Esta branch representa o código que está em produção e acessível publicamente. Somente alterações testadas e aprovadas devem ser mescladas nesta branch. O pipeline associado a main fará o deploy para o ambiente de produção.
  • homolog (Homologação/Staging): Esta branch é usada para testar novas funcionalidades, correções de bugs ou alterações de conteúdo antes que elas cheguem à produção. O pipeline associado a homolog fará o deploy para um ambiente de homologação, onde você pode revisar e validar as mudanças. Desenvolvedores devem fazer seus commits iniciais nesta branch.
  • Branches de Feature/Bugfix: Para cada nova funcionalidade ou correção de bug, crie uma branch separada a partir de homolog. Trabalhe nesta branch, e quando a funcionalidade estiver pronta, mescle-a de volta para homolog para testes.

Este modelo de branches garante que o ambiente de produção esteja sempre estável e que as alterações sejam devidamente testadas antes de serem lançadas.

Protegendo a Branch homolog (e main)

Como as variáveis do Docker Hub que configuramos são protegidas, as branches que as utilizam (como homolog e main) também precisam ser protegidas no GitLab. Isso restringe quem pode fazer push ou merge nessas branches, aumentando a segurança e a integridade do seu código.

Siga estes passos para proteger a branch homolog (e repita para main):

  1. Acesse o repositório jekyll-chirpy no GitLab.
  2. No menu lateral esquerdo, navegue até Settings > Repository.
  3. Role para baixo até a seção Protected branches e clique em Expand.
  4. Clique em Add protected branch.
  5. Configure as opções para a branch homolog:
    • Branch: Selecione homolog no dropdown.
    • Allowed to merge: Escolha Maintainers. Isso significa que apenas usuários com a função de Maintainer (ou superior) podem mesclar código nesta branch.
    • Allowed to push and merge: Escolha Maintainers. Isso restringe quem pode fazer push direto para a branch e quem pode mesclar. Para a branch main, você pode considerar No one para push direto e Maintainers para merge, forçando o uso de Merge Requests.
  6. Clique em Protect.

Repita o processo para a branch main, aplicando as mesmas ou mais restritivas configurações de proteção, conforme a política de segurança do seu projeto.

Testando o Pipeline CI/CD

Com o Dockerfile e o .gitlab-ci.yml configurados, é hora de testar nosso pipeline. Começaremos testando o fluxo de homologação e, em seguida, o fluxo de produção.

Testando o Fluxo de Homologação

  1. Faça um commit para a branch homolog: Certifique-se de que você está na branch homolog localmente. Se não estiver, use git checkout homolog.
    1
    2
    3
    4
    
    git checkout homolog
    git add Dockerfile .gitlab-ci.yml # Adicione os novos arquivos
    git commit -m "feat: Adiciona Dockerfile e pipeline CI/CD para homologação"
    git push
    

    Este git push acionará o pipeline de CI/CD para a branch homolog no GitLab.

  2. Verifique a execução do pipeline no GitLab:
    • Acesse o repositório jekyll-chirpy no GitLab.
    • No menu lateral, navegue até CI/CD > Pipelines.
    • Você deverá ver um novo pipeline em execução para a branch homolog. Clique nele para ver o progresso dos jobs (build:homolog, deploy:homolog, cleanup:homolog, cleanup:registry:homolog).
    • Monitore os logs de cada job. Se houver algum erro, os logs fornecerão informações para depuração.

Testando o Fluxo de Produção

Após validar que o ambiente de homologação está funcionando corretamente e que seu blog aparece como esperado, você pode promover as alterações para a branch main.

  1. Mescle homolog para main: Volte para a branch main localmente e mescle as alterações da branch homolog.
    1
    2
    3
    
    git checkout main
    git merge homolog
    git push
    

    Este git push para a branch main acionará o pipeline de CI/CD para o ambiente de produção.

  2. Verifique a execução do pipeline para main:
    • Novamente, acesse CI/CD > Pipelines no GitLab.
    • Você verá um novo pipeline em execução para a branch main. Monitore os jobs (build:production, deploy:production, cleanup:production, cleanup:registry:production).
    • Após a conclusão bem-sucedida, seu blog deverá estar atualizado no ambiente de produção.

Com isso, você conclui a primeira parte da configuração do seu blog Jekyll com CI/CD. Na próxima parte, abordaremos a configuração do GitLab Runner na sua VPS e a integração com um proxy reverso para servir seu blog de forma eficiente e segura.

This post is licensed under CC BY 4.0 by the author.