Acesse os slides remotamente em:
Acesse os slides e os códigos das demonstrações localmente clonando o repositório:
$ git clone --recursive git@github.com:vcampitelli/slides-criando-deployment-zero.git
github.com/vcampitelli/slides-criando-deployment-zero
Desliga a versão atual e sobe a nova em seguida. Simples, porém com indisponibilidade temporária.
Atualiza gradualmente instância por instância. Minimiza downtime, mas pode misturar versões durante a transição.
Libera para uma pequena parcela dos usuários primeiro. Monitora métricas; se tudo estiver bem, expande até 100%. Ideal para reduzir risco.
Mantém dois ambientes idênticos (blue e green). Você publica na “nova” cor, valida e muda o tráfego em um clique, com rollback instantâneo.
O intuito dessa palestra é meramente educacional! Em cenários reais, é extremamente recomendado utilizar ferramentas robustas de mercado.
services:
nginx:
image: nginx:1-alpine
ports:
- "80:80"
volumes:
- "./nginx.conf.template:/etc/nginx/templates/default.conf.template"
environment:
APP_HOST: $APP_HOST
healthcheck:
test: ["CMD", "curl", "-f", "localhost/ping"]
app1:
image: $IMAGE1
restart: unless-stopped
env_file:
- app1.env
app2:
image: $IMAGE2
restart: unless-stopped
env_file:
- app2.env
Arquivo compose.yaml com a configuração necessária para ter as duas aplicações possíveis
APP_HOST=app1
IMAGE1=aplicacao:1.0.0
IMAGE2=aplicacao:1.0.1
Arquivo .env com as variáveis de ambiente para o Docker Compose
server {
listen 80 default_server;
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass ${APP_HOST}:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
}
Arquivo nginx.conf.template com o template da configuração do nginx
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Uso: $0 <imagem-docker>"
exit 1
fi
image="$1"
docker_compose_file="compose.yaml"
service1="app1"
service2="app2"
service1_env="IMAGE1"
service2_env="IMAGE2"
env_file=".env"
nginx_service="nginx"
timeout=60 # timeout em segundos para cada checagem de serviço saudável
sleep_interval=3 # tempo em segundos para esperar entre tentativas
max_retries=$((timeout / sleep_interval))
# Checando existência dos arquivos
if [ ! -f "$docker_compose_file" ]; then
echo "Arquivo $docker_compose_file não encontrado"
exit 1
fi
if [ ! -f "$env_file" ]; then
echo "Arquivo $env_file não encontrado"
exit 1
fi
#
# Checando se o serviço está saudável
# @param string $1 Nome do serviço do Docker
#
check_health() {
i=1
while [ "$i" -le $max_retries ]; do
status=$(docker compose -f "$docker_compose_file" ps "$1" --status running --format "{{.Health}}")
echo " Healthcheck: $status"
if [ "$status" = "healthy" ]; then
echo "$1 is healthy"
return 0
fi
sleep "$sleep_interval"
i=$((i + 1))
done
return 1
}
# Descobrindo qual serviço (1 ou 2) está ativo
if [ -n "$(docker compose -f "$docker_compose_file" ps "$service1" --status running --quiet)" ]; then
old_service=$service1
new_service=$service2
env_name=$service2_env
elif [ -n "$(docker compose -f "$docker_compose_file" ps "$service2" --status running --quiet)" ]; then
old_service=$service2
new_service=$service1
env_name=$service1_env
else
old_service=""
new_service=$service2
env_name=$service2_env
fi
# Trocando o nome da imagem do Docker no arquivo .env
sed -i "/$env_name=/c\\$env_name=$image" "$env_file"
echo "Iniciando serviço $new_service"
docker compose -f "$docker_compose_file" up --build --remove-orphans --detach "$new_service"
echo "Aguardando até que $new_service esteja saudável.."
if ! check_health "$new_service"; then
echo "$new_service não ficou saudável em $timeout segundos"
docker compose -f "$docker_compose_file" stop --timeout=10 "$new_service"
exit 2
fi
echo "Recarregando configuração do nginx"
docker compose -f "$docker_compose_file" exec --env "APP_HOST=$new_service" "$nginx_service" /docker-entrypoint.d/20-envsubst-on-templates.sh
docker compose -f "$docker_compose_file" exec "$nginx_service" nginx -s reload
if ! check_health "$nginx_service"; then
echo "$new_service não ficou saudável em $timeout segundos"
docker compose -f "$docker_compose_file" stop --timeout=10 "$new_service"
exit 2
fi
echo "Parando container $old_service"
docker compose -f "$docker_compose_file" stop --timeout=10 "$old_service"
echo "Deploy finalizado com sucesso"
Automatize, personalize e execute seus fluxos de trabalho de desenvolvimento do software diretamente no seu repositório com o GitHub Actions. Você pode descobrir, criar e compartilhar ações para realizar qualquer trabalho que desejar, incluindo CI/CD, bem como combinar ações em um fluxo de trabalho completamente personalizado.GitHub Actions
Um Fluxo de trabalho ("workflow") é configurado através de um arquivo
YAML na pasta
.github/workflows
do seu repositório
Ao mesclarmos uma PR, invocamos um endpoint protegido na máquina de destino com o hash do commit
Prós
Contras
Instalamos um agente na máquina de destino que fica consultando a API do GitHub a cada x minutos para verificar se há um novo commit
Prós
Contras
Fazemos o GitHub acessar a máquina via SSH e executar um comando que irá iniciar o processo
Prós
Contras
name: Deploy do Backend
on:
push:
branches: [ "main" ]
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Fazendo login no Registry de Containers
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Configurando Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: type=sha
- name: Faz o build da imagem do Docker
uses: docker/build-push-action@v6
id: build
with:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Executando comandos via SSH
uses: appleboy/ssh-action@v1.2.2
with:
host: ${{ secrets.SSH_HOST }}
port: ${{ secrets.SSH_PORT }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
passphrase: ${{ secrets.SSH_KEY_PASSPHRASE }}
script: docker-compose-update ${{ steps.meta.outputs.tags }}