Implemente DQE One Standalone en Azure Container Instance (ACI) para entornos de Microsoft Dynamics, utilizando una instancia administrada de Azure Database for PostgreSQL en lugar de un contenedor de base de datos.
Nota: todos los elementos descritos a continuación son recomendaciones de DQE, basadas en la experiencia de implementación con distintos clientes. El cliente es responsable de integrar la solución en su propia arquitectura. Un arquitecto familiarizado con el contexto del cliente y la infraestructura interna debe alinear las recomendaciones de DQE con la infraestructura del cliente.
1. Arquitectura
Este documento describe cómo implementar el backend de DQE One Standalone como un grupo de contenedores de Azure Container Instance (ACI). Todos los contenedores del grupo comparten el mismo espacio de nombres de red y se comunican a través de localhost. Un Application Gateway expone la aplicación mediante HTTPS y enruta el tráfico hacia el ACI a través de una VNET privada.
Arquitectura recomendada
Internet (HTTPS port 443)
|
Application Gateway (public IP + WAF)
|
Private VNET subnet
|
ACI Container Group (private IP)
|
NGINX sidecar (port 80, internal)
|
DQE One Standalone (port 8000, internal)
|
Redis (internal)
|
Azure Database for PostgreSQL (private VNET)Medidas de seguridad
- Application Gateway: finaliza el HTTPS y enruta el tráfico hacia el ACI a través de la VNET privada. Utilice el WAF para restringir los rangos de IP entrantes.
- VNET: aísla el ACI del acceso público directo a internet. Todo el tráfico procedente del Application Gateway pasa por una subred privada.
- Certificado SSL: se gestiona a nivel del Application Gateway o a nivel de NGINX dentro del ACI.
-
Secretos: utilice
secureValueen el YAML del grupo de contenedores para todos los valores sensibles (contraseñas, claves de licencia, claves de cifrado).
Dimensionamiento recomendado
| Contenedor | CPU | Memoria |
|---|---|---|
| nginx | 0.25 vCPU | 0.5 GB |
| redis | 0.5 vCPU | 1.0 GB |
| dqeone | 1.0 vCPU | 2.0 GB |
| Total | 1.75 vCPU | 3.5 GB |
Los grupos de contenedores de ACI están limitados a 4 vCPU y 16 GB de memoria por grupo.
PostgreSQL se aloja en Azure Database for PostgreSQL (fuera del grupo de contenedores de ACI). Consulte la sección 2.5 para la configuración.
2. Instalación
2.1. Azure CLI
Azure CLI debe estar instalado en su máquina local.
Linux / macOS:
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bashWindows: Consulte la documentación de Microsoft.
2.2. Crear un Application Gateway
El Application Gateway expone el ACI con una dirección IP pública y un nombre DNS. También gestiona la finalización de HTTPS y el filtrado de IP mediante el WAF.
Para obtener información completa sobre la configuración, consulte la documentación de Microsoft Application Gateway.
Pestaña Basic
Seleccione su suscripción, grupo de recursos y región. Elija el nivel WAF V2 para habilitar el Web Application Firewall.
WAF Policy
Cree una nueva política WAF en el campo WAF policy. Utilice el WAF para definir los rangos de IP entrantes autorizados a llamar al Application Gateway.
VNET
Cree una nueva red virtual (VNET) en el campo Virtual network. Esta VNET conecta el Application Gateway con el ACI.
Frontends
Cree una nueva dirección IP pública. Es la dirección IP expuesta externamente y utilizada para enrutar el tráfico hacia el ACI.
Backends
Cree un nuevo pool de backend. Déjelo vacío por ahora: la dirección IP privada del ACI se añade después de implementar el ACI (sección 3.3).
Configuration — Reglas de enrutamiento
Cree una regla de enrutamiento con:
- Listener: HTTPS en el puerto 443, con su certificado SSL.
- Backend target: el pool de backend creado anteriormente.
- Backend setting: HTTP en el puerto 80 (el tráfico entre el Application Gateway y el ACI viaja a través de la VNET privada y no requiere HTTPS internamente).
Haga clic en Review + create para implementar el Application Gateway.
2.3. Añadir una subred de VNET para el ACI
En la VNET creada en la sección 2.2, cree una nueva subred dedicada al ACI. Delegue esta subred a Microsoft.ContainerInstance/containerGroups.
az network vnet subnet create \
--resource-group MY_RESOURCE_GROUP \
--vnet-name VNET_NAME \
--name aci-subnet \
--address-prefixes 10.0.1.0/24 \
--delegations Microsoft.ContainerInstance/containerGroupsAnote el ID de recurso completo de la subred: es necesario en el YAML del grupo de contenedores:
/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP/providers/Microsoft.Network/virtualNetworks/VNET_NAME/subnets/SUBNET_NAME2.4. Configurar Azure Storage
Los contenedores de ACI utilizan Azure File Shares para el almacenamiento persistente. Cree una cuenta de almacenamiento y, a continuación, cree los siguientes recursos compartidos de archivos:
| Recurso compartido | Finalidad |
|---|---|
nginxconf |
Configuración de NGINX y archivos de certificado SSL |
redisdata |
Datos persistentes de Redis |
Paso 1 — Crear la cuenta de almacenamiento:
az storage account create \
--name MY_STORAGE_ACCOUNT \
--resource-group MY_RESOURCE_GROUP \
--location MY_LOCATION \
--sku Standard_LRSPaso 2 — Recuperar la clave de la cuenta de almacenamiento:
az storage account keys list \
--account-name MY_STORAGE_ACCOUNT \
--resource-group MY_RESOURCE_GROUP \
--query "[0].value" -o tsvPaso 3 — Crear los recursos compartidos de archivos:
az storage share create --name nginxconf --account-name MY_STORAGE_ACCOUNT
az storage share create --name redisdata --account-name MY_STORAGE_ACCOUNT2.5. Crear Azure Database for PostgreSQL
PostgreSQL se aloja como servicio administrado de Azure fuera del grupo de contenedores de ACI. Azure File Shares (SMB) no admite las operaciones de sistema de archivos POSIX que requiere PostgreSQL y provocará un CrashLoopBackOff.
Paso 1 — Crear una subred dedicada para PostgreSQL
En la VNET creada en la sección 2.2, añada una nueva subred (por ejemplo, postgres-subnet). Delegúela a Microsoft.DBforPostgreSQL/flexibleServers. Esta subred no puede compartirse con la subred del ACI.
az network vnet subnet create \
--resource-group MY_RESOURCE_GROUP \
--vnet-name VNET_NAME \
--name postgres-subnet \
--address-prefixes 10.0.2.0/24 \
--delegations Microsoft.DBforPostgreSQL/flexibleServersPaso 2 — Crear el servidor
En Azure Portal, cree un recurso de Azure Database for PostgreSQL:
- Networking: seleccione Private access (VNet Integration).
- Virtual network: seleccione la misma VNet que el ACI. El ACI y el servidor deben compartir la misma VNet.
- Subnet: seleccione la subred creada en el paso 1.
-
Admin username / password: establezca las credenciales. Estos valores se utilizan en
DB_USERyDB_PASSWORDen el YAML del grupo de contenedores.
az postgres flexible-server create \
--resource-group MY_RESOURCE_GROUP \
--name SERVERNAME \
--location MY_LOCATION \
--vnet VNET_NAME \
--subnet postgres-subnet \
--admin-user DB_ADMIN_USER \
--admin-password "DB_ADMIN_PASSWORD" \
--sku-name Standard_B1ms \
--tier Burstable \
--version 16Paso 3 — Crear la base de datos de la aplicación
En Azure Portal, vaya al recurso de Azure Database for PostgreSQL → Databases → + Add, introduzca dqeone como nombre y guarde.
az postgres flexible-server db create \
--resource-group MY_RESOURCE_GROUP \
--server-name SERVERNAME \
--database-name dqeone2.6. Crear un Log Analytics Workspace
Un Log Analytics workspace centraliza los registros de los contenedores y permite la supervisión a través de Azure Monitor. De forma predeterminada, los registros se conservan durante 30 días.
Paso 1 — Crear el workspace:
az monitor log-analytics workspace create \
--resource-group MY_RESOURCE_GROUP \
--workspace-name dqe-standalone-logs \
--location MY_LOCATIONPaso 2 — Recuperar el Workspace ID:
az monitor log-analytics workspace show \
--resource-group MY_RESOURCE_GROUP \
--workspace-name dqe-standalone-logs \
--query customerId -o tsvPaso 3 — Recuperar la Workspace Primary Key:
az monitor log-analytics workspace get-shared-keys \
--resource-group MY_RESOURCE_GROUP \
--workspace-name dqe-standalone-logs \
--query primarySharedKey -o tsvConserve el Workspace ID y la Primary Key: son necesarios en el YAML del grupo de contenedores (sección 2.8).
2.7. Configurar NGINX
NGINX actúa como proxy inverso dentro del grupo de contenedores de ACI, reenviando las solicitudes entrantes al contenedor dqeone en el puerto 8000. Dado que todos los contenedores de un grupo de ACI comparten el mismo espacio de nombres de red, el destino del proxy utiliza localhost.
Cree un archivo llamado default.conf con el siguiente contenido:
server {
listen 80;
location / {
proxy_pass http://localhost:8000;
proxy_read_timeout 300s;
}
}Si el HTTPS se gestiona directamente a nivel del ACI (sin descarga SSL en el Application Gateway), añada un segundo bloque server para el puerto 443 y cargue los archivos de certificado y clave SSL en el recurso compartido de archivos nginxconf, en un subdirectorio ssl/.
Cargue default.conf en el recurso compartido de archivos nginxconf:
az storage file upload \
--account-name MY_STORAGE_ACCOUNT \
--share-name nginxconf \
--source ./default.conf \
--path default.conf2.8. YAML del grupo de contenedores
Cree un archivo llamado container-group.yaml. Sustituya todos los marcadores de posición antes de implementar.
| Marcador de posición | Descripción |
|---|---|
MY_LOCATION |
Región de Azure, por ejemplo francecentral
|
DQE_REGISTRY_LOGIN |
Nombre de usuario del registro proporcionado por DQE |
DQE_REGISTRY_PASSWORD |
Contraseña del registro proporcionada por DQE |
MY_STORAGE_ACCOUNT |
Nombre de la cuenta de almacenamiento creada en la sección 2.4 |
MY_STORAGE_ACCOUNT_KEY |
Clave de la cuenta de almacenamiento obtenida en el paso 2 de la sección 2.4 |
LOG_ANALYTICS_WORKSPACE_ID |
Workspace ID obtenido en el paso 2 de la sección 2.6 |
LOG_ANALYTICS_WORKSPACE_KEY |
Primary Key obtenida en el paso 3 de la sección 2.6 |
SUBNET_RESOURCE_ID |
ID de recurso completo de la subred del ACI anotado en la sección 2.3 |
SERVERNAME |
Nombre del servidor de PostgreSQL creado en la sección 2.5 |
DB_ADMIN_USER |
Nombre de usuario administrador del servidor PostgreSQL |
DB_ADMIN_PASSWORD |
Contraseña de administrador del servidor PostgreSQL |
DQE_ADMIN_USER |
Nombre de usuario administrador de la aplicación DQE One |
DQE_ADMIN_PASSWORD |
Contraseña de administrador de la aplicación DQE One |
name: dqe-standalone
apiVersion: '2021-10-01'
location: MY_LOCATION
tags: {"docker-compose-application": "docker-compose-application"}
properties:
containers:
- name: nginx
properties:
image: dqeone.azurecr.io/dqe-one-nginx:latest
ports:
- protocol: TCP
port: 80
resources:
requests:
memoryInGB: 0.5
cpu: 0.25
volumeMounts:
- name: nginxconf
mountPath: /etc/nginx/conf.d
- name: redis
properties:
image: dqeone.azurecr.io/dqe-one-redis:v1.0
resources:
requests:
memoryInGB: 1.0
cpu: 0.5
volumeMounts:
- name: redisdata
mountPath: /data
- name: dqeone
properties:
image: dqeone.azurecr.io/standalone:v1.4.0
command:
- "bash"
- "./entrypoint.sh"
ports:
- protocol: TCP
port: 8000
resources:
requests:
memoryInGB: 2.0
cpu: 1.0
environmentVariables:
- name: SFAPIVERSION
value: v65.0
- name: CREATE_SUPERUSER
value: "true"
- name: RUN_COLLECTSTATIC
value: "false"
- name: DQE_ONE_SERVER_ADMIN_USER
value: DQE_ADMIN_USER
- name: DQE_ONE_SERVER_ADMIN_PASSWORD
secureValue: DQE_ADMIN_PASSWORD
- name: DQE_CLIENT_LICENCE
value: CLIENT_LICENCE
- name: WEBSITE_HOSTNAME
value: https://YOUR_DNS_NAME
- name: SECRET_ENCRYPTION_KEY
secureValue: SECRET_ENCRYPTION_KEY_VALUE
- name: WAIT_HOSTS
value: "localhost:6379"
- name: WAIT_HOSTS_TIMEOUT
value: "300"
- name: WAIT_SLEEP_INTERVAL
value: "5"
- name: WAIT_HOST_CONNECT_TIMEOUT
value: "30"
- name: REDIS_URL
value: redis://localhost:6379
- name: PORT
value: "8000"
- name: DEBUG
value: "false"
- name: DB_USER
value: DB_ADMIN_USER
- name: DB_PASSWORD
secureValue: DB_ADMIN_PASSWORD
- name: DB_NAME
value: dqeone
- name: DB_HOST
value: SERVERNAME.postgres.database.azure.com
- name: DB_PORT
value: "5432"
- name: DB_VOLUME_PATH
value: ./db/
- name: DB_MAX_CAPACITY
value: "8000000000"
- name: AUTHORIZED_SFTP_HOSTS
value: AUTHORIZED_SFTP_HOSTS
# Only required if WEBSITE_HOSTNAME does not match the URL used to access the app:
# - name: CSRF_TRUSTED_ORIGINS
# value: https://YOUR_IP_OR_ALTERNATE_URL
imageRegistryCredentials:
- server: dqeone.azurecr.io
username: DQE_REGISTRY_LOGIN
password: DQE_REGISTRY_PASSWORD
diagnostics:
logAnalytics:
workspaceId: LOG_ANALYTICS_WORKSPACE_ID
workspaceKey: LOG_ANALYTICS_WORKSPACE_KEY
restartPolicy: Always
ipAddress:
ports:
- protocol: TCP
port: 80
type: Private
osType: Linux
volumes:
- name: nginxconf
azureFile:
shareName: nginxconf
readOnly: false
storageAccountName: MY_STORAGE_ACCOUNT
storageAccountKey: MY_STORAGE_ACCOUNT_KEY
- name: redisdata
azureFile:
shareName: redisdata
readOnly: false
storageAccountName: MY_STORAGE_ACCOUNT
storageAccountKey: MY_STORAGE_ACCOUNT_KEY
subnetIds:
- id: SUBNET_RESOURCE_IDImportante: utilice las versiones de imagen proporcionadas por DQE. No las sustituya por la etiqueta latest.
Nota clave sobre la red de ACI: todos los contenedores del grupo comparten el mismo espacio de nombres de red. La comunicación entre contenedores utiliza localhost; por eso REDIS_URL es redis://localhost:6379. PostgreSQL se ejecuta fuera del grupo de contenedores como servicio administrado de Azure, por lo que DB_HOST es el FQDN de Azure Database for PostgreSQL.
2.9. Variables de entorno
| Variable | Valor de ejemplo | Descripción |
|---|---|---|
SFAPIVERSION |
v65.0 |
Versión de la API de Salesforce utilizada por la aplicación. |
CREATE_SUPERUSER |
true |
Crea la cuenta de administrador inicial durante el primer arranque. |
RUN_COLLECTSTATIC |
false |
Ejecuta el comando collectstatic de Django durante el arranque. Déjelo en false salvo que sea explícitamente necesario. |
DQE_ONE_SERVER_ADMIN_USER |
— | Nombre de usuario de la cuenta de administrador inicial. |
DQE_ONE_SERVER_ADMIN_PASSWORD |
— | Contraseña de la cuenta de administrador inicial. Utilice secureValue. |
DQE_CLIENT_LICENCE |
— | Clave de licencia del cliente proporcionada por DQE. |
WEBSITE_HOSTNAME |
https://myapp.example.com |
URL pública HTTPS. Debe coincidir con el nombre DNS que apunta al Application Gateway. |
SECRET_ENCRYPTION_KEY |
— | Clave de cifrado para datos sensibles. Genérela una sola vez y no la cambie tras la implementación. Utilice secureValue. |
WAIT_HOSTS |
localhost:6379 |
Servicio que se debe esperar antes de arrancar. Utiliza localhost en ACI. |
WAIT_HOSTS_TIMEOUT |
300 |
Tiempo máximo de espera en segundos para los servicios dependientes. |
WAIT_SLEEP_INTERVAL |
5 |
Retardo en segundos entre comprobaciones de disponibilidad. |
WAIT_HOST_CONNECT_TIMEOUT |
30 |
Tiempo de espera en segundos para cada intento de conexión. |
REDIS_URL |
redis://localhost:6379 |
URL de conexión a Redis. Utiliza localhost en ACI. |
PORT |
8000 |
Puerto interno de escucha de la aplicación. |
DEBUG |
false |
Modo de depuración. Debe ser false en producción. |
DB_USER |
dqeone |
Nombre de usuario administrador de PostgreSQL definido al crear el Postgres Server. |
DB_PASSWORD |
— | Contraseña de administrador de PostgreSQL. Utilice secureValue. |
DB_NAME |
dqeone |
Nombre de la base de datos PostgreSQL. Obligatorio: la aplicación no se iniciará sin este valor. |
DB_HOST |
myserver.postgres.database.azure.com |
FQDN del servidor PostgreSQL. |
DB_PORT |
5432 |
Puerto de PostgreSQL. |
DB_VOLUME_PATH |
./db/ |
Ruta para el almacenamiento relacionado con la base de datos. |
DB_MAX_CAPACITY |
8000000000 |
Capacidad máxima de la base de datos en bytes. |
AUTHORIZED_SFTP_HOSTS |
depot-1.dqe-software.net |
Lista de hosts SFTP autorizados, separados por comas. |
CSRF_TRUSTED_ORIGINS |
https://myapp.example.com |
Orígenes de confianza para el CSRF de Django. Solo es necesario si se accede a la aplicación mediante una URL distinta de WEBSITE_HOSTNAME. |
Importante: el SECRET_ENCRYPTION_KEY debe generarse una sola vez y conservarse durante toda la vida útil de la implementación. Para generar una clave compatible:
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"3. Launcher
3.1. Iniciar sesión en Azure
az login3.2. Implementar el grupo de contenedores
az container create -g MY_RESOURCE_GROUP -f container-group.yamlUna vez finalizada la operación, recupere la dirección IP privada asignada al grupo de contenedores:
az container show \
--resource-group MY_RESOURCE_GROUP \
--name dqe-standalone \
--query "ipAddress.ip" -o tsvEsta dirección IP privada solo es accesible desde dentro de la VNET de Azure.
3.3. Configurar el Backend Pool del Application Gateway
Una vez implementado el ACI, vaya a Azure Portal > Application Gateway > Backend pools > su pool de backend > Edit.
Añada la dirección IP privada del ACI obtenida en el paso 3.2. Haga clic en Save.
3.4. Configurar el Health Probe del Application Gateway
El health probe permite al Application Gateway verificar que la aplicación está en funcionamiento. Vaya a Azure Portal > Application Gateway > Health probes > Add.
Configure el probe para que llame a la ruta raíz de la aplicación mediante HTTP. Haga clic en Test. Si el estado muestra Healthy, el ACI está correctamente configurado y el Application Gateway puede enrutar tráfico hacia él. Haga clic en Add para guardar el health probe.
3.5. Configurar el DNS
Contacte con sus administradores de DNS para crear un registro DNS que apunte a la dirección IP pública del Application Gateway:
| Tipo de registro | Nombre | Valor |
|---|---|---|
| A | standalone.yourdomain.com |
IP pública del Application Gateway |
3.6. Verificar la instalación
Compruebe que todos los contenedores estén en ejecución:
az container show \
--resource-group MY_RESOURCE_GROUP \
--name dqe-standalone \
--query "containers[].{Name:name, State:instanceView.currentState.state}" \
-o tableSalida esperada:
Name State
-------- -------
nginx Running
redis Running
dqeone RunningNota: ACI inicia todos los contenedores simultáneamente. El mecanismo WAIT_HOSTS gestiona el orden de arranque reintentando la conexión. Es normal que se produzca un retardo de 1 a 2 minutos antes de que la aplicación esté completamente operativa en el primer arranque.
Una vez que todos los contenedores estén en ejecución y el DNS se haya propagado, acceda a https://standalone.yourdomain.com.
4. Direcciones IP que autorizar
Una vez que la aplicación esté en funcionamiento, configure el WAF o el firewall situado aguas arriba para autorizar los siguientes rangos de IP entrantes:
- DQE Software Office Server: contacte con el soporte de DQE Software para obtener la dirección IP que debe autorizarse.
- DQE Deduplication Service: contacte con el soporte de DQE Software para obtener la dirección IP que debe autorizarse.
- DQE Quality Service: contacte con el soporte de DQE Software para obtener la dirección IP que debe autorizarse.
Acción requerida: proporcione a DQE Software la dirección IP pública de salida utilizada por su infraestructura de Azure (NAT Gateway, Azure Firewall o equivalente) para que pueda autorizarse en los servicios de DQE.
5. Solución de problemas
Ver registros de contenedores
Los registros de los contenedores están disponibles en Azure Monitor, en la tabla ContainerInstanceLog_CL. También puede obtenerlos directamente mediante la CLI:
az container logs \
--resource-group MY_RESOURCE_GROUP \
--name dqe-standalone \
--container-name CONTAINER_NAMEUnauthorised al descargar imágenes
Verifique que:
- la sección
imageRegistryCredentialscontiene el login y la contraseña correctos proporcionados por DQE; - todas las imágenes hacen referencia al registro de producción de DQE
dqeone.azurecr.io; - las versiones de imagen coinciden con las proporcionadas por DQE.
Configuración de PostgreSQL
Siga la sección 2.5 para el procedimiento de configuración completo. Puntos clave:
- El servidor debe estar en la misma VNet que el ACI: VNets distintas requieren peering y configuración de zona DNS.
- Azure Database for PostgreSQL requiere una subred dedicada delegada a
Microsoft.DBforPostgreSQL/flexibleServers. - Tras crear el servidor, cree la base de datos
dqeonedesde Azure Portal: Azure Database for PostgreSQL → Databases → + Add. - Todas las variables de entorno
DB_*deben establecerse en el YAML del grupo de contenedores antes de la primera implementación; consulte la sección 2.8.
CSRF Verification Failed (403)
Django valida que las solicitudes entrantes procedan de un origen de confianza que coincida con WEBSITE_HOSTNAME. Se produce un error 403 CSRF al acceder a la aplicación mediante una URL distinta del hostname configurado.
Solución permanente: configure el DNS para que el hostname definido en WEBSITE_HOSTNAME resuelva a la IP pública del Application Gateway.
Solución temporal (pruebas sin DNS): establezca WEBSITE_HOSTNAME en la dirección IP y añada CSRF_TRUSTED_ORIGINS:
- name: WEBSITE_HOSTNAME
value: https://YOUR_GATEWAY_IP
- name: CSRF_TRUSTED_ORIGINS
value: https://YOUR_GATEWAY_IPUna vez configurado el DNS, revierta ambos valores al nombre de dominio adecuado.
El Health Probe del Application Gateway falla
Verifique que:
- todos los contenedores del ACI muestran el estado
Running; - el pool de backend contiene la dirección IP privada correcta del ACI;
- la subred está correctamente delegada a
Microsoft.ContainerInstance/containerGroups; - el NSG asociado a la subred permite el tráfico entrante en el puerto 80 desde la subred del Application Gateway.