Add production setup and update Docker configurations
- Introduced a new docker-compose.prod.yml for production deployment. - Updated docker-compose.yml to change backend port mapping. - Added svelte-sonner dependency to frontend package.json. - Created Dockerfile.prod for building the frontend application. - Enhanced README.md with production deployment instructions.
This commit is contained in:
15
README.md
15
README.md
@@ -132,6 +132,21 @@ python -c "from core.database import init_db; init_db()"
|
||||
- **Frontend**: http://localhost:5173
|
||||
- **Keycloak Admin**: http://localhost:8080
|
||||
|
||||
## 🛠️ Produccion
|
||||
|
||||
```
|
||||
docker build -t dev.aduanasoft.com/anexo76/backend:latest -f ./backend/Dockerfile ./backend
|
||||
docker build -t dev.aduanasoft.com/anexo76/frontend:latest -f ./frontend/Dockerfile.prod ./frontend
|
||||
```
|
||||
|
||||
Publica en el registro (ajusta el registry si corresponde):
|
||||
|
||||
```
|
||||
docker login dev.aduanasoft.com
|
||||
docker push dev.aduanasoft.com/anexo76/backend:latest
|
||||
docker push dev.aduanasoft.com/anexo76/frontend:latest
|
||||
```
|
||||
|
||||
## 🛠️ Desarrollo Local
|
||||
|
||||
### Backend
|
||||
|
||||
260
docker-compose.prod.yml
Normal file
260
docker-compose.prod.yml
Normal file
@@ -0,0 +1,260 @@
|
||||
services:
|
||||
# PostgreSQL - Base de datos core (app)
|
||||
postgres-a76:
|
||||
image: postgres:18-alpine
|
||||
container_name: anexo76-postgres-a76
|
||||
environment:
|
||||
POSTGRES_DB: anexo76_core
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_APP_PASSWORD:-postgres}
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF8"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_app_data:/var/lib/postgresql/data
|
||||
- ./scripts/postgres-app-entrypoint.sh:/docker-entrypoint-initdb.d/init-app.sh:ro
|
||||
networks:
|
||||
- backend-net
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d anexo76_core || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
reservations:
|
||||
memory: 256M
|
||||
shm_size: 128mb
|
||||
|
||||
# PostgreSQL - Base de datos Keycloak
|
||||
postgres-keycloak:
|
||||
image: postgres:18-alpine
|
||||
container_name: anexo76-postgres-keycloak
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_KEYCLOAK_PASSWORD:-postgres}
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF8"
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_keycloak_data:/var/lib/postgresql/data
|
||||
- ./scripts/postgres-keycloak-entrypoint.sh:/docker-entrypoint-initdb.d/init-keycloak.sh:ro
|
||||
networks:
|
||||
- auth-net
|
||||
- backend-net
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d keycloak || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
reservations:
|
||||
memory: 256M
|
||||
shm_size: 128mb
|
||||
|
||||
# Keycloak - Servidor de autenticación
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:26.4
|
||||
container_name: anexo76-keycloak
|
||||
environment:
|
||||
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN:-admin}
|
||||
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
|
||||
KC_DB: postgres
|
||||
KC_DB_URL_HOST: postgres-keycloak
|
||||
KC_DB_URL_PORT: "5432"
|
||||
KC_DB_URL_DATABASE: keycloak
|
||||
KC_DB_URL: jdbc:postgresql://postgres-keycloak:5432/keycloak
|
||||
KC_DB_USERNAME: postgres
|
||||
KC_DB_PASSWORD: ${POSTGRES_KEYCLOAK_PASSWORD:-postgres}
|
||||
KC_DB_SCHEMA: public
|
||||
KC_HOSTNAME: localhost
|
||||
KC_HTTP_ENABLED: "true"
|
||||
KC_HOSTNAME_STRICT: "false"
|
||||
KC_HOSTNAME_STRICT_HTTPS: "false"
|
||||
KC_PROXY_HEADERS: "xforwarded"
|
||||
KC_HEALTH_ENABLED: "true"
|
||||
KC_METRICS_ENABLED: "true"
|
||||
KC_LOG_LEVEL: INFO
|
||||
JAVA_OPTS_APPEND: "-Xms256m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true"
|
||||
command:
|
||||
- start-dev
|
||||
- --db=postgres
|
||||
- --db-url-host=postgres-keycloak
|
||||
- --db-url-port=5432
|
||||
- --db-url-database=keycloak
|
||||
- --db-username=postgres
|
||||
- --db-password=${POSTGRES_KEYCLOAK_PASSWORD:-postgres}
|
||||
- --http-enabled=true
|
||||
- --hostname-strict=false
|
||||
- --proxy-headers=xforwarded
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "9000:9000"
|
||||
depends_on:
|
||||
postgres-keycloak:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- keycloak_data:/opt/keycloak/data
|
||||
networks:
|
||||
- auth-net
|
||||
- backend-net
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000; echo -e 'GET /health/ready HTTP/1.1\r\nhost: 127.0.0.1\r\nConnection: close\r\n\r\n' >&3; grep -q 'HTTP/1.1 200' <&3 || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 90s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 768M
|
||||
reservations:
|
||||
memory: 512M
|
||||
|
||||
# Backend - FastAPI
|
||||
backend:
|
||||
image: dev.aduanasoft.com/anexo76/backend:latest
|
||||
container_name: anexo76-backend
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-True}
|
||||
- ENVIRONMENT=${ENVIRONMENT:-development}
|
||||
- PYTHONUNBUFFERED=1
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- CORE_DB_HOST=${CORE_DB_HOST:-postgres-a76}
|
||||
- CORE_DB_PORT=${CORE_DB_PORT:-5432}
|
||||
- CORE_DB_NAME=${CORE_DB_NAME:-anexo76_core}
|
||||
- CORE_DB_USER=${CORE_DB_USER:-postgres}
|
||||
- CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres}
|
||||
- KEYCLOAK_SERVER_URL=${KEYCLOAK_SERVER_URL:-http://keycloak:8080}
|
||||
- KEYCLOAK_REALM=${KEYCLOAK_REALM:-master}
|
||||
- KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-anexo76-backend}
|
||||
- KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-dev-secret}
|
||||
- CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5173,http://localhost:3000}
|
||||
ports:
|
||||
- "8000:8000"
|
||||
depends_on:
|
||||
postgres-a76:
|
||||
condition: service_healthy
|
||||
keycloak:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
- backend_cache:/app/__pycache__
|
||||
- ./scripts/backend-entrypoint.sh:/entrypoint.sh:ro
|
||||
networks:
|
||||
- backend-net
|
||||
- frontend-net
|
||||
restart: unless-stopped
|
||||
entrypoint: ["/entrypoint.sh"]
|
||||
command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--log-level", "info"]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:8000/api/health || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
reservations:
|
||||
memory: 256M
|
||||
|
||||
# Frontend - SvelteKit
|
||||
frontend:
|
||||
image: dev.aduanasoft.com/anexo76/frontend:latest
|
||||
container_name: anexo76-frontend
|
||||
environment:
|
||||
- NODE_ENV=${NODE_ENV:-development}
|
||||
- VITE_API_URL=${VITE_API_URL:-http://localhost:8000/api/}
|
||||
- INTERNAL_API_URL=${INTERNAL_API_URL:-http://backend:8000/api/}
|
||||
- VITE_KEYCLOAK_URL=${VITE_KEYCLOAK_URL:-http://localhost:8080}
|
||||
- VITE_KEYCLOAK_REALM=${VITE_KEYCLOAK_REALM:-master}
|
||||
- VITE_KEYCLOAK_CLIENT_ID=${VITE_KEYCLOAK_CLIENT_ID:-anexo76-frontend}
|
||||
- KEYCLOAK_URL=${KEYCLOAK_URL:-http://keycloak:8080}
|
||||
- KEYCLOAK_REALM=${KEYCLOAK_REALM:-master}
|
||||
- KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-anexo76-backend}
|
||||
- KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-zRU5NuvUFtBSOuh7Kdc372AItoWGLgz9}
|
||||
ports:
|
||||
- "5173:5173"
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
entrypoint: ["/frontend-entrypoint.sh"]
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
- frontend_node_modules:/app/node_modules
|
||||
- ./scripts/frontend-entrypoint.sh:/frontend-entrypoint.sh:ro
|
||||
networks:
|
||||
- frontend-net
|
||||
- auth-net
|
||||
restart: unless-stopped
|
||||
command: ["pnpm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5173/ || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 45s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1G
|
||||
reservations:
|
||||
memory: 512M
|
||||
|
||||
volumes:
|
||||
postgres_app_data:
|
||||
driver: local
|
||||
postgres_keycloak_data:
|
||||
driver: local
|
||||
keycloak_data:
|
||||
driver: local
|
||||
frontend_node_modules:
|
||||
driver: local
|
||||
backend_cache:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
backend-net:
|
||||
driver: bridge
|
||||
auth-net:
|
||||
driver: bridge
|
||||
frontend-net:
|
||||
driver: bridge
|
||||
@@ -162,7 +162,7 @@ services:
|
||||
- KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-dev-secret}
|
||||
- CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5173,http://localhost:3000}
|
||||
ports:
|
||||
- "5050:8000"
|
||||
- "8000:8000"
|
||||
depends_on:
|
||||
postgres-a76:
|
||||
condition: service_healthy
|
||||
|
||||
56
frontend/Dockerfile.prod
Normal file
56
frontend/Dockerfile.prod
Normal file
@@ -0,0 +1,56 @@
|
||||
# ==========================
|
||||
# Etapa de build
|
||||
# ==========================
|
||||
FROM node:22-alpine AS build
|
||||
|
||||
# Directorio de trabajo
|
||||
WORKDIR /app
|
||||
|
||||
# Configurar npm para trabajar con certificados autofirmados e instalar pnpm
|
||||
RUN npm config set strict-ssl false && \
|
||||
npm install -g pnpm
|
||||
|
||||
# Copiar archivos de dependencias
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
|
||||
# Instalar dependencias con pnpm
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
ARG VITE_API_URL
|
||||
ENV VITE_API_URL=${VITE_API_URL}
|
||||
|
||||
# Copiar el resto del código
|
||||
COPY . .
|
||||
|
||||
# Construir el proyecto
|
||||
RUN pnpm run build
|
||||
|
||||
|
||||
# ==========================
|
||||
# Etapa de ejecución con Node.js
|
||||
# ==========================
|
||||
FROM node:22-alpine AS runtime
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Crear usuario no-root para seguridad antes de copiar con --chown
|
||||
RUN addgroup -g 1001 -S nodejs
|
||||
RUN adduser -S svelte -u 1001
|
||||
|
||||
# Copiar solo archivos necesarios para producción y aplicar propietario en la copia
|
||||
COPY --from=build --chown=svelte:nodejs /app/build ./build
|
||||
COPY --from=build --chown=svelte:nodejs /app/package.json ./
|
||||
COPY --from=build --chown=svelte:nodejs /app/node_modules ./node_modules
|
||||
|
||||
USER svelte
|
||||
|
||||
# Puerto para SvelteKit con adapter-node
|
||||
EXPOSE 3000
|
||||
|
||||
# Variables de entorno
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
ENV HOST=0.0.0.0
|
||||
|
||||
# Ejecutar aplicación con Node.js
|
||||
CMD ["node", "build"]
|
||||
@@ -56,6 +56,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"keycloak-js": "^26.2.1",
|
||||
"lucide-svelte": "^0.553.0"
|
||||
"lucide-svelte": "^0.553.0",
|
||||
"svelte-sonner": "^1.0.7"
|
||||
}
|
||||
}
|
||||
|
||||
23
frontend/pnpm-lock.yaml
generated
23
frontend/pnpm-lock.yaml
generated
@@ -14,6 +14,9 @@ importers:
|
||||
lucide-svelte:
|
||||
specifier: ^0.553.0
|
||||
version: 0.553.0(svelte@5.40.2)
|
||||
svelte-sonner:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(svelte@5.40.2)
|
||||
devDependencies:
|
||||
'@eslint/compat':
|
||||
specifier: ^1.4.0
|
||||
@@ -1675,6 +1678,11 @@ packages:
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
runed@0.28.0:
|
||||
resolution: {integrity: sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==}
|
||||
peerDependencies:
|
||||
svelte: ^5.7.0
|
||||
|
||||
runed@0.35.1:
|
||||
resolution: {integrity: sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==}
|
||||
peerDependencies:
|
||||
@@ -1761,6 +1769,11 @@ packages:
|
||||
svelte:
|
||||
optional: true
|
||||
|
||||
svelte-sonner@1.0.7:
|
||||
resolution: {integrity: sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A==}
|
||||
peerDependencies:
|
||||
svelte: ^5.0.0
|
||||
|
||||
svelte-toolbelt@0.10.6:
|
||||
resolution: {integrity: sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==}
|
||||
engines: {node: '>=18', pnpm: '>=8.7.0'}
|
||||
@@ -3379,6 +3392,11 @@ snapshots:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
|
||||
runed@0.28.0(svelte@5.40.2):
|
||||
dependencies:
|
||||
esm-env: 1.2.2
|
||||
svelte: 5.40.2
|
||||
|
||||
runed@0.35.1(@sveltejs/kit@2.47.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2):
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
@@ -3460,6 +3478,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
svelte: 5.40.2
|
||||
|
||||
svelte-sonner@1.0.7(svelte@5.40.2):
|
||||
dependencies:
|
||||
runed: 0.28.0(svelte@5.40.2)
|
||||
svelte: 5.40.2
|
||||
|
||||
svelte-toolbelt@0.10.6(@sveltejs/kit@2.47.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2):
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
|
||||
Reference in New Issue
Block a user