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á:
- 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.
- 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.
- 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:
- Faça login na sua conta do GitLab.
- No menu superior, navegue até Menu > Projects > New Project.
- 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.
- 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, masPrivate
oferece mais controle.
- Project name: Sugerimos
- 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.
- 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.
- 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
- 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
ouexa -l
(se tiver oexa
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
- 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 comandogit remote add origin
. Além disso, configure seu nome e e-mail nogit config --local
para que seus commits sejam atribuídos corretamente. - 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 branchmain
(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:
- Acesse o repositório
jekyll-chirpy
no GitLab através do seu navegador. - Confirme se todos os arquivos e diretórios do tema Chirpy estão presentes na branch
main
. - Verifique se as branches
main
ehomolog
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:
- Acesse sua conta no Docker Hub.
- Faça login com seu nome de usuário e senha.
- No canto superior direito, clique no seu avatar ou nome de usuário para abrir o menu de usuário.
- Navegue até Account Settings > Security > Personal Access Tokens.
- Na página de tokens, clique em New Access Token.
- 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.
- Token Description: Insira um nome descritivo para o token, como
- Clique em Generate.
- 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.
- Acesse o repositório
jekyll-chirpy
no GitLab. - No menu lateral esquerdo, navegue até Settings > CI/CD.
- Expanda a seção Variables.
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.
- Key:
- 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.
- Key:
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.
- Variável 1:
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 comobuilder
. 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 ecurl
eca-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 (viabundle install
) e do Node.js (vianpm install
).bundle install --jobs=$(nproc) --retry 3
: Instala as gems do Ruby definidas noGemfile
.--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 nopackage.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 nopackage.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. Usamosnginx:1.27-alpine
porque o Nginx é um servidor web leve e eficiente, ideal para servir arquivos estáticos. A versãoalpine
é 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ágiobuilder
) 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ágiobuilder
, 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.
- 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 comoimg-jekyll-chirpy
. O processo pode levar alguns minutos na primeira vez, pois todas as dependências precisam ser baixadas e instaladas. - 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 mododetached
(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.
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.
- 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
- 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 globalIMAGE_NAME
que será usada em todos os jobs. Ela combina o nome de usuário do Docker Hub (obtido da variável protegidaDOCKER_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ágiobuild
.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, comodocker build
edocker 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. Oecho
e o pipe (|
) são usados para passar a senha de forma segura para odocker login
.docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
: Constrói a imagem Docker a partir doDockerfile
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 adicionalproduction
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 taglatest
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 branchmain
.
build:homolog
:- Similar ao
build:production
, mas com tags específicas para homologação ($CI_COMMIT_SHA-homolog
ehomolog
). only: - homolog
: Este job só será executado quando houver um push ou merge para a branchhomolog
.
- Similar ao
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ágiodeploy
.tags: - jekyll-chirpy
: Esta linha é crucial. Ela especifica que este job deve ser executado por um GitLab Runner que tenha a tagjekyll-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ágiobuild: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
: Mododetached
.--network proxy-net
: Conecta o contêiner a uma rede Docker chamadaproxy-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 jobbuild:production
.only: - main
: Este job só será executado para a branchmain
.
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 branchhomolog
.
- Similar ao
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
ecleanup:homolog
:stage: cleanup
: Pertencem ao estágiocleanup
.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
ecleanup:registry:homolog
:stage: cleanup
: Pertencem ao estágiocleanup
.image: alpine
: Usam uma imagem Alpine mínima que incluicurl
ejq
para interações HTTP e parsing JSON.script
:apk add curl jq
: Instalacurl
ejq
no contêiner Alpine.- O bloco
TOKEN=$(curl ...)
ecurl -s -H ... | jq ... | xargs ...
é um script complexo que:- Autentica no Docker Hub para obter um token JWT.
- Lista todas as tags do seu repositório de imagens no Docker Hub.
- Filtra as tags, excluindo
latest
eproduction
(ouhomolog
). - 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 amain
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 ahomolog
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 parahomolog
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
):
- Acesse o repositório
jekyll-chirpy
no GitLab. - No menu lateral esquerdo, navegue até Settings > Repository.
- Role para baixo até a seção Protected branches e clique em Expand.
- Clique em Add protected branch.
- 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 branchmain
, você pode considerarNo one
para push direto eMaintainers
para merge, forçando o uso de Merge Requests.
- Branch: Selecione
- 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
- Faça um commit para a branch
homolog
: Certifique-se de que você está na branchhomolog
localmente. Se não estiver, usegit 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 branchhomolog
no GitLab. - 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.
- Acesse o repositório
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
.
- Mescle
homolog
paramain
: Volte para a branchmain
localmente e mescle as alterações da branchhomolog
.1 2 3
git checkout main git merge homolog git push
Este
git push
para a branchmain
acionará o pipeline de CI/CD para o ambiente de produção. - 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.