Initial commit: Terraform infrastructure, pipelines, docs and scripts
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
# Skill Registry — proyectosacc
|
||||||
|
|
||||||
|
> Inventario de skills recomendados para el proyecto `proyectosacc`.
|
||||||
|
> Sub-proyecto de AWS/CI-CD de Cómputo Contable Soft SA de CV.
|
||||||
|
|
||||||
|
## Descripción del Proyecto
|
||||||
|
|
||||||
|
- **Tipo**: IaC/CD — Despliegue de aplicaciones vía SSH desde Bitbucket Pipelines
|
||||||
|
- **Target**: VM `172.16.20.208` (SSH, usuario + password)
|
||||||
|
- **Trigger CI/CD**: Bitbucket Pipelines
|
||||||
|
- **Componentes**: Pipeline config, deployment scripts, infra docs, operational runbooks
|
||||||
|
|
||||||
|
## Skills Principales
|
||||||
|
|
||||||
|
### CI/CD y Pipelines
|
||||||
|
|
||||||
|
| Skill | Descripción | Trigger |
|
||||||
|
|-------|-------------|---------|
|
||||||
|
| `cicd-expert` | Pipelines CI/CD (GitHub Actions, GitLab CI, Jenkins). Seguridad, supply chain, GitOps. | Diseño de pipelines, gates de seguridad, troubleshooting CI/CD. |
|
||||||
|
| `bitbucket-workflow` | Mejores prácticas Bitbucket: PRs, Pipelines, Jira integration. | Trabajo con Bitbucket Cloud, pipelines, integración Atlassian. |
|
||||||
|
| `bitbucket-server` | Interacción con Bitbucket Server REST API. | Gestión de PRs vía API. |
|
||||||
|
|
||||||
|
### Infrastructure as Code (IaC)
|
||||||
|
|
||||||
|
| Skill | Descripción | Trigger |
|
||||||
|
|-------|-------------|---------|
|
||||||
|
| `terraform-best-practices` | Optimización Terraform/OpenTofu. | Código Terraform, IaC. |
|
||||||
|
| `terraform-specialist` | Terraform avanzado: state management, enterprise patterns. | Infraestructura compleja. |
|
||||||
|
| `ansible-expert` | Configuration management con Ansible. | Playbooks, roles, inventarios. |
|
||||||
|
|
||||||
|
### AWS y Cloud
|
||||||
|
|
||||||
|
| Skill | Descripción | Trigger |
|
||||||
|
|-------|-------------|---------|
|
||||||
|
| `aws-solution-architect` | Arquitecturas AWS serverless, CloudFormation, cost optimization. | Diseño de arquitectura AWS. |
|
||||||
|
|
||||||
|
### Documentación y Gestión
|
||||||
|
|
||||||
|
| Skill | Descripción | Trigger |
|
||||||
|
|-------|-------------|---------|
|
||||||
|
| `confluence` | Gestión de documentación en Confluence. | Lectura/escritura de páginas. |
|
||||||
|
| `jira` | Gestión de issues en Jira. | Tickets, sprints, backlog. |
|
||||||
|
| `jira-cli` | Jira desde línea de comandos. | Operaciones rápidas de issues. |
|
||||||
|
| `engram-memory-protocol` | Memoria persistente entre sesiones. | Decisiones, descubrimientos, preferencias. |
|
||||||
|
|
||||||
|
### Control de Versiones
|
||||||
|
|
||||||
|
| Skill | Descripción | Trigger |
|
||||||
|
|-------|-------------|---------|
|
||||||
|
| `branch-pr` | Workflow de PRs con sistema issue-first. | Creación de PRs. |
|
||||||
|
| `issue-creation` | Workflow de issues con sistema issue-first. | Reporte de bugs, features. |
|
||||||
|
|
||||||
|
### Diagramas y Visualización
|
||||||
|
|
||||||
|
| Skill | Descripción | Trigger |
|
||||||
|
|-------|-------------|---------|
|
||||||
|
| `excalidraw-diagram-generator` | Diagramas Excalidraw desde lenguaje natural. | Arquitectura, flujos, mind maps. |
|
||||||
|
|
||||||
|
## Convenciones
|
||||||
|
|
||||||
|
1. Los secrets se almacenan en Bitbucket Repository Variables o `.env` (nunca en el repo).
|
||||||
|
2. Pipeline estándar de 7 pasos (image → repo → deps → build → publish → install → deploy).
|
||||||
|
3. Scripts con `set -euo pipefail` y headers de CCsoft.
|
||||||
|
4. SSH deployment con usuario `osiris` o `thoth`.
|
||||||
|
5. Notificaciones vía Telegram (@CCAlertasBot, @CCDevRoBot).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Cómputo Contable Soft SA de CV — Skill Registry proyectosacc — Abril 2026*
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
# ===============================================================================
|
||||||
|
# 🚫 ARCHIVOS Y DIRECTORIOS IGNORADOS — Proyecto proyectosacc
|
||||||
|
# ===============================================================================
|
||||||
|
|
||||||
|
# Variables de entorno y secrets
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Terraform
|
||||||
|
terraform/.terraform/
|
||||||
|
terraform/*.tfstate
|
||||||
|
terraform/*.tfstate.*
|
||||||
|
terraform/*.tfplan
|
||||||
|
terraform/.terraform.lock.hcl
|
||||||
|
terraform/crash.log
|
||||||
|
terraform/crash.*.log
|
||||||
|
terraform/override.tf
|
||||||
|
terraform/override.tf.json
|
||||||
|
terraform/*_override.tf
|
||||||
|
terraform/*_override.tf.json
|
||||||
|
|
||||||
|
# Claves SSH y certificados
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.pub
|
||||||
|
*.crt
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# IDEs y editores
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Dependencias y builds
|
||||||
|
node_modules/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
.gradle/
|
||||||
|
gradle/
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
# Archivos temporales
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
|
||||||
|
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
|
||||||
|
Licensor: HashiCorp, Inc.
|
||||||
|
Licensed Work: Terraform Version 1.6.0 or later. The Licensed Work is (c) 2024
|
||||||
|
HashiCorp, Inc.
|
||||||
|
Additional Use Grant: You may make production use of the Licensed Work, provided
|
||||||
|
Your use does not include offering the Licensed Work to third
|
||||||
|
parties on a hosted or embedded basis in order to compete with
|
||||||
|
HashiCorp's paid version(s) of the Licensed Work. For purposes
|
||||||
|
of this license:
|
||||||
|
|
||||||
|
A "competitive offering" is a Product that is offered to third
|
||||||
|
parties on a paid basis, including through paid support
|
||||||
|
arrangements, that significantly overlaps with the capabilities
|
||||||
|
of HashiCorp's paid version(s) of the Licensed Work. If Your
|
||||||
|
Product is not a competitive offering when You first make it
|
||||||
|
generally available, it will not become a competitive offering
|
||||||
|
later due to HashiCorp releasing a new version of the Licensed
|
||||||
|
Work with additional capabilities. In addition, Products that
|
||||||
|
are not provided on a paid basis are not competitive.
|
||||||
|
|
||||||
|
"Product" means software that is offered to end users to manage
|
||||||
|
in their own environments or offered as a service on a hosted
|
||||||
|
basis.
|
||||||
|
|
||||||
|
"Embedded" means including the source code or executable code
|
||||||
|
from the Licensed Work in a competitive offering. "Embedded"
|
||||||
|
also means packaging the competitive offering in such a way
|
||||||
|
that the Licensed Work must be accessed or downloaded for the
|
||||||
|
competitive offering to operate.
|
||||||
|
|
||||||
|
Hosting or using the Licensed Work(s) for internal purposes
|
||||||
|
within an organization is not considered a competitive
|
||||||
|
offering. HashiCorp considers your organization to include all
|
||||||
|
of your affiliates under common control.
|
||||||
|
|
||||||
|
For binding interpretive guidance on using HashiCorp products
|
||||||
|
under the Business Source License, please visit our FAQ.
|
||||||
|
(https://www.hashicorp.com/license-faq)
|
||||||
|
Change Date: Four years from the date the Licensed Work is published.
|
||||||
|
Change License: MPL 2.0
|
||||||
|
|
||||||
|
For information about alternative licensing arrangements for the Licensed Work,
|
||||||
|
please contact licensing@hashicorp.com.
|
||||||
|
|
||||||
|
Notice
|
||||||
|
|
||||||
|
Business Source License 1.1
|
||||||
|
|
||||||
|
Terms
|
||||||
|
|
||||||
|
The Licensor hereby grants you the right to copy, modify, create derivative
|
||||||
|
works, redistribute, and make non-production use of the Licensed Work. The
|
||||||
|
Licensor may make an Additional Use Grant, above, permitting limited production use.
|
||||||
|
|
||||||
|
Effective on the Change Date, or the fourth anniversary of the first publicly
|
||||||
|
available distribution of a specific version of the Licensed Work under this
|
||||||
|
License, whichever comes first, the Licensor hereby grants you rights under
|
||||||
|
the terms of the Change License, and the rights granted in the paragraph
|
||||||
|
above terminate.
|
||||||
|
|
||||||
|
If your use of the Licensed Work does not comply with the requirements
|
||||||
|
currently in effect as described in this License, you must purchase a
|
||||||
|
commercial license from the Licensor, its affiliated entities, or authorized
|
||||||
|
resellers, or you must refrain from using the Licensed Work.
|
||||||
|
|
||||||
|
All copies of the original and modified Licensed Work, and derivative works
|
||||||
|
of the Licensed Work, are subject to this License. This License applies
|
||||||
|
separately for each version of the Licensed Work and the Change Date may vary
|
||||||
|
for each version of the Licensed Work released by Licensor.
|
||||||
|
|
||||||
|
You must conspicuously display this License on each original or modified copy
|
||||||
|
of the Licensed Work. If you receive the Licensed Work in original or
|
||||||
|
modified form from a third party, the terms and conditions set forth in this
|
||||||
|
License apply to your use of that work.
|
||||||
|
|
||||||
|
Any use of the Licensed Work in violation of this License will automatically
|
||||||
|
terminate your rights under this License for the current and all other
|
||||||
|
versions of the Licensed Work.
|
||||||
|
|
||||||
|
This License does not grant you any right in any trademark or logo of
|
||||||
|
Licensor or its affiliates (provided that you may use a trademark or logo of
|
||||||
|
Licensor as expressly required by this License).
|
||||||
|
|
||||||
|
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
||||||
|
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
||||||
|
TITLE.
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,186 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Pipeline de 7 pasos estándar de CCsoft para desplegar el frontend
|
||||||
|
# React (S3+CloudFront) y la API backend (EC2) de SACC.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
image: atlassian/default-image:5
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
steps:
|
||||||
|
- step: ¬ify-start
|
||||||
|
name: Notify Start
|
||||||
|
script:
|
||||||
|
- export TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN}"
|
||||||
|
- export TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID}"
|
||||||
|
- bash ci-cd-commons/telegram_alert.sh "🚀 Iniciando pipeline de proyectosacc (${BITBUCKET_BRANCH})"
|
||||||
|
|
||||||
|
- step: ¬ify-fail
|
||||||
|
name: Notify Failure
|
||||||
|
script:
|
||||||
|
- export TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN}"
|
||||||
|
- export TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID}"
|
||||||
|
- bash ci-cd-commons/telegram_alert.sh "❌ Pipeline de proyectosacc falló en el paso ${BITBUCKET_STEP_KEY}"
|
||||||
|
|
||||||
|
pipelines:
|
||||||
|
default:
|
||||||
|
- step:
|
||||||
|
name: 04_build
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- echo "=== Build de proyectosacc (sin deploy) ==="
|
||||||
|
- npm ci
|
||||||
|
- npm run build
|
||||||
|
- ./gradlew clean bootJar
|
||||||
|
|
||||||
|
branches:
|
||||||
|
develop:
|
||||||
|
- step:
|
||||||
|
name: 01_image-setup
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk awscli
|
||||||
|
- mkdir -p ~/.ssh
|
||||||
|
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
|
- ssh-keyscan -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" "${DEV_SERVER_IP_PROYECTOSACC}" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
- export TELEGRAM_BOT_TOKEN="${DEV_TELEGRAM_BOT_TOKEN}"
|
||||||
|
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
|
||||||
|
- bash ci-cd-commons/telegram_alert.sh "🚀 Iniciando pipeline DEV de proyectosacc"
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 02_repo-config
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-commons.git" ci-cd-commons
|
||||||
|
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-saac4.git" ci-cd-saac4
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 03_dependencies
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- npm ci
|
||||||
|
- ./gradlew dependencies
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 04_build
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- npm ci
|
||||||
|
- npm run build
|
||||||
|
- ./gradlew clean bootJar
|
||||||
|
artifacts:
|
||||||
|
- build/**
|
||||||
|
- build/libs/*.jar
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 05_publish
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- aws s3 sync build/ "s3://${S3_FRONTEND_BUCKET}/" --delete
|
||||||
|
- aws s3 cp build/libs/*.jar "s3://${S3_ARTIFACTS_BUCKET}/develop/proyectosacc-app.jar"
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 06_install
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
|
- |
|
||||||
|
ssh -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" \
|
||||||
|
-i ~/.ssh/sacc4_key \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
|
||||||
|
"bash -c 'mkdir -p /home/thoth/deploy/artifacts/current && aws s3 cp s3://${S3_ARTIFACTS_BUCKET}/develop/proyectosacc-app.jar /home/thoth/deploy/artifacts/current/proyectosacc-app.jar && chown osiris:osiris /home/thoth/deploy/artifacts/current/proyectosacc-app.jar'"
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 07_deploy
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
|
- |
|
||||||
|
ssh -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" \
|
||||||
|
-i ~/.ssh/sacc4_key \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
|
||||||
|
"bash /home/thoth/deploy/setup/deploy.sh"
|
||||||
|
- aws cloudfront create-invalidation --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" --paths "/*"
|
||||||
|
- bash ci-cd-commons/telegram_alert.sh "✅ Deploy DEV de proyectosacc completado exitosamente"
|
||||||
|
|
||||||
|
main:
|
||||||
|
- step:
|
||||||
|
name: 01_image-setup
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk awscli
|
||||||
|
- mkdir -p ~/.ssh
|
||||||
|
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
|
- ssh-keyscan -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" "${PROD_SERVER_IP_PROYECTOSACC}" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
||||||
|
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
|
||||||
|
- bash ci-cd-commons/telegram_alert.sh "🚀 Iniciando pipeline PROD de proyectosacc"
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 02_repo-config
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-commons.git" ci-cd-commons
|
||||||
|
- git clone "https://x-token-auth:${BITBUCKET_PASSWORD}@bitbucket.org/ccsoft1/ci-cd-saac4.git" ci-cd-saac4
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 03_dependencies
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- npm ci
|
||||||
|
- ./gradlew dependencies
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 04_build
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- npm ci
|
||||||
|
- npm run build
|
||||||
|
- ./gradlew clean bootJar
|
||||||
|
artifacts:
|
||||||
|
- build/**
|
||||||
|
- build/libs/*.jar
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 05_publish
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- aws s3 sync build/ "s3://${S3_FRONTEND_BUCKET}/" --delete
|
||||||
|
- aws s3 cp build/libs/*.jar "s3://${S3_ARTIFACTS_BUCKET}/main/proyectosacc-app.jar"
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 06_install
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
|
- |
|
||||||
|
ssh -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" \
|
||||||
|
-i ~/.ssh/sacc4_key \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
|
||||||
|
"bash -c 'mkdir -p /home/thoth/deploy/artifacts/current && aws s3 cp s3://${S3_ARTIFACTS_BUCKET}/main/proyectosacc-app.jar /home/thoth/deploy/artifacts/current/proyectosacc-app.jar && chown osiris:osiris /home/thoth/deploy/artifacts/current/proyectosacc-app.jar'"
|
||||||
|
|
||||||
|
- step:
|
||||||
|
name: 07_deploy
|
||||||
|
script:
|
||||||
|
- set -euo pipefail
|
||||||
|
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
|
- |
|
||||||
|
ssh -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" \
|
||||||
|
-i ~/.ssh/sacc4_key \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
|
||||||
|
"bash /home/thoth/deploy/setup/deploy.sh"
|
||||||
|
- aws cloudfront create-invalidation --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" --paths "/*"
|
||||||
|
- bash ci-cd-commons/telegram_alert.sh "✅ Deploy PROD de proyectosacc completado exitosamente"
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# 01 - ¿Qué es proyectosacc?
|
||||||
|
|
||||||
|
> Guía básica para entender el proyecto `proyectosacc` de Cómputo Contable Soft.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Qué es este proyecto?
|
||||||
|
|
||||||
|
**proyectosacc** es el sistema de despliegue automático para la aplicación **SACC** (Sistema de Administración Contable y Comercial) de Cómputo Contable Soft.
|
||||||
|
|
||||||
|
En palabras simples: es el "puente" que lleva el código de los programadores desde **Bitbucket** hasta el servidor de AWS donde los usuarios pueden usarlo.
|
||||||
|
|
||||||
|
El proyecto tiene tres partes principales:
|
||||||
|
1. **Infraestructura en AWS**: el servidor de la API, la base de datos, el bucket S3 para el frontend, CloudFront y el dominio.
|
||||||
|
2. **Pipeline de Bitbucket**: el proceso automático que compila y publica tanto el frontend React como la API backend.
|
||||||
|
3. **Scripts de despliegue**: los archivos que instalan la API en el servidor y suben el frontend a S3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. ¿Qué problema resuelve?
|
||||||
|
|
||||||
|
Antes de `proyectosacc`, desplegar la aplicación SACC era un proceso manual y lento:
|
||||||
|
- Los desarrolladores tenían que copiar archivos a mano.
|
||||||
|
- Había errores porque olvidaban pasos.
|
||||||
|
- Era difícil saber qué versión estaba corriendo en producción.
|
||||||
|
|
||||||
|
**proyectosacc** resuelve esto automatizando todo:
|
||||||
|
- Cada vez que un programador sube código a Bitbucket, el pipeline se encarga de compilar, probar y desplegar.
|
||||||
|
- Si algo falla, se envía una alerta por Telegram.
|
||||||
|
- La infraestructura se crea con **Terraform**, así que siempre es la misma y no hay "magia" escondida.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. ¿Quién lo usa?
|
||||||
|
|
||||||
|
| Rol | Cómo interactúa con el proyecto |
|
||||||
|
|-----|--------------------------------|
|
||||||
|
| **Desarrolladores** | Suben código a Bitbucket. El pipeline hace el resto. |
|
||||||
|
| **DevOps / SysAdmin** | Configuran el pipeline, revisan logs y arreglan problemas de infraestructura. |
|
||||||
|
| **Usuarios finales** | Acceden a `https://sacc.ccsoft.mx` para usar la aplicación. No ven el pipeline, pero se benefician de que siempre hay una versión estable. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Dato clave
|
||||||
|
|
||||||
|
El dominio oficial de la aplicación es:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://sacc.ccsoft.mx
|
||||||
|
```
|
||||||
|
|
||||||
|
Esto significa que cuando el despliegue termina exitosamente, la aplicación está disponible en esa dirección.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Siguiente: [`02-arquitectura-general.md`](02-arquitectura-general.md)*
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# 02 - Arquitectura General
|
||||||
|
|
||||||
|
> Cómo se conectan todas las piezas de `proyectosacc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Diagrama general
|
||||||
|
|
||||||
|
Aquí tienes un diagrama en texto que muestra el flujo completo:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ INTERNET (usuarios) │
|
||||||
|
│ ▼ │
|
||||||
|
│ https://sacc.ccsoft.mx │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ AWS Route 53 │
|
||||||
|
│ (traduce el dominio al destino correcto) │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ AWS CloudFront + ACM (SSL) │
|
||||||
|
│ (distribuye el frontend globalmente y pone el candadito verde HTTPS) │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ S3 Static Website Hosting │
|
||||||
|
│ (almacena y sirve el frontend React compilado) │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
||||||
|
FLUJO DE LA API (desde el navegador o la app)
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Navegador/app │─────▶│ EC2 T3.small │─────▶│ RDS MariaDB │
|
||||||
|
│ (llama a /api) │ │ (API backend) │ │ (base de datos)│
|
||||||
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||||
|
│
|
||||||
|
│ Nginx Proxy (solo API, puerto 8080)
|
||||||
|
▼
|
||||||
|
Servicio systemd
|
||||||
|
(usuario osiris)
|
||||||
|
|
||||||
|
|
||||||
|
OTRO FLUJO: El pipeline de despliegue
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||||
|
│ Bitbucket │────▶│ Pipeline │────▶│ S3 Buckets │────▶│ EC2 T3 │
|
||||||
|
│ (código) │ │ (7 pasos) │ │ (frontend + │ │ (API) │
|
||||||
|
│ │ │ │ │ artefactos) │ │ │
|
||||||
|
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
|
||||||
|
│ ▲
|
||||||
|
│ │
|
||||||
|
▼ │
|
||||||
|
CloudFront invalidation │
|
||||||
|
(actualiza cache global) │
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Explicación de cada pieza
|
||||||
|
|
||||||
|
> 💡 **Dato clave**: aunque CloudFront sirve principalmente el frontend desde S3, también tiene una regla especial (`/api/*`) que envía las peticiones de la API directamente a la EC2. Así todo el tráfico de `sacc.ccsoft.mx` pasa por un solo punto.
|
||||||
|
|
||||||
|
### 🔷 Bitbucket
|
||||||
|
Es el "banco" donde se guarda el código fuente de SACC.
|
||||||
|
- Los desarrolladores suben sus cambios aquí.
|
||||||
|
- Cuando hay un cambio nuevo, Bitbucket "despierta" el pipeline.
|
||||||
|
|
||||||
|
### 🔷 Pipeline de Bitbucket (7 pasos)
|
||||||
|
Es el robot que hace todo el trabajo:
|
||||||
|
1. Prepara la imagen de compilación.
|
||||||
|
2. Descarga los repositorios.
|
||||||
|
3. Instala dependencias.
|
||||||
|
4. **Compila el frontend React (`npm run build`) y la API backend (`./gradlew bootJar`).**
|
||||||
|
5. **Sube el frontend al bucket S3 y el artefacto de la API a S3.**
|
||||||
|
6. Se conecta por SSH al servidor EC2 para desplegar la API.
|
||||||
|
7. **Invalida la caché de CloudFront, reinicia la API y verifica que todo esté saludable.**
|
||||||
|
|
||||||
|
### 🔷 S3 Bucket
|
||||||
|
Es un "almacén" de AWS con dos trabajos en `proyectosacc`:
|
||||||
|
1. **Sirve el frontend React** como un sitio web estático.
|
||||||
|
2. **Guarda los artefactos** de la API generados por el pipeline.
|
||||||
|
- También sirve como respaldo de versiones anteriores.
|
||||||
|
|
||||||
|
### 🔷 CloudFront
|
||||||
|
Es la red de distribución de contenido (CDN) de AWS.
|
||||||
|
- **Rol**: Toma los archivos del frontend desde S3 y los distribuye por todo el mundo de forma rápida y segura.
|
||||||
|
- También maneja el HTTPS con el certificado de ACM.
|
||||||
|
|
||||||
|
### 🔷 EC2 T3.small
|
||||||
|
Es el servidor virtual de AWS donde **solo vive la API/backend**.
|
||||||
|
- **Tipo**: `t3.small` (2 vCPU, 2 GB RAM).
|
||||||
|
- **Sistema operativo**: Ubuntu 22.04 LTS.
|
||||||
|
- **Rol**: Ejecuta el servicio de la API (Java) en el puerto `8080`.
|
||||||
|
- Nginx en la EC2 solo actúa como proxy hacia la API, **no sirve el frontend React**.
|
||||||
|
|
||||||
|
### 🔷 RDS MariaDB
|
||||||
|
Es la base de datos gestionada por AWS.
|
||||||
|
- **Tipo**: `db.t3.micro`.
|
||||||
|
- **Rol**: Guarda toda la información de la aplicación (clientes, facturas, usuarios, etc.).
|
||||||
|
- La EC2 se conecta a ella por la red interna de AWS.
|
||||||
|
|
||||||
|
### 🔷 Route 53
|
||||||
|
Es el servicio de DNS de AWS.
|
||||||
|
- **Rol**: Cuando un usuario escribe `sacc.ccsoft.mx`, Route 53 le dice al navegador que debe ir a **CloudFront** (no directamente a la EC2).
|
||||||
|
- Es como la "guía telefónica" de internet.
|
||||||
|
|
||||||
|
### 🔷 ACM (AWS Certificate Manager)
|
||||||
|
Es el servicio que gestiona los certificados SSL.
|
||||||
|
- **Rol**: Permite que `https://sacc.ccsoft.mx` funcione con el candadito verde.
|
||||||
|
- El certificado se adjunta a **CloudFront**, no a Nginx en la EC2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Integración con proyectos CI/CD compartidos
|
||||||
|
|
||||||
|
`proyectosacc` se conecta con otros repositorios de CI/CD de Cómputo Contable Soft que le prestan "herramientas y ladrillos" ya hechos:
|
||||||
|
|
||||||
|
- **`ci-cd-commons`**: scripts base, utilerías como `telegram_alert.sh` y `logger_bash.sh`, y buenas prácticas de pipeline.
|
||||||
|
- **`ci-cd-saac4`**: módulos de Terraform, patrones de infraestructura AWS y scripts de despliegue adaptados para la familia de aplicaciones SACC.
|
||||||
|
|
||||||
|
Esto evita reinventar la rueda en cada proyecto y mantiene todo alineado con los estándares de la empresa.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. ¿Por qué cada pieza existe?
|
||||||
|
|
||||||
|
| Pieza | ¿Por qué la necesitamos? |
|
||||||
|
|-------|--------------------------|
|
||||||
|
| Bitbucket | Para guardar el código y activar el pipeline automáticamente. |
|
||||||
|
| Pipeline | Para que nadie tenga que copiar archivos a mano. |
|
||||||
|
| S3 | Para servir el frontend React y guardar los artefactos de la API de forma segura. |
|
||||||
|
| CloudFront | Para distribuir el frontend rápidamente por todo el mundo con HTTPS. |
|
||||||
|
| EC2 T3 | Es el servidor donde corre la API backend. Sin él, no hay dónde procesar las peticiones de datos. |
|
||||||
|
| RDS | Es mejor que instalar la base de datos en la misma EC2 porque AWS la respalda, actualiza y monitorea automáticamente. |
|
||||||
|
| Route 53 | Para que los usuarios no tengan que memorizar una IP numérica. |
|
||||||
|
| ACM | Para que la conexión sea segura (HTTPS) y proteger los datos de los usuarios. |
|
||||||
|
| Nginx | Para recibir las peticiones a la API y pasarlas al servicio backend en el puerto 8080. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`01-que-es-proyectosacc.md`](01-que-es-proyectosacc.md)*
|
||||||
|
*Siguiente: [`03-infraestructura-aws.md`](03-infraestructura-aws.md)*
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
# 03 - Infraestructura AWS
|
||||||
|
|
||||||
|
> Descripción detallada de cada recurso de AWS que usa `proyectosacc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. EC2 T3.small
|
||||||
|
|
||||||
|
La **EC2** (Elastic Compute Cloud) es el servidor virtual donde corre la aplicación SACC.
|
||||||
|
|
||||||
|
### Especificaciones
|
||||||
|
- **Tipo de instancia**: `t3.small`
|
||||||
|
- **vCPU**: 2
|
||||||
|
- **RAM**: 2 GB
|
||||||
|
- **Sistema operativo**: Ubuntu 22.04 LTS
|
||||||
|
- **Disco raíz**: 20 GB SSD (`gp3`), con cifrado obligatorio
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
- **Ejecuta la API/backend de SACC** como un servicio de `systemd` (puerto `8080`).
|
||||||
|
- Corre **Nginx Proxy** solo para redirigir las peticiones de la API hacia el backend.
|
||||||
|
- **NO sirve el frontend React** (eso lo hace S3 + CloudFront).
|
||||||
|
- Se conecta a la base de datos RDS para leer y guardar datos.
|
||||||
|
- **Solo acepta conexiones SSH con llaves dedicadas**: el pipeline y los administradores usan un par de llaves SSH generado específicamente para `proyectosacc`. No se permite acceso con contraseña.
|
||||||
|
|
||||||
|
### Dato importante
|
||||||
|
En Cómputo Contable Soft hay una regla obligatoria: **todas las instancias EC2 deben ser de la familia T3**. Esto garantiza el mejor balance entre costo y rendimiento.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. RDS MariaDB
|
||||||
|
|
||||||
|
La **RDS** (Relational Database Service) es la base de datos gestionada por AWS.
|
||||||
|
|
||||||
|
### Especificaciones
|
||||||
|
- **Motor**: MariaDB
|
||||||
|
- **Tipo de instancia**: `db.t3.micro`
|
||||||
|
- **vCPU**: 2
|
||||||
|
- **RAM**: 1 GB
|
||||||
|
- **Uso**: Base de datos de la aplicación SACC
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
- Guarda todos los datos de la aplicación: usuarios, clientes, productos, facturas, etc.
|
||||||
|
- AWS se encarga de hacer respaldos automáticos, aplicar parches de seguridad y monitorear el rendimiento.
|
||||||
|
|
||||||
|
### Ventaja principal
|
||||||
|
No tenemos que instalar ni mantener MariaDB nosotros mismos en el servidor. AWS lo hace por nosotros.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. S3 Bucket
|
||||||
|
|
||||||
|
El **S3** (Simple Storage Service) es el almacén de archivos de AWS.
|
||||||
|
|
||||||
|
### ¿Qué guardamos aquí?
|
||||||
|
1. **El frontend React compilado** (`build/`) como un sitio web estático. CloudFront lee estos archivos para servirlos a los usuarios.
|
||||||
|
2. Los archivos compilados (artefactos `.jar`) de la API backend.
|
||||||
|
3. Versiones anteriores por si necesitamos hacer un *rollback*.
|
||||||
|
4. Logs y respaldos temporales.
|
||||||
|
|
||||||
|
### Ejemplo de rutas dentro del bucket
|
||||||
|
```
|
||||||
|
s3://ccsoft-proyectosacc-frontend/index.html
|
||||||
|
s3://ccsoft-artifacts-sacc4/develop/proyectosacc-app-1.0.0.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
### ¿Por qué S3?
|
||||||
|
- Es muy económico.
|
||||||
|
- Es duradero (AWS garantiza que no se pierdan los archivos).
|
||||||
|
- El pipeline puede subir y descargar archivos fácilmente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. CloudFront
|
||||||
|
|
||||||
|
**CloudFront** es la red de distribución de contenido (CDN) de AWS.
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
- Distribuye el frontend React desde el bucket S3 a los usuarios de todo el mundo.
|
||||||
|
- Reduce la latencia porque los archivos se sirven desde el "edge location" más cercano al usuario.
|
||||||
|
- Termina el cifrado SSL/TLS (HTTPS) usando el certificado de ACM.
|
||||||
|
- Se conecta a S3 mediante **Origin Access Control (OAC)** para que el bucket no sea público directamente.
|
||||||
|
|
||||||
|
### Relación con S3 y la API
|
||||||
|
CloudFront lee los archivos estáticos (`index.html`, CSS, JS) desde S3. Además, tiene un behavior `/api/*` que redirige las peticiones de la API hacia la EC2. De esta forma, tanto el frontend como el backend comparten el mismo dominio `https://sacc.ccsoft.mx`, pero los usuarios nunca acceden directamente al bucket S3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Route 53
|
||||||
|
|
||||||
|
**Route 53** es el servicio de DNS de AWS.
|
||||||
|
|
||||||
|
### Registro configurado
|
||||||
|
- **Nombre**: `sacc.ccsoft.mx`
|
||||||
|
- **Tipo**: A (Alias)
|
||||||
|
- **Destino**: **CloudFront Distribution** (no la IP de la EC2)
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
Cuando un usuario escribe `https://sacc.ccsoft.mx` en su navegador, Route 53 le dice: "ve a CloudFront". Sin Route 53, los usuarios tendrían que escribir una URL larga de CloudFront.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. ACM (AWS Certificate Manager)
|
||||||
|
|
||||||
|
**ACM** es el servicio que gestiona certificados SSL/TLS.
|
||||||
|
|
||||||
|
### ¿Qué es un certificado SSL?
|
||||||
|
Es un documento digital que permite que la conexión entre el usuario y el servidor esté cifrada. Se ve como el candadito verde 🔒 al lado de la dirección web.
|
||||||
|
|
||||||
|
### ¿Cómo lo usamos?
|
||||||
|
1. Solicitamos un certificado en ACM para el dominio `sacc.ccsoft.mx`.
|
||||||
|
2. ACM valida que el dominio nos pertenece.
|
||||||
|
3. El certificado se adjunta a la **distribución de CloudFront**.
|
||||||
|
4. Los usuarios acceden por **HTTPS** de forma segura. El certificado ya no vive en Nginx de la EC2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Security Groups
|
||||||
|
|
||||||
|
Un **Security Group** es como un "guardia de seguridad" virtual que controla quién puede entrar y salir de la EC2.
|
||||||
|
|
||||||
|
### Reglas de entrada (inbound)
|
||||||
|
|
||||||
|
| Puerto | Origen | ¿Para qué? |
|
||||||
|
|--------|--------|-----------|
|
||||||
|
| `22` | IP del pipeline / VPN | Conexión SSH para despliegues de la API |
|
||||||
|
| `80` | `0.0.0.0/0` (todo internet) | Tráfico HTTP hacia la API (puede redirigir a HTTPS) |
|
||||||
|
| `443` | `0.0.0.0/0` (todo internet) | Tráfico HTTPS hacia la API |
|
||||||
|
| `8080` | IPs de la VPC / CloudFront | Acceso directo a la API backend |
|
||||||
|
|
||||||
|
> 💡 **Nota**: los puertos `80` y `443` en la EC2 ya no sirven el frontend React. Solo reciben peticiones a la API. El frontend se sirve desde CloudFront.
|
||||||
|
|
||||||
|
### Reglas de salida (outbound)
|
||||||
|
|
||||||
|
| Puerto | Destino | ¿Para qué? |
|
||||||
|
|--------|---------|-----------|
|
||||||
|
| Todo (`0-65535`) | `0.0.0.0/0` | La EC2 puede salir a internet (descargar paquetes, consultar APIs, etc.) |
|
||||||
|
|
||||||
|
### Regla de seguridad importante
|
||||||
|
El puerto `22` (SSH) **solo debe abrirse desde IPs confiables**. Nunca dejarlo abierto a todo el mundo (`0.0.0.0/0`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Nginx Proxy en la EC2
|
||||||
|
|
||||||
|
**Nginx** es un servidor web que en este proyecto actúa como **proxy inverso exclusivo para la API**.
|
||||||
|
|
||||||
|
### ¿Qué hace Nginx?
|
||||||
|
1. Escucha en los puertos `80` y/o `443`.
|
||||||
|
2. Recibe las peticiones dirigidas a la API (por ejemplo, `sacc.ccsoft.mx/api`).
|
||||||
|
3. Redirige el tráfico al backend que corre en el puerto interno `8080`.
|
||||||
|
4. **NO sirve archivos estáticos del frontend React** (eso es trabajo de S3 + CloudFront).
|
||||||
|
|
||||||
|
### Ejemplo mental simple
|
||||||
|
```
|
||||||
|
Navegador/app ──▶ Nginx (443) ──▶ API SACC (8080)
|
||||||
|
```
|
||||||
|
|
||||||
|
### ¿Por qué Nginx y no Apache?
|
||||||
|
En `proyectosacc` se decidió usar **Nginx** porque:
|
||||||
|
- Consume menos memoria RAM.
|
||||||
|
- Es más rápido manejando muchas conexiones simultáneas.
|
||||||
|
- La configuración de proxy inverso es más sencilla.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`02-arquitectura-general.md`](02-arquitectura-general.md)*
|
||||||
|
*Siguiente: [`04-usuarios-y-permisos.md`](04-usuarios-y-permisos.md)*
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
# 04 - Usuarios y Permisos
|
||||||
|
|
||||||
|
> Quién puede hacer qué en el servidor de `proyectosacc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Usuarios del sistema
|
||||||
|
|
||||||
|
En el servidor EC2 de `proyectosacc` existen tres usuarios principales. Cada uno tiene un trabajo específico y **no se mezclan los roles**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 👤 `ubuntu`
|
||||||
|
|
||||||
|
Este es el usuario administrador por defecto de Ubuntu en AWS.
|
||||||
|
|
||||||
|
#### ¿Qué puede hacer?
|
||||||
|
- Todo. Es el usuario con permisos de `sudo` (superusuario).
|
||||||
|
- Se usa para configurar el servidor por primera vez.
|
||||||
|
- Puede instalar programas, crear otros usuarios y revisar logs del sistema.
|
||||||
|
|
||||||
|
#### ¿Cuándo se usa?
|
||||||
|
- Durante el setup inicial del servidor.
|
||||||
|
- Para tareas de mantenimiento que requieren privilegios elevados.
|
||||||
|
- **No se usa para despliegues automáticos ni para ejecutar la aplicación.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 👤 `thoth`
|
||||||
|
|
||||||
|
Este es el usuario de **CI/CD** (Integración Continua / Despliegue Continuo).
|
||||||
|
|
||||||
|
#### ¿Qué puede hacer?
|
||||||
|
- ✅ Descargar actualizaciones de los repositorios de código (solo lectura).
|
||||||
|
- ✅ Subir el frontend compilado al bucket S3 (`aws s3 sync`).
|
||||||
|
- ✅ Mover archivos compilados de la API (binarios, JARs, etc.) a los directorios correctos en la EC2.
|
||||||
|
- ✅ Hacer copias de seguridad (backups) de los binarios antes de actualizarlos.
|
||||||
|
- ✅ Ejecutar los scripts de despliegue de la API.
|
||||||
|
|
||||||
|
#### ¿Qué NO puede hacer?
|
||||||
|
- ❌ Ejecutar la aplicación final en producción. Ese trabajo es de `osiris`.
|
||||||
|
|
||||||
|
#### ¿Por qué existe?
|
||||||
|
Para que el pipeline de Bitbucket se conecte por SSH al servidor y haga el despliegue sin necesidad de usar el usuario administrador.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 👤 `osiris`
|
||||||
|
|
||||||
|
Este es el usuario de **ejecución** de la aplicación.
|
||||||
|
|
||||||
|
#### ¿Qué puede hacer?
|
||||||
|
- ✅ Iniciar y detener la aplicación SACC.
|
||||||
|
- ✅ Ejecutar los binarios finales (por ejemplo, archivos `.jar` de Java).
|
||||||
|
- ✅ Escribir logs en los directorios asignados.
|
||||||
|
|
||||||
|
#### ¿Qué NO puede hacer?
|
||||||
|
- ❌ Realizar despliegues.
|
||||||
|
- ❌ Mover archivos de instalación.
|
||||||
|
- ❌ Acceder a repositorios de código.
|
||||||
|
|
||||||
|
#### ¿Por qué existe?
|
||||||
|
Por seguridad. Si alguien compromete la aplicación, el atacante solo tendría los permisos de `osiris`, que son muy limitados.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Separación de privilegios
|
||||||
|
|
||||||
|
Este modelo se llama **SoD** (Segregation of Duties) o "Separación de Funciones".
|
||||||
|
|
||||||
|
La idea es simple: **una sola persona (o usuario) no debe poder hacer todo**.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ thoth │ │ osiris │ │ ubuntu │
|
||||||
|
│ (CI/CD) │ │ (ejecución) │ │ (admin) │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ Despliega │ ──▶ │ Ejecuta │ ◀── │ Administra │
|
||||||
|
│ la app │ │ la app │ │ el server │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Llaves SSH
|
||||||
|
|
||||||
|
Las **llaves SSH** son como un "carnet de identidad digital" que permite conectarse al servidor sin escribir una contraseña cada vez.
|
||||||
|
En `proyectosacc` usamos un **par de llaves SSH dedicado**, generado específicamente para este proyecto. No se usa contraseña ni herramientas como `sshpass`.
|
||||||
|
|
||||||
|
### ¿Cómo funcionan?
|
||||||
|
|
||||||
|
Una llave SSH siempre viene en **par**:
|
||||||
|
1. **Llave privada**: es secreta. Solo la tiene quien se va a conectar.
|
||||||
|
2. **Llave pública**: se puede compartir. Se coloca en el servidor para decirle "confía en quien tenga la llave privada que corresponde a esta pública".
|
||||||
|
|
||||||
|
### Analogía simple
|
||||||
|
Imagina un candado con dos llaves:
|
||||||
|
- La **pública** es el candado mismo. Lo pones en la puerta del servidor.
|
||||||
|
- La **privada** es la llave que abre el candado. Solo tú la tienes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Dónde viven las llaves
|
||||||
|
|
||||||
|
### Llave privada
|
||||||
|
- **Vive en**: las variables seguras de **Bitbucket Pipelines**.
|
||||||
|
- **Nombre de variable**: `PROYECTOSACC_SSH_KEY`
|
||||||
|
- **Formato**: codificada en **base64** (esto evita que Bitbucket modifique el formato).
|
||||||
|
- **Quién la usa**: el pipeline de Bitbucket cuando se conecta por SSH al servidor.
|
||||||
|
|
||||||
|
### Llave pública
|
||||||
|
- **Vive en**: el archivo `~/.ssh/authorized_keys` del usuario `thoth` en la EC2.
|
||||||
|
- **Ruta completa**: `/home/thoth/.ssh/authorized_keys`
|
||||||
|
- **Función**: permite que el pipeline se conecte sin contraseña.
|
||||||
|
|
||||||
|
### Llaves del servidor hacia Bitbucket
|
||||||
|
- El usuario `thoth` también tiene sus propias llaves SSH para clonar repositorios desde Bitbucket.
|
||||||
|
- **Ubicación**: `/home/thoth/.ssh/`
|
||||||
|
- **Ejemplo de nombre**: `proyectosacc_server_thoth_develop_TO_bitbucket_pipeline_ci_cd`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Resumen rápido
|
||||||
|
|
||||||
|
| Usuario | Rol principal | Llave SSH asociada |
|
||||||
|
|---------|---------------|-------------------|
|
||||||
|
| `ubuntu` | Administrador del servidor | Llave de acceso inicial a la EC2 (creada al lanzarla en AWS) |
|
||||||
|
| `thoth` | Despliegue automático (CI/CD) | `PROYECTOSACC_SSH_KEY` en Bitbucket Variables |
|
||||||
|
| `osiris` | Ejecutar la aplicación | No necesita llave SSH para conexiones externas |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`03-infraestructura-aws.md`](03-infraestructura-aws.md)*
|
||||||
|
*Siguiente: [`05-pipeline-bitbucket.md`](05-pipeline-bitbucket.md)*
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
# 05 - Pipeline Bitbucket
|
||||||
|
|
||||||
|
> Cómo funciona el pipeline de 7 pasos que despliega `proyectosacc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Qué es un pipeline?
|
||||||
|
|
||||||
|
Un **pipeline** es una serie de pasos automáticos que se ejecutan cada vez que alguien sube código al repositorio.
|
||||||
|
|
||||||
|
En Cómputo Contable Soft, todos los pipelines web usan **7 pasos** estandarizados. Esto significa que, sin importar el proyecto, siempre hay los mismos pasos con los mismos nombres.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Los 7 pasos del pipeline
|
||||||
|
|
||||||
|
### Paso 1: `01_image-setup`
|
||||||
|
**Prepara la computadora virtual donde se va a compilar todo.**
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. Actualiza los paquetes del sistema.
|
||||||
|
2. Instala herramientas necesarias: `openssh-client`, `openjdk-21-jdk`, `aws-cli`.
|
||||||
|
3. Configura las llaves SSH y los `known_hosts`.
|
||||||
|
4. Carga los scripts de utilería (`logger_bash.sh`, `telegram_alert.sh`).
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: como cuando llegas a una cocina nueva y primero lavas los trastes, prendes la estufa y sacas los utensilios.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Paso 2: `02_repo-config`
|
||||||
|
**Descarga todos los repositorios que necesita la aplicación.**
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. Clona el repositorio principal de la aplicación.
|
||||||
|
2. Clona los repositorios de componentes compartidos (por ejemplo, `cmp-commons`, `cmp-security`).
|
||||||
|
3. Clona `ci-cd-commons` (scripts base de CCsoft).
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: bajas todos los ingredientes de la receta antes de empezar a cocinar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Paso 3: `03_dependencies`
|
||||||
|
**Compila o descarga todo lo que la aplicación necesita para funcionar.**
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. Compila las librerías compartidas.
|
||||||
|
2. Descarga dependencias de Maven, Gradle, npm, etc.
|
||||||
|
3. Publica las librerías locales si es necesario.
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: preparas la masa, cortas las verduras y sacas los condimentos antes de armar el platillo final.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Paso 4: `04_build`
|
||||||
|
**Compila el frontend React y la API backend.**
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. **Frontend**: ejecuta `npm run build` (o `npm ci && npm run build`) para generar la carpeta `build/` con el React estático.
|
||||||
|
2. **Backend**: ejecuta el comando de build de la API (por ejemplo, `./gradlew clean bootJar` para Java).
|
||||||
|
3. Genera los archivos finales: la carpeta `build/` para el frontend y el `.jar` para la API.
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: aquí cocinamos dos platillos: la entrada (frontend) y el plato fuerte (API).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Paso 5: `05_publish`
|
||||||
|
**Sube el frontend a S3 y el artefacto de la API a Amazon S3.**
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. **Frontend**: sincroniza la carpeta `build/` al bucket S3 designado para el sitio estático (`aws s3 sync build/ s3://bucket-name`).
|
||||||
|
2. **Backend**: sube el `.jar` (o artefacto equivalente) al bucket S3 para que la EC2 lo descargue después.
|
||||||
|
3. Esto evita transferir archivos grandes directamente por SSH y permite recuperar versiones anteriores fácilmente.
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: pones la entrada en el mostrador (S3) y el plato fuerte en la bandeja de la cocina (S3) para servirlo después.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Paso 6: `06_install`
|
||||||
|
**Descarga el artefacto de la API en el servidor EC2.**
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. Se conecta por SSH al servidor EC2 como `thoth`.
|
||||||
|
2. Ejecuta un script de preparación que descarga el `.jar` desde **S3** y lo coloca en `/home/thoth/deploy/artifacts/current/`.
|
||||||
|
3. También hace una copia de seguridad de la versión anterior.
|
||||||
|
4. **Aún no reinicia el servicio**: eso ocurre en el paso 7.
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: pones el plato fuerte en el plato y lo dejas listo, pero aún no lo sirves.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Paso 7: `07_deploy`
|
||||||
|
**Invalida la caché de CloudFront, despliega la API y verifica que todo esté saludable.**
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. **Invalida la caché de CloudFront** (`aws cloudfront create-invalidation`) para que los usuarios vean la nueva versión del frontend inmediatamente.
|
||||||
|
2. Se conecta por SSH al servidor EC2 como `thoth` y ejecuta `bash /home/thoth/deploy/setup/deploy.sh`.
|
||||||
|
3. El script `deploy.sh` crea o actualiza el servicio de `systemd`, detiene la versión anterior e inicia la nueva con el usuario `osiris`.
|
||||||
|
4. Ejecuta un **health check** en la API para verificar que responde correctamente.
|
||||||
|
5. Envía una notificación por Telegram con el resultado.
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: actualizas el menú en todos los restaurantes (CloudFront) y sirves el plato fuerte calentito desde la cocina (EC2).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Diagrama del pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ BITBUCKET PIPELINE │
|
||||||
|
│ │
|
||||||
|
│ 1. image-setup ▶ Instala herramientas │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 2. repo-config ▶ Descarga repositorios │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 3. dependencies ▶ Compila librerías y dependencias │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 4. build ▶ Compila React frontend + API backend (.jar) │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 5. publish ▶ Sube frontend a S3 web + API .jar a S3 artifacts │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 6. install ▶ Prepara la API en el servidor EC2 │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ 7. deploy ▶ Invalida CloudFront, reinicia API y health check │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. ¿Cómo se conecta el pipeline a la EC2?
|
||||||
|
|
||||||
|
La conexión se hace por **SSH** (Secure Shell).
|
||||||
|
|
||||||
|
1. El pipeline tiene guardada la **llave privada** de `thoth` en una variable segura de Bitbucket.
|
||||||
|
2. Al llegar al paso `05_publish` o `06_install`, el pipeline decodifica la llave y la guarda en `/root/.ssh/sacc4_key`.
|
||||||
|
3. Ejecuta un comando `ssh` usando esa llave para ejecutar scripts remotos.
|
||||||
|
4. La **llave pública** correspondiente ya está en el archivo `~/.ssh/authorized_keys` del usuario `thoth` en la EC2.
|
||||||
|
5. ¡La puerta se abre y el pipeline puede ejecutar comandos en el servidor!
|
||||||
|
|
||||||
|
### Ejemplo del comando que usa el pipeline
|
||||||
|
```bash
|
||||||
|
ssh -p 22 \
|
||||||
|
-i /root/.ssh/sacc4_key \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
thoth@<IP_DE_LA_EC2> \
|
||||||
|
"bash /home/thoth/deploy/setup/deploy.sh"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. ¿De dónde salen los secrets?
|
||||||
|
|
||||||
|
Los **secrets** (contraseñas, llaves, tokens) viven en las **variables de repositorio** de Bitbucket.
|
||||||
|
|
||||||
|
### Cómo se configuran
|
||||||
|
1. Entra al repositorio en Bitbucket.
|
||||||
|
2. Ve a **Repository settings > Pipelines > Repository variables**.
|
||||||
|
3. Agrega cada variable y márcala como **Secured** 🔒.
|
||||||
|
4. Las variables marcadas como secured se ofuscan en los logs (aparecen como `***`).
|
||||||
|
|
||||||
|
### Variables principales
|
||||||
|
|
||||||
|
| Variable | Descripción |
|
||||||
|
|----------|-------------|
|
||||||
|
| `PROYECTOSACC_SSH_KEY` | Llave privada SSH (en base64) dedicada para conectar al servidor |
|
||||||
|
| `PROYECTOSACC_SERVER_IP` | IP pública de la EC2 |
|
||||||
|
| `PROYECTOSACC_SSH_PORT` | Puerto SSH (normalmente `22`) |
|
||||||
|
| `PROYECTOSACC_SERVER_USER` | Usuario SSH para despliegue (`thoth`) |
|
||||||
|
| `PROYECTOSACC_TELEGRAM_BOT_TOKEN` | Token del bot de Telegram para alertas |
|
||||||
|
| `PROYECTOSACC_TELEGRAM_CHAT_ID` | ID del chat donde llegan las alertas |
|
||||||
|
| `AWS_ACCESS_KEY_ID` | Credencial de AWS para subir a S3 |
|
||||||
|
| `AWS_SECRET_ACCESS_KEY` | Credencial secreta de AWS |
|
||||||
|
| `CLOUDFRONT_DISTRIBUTION_ID` | ID de la distribución de CloudFront para invalidaciones |
|
||||||
|
| `S3_FRONTEND_BUCKET` | Nombre del bucket S3 donde se publica el frontend |
|
||||||
|
|
||||||
|
### ⚠️ Regla de oro
|
||||||
|
**NUNCA** escribas secrets directamente en el código del pipeline (`bitbucket-pipelines.yml`). Siempre usa variables de entorno.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Integración con proyectos CI/CD compartidos
|
||||||
|
|
||||||
|
`proyectosacc` no trabaja solo. Se conecta con otros proyectos de CI/CD de la empresa que proveen piezas reutilizables:
|
||||||
|
|
||||||
|
- **`ci-cd-commons`**: scripts base compartidos como `telegram_alert.sh` y `logger_bash.sh`.
|
||||||
|
- **`ci-cd-saac4`**: módulos de Terraform, patrones de infraestructura y scripts de despliegue específicos para la familia SACC.
|
||||||
|
|
||||||
|
Esto significa que parte del pipeline y de los scripts de despliegue vienen de esos repositorios, no de este. Es como armar una casa con ladrillos que ya están hechos en otra fábrica.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`04-usuarios-y-permisos.md`](04-usuarios-y-permisos.md)*
|
||||||
|
*Siguiente: [`06-scripts-de-despliegue.md`](06-scripts-de-despliegue.md)*
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
# 06 - Scripts de Despliegue
|
||||||
|
|
||||||
|
> Qué hace cada script y cuándo se ejecuta.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Qué son los scripts de despliegue?
|
||||||
|
|
||||||
|
Los **scripts** son archivos de texto con instrucciones que la computadora puede ejecutar automáticamente.
|
||||||
|
|
||||||
|
En `proyectosacc` usamos scripts escritos en **Bash** (el lenguaje de comandos de Linux). Todos comienzan con esta línea mágica:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
set -euo pipefail
|
||||||
|
```
|
||||||
|
|
||||||
|
Esto significa tres cosas:
|
||||||
|
1. **`set -e`**: Si un comando falla, el script se detiene inmediatamente.
|
||||||
|
2. **`set -u`**: Si usas una variable que no existe, el script se detiene.
|
||||||
|
3. **`set -o pipefail`**: Si un comando dentro de una "tubería" (`|`) falla, el script también se detiene.
|
||||||
|
|
||||||
|
> 💡 **En palabras simples**: es como poner el cinturón de seguridad antes de manejar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.1. Scripts provenientes de otros proyectos CI/CD
|
||||||
|
|
||||||
|
`proyectosacc` reutiliza scripts y utilerías que viven en repositorios compartidos:
|
||||||
|
|
||||||
|
- **`ci-cd-commons`**: contiene `telegram_alert.sh`, `logger_bash.sh` y otras utilerías base que usan todos los pipelines de la empresa.
|
||||||
|
- **`ci-cd-saac4`**: provee módulos de Terraform y scripts de despliegue adaptados para la familia SACC.
|
||||||
|
|
||||||
|
Estos repositorios se clonan dentro de la carpeta `/home/thoth/deploy/` (por ejemplo, `/home/thoth/deploy/ci-cd-commons/`) para que los scripts locales puedan usarlos.
|
||||||
|
|
||||||
|
### 1.2. Nuevo script: `deploy-frontend-s3.sh`
|
||||||
|
|
||||||
|
Este script se ejecuta **desde el pipeline de Bitbucket** (no dentro de la EC2). Su trabajo es subir el frontend React a S3.
|
||||||
|
|
||||||
|
Qué hace:
|
||||||
|
1. Sincroniza la carpeta `build/` con el bucket S3 (`aws s3 sync build/ s3://bucket-name --delete`).
|
||||||
|
2. Espera a que la sincronización termine.
|
||||||
|
|
||||||
|
> 💡 **Nota**: la invalidación de la caché de CloudFront se hace en el **paso 7** del pipeline (`07_deploy`), después de confirmar que la API está saludable. Así evitamos refrescar el frontend antes de que el backend esté listo.
|
||||||
|
|
||||||
|
Ejemplo de comando:
|
||||||
|
```bash
|
||||||
|
bash /home/thoth/deploy/scripts/deploy-frontend-s3.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. `deploy.sh`
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
Este es el script principal de despliegue de la **API backend**. Se ejecuta **dentro del servidor EC2** y se encarga de instalar la nueva versión del servicio.
|
||||||
|
|
||||||
|
### Pasos que sigue
|
||||||
|
1. **Crear directorios**: verifica que existan las carpetas necesarias (`backup`, `current`, `pids`, `logs`).
|
||||||
|
2. **Detener servicio anterior**: si la aplicación ya está corriendo, la detiene con `systemctl`.
|
||||||
|
3. **Configurar systemd**: crea o actualiza el archivo de servicio para que la app inicie automáticamente.
|
||||||
|
4. **Recargar systemd**: ejecuta `sudo systemctl daemon-reload`.
|
||||||
|
5. **Iniciar servicio**: ejecuta `sudo systemctl start <nombre-del-servicio>`.
|
||||||
|
6. **Health check**: hace peticiones a `http://localhost:8080/actuator/health` para verificar que la app responde.
|
||||||
|
7. **Guardar PID**: anota el ID del proceso para poder monitorearlo después.
|
||||||
|
|
||||||
|
### ¿Cuándo se llama?
|
||||||
|
- En el **paso 7** del pipeline (`07_deploy`).
|
||||||
|
- También puede ejecutarse manualmente si necesitas hacer un despliegue fuera del pipeline.
|
||||||
|
|
||||||
|
### Ejemplo de comando
|
||||||
|
```bash
|
||||||
|
bash /home/thoth/deploy/setup/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. `health-check.sh`
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
Verifica que la aplicación esté viva y respondiendo correctamente.
|
||||||
|
|
||||||
|
### Cómo funciona
|
||||||
|
1. Intenta conectarse a la URL de salud de la aplicación:
|
||||||
|
```
|
||||||
|
http://localhost:8080/actuator/health
|
||||||
|
```
|
||||||
|
2. Si la respuesta contiene `"status":"UP"`, todo está bien.
|
||||||
|
3. Si no responde después de varios intentos, reporta un error.
|
||||||
|
|
||||||
|
### ¿Cuándo se llama?
|
||||||
|
- **Dentro de `deploy.sh`**, inmediatamente después de iniciar el servicio.
|
||||||
|
- También puede usarse manualmente para revisar si la app sigue funcionando horas después del despliegue.
|
||||||
|
|
||||||
|
### Ejemplo de comando manual
|
||||||
|
```bash
|
||||||
|
bash /home/thoth/deploy/scripts/health-check.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. `rollback.sh`
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
Regresa la API backend a la versión anterior si el nuevo despliegue falló. También puede restaurar el frontend en S3 si se habilitó **versionamiento de objetos** en el bucket.
|
||||||
|
|
||||||
|
### Pasos que sigue (API en EC2)
|
||||||
|
1. Busca el último backup en la carpeta `/home/thoth/deploy/artifacts/backup/`.
|
||||||
|
2. Detiene el servicio actual.
|
||||||
|
3. Copia el backup a la carpeta `current`.
|
||||||
|
4. Reinicia el servicio con la versión anterior.
|
||||||
|
5. Ejecuta un health check para confirmar que la versión anterior todavía funciona.
|
||||||
|
|
||||||
|
### Pasos que sigue (frontend en S3)
|
||||||
|
Si el frontend también necesita rollback:
|
||||||
|
1. Identifica la versión anterior del archivo en S3 usando el versionamiento del bucket.
|
||||||
|
2. Restaura la versión anterior de `index.html` y los assets estáticos.
|
||||||
|
3. Invalida la caché de CloudFront para que los usuarios vean la versión anterior.
|
||||||
|
|
||||||
|
### ¿Cuándo se llama?
|
||||||
|
- Cuando el `deploy.sh` o el `health-check.sh` reportan un fallo en la API.
|
||||||
|
- Manualmente, si descubres que la nueva versión del frontend o la API tiene un bug grave.
|
||||||
|
|
||||||
|
### Ejemplo de comando
|
||||||
|
```bash
|
||||||
|
bash /home/thoth/deploy/scripts/rollback.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. `notify-telegram.sh`
|
||||||
|
|
||||||
|
### ¿Qué hace?
|
||||||
|
Envía mensajes a un grupo o chat de Telegram para informar sobre el estado del pipeline.
|
||||||
|
|
||||||
|
### Qué tipo de mensajes envía
|
||||||
|
- 🟢 **Éxito**: "El despliegue de proyectosacc terminó correctamente."
|
||||||
|
- 🔴 **Error**: "El pipeline de proyectosacc falló en el paso 4. Revisar logs."
|
||||||
|
- 🟡 **Advertencia**: "Health check falló. Iniciando rollback automático."
|
||||||
|
|
||||||
|
### ¿Cuándo se llama?
|
||||||
|
- Al **inicio** del pipeline.
|
||||||
|
- Al **final** del pipeline (éxito o fracaso).
|
||||||
|
- Cuando ocurre un error en cualquier paso.
|
||||||
|
|
||||||
|
### Variables necesarias
|
||||||
|
```bash
|
||||||
|
TELEGRAM_BOT_TOKEN=<token-del-bot>
|
||||||
|
TELEGRAM_CHAT_ID=<id-del-chat>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ejemplo de comando
|
||||||
|
```bash
|
||||||
|
bash /home/thoth/deploy/ci-cd-commons/telegram_alert.sh "Despliegue exitoso"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Resumen de cuándo usar cada script
|
||||||
|
|
||||||
|
| Script | ¿Cuándo se ejecuta? | ¿Dónde corre? |
|
||||||
|
|--------|---------------------|---------------|
|
||||||
|
| `deploy-frontend-s3.sh` | Paso 5 del pipeline (subir React a S3) | Pipeline (Bitbucket) |
|
||||||
|
| `deploy.sh` | Paso 7 del pipeline (desplegar API en la EC2), o manualmente | Dentro de la EC2 |
|
||||||
|
| `health-check.sh` | Después de `deploy.sh`, o manualmente | Dentro de la EC2 |
|
||||||
|
| `rollback.sh` | Cuando `deploy.sh` o `health-check.sh` fallan, o manualmente | Pipeline o EC2 |
|
||||||
|
| `notify-telegram.sh` | Inicio, fin o error del pipeline | Pipeline (Bitbucket) o EC2 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Dato extra: ¿dónde viven los scripts?
|
||||||
|
|
||||||
|
En el servidor EC2, los scripts se organizan así:
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/thoth/deploy/
|
||||||
|
├── artifacts/
|
||||||
|
│ ├── backup/ ← versiones anteriores de la API
|
||||||
|
│ ├── current/ ← versión activa de la API
|
||||||
|
│ ├── logs/ ← logs del pipeline
|
||||||
|
│ └── pids/ ← archivos con el ID de proceso
|
||||||
|
├── ci-cd-commons/
|
||||||
|
│ ├── telegram_alert.sh
|
||||||
|
│ └── logger_bash.sh
|
||||||
|
├── scripts/
|
||||||
|
│ ├── deploy-frontend-s3.sh ← sube React a S3
|
||||||
|
│ ├── health-check.sh
|
||||||
|
│ └── rollback.sh
|
||||||
|
└── setup/
|
||||||
|
└── deploy.sh ← script principal del pipeline para la API (llamado vía SSH)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Nota**: el artefacto de la API que termina en `artifacts/current/` se descarga desde **Amazon S3** durante el paso de instalación del pipeline. No se transfiere directamente por SSH. El frontend React se sube directamente a S3 desde el pipeline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`05-pipeline-bitbucket.md`](05-pipeline-bitbucket.md)*
|
||||||
|
*Siguiente: [`07-terraform-iac.md`](07-terraform-iac.md)*
|
||||||
@@ -0,0 +1,322 @@
|
|||||||
|
# 07 - Terraform (IaC)
|
||||||
|
|
||||||
|
> Cómo creamos la infraestructura de `proyectosacc` con código.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Qué es Terraform?
|
||||||
|
|
||||||
|
**Terraform** es una herramienta que permite crear infraestructura en la nube escribiendo código en lugar de hacer clic en la consola de AWS.
|
||||||
|
|
||||||
|
A esto se le llama **IaC** (Infrastructure as Code) o "Infraestructura como Código".
|
||||||
|
|
||||||
|
### Ventajas de usar Terraform
|
||||||
|
- ✅ **Repetible**: puedes crear la misma infraestructura 10 veces y siempre será idéntica.
|
||||||
|
- ✅ **Versionable**: el código se guarda en Git, así que sabes quién hizo qué cambio y cuándo.
|
||||||
|
- ✅ **Rápido**: un solo comando crea servidor, base de datos, DNS y seguridad.
|
||||||
|
- ✅ **Sin sorpresas**: todo está documentado en archivos de texto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. ¿Qué crea Terraform en proyectosacc?
|
||||||
|
|
||||||
|
Terraform se encarga de crear todos estos recursos en AWS:
|
||||||
|
|
||||||
|
1. **VPC y subredes**: la red virtual donde vive todo.
|
||||||
|
2. **Security Groups**: las reglas de firewall.
|
||||||
|
3. **EC2 T3.small**: el servidor de la **API backend**.
|
||||||
|
4. **RDS MariaDB**: la base de datos gestionada.
|
||||||
|
5. **S3 Bucket (artefactos)**: el almacén de archivos de la API.
|
||||||
|
6. **S3 Bucket (frontend)**: el sitio web estático que sirve el React app.
|
||||||
|
7. **CloudFront Distribution**: la CDN que distribuye el frontend globalmente con HTTPS.
|
||||||
|
8. **Route 53 Record**: el dominio `sacc.ccsoft.mx` apunta a CloudFront.
|
||||||
|
9. **ACM Certificate**: certificado SSL para CloudFront.
|
||||||
|
10. **IAM Role**: permisos para que la EC2 y CloudFront accedan a S3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Archivos principales de Terraform
|
||||||
|
|
||||||
|
### `main.tf`
|
||||||
|
Este es el archivo central. Aquí se definen los recursos que Terraform va a crear.
|
||||||
|
|
||||||
|
Ejemplo de la EC2 para la API:
|
||||||
|
```hcl
|
||||||
|
resource "aws_instance" "sacc_api" {
|
||||||
|
ami = "ami-0c02fb55956c7d316" # Ubuntu 22.04
|
||||||
|
instance_type = "t3.small"
|
||||||
|
|
||||||
|
vpc_security_group_ids = [aws_security_group.sacc_sg.id]
|
||||||
|
|
||||||
|
user_data = file("user-data.sh")
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "proyectosacc-api"
|
||||||
|
Environment = "develop"
|
||||||
|
Project = "proyectosacc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ejemplo del bucket S3 para el frontend React:
|
||||||
|
```hcl
|
||||||
|
resource "aws_s3_bucket" "sacc_frontend" {
|
||||||
|
bucket = "ccsoft-proyectosacc-frontend"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_website_configuration" "sacc_frontend" {
|
||||||
|
bucket = aws_s3_bucket.sacc_frontend.id
|
||||||
|
|
||||||
|
index_document {
|
||||||
|
suffix = "index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
error_document {
|
||||||
|
key = "index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ejemplo de CloudFront:
|
||||||
|
```hcl
|
||||||
|
resource "aws_cloudfront_distribution" "sacc_cdn" {
|
||||||
|
enabled = true
|
||||||
|
default_root_object = "index.html"
|
||||||
|
aliases = ["sacc.ccsoft.mx"]
|
||||||
|
|
||||||
|
# Origin 1: S3 bucket para el frontend React
|
||||||
|
origin {
|
||||||
|
domain_name = aws_s3_bucket.sacc_frontend.bucket_regional_domain_name
|
||||||
|
origin_id = "saccS3Origin"
|
||||||
|
|
||||||
|
s3_origin_config {
|
||||||
|
origin_access_identity = aws_cloudfront_origin_access_identity.sacc_oai.cloudfront_access_identity_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Origin 2: EC2 para la API backend
|
||||||
|
origin {
|
||||||
|
domain_name = aws_instance.sacc_api.public_dns
|
||||||
|
origin_id = "saccApiOrigin"
|
||||||
|
|
||||||
|
custom_origin_config {
|
||||||
|
http_port = 80
|
||||||
|
https_port = 443
|
||||||
|
origin_protocol_policy = "http-only"
|
||||||
|
origin_ssl_protocols = ["TLSv1.2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default_cache_behavior {
|
||||||
|
target_origin_id = "saccS3Origin"
|
||||||
|
viewer_protocol_policy = "redirect-to-https"
|
||||||
|
allowed_methods = ["GET", "HEAD", "OPTIONS"]
|
||||||
|
cached_methods = ["GET", "HEAD"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# API requests go to the EC2 origin
|
||||||
|
ordered_cache_behavior {
|
||||||
|
path_pattern = "/api/*"
|
||||||
|
target_origin_id = "saccApiOrigin"
|
||||||
|
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
|
||||||
|
cached_methods = ["GET", "HEAD"]
|
||||||
|
viewer_protocol_policy = "redirect-to-https"
|
||||||
|
|
||||||
|
forwarded_values {
|
||||||
|
query_string = true
|
||||||
|
headers = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"]
|
||||||
|
cookies {
|
||||||
|
forward = "all"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer_certificate {
|
||||||
|
acm_certificate_arn = aws_acm_certificate.sacc_cert.arn
|
||||||
|
ssl_support_method = "sni-only"
|
||||||
|
minimum_protocol_version = "TLSv1.2_2021"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **¿Por qué dos origins?** CloudFront sirve el frontend desde S3 por defecto. Pero cuando el navegador pide algo bajo `/api/*`, CloudFront lo envía a la EC2 donde Nginx lo redirige al backend en el puerto 8080. Así todo comparte el mismo dominio `sacc.ccsoft.mx`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `variables.tf`
|
||||||
|
Aquí se definen las variables que personalizan la infraestructura.
|
||||||
|
|
||||||
|
Ejemplo:
|
||||||
|
```hcl
|
||||||
|
variable "region" {
|
||||||
|
description = "Región de AWS"
|
||||||
|
type = string
|
||||||
|
default = "us-east-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "instance_type" {
|
||||||
|
description = "Tipo de instancia EC2"
|
||||||
|
type = string
|
||||||
|
default = "t3.small"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_password" {
|
||||||
|
description = "Contraseña de la base de datos"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ La variable `db_password` tiene `sensitive = true` para que no aparezca en los logs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `user-data.sh`
|
||||||
|
Este archivo contiene los comandos que la EC2 ejecuta la **primera vez** que se enciende.
|
||||||
|
|
||||||
|
Ejemplo de lo que hace:
|
||||||
|
1. Actualiza el sistema operativo.
|
||||||
|
2. Instala Nginx.
|
||||||
|
3. Instala Java (para correr la aplicación SACC).
|
||||||
|
4. Instala AWS CLI.
|
||||||
|
5. Crea los usuarios `thoth` y `osiris`.
|
||||||
|
6. Inyecta la **llave pública SSH dedicada** para el pipeline en `~/.ssh/authorized_keys` del usuario `thoth`.
|
||||||
|
7. Crea los directorios necesarios para el despliegue.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Actualizar sistema
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y nginx openjdk-21-jdk awscli
|
||||||
|
|
||||||
|
# Crear usuarios
|
||||||
|
useradd -m -s /bin/bash thoth || true
|
||||||
|
useradd -m -s /bin/bash osiris || true
|
||||||
|
|
||||||
|
# Crear directorios de despliegue
|
||||||
|
mkdir -p /home/thoth/deploy/artifacts/{backup,current,logs,pids}
|
||||||
|
chown -R thoth:thoth /home/thoth/deploy
|
||||||
|
|
||||||
|
# Configurar Nginx como proxy SOLO para la API (no para el frontend)
|
||||||
|
cat > /etc/nginx/sites-available/proyectosacc-api <<'EOF'
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:8080/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
ln -sf /etc/nginx/sites-available/proyectosacc-api /etc/nginx/sites-enabled/default
|
||||||
|
|
||||||
|
# Iniciar Nginx
|
||||||
|
systemctl enable nginx
|
||||||
|
systemctl start nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `outputs.tf`
|
||||||
|
Aquí definimos qué información queremos que Terraform nos muestre al final.
|
||||||
|
|
||||||
|
Ejemplo:
|
||||||
|
```hcl
|
||||||
|
output "ec2_public_ip" {
|
||||||
|
description = "IP pública del servidor de la API"
|
||||||
|
value = aws_instance.sacc_api.public_ip
|
||||||
|
}
|
||||||
|
|
||||||
|
output "rds_endpoint" {
|
||||||
|
description = "Dirección de conexión a la base de datos"
|
||||||
|
value = aws_db_instance.sacc_db.endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
output "s3_artifacts_bucket" {
|
||||||
|
description = "Nombre del bucket de artefactos de la API"
|
||||||
|
value = aws_s3_bucket.artifacts.bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
output "s3_frontend_bucket" {
|
||||||
|
description = "Nombre del bucket del frontend React"
|
||||||
|
value = aws_s3_bucket.sacc_frontend.bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudfront_domain" {
|
||||||
|
description = "Dominio de CloudFront"
|
||||||
|
value = aws_cloudfront_distribution.sacc_cdn.domain_name
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Después de ejecutar `terraform apply`, verás estos valores en la pantalla.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Cómo ejecutar Terraform paso a paso
|
||||||
|
|
||||||
|
### Paso 1: Ir a la carpeta de Terraform
|
||||||
|
```bash
|
||||||
|
cd /home/evert/Servidores/Nuve/AWS/proyectosacc/terraform
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 2: Inicializar Terraform
|
||||||
|
```bash
|
||||||
|
terraform init
|
||||||
|
```
|
||||||
|
Esto descarga los plugins necesarios para hablar con AWS.
|
||||||
|
|
||||||
|
### Paso 3: Revisar el plan
|
||||||
|
```bash
|
||||||
|
terraform plan
|
||||||
|
```
|
||||||
|
Terraform te muestra **todo lo que va a crear, modificar o destruir**. Revísalo con cuidado.
|
||||||
|
|
||||||
|
### Paso 4: Aplicar los cambios
|
||||||
|
```bash
|
||||||
|
terraform apply
|
||||||
|
```
|
||||||
|
Terraform te pedirá que escribas `yes` para confirmar. Luego empezará a crear los recursos en AWS.
|
||||||
|
|
||||||
|
### Paso 5: Ver los outputs
|
||||||
|
Al finalizar, Terraform mostrará los valores definidos en `outputs.tf`:
|
||||||
|
- IP pública de la EC2 (API backend)
|
||||||
|
- Endpoint de la base de datos
|
||||||
|
- Nombre del bucket S3 de artefactos
|
||||||
|
- Nombre del bucket S3 del frontend
|
||||||
|
- Dominio de CloudFront
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Comandos útiles adicionales
|
||||||
|
|
||||||
|
| Comando | Para qué sirve |
|
||||||
|
|---------|----------------|
|
||||||
|
| `terraform validate` | Revisa que la sintaxis de los archivos esté correcta. |
|
||||||
|
| `terraform fmt` | Ordena y da formato a los archivos `.tf`. |
|
||||||
|
| `terraform show` | Muestra el estado actual de la infraestructura. |
|
||||||
|
| `terraform destroy` | **Elimina** todos los recursos creados. ¡Úsalo con cuidado! |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Checklist antes de ejecutar Terraform
|
||||||
|
|
||||||
|
- [ ] Tienes configuradas las credenciales de AWS (`aws configure`).
|
||||||
|
- [ ] La variable `db_password` no está escrita en el código, viene de un archivo seguro o variable de entorno.
|
||||||
|
- [ ] Revisaste el `terraform plan` antes de aplicar.
|
||||||
|
- [ ] Sabes en qué región de AWS se va a crear todo (`us-east-1`, `us-west-2`, etc.).
|
||||||
|
- [ ] El bucket S3 para el estado remoto ya existe (si usas backend remoto).
|
||||||
|
- [ ] El `user_data.sh` incluye la llave pública SSH dedicada para el pipeline.
|
||||||
|
- [ ] El bucket S3 del frontend tiene habilitado el versionamiento de objetos (para rollback del frontend).
|
||||||
|
- [ ] CloudFront tiene configurado el Origin Access Identity (OAI) u Origin Access Control (OAC) para leer de S3.
|
||||||
|
- [ ] Route 53 apunta a CloudFront, no a la IP de la EC2.
|
||||||
|
- [ ] CloudFront tiene un behavior `/api/*` que apunta a la EC2.
|
||||||
|
- [ ] La EC2 tiene el Security Group configurado para recibir tráfico HTTP desde CloudFront.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`06-scripts-de-despliegue.md`](06-scripts-de-despliegue.md)*
|
||||||
|
*Siguiente: [`08-seguridad-y-secretos.md`](08-seguridad-y-secretos.md)*
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
# 08 - Seguridad y Secretos
|
||||||
|
|
||||||
|
> Cómo protegemos las contraseñas, llaves y datos sensibles de `proyectosacc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Qué es un "secreto"?
|
||||||
|
|
||||||
|
Un **secreto** es cualquier información que no debe ser pública. En `proyectosacc`, los secrets incluyen:
|
||||||
|
|
||||||
|
- Contraseñas de bases de datos.
|
||||||
|
- Llaves SSH privadas.
|
||||||
|
- Tokens de Telegram.
|
||||||
|
- Credenciales de AWS (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`).
|
||||||
|
|
||||||
|
> 💡 **Regla de oro**: si perder esa información causaría un problema de seguridad, es un secreto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. ¿Dónde viven los secrets?
|
||||||
|
|
||||||
|
### Opción 1: Bitbucket Repository Variables (la más usada)
|
||||||
|
|
||||||
|
En Bitbucket Pipelines podemos guardar variables de entorno que los scripts usan sin exponer su valor.
|
||||||
|
|
||||||
|
**Cómo configurarlas:**
|
||||||
|
1. Ve al repositorio en Bitbucket.
|
||||||
|
2. Entra a **Repository settings > Pipelines > Repository variables**.
|
||||||
|
3. Agrega el nombre y el valor.
|
||||||
|
4. **Marca la casilla "Secured"** 🔒.
|
||||||
|
|
||||||
|
**Ventaja**: cuando el pipeline corre, las variables secured aparecen como `***` en los logs. Nadie puede leerlas.
|
||||||
|
|
||||||
|
### Opción 2: AWS Secrets Manager
|
||||||
|
|
||||||
|
AWS tiene un servicio llamado **Secrets Manager** que guarda secrets de forma segura y permite rotarlos automáticamente.
|
||||||
|
|
||||||
|
**Ejemplo de uso en un pipeline de CodeBuild:**
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
secrets-manager:
|
||||||
|
DB_PASSWORD: "prod/database/password"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opción 3: AWS Systems Manager Parameter Store
|
||||||
|
|
||||||
|
Similar a Secrets Manager, pero más simple y económico. Se usa para guardar configuraciones sensibles.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
parameter-store:
|
||||||
|
DATABASE_URL: "/app/database/url"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opción 4: Archivo `.env` (solo local)
|
||||||
|
|
||||||
|
En tu computadora personal puedes tener un archivo `.env` con variables locales. **Este archivo NUNCA debe subirse al repositorio.**
|
||||||
|
|
||||||
|
El archivo `.gitignore` del proyecto ya debería incluir `.env` para evitar accidentes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. ¿Qué NUNCA poner en el repositorio?
|
||||||
|
|
||||||
|
### ❌ NUNCA hagas esto
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Mal ejemplo en bitbucket-pipelines.yml
|
||||||
|
- step:
|
||||||
|
script:
|
||||||
|
- export DB_PASSWORD="supersecreta123"
|
||||||
|
- export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# Mal ejemplo en Terraform
|
||||||
|
resource "aws_db_instance" "example" {
|
||||||
|
password = "mi-contrasena-123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mal ejemplo en un script
|
||||||
|
API_KEY="sk-1234567890abcdef"
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ SIEMPRE haz esto
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Bien: usa variables de entorno
|
||||||
|
- step:
|
||||||
|
script:
|
||||||
|
- export DB_PASSWORD="$DB_PASSWORD"
|
||||||
|
```
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# Bien: usa variables de Terraform
|
||||||
|
resource "aws_db_instance" "example" {
|
||||||
|
password = var.db_password
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Bien: lee desde una variable de entorno
|
||||||
|
API_KEY="$API_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. ¿Cómo rotar las llaves SSH?
|
||||||
|
|
||||||
|
**Rotar** una llave significa crear una nueva y dejar de usar la anterior. Es una buena práctica de seguridad, especialmente si alguien con acceso a la llave privada deja la empresa.
|
||||||
|
|
||||||
|
### Paso 1: Generar un nuevo par de llaves
|
||||||
|
```bash
|
||||||
|
ssh-keygen -t ed25519 -b 4096 \
|
||||||
|
-C "bitbucket.pipeline.ci.cd.proyectosacc.thoth.develop@computocontable.com" \
|
||||||
|
-f bitbucket_pipeline_ci_cd_TO_proyectosacc_server_thoth_develop
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 2: Codificar la llave privada en base64
|
||||||
|
```bash
|
||||||
|
base64 -i bitbucket_pipeline_ci_cd_TO_proyectosacc_server_thoth_develop | tr -d '\n' > nueva_llave.b64
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 3: Actualizar la variable en Bitbucket
|
||||||
|
1. Copia el contenido de `nueva_llave.b64`.
|
||||||
|
2. Ve a **Repository settings > Pipelines > Repository variables**.
|
||||||
|
3. Actualiza la variable `PROYECTOSACC_SSH_KEY` con el nuevo valor.
|
||||||
|
|
||||||
|
### Paso 4: Colocar la llave pública en el servidor
|
||||||
|
```bash
|
||||||
|
# Copia el contenido del archivo .pub
|
||||||
|
cat bitbucket_pipeline_ci_cd_TO_proyectosacc_server_thoth_develop.pub
|
||||||
|
|
||||||
|
# Conéctate al servidor y agrégala a authorized_keys
|
||||||
|
ssh ubuntu@<IP_DE_LA_EC2>
|
||||||
|
sudo su - thoth
|
||||||
|
nano ~/.ssh/authorized_keys
|
||||||
|
# Pega la nueva llave pública al final del archivo
|
||||||
|
|
||||||
|
> 💡 **Tip**: en un despliegue automatizado con Terraform, esta llave pública se inyecta directamente desde el archivo `user_data`. No hace falta editar `authorized_keys` a mano.
|
||||||
|
|
||||||
|
### Paso 5: Probar la conexión
|
||||||
|
Ejecuta un pipeline de prueba o conéctate manualmente con la nueva llave privada para confirmar que funciona.
|
||||||
|
|
||||||
|
### Paso 6: Eliminar la llave anterior
|
||||||
|
Una vez confirmado que la nueva llave funciona:
|
||||||
|
1. Borra la llave pública antigua de `~/.ssh/authorized_keys`.
|
||||||
|
2. Borra la llave privada antigua de tu computadora.
|
||||||
|
3. Actualiza cualquier otra copia que exista.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Seguridad del frontend en S3 + CloudFront
|
||||||
|
|
||||||
|
### Bucket policy para CloudFront (OAI)
|
||||||
|
El bucket S3 del frontend NO debe ser público. Solo CloudFront puede leer los objetos:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [{
|
||||||
|
"Sid": "AllowCloudFrontOAI",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"CanonicalUser": "<OAI-canonical-user-id>"
|
||||||
|
},
|
||||||
|
"Action": "s3:GetObject",
|
||||||
|
"Resource": "arn:aws:s3:::ccsoft-proyectosacc-frontend/*"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CORS en S3 (si el frontend llama a la API)
|
||||||
|
Como el frontend React y la API backend comparten el mismo dominio `https://sacc.ccsoft.mx` (gracias al behavior `/api/*` de CloudFront), **no hay problema de CORS**. Si en el futuro decides mover la API a un subdominio diferente (por ejemplo, `api.sacc.ccsoft.mx`), ahí sí necesitarás configurar CORS en el backend.
|
||||||
|
|
||||||
|
### Versionamiento de objetos en S3
|
||||||
|
Habilita el versionamiento en el bucket del frontend. Así puedes restaurar una versión anterior del sitio si un deploy falla:
|
||||||
|
```bash
|
||||||
|
aws s3api put-bucket-versioning \
|
||||||
|
--bucket ccsoft-proyectosacc-frontend \
|
||||||
|
--versioning-configuration Status=Enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Checklist de seguridad básica
|
||||||
|
|
||||||
|
- [ ] El archivo `.env` está en `.gitignore`.
|
||||||
|
- [ ] Ningún archivo del repositorio contiene contraseñas escritas directamente.
|
||||||
|
- [ ] Las variables de Bitbucket están marcadas como **Secured**.
|
||||||
|
- [ ] La llave privada SSH dedicada para `proyectosacc` está en formato **base64** en Bitbucket.
|
||||||
|
- [ ] No se usa acceso SSH por contraseña ni herramientas como `sshpass`.
|
||||||
|
- [ ] El puerto SSH (22) de la EC2 solo está abierto a IPs conocidas.
|
||||||
|
- [ ] La base de datos RDS no es accesible desde internet (solo desde la VPC).
|
||||||
|
- [ ] Los certificados SSL están configurados correctamente en **CloudFront** (HTTPS obligatorio).
|
||||||
|
- [ ] El bucket S3 del frontend NO es público; solo CloudFront tiene acceso mediante OAI/OAC.
|
||||||
|
- [ ] El versionamiento de objetos está habilitado en el bucket S3 del frontend.
|
||||||
|
- [ ] Se ha programado una rotación de llaves SSH al menos una vez al año.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`07-terraform-iac.md`](07-terraform-iac.md)*
|
||||||
|
*Siguiente: [`09-runbook-deploy.md`](09-runbook-deploy.md)*
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
# 09 - Runbook: Deploy Manual
|
||||||
|
|
||||||
|
> Qué hacer si el pipeline automático falla y necesitas desplegar a mano.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Cuándo hacer un deploy manual?
|
||||||
|
|
||||||
|
Un **deploy manual** significa que tú, persona humana, ejecutas los pasos que normalmente hace el pipeline automático.
|
||||||
|
|
||||||
|
Haz un deploy manual cuando:
|
||||||
|
- 🔴 El pipeline de Bitbucket falló y no se puede reintentar.
|
||||||
|
- 🟡 Necesitas desplegar una corrección urgente fuera de horario.
|
||||||
|
- 🟠 El servidor EC2 fue recreado y el pipeline no está configurado todavía.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Paso a paso: deploy manual
|
||||||
|
|
||||||
|
Un deploy manual ahora tiene **dos partes**: subir el frontend a S3 + CloudFront, y desplegar la API en la EC2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PARTE A: Subir el frontend React a S3 e invalidar CloudFront
|
||||||
|
|
||||||
|
Esta parte puedes hacerla desde tu computadora local o desde cualquier máquina con AWS CLI configurada.
|
||||||
|
|
||||||
|
#### Paso A1: Ve a la carpeta del frontend compilado
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /ruta/al/proyecto/frontend/build
|
||||||
|
```
|
||||||
|
|
||||||
|
Si no has compilado el frontend aún:
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso A2: Sincroniza la carpeta `build/` con S3
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws s3 sync . s3://ccsoft-proyectosacc-frontend --delete
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso A3: Invalida la caché de CloudFront
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws cloudfront create-invalidation \
|
||||||
|
--distribution-id <DISTRIBUTION_ID> \
|
||||||
|
--paths "/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Nota**: el `DISTRIBUTION_ID` lo encuentras en la consola de AWS o en los outputs de Terraform.
|
||||||
|
|
||||||
|
#### Paso A4: Verifica el frontend
|
||||||
|
|
||||||
|
Abre tu navegador y visita:
|
||||||
|
```
|
||||||
|
https://sacc.ccsoft.mx
|
||||||
|
```
|
||||||
|
|
||||||
|
Si ves la interfaz de SACC, la parte A fue exitosa.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PARTE B: Desplegar la API backend en la EC2
|
||||||
|
|
||||||
|
#### Paso B1: Conéctate al servidor EC2
|
||||||
|
|
||||||
|
Necesitas la IP pública de la instancia y la **llave SSH dedicada** generada para `proyectosacc`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/tu_llave.pem ubuntu@<IP_DE_LA_EC2>
|
||||||
|
```
|
||||||
|
|
||||||
|
O si ya estás dentro de la red de la empresa:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh thoth@<IP_DE_LA_EC2>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Paso B2: Conviértete en el usuario thoth
|
||||||
|
|
||||||
|
Si entraste como `ubuntu`, cambia al usuario de despliegue:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo su - thoth
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Paso B3: Ve al directorio de despliegue
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/thoth/deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Paso B4: Descarga el artefacto compilado de la API
|
||||||
|
|
||||||
|
Si el artefacto está en S3, descárgalo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws s3 cp s3://ccsoft-artifacts-sacc4/develop/proyectosacc-app-1.0.0.jar \
|
||||||
|
/home/thoth/deploy/artifacts/current/
|
||||||
|
```
|
||||||
|
|
||||||
|
Si no tienes acceso a S3, pide el archivo `.jar` a un compañero y súbelo con `scp`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Paso B5: Ejecuta el script de deploy de la API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash /home/thoth/deploy/setup/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Este script hará lo siguiente automáticamente:
|
||||||
|
1. Hará backup de la versión anterior.
|
||||||
|
2. Detendrá el servicio actual.
|
||||||
|
3. Copiará el nuevo archivo a su lugar.
|
||||||
|
4. Configurará y recargará `systemd`.
|
||||||
|
5. Iniciará el nuevo servicio.
|
||||||
|
6. Hará un health check.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Paso B6: Verifica que el servicio esté corriendo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl status proyectosacc-app.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Deberías ver algo como:
|
||||||
|
```
|
||||||
|
● proyectosacc-app.service - Proyecto SACC App Service
|
||||||
|
Loaded: loaded (/etc/systemd/system/proyectosacc-app.service; enabled)
|
||||||
|
Active: active (running) since Mon 2026-04-14 10:30:00 UTC
|
||||||
|
```
|
||||||
|
|
||||||
|
Si dice `active (running)`, ¡excelente!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Paso B7: Verifica el health check de la API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/actuator/health
|
||||||
|
```
|
||||||
|
|
||||||
|
La respuesta debe incluir:
|
||||||
|
```json
|
||||||
|
{"status":"UP"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Paso B8: Verifica la API desde fuera
|
||||||
|
|
||||||
|
Desde tu navegador o con `curl`, prueba un endpoint de la API:
|
||||||
|
```bash
|
||||||
|
curl https://sacc.ccsoft.mx/api/actuator/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Si responde correctamente, el deploy manual completo fue exitoso.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Cómo revisar si el deploy funcionó
|
||||||
|
|
||||||
|
### Frontend (S3 + CloudFront)
|
||||||
|
- Abre `https://sacc.ccsoft.mx` en el navegador.
|
||||||
|
- Revisa la consola de AWS > S3 para ver que los archivos nuevos estén en el bucket.
|
||||||
|
- Verifica el estado de la invalidación de CloudFront en la consola de AWS.
|
||||||
|
|
||||||
|
### API (EC2)
|
||||||
|
|
||||||
|
#### Revisar los logs del servicio
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u proyectosacc-app.service -n 50 --no-pager
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Revisar el archivo de log de la aplicación
|
||||||
|
```bash
|
||||||
|
tail -n 50 /var/log/proyectosacc/proyectosacc-app/proyectosacc-app-service.log
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Revisar que Nginx esté funcionando
|
||||||
|
```bash
|
||||||
|
sudo systemctl status nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Revisar que la API responda en el puerto correcto
|
||||||
|
```bash
|
||||||
|
ss -tlnp | grep 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Errores comunes y cómo arreglarlos
|
||||||
|
|
||||||
|
### Error 1: "Permission denied" al conectarse por SSH
|
||||||
|
**Causa**: la llave SSH no tiene los permisos correctos o no está en `authorized_keys`.
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```bash
|
||||||
|
# En tu computadora local
|
||||||
|
chmod 600 ~/.ssh/tu_llave.pem
|
||||||
|
|
||||||
|
# En el servidor, como thoth
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
chmod 600 ~/.ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Error 2: "No such file or directory" al ejecutar deploy.sh
|
||||||
|
**Causa**: el script no está donde esperas o la ruta es incorrecta.
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```bash
|
||||||
|
# Busca el script
|
||||||
|
find /home/thoth -name "deploy.sh"
|
||||||
|
|
||||||
|
# Luego ejecútalo con la ruta completa
|
||||||
|
bash /home/thoth/deploy/setup/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Error 3: El servicio no inicia (failed)
|
||||||
|
**Causa**: puede ser que falta Java, que el puerto 8080 ya está ocupado, o que hay un error en el archivo `.jar`.
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```bash
|
||||||
|
# Ver qué dice el log
|
||||||
|
sudo journalctl -u proyectosacc-app.service -n 100 --no-pager
|
||||||
|
|
||||||
|
# Ver si el puerto 8080 está ocupado
|
||||||
|
sudo lsof -i :8080
|
||||||
|
|
||||||
|
# Verificar que Java esté instalado
|
||||||
|
java -version
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Error 4: Health check falla después del deploy
|
||||||
|
**Causa**: la aplicación inició pero no responde correctamente, o tarda más de lo esperado.
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```bash
|
||||||
|
# Espera unos segundos más y vuelve a intentar
|
||||||
|
curl -v http://localhost:8080/actuator/health
|
||||||
|
|
||||||
|
# Revisa si hay errores en el log de la app
|
||||||
|
tail -n 100 /var/log/proyectosacc/proyectosacc-app/proyectosacc-app-service.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Si sigue fallando, considera ejecutar el **rollback** (ver [`10-runbook-rollback.md`](10-runbook-rollback.md)).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`08-seguridad-y-secretos.md`](08-seguridad-y-secretos.md)*
|
||||||
|
*Siguiente: [`10-runbook-rollback.md`](10-runbook-rollback.md)*
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
# 10 - Runbook: Rollback
|
||||||
|
|
||||||
|
> Cómo regresar a la versión anterior de la aplicación cuando algo sale mal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Qué es un rollback?
|
||||||
|
|
||||||
|
Un **rollback** es el proceso de "deshacer" un despliegue. Si la nueva versión de la aplicación tiene errores, el rollback vuelve a poner la versión anterior para que los usuarios no se vean afectados.
|
||||||
|
|
||||||
|
> 💡 **Analogía simple**: es como cuando instalas una actualización en tu celular y algo deja de funcionar, así que regresas a la versión anterior del sistema.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. ¿Cuándo hacer un rollback?
|
||||||
|
|
||||||
|
Haz rollback **inmediatamente** si ves alguna de estas situaciones:
|
||||||
|
|
||||||
|
- 🔴 El **health check** falla después del despliegue.
|
||||||
|
- 🔴 La aplicación se cae al poco tiempo de iniciar.
|
||||||
|
- 🟡 Los usuarios reportan errores graves que no existían antes.
|
||||||
|
- 🟡 El pipeline detectó un fallo en el paso de deploy.
|
||||||
|
- 🟠 El servicio de `systemd` no puede arrancar.
|
||||||
|
|
||||||
|
> ⚠️ **Regla de oro**: si dudas, haz rollback. Es mejor tener la versión anterior estable mientras investigas el problema.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Paso a paso: cómo hacer rollback
|
||||||
|
|
||||||
|
El rollback ahora tiene **dos partes**: el frontend en S3 y la API en la EC2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PARTE A: Rollback del frontend en S3
|
||||||
|
|
||||||
|
Si el problema es visual (interfaz rota, pantalla en blanco, etc.), restaura el frontend anterior.
|
||||||
|
|
||||||
|
#### Paso A1: Lista las versiones anteriores del archivo `index.html`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws s3api list-object-versions \
|
||||||
|
--bucket ccsoft-proyectosacc-frontend \
|
||||||
|
--prefix index.html \
|
||||||
|
--query 'Versions[?IsLatest==`false`].[VersionId,LastModified]' \
|
||||||
|
--output table
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso A2: Restaura la versión anterior
|
||||||
|
|
||||||
|
Copia el `VersionId` de la versión anterior y restáurala:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws s3api copy-object \
|
||||||
|
--bucket ccsoft-proyectosacc-frontend \
|
||||||
|
--copy-source ccsoft-proyectosacc-frontend/index.html?versionId=<VERSION_ID> \
|
||||||
|
--key index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso A3: Invalida la caché de CloudFront
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws cloudfront create-invalidation \
|
||||||
|
--distribution-id <DISTRIBUTION_ID> \
|
||||||
|
--paths "/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso A4: Verifica el frontend
|
||||||
|
|
||||||
|
Abre `https://sacc.ccsoft.mx` y confirma que la interfaz anterior ha vuelto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PARTE B: Rollback de la API en la EC2
|
||||||
|
|
||||||
|
Si el problema es del backend (errores 500, API caída, etc.), restaura la API.
|
||||||
|
|
||||||
|
#### Paso B1: Conéctate al servidor EC2
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/tu_llave.pem ubuntu@<IP_DE_LA_EC2>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso B2: Cambia al usuario thoth
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo su - thoth
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso B3: Ve al directorio de artefactos
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/thoth/deploy/artifacts
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso B4: Revisa los backups disponibles
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -lah backup/
|
||||||
|
```
|
||||||
|
|
||||||
|
Verás algo como:
|
||||||
|
```
|
||||||
|
proyectosacc-app-20260414_103000.jar
|
||||||
|
proyectosacc-app-20260413_154500.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Cada archivo tiene una fecha y hora en su nombre.
|
||||||
|
|
||||||
|
#### Paso B5: Identifica cuál es la versión anterior estable
|
||||||
|
|
||||||
|
Normalmente es el backup **más reciente antes del deploy fallido**.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ordena por fecha para ver el más reciente
|
||||||
|
ls -lt backup/ | head -5
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso B6: Detén el servicio actual
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop proyectosacc-app.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso B7: Reemplaza el archivo actual con el backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Guarda una copia del archivo fallido (por si necesitas investigarlo después)
|
||||||
|
mv current/proyectosacc-app.jar current/proyectosacc-app.jar.fallo-$(date +%Y%m%d_%H%M%S)
|
||||||
|
|
||||||
|
# Copia el backup al directorio current
|
||||||
|
cp backup/proyectosacc-app-20260414_103000.jar current/proyectosacc-app.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Tip**: reemplaza `20260414_103000` con la fecha real del backup que quieres restaurar.
|
||||||
|
|
||||||
|
#### Paso B8: Recarga systemd y reinicia el servicio
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl start proyectosacc-app.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso B9: Verifica que el servicio esté corriendo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl status proyectosacc-app.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Debe decir `active (running)`.
|
||||||
|
|
||||||
|
#### Paso B10: Ejecuta el health check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/actuator/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Debe responder:
|
||||||
|
```json
|
||||||
|
{"status":"UP"}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Paso B11: Verifica la API desde fuera
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://sacc.ccsoft.mx/api/actuator/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Si todo funciona, el rollback fue exitoso.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Cómo verificar que el rollback funcionó
|
||||||
|
|
||||||
|
### Checklist de verificación (API)
|
||||||
|
|
||||||
|
- [ ] El servicio `systemd` está en estado `active (running)`.
|
||||||
|
- [ ] El health check responde `"status":"UP"`.
|
||||||
|
- [ ] El endpoint `https://sacc.ccsoft.mx/api/actuator/health` responde sin errores.
|
||||||
|
- [ ] Los usuarios pueden iniciar sesión y usar la aplicación.
|
||||||
|
- [ ] No hay errores críticos recientes en los logs.
|
||||||
|
|
||||||
|
### Checklist de verificación (Frontend)
|
||||||
|
|
||||||
|
- [ ] La página `https://sacc.ccsoft.mx` carga sin errores.
|
||||||
|
- [ ] La invalidación de CloudFront se completó.
|
||||||
|
- [ ] No hay errores de consola del navegador relacionados con assets faltantes.
|
||||||
|
|
||||||
|
### Comandos útiles para verificar
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Estado del servicio API
|
||||||
|
sudo systemctl status proyectosacc-app.service
|
||||||
|
|
||||||
|
# Health check local
|
||||||
|
curl -s http://localhost:8080/actuator/health | grep '"status"'
|
||||||
|
|
||||||
|
# Health check remoto
|
||||||
|
curl -s https://sacc.ccsoft.mx/api/actuator/health | grep '"status"'
|
||||||
|
|
||||||
|
# Últimos logs
|
||||||
|
tail -n 30 /var/log/proyectosacc/proyectosacc-app/proyectosacc-app-service.log
|
||||||
|
|
||||||
|
# Verificar que Nginx sigue redirigiendo bien la API
|
||||||
|
sudo systemctl status nginx
|
||||||
|
|
||||||
|
# Verificar invalidación de CloudFront
|
||||||
|
aws cloudfront list-invalidations --distribution-id <DISTRIBUTION_ID>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Después del rollback
|
||||||
|
|
||||||
|
Una vez que la versión anterior esté estable:
|
||||||
|
|
||||||
|
1. **Investiga qué falló** en la nueva versión.
|
||||||
|
2. **Revisa los logs** del intento fallido.
|
||||||
|
3. **Notifica al equipo** por Telegram o el canal de comunicación que usen.
|
||||||
|
4. **Corrige el problema** en el código.
|
||||||
|
5. **Haz un nuevo deploy** solo cuando estés seguro de que el error está corregido.
|
||||||
|
|
||||||
|
### Mensaje de notificación sugerido
|
||||||
|
|
||||||
|
```
|
||||||
|
🚨 Rollback ejecutado en proyectosacc
|
||||||
|
Motivo: [describe brevemente el problema]
|
||||||
|
Estado actual: Aplicación estable con versión anterior
|
||||||
|
Próximo paso: Investigación del error en la nueva versión
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`09-runbook-deploy.md`](09-runbook-deploy.md)*
|
||||||
|
*Siguiente: [`11-checklist-primer-deploy.md`](11-checklist-primer-deploy.md)*
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
# 11 - Checklist: Primer Deploy
|
||||||
|
|
||||||
|
> Lista de verificación de todo lo que debe estar listo antes de hacer el primer despliegue de `proyectosacc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Infraestructura AWS
|
||||||
|
|
||||||
|
1. [ ] La instancia **EC2 T3.small** con **Ubuntu 22.04** está creada y encendida.
|
||||||
|
2. [ ] La **EC2** tiene un **Security Group** que permite:
|
||||||
|
- Puerto `22` (SSH) desde IPs confiables.
|
||||||
|
- Puerto `80` (HTTP) desde cualquier lugar (solo para la API).
|
||||||
|
- Puerto `443` (HTTPS) desde cualquier lugar (solo para la API).
|
||||||
|
3. [ ] La **RDS MariaDB** (`db.t3.micro`) está creada y accesible desde la VPC.
|
||||||
|
4. [ ] El **bucket S3** para artefactos de la API está creado y la EC2 tiene permisos para leer/escribir en él.
|
||||||
|
5. [ ] El **bucket S3 para el frontend React** está creado con **Static Website Hosting** habilitado.
|
||||||
|
6. [ ] El bucket S3 del frontend tiene **versionamiento de objetos** habilitado.
|
||||||
|
7. [ ] La **distribución CloudFront** está creada, apunta al bucket S3 del frontend, tiene HTTPS y un behavior `/api/*` que apunta a la EC2.
|
||||||
|
8. [ ] El registro **Route 53** `sacc.ccsoft.mx` apunta a **CloudFront** (no a la IP de la EC2).
|
||||||
|
9. [ ] El certificado **ACM** para `sacc.ccsoft.mx` está emitido, válido y adjunto a **CloudFront**.
|
||||||
|
10. [ ] El bucket S3 del frontend tiene un **bucket policy** que permite leer objetos solo a CloudFront (OAI/OAC).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Servidor EC2 (configuración interna)
|
||||||
|
|
||||||
|
11. [ ] El usuario `ubuntu` puede conectarse por SSH.
|
||||||
|
12. [ ] El usuario `thoth` está creado con su directorio `/home/thoth`.
|
||||||
|
13. [ ] El usuario `osiris` está creado con su directorio `/home/osiris`.
|
||||||
|
14. [ ] Los directorios de despliegue existen:
|
||||||
|
```
|
||||||
|
/home/thoth/deploy/artifacts/backup
|
||||||
|
/home/thoth/deploy/artifacts/current
|
||||||
|
/home/thoth/deploy/artifacts/logs
|
||||||
|
/home/thoth/deploy/artifacts/pids
|
||||||
|
```
|
||||||
|
15. [ ] **Nginx** está instalado, configurado como proxy inverso **solo para la API** (`/api` → `localhost:8080`) y corriendo.
|
||||||
|
16. [ ] **Java** (`openjdk-21-jdk`) está instalado.
|
||||||
|
17. [ ] **AWS CLI** está instalado y configurado.
|
||||||
|
18. [ ] Los permisos de carpetas están correctos (`thoth` y `osiris` pueden acceder donde deben).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Llaves SSH
|
||||||
|
|
||||||
|
15. [ ] Se generó el **par de llaves SSH dedicado** para `proyectosacc` (no se reutilizan llaves de otros proyectos).
|
||||||
|
16. [ ] La **llave privada** está codificada en base64 y guardada en **Bitbucket Repository Variables** (`PROYECTOSACC_SSH_KEY`).
|
||||||
|
17. [ ] La **llave pública** está en `/home/thoth/.ssh/authorized_keys` (inyectada vía Terraform `user_data`).
|
||||||
|
18. [ ] Se generó el par de llaves SSH para que `thoth` clone repositorios desde Bitbucket.
|
||||||
|
19. [ ] Las **Access Keys** correspondientes están configuradas en los repositorios de Bitbucket (`ci-cd-commons`, `ci-cd-saac4`).
|
||||||
|
20. [ ] Se probó la conexión SSH desde el pipeline (o manualmente) y funciona.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Base de datos
|
||||||
|
|
||||||
|
21. [ ] La base de datos RDS está accesible desde la EC2.
|
||||||
|
22. [ ] Se creó el esquema o las tablas necesarias para la aplicación.
|
||||||
|
23. [ ] La aplicación tiene configurada la URL de conexión a la base de datos.
|
||||||
|
24. [ ] Las credenciales de la base de datos están guardadas de forma segura (no en el código fuente).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Pipeline de Bitbucket
|
||||||
|
|
||||||
|
30. [ ] El archivo `bitbucket-pipelines.yml` está en la raíz del repositorio.
|
||||||
|
31. [ ] Los 7 pasos del pipeline están configurados correctamente:
|
||||||
|
- Paso 4 compila tanto el frontend React (`npm run build`) como la API backend.
|
||||||
|
- Paso 5 sube el frontend a S3 y el `.jar` de la API a S3.
|
||||||
|
- Paso 7 invalida la caché de CloudFront y reinicia la API.
|
||||||
|
32. [ ] Las **variables de entorno** están creadas en Bitbucket:
|
||||||
|
- `PROYECTOSACC_SSH_KEY`
|
||||||
|
- `PROYECTOSACC_SERVER_IP`
|
||||||
|
- `PROYECTOSACC_SSH_PORT`
|
||||||
|
- `PROYECTOSACC_SERVER_USER`
|
||||||
|
- `PROYECTOSACC_TELEGRAM_BOT_TOKEN`
|
||||||
|
- `PROYECTOSACC_TELEGRAM_CHAT_ID`
|
||||||
|
- `AWS_ACCESS_KEY_ID`
|
||||||
|
- `AWS_SECRET_ACCESS_KEY`
|
||||||
|
- `CLOUDFRONT_DISTRIBUTION_ID`
|
||||||
|
- `S3_FRONTEND_BUCKET`
|
||||||
|
33. [ ] Todas las variables sensibles están marcadas como **Secured** 🔒.
|
||||||
|
34. [ ] El pipeline puede clonar los repositorios necesarios (`ci-cd-commons`, `ci-cd-saac4`, etc.).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Scripts de despliegue
|
||||||
|
|
||||||
|
35. [ ] El script `deploy-frontend-s3.sh` existe y puede subir archivos a S3 + invalidar CloudFront.
|
||||||
|
36. [ ] El script `setup/deploy.sh` existe en el servidor y tiene permisos de ejecución (solo para la API).
|
||||||
|
37. [ ] El script `health-check.sh` existe y funciona correctamente.
|
||||||
|
38. [ ] El script `rollback.sh` existe y tiene al menos un backup de prueba de la API.
|
||||||
|
39. [ ] El script `notify-telegram.sh` envía mensajes de prueba exitosamente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Aplicación SACC
|
||||||
|
|
||||||
|
40. [ ] El código fuente del **frontend React** compila sin errores (`npm run build`).
|
||||||
|
41. [ ] El código fuente de la **API backend** compila sin errores localmente.
|
||||||
|
42. [ ] El pipeline puede generar el artefacto final del frontend (`build/`) y de la API (`.jar`).
|
||||||
|
43. [ ] El artefacto de la API se puede ejecutar manualmente en la EC2.
|
||||||
|
44. [ ] La API responde en `http://localhost:8080/actuator/health` cuando corre localmente en la EC2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Dominio y SSL
|
||||||
|
|
||||||
|
45. [ ] El dominio `sacc.ccsoft.mx` resuelve a **CloudFront** (verifica con `dig` o `nslookup`).
|
||||||
|
46. [ ] **CloudFront** sirve el frontend React por HTTPS sin errores de certificado.
|
||||||
|
47. [ ] **Nginx** en la EC2 redirige las peticiones de la API correctamente (`/api` → `localhost:8080`).
|
||||||
|
48. [ ] El endpoint `https://sacc.ccsoft.mx/api/actuator/health` responde correctamente.
|
||||||
|
49. [ ] Al entrar a `https://sacc.ccsoft.mx` se ve la interfaz de SACC (o al menos una página de prueba del frontend).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Seguridad
|
||||||
|
|
||||||
|
50. [ ] El archivo `.env` (si existe) está en `.gitignore`.
|
||||||
|
51. [ ] No hay contraseñas, tokens ni llaves privadas en el código del repositorio.
|
||||||
|
52. [ ] No se usa `sshpass` ni acceso SSH por contraseña.
|
||||||
|
53. [ ] El puerto SSH (22) del Security Group no está abierto a `0.0.0.0/0`.
|
||||||
|
54. [ ] La base de datos RDS no es pública (solo accesible desde la VPC).
|
||||||
|
55. [ ] El bucket S3 del frontend NO es público directamente; solo CloudFront accede a él (OAI/OAC).
|
||||||
|
56. [ ] Los logs sensibles no se imprimen en los archivos de configuración del pipeline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Comunicación y documentación
|
||||||
|
|
||||||
|
57. [ ] El equipo de desarrollo sabe que el primer deploy se va a realizar.
|
||||||
|
58. [ ] Hay un canal de comunicación activo (Telegram, Slack, etc.) para alertas.
|
||||||
|
59. [ ] Este checklist ha sido revisado por al menos una persona más.
|
||||||
|
60. [ ] Se tiene planificado un horario de bajo tráfico para el primer deploy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ¿Listo?
|
||||||
|
|
||||||
|
Si marcaste **todas** las 60 casillas, estás listo para hacer el primer deploy. 🚀
|
||||||
|
|
||||||
|
Si te falta alguna, resuélvela antes de continuar. Es mejor tardar un poco más y hacerlo bien, que apurarse y dejar el servicio caído.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Anterior: [`10-runbook-rollback.md`](10-runbook-rollback.md)*
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# proyectosacc-api.conf - Configuración de Nginx para API de proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Proxy inverso EXCLUSIVO para la API backend. Nginx escucha en
|
||||||
|
# puerto 80 y redirige /api/ hacia localhost:8080.
|
||||||
|
# NO sirve el frontend React (eso es S3 + CloudFront).
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# Copiar a /etc/nginx/sites-available/proyectosacc-api y crear
|
||||||
|
# enlace simbólico en sites-enabled.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Proxy inverso hacia la API backend en localhost:8080
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:8080/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cualquier otra ruta en la EC2 debe devolver 404
|
||||||
|
location / {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Página de error genérica
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
location = /50x.html {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ===============================================================================================================
|
||||||
|
# deploy-frontend-s3.sh - Sincroniza el frontend React con S3
|
||||||
|
# Descripción:
|
||||||
|
# Sube la carpeta build/ al bucket S3 designado para el sitio estático.
|
||||||
|
# La invalidación de CloudFront se realiza en el pipeline (paso 7).
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# S3_FRONTEND_BUCKET=ccsoft-proyectosacc-frontend bash deploy-frontend-s3.sh
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Validación de variables
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
S3_FRONTEND_BUCKET="${S3_FRONTEND_BUCKET:-}"
|
||||||
|
BUILD_DIR="${BUILD_DIR:-build}"
|
||||||
|
|
||||||
|
if [[ -z "$S3_FRONTEND_BUCKET" ]]; then
|
||||||
|
echo "[ERROR] Variable S3_FRONTEND_BUCKET no definida" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$BUILD_DIR" ]]; then
|
||||||
|
echo "[ERROR] Directorio ${BUILD_DIR} no encontrado" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Sincronización a S3
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
echo "[INFO] Sincronizando ${BUILD_DIR}/ a s3://${S3_FRONTEND_BUCKET}/ ..."
|
||||||
|
aws s3 sync "${BUILD_DIR}/" "s3://${S3_FRONTEND_BUCKET}/" --delete
|
||||||
|
echo "[INFO] Sincronización completada exitosamente"
|
||||||
Executable
+149
@@ -0,0 +1,149 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ===============================================================================================================
|
||||||
|
# deploy.sh - Script de despliegue de la API backend en EC2
|
||||||
|
# Descripción:
|
||||||
|
# Despliega el artefacto .jar de la API en la EC2, gestiona backups,
|
||||||
|
# configura el servicio systemd y ejecuta health check.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# bash /home/thoth/deploy/setup/deploy.sh
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Configuración
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
readonly APP_NAME="proyectosacc-app"
|
||||||
|
readonly ARTIFACTS_DIR="/home/thoth/deploy/artifacts"
|
||||||
|
readonly CURRENT_DIR="${ARTIFACTS_DIR}/current"
|
||||||
|
readonly BACKUP_DIR="${ARTIFACTS_DIR}/backup"
|
||||||
|
readonly LOGS_DIR="/var/log/proyectosacc/${APP_NAME}"
|
||||||
|
readonly SYSTEMD_SERVICE="${APP_NAME}.service"
|
||||||
|
readonly HEALTH_ENDPOINT="http://localhost:8080/actuator/health"
|
||||||
|
readonly MAX_RETRIES=30
|
||||||
|
readonly RETRY_INTERVAL=2
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Colores y logging
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
readonly CLR_RED='\033[0;31m'
|
||||||
|
readonly CLR_GREEN='\033[0;32m'
|
||||||
|
readonly CLR_YELLOW='\033[1;33m'
|
||||||
|
readonly CLR_BLUE='\033[0;34m'
|
||||||
|
readonly CLR_NC='\033[0m'
|
||||||
|
|
||||||
|
_log_info() { echo -e "${CLR_GREEN}[INFO]${CLR_NC} $*"; }
|
||||||
|
_log_warn() { echo -e "${CLR_YELLOW}[WARN]${CLR_NC} $*" >&2; }
|
||||||
|
_log_error() { echo -e "${CLR_RED}[ERROR]${CLR_NC} $*" >&2; }
|
||||||
|
_log_step() { echo ""; echo -e "${CLR_BLUE}==== $* ====${CLR_NC}"; }
|
||||||
|
_fail() { _log_error "$*"; exit 1; }
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Funciones
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
ensure_directories() {
|
||||||
|
_log_step "Verificando directorios de despliegue"
|
||||||
|
mkdir -p "${CURRENT_DIR}" "${BACKUP_DIR}" "${ARTIFACTS_DIR}/pids" "${LOGS_DIR}"
|
||||||
|
chown -R osiris:osiris "${ARTIFACTS_DIR}"
|
||||||
|
chmod 750 "${ARTIFACTS_DIR}"
|
||||||
|
_log_info "Directorios listos"
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_current_version() {
|
||||||
|
_log_step "Creando backup de la versión actual"
|
||||||
|
local current_jar="${CURRENT_DIR}/${APP_NAME}.jar"
|
||||||
|
if [[ -f "$current_jar" ]]; then
|
||||||
|
local timestamp
|
||||||
|
timestamp=$(date +%Y%m%d_%H%M%S)
|
||||||
|
local backup_name="${BACKUP_DIR}/${APP_NAME}-${timestamp}.jar"
|
||||||
|
cp "$current_jar" "$backup_name"
|
||||||
|
_log_info "Backup creado: ${backup_name}"
|
||||||
|
else
|
||||||
|
_log_warn "No hay versión actual para hacer backup"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
_log_step "Deteniendo servicio actual"
|
||||||
|
if systemctl is-active --quiet "${SYSTEMD_SERVICE}"; then
|
||||||
|
sudo systemctl stop "${SYSTEMD_SERVICE}" || true
|
||||||
|
_log_info "Servicio detenido"
|
||||||
|
else
|
||||||
|
_log_warn "El servicio no estaba activo"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_systemd_service() {
|
||||||
|
_log_step "Configurando servicio systemd"
|
||||||
|
sudo tee "/etc/systemd/system/${SYSTEMD_SERVICE}" > /dev/null <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Proyecto SACC App Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=osiris
|
||||||
|
Group=osiris
|
||||||
|
WorkingDirectory=${CURRENT_DIR}
|
||||||
|
ExecStart=/usr/bin/java -jar ${CURRENT_DIR}/${APP_NAME}.jar
|
||||||
|
SuccessExitStatus=143
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=append:${LOGS_DIR}/${APP_NAME}-service.log
|
||||||
|
StandardError=append:${LOGS_DIR}/${APP_NAME}-service.log
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
_log_info "Servicio systemd actualizado"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
_log_step "Iniciando servicio"
|
||||||
|
sudo systemctl enable "${SYSTEMD_SERVICE}" || true
|
||||||
|
sudo systemctl start "${SYSTEMD_SERVICE}"
|
||||||
|
_log_info "Servicio iniciado"
|
||||||
|
}
|
||||||
|
|
||||||
|
health_check() {
|
||||||
|
_log_step "Ejecutando health check"
|
||||||
|
local attempt=1
|
||||||
|
while [[ $attempt -le $MAX_RETRIES ]]; do
|
||||||
|
_log_info "Intento ${attempt}/${MAX_RETRIES}..."
|
||||||
|
if curl -sf "${HEALTH_ENDPOINT}" | grep -q '"status":"UP"'; then
|
||||||
|
_log_info "Health check PASÓ"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep "${RETRY_INTERVAL}"
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
_fail "Health check FALLÓ después de ${MAX_RETRIES} intentos"
|
||||||
|
}
|
||||||
|
|
||||||
|
save_pid() {
|
||||||
|
local pid
|
||||||
|
pid=$(systemctl show -p MainPID --value "${SYSTEMD_SERVICE}")
|
||||||
|
echo "$pid" > "${ARTIFACTS_DIR}/pids/${APP_NAME}.pid"
|
||||||
|
_log_info "PID guardado: ${pid}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
main() {
|
||||||
|
_log_step "Iniciando despliegue de ${APP_NAME}"
|
||||||
|
ensure_directories
|
||||||
|
backup_current_version
|
||||||
|
stop_service
|
||||||
|
create_systemd_service
|
||||||
|
start_service
|
||||||
|
health_check
|
||||||
|
save_pid
|
||||||
|
_log_info "Despliegue de ${APP_NAME} completado exitosamente"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ===============================================================================================================
|
||||||
|
# health-check.sh - Verifica que la API backend esté saludable
|
||||||
|
# Descripción:
|
||||||
|
# Realiza peticiones al endpoint de salud de la API con reintentos.
|
||||||
|
# Puede ejecutarse desde la EC2 localmente o desde el pipeline.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# HEALTH_URL=http://localhost:8080/actuator/health bash health-check.sh
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Configuración
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
HEALTH_URL="${HEALTH_URL:-http://localhost:8080/actuator/health}"
|
||||||
|
MAX_RETRIES="${MAX_RETRIES:-30}"
|
||||||
|
RETRY_INTERVAL="${RETRY_INTERVAL:-2}"
|
||||||
|
|
||||||
|
readonly CLR_RED='\033[0;31m'
|
||||||
|
readonly CLR_GREEN='\033[0;32m'
|
||||||
|
readonly CLR_YELLOW='\033[1;33m'
|
||||||
|
readonly CLR_BLUE='\033[0;34m'
|
||||||
|
readonly CLR_NC='\033[0m'
|
||||||
|
|
||||||
|
_log_info() { echo -e "${CLR_GREEN}[INFO]${CLR_NC} $*"; }
|
||||||
|
_log_warn() { echo -e "${CLR_YELLOW}[WARN]${CLR_NC} $*" >&2; }
|
||||||
|
_log_error() { echo -e "${CLR_RED}[ERROR]${CLR_NC} $*" >&2; }
|
||||||
|
_log_step() { echo ""; echo -e "${CLR_BLUE}==== $* ====${CLR_NC}"; }
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Health Check
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
main() {
|
||||||
|
_log_step "Health Check: ${HEALTH_URL}"
|
||||||
|
|
||||||
|
local attempt=1
|
||||||
|
while [[ $attempt -le $MAX_RETRIES ]]; do
|
||||||
|
_log_info "Intento ${attempt}/${MAX_RETRIES}..."
|
||||||
|
|
||||||
|
if curl -sf "${HEALTH_URL}" | grep -q '"status":"UP"'; then
|
||||||
|
_log_info "Health check PASÓ - API saludable"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $attempt -lt $MAX_RETRIES ]]; then
|
||||||
|
_log_warn "API no responde aún, reintentando en ${RETRY_INTERVAL}s..."
|
||||||
|
sleep "${RETRY_INTERVAL}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
|
||||||
|
_log_error "Health check FALLÓ después de ${MAX_RETRIES} intentos"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ===============================================================================================================
|
||||||
|
# notify-telegram.sh - Envía notificaciones a Telegram
|
||||||
|
# Descripción:
|
||||||
|
# Wrapper simple para enviar mensajes a un chat de Telegram.
|
||||||
|
# Requiere TELEGRAM_BOT_TOKEN y TELEGRAM_CHAT_ID como variables
|
||||||
|
# de entorno.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# bash notify-telegram.sh "Mensaje de prueba"
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Validación
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}"
|
||||||
|
TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-}"
|
||||||
|
MESSAGE="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then
|
||||||
|
echo "[ERROR] TELEGRAM_BOT_TOKEN no definido" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$TELEGRAM_CHAT_ID" ]]; then
|
||||||
|
echo "[ERROR] TELEGRAM_CHAT_ID no definido" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$MESSAGE" ]]; then
|
||||||
|
echo "[ERROR] Debe proporcionar un mensaje como primer argumento" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Envío de mensaje
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||||
|
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||||
|
-d "text=${MESSAGE}" \
|
||||||
|
-d "parse_mode=Markdown" > /dev/null
|
||||||
|
|
||||||
|
echo "[INFO] Mensaje enviado a Telegram"
|
||||||
Executable
+163
@@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ===============================================================================================================
|
||||||
|
# rollback.sh - Rollback de la API backend y/o frontend de proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Restaura la versión anterior de la API desde backup local en la EC2.
|
||||||
|
# Opcionalmente puede restaurar el frontend en S3 usando versionamiento.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# bash /home/thoth/deploy/scripts/rollback.sh [api|frontend|both]
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Configuración
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
ROLLBACK_TARGET="${1:-api}"
|
||||||
|
readonly APP_NAME="proyectosacc-app"
|
||||||
|
readonly ARTIFACTS_DIR="/home/thoth/deploy/artifacts"
|
||||||
|
readonly CURRENT_DIR="${ARTIFACTS_DIR}/current"
|
||||||
|
readonly BACKUP_DIR="${ARTIFACTS_DIR}/backup"
|
||||||
|
readonly SYSTEMD_SERVICE="${APP_NAME}.service"
|
||||||
|
|
||||||
|
readonly CLR_RED='\033[0;31m'
|
||||||
|
readonly CLR_GREEN='\033[0;32m'
|
||||||
|
readonly CLR_YELLOW='\033[1;33m'
|
||||||
|
readonly CLR_BLUE='\033[0;34m'
|
||||||
|
readonly CLR_NC='\033[0m'
|
||||||
|
|
||||||
|
_log_info() { echo -e "${CLR_GREEN}[INFO]${CLR_NC} $*"; }
|
||||||
|
_log_warn() { echo -e "${CLR_YELLOW}[WARN]${CLR_NC} $*" >&2; }
|
||||||
|
_log_error() { echo -e "${CLR_RED}[ERROR]${CLR_NC} $*" >&2; }
|
||||||
|
_log_step() { echo ""; echo -e "${CLR_BLUE}==== $* ====${CLR_NC}"; }
|
||||||
|
_fail() { _log_error "$*"; exit 1; }
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Rollback API
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
rollback_api() {
|
||||||
|
_log_step "Iniciando rollback de la API"
|
||||||
|
|
||||||
|
if [[ ! -d "$BACKUP_DIR" ]]; then
|
||||||
|
_fail "Directorio de backups no encontrado: ${BACKUP_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local latest_backup
|
||||||
|
latest_backup=$(ls -t "${BACKUP_DIR}/${APP_NAME}"-*.jar 2>/dev/null | head -n 1 || true)
|
||||||
|
|
||||||
|
if [[ -z "$latest_backup" ]]; then
|
||||||
|
_fail "No se encontraron backups de la API en ${BACKUP_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_log_info "Backup seleccionado: ${latest_backup}"
|
||||||
|
|
||||||
|
# Detener servicio actual
|
||||||
|
if systemctl is-active --quiet "${SYSTEMD_SERVICE}"; then
|
||||||
|
sudo systemctl stop "${SYSTEMD_SERVICE}" || true
|
||||||
|
_log_info "Servicio detenido"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Guardar versión fallida
|
||||||
|
local current_jar="${CURRENT_DIR}/${APP_NAME}.jar"
|
||||||
|
if [[ -f "$current_jar" ]]; then
|
||||||
|
mv "$current_jar" "${current_jar}.fallo-$(date +%Y%m%d_%H%M%S)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restaurar backup
|
||||||
|
cp "$latest_backup" "$current_jar"
|
||||||
|
chown osiris:osiris "$current_jar"
|
||||||
|
_log_info "Backup restaurado a ${current_jar}"
|
||||||
|
|
||||||
|
# Reiniciar servicio
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl start "${SYSTEMD_SERVICE}"
|
||||||
|
_log_info "Servicio reiniciado con versión anterior"
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
local attempt=1
|
||||||
|
local max_retries=30
|
||||||
|
local retry_interval=2
|
||||||
|
|
||||||
|
while [[ $attempt -le $max_retries ]]; do
|
||||||
|
_log_info "Health check intento ${attempt}/${max_retries}..."
|
||||||
|
if curl -sf http://localhost:8080/actuator/health | grep -q '"status":"UP"'; then
|
||||||
|
_log_info "Rollback de API exitoso - API saludable"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep "$retry_interval"
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
|
||||||
|
_fail "Rollback de API falló - Health check no pasó"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Rollback Frontend
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
rollback_frontend() {
|
||||||
|
_log_step "Iniciando rollback del frontend en S3"
|
||||||
|
|
||||||
|
local bucket="${S3_FRONTEND_BUCKET:-}"
|
||||||
|
local distribution_id="${CLOUDFRONT_DISTRIBUTION_ID:-}"
|
||||||
|
|
||||||
|
if [[ -z "$bucket" ]]; then
|
||||||
|
_fail "Variable S3_FRONTEND_BUCKET no definida"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_log_info "Listando versiones anteriores de index.html..."
|
||||||
|
local version_id
|
||||||
|
version_id=$(aws s3api list-object-versions \
|
||||||
|
--bucket "$bucket" \
|
||||||
|
--prefix index.html \
|
||||||
|
--query 'Versions[?IsLatest==`false`].VersionId' \
|
||||||
|
--output text 2>/dev/null | awk '{print $1}' || true)
|
||||||
|
|
||||||
|
if [[ -z "$version_id" ]]; then
|
||||||
|
_fail "No se encontró versión anterior de index.html en S3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
aws s3api copy-object \
|
||||||
|
--bucket "$bucket" \
|
||||||
|
--copy-source "${bucket}/index.html?versionId=${version_id}" \
|
||||||
|
--key index.html
|
||||||
|
|
||||||
|
_log_info "index.html restaurado a versión anterior"
|
||||||
|
|
||||||
|
if [[ -n "$distribution_id" ]]; then
|
||||||
|
aws cloudfront create-invalidation \
|
||||||
|
--distribution-id "$distribution_id" \
|
||||||
|
--paths "/*"
|
||||||
|
_log_info "Invalidación de CloudFront creada"
|
||||||
|
else
|
||||||
|
_log_warn "CLOUDFRONT_DISTRIBUTION_ID no definido, omitiendo invalidación"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
main() {
|
||||||
|
case "$ROLLBACK_TARGET" in
|
||||||
|
api)
|
||||||
|
rollback_api
|
||||||
|
;;
|
||||||
|
frontend)
|
||||||
|
rollback_frontend
|
||||||
|
;;
|
||||||
|
both)
|
||||||
|
rollback_api
|
||||||
|
rollback_frontend
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Uso: $0 [api|frontend|both]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
_log_info "Rollback completado"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
bucket = "ccsoft-terraform-state-739086995772"
|
||||||
|
key = "proyectosacc/terraform.tfstate"
|
||||||
|
region = "mx-central-1"
|
||||||
|
encrypt = true
|
||||||
|
dynamodb_table = "terraform-locks-proyectosacc-prod"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# backend.tf - Configuración del backend de estado para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Configura el backend remoto de Terraform en S3 para estado compartido.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# - DEV (default): terraform init
|
||||||
|
# - PROD: terraform init -backend-config=backend.prod.hcl
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
terraform {
|
||||||
|
backend "s3" {
|
||||||
|
bucket = "ccsoft-terraform-state"
|
||||||
|
key = "proyectosacc/terraform.tfstate"
|
||||||
|
region = "mx-central-1"
|
||||||
|
encrypt = true
|
||||||
|
dynamodb_table = "terraform-locks"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# dev.tfvars - Variables de ambiente develop para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Valores representativos para el ambiente de desarrollo.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
# Nota: mx-central-1 es una región opt-in. Debe estar habilitada en la cuenta de AWS antes del despliegue.
|
||||||
|
|
||||||
|
environment = "dev"
|
||||||
|
aws_region = "mx-central-1"
|
||||||
|
vpc_cidr = "10.1.0.0/16"
|
||||||
|
availability_zones = ["mx-central-1a", "mx-central-1b"]
|
||||||
|
ec2_instance_type = "t3.small"
|
||||||
|
ec2_key_name = "ccsoft-dev-key"
|
||||||
|
pipeline_public_key = "ssh-ed25519 AAAAC3NzaC... bitbucket.pipeline.ci.cd.proyectosacc.thoth.develop@computocontable.com"
|
||||||
|
db_instance_class = "db.t3.micro"
|
||||||
|
db_name = "sacc_db_dev"
|
||||||
|
db_username = "sacc_admin_dev"
|
||||||
|
db_password = "<cambiar-por-secret-real>"
|
||||||
|
s3_frontend_bucket = "ccsoft-proyectosacc-frontend-dev"
|
||||||
|
s3_artifacts_bucket = "ccsoft-proyectosacc-artifacts-dev"
|
||||||
|
cloudfront_price_class = "PriceClass_100"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# prod.tfvars - Variables de ambiente producción para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Valores representativos para el ambiente de producción.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
# Nota: mx-central-1 es una región opt-in. Debe estar habilitada en la cuenta de AWS antes del despliegue.
|
||||||
|
|
||||||
|
environment = "prod"
|
||||||
|
aws_region = "mx-central-1"
|
||||||
|
vpc_cidr = "10.2.0.0/16"
|
||||||
|
availability_zones = ["mx-central-1a", "mx-central-1b"]
|
||||||
|
ec2_instance_type = "t3.small"
|
||||||
|
ec2_key_name = "ccsoft-prod-key"
|
||||||
|
pipeline_public_key = "ssh-ed25519 AAAAC3NzaC... bitbucket.pipeline.ci.cd.proyectosacc.thoth.prod@computocontable.com"
|
||||||
|
db_instance_class = "db.t3.micro"
|
||||||
|
db_name = "sacc_db_prod"
|
||||||
|
db_username = "sacc_admin_prod"
|
||||||
|
db_password = "<cambiar-por-secret-real>"
|
||||||
|
s3_frontend_bucket = "ccsoft-proyectosacc-frontend-prod"
|
||||||
|
s3_artifacts_bucket = "ccsoft-proyectosacc-artifacts-prod"
|
||||||
|
cloudfront_price_class = "PriceClass_100"
|
||||||
@@ -0,0 +1,576 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# main.tf - Recursos principales de infraestructura para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Crea VPC, EC2, RDS, S3, CloudFront, Route 53, ACM e IAM para la
|
||||||
|
# arquitectura híbrida S3+CloudFront+EC2 de SACC.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# VPC y Networking
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_vpc" "main" {
|
||||||
|
cidr_block = var.vpc_cidr
|
||||||
|
enable_dns_support = true
|
||||||
|
enable_dns_hostnames = true
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-vpc-${var.environment}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_internet_gateway" "main" {
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-igw-${var.environment}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "public" {
|
||||||
|
count = length(var.availability_zones)
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
|
||||||
|
availability_zone = var.availability_zones[count.index]
|
||||||
|
map_public_ip_on_launch = true
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-public-${var.availability_zones[count.index]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "private" {
|
||||||
|
count = length(var.availability_zones)
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 10)
|
||||||
|
availability_zone = var.availability_zones[count.index]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-private-${var.availability_zones[count.index]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_eip" "nat" {
|
||||||
|
domain = "vpc"
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-nat-eip-${var.environment}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_nat_gateway" "main" {
|
||||||
|
allocation_id = aws_eip.nat.id
|
||||||
|
subnet_id = aws_subnet.public[0].id
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-nat-${var.environment}"
|
||||||
|
}
|
||||||
|
|
||||||
|
depends_on = [aws_internet_gateway.main]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table" "public" {
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
|
||||||
|
route {
|
||||||
|
cidr_block = "0.0.0.0/0"
|
||||||
|
gateway_id = aws_internet_gateway.main.id
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-public-rt-${var.environment}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table_association" "public" {
|
||||||
|
count = length(aws_subnet.public)
|
||||||
|
subnet_id = aws_subnet.public[count.index].id
|
||||||
|
route_table_id = aws_route_table.public.id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table" "private" {
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
|
||||||
|
route {
|
||||||
|
cidr_block = "0.0.0.0/0"
|
||||||
|
nat_gateway_id = aws_nat_gateway.main.id
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-private-rt-${var.environment}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table_association" "private" {
|
||||||
|
count = length(aws_subnet.private)
|
||||||
|
subnet_id = aws_subnet.private[count.index].id
|
||||||
|
route_table_id = aws_route_table.private.id
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Security Groups
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_security_group" "ec2_api" {
|
||||||
|
name_prefix = "${var.project_name}-ec2-api-"
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
description = "Security Group para la API backend de ${var.project_name}"
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "SSH desde IPs confiables"
|
||||||
|
from_port = 22
|
||||||
|
to_port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["10.0.0.0/8"] # Ajustar a IP/VPN real del pipeline
|
||||||
|
}
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "HTTP API"
|
||||||
|
from_port = 80
|
||||||
|
to_port = 80
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "HTTPS API"
|
||||||
|
from_port = 443
|
||||||
|
to_port = 443
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "API backend directo"
|
||||||
|
from_port = 8080
|
||||||
|
to_port = 8080
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = [aws_vpc.main.cidr_block]
|
||||||
|
}
|
||||||
|
|
||||||
|
egress {
|
||||||
|
description = "Salida libre"
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-sg-ec2-api"
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
create_before_destroy = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "rds" {
|
||||||
|
name_prefix = "${var.project_name}-rds-"
|
||||||
|
vpc_id = aws_vpc.main.id
|
||||||
|
description = "Security Group para RDS MariaDB de ${var.project_name}"
|
||||||
|
|
||||||
|
ingress {
|
||||||
|
description = "MariaDB desde EC2 API"
|
||||||
|
from_port = 3306
|
||||||
|
to_port = 3306
|
||||||
|
protocol = "tcp"
|
||||||
|
security_groups = [aws_security_group.ec2_api.id]
|
||||||
|
}
|
||||||
|
|
||||||
|
egress {
|
||||||
|
description = "Salida libre"
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-sg-rds"
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
create_before_destroy = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# IAM Role / Instance Profile para EC2
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_iam_role" "ec2_role" {
|
||||||
|
name = "${var.project_name}-ec2-role-${var.environment}"
|
||||||
|
|
||||||
|
assume_role_policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [{
|
||||||
|
Effect = "Allow"
|
||||||
|
Principal = {
|
||||||
|
Service = "ec2.amazonaws.com"
|
||||||
|
}
|
||||||
|
Action = "sts:AssumeRole"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-ec2-role"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy" "ec2_policy" {
|
||||||
|
name = "${var.project_name}-ec2-policy-${var.environment}"
|
||||||
|
role = aws_iam_role.ec2_role.id
|
||||||
|
|
||||||
|
policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = [
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:ListBucket"
|
||||||
|
]
|
||||||
|
Resource = [
|
||||||
|
aws_s3_bucket.artifacts.arn,
|
||||||
|
"${aws_s3_bucket.artifacts.arn}/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = [
|
||||||
|
"logs:CreateLogGroup",
|
||||||
|
"logs:CreateLogStream",
|
||||||
|
"logs:PutLogEvents"
|
||||||
|
]
|
||||||
|
Resource = "arn:aws:logs:*:*:*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = [
|
||||||
|
"cloudfront:CreateInvalidation"
|
||||||
|
]
|
||||||
|
Resource = "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_instance_profile" "ec2_profile" {
|
||||||
|
name = "${var.project_name}-ec2-profile-${var.environment}"
|
||||||
|
role = aws_iam_role.ec2_role.name
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-ec2-profile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# EC2 (API Backend)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_instance" "api" {
|
||||||
|
ami = var.ec2_ami
|
||||||
|
instance_type = var.ec2_instance_type
|
||||||
|
subnet_id = aws_subnet.public[0].id
|
||||||
|
vpc_security_group_ids = [aws_security_group.ec2_api.id]
|
||||||
|
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
|
||||||
|
key_name = var.ec2_key_name
|
||||||
|
|
||||||
|
root_block_device {
|
||||||
|
volume_type = "gp3"
|
||||||
|
volume_size = var.ec2_root_volume_size
|
||||||
|
encrypted = true
|
||||||
|
delete_on_termination = true
|
||||||
|
}
|
||||||
|
|
||||||
|
user_data = base64encode(templatefile("${path.module}/user-data.sh", {
|
||||||
|
pipeline_public_key = var.pipeline_public_key
|
||||||
|
}))
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-api-${var.environment}"
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# RDS MariaDB
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_db_subnet_group" "rds" {
|
||||||
|
name = "${var.project_name}-rds-subnet-group-${var.environment}"
|
||||||
|
subnet_ids = aws_subnet.private[*].id
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-rds-subnet-group"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_db_instance" "main" {
|
||||||
|
identifier = "${var.project_name}-db-${var.environment}"
|
||||||
|
engine = var.db_engine
|
||||||
|
engine_version = var.db_engine_version
|
||||||
|
instance_class = var.db_instance_class
|
||||||
|
allocated_storage = var.db_allocated_storage
|
||||||
|
storage_type = "gp3"
|
||||||
|
storage_encrypted = true
|
||||||
|
db_name = var.db_name
|
||||||
|
username = var.db_username
|
||||||
|
password = var.db_password
|
||||||
|
db_subnet_group_name = aws_db_subnet_group.rds.name
|
||||||
|
vpc_security_group_ids = [aws_security_group.rds.id]
|
||||||
|
publicly_accessible = false
|
||||||
|
skip_final_snapshot = true
|
||||||
|
backup_retention_period = 7
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-rds"
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# S3 Buckets
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_s3_bucket" "frontend" {
|
||||||
|
bucket = var.s3_frontend_bucket
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-frontend"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_versioning" "frontend" {
|
||||||
|
bucket = aws_s3_bucket.frontend.id
|
||||||
|
|
||||||
|
versioning_configuration {
|
||||||
|
status = "Enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_public_access_block" "frontend" {
|
||||||
|
bucket = aws_s3_bucket.frontend.id
|
||||||
|
|
||||||
|
block_public_acls = true
|
||||||
|
block_public_policy = true
|
||||||
|
ignore_public_acls = true
|
||||||
|
restrict_public_buckets = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_website_configuration" "frontend" {
|
||||||
|
bucket = aws_s3_bucket.frontend.id
|
||||||
|
|
||||||
|
index_document {
|
||||||
|
suffix = "index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
error_document {
|
||||||
|
key = "index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket" "artifacts" {
|
||||||
|
bucket = var.s3_artifacts_bucket
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-artifacts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_versioning" "artifacts" {
|
||||||
|
bucket = aws_s3_bucket.artifacts.id
|
||||||
|
|
||||||
|
versioning_configuration {
|
||||||
|
status = "Enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_public_access_block" "artifacts" {
|
||||||
|
bucket = aws_s3_bucket.artifacts.id
|
||||||
|
|
||||||
|
block_public_acls = true
|
||||||
|
block_public_policy = true
|
||||||
|
ignore_public_acls = true
|
||||||
|
restrict_public_buckets = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# CloudFront Origin Access Control
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_cloudfront_origin_access_control" "frontend" {
|
||||||
|
name = "${var.project_name}-oac-${var.environment}"
|
||||||
|
description = "OAC para bucket frontend de ${var.project_name}"
|
||||||
|
origin_access_control_origin_type = "s3"
|
||||||
|
signing_behavior = "always"
|
||||||
|
signing_protocol = "sigv4"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_policy" "frontend" {
|
||||||
|
bucket = aws_s3_bucket.frontend.id
|
||||||
|
|
||||||
|
policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [{
|
||||||
|
Sid = "AllowCloudFrontOAC"
|
||||||
|
Effect = "Allow"
|
||||||
|
Principal = {
|
||||||
|
Service = "cloudfront.amazonaws.com"
|
||||||
|
}
|
||||||
|
Action = "s3:GetObject"
|
||||||
|
Resource = "${aws_s3_bucket.frontend.arn}/*"
|
||||||
|
Condition = {
|
||||||
|
StringEquals = {
|
||||||
|
"AWS:SourceArn" = aws_cloudfront_distribution.main.arn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
depends_on = [aws_s3_bucket_public_access_block.frontend]
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# ACM Certificate (us-east-1 obligatorio para CloudFront)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_acm_certificate" "main" {
|
||||||
|
provider = aws.us_east_1
|
||||||
|
domain_name = var.domain_name
|
||||||
|
validation_method = "DNS"
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-cert"
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
create_before_destroy = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route53_record" "cert_validation" {
|
||||||
|
for_each = {
|
||||||
|
for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => {
|
||||||
|
name = dvo.resource_record_name
|
||||||
|
record = dvo.resource_record_value
|
||||||
|
type = dvo.resource_record_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allow_overwrite = true
|
||||||
|
name = each.value.name
|
||||||
|
records = [each.value.record]
|
||||||
|
ttl = 60
|
||||||
|
type = each.value.type
|
||||||
|
zone_id = data.aws_route53_zone.main.zone_id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_acm_certificate_validation" "main" {
|
||||||
|
provider = aws.us_east_1
|
||||||
|
certificate_arn = aws_acm_certificate.main.arn
|
||||||
|
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Route 53
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
data "aws_route53_zone" "main" {
|
||||||
|
name = "ccsoft.mx"
|
||||||
|
private_zone = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route53_record" "main" {
|
||||||
|
zone_id = data.aws_route53_zone.main.zone_id
|
||||||
|
name = var.domain_name
|
||||||
|
type = "A"
|
||||||
|
|
||||||
|
alias {
|
||||||
|
name = aws_cloudfront_distribution.main.domain_name
|
||||||
|
zone_id = aws_cloudfront_distribution.main.hosted_zone_id
|
||||||
|
evaluate_target_health = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# CloudFront Distribution
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_cloudfront_distribution" "main" {
|
||||||
|
enabled = true
|
||||||
|
is_ipv6_enabled = true
|
||||||
|
comment = "CDN para ${var.project_name}"
|
||||||
|
default_root_object = "index.html"
|
||||||
|
aliases = [var.domain_name]
|
||||||
|
price_class = var.cloudfront_price_class
|
||||||
|
|
||||||
|
origin {
|
||||||
|
domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name
|
||||||
|
origin_id = "saccS3Origin"
|
||||||
|
origin_access_control_id = aws_cloudfront_origin_access_control.frontend.id
|
||||||
|
}
|
||||||
|
|
||||||
|
origin {
|
||||||
|
domain_name = aws_instance.api.public_dns
|
||||||
|
origin_id = "saccApiOrigin"
|
||||||
|
|
||||||
|
custom_origin_config {
|
||||||
|
http_port = 80
|
||||||
|
https_port = 443
|
||||||
|
origin_protocol_policy = "http-only"
|
||||||
|
origin_ssl_protocols = ["TLSv1.2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default_cache_behavior {
|
||||||
|
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
|
||||||
|
cached_methods = ["GET", "HEAD"]
|
||||||
|
target_origin_id = "saccS3Origin"
|
||||||
|
viewer_protocol_policy = "redirect-to-https"
|
||||||
|
compress = true
|
||||||
|
|
||||||
|
forwarded_values {
|
||||||
|
query_string = false
|
||||||
|
cookies {
|
||||||
|
forward = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min_ttl = 0
|
||||||
|
default_ttl = 3600
|
||||||
|
max_ttl = 86400
|
||||||
|
}
|
||||||
|
|
||||||
|
ordered_cache_behavior {
|
||||||
|
path_pattern = "/api/*"
|
||||||
|
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
|
||||||
|
cached_methods = ["GET", "HEAD"]
|
||||||
|
target_origin_id = "saccApiOrigin"
|
||||||
|
viewer_protocol_policy = "redirect-to-https"
|
||||||
|
compress = true
|
||||||
|
|
||||||
|
forwarded_values {
|
||||||
|
query_string = true
|
||||||
|
headers = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"]
|
||||||
|
cookies {
|
||||||
|
forward = "all"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min_ttl = 0
|
||||||
|
default_ttl = 0
|
||||||
|
max_ttl = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
restrictions {
|
||||||
|
geo_restriction {
|
||||||
|
restriction_type = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer_certificate {
|
||||||
|
acm_certificate_arn = aws_acm_certificate_validation.main.certificate_arn
|
||||||
|
ssl_support_method = "sni-only"
|
||||||
|
minimum_protocol_version = "TLSv1.2_2021"
|
||||||
|
}
|
||||||
|
|
||||||
|
depends_on = [aws_acm_certificate_validation.main]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "${var.project_name}-cdn"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# outputs.tf - Outputs de infraestructura para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Expone información útil de los recursos creados por Terraform.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
output "ec2_public_ip" {
|
||||||
|
description = "IP pública del servidor de la API"
|
||||||
|
value = aws_instance.api.public_ip
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ec2_public_dns" {
|
||||||
|
description = "DNS público del servidor de la API"
|
||||||
|
value = aws_instance.api.public_dns
|
||||||
|
}
|
||||||
|
|
||||||
|
output "rds_endpoint" {
|
||||||
|
description = "Endpoint de conexión a la base de datos"
|
||||||
|
value = aws_db_instance.main.endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
output "s3_frontend_bucket" {
|
||||||
|
description = "Nombre del bucket S3 del frontend React"
|
||||||
|
value = aws_s3_bucket.frontend.bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
output "s3_artifacts_bucket" {
|
||||||
|
description = "Nombre del bucket S3 de artefactos de la API"
|
||||||
|
value = aws_s3_bucket.artifacts.bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudfront_domain" {
|
||||||
|
description = "Dominio de la distribución CloudFront"
|
||||||
|
value = aws_cloudfront_distribution.main.domain_name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cloudfront_distribution_id" {
|
||||||
|
description = "ID de la distribución CloudFront"
|
||||||
|
value = aws_cloudfront_distribution.main.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "route53_record" {
|
||||||
|
description = "Registro DNS creado en Route 53"
|
||||||
|
value = aws_route53_record.main.name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "acm_certificate_arn" {
|
||||||
|
description = "ARN del certificado SSL en ACM"
|
||||||
|
value = aws_acm_certificate.main.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
output "vpc_id" {
|
||||||
|
description = "ID de la VPC creada"
|
||||||
|
value = aws_vpc.main.id
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# provider.tf - Configuración del proveedor AWS para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Define la región y versiones del provider AWS para Terraform.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# terraform init
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
terraform {
|
||||||
|
required_version = ">= 1.5.0"
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
region = var.aws_region
|
||||||
|
|
||||||
|
default_tags {
|
||||||
|
tags = {
|
||||||
|
Project = var.project_name
|
||||||
|
ManagedBy = "terraform"
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Provider exclusivo para ACM en us-east-1 (requerido por CloudFront)
|
||||||
|
provider "aws" {
|
||||||
|
alias = "us_east_1"
|
||||||
|
region = "us-east-1"
|
||||||
|
|
||||||
|
default_tags {
|
||||||
|
tags = {
|
||||||
|
Project = var.project_name
|
||||||
|
ManagedBy = "terraform"
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+122
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ===============================================================================================================
|
||||||
|
# user-data.sh - Script de inicialización de la EC2 para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Configura la instancia EC2 al primer boot: instala dependencias,
|
||||||
|
# crea usuarios, configura Nginx como proxy de API, y prepara
|
||||||
|
# directorios de despliegue.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Variables
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
PIPELINE_PUBLIC_KEY="${pipeline_public_key}"
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Actualizar sistema e instalar dependencias
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y nginx openjdk-21-jdk awscli curl jq
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Crear usuarios del sistema
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
useradd -m -s /bin/bash thoth || true
|
||||||
|
useradd -m -s /bin/bash osiris || true
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Configurar SSH para el pipeline (usuario thoth)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
mkdir -p /home/thoth/.ssh
|
||||||
|
chmod 700 /home/thoth/.ssh
|
||||||
|
|
||||||
|
echo "$PIPELINE_PUBLIC_KEY" > /home/thoth/.ssh/authorized_keys
|
||||||
|
chmod 600 /home/thoth/.ssh/authorized_keys
|
||||||
|
chown -R thoth:thoth /home/thoth/.ssh
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Crear estructura de directorios de despliegue
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
mkdir -p /home/thoth/deploy/artifacts/{backup,current,logs,pids}
|
||||||
|
mkdir -p /home/thoth/deploy/{scripts,setup}
|
||||||
|
chown -R thoth:thoth /home/thoth/deploy
|
||||||
|
|
||||||
|
mkdir -p /var/log/proyectosacc/proyectosacc-app
|
||||||
|
chown -R osiris:osiris /var/log/proyectosacc
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Configurar Nginx como proxy inverso SOLO para la API
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
cat > /etc/nginx/sites-available/proyectosacc-api <<'NGINX_EOF'
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:8080/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
location = /50x.html {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NGINX_EOF
|
||||||
|
|
||||||
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
|
ln -sf /etc/nginx/sites-available/proyectosacc-api /etc/nginx/sites-enabled/proyectosacc-api
|
||||||
|
|
||||||
|
nginx -t
|
||||||
|
systemctl enable nginx
|
||||||
|
systemctl restart nginx
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Crear servicio systemd template para la API (será sobrescrito por deploy.sh)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
cat > /etc/systemd/system/proyectosacc-app.service <<'SYSTEMD_EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Proyecto SACC App Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=osiris
|
||||||
|
Group=osiris
|
||||||
|
WorkingDirectory=/home/thoth/deploy/artifacts/current
|
||||||
|
ExecStart=/usr/bin/java -jar /home/thoth/deploy/artifacts/current/proyectosacc-app.jar
|
||||||
|
SuccessExitStatus=143
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=append:/var/log/proyectosacc/proyectosacc-app/proyectosacc-app-service.log
|
||||||
|
StandardError=append:/var/log/proyectosacc/proyectosacc-app/proyectosacc-app-service.log
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SYSTEMD_EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable proyectosacc-app.service || true
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Ajustar permisos finales
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
usermod -aG osiris thoth || true
|
||||||
|
chown -R osiris:osiris /home/thoth/deploy/artifacts
|
||||||
|
chmod 750 /home/thoth/deploy/artifacts
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# variables.tf - Variables de infraestructura para proyectosacc
|
||||||
|
# Descripción:
|
||||||
|
# Define todas las variables parametrizables de la infraestructura AWS.
|
||||||
|
#
|
||||||
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Generales
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
variable "aws_region" {
|
||||||
|
description = "Región principal de AWS"
|
||||||
|
type = string
|
||||||
|
default = "mx-central-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "project_name" {
|
||||||
|
description = "Nombre del proyecto"
|
||||||
|
type = string
|
||||||
|
default = "proyectosacc"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "environment" {
|
||||||
|
description = "Ambiente de despliegue (dev, uat, prod)"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "domain_name" {
|
||||||
|
description = "Dominio principal de la aplicación"
|
||||||
|
type = string
|
||||||
|
default = "sacc.ccsoft.mx"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Red
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
variable "vpc_cidr" {
|
||||||
|
description = "CIDR block de la VPC"
|
||||||
|
type = string
|
||||||
|
default = "10.0.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "availability_zones" {
|
||||||
|
description = "Zonas de disponibilidad a utilizar"
|
||||||
|
type = list(string)
|
||||||
|
default = ["mx-central-1a", "mx-central-1b"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# EC2 (API Backend)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
variable "ec2_instance_type" {
|
||||||
|
description = "Tipo de instancia EC2 para la API"
|
||||||
|
type = string
|
||||||
|
default = "t3.small"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ec2_ami" {
|
||||||
|
description = "AMI de Ubuntu 22.04 LTS"
|
||||||
|
type = string
|
||||||
|
# AMI oficial de Ubuntu 22.04 LTS en mx-central-1 (validada: 2026-04-10)
|
||||||
|
default = "ami-09289f290e76061f8"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ec2_root_volume_size" {
|
||||||
|
description = "Tamaño del volumen raíz en GB"
|
||||||
|
type = number
|
||||||
|
default = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ec2_key_name" {
|
||||||
|
description = "Nombre del Key Pair SSH para acceso inicial (administrado externamente)"
|
||||||
|
type = string
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pipeline_public_key" {
|
||||||
|
description = "Llave pública SSH del pipeline CI/CD (usuario thoth)"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# RDS (Base de datos)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
variable "db_instance_class" {
|
||||||
|
description = "Clase de instancia RDS"
|
||||||
|
type = string
|
||||||
|
default = "db.t3.micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_engine" {
|
||||||
|
description = "Motor de base de datos"
|
||||||
|
type = string
|
||||||
|
default = "mariadb"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_engine_version" {
|
||||||
|
description = "Versión del motor de base de datos"
|
||||||
|
type = string
|
||||||
|
default = "10.11"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_name" {
|
||||||
|
description = "Nombre de la base de datos inicial"
|
||||||
|
type = string
|
||||||
|
default = "sacc_db"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_username" {
|
||||||
|
description = "Usuario administrador de la base de datos"
|
||||||
|
type = string
|
||||||
|
default = "sacc_admin"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_password" {
|
||||||
|
description = "Contraseña del usuario administrador de la base de datos"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_allocated_storage" {
|
||||||
|
description = "Almacenamiento asignado a RDS en GB"
|
||||||
|
type = number
|
||||||
|
default = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# S3
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
variable "s3_frontend_bucket" {
|
||||||
|
description = "Nombre del bucket S3 para el frontend React"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "s3_artifacts_bucket" {
|
||||||
|
description = "Nombre del bucket S3 para artefactos de la API"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# CloudFront / ACM
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
variable "cloudfront_price_class" {
|
||||||
|
description = "Clase de precio de CloudFront"
|
||||||
|
type = string
|
||||||
|
default = "PriceClass_100"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user