diff --git a/.drone.yml b/.drone.yml index c0e00a9..dcc0c41 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,8 +1,10 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# PIPELINE 1: DEV (branch dev) +# ═══════════════════════════════════════════════════════════════════════════════ kind: pipeline type: kubernetes name: deploy-backend-dev -# ✅ SERVICE ACCOUNT CORRETO (CLUSTER-WIDE) service_account_name: drone-deployer trigger: @@ -17,6 +19,7 @@ steps: repo: in.gohorsejobs.com/gohorsejobsdev/gohorsejobs-backend tags: - latest + - ${DRONE_COMMIT_SHA:0:8} context: backend dockerfile: backend/Dockerfile insecure: true @@ -33,14 +36,14 @@ steps: AWS_ENDPOINT: { from_secret: AWS_ENDPOINT } AWS_REGION: { from_secret: AWS_REGION } AWS_SECRET_ACCESS_KEY: { from_secret: AWS_SECRET_ACCESS_KEY } - CORS_ORIGINS: { from_secret: CORS_ORIGINS } + CORS_ORIGINS: { from_secret: CORS_ORIGINS_DEV } DB_HOST: { from_secret: DB_HOST } - DB_NAME: { from_secret: DB_NAME_DEV } # 👈 ÚNICA MUDANÇA + DB_NAME: { from_secret: DB_NAME_DEV } DB_PASSWORD: { from_secret: DB_PASSWORD } DB_PORT: { from_secret: DB_PORT } DB_SSLMODE: { from_secret: DB_SSLMODE } DB_USER: { from_secret: DB_USER } - ENV: { from_secret: ENV } + ENV: "development" JWT_SECRET: { from_secret: JWT_SECRET } MAX_UPLOAD_CAD_SIZE: { from_secret: MAX_UPLOAD_CAD_SIZE } PORT: { from_secret: PORT } @@ -76,5 +79,178 @@ steps: image: bitnami/kubectl:latest commands: - kubectl get pods -n gohorsejobsdev - - kubectl apply -n gohorsejobsdev -f k8s/backend-deployment.yaml - - kubectl apply -n gohorsejobsdev -f k8s/backend-service.yaml + - kubectl apply -f k8s/dev/backend-deployment.yaml + - kubectl apply -f k8s/dev/backend-service.yaml + - kubectl rollout restart deployment/gohorse-backend -n gohorsejobsdev + +--- +# ═══════════════════════════════════════════════════════════════════════════════ +# PIPELINE 2: HML (branch hml) +# ═══════════════════════════════════════════════════════════════════════════════ +kind: pipeline +type: kubernetes +name: deploy-backend-hml + +service_account_name: drone-deployer + +trigger: + branch: + - hml + +steps: + - name: build-and-push-backend + image: plugins/docker:latest + settings: + registry: in.gohorsejobs.com + repo: in.gohorsejobs.com/gohorsejobshml/gohorsejobs-backend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + context: backend + dockerfile: backend/Dockerfile + insecure: true + insecure_skip_verify: true + username: + from_secret: HARBOR_USERNAME + password: + from_secret: HARBOR_PASSWORD + + - name: export-envs-to-k8s + image: bitnami/kubectl:latest + environment: + AWS_ACCESS_KEY_ID: { from_secret: AWS_ACCESS_KEY_ID } + AWS_ENDPOINT: { from_secret: AWS_ENDPOINT } + AWS_REGION: { from_secret: AWS_REGION } + AWS_SECRET_ACCESS_KEY: { from_secret: AWS_SECRET_ACCESS_KEY } + CORS_ORIGINS: { from_secret: CORS_ORIGINS_HML } + DB_HOST: { from_secret: DB_HOST } + DB_NAME: { from_secret: DB_NAME_HML } + DB_PASSWORD: { from_secret: DB_PASSWORD } + DB_PORT: { from_secret: DB_PORT } + DB_SSLMODE: { from_secret: DB_SSLMODE } + DB_USER: { from_secret: DB_USER } + ENV: "homologation" + JWT_SECRET: { from_secret: JWT_SECRET } + MAX_UPLOAD_CAD_SIZE: { from_secret: MAX_UPLOAD_CAD_SIZE } + PORT: { from_secret: PORT } + S3_BUCKET: { from_secret: S3_BUCKET } + UPLOAD_DIR: { from_secret: UPLOAD_DIR } + + commands: + - | + cat < .env.k8s + AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID + AWS_ENDPOINT=$AWS_ENDPOINT + AWS_REGION=$AWS_REGION + AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY + CORS_ORIGINS=$CORS_ORIGINS + DB_HOST=$DB_HOST + DB_NAME=$DB_NAME + DB_PASSWORD=$DB_PASSWORD + DB_PORT=$DB_PORT + DB_SSLMODE=$DB_SSLMODE + DB_USER=$DB_USER + ENV=$ENV + JWT_SECRET=$JWT_SECRET + MAX_UPLOAD_CAD_SIZE=$MAX_UPLOAD_CAD_SIZE + PORT=$PORT + S3_BUCKET=$S3_BUCKET + UPLOAD_DIR=$UPLOAD_DIR + EOF + + - kubectl -n gohorsejobshml delete secret backend-secrets --ignore-not-found + - kubectl -n gohorsejobshml create secret generic backend-secrets --from-env-file=.env.k8s + + - name: deploy-backend + image: bitnami/kubectl:latest + commands: + - kubectl get pods -n gohorsejobshml + - kubectl apply -f k8s/hml/backend-deployment.yaml + - kubectl apply -f k8s/hml/backend-service.yaml + - kubectl rollout restart deployment/gohorse-backend -n gohorsejobshml + +--- +# ═══════════════════════════════════════════════════════════════════════════════ +# PIPELINE 3: PRD (branch main) +# ═══════════════════════════════════════════════════════════════════════════════ +kind: pipeline +type: kubernetes +name: deploy-backend-prd + +service_account_name: drone-deployer + +trigger: + branch: + - main + +steps: + - name: build-and-push-backend + image: plugins/docker:latest + settings: + registry: in.gohorsejobs.com + repo: in.gohorsejobs.com/gohorsejobs/gohorsejobs-backend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + context: backend + dockerfile: backend/Dockerfile + insecure: true + insecure_skip_verify: true + username: + from_secret: HARBOR_USERNAME + password: + from_secret: HARBOR_PASSWORD + + - name: export-envs-to-k8s + image: bitnami/kubectl:latest + environment: + AWS_ACCESS_KEY_ID: { from_secret: AWS_ACCESS_KEY_ID } + AWS_ENDPOINT: { from_secret: AWS_ENDPOINT } + AWS_REGION: { from_secret: AWS_REGION } + AWS_SECRET_ACCESS_KEY: { from_secret: AWS_SECRET_ACCESS_KEY } + CORS_ORIGINS: { from_secret: CORS_ORIGINS } + DB_HOST: { from_secret: DB_HOST } + DB_NAME: { from_secret: DB_NAME } + DB_PASSWORD: { from_secret: DB_PASSWORD } + DB_PORT: { from_secret: DB_PORT } + DB_SSLMODE: { from_secret: DB_SSLMODE } + DB_USER: { from_secret: DB_USER } + ENV: "production" + JWT_SECRET: { from_secret: JWT_SECRET } + MAX_UPLOAD_CAD_SIZE: { from_secret: MAX_UPLOAD_CAD_SIZE } + PORT: { from_secret: PORT } + S3_BUCKET: { from_secret: S3_BUCKET } + UPLOAD_DIR: { from_secret: UPLOAD_DIR } + + commands: + - | + cat < .env.k8s + AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID + AWS_ENDPOINT=$AWS_ENDPOINT + AWS_REGION=$AWS_REGION + AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY + CORS_ORIGINS=$CORS_ORIGINS + DB_HOST=$DB_HOST + DB_NAME=$DB_NAME + DB_PASSWORD=$DB_PASSWORD + DB_PORT=$DB_PORT + DB_SSLMODE=$DB_SSLMODE + DB_USER=$DB_USER + ENV=$ENV + JWT_SECRET=$JWT_SECRET + MAX_UPLOAD_CAD_SIZE=$MAX_UPLOAD_CAD_SIZE + PORT=$PORT + S3_BUCKET=$S3_BUCKET + UPLOAD_DIR=$UPLOAD_DIR + EOF + + - kubectl -n gohorsejobs delete secret backend-secrets --ignore-not-found + - kubectl -n gohorsejobs create secret generic backend-secrets --from-env-file=.env.k8s + + - name: deploy-backend + image: bitnami/kubectl:latest + commands: + - kubectl get pods -n gohorsejobs + - kubectl apply -f k8s/prd/backend-deployment.yaml + - kubectl apply -f k8s/prd/backend-service.yaml + - kubectl rollout restart deployment/gohorse-backend -n gohorsejobs diff --git a/DEVOPS.md b/DEVOPS.md new file mode 100644 index 0000000..65cc9e2 --- /dev/null +++ b/DEVOPS.md @@ -0,0 +1,251 @@ +# DevOps - GoHorseJobs + +Documentação de infraestrutura, CI/CD e deploy do projeto GoHorseJobs. + +--- + +## 📁 Estrutura + +``` +. +├── .drone.yml # Pipeline CI/CD (Drone) +├── k8s/ +│ ├── dev/ # Manifests Kubernetes - Desenvolvimento +│ │ ├── backend-deployment.yaml +│ │ └── backend-service.yaml +│ ├── hml/ # Manifests Kubernetes - Homologação +│ │ ├── backend-deployment.yaml +│ │ └── backend-service.yaml +│ └── prd/ # Manifests Kubernetes - Produção +│ ├── backend-deployment.yaml +│ └── backend-service.yaml +├── backend/ +│ ├── Dockerfile # Build da API Go +│ └── .env.example # Variáveis de ambiente +└── seeder-api/ # Seeder Node.js para popular DB +``` + +--- + +## 🌍 Ambientes + +| Ambiente | Branch | Namespace K8s | Registry Harbor | Réplicas | +|----------|--------|---------------|-----------------|----------| +| **DEV** | `dev` | `gohorsejobsdev` | `gohorsejobsdev/gohorsejobs-backend` | 1 | +| **HML** | `hml` | `gohorsejobshml` | `gohorsejobshml/gohorsejobs-backend` | 2 | +| **PRD** | `main` | `gohorsejobs` | `gohorsejobs/gohorsejobs-backend` | 3 | + +--- + +## 🔄 Pipeline CI/CD (Drone) + +### Fluxo de Deploy + +``` +dev branch → build → push (Harbor) → deploy (K8s gohorsejobsdev) + ↓ +hml branch → build → push (Harbor) → deploy (K8s gohorsejobshml) + ↓ +main branch → build → push (Harbor) → deploy (K8s gohorsejobs) +``` + +### Triggers + +- Push na branch `dev` → executa pipeline `deploy-backend-dev` +- Push na branch `hml` → executa pipeline `deploy-backend-hml` +- Push na branch `main` → executa pipeline `deploy-backend-prd` + +### Etapas do Pipeline + +1. **build-and-push-backend** - Builda imagem Docker e envia para Harbor +2. **export-envs-to-k8s** - Cria secret `backend-secrets` no namespace +3. **deploy-backend** - Aplica manifests K8s e reinicia deployment + +--- + +## 🔐 Secrets (Drone CI) + +Secrets que precisam estar configurados no Drone: + +### Registry +| Secret | Descrição | +|--------|-----------| +| `HARBOR_USERNAME` | Usuário do Harbor | +| `HARBOR_PASSWORD` | Senha do Harbor | + +### Database +| Secret | Ambiente | Descrição | +|--------|----------|-----------| +| `DB_HOST` | Todos | Host do PostgreSQL | +| `DB_PORT` | Todos | Porta do PostgreSQL | +| `DB_USER` | Todos | Usuário do PostgreSQL | +| `DB_PASSWORD` | Todos | Senha do PostgreSQL | +| `DB_SSLMODE` | Todos | `require` ou `disable` | +| `DB_NAME_DEV` | DEV | Nome do banco dev | +| `DB_NAME_HML` | HML | Nome do banco hml | +| `DB_NAME` | PRD | Nome do banco produção | + +### S3/Object Storage +| Secret | Descrição | +|--------|-----------| +| `AWS_ACCESS_KEY_ID` | Access Key | +| `AWS_SECRET_ACCESS_KEY` | Secret Key | +| `AWS_ENDPOINT` | Endpoint S3-compatible | +| `AWS_REGION` | Região | +| `S3_BUCKET` | Nome do bucket | + +### Aplicação +| Secret | Descrição | +|--------|-----------| +| `JWT_SECRET` | Secret para tokens JWT (min. 32 chars) | +| `PORT` | Porta da API (8521) | +| `CORS_ORIGINS_DEV` | URLs permitidas CORS (dev) | +| `CORS_ORIGINS_HML` | URLs permitidas CORS (hml) | +| `CORS_ORIGINS` | URLs permitidas CORS (prd) | + +--- + +## ☸️ Kubernetes + +### Namespaces + +```bash +# Criar namespaces +kubectl create namespace gohorsejobsdev +kubectl create namespace gohorsejobshml +kubectl create namespace gohorsejobs +``` + +### Registry Secret + +Criar secret para pull de imagens do Harbor em cada namespace: + +```bash +kubectl create secret docker-registry harbor-registry \ + --docker-server=in.gohorsejobs.com \ + --docker-username= \ + --docker-password= \ + -n gohorsejobsdev + +# Repetir para gohorsejobshml e gohorsejobs +``` + +### Deploy Manual + +```bash +# DEV +kubectl apply -f k8s/dev/backend-deployment.yaml +kubectl apply -f k8s/dev/backend-service.yaml + +# HML +kubectl apply -f k8s/hml/backend-deployment.yaml +kubectl apply -f k8s/hml/backend-service.yaml + +# PRD +kubectl apply -f k8s/prd/backend-deployment.yaml +kubectl apply -f k8s/prd/backend-service.yaml +``` + +### Comandos Úteis + +```bash +# Ver pods +kubectl get pods -n gohorsejobsdev + +# Ver logs +kubectl logs -f deployment/gohorse-backend -n gohorsejobsdev + +# Restart deployment +kubectl rollout restart deployment/gohorse-backend -n gohorsejobsdev + +# Ver secrets +kubectl get secrets -n gohorsejobsdev + +# Descrever deployment +kubectl describe deployment gohorse-backend -n gohorsejobsdev +``` + +--- + +## 🐳 Docker + +### Build Local + +```bash +cd backend +docker build -t gohorsejobs-backend:local . +``` + +### Variáveis de Ambiente + +Ver `.env.example` para lista completa. Principais: + +| Variável | Descrição | Exemplo | +|----------|-----------|---------| +| `PORT` | Porta da API | `8521` | +| `DB_HOST` | Host PostgreSQL | `db.example.com` | +| `DB_NAME` | Nome do banco | `gohorsejobs_dev` | +| `DB_SSLMODE` | Modo SSL | `require` | +| `JWT_SECRET` | Secret JWT | `sua-chave-secreta-32-chars` | + +--- + +## 🗄️ Banco de Dados + +### Conexão + +``` +Host: db-60059.dc-sp-1.absamcloud.com +Port: 26868 +SSL: require +``` + +### Bancos por Ambiente + +| Ambiente | Database | +|----------|----------| +| DEV | `gohorsejobs_dev` | +| HML | `gohorsejobs_hml` | +| PRD | `gohorsejobs` | + +### Seeder + +```bash +cd seeder-api +npm install +npm run seed # Popular banco +npm run seed:reset # Limpar banco +``` + +--- + +## 🧑‍💻 Usuários de Teste + +### SuperAdmin +- **Login:** `superadmin` +- **Senha:** `Admin@2025!` + +### Company Admins +| Login | Senha | Empresa | +|-------|-------|---------| +| `takeshi_yamamoto` | `Takeshi@2025` | TechCorp | +| `maria_santos` | `User@2025` | DesignHub | + +### Candidatos +| Login | Senha | +|-------|-------| +| `paulo_santos` | `User@2025` | +| `maria@email.com` | `User@2025` | + +--- + +## 📋 Checklist Deploy Novo Ambiente + +- [ ] Criar namespace no K8s +- [ ] Criar secret `harbor-registry` no namespace +- [ ] Adicionar secrets no Drone CI +- [ ] Criar banco de dados +- [ ] Executar seeder (opcional) +- [ ] Fazer push na branch correspondente +- [ ] Verificar logs do pipeline +- [ ] Testar endpoint `/health` diff --git a/k8s/backend-deployment.yaml b/k8s/backend-deployment.yaml deleted file mode 100644 index 2089eab..0000000 --- a/k8s/backend-deployment.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: gohorse-backend - namespace: gohorsejobsdev -spec: - replicas: 1 - selector: - matchLabels: - app: gohorse-backend - template: - metadata: - labels: - app: gohorse-backend - spec: - containers: - - name: backend - image: in.gohorsejobs.com/gohorsejobs/gohorsejobs-backend:latest - imagePullPolicy: Always - ports: - - containerPort: 8521 - env: - - name: NODE_ENV - value: "production" - - name: PORT - value: "8521" - envFrom: - - secretRef: - name: backend-secrets - imagePullSecrets: - - name: harbor-registry diff --git a/k8s/dev/backend-deployment.yaml b/k8s/dev/backend-deployment.yaml new file mode 100644 index 0000000..0d83d02 --- /dev/null +++ b/k8s/dev/backend-deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gohorse-backend + namespace: gohorsejobsdev +spec: + replicas: 1 + selector: + matchLabels: + app: gohorse-backend + template: + metadata: + labels: + app: gohorse-backend + env: development + spec: + containers: + - name: backend + image: in.gohorsejobs.com/gohorsejobsdev/gohorsejobs-backend:latest + imagePullPolicy: Always + ports: + - containerPort: 8521 + envFrom: + - secretRef: + name: backend-secrets + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8521 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /health + port: 8521 + initialDelaySeconds: 5 + periodSeconds: 10 + imagePullSecrets: + - name: harbor-registry diff --git a/k8s/backend-service.yaml b/k8s/dev/backend-service.yaml similarity index 100% rename from k8s/backend-service.yaml rename to k8s/dev/backend-service.yaml diff --git a/k8s/hml/backend-deployment.yaml b/k8s/hml/backend-deployment.yaml new file mode 100644 index 0000000..cafbeee --- /dev/null +++ b/k8s/hml/backend-deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gohorse-backend + namespace: gohorsejobshml +spec: + replicas: 2 + selector: + matchLabels: + app: gohorse-backend + template: + metadata: + labels: + app: gohorse-backend + env: homologation + spec: + containers: + - name: backend + image: in.gohorsejobs.com/gohorsejobshml/gohorsejobs-backend:latest + imagePullPolicy: Always + ports: + - containerPort: 8521 + envFrom: + - secretRef: + name: backend-secrets + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8521 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /health + port: 8521 + initialDelaySeconds: 5 + periodSeconds: 10 + imagePullSecrets: + - name: harbor-registry diff --git a/k8s/hml/backend-service.yaml b/k8s/hml/backend-service.yaml new file mode 100644 index 0000000..ab3b51f --- /dev/null +++ b/k8s/hml/backend-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: gohorse-backend + namespace: gohorsejobshml +spec: + selector: + app: gohorse-backend + ports: + - name: http + port: 8521 + targetPort: 8521 + type: ClusterIP diff --git a/k8s/prd/backend-deployment.yaml b/k8s/prd/backend-deployment.yaml new file mode 100644 index 0000000..fc4c1ed --- /dev/null +++ b/k8s/prd/backend-deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gohorse-backend + namespace: gohorsejobs +spec: + replicas: 3 + selector: + matchLabels: + app: gohorse-backend + template: + metadata: + labels: + app: gohorse-backend + env: production + spec: + containers: + - name: backend + image: in.gohorsejobs.com/gohorsejobs/gohorsejobs-backend:latest + imagePullPolicy: Always + ports: + - containerPort: 8521 + envFrom: + - secretRef: + name: backend-secrets + resources: + requests: + memory: "256Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1000m" + livenessProbe: + httpGet: + path: /health + port: 8521 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /health + port: 8521 + initialDelaySeconds: 5 + periodSeconds: 10 + imagePullSecrets: + - name: harbor-registry diff --git a/k8s/prd/backend-service.yaml b/k8s/prd/backend-service.yaml new file mode 100644 index 0000000..080faff --- /dev/null +++ b/k8s/prd/backend-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: gohorse-backend + namespace: gohorsejobs +spec: + selector: + app: gohorse-backend + ports: + - name: http + port: 8521 + targetPort: 8521 + type: ClusterIP