feat(azure): add infra + build/deploy scripts for Azure Container Apps
Split Bicep into infra.bicep (one-time: ACR + Log Analytics + Env) and container-app.bicep (per-deploy: Container App with ACR auth). Add 1-infra.sh and 2-build-deploy.sh shell scripts with config.sh.example covering all .env variables. Gitignore azure/config.sh to prevent secret leakage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b5c4d4a336
commit
ffe6c537d0
|
|
@ -60,3 +60,6 @@ backend/uploads/
|
||||||
data/
|
data/
|
||||||
# Git worktrees
|
# Git worktrees
|
||||||
.worktrees/
|
.worktrees/
|
||||||
|
|
||||||
|
# Configuració Azure amb secrets (no comitejar mai)
|
||||||
|
azure/config.sh
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 1-infra.sh — Crea la infraestructura base de MiroFish a Azure
|
||||||
|
#
|
||||||
|
# Executa UNA SOLA VEGADA (o si vols recrear la infraestructura).
|
||||||
|
# Idempotent: pot executar-se múltiples vegades sense errors.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - az login executat
|
||||||
|
# - azure/config.sh existent (còpia de config.sh.example)
|
||||||
|
#
|
||||||
|
# Crea:
|
||||||
|
# - Resource Group: rg_mirofish
|
||||||
|
# - Azure Container Registry (ACR): ${PROJECT_NAME}acr
|
||||||
|
# - Log Analytics Workspace: ${PROJECT_NAME}-logs
|
||||||
|
# - Container Apps Environment: ${PROJECT_NAME}-env
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# ── Carregar configuració ─────────────────────────────────────────────────────
|
||||||
|
CONFIG_FILE="${SCRIPT_DIR}/config.sh"
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
echo "ERROR: No s'ha trobat azure/config.sh"
|
||||||
|
echo " Còpia l'exemple: cp azure/config.sh.example azure/config.sh"
|
||||||
|
echo " Després omple els valors i torna a executar."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# shellcheck source=config.sh.example
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
|
# ── Validar variables obligatòries ───────────────────────────────────────────
|
||||||
|
REQUIRED_VARS=(
|
||||||
|
AZURE_SUBSCRIPTION_ID AZURE_LOCATION
|
||||||
|
RESOURCE_GROUP PROJECT_NAME
|
||||||
|
)
|
||||||
|
for var in "${REQUIRED_VARS[@]}"; do
|
||||||
|
if [[ -z "${!var:-}" ]]; then
|
||||||
|
echo "ERROR: La variable $var no està configurada a config.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
ACR_NAME="${PROJECT_NAME}acr"
|
||||||
|
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " MiroFish — Creació d'infraestructura Azure"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " Subscripció : $AZURE_SUBSCRIPTION_ID"
|
||||||
|
echo " Localització: $AZURE_LOCATION"
|
||||||
|
echo " Grup recurs : $RESOURCE_GROUP"
|
||||||
|
echo " ACR : $ACR_NAME"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Seleccionar subscripció ───────────────────────────────────────────────────
|
||||||
|
echo "→ Seleccionant subscripció..."
|
||||||
|
az account set --subscription "$AZURE_SUBSCRIPTION_ID"
|
||||||
|
|
||||||
|
# ── Registrar proveïdors necessaris ──────────────────────────────────────────
|
||||||
|
echo "→ Registrant proveïdors Azure (pot trigar uns minuts la primera vegada)..."
|
||||||
|
az provider register --namespace Microsoft.App --wait
|
||||||
|
az provider register --namespace Microsoft.OperationalInsights --wait
|
||||||
|
az provider register --namespace Microsoft.ContainerRegistry --wait
|
||||||
|
|
||||||
|
# ── Crear Resource Group ──────────────────────────────────────────────────────
|
||||||
|
echo "→ Creant Resource Group '$RESOURCE_GROUP'..."
|
||||||
|
az group create \
|
||||||
|
--name "$RESOURCE_GROUP" \
|
||||||
|
--location "$AZURE_LOCATION" \
|
||||||
|
--output none
|
||||||
|
echo " ✓ Resource Group llest"
|
||||||
|
|
||||||
|
# ── Desplegar infraestructura via Bicep ──────────────────────────────────────
|
||||||
|
echo "→ Desplegant infraestructura (ACR + Log Analytics + Container Apps Env)..."
|
||||||
|
INFRA_OUTPUT=$(az deployment group create \
|
||||||
|
--resource-group "$RESOURCE_GROUP" \
|
||||||
|
--template-file "${SCRIPT_DIR}/infra.bicep" \
|
||||||
|
--parameters \
|
||||||
|
projectName="$PROJECT_NAME" \
|
||||||
|
location="$AZURE_LOCATION" \
|
||||||
|
--output json)
|
||||||
|
|
||||||
|
# Extreure outputs del desplegament
|
||||||
|
ACR_LOGIN_SERVER=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['acrLoginServer']['value'])")
|
||||||
|
ACR_NAME_OUT=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['acrName']['value'])")
|
||||||
|
ENV_ID=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['containerAppsEnvId']['value'])")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " Infraestructura creada correctament!"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " ACR Login Server : $ACR_LOGIN_SERVER"
|
||||||
|
echo " Container Apps Env ID: $ENV_ID"
|
||||||
|
echo ""
|
||||||
|
echo " Proper pas: bash azure/2-build-deploy.sh"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 2-build-deploy.sh — Build Docker + push a ACR + deploy Container App
|
||||||
|
#
|
||||||
|
# Executar a cada nova versió de l'aplicació.
|
||||||
|
# Requereix que 1-infra.sh hagi estat executat prèviament.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - az login executat
|
||||||
|
# - azure/config.sh existent i configurat
|
||||||
|
# - Docker instal·lat i en execució
|
||||||
|
# - Infraestructura creada (azure/1-infra.sh)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
|
||||||
|
# ── Carregar configuració ─────────────────────────────────────────────────────
|
||||||
|
CONFIG_FILE="${SCRIPT_DIR}/config.sh"
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
echo "ERROR: No s'ha trobat azure/config.sh"
|
||||||
|
echo " Còpia l'exemple: cp azure/config.sh.example azure/config.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# shellcheck source=config.sh.example
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
|
# ── Validar variables obligatòries ───────────────────────────────────────────
|
||||||
|
REQUIRED_VARS=(
|
||||||
|
AZURE_SUBSCRIPTION_ID RESOURCE_GROUP PROJECT_NAME
|
||||||
|
DEMO_PASSWORD SECRET_KEY LLM_API_KEY LLM_BASE_URL LLM_MODEL_NAME ZEP_API_KEY
|
||||||
|
)
|
||||||
|
for var in "${REQUIRED_VARS[@]}"; do
|
||||||
|
if [[ -z "${!var:-}" ]]; then
|
||||||
|
echo "ERROR: La variable $var no està configurada a config.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
ACR_NAME="${PROJECT_NAME}acr"
|
||||||
|
|
||||||
|
# ── Seleccionar subscripció ───────────────────────────────────────────────────
|
||||||
|
echo "→ Seleccionant subscripció..."
|
||||||
|
az account set --subscription "$AZURE_SUBSCRIPTION_ID"
|
||||||
|
|
||||||
|
# ── Obtenir dades de la infraestructura existent ──────────────────────────────
|
||||||
|
echo "→ Obtenint dades de la infraestructura..."
|
||||||
|
|
||||||
|
ACR_LOGIN_SERVER=$(az acr show \
|
||||||
|
--name "$ACR_NAME" \
|
||||||
|
--resource-group "$RESOURCE_GROUP" \
|
||||||
|
--query loginServer --output tsv)
|
||||||
|
|
||||||
|
ENV_ID=$(az containerapp env show \
|
||||||
|
--name "${PROJECT_NAME}-env" \
|
||||||
|
--resource-group "$RESOURCE_GROUP" \
|
||||||
|
--query id --output tsv)
|
||||||
|
|
||||||
|
if [[ -z "$ACR_LOGIN_SERVER" || -z "$ENV_ID" ]]; then
|
||||||
|
echo "ERROR: No s'ha trobat la infraestructura. Executa primer: bash azure/1-infra.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Generar tag de versió ─────────────────────────────────────────────────────
|
||||||
|
# Format: <git-sha-curt>-<timestamp> per a traçabilitat
|
||||||
|
GIT_SHA=$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || echo "nogit")
|
||||||
|
TIMESTAMP=$(date +%Y%m%d%H%M)
|
||||||
|
IMAGE_TAG="${GIT_SHA}-${TIMESTAMP}"
|
||||||
|
FULL_IMAGE="${ACR_LOGIN_SERVER}/${PROJECT_NAME}:${IMAGE_TAG}"
|
||||||
|
LATEST_IMAGE="${ACR_LOGIN_SERVER}/${PROJECT_NAME}:latest"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " MiroFish — Build & Deploy"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " ACR : $ACR_LOGIN_SERVER"
|
||||||
|
echo " Imatge : ${PROJECT_NAME}:${IMAGE_TAG}"
|
||||||
|
echo " Container Env : ${PROJECT_NAME}-env"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Login a l'ACR ─────────────────────────────────────────────────────────────
|
||||||
|
echo "→ Login a l'ACR..."
|
||||||
|
az acr login --name "$ACR_NAME"
|
||||||
|
|
||||||
|
# ── Build de la imatge Docker ─────────────────────────────────────────────────
|
||||||
|
echo "→ Build de la imatge Docker..."
|
||||||
|
docker build \
|
||||||
|
--tag "$FULL_IMAGE" \
|
||||||
|
--tag "$LATEST_IMAGE" \
|
||||||
|
"$REPO_ROOT"
|
||||||
|
echo " ✓ Build completat"
|
||||||
|
|
||||||
|
# ── Push de la imatge a l'ACR ─────────────────────────────────────────────────
|
||||||
|
echo "→ Push a l'ACR ($FULL_IMAGE)..."
|
||||||
|
docker push "$FULL_IMAGE"
|
||||||
|
docker push "$LATEST_IMAGE"
|
||||||
|
echo " ✓ Push completat"
|
||||||
|
|
||||||
|
# ── Obtenir credencials ACR per al Bicep ─────────────────────────────────────
|
||||||
|
echo "→ Obtenint credencials ACR..."
|
||||||
|
ACR_USERNAME=$(az acr credential show \
|
||||||
|
--name "$ACR_NAME" \
|
||||||
|
--resource-group "$RESOURCE_GROUP" \
|
||||||
|
--query username --output tsv)
|
||||||
|
ACR_PASSWORD=$(az acr credential show \
|
||||||
|
--name "$ACR_NAME" \
|
||||||
|
--resource-group "$RESOURCE_GROUP" \
|
||||||
|
--query "passwords[0].value" --output tsv)
|
||||||
|
|
||||||
|
# ── Desplegar Container App via Bicep ─────────────────────────────────────────
|
||||||
|
echo "→ Desplegant Container App..."
|
||||||
|
DEPLOY_OUTPUT=$(az deployment group create \
|
||||||
|
--resource-group "$RESOURCE_GROUP" \
|
||||||
|
--template-file "${SCRIPT_DIR}/container-app.bicep" \
|
||||||
|
--parameters \
|
||||||
|
projectName="$PROJECT_NAME" \
|
||||||
|
containerAppsEnvId="$ENV_ID" \
|
||||||
|
containerImage="$FULL_IMAGE" \
|
||||||
|
acrLoginServer="$ACR_LOGIN_SERVER" \
|
||||||
|
acrUsername="$ACR_USERNAME" \
|
||||||
|
acrPassword="$ACR_PASSWORD" \
|
||||||
|
demoPassword="$DEMO_PASSWORD" \
|
||||||
|
llmApiKey="$LLM_API_KEY" \
|
||||||
|
llmBoostApiKey="${LLM_BOOST_API_KEY:-}" \
|
||||||
|
zepApiKey="$ZEP_API_KEY" \
|
||||||
|
secretKey="$SECRET_KEY" \
|
||||||
|
llmBaseUrl="$LLM_BASE_URL" \
|
||||||
|
llmModelName="$LLM_MODEL_NAME" \
|
||||||
|
llmBoostBaseUrl="${LLM_BOOST_BASE_URL:-}" \
|
||||||
|
llmBoostModelName="${LLM_BOOST_MODEL_NAME:-}" \
|
||||||
|
oasisDefaultMaxRounds="${OASIS_DEFAULT_MAX_ROUNDS:-10}" \
|
||||||
|
reportAgentMaxToolCalls="${REPORT_AGENT_MAX_TOOL_CALLS:-5}" \
|
||||||
|
reportAgentMaxReflectionRounds="${REPORT_AGENT_MAX_REFLECTION_ROUNDS:-2}" \
|
||||||
|
reportAgentTemperature="${REPORT_AGENT_TEMPERATURE:-0.5}" \
|
||||||
|
--output json)
|
||||||
|
|
||||||
|
FQDN=$(echo "$DEPLOY_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['containerAppFqdn']['value'])")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " Deploy completat!"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
echo " URL de l'aplicació: https://$FQDN"
|
||||||
|
echo " Imatge desplegada : $FULL_IMAGE"
|
||||||
|
echo "════════════════════════════════════════════════════════"
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Configuració de desplegament MiroFish a Azure
|
||||||
|
#
|
||||||
|
# INSTRUCCIONS:
|
||||||
|
# 1. Còpia aquest fitxer a azure/config.sh (NO comitegis config.sh — té secrets)
|
||||||
|
# cp azure/config.sh.example azure/config.sh
|
||||||
|
# 2. Omple tots els valors marcats amb <...>
|
||||||
|
# 3. Executa: az login
|
||||||
|
# 4. Executa: bash azure/1-infra.sh (una sola vegada)
|
||||||
|
# 5. Executa: bash azure/2-build-deploy.sh (a cada nova versió)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# ── Subscripció i localització Azure ─────────────────────────────────────────
|
||||||
|
AZURE_SUBSCRIPTION_ID="<la-teva-subscription-id>"
|
||||||
|
AZURE_LOCATION="westeurope" # canvia si prefereixes altra regió
|
||||||
|
|
||||||
|
# ── Noms de recursos (pots deixar els valors per defecte) ─────────────────────
|
||||||
|
RESOURCE_GROUP="rg_mirofish"
|
||||||
|
PROJECT_NAME="mirofish" # prefix per a tots els recursos Azure
|
||||||
|
# Nota: el nom de l'ACR serà "${PROJECT_NAME}acr" (sense guions, tot minúscula)
|
||||||
|
|
||||||
|
# ── Secrets de l'aplicació ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Contrasenya de l'usuari "demo" per fer login a l'app
|
||||||
|
DEMO_PASSWORD="<contrasenya-segura>"
|
||||||
|
|
||||||
|
# Flask SECRET_KEY per signar tokens JWT
|
||||||
|
# Genera-la amb: python -c "import secrets; print(secrets.token_hex(32))"
|
||||||
|
SECRET_KEY="<flask-secret-key>"
|
||||||
|
|
||||||
|
# ── LLM principal (OpenAI-compatible) ─────────────────────────────────────────
|
||||||
|
LLM_API_KEY="<la-teva-llm-api-key>"
|
||||||
|
LLM_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
|
LLM_MODEL_NAME="qwen-plus"
|
||||||
|
|
||||||
|
# ── LLM accelerador (opcional — deixar buit per desactivar) ───────────────────
|
||||||
|
LLM_BOOST_API_KEY=""
|
||||||
|
LLM_BOOST_BASE_URL=""
|
||||||
|
LLM_BOOST_MODEL_NAME=""
|
||||||
|
|
||||||
|
# ── Zep Cloud (graf de memòria) ───────────────────────────────────────────────
|
||||||
|
ZEP_API_KEY="<la-teva-zep-api-key>"
|
||||||
|
|
||||||
|
# ── Simulació OASIS (valors per defecte recomanats) ───────────────────────────
|
||||||
|
OASIS_DEFAULT_MAX_ROUNDS="10"
|
||||||
|
|
||||||
|
# ── Report Agent (valors per defecte recomanats) ──────────────────────────────
|
||||||
|
REPORT_AGENT_MAX_TOOL_CALLS="5"
|
||||||
|
REPORT_AGENT_MAX_REFLECTION_ROUNDS="2"
|
||||||
|
REPORT_AGENT_TEMPERATURE="0.5"
|
||||||
|
|
@ -1,34 +1,42 @@
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// MiroFish — Azure Container App
|
// MiroFish — Container App (executar a cada deploy)
|
||||||
// Segueix les directrius CTTI per a desplegament d'aplicacions a Azure
|
|
||||||
// (DA/TM: Azure Container Apps com a plataforma de desplegament de contenidors)
|
|
||||||
//
|
//
|
||||||
// Paràmetres que l'equip d'ops ha de proporcionar en desplegar:
|
// Rep com a paràmetres els outputs d'infra.bicep (containerAppsEnvId,
|
||||||
// Secrets (@secure): demoPassword, llmApiKey, llmBoostApiKey, zepApiKey, secretKey
|
// acrLoginServer) i desplega/actualitza la Container App amb la nova imatge.
|
||||||
// Valors: containerImage, llmBaseUrl, llmModelName, llmBoostBaseUrl,
|
//
|
||||||
// llmBoostModelName, oasisDefaultMaxRounds,
|
// Executar amb: azure/2-build-deploy.sh
|
||||||
// reportAgentMaxToolCalls, reportAgentMaxReflectionRounds,
|
|
||||||
// reportAgentTemperature
|
|
||||||
//
|
//
|
||||||
// Extensions pendents per a l'equip d'operacions:
|
// Extensions pendents per a l'equip d'operacions:
|
||||||
// - DNS: afegir CNAME a *.intranet.gencat.cat (PRE) / *.gencat.cat (PRO)
|
// - DNS: afegir CNAME a *.intranet.gencat.cat (PRE) / *.gencat.cat (PRO)
|
||||||
// - Xarxa: integrar en VNet Hub-Spoke + Private Link per a serveis interns
|
|
||||||
// (descomentar el bloc vnetConfiguration a l'entorn)
|
|
||||||
// - TLS: certificat gestionat via Container Apps o Azure Front Door
|
|
||||||
// - ingress.external: canviar a false per a accés exclusiu per intranet
|
// - ingress.external: canviar a false per a accés exclusiu per intranet
|
||||||
|
// - TLS: certificat gestionat via Container Apps o Azure Front Door
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@description('Nom base del projecte (es fa servir per als noms dels recursos)')
|
@description('Nom base del projecte')
|
||||||
param projectName string = 'mirofish'
|
param projectName string = 'mirofish'
|
||||||
|
|
||||||
@description('Localització Azure dels recursos')
|
@description('Localització Azure dels recursos')
|
||||||
param location string = resourceGroup().location
|
param location string = resourceGroup().location
|
||||||
|
|
||||||
@description('Imatge Docker completa (registry/imatge:tag)')
|
@description('ID del Container Apps Environment (output d\'infra.bicep)')
|
||||||
|
param containerAppsEnvId string
|
||||||
|
|
||||||
|
@description('Imatge Docker completa (acrLoginServer/nom:tag)')
|
||||||
param containerImage string
|
param containerImage string
|
||||||
|
|
||||||
|
@description('Login server de l\'ACR (ex: mirofsihacr.azurecr.io)')
|
||||||
|
param acrLoginServer string
|
||||||
|
|
||||||
// ─── Paràmetres secrets (@secure — mai visibles als logs de desplegament) ────
|
// ─── Paràmetres secrets (@secure — mai visibles als logs de desplegament) ────
|
||||||
|
|
||||||
|
@description('Nom d\'usuari de l\'ACR (az acr credential show --name <acr> --query username)')
|
||||||
|
@secure()
|
||||||
|
param acrUsername string
|
||||||
|
|
||||||
|
@description('Contrasenya de l\'ACR (az acr credential show --name <acr> --query passwords[0].value)')
|
||||||
|
@secure()
|
||||||
|
param acrPassword string
|
||||||
|
|
||||||
@description('Contrasenya de l\'usuari demo')
|
@description('Contrasenya de l\'usuari demo')
|
||||||
@secure()
|
@secure()
|
||||||
param demoPassword string
|
param demoPassword string
|
||||||
|
|
@ -45,7 +53,7 @@ param llmBoostApiKey string = ''
|
||||||
@secure()
|
@secure()
|
||||||
param zepApiKey string
|
param zepApiKey string
|
||||||
|
|
||||||
@description('SECRET_KEY de Flask per a JWT (generar amb: python -c "import secrets; print(secrets.token_hex(32))")')
|
@description('SECRET_KEY de Flask per a JWT (python -c "import secrets; print(secrets.token_hex(32))")')
|
||||||
@secure()
|
@secure()
|
||||||
param secretKey string
|
param secretKey string
|
||||||
|
|
||||||
|
|
@ -81,54 +89,31 @@ param reportAgentMaxReflectionRounds string = '2'
|
||||||
@description('Temperatura del model LLM per al Report Agent')
|
@description('Temperatura del model LLM per al Report Agent')
|
||||||
param reportAgentTemperature string = '0.5'
|
param reportAgentTemperature string = '0.5'
|
||||||
|
|
||||||
// ─── Log Analytics Workspace ──────────────────────────────────────────────────
|
|
||||||
// NOR0016-C: retenció mínima de logs de seguretat = 90 dies
|
|
||||||
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
|
|
||||||
name: '${projectName}-logs'
|
|
||||||
location: location
|
|
||||||
properties: {
|
|
||||||
sku: {
|
|
||||||
name: 'PerGB2018'
|
|
||||||
}
|
|
||||||
retentionInDays: 90
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Container Apps Environment ───────────────────────────────────────────────
|
|
||||||
resource containerAppsEnv 'Microsoft.App/managedEnvironments@2023-05-01' = {
|
|
||||||
name: '${projectName}-env'
|
|
||||||
location: location
|
|
||||||
properties: {
|
|
||||||
appLogsConfiguration: {
|
|
||||||
destination: 'log-analytics'
|
|
||||||
logAnalyticsConfiguration: {
|
|
||||||
customerId: logAnalytics.properties.customerId
|
|
||||||
sharedKey: logAnalytics.listKeys().primarySharedKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO (ops): descomentar per integrar en VNet Hub-Spoke CTTI
|
|
||||||
// vnetConfiguration: {
|
|
||||||
// infrastructureSubnetId: '/subscriptions/.../subnets/container-apps-subnet'
|
|
||||||
// internal: true // true = accés únicament per intranet
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Container App ─────────────────────────────────────────────────────────────
|
// ─── Container App ─────────────────────────────────────────────────────────────
|
||||||
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
|
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
|
||||||
name: projectName
|
name: projectName
|
||||||
location: location
|
location: location
|
||||||
properties: {
|
properties: {
|
||||||
managedEnvironmentId: containerAppsEnv.id
|
managedEnvironmentId: containerAppsEnvId
|
||||||
|
|
||||||
configuration: {
|
configuration: {
|
||||||
// Secrets de Container Apps — mai en text pla a les variables d'entorn
|
// Secrets: credencials ACR + variables sensibles de l'aplicació
|
||||||
secrets: [
|
secrets: [
|
||||||
{ name: 'demo-password', value: demoPassword }
|
{ name: 'acr-password', value: acrPassword }
|
||||||
{ name: 'llm-api-key', value: llmApiKey }
|
{ name: 'demo-password', value: demoPassword }
|
||||||
{ name: 'llm-boost-api-key', value: llmBoostApiKey }
|
{ name: 'llm-api-key', value: llmApiKey }
|
||||||
{ name: 'zep-api-key', value: zepApiKey }
|
{ name: 'llm-boost-api-key', value: llmBoostApiKey }
|
||||||
{ name: 'secret-key', value: secretKey }
|
{ name: 'zep-api-key', value: zepApiKey }
|
||||||
|
{ name: 'secret-key', value: secretKey }
|
||||||
|
]
|
||||||
|
|
||||||
|
// Credencials del registre privat (ACR)
|
||||||
|
registries: [
|
||||||
|
{
|
||||||
|
server: acrLoginServer
|
||||||
|
username: acrUsername
|
||||||
|
passwordSecretRef: 'acr-password'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Ingrés: port únic 5001 (Flask serveix frontend + API)
|
// Ingrés: port únic 5001 (Flask serveix frontend + API)
|
||||||
|
|
@ -138,11 +123,7 @@ resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
|
||||||
transport: 'http'
|
transport: 'http'
|
||||||
// TODO (ops): afegir domini corporatiu quan estigui assignat
|
// TODO (ops): afegir domini corporatiu quan estigui assignat
|
||||||
// customDomains: [
|
// customDomains: [
|
||||||
// {
|
// { name: 'mirofish.intranet.gencat.cat', certificateId: '...', bindingType: 'SniEnabled' }
|
||||||
// name: 'mirofish.intranet.gencat.cat' // PRE
|
|
||||||
// certificateId: '<id-certificat-aca>'
|
|
||||||
// bindingType: 'SniEnabled'
|
|
||||||
// }
|
|
||||||
// ]
|
// ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,11 +191,8 @@ resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Outputs ──────────────────────────────────────────────────────────────────
|
// ─── Outputs ──────────────────────────────────────────────────────────────────
|
||||||
@description('FQDN de l\'aplicació desplegada')
|
@description('FQDN públic de l\'aplicació')
|
||||||
output containerAppFqdn string = containerApp.properties.configuration.ingress.fqdn
|
output containerAppFqdn string = containerApp.properties.configuration.ingress.fqdn
|
||||||
|
|
||||||
@description('Nom del recurs Container App')
|
@description('Nom del recurs Container App')
|
||||||
output containerAppName string = containerApp.name
|
output containerAppName string = containerApp.name
|
||||||
|
|
||||||
@description('ID del workspace de Log Analytics')
|
|
||||||
output logAnalyticsWorkspaceId string = logAnalytics.id
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// MiroFish — Infraestructura base (executar una sola vegada)
|
||||||
|
//
|
||||||
|
// Crea:
|
||||||
|
// - Azure Container Registry (ACR) per emmagatzemar la imatge Docker
|
||||||
|
// - Log Analytics Workspace (NOR0016-C: 90 dies retenció)
|
||||||
|
// - Container Apps Environment (plataforma d'execució CTTI)
|
||||||
|
//
|
||||||
|
// Executar amb: azure/1-infra.sh
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@description('Nom base del projecte')
|
||||||
|
param projectName string = 'mirofish'
|
||||||
|
|
||||||
|
@description('Localització Azure dels recursos')
|
||||||
|
param location string = resourceGroup().location
|
||||||
|
|
||||||
|
// ─── Azure Container Registry ─────────────────────────────────────────────────
|
||||||
|
// SKU Basic: suficient per a imatges privades sense geo-replicació
|
||||||
|
resource acr 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
|
||||||
|
name: '${projectName}acr' // ACR no admet guions, tot minúscula
|
||||||
|
location: location
|
||||||
|
sku: {
|
||||||
|
name: 'Basic'
|
||||||
|
}
|
||||||
|
properties: {
|
||||||
|
adminUserEnabled: true // necessari per a la autenticació des dels scripts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Log Analytics Workspace ──────────────────────────────────────────────────
|
||||||
|
// NOR0016-C: retenció mínima de logs de seguretat = 90 dies
|
||||||
|
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
|
||||||
|
name: '${projectName}-logs'
|
||||||
|
location: location
|
||||||
|
properties: {
|
||||||
|
sku: {
|
||||||
|
name: 'PerGB2018'
|
||||||
|
}
|
||||||
|
retentionInDays: 90
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Container Apps Environment ───────────────────────────────────────────────
|
||||||
|
resource containerAppsEnv 'Microsoft.App/managedEnvironments@2023-05-01' = {
|
||||||
|
name: '${projectName}-env'
|
||||||
|
location: location
|
||||||
|
properties: {
|
||||||
|
appLogsConfiguration: {
|
||||||
|
destination: 'log-analytics'
|
||||||
|
logAnalyticsConfiguration: {
|
||||||
|
customerId: logAnalytics.properties.customerId
|
||||||
|
sharedKey: logAnalytics.listKeys().primarySharedKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO (ops): descomentar per integrar en VNet Hub-Spoke CTTI
|
||||||
|
// vnetConfiguration: {
|
||||||
|
// infrastructureSubnetId: '/subscriptions/.../subnets/container-apps-subnet'
|
||||||
|
// internal: true
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Outputs (usats pels scripts de deploy) ───────────────────────────────────
|
||||||
|
@description('URL de login de l\'ACR (ex: mirofsihacr.azurecr.io)')
|
||||||
|
output acrLoginServer string = acr.properties.loginServer
|
||||||
|
|
||||||
|
@description('Nom del recurs ACR (per a az acr build)')
|
||||||
|
output acrName string = acr.name
|
||||||
|
|
||||||
|
@description('ID del Container Apps Environment')
|
||||||
|
output containerAppsEnvId string = containerAppsEnv.id
|
||||||
|
|
||||||
|
@description('ID del Log Analytics Workspace')
|
||||||
|
output logAnalyticsId string = logAnalytics.id
|
||||||
Loading…
Reference in New Issue