diff --git a/CLAUDE.md b/CLAUDE.md index 8397569..554d5a6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,6 +18,7 @@ This is a workshop repository for teaching Docker and Kubernetes concepts, speci - **aula-08/**: OpenTofu - provisioning HA Talos Kubernetes cluster on Hetzner Cloud with CCM and LoadBalancer - **aula-09/**: Kubernetes lesson - n8n deployment via Helm (Hetzner Cloud with CSI Driver and multi-tenant support) - **aula-10/**: Kubernetes lesson - GitLab deployment via Helm with Container Registry and SSH +- **aula-11/**: Kubernetes lesson - ArgoCD + GitLab Runner for GitOps CI/CD pipelines ## Running the Examples @@ -141,6 +142,27 @@ Prerequisites: To remove: `./cleanup.sh` +### Aula 11 (Kubernetes - ArgoCD + GitLab Runner) +```bash +cd aula-11 +export KUBECONFIG=/path/to/aula-08/kubeconfig +./setup.sh +``` +Implements GitOps CI/CD pipeline with: +- GitLab Runner (Kubernetes executor) for CI pipelines +- ArgoCD for declarative GitOps continuous deployment +- Integration with GitLab self-hosted (aula-10) +- Example application with Dockerfile and GitLab CI pipeline + +Pipeline flow: git push → GitLab CI (build) → Registry (push) → GitOps repo (update) → ArgoCD (sync) → Kubernetes (deploy) + +Prerequisites: +- Completed Aula 10 (GitLab) +- NGINX Ingress with LoadBalancer +- DNS configured for ArgoCD hostname + +To remove: `./cleanup.sh` + ## App Behavior The Node.js app (`app.js`) is intentionally designed to: diff --git a/aula-03/README.md b/aula-03/README.md new file mode 100644 index 0000000..bfeb55a --- /dev/null +++ b/aula-03/README.md @@ -0,0 +1,141 @@ +# Aula 03 - Alta Disponibilidade com Replicas e Readiness Probe + +Evolução da aula-02 adicionando múltiplas réplicas e readiness probe para zero-downtime. + +## Conceitos + +### Liveness vs Readiness Probe + +| Probe | Função | Quando Falha | +|-------|--------|--------------| +| **Liveness** | "O container está vivo?" | Reinicia o container | +| **Readiness** | "O container está pronto para receber tráfego?" | Remove do Service (sem reiniciar) | + +### Por que 5 Réplicas? + +``` +Cenário: MAX_REQUESTS=3, 5 réplicas + +Tempo 0s: [Pod1: OK] [Pod2: OK] [Pod3: OK] [Pod4: OK] [Pod5: OK] + ↑ Service distribui tráfego entre todos + +Tempo 5s: [Pod1: HANG] [Pod2: OK] [Pod3: OK] [Pod4: OK] [Pod5: OK] + ↑ readinessProbe falha → removido do Service + ↑ livenessProbe falha → reiniciando... + +Tempo 7s: [Pod1: RESTART] [Pod2: OK] [Pod3: OK] [Pod4: OK] [Pod5: OK] + ↑ Pod1 reiniciou, readiness OK → volta ao Service + +Resultado: Usuário NUNCA vê erro 503 +``` + +## Arquitetura + +``` + LoadBalancer (:3000) + │ + ┌──────────────┼──────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ Pod 1 │ │ Pod 2 │ │ Pod 3 │ ... (5 réplicas) + │ :3000 │ │ :3000 │ │ :3000 │ + └────┬────┘ └────┬────┘ └────┬────┘ + │ │ │ + readinessProbe readinessProbe readinessProbe + livenessProbe livenessProbe livenessProbe +``` + +## Execução + +```bash +cd aula-03 + +# Aplicar recursos +kubectl apply -f configmap.yaml +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml + +# Verificar pods +kubectl get pods -w + +# Acessar a aplicação +# Docker Desktop: http://localhost:3000 +# Minikube: minikube service node-bugado --url +``` + +## Configuração das Probes + +```yaml +# Readiness - remove do Service rapidamente +readinessProbe: + httpGet: + path: /health + port: 3000 + periodSeconds: 1 # Verifica a cada 1 segundo + failureThreshold: 1 # 1 falha = remove do Service + +# Liveness - reinicia o container +livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 2 # Espera app iniciar + periodSeconds: 1 # Verifica a cada 1 segundo + failureThreshold: 2 # 2 falhas = reinicia +``` + +## Teste de Resiliência + +```bash +# Terminal 1: Monitorar pods +kubectl get pods -w + +# Terminal 2: Fazer requisições contínuas +while true; do curl -s http://localhost:3000 && echo; sleep 0.5; done + +# Observar: +# 1. Pods recebem requisições e eventualmente "travam" +# 2. readinessProbe remove pod do Service (sem interromper requests) +# 3. livenessProbe reinicia o pod +# 4. Pod volta ao Service quando ready +# 5. Usuário NÃO vê erros durante todo o processo +``` + +## Diferença da Aula 02 + +| Aspecto | Aula 02 | Aula 03 | +|---------|---------|---------| +| Réplicas | 1 | 5 | +| Probes | Liveness apenas | Liveness + Readiness | +| Downtime | Sim (durante restart) | Não (outras réplicas atendem) | +| Service | NodePort | LoadBalancer | + +## Fluxo de Eventos + +1. **Pod recebe requisições** → contador incrementa +2. **MAX_REQUESTS atingido** → app para de responder +3. **readinessProbe falha (1s)** → pod removido do Service +4. **Tráfego redirecionado** → outros pods atendem +5. **livenessProbe falha (2s)** → Kubernetes reinicia container +6. **Pod reinicia** → contador zerado, app responde +7. **readinessProbe OK** → pod volta ao Service + +## Lições + +1. **Réplicas = Resiliência**: Múltiplas cópias garantem disponibilidade +2. **Readiness ≠ Liveness**: Propósitos diferentes, ambos importantes +3. **Graceful Degradation**: Sistema continua funcionando com capacidade reduzida +4. **Self-Healing**: Kubernetes detecta e corrige problemas automaticamente + +## Cleanup + +```bash +./cleanup.sh +# ou +kubectl delete -f . +``` + +## Próxima Aula + +**Aula 04**: NGINX Ingress com Keep Request (Lua) para zero-downtime ainda mais robusto, segurando requisições durante a janela de restart. diff --git a/aula-11/README.md b/aula-11/README.md new file mode 100644 index 0000000..7f6c3b4 --- /dev/null +++ b/aula-11/README.md @@ -0,0 +1,328 @@ +# Aula 11 - ArgoCD + GitLab Runner (GitOps) + +Deploy de ArgoCD e GitLab Runner para pipeline CI/CD completo com GitOps. + +## Arquitetura + +``` +┌─────────────────────────────────────────────────────────────┐ +│ GitLab (aula-10) │ +│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │ +│ │ git push │───►│ GitLab CI │───►│ Registry │ │ +│ │ (código) │ │ (Runner K8s)│ │ (imagem:tag) │ │ +│ └─────────────┘ └──────┬───────┘ └───────────────┘ │ +└────────────────────────────┼────────────────────────────────┘ + │ atualiza manifests + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Repositório GitOps │ +│ apps/node-bugado/ │ +│ ├── deployment.yaml (image: registry.../node-bugado:sha) │ +│ ├── service.yaml │ +│ └── configmap.yaml │ +└─────────────────────────────────────────────────────────────┘ + │ + │ sync automático + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ ArgoCD │ +│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │ +│ │ Application │───►│ Sync │───►│ Kubernetes │ │ +│ │ CRD │ │ Controller │ │ (deploy) │ │ +│ └─────────────┘ └──────────────┘ └───────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Pré-requisitos + +1. **Cluster Talos na Hetzner** (aula-08) com: + - NGINX Ingress Controller com LoadBalancer + - Hetzner CSI Driver +2. **GitLab instalado** (aula-10) +3. **kubectl** e **helm** instalados + +## Contexto do Cluster + +```bash +# Usar kubeconfig da aula-08 +export KUBECONFIG=$(pwd)/../aula-08/kubeconfig +kubectl cluster-info +``` + +## Instalação + +```bash +cd aula-11 + +# Executar setup interativo +chmod +x setup.sh +./setup.sh +``` + +O script instala: +1. **GitLab Runner** - Executor Kubernetes para pipelines CI +2. **ArgoCD** - GitOps CD para Kubernetes +3. Configura integração SSH com GitLab + +## Componentes Instalados + +| Componente | Namespace | Recursos | Função | +|------------|-----------|----------|--------| +| ArgoCD Server | argocd | 256Mi/512Mi | UI + API | +| ArgoCD Repo Server | argocd | 256Mi/512Mi | Git clone/sync | +| ArgoCD Controller | argocd | 256Mi/512Mi | Reconciliation | +| ArgoCD Redis | argocd | 64Mi/128Mi | Cache | +| GitLab Runner | gitlab | 128Mi/256Mi | CI jobs como pods | +| **Total** | | ~1.2Gi | | + +## Acesso + +### ArgoCD + +``` +URL: https://argocd.{domain} +Username: admin +Senha: kubectl get secret argocd-initial-admin-secret -n argocd \ + -o jsonpath='{.data.password}' | base64 -d +``` + +### GitLab Runner + +Verificar status em: `https://{gitlab-host}/admin/runners` + +```bash +# Ver pods do runner +kubectl get pods -n gitlab -l app=gitlab-runner + +# Ver logs +kubectl logs -n gitlab -l app=gitlab-runner -f +``` + +## Configurar Pipeline GitOps + +### 1. Criar Repositório GitOps no GitLab + +Crie um repositório `gitops-demo` com a estrutura: + +``` +gitops-demo/ +└── apps/ + └── node-bugado/ + ├── configmap.yaml + ├── deployment.yaml + ├── service.yaml + └── ingress.yaml +``` + +Use os arquivos de exemplo em `node-bugado/k8s/`. + +### 2. Configurar Deploy Key + +```bash +# Gerar par de chaves +ssh-keygen -t ed25519 -f argocd-deploy-key -N '' + +# Adicionar chave pública no GitLab: +# Settings → Repository → Deploy Keys +# Marcar "Grant write permissions" +``` + +### 3. Conectar Repositório no ArgoCD + +Via UI: +1. Acesse https://argocd.{domain} +2. Settings → Repositories → Connect Repo +3. Method: SSH +4. URL: `git@git.{domain}:{usuario}/gitops-demo.git` +5. SSH private key: (conteúdo de argocd-deploy-key) + +Ou via CLI: +```bash +argocd repo add git@git.kube.quest:usuario/gitops-demo.git \ + --ssh-private-key-path argocd-deploy-key +``` + +### 4. Criar ArgoCD Application + +```bash +kubectl apply -f - << 'EOF' +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: node-bugado + namespace: argocd +spec: + project: default + source: + repoURL: git@git.kube.quest:usuario/gitops-demo.git + targetRevision: HEAD + path: apps/node-bugado + destination: + server: https://kubernetes.default.svc + namespace: demo + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +EOF +``` + +### 5. Configurar Pipeline no Repositório da App + +No repositório `node-bugado`: + +1. Copie `.gitlab-ci.yml` de `node-bugado/.gitlab-ci.yml` +2. Configure variáveis em Settings → CI/CD → Variables: + - `GITOPS_REPO`: `git@git.kube.quest:usuario/gitops-demo.git` + - `DEPLOY_KEY`: Chave SSH privada (com write access ao repo gitops) + +## Fluxo de Deploy + +1. **Desenvolvedor** faz push no repositório `node-bugado` +2. **GitLab CI** dispara pipeline: + - Build: Constrói imagem Docker + - Push: Envia para GitLab Registry + - Deploy: Atualiza `deployment.yaml` no repo GitOps +3. **ArgoCD** detecta mudança no repo GitOps +4. **ArgoCD** sincroniza com cluster Kubernetes +5. **Kubernetes** faz rolling update dos pods + +## GitLab Runner - Executor Kubernetes + +O runner usa executor `kubernetes`, onde cada job CI: + +- Roda como um **pod efêmero** no cluster +- Tem acesso a **Docker-in-Docker** para builds +- É automaticamente **limpo** após conclusão +- **Escala** conforme demanda de jobs + +```yaml +# gitlab-runner-values.yaml +runners: + executor: kubernetes + privileged: true # Necessário para Docker-in-Docker + namespace: gitlab +``` + +### Por que Docker-in-Docker? + +Para construir imagens Docker dentro de um container (o job do CI), precisamos: +1. **Privileged mode**: Acesso ao kernel para criar containers +2. **Docker daemon**: Serviço Docker rodando no job + +Alternativas (mais seguras, mas mais complexas): +- **Kaniko**: Build sem Docker daemon +- **Buildah**: Build rootless + +## Troubleshooting + +### ArgoCD não sincroniza + +```bash +# Verificar status da Application +kubectl get applications -n argocd + +# Ver detalhes +kubectl describe application node-bugado -n argocd + +# Ver logs do controller +kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller +``` + +### Runner não aparece no GitLab + +```bash +# Verificar pod do runner +kubectl get pods -n gitlab -l app=gitlab-runner + +# Ver logs +kubectl logs -n gitlab -l app=gitlab-runner + +# Verificar registration token +kubectl get secret gitlab-gitlab-runner-secret -n gitlab -o yaml +``` + +### Jobs CI falham + +```bash +# Ver pods dos jobs +kubectl get pods -n gitlab + +# Ver logs de um job específico +kubectl logs -n gitlab runner-xxxxx-project-xxx-concurrent-xxx +``` + +### Erro SSH ao conectar repositório + +```bash +# Verificar known hosts +kubectl get configmap argocd-ssh-known-hosts-cm -n argocd -o yaml + +# Adicionar manualmente +ssh-keyscan git.kube.quest | kubectl create configmap argocd-ssh-known-hosts-cm \ + --from-file=ssh_known_hosts=/dev/stdin -n argocd --dry-run=client -o yaml | kubectl apply -f - +``` + +## Comandos Úteis + +```bash +# ArgoCD +kubectl get applications -n argocd +kubectl get pods -n argocd +argocd app list +argocd app sync node-bugado + +# GitLab Runner +kubectl get pods -n gitlab -l app=gitlab-runner +kubectl logs -n gitlab -l app=gitlab-runner -f + +# Ver todos os recursos do demo +kubectl get all -n demo + +# Forçar re-sync +argocd app sync node-bugado --force + +# Ver diff antes de sync +argocd app diff node-bugado +``` + +## Lições do Workshop + +1. **GitOps**: Git como fonte única de verdade para estado do cluster +2. **Separação CI/CD**: GitLab CI = build, ArgoCD = deploy +3. **Runner Kubernetes**: Jobs como pods efêmeros e escaláveis +4. **Docker-in-Docker**: Build de imagens em containers privilegiados +5. **Auditoria**: Histórico de deploys = histórico Git +6. **Self-Heal**: ArgoCD corrige drift automaticamente +7. **Segurança**: Deploy Keys com permissão mínima + +## Cleanup + +```bash +./cleanup.sh +``` + +Remove ArgoCD e GitLab Runner. Não remove GitLab ou infraestrutura base. + +## Custos + +| Recurso | Custo/mês | +|---------|-----------| +| ArgoCD + Runner (~1.2Gi) | Usa workers existentes | +| **Total Adicional** | ~$0 | + +## Próximos Passos + +- **Aula 12**: eStargz + Lazy Pulling + - Converter imagens para formato eStargz + - Demonstrar startup mais rápido com lazy pulling + - Medir diferença de performance + +## Referências + +- [ArgoCD Docs](https://argo-cd.readthedocs.io/en/stable/) +- [ArgoCD Helm Chart](https://github.com/argoproj/argo-helm) +- [GitLab Runner Kubernetes Executor](https://docs.gitlab.com/runner/executors/kubernetes.html) +- [GitOps with GitLab + ArgoCD](https://medium.com/@andrew.kaczynski/gitops-in-kubernetes-argo-cd-and-gitlab-ci-cd-5828c8eb34d6) diff --git a/aula-11/argocd-values.yaml b/aula-11/argocd-values.yaml new file mode 100644 index 0000000..5eb6b87 --- /dev/null +++ b/aula-11/argocd-values.yaml @@ -0,0 +1,119 @@ +# ============================================================================= +# ArgoCD Helm Chart - Configuração para Hetzner CAX11 +# ============================================================================= +# +# Recursos mínimos otimizados para cluster pequeno (~1Gi total). +# Desabilita HA e componentes não essenciais. +# +# Valores dinâmicos (configurados via --set no setup.sh): +# - server.ingress.hosts[0] +# - server.ingress.tls[0].hosts[0] +# - server.ingress.annotations.cert-manager.io/cluster-issuer (se Let's Encrypt) +# +# ============================================================================= + +global: + # Domínio base (será sobrescrito via --set) + domain: argocd.kube.quest + +# ============================================================================= +# ARGOCD SERVER (UI + API) +# ============================================================================= +server: + replicas: 1 + + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 512Mi + cpu: 500m + + ingress: + enabled: true + ingressClassName: nginx + # Não usar ssl-passthrough nem backend-protocol HTTPS + # O ArgoCD roda em modo insecure (HTTP na porta 80) + # TLS é terminado no NGINX Ingress (ou CloudFlare) + # hosts e tls configurados via --set + + # Modo insecure - TLS termina no ingress/proxy, não no server + extraArgs: + - --insecure + +# ============================================================================= +# ARGOCD REPO SERVER (Git operations) +# ============================================================================= +repoServer: + replicas: 1 + + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 512Mi + cpu: 500m + +# ============================================================================= +# ARGOCD APPLICATION CONTROLLER (Reconciliation) +# ============================================================================= +controller: + replicas: 1 + + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 512Mi + cpu: 500m + +# ============================================================================= +# REDIS (Cache) +# ============================================================================= +redis: + resources: + requests: + memory: 64Mi + cpu: 50m + limits: + memory: 128Mi + cpu: 100m + +# ============================================================================= +# COMPONENTES DESABILITADOS +# ============================================================================= + +# Redis HA - não necessário para cluster pequeno +redis-ha: + enabled: false + +# Dex (SSO) - usar autenticação local +dex: + enabled: false + +# ApplicationSet Controller - pode habilitar depois se necessário +applicationSet: + enabled: false + +# Notifications Controller - não essencial +notifications: + enabled: false + +# ============================================================================= +# CONFIGURAÇÕES GLOBAIS +# ============================================================================= +configs: + params: + # Timeout para operações Git + server.repo.server.timeout.seconds: "60" + # Intervalo de reconciliação (3 minutos) + timeout.reconciliation: "180s" + + cm: + # Permitir acesso via HTTP (terminação TLS no ingress) + server.insecure: "true" + # Timeout de sessão admin + admin.enabled: "true" diff --git a/aula-11/cleanup.sh b/aula-11/cleanup.sh new file mode 100755 index 0000000..bcb1df3 --- /dev/null +++ b/aula-11/cleanup.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# ============================================================================= +# Aula 11 - Cleanup +# ============================================================================= +# +# Remove ArgoCD e GitLab Runner instalados pelo setup.sh. +# NÃO remove a infraestrutura base (GitLab, NGINX Ingress, etc). +# +# ============================================================================= + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_success() { echo -e "${GREEN}[OK]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +echo "" +echo "==========================================" +echo " Removendo ArgoCD e GitLab Runner" +echo "==========================================" +echo "" + +# Remover ArgoCD Applications primeiro +log_info "Removendo ArgoCD Applications..." +kubectl delete applications --all -n argocd 2>/dev/null || true + +# Remover ArgoCD +log_info "Removendo ArgoCD..." +if helm status argocd -n argocd &> /dev/null; then + helm uninstall argocd -n argocd --wait + log_success "ArgoCD removido" +else + log_warn "ArgoCD não estava instalado" +fi + +# Remover namespace argocd +log_info "Removendo namespace argocd..." +kubectl delete namespace argocd --timeout=60s 2>/dev/null || true + +# Remover GitLab Runner +log_info "Removendo GitLab Runner..." +if helm status gitlab-runner -n gitlab &> /dev/null; then + helm uninstall gitlab-runner -n gitlab --wait + log_success "GitLab Runner removido" +else + log_warn "GitLab Runner não estava instalado" +fi + +# Limpar secrets residuais +log_info "Limpando secrets residuais..." +kubectl delete secret argocd-ssh-known-hosts-cm -n argocd 2>/dev/null || true + +# Remover arquivo .env local +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [[ -f "${SCRIPT_DIR}/.env" ]]; then + rm "${SCRIPT_DIR}/.env" + log_info "Arquivo .env removido" +fi + +echo "" +log_success "Cleanup concluído!" +echo "" +echo "Nota: GitLab, NGINX Ingress e infraestrutura base foram mantidos." +echo "Para remover o GitLab, execute: ../aula-10/cleanup.sh" +echo "" diff --git a/aula-11/gitlab-runner-values.yaml b/aula-11/gitlab-runner-values.yaml new file mode 100644 index 0000000..dd79686 --- /dev/null +++ b/aula-11/gitlab-runner-values.yaml @@ -0,0 +1,132 @@ +# ============================================================================= +# GitLab Runner Helm Chart - Executor Kubernetes +# ============================================================================= +# +# Configura GitLab Runner para executar jobs como pods no Kubernetes. +# Suporta Docker-in-Docker para build de imagens. +# +# Valores dinâmicos (configurados via --set no setup.sh): +# - gitlabUrl +# - runnerToken (novo método) ou runnerRegistrationToken (legacy) +# +# ============================================================================= + +# Número máximo de jobs simultâneos +concurrent: 2 + +# Intervalo de check por novos jobs (segundos) +checkInterval: 30 + +# Intervalo de heartbeat (segundos) +heartbeatInterval: 30 + +# ============================================================================= +# CONFIGURAÇÃO DO RUNNER +# ============================================================================= +runners: + # Executor: kubernetes (jobs rodam como pods) + executor: kubernetes + + # Privileged mode necessário para Docker-in-Docker + privileged: true + + # Namespace onde os jobs serão executados + namespace: gitlab + + # Tags para identificar o runner + tags: "kubernetes,docker,hetzner" + + # Rodar jobs sem tag também + runUntagged: true + + # Proteger branches protegidas + protected: false + + # Imagem padrão para jobs + image: alpine:latest + + # Helper image (para git clone, artifacts, etc) + helper: + image: gitlab/gitlab-runner-helper:alpine-latest + + # Configuração TOML adicional + config: | + [[runners]] + [runners.kubernetes] + image = "alpine:latest" + privileged = true + + # Recursos para pods de job + cpu_request = "100m" + cpu_limit = "500m" + memory_request = "256Mi" + memory_limit = "512Mi" + + # Timeout para pods + poll_timeout = 600 + + # Pull policy + pull_policy = ["if-not-present"] + + # Volume para Docker certs (DinD) + [[runners.kubernetes.volumes.empty_dir]] + name = "docker-certs" + mount_path = "/certs/client" + medium = "Memory" + + # Volume para cache de build + [[runners.kubernetes.volumes.empty_dir]] + name = "build-cache" + mount_path = "/cache" + medium = "" + +# ============================================================================= +# RECURSOS DO RUNNER (manager pod) +# ============================================================================= +resources: + requests: + memory: 128Mi + cpu: 50m + limits: + memory: 256Mi + cpu: 200m + +# ============================================================================= +# RBAC +# ============================================================================= +rbac: + create: true + # Permissões para criar pods, secrets, configmaps + rules: + - apiGroups: [""] + resources: ["pods", "pods/exec", "secrets", "configmaps"] + verbs: ["get", "list", "watch", "create", "patch", "update", "delete"] + - apiGroups: [""] + resources: ["pods/attach", "pods/log"] + verbs: ["get", "create"] + +# ============================================================================= +# SERVICE ACCOUNT +# ============================================================================= +serviceAccount: + create: true + name: gitlab-runner + +# ============================================================================= +# MÉTRICAS (opcional) +# ============================================================================= +metrics: + enabled: false + +# ============================================================================= +# POD SECURITY +# ============================================================================= +podSecurityContext: + runAsNonRoot: true + runAsUser: 100 + +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: ["ALL"] diff --git a/aula-11/node-bugado/.gitlab-ci.yml b/aula-11/node-bugado/.gitlab-ci.yml new file mode 100644 index 0000000..e220b84 --- /dev/null +++ b/aula-11/node-bugado/.gitlab-ci.yml @@ -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 = +# - Marque como "Protected" e "Masked" +# +# ============================================================================= diff --git a/aula-11/node-bugado/Dockerfile b/aula-11/node-bugado/Dockerfile new file mode 100644 index 0000000..4564436 --- /dev/null +++ b/aula-11/node-bugado/Dockerfile @@ -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//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"] diff --git a/aula-11/node-bugado/app.js b/aula-11/node-bugado/app.js new file mode 100644 index 0000000..17ceaf6 --- /dev/null +++ b/aula-11/node-bugado/app.js @@ -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"); +}); diff --git a/aula-11/node-bugado/k8s/configmap.yaml b/aula-11/node-bugado/k8s/configmap.yaml new file mode 100644 index 0000000..191f671 --- /dev/null +++ b/aula-11/node-bugado/k8s/configmap.yaml @@ -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" diff --git a/aula-11/node-bugado/k8s/deployment.yaml b/aula-11/node-bugado/k8s/deployment.yaml new file mode 100644 index 0000000..3ebeb61 --- /dev/null +++ b/aula-11/node-bugado/k8s/deployment.yaml @@ -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 diff --git a/aula-11/node-bugado/k8s/ingress.yaml b/aula-11/node-bugado/k8s/ingress.yaml new file mode 100644 index 0000000..9d05a5c --- /dev/null +++ b/aula-11/node-bugado/k8s/ingress.yaml @@ -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 diff --git a/aula-11/node-bugado/k8s/service.yaml b/aula-11/node-bugado/k8s/service.yaml new file mode 100644 index 0000000..da359cf --- /dev/null +++ b/aula-11/node-bugado/k8s/service.yaml @@ -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 diff --git a/aula-11/setup.sh b/aula-11/setup.sh new file mode 100755 index 0000000..4bb1ed4 --- /dev/null +++ b/aula-11/setup.sh @@ -0,0 +1,456 @@ +#!/bin/bash +# ============================================================================= +# Aula 11 - ArgoCD + GitLab Runner (GitOps) +# ============================================================================= +# +# Este script instala: +# 1. GitLab Runner (executor kubernetes) para CI +# 2. ArgoCD para CD declarativo (GitOps) +# 3. Integração com GitLab self-hosted (aula-10) +# +# Pré-requisitos: +# - Cluster Kubernetes (aula-08) +# - GitLab instalado (aula-10) +# - NGINX Ingress Controller +# - kubectl e helm instalados +# +# ============================================================================= + +set -e + +# Cores para output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Funções de log +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_success() { echo -e "${GREEN}[OK]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# Diretório do script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ENV_FILE="${SCRIPT_DIR}/.env" + +# ============================================================================= +# VERIFICAR PRÉ-REQUISITOS +# ============================================================================= + +log_info "Verificando pré-requisitos..." + +# Verificar kubectl +if ! command -v kubectl &> /dev/null; then + log_error "kubectl não encontrado. Instale com: brew install kubectl" + exit 1 +fi + +# Verificar helm +if ! command -v helm &> /dev/null; then + log_error "helm não encontrado. Instale com: brew install helm" + exit 1 +fi + +# Verificar conexão com cluster +if ! kubectl cluster-info &> /dev/null; then + log_error "Não foi possível conectar ao cluster Kubernetes" + log_info "Verifique se KUBECONFIG está configurado corretamente" + log_info "Exemplo: export KUBECONFIG=\$(pwd)/../aula-08/kubeconfig" + exit 1 +fi + +# Verificar se GitLab está instalado +if ! kubectl get namespace gitlab &> /dev/null; then + log_error "Namespace 'gitlab' não encontrado" + log_info "Execute primeiro a aula-10 para instalar o GitLab" + exit 1 +fi + +# Verificar NGINX Ingress +if ! kubectl get ingressclass nginx &> /dev/null; then + log_error "NGINX Ingress Controller não encontrado" + log_info "Execute o script de instalação do NGINX Ingress na aula-08" + exit 1 +fi + +log_success "Pré-requisitos verificados" + +# ============================================================================= +# CARREGAR CONFIGURAÇÃO EXISTENTE +# ============================================================================= + +# Carregar configuração local PRIMEIRO (se existir) +if [[ -f "$ENV_FILE" ]]; then + log_info "Carregando configuração local..." + source "$ENV_FILE" +fi + +# Se não tiver configuração local, tentar herdar da aula-10 +if [[ -z "$GITLAB_HOST" ]]; then + AULA10_ENV="${SCRIPT_DIR}/../aula-10/.env" + if [[ -f "$AULA10_ENV" ]]; then + log_info "Herdando configuração da aula-10..." + source "$AULA10_ENV" + GITLAB_HOST="${GITLAB_HOST:-}" + DOMAIN="${DOMAIN:-}" + USE_CLOUDFLARE="${USE_CLOUDFLARE:-false}" + USE_LETSENCRYPT="${USE_LETSENCRYPT:-false}" + LETSENCRYPT_EMAIL="${LETSENCRYPT_EMAIL:-}" + fi +fi + +# ============================================================================= +# COLETAR CONFIGURAÇÃO +# ============================================================================= + +echo "" +echo "==========================================" +echo " Configuração do ArgoCD + GitLab CI" +echo "==========================================" +echo "" + +# GitLab Host +if [[ -z "$GITLAB_HOST" ]]; then + read -p "Hostname do GitLab (ex: git.kube.quest): " GITLAB_HOST +fi +log_info "GitLab: https://${GITLAB_HOST}" + +# Extrair domínio base +if [[ -z "$DOMAIN" ]]; then + DOMAIN=$(echo "$GITLAB_HOST" | sed 's/^[^.]*\.//') +fi + +# ArgoCD Host +if [[ -z "$ARGOCD_HOST" ]]; then + DEFAULT_ARGOCD="argocd.${DOMAIN}" + read -p "Hostname do ArgoCD [${DEFAULT_ARGOCD}]: " ARGOCD_HOST + ARGOCD_HOST="${ARGOCD_HOST:-$DEFAULT_ARGOCD}" +fi +log_info "ArgoCD: https://${ARGOCD_HOST}" + +# TLS (herdar da aula-10 ou perguntar) +if [[ "$USE_CLOUDFLARE" != "true" && "$USE_LETSENCRYPT" != "true" ]]; then + echo "" + echo "Configuração de TLS:" + echo " 1) CloudFlare (proxy ativo - TLS na borda)" + echo " 2) Let's Encrypt (cert-manager)" + echo " 3) HTTP apenas (desenvolvimento)" + read -p "Escolha [1-3]: " TLS_CHOICE + + case $TLS_CHOICE in + 1) + USE_CLOUDFLARE=true + USE_LETSENCRYPT=false + ;; + 2) + USE_CLOUDFLARE=false + USE_LETSENCRYPT=true + if [[ -z "$LETSENCRYPT_EMAIL" ]]; then + read -p "Email para Let's Encrypt: " LETSENCRYPT_EMAIL + fi + ;; + *) + USE_CLOUDFLARE=false + USE_LETSENCRYPT=false + ;; + esac +fi + +# Salvar configuração +cat > "$ENV_FILE" << EOF +# Configuração gerada pelo setup.sh +# $(date) +GITLAB_HOST=${GITLAB_HOST} +ARGOCD_HOST=${ARGOCD_HOST} +DOMAIN=${DOMAIN} +USE_CLOUDFLARE=${USE_CLOUDFLARE} +USE_LETSENCRYPT=${USE_LETSENCRYPT} +LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL} +EOF + +log_success "Configuração salva em ${ENV_FILE}" + +# ============================================================================= +# INSTALAR GITLAB RUNNER +# ============================================================================= + +echo "" +log_info "=== Instalando GitLab Runner ===" + +# Adicionar repositório Helm +helm repo add gitlab https://charts.gitlab.io 2>/dev/null || true +helm repo update + +# Verificar se já está instalado +if helm status gitlab-runner -n gitlab &> /dev/null; then + log_warn "GitLab Runner já instalado, fazendo upgrade..." + HELM_CMD="upgrade" +else + HELM_CMD="install" +fi + +# Obter runner registration token +log_info "Obtendo token de registro do GitLab Runner..." + +# Tentar obter o token do secret +RUNNER_TOKEN="" +if kubectl get secret gitlab-gitlab-runner-secret -n gitlab &> /dev/null; then + RUNNER_TOKEN=$(kubectl get secret gitlab-gitlab-runner-secret -n gitlab -o jsonpath='{.data.runner-registration-token}' 2>/dev/null | base64 -d 2>/dev/null || echo "") +fi + +if [[ -z "$RUNNER_TOKEN" ]]; then + log_warn "Token de registro não encontrado automaticamente" + echo "" + echo "Para obter o token:" + echo " 1. Acesse https://${GITLAB_HOST}/admin/runners" + echo " 2. Clique em 'New instance runner'" + echo " 3. Copie o registration token" + echo "" + read -p "Cole o registration token: " RUNNER_TOKEN +fi + +if [[ -z "$RUNNER_TOKEN" ]]; then + log_error "Token de registro é obrigatório" + exit 1 +fi + +# Instalar GitLab Runner +log_info "Instalando GitLab Runner via Helm..." +helm ${HELM_CMD} gitlab-runner gitlab/gitlab-runner \ + --namespace gitlab \ + -f "${SCRIPT_DIR}/gitlab-runner-values.yaml" \ + --set gitlabUrl="https://${GITLAB_HOST}" \ + --set runnerRegistrationToken="${RUNNER_TOKEN}" \ + --wait --timeout 5m + +log_success "GitLab Runner instalado" + +# Verificar pods +log_info "Verificando pods do GitLab Runner..." +kubectl get pods -n gitlab -l app=gitlab-runner + +# ============================================================================= +# INSTALAR CERT-MANAGER (se Let's Encrypt) +# ============================================================================= + +if [[ "$USE_LETSENCRYPT" == "true" ]]; then + echo "" + log_info "=== Verificando cert-manager ===" + + if ! kubectl get namespace cert-manager &> /dev/null; then + log_info "Instalando cert-manager..." + helm repo add jetstack https://charts.jetstack.io 2>/dev/null || true + helm repo update + + helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true \ + --wait --timeout 5m + + log_success "cert-manager instalado" + else + log_success "cert-manager já instalado" + fi + + # Criar ClusterIssuer se não existir + if ! kubectl get clusterissuer letsencrypt-prod &> /dev/null; then + log_info "Criando ClusterIssuer letsencrypt-prod..." + kubectl apply -f - << EOF +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: ${LETSENCRYPT_EMAIL} + privateKeySecretRef: + name: letsencrypt-prod + solvers: + - http01: + ingress: + class: nginx +EOF + log_success "ClusterIssuer criado" + fi +fi + +# ============================================================================= +# INSTALAR ARGOCD +# ============================================================================= + +echo "" +log_info "=== Instalando ArgoCD ===" + +# Adicionar repositório Helm +helm repo add argo https://argoproj.github.io/argo-helm 2>/dev/null || true +helm repo update + +# Criar namespace +kubectl create namespace argocd 2>/dev/null || true + +# Verificar se já está instalado +if helm status argocd -n argocd &> /dev/null; then + log_warn "ArgoCD já instalado, fazendo upgrade..." + HELM_CMD="upgrade" +else + HELM_CMD="install" +fi + +# Construir argumentos Helm +HELM_ARGS="" +HELM_ARGS="$HELM_ARGS --set global.domain=${ARGOCD_HOST}" +HELM_ARGS="$HELM_ARGS --set server.ingress.hosts[0]=${ARGOCD_HOST}" + +if [[ "$USE_LETSENCRYPT" == "true" ]]; then + HELM_ARGS="$HELM_ARGS --set server.ingress.tls[0].secretName=argocd-server-tls" + HELM_ARGS="$HELM_ARGS --set server.ingress.tls[0].hosts[0]=${ARGOCD_HOST}" + HELM_ARGS="$HELM_ARGS --set 'server.ingress.annotations.cert-manager\.io/cluster-issuer=letsencrypt-prod'" +elif [[ "$USE_CLOUDFLARE" == "true" ]]; then + # CloudFlare faz TLS na borda + HELM_ARGS="$HELM_ARGS --set server.ingress.tls=" +fi + +# Instalar ArgoCD +log_info "Instalando ArgoCD via Helm..." +eval helm ${HELM_CMD} argocd argo/argo-cd \ + --namespace argocd \ + -f "${SCRIPT_DIR}/argocd-values.yaml" \ + ${HELM_ARGS} \ + --wait --timeout 10m + +log_success "ArgoCD instalado" + +# ============================================================================= +# OBTER SENHA DO ADMIN +# ============================================================================= + +echo "" +log_info "=== Credenciais do ArgoCD ===" + +# Aguardar secret ser criado +log_info "Aguardando secret de credenciais..." +for i in {1..30}; do + if kubectl get secret argocd-initial-admin-secret -n argocd &> /dev/null; then + break + fi + sleep 2 +done + +ARGOCD_PASSWORD=$(kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null || echo "") + +if [[ -z "$ARGOCD_PASSWORD" ]]; then + log_warn "Não foi possível obter a senha inicial" + log_info "Tente: kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath='{.data.password}' | base64 -d" +else + echo "" + echo "==========================================" + echo " ArgoCD Credenciais" + echo "==========================================" + echo " URL: https://${ARGOCD_HOST}" + echo " Username: admin" + echo " Password: ${ARGOCD_PASSWORD}" + echo "==========================================" +fi + +# ============================================================================= +# CONFIGURAR INTEGRAÇÃO GITLAB +# ============================================================================= + +echo "" +log_info "=== Configurando Integração GitLab ===" + +# Obter host key do GitLab +log_info "Obtendo SSH host key do GitLab..." +SSH_HOST_KEY=$(ssh-keyscan -t ed25519 ${GITLAB_HOST} 2>/dev/null || ssh-keyscan ${GITLAB_HOST} 2>/dev/null || echo "") + +if [[ -n "$SSH_HOST_KEY" ]]; then + # Criar ConfigMap com known hosts + kubectl create configmap argocd-ssh-known-hosts-cm \ + --from-literal=ssh_known_hosts="${SSH_HOST_KEY}" \ + -n argocd \ + --dry-run=client -o yaml | kubectl apply -f - + log_success "SSH host key configurado" +else + log_warn "Não foi possível obter SSH host key" + log_info "Configure manualmente: argocd cert add-ssh --batch < known_hosts" +fi + +# ============================================================================= +# INSTRUÇÕES FINAIS +# ============================================================================= + +echo "" +echo "==========================================" +echo " Instalação Concluída!" +echo "==========================================" +echo "" +echo "ArgoCD:" +echo " URL: https://${ARGOCD_HOST}" +echo " Username: admin" +if [[ -n "$ARGOCD_PASSWORD" ]]; then + echo " Password: ${ARGOCD_PASSWORD}" +else + echo " Password: kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath='{.data.password}' | base64 -d" +fi +echo "" +echo "GitLab Runner:" +echo " Namespace: gitlab" +echo " Verificar: kubectl get pods -n gitlab -l app=gitlab-runner" +echo " Status no GitLab: https://${GITLAB_HOST}/admin/runners" +echo "" +echo "Próximos passos:" +echo "" +echo "1. Configure DNS:" +echo " Adicione registro A para ${ARGOCD_HOST} apontando para o LoadBalancer" +echo "" +echo "2. Crie um repositório GitOps no GitLab:" +echo " - Nome: gitops-demo" +echo " - Estrutura: apps/node-bugado/{deployment,service,configmap}.yaml" +echo "" +echo "3. Configure repositório no ArgoCD:" +echo " a) Gere uma deploy key:" +echo " ssh-keygen -t ed25519 -f argocd-deploy-key -N ''" +echo "" +echo " b) Adicione a chave pública no GitLab:" +echo " Settings → Repository → Deploy Keys" +echo "" +echo " c) Conecte o repositório no ArgoCD:" +echo " - Acesse https://${ARGOCD_HOST}" +echo " - Settings → Repositories → Connect Repo" +echo " - Method: SSH" +echo " - URL: git@${GITLAB_HOST}:/gitops-demo.git" +echo " - SSH private key: (conteúdo de argocd-deploy-key)" +echo "" +echo "4. Crie uma Application no ArgoCD:" +echo " kubectl apply -f - << 'EOF'" +echo " apiVersion: argoproj.io/v1alpha1" +echo " kind: Application" +echo " metadata:" +echo " name: node-bugado" +echo " namespace: argocd" +echo " spec:" +echo " project: default" +echo " source:" +echo " repoURL: git@${GITLAB_HOST}:/gitops-demo.git" +echo " targetRevision: HEAD" +echo " path: apps/node-bugado" +echo " destination:" +echo " server: https://kubernetes.default.svc" +echo " namespace: demo" +echo " syncPolicy:" +echo " automated:" +echo " prune: true" +echo " selfHeal: true" +echo " syncOptions:" +echo " - CreateNamespace=true" +echo " EOF" +echo "" +echo "Comandos úteis:" +echo " kubectl get pods -n argocd" +echo " kubectl get applications -n argocd" +echo " kubectl logs -n gitlab -l app=gitlab-runner -f" +echo ""