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-chirpypara 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.
Privatesignifica que apenas você e os membros que você convidar terão acesso.Publictornará o código visível para qualquer pessoa na internet. Para a maioria dos blogs pessoais,Publicé uma boa opção, masPrivateoferece 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 -laouexa -l(se tiver oexainstalado) 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-usuariopelo seu nome de usuário real do GitLab no comandogit remote add origin. Além disso, configure seu nome e e-mail nogit config --localpara 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-chirpyno 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
mainehomologestã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-chirpyno 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),gitpara operações de repositório ecurleca-certificatespara 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 3tenta 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/appdentro do contêiner. Isso inclui seus posts, configurações, layouts, etc.RUN npm run build: Executa o script de build definido nopackage.jsondo 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=productiongarante que o Jekyll seja construído no modo de produção, o que pode ativar otimizações e desativar funcionalidades de desenvolvimento.--destination /app/_siteespecifica que os arquivos HTML estáticos gerados devem ser colocados no diretório/app/_sitedentro 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-alpineporque 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/_siteno 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
Dockerfileestá 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
Dockerfilee 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-testpelo 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_NAMEque 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 buildedocker 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. Oechoe 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 doDockerfileno 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 adicionalproductionpara 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 taglatestque 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-homologehomolog). 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|| truegarante 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-hmle$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:productionecleanup: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|| trueevita 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-aremove todas as imagens não utilizadas, e-fforç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:productionecleanup:registry:homolog:stage: cleanup: Pertencem ao estágiocleanup.image: alpine: Usam uma imagem Alpine mínima que incluicurlejqpara interações HTTP e parsing JSON.script:apk add curl jq: Instalacurlejqno 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
latesteproduction(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 amainfará 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 ahomologfará 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 parahomologpara 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-chirpyno 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
homologno 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 onepara push direto eMaintainerspara 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 branchhomologlocalmente. 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 pushacionará o pipeline de CI/CD para a branchhomologno GitLab. - Verifique a execução do pipeline no GitLab:
- Acesse o repositório
jekyll-chirpyno 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
homologparamain: Volte para a branchmainlocalmente e mescle as alterações da branchhomolog.1 2 3
git checkout main git merge homolog git push
Este
git pushpara a branchmainacionará 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.