aula-11: ArgoCD + GitLab Runner para GitOps CI/CD

- ArgoCD via Helm com recursos mínimos (~1Gi)
- GitLab Runner com executor Kubernetes
- Exemplo node-bugado com Dockerfile e .gitlab-ci.yml
- Manifests K8s para repositório GitOps
- README.md da aula-03 (liveness + readiness probes)
This commit is contained in:
Allyson de Paula
2025-12-31 21:19:40 -03:00
parent 07b7ee62d3
commit 8e743f6e69
14 changed files with 1611 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
# =============================================================================
# GitLab CI/CD Pipeline - node-bugado
# =============================================================================
#
# Pipeline GitOps:
# 1. Build: Constrói imagem Docker e faz push para GitLab Registry
# 2. Deploy: Atualiza manifests no repo GitOps (ArgoCD faz sync)
#
# Variáveis necessárias (Settings → CI/CD → Variables):
# - GITOPS_REPO: URL do repositório GitOps (ex: git@git.kube.quest:user/gitops-demo.git)
# - DEPLOY_KEY: Chave SSH privada para push no repo GitOps
#
# =============================================================================
stages:
- build
- deploy
variables:
# Registry do GitLab
REGISTRY: ${CI_REGISTRY}
IMAGE_NAME: ${CI_REGISTRY_IMAGE}
# Para usar registry externo, descomente:
# REGISTRY: registry.kube.quest
# IMAGE_NAME: ${REGISTRY}/${CI_PROJECT_PATH}
# =============================================================================
# BUILD - Construir e publicar imagem Docker
# =============================================================================
build:
stage: build
image: docker:24
services:
- docker:24-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
script:
- echo "Building ${IMAGE_NAME}:${CI_COMMIT_SHA}"
- docker build -t ${IMAGE_NAME}:${CI_COMMIT_SHA} .
- docker tag ${IMAGE_NAME}:${CI_COMMIT_SHA} ${IMAGE_NAME}:latest
- docker push ${IMAGE_NAME}:${CI_COMMIT_SHA}
- docker push ${IMAGE_NAME}:latest
only:
- main
- master
tags:
- kubernetes
- docker
# =============================================================================
# DEPLOY - Atualizar manifests no repositório GitOps
# =============================================================================
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache git openssh-client
# Configurar SSH para o repo GitOps
- mkdir -p ~/.ssh
- echo "${DEPLOY_KEY}" | tr -d '\r' > ~/.ssh/id_ed25519
- chmod 600 ~/.ssh/id_ed25519
- ssh-keyscan -t ed25519 $(echo ${GITOPS_REPO} | sed 's/.*@\([^:]*\).*/\1/') >> ~/.ssh/known_hosts 2>/dev/null || true
# Configurar git
- git config --global user.email "ci@gitlab.local"
- git config --global user.name "GitLab CI"
script:
- echo "Updating GitOps repo with image ${IMAGE_NAME}:${CI_COMMIT_SHA}"
# Clonar repo GitOps
- git clone ${GITOPS_REPO} gitops
- cd gitops
# Atualizar tag da imagem no deployment
- |
if [ -f apps/node-bugado/deployment.yaml ]; then
sed -i "s|image:.*node-bugado.*|image: ${IMAGE_NAME}:${CI_COMMIT_SHA}|g" apps/node-bugado/deployment.yaml
git add apps/node-bugado/deployment.yaml
git commit -m "Deploy node-bugado ${CI_COMMIT_SHA:0:8}
Pipeline: ${CI_PIPELINE_URL}
Commit: ${CI_COMMIT_SHA}
Author: ${CI_COMMIT_AUTHOR}"
git push
echo "GitOps repo updated successfully"
else
echo "WARNING: apps/node-bugado/deployment.yaml not found"
echo "Please create the GitOps structure first"
exit 1
fi
only:
- main
- master
tags:
- kubernetes
when: on_success
needs:
- build
# =============================================================================
# NOTAS
# =============================================================================
#
# Para configurar as variáveis:
#
# 1. GITOPS_REPO:
# - Vá em Settings → CI/CD → Variables
# - Adicione: GITOPS_REPO = git@git.kube.quest:usuario/gitops-demo.git
#
# 2. DEPLOY_KEY:
# - Gere uma chave: ssh-keygen -t ed25519 -f deploy-key -N ''
# - Adicione a chave PÚBLICA no repo GitOps: Settings → Repository → Deploy Keys
# - Marque "Grant write permissions to this key"
# - Adicione a chave PRIVADA como variável: DEPLOY_KEY = <conteúdo de deploy-key>
# - Marque como "Protected" e "Masked"
#
# =============================================================================

View File

@@ -0,0 +1,35 @@
# =============================================================================
# Dockerfile - node-bugado
# =============================================================================
#
# Imagem simples para demonstrar CI/CD com GitLab + ArgoCD.
# A aplicação "trava" após MAX_REQUESTS requisições para simular
# falhas e demonstrar auto-healing do Kubernetes.
#
# Build:
# docker build -t registry.kube.quest/<usuario>/node-bugado:v1 .
#
# =============================================================================
FROM node:24-alpine
LABEL maintainer="workshop"
LABEL description="App que trava para demonstrar liveness probes"
# Metadados OCI
LABEL org.opencontainers.image.source="https://git.kube.quest"
LABEL org.opencontainers.image.title="node-bugado"
WORKDIR /app
# Copiar código da aplicação
COPY app.js .
# Porta da aplicação
EXPOSE 3000
# Usuário não-root
USER node
# Comando de inicialização
CMD ["node", "app.js"]

View File

@@ -0,0 +1,35 @@
const http = require("http");
const MAX_REQUESTS = Number(process.env.MAX_REQUESTS || 3);
let requestCount = 0;
console.log("MAX_REQUESTS =", MAX_REQUESTS);
const server = http.createServer((req, res) => {
if (req.url === "/health") {
if (requestCount > MAX_REQUESTS) {
// app travado, mas processo vivo
return;
}
res.writeHead(200);
res.end(`ok`);
return;
}
requestCount++;
console.log("request", requestCount);
if (requestCount > MAX_REQUESTS) {
console.log(`App travado apos ${MAX_REQUESTS} requests`);
return;
}
res.writeHead(200);
res.end(`Req -> ${requestCount}/${MAX_REQUESTS}`);
});
server.listen(3000, () => {
console.log("App rodando na porta 3000");
});

View File

@@ -0,0 +1,17 @@
# =============================================================================
# ConfigMap - node-bugado
# =============================================================================
#
# Configuração da aplicação.
# MAX_REQUESTS define quantas requisições antes de "travar".
#
# =============================================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: node-bugado-config
labels:
app: node-bugado
data:
MAX_REQUESTS: "5"

View File

@@ -0,0 +1,76 @@
# =============================================================================
# Deployment - node-bugado
# =============================================================================
#
# Deployment com liveness e readiness probes.
# A imagem é atualizada automaticamente pelo pipeline GitLab CI.
#
# =============================================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-bugado
labels:
app: node-bugado
spec:
replicas: 2
selector:
matchLabels:
app: node-bugado
template:
metadata:
labels:
app: node-bugado
spec:
terminationGracePeriodSeconds: 5
containers:
- name: node-bugado
# IMPORTANTE: Esta linha é atualizada automaticamente pelo GitLab CI
image: registry.kube.quest/workshop/node-bugado:latest
ports:
- containerPort: 3000
name: http
# Variáveis de ambiente via ConfigMap
env:
- name: MAX_REQUESTS
valueFrom:
configMapKeyRef:
name: node-bugado-config
key: MAX_REQUESTS
# Recursos
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
# Liveness probe - detecta quando a app trava
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 3
failureThreshold: 2
timeoutSeconds: 2
# Readiness probe - remove do service enquanto não está pronta
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 2
periodSeconds: 2
failureThreshold: 1
timeoutSeconds: 1
# Pod security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000

View File

@@ -0,0 +1,38 @@
# =============================================================================
# Ingress - node-bugado
# =============================================================================
#
# Ingress NGINX para expor a aplicação externamente.
# Configure o hostname de acordo com seu domínio.
#
# =============================================================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: node-bugado
labels:
app: node-bugado
annotations:
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
# Descomente para Let's Encrypt:
# cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
rules:
- host: bugado.kube.quest
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: node-bugado
port:
number: 80
# Descomente para TLS:
# tls:
# - hosts:
# - bugado.kube.quest
# secretName: node-bugado-tls

View File

@@ -0,0 +1,24 @@
# =============================================================================
# Service - node-bugado
# =============================================================================
#
# Service ClusterIP para expor a aplicação internamente.
# Use com Ingress para acesso externo.
#
# =============================================================================
apiVersion: v1
kind: Service
metadata:
name: node-bugado
labels:
app: node-bugado
spec:
type: ClusterIP
selector:
app: node-bugado
ports:
- name: http
port: 80
targetPort: 3000
protocol: TCP