Saltar a contenido

Visión general de la Arquitectura

QFieldCloud tiene una arquitectura en contenedores con múltiples contenedores y volúmenes.

QFieldCloud architecture: Rounded rectangles represent containers, ellipses represent volumes. The text in the square brackets is the service name found in the `docker-compose.yml` file, the rest of the text is the function of the container. Arrows between containers shows who initiates the communication. Arrows between a container and a volume represents whether the container reads (arrow pointing to container) or writes (arrow pointing to volume) data. Arrows and containers in gray represent deprecated services. Arrows and containers in dashed line represent optional services.
QFieldCloud architecture: Rounded rectangles represent containers, ellipses represent volumes. The text in the square brackets is the service name found in the docker-compose.yml file, the rest of the text is the function of the container. Arrows between containers shows who initiates the communication. Arrows between a container and a volume represents whether the container reads (arrow pointing to container) or writes (arrow pointing to volume) data. Arrows and containers in gray represent deprecated services. Arrows and containers in dashed line represent optional services.

See an interactive version of the drawing above.

Nota Por simplicidad y claridad, todos los gráficos muestran el llamado "camino feliz" sin detallar la gestión de errores a lo largo del proceso.

Objetos Docker

Contenedores

[nginx] Proxy inverso

El proxy inverso que se sitúa delante de QFieldCloud. Debe tener un nginx para este propósito, ya que la cabecera HTTP X-Accel-Redirect se utiliza mucho en producción para servir archivos directamente desde el [minio] Almacenamiento de Archivos.

Requiere la presencia de un certificado SSL: autofirmado, Let's Encrypt u otro.

[app] Aplicación QFieldCloud

El software principal que ejecuta QFieldCloud, incluyendo autenticación, gestión de permisos, modelos de datos, archivos estáticos, interfaz administrativa y más.

Consta de varias aplicaciones Django, a saber:

  • autenticación - responsable de la autenticación de la API.
  • core - modelos de datos, permisos, gestión de colas de trabajo, API REST, interfaz administrativa y todo lo demás de QFieldCloud que no gestionan las otras aplicaciones Django.
  • filestorage - responsable de la gestión de archivos del proyecto y de su respectiva API REST.
  • notifs - módulo de notificaciones.
  • suscripción - gestión de los planes de usuario, suscripción y cuota de almacenamiento.

[memcached] Caché en memoria

Utiliza memcached para la caché en memoria de la configuración de Django y otros valores calculados.

[ofelia] Runner CRON

Utiliza ofelia para el runner CRON.

[mkcert] Creador del certificado autofirmado

Crea automáticamente un certificado SSL autofirmado para el desarrollo local y las implantaciones de prueba.

[certbot] Gestor de Letsencrypt

Utiliza certbot para gestionar los certificados Let's Encrypt. Recrea automáticamente los certificados SSL que caducan y son recargados automáticamente por [nginx] Reverse Proxy.

[worker_wrapper] Cola Consumer

One or more containers to consume and manage Jobs from the queue. The [worker_wrapper] Queue Consumer regularly polls the [db] App Database for new pending Jobs. Once a Job in PENDING status is encountered, the [worker_wrapper] Queue Consumer sets it to QUEUED status and starts processing it. Then the container will set a bunch of metadata on the Job object in the [db] App Database and start a completely new temporary [qgis] Worker container. It waits for the [qgis] Worker container to finish, gets the logs and stores them in the [db] App Database.

Por último, actualiza un conjunto de metadatos de otros Trabajos en la [db] Base de Datos basándose en el código de salida y en los registros del [qgis] Worker, y establece el estado del Trabajo en FINISHED o FAILED.

El contenedor ejecuta el mismo código Django utilizado en [app] QFieldCloud App.

Lee cómo funciona la cola del consumer [worker_wrapper] en la sección Cola de trabajos.

[qgis] Worker

El contenedor del Worker es el lugar real donde se ejecutan los Trabajos creados por la [app] QFieldCloud App. Cada contenedor Worker es creado dinámicamente por el [worker_wrapper] Cola del Consumer para ejecutar un único trabajo para un único proyecto y eliminado justo después. La ejecución típica de un Worker consiste en iniciar una aplicación QGIS cualquiera, descargar los archivos del proyecto, procesar los archivos del proyecto y volver a cargar los archivos procesados resultantes.

El contenedor [qgis] Worker obtiene variables de entorno, incluido un token temporal de autenticación QFieldCloud, del [worker_wrapper] Cola de Consumer, y le devuelve datos escribiendo registros en el STDERR y escribiendo archivos en el volumen compartido temporal, principalmente feedback.json.

Volúmenes

[cerbot_www]

Almacena los certificados SSL creados por [mkcert] Creador de certificados autofirmados o [certbot] Gestor de Letsencrypt.

[transformation_grids]

Contiene todos los grip de transformación disponibles, descargadas de https://cdn.proj.org/ y puestas a disposición de todos los Trabajos.

[static_volume]

Contiene todos los activos estáticos, como fuentes, CSS, JS o archivos de imagen.

[media_volume]

Aviso Obsoleto, puede ser eliminado en cualquier momento.

La ubicación por defecto para los archivos subidos por el usuario. No debe utilizarse nunca.

Servicios de desarrollo

Los siguientes contenedores sólo están disponibles para fines de desarrollo local y no deben utilizarse en producción, ya que estos servicios no están monitorizados, no tienen copia de seguridad y, en general, están diseñados para un uso crítico dentro del stack.

[minio] Almacenamiento de archivos

Almacenamiento local de objetos (tipo S3) utilizado para el desarrollo. Los datos se almacenan en 4 volúmenes [minio_data], ya que minio impone la replicación.

Debería sustituirse por un proveedor SaaS de almacenamiento de objetos similar a S3.

Si se está ejecutando minio, asegúrate de que el cortafuegos del host permite el puerto 8009, requerido por el servicio minio (o el puerto configurado con la variable de entorno MINIO_API_PORT).

[createbuckets] Crear Buckets de Minio

Contenedor de un solo shot para crear los buckets necesarios en el Almacenamiento de Objetos bajo [minio] Almacenamiento de Archivos.

[webdav] Alternative File Storage

Local WebDAV storage used for development, using WebDAV protocol and specifications.

The data is stored on the [webdav_data] volume.

Can alternatively be used in place of the minio File Storage for storing the files. Can optionally be used for storing only attachments on it.

Info

The webdav storage is optional, it is not a requirement for the system to work properly.

If used, the webdav storage service should be replaced by a proper WebDAV server, e.g. NextCloud.

[db] Aplicación PostgreSQL

Servidor local de base de datos PostgreSQL para alojar los datos de la [app] QFieldCloud Aplicación. El esquema de la base de datos está totalmente gestionado por Django. El esquema no debe ser modificado por servicios externos.

Should be replaced by a proper PostgreSQL database with PostGIS installed SaaS provider.

[smtpdev] Mailing Server

Local mailing server to handle emails being sent, such as registration activation, reset password or notifications.

Should be replaced by a proper email SaaS provider that supports SMTP protocol.

[geodb] GeoDB PostgreSQL

Warning

Deprecated, might be removed at any time.

Stores dynamically created user PostGIS databases.

[minio_data]

Stores data for the [minio] S3 service.

[webdav_data]

Stores data for the [webdav] storage service if present.

[postgres_data]

Almacena datos para [db] Aplicación PostgreSQL.

[smtp4dev_data]

Stores data for [smtpdev] Mailing Server.

[geodb_data]

Warning

Deprecated, might be removed at any time.

Stores data for [geodb] GeoDB PostgreSQL.

Job Queue

QFieldCloud runs very heavy operations to make Project synchronization work smoothly for users. Therefore such operations are executed using the Jobs that run in the background.

Here we have a sequence diagram of the stages each Job run follows.

Some of the steps are intentionally shortened for clarity.

sequenceDiagram
    autonumber
    participant DB as QFieldCloud DB
    participant Queue as QFieldCloud Queue

    loop poll database
      Queue->>DB: select the oldest Job with `PENDING` status
      Queue->>DB: set the status of Job to `STARTED` status

      Queue->>DB: create a new temporary authentication token

      create participant Worker
      Queue->>Worker: create the worker and pass the authentication token

      note over Worker: job specific processing...

      destroy Worker
      Queue-xWorker: get worker's structured and free text logs and exit code

      Queue->>DB: set job's structured logs, free text logs and exit code

      alt Worker has zero exit status
        Queue->>DB: set the Job  status to `FINISHED`
      else Worker has non-zero exit status
        Queue->>DB: set the Job  status to `FAILED`
      end

      break when any of the steps above fails
          Queue-->DB: set the status Job to `FAILED`
      end
    end

While some of the steps are still simplified, this diagram contains much more information about the Job's attribute lifecycle.

sequenceDiagram
    autonumber

    participant DB as QFieldCloud DB
    participant Queue as QFieldCloud Queue

    loop [every few seconds]
      Queue->>DB: select the oldest job with `PENDING` status

      Queue->>DB: set the status of current job to `QUEUED`
      Queue->>DB: set the status of current job to `STARTED` and `started_at` to `now()`

      Queue->>DB: create a new temporary authentication token
      Queue->>DB: set `docker_started_at` to `now()`


      create participant Worker
      Queue->>Worker: - mount `transformation_grids` volume <br> - set shared data volume <br> - set the environment variable with the authentication token <br> - set the environment variables from secrets <br> - set the `pgservice` file from secrets <br><br> Create the worker
      activate Worker

      par run worker
        Worker->>API: Download files
        API->>Worker: Files downloaded

        note over Worker: job specific processing...

        Worker->>API: Upload files
        API->>Worker: Files uploaded

        deactivate Worker

      and wait for Worker to finish on Queue
        Queue->>DB: set `container_id` to the newly created Docker container id
        Queue->>Queue: wait until the Worker is finished or timeout reached
      end

      Queue->>DB: set `docker_finished_at` to `now()`


      destroy Worker
      Queue-xWorker: - get Worker's `stdout` logs <br> - get Worker's structured logs from the `feedback.json` in the shared volume <br> - get Worker's exit status code <br><br> Remove the container and cleanup all Job data

      Queue->>DB: set job's `output` and `feedback`

      alt [Worker has zero exit status]
        Queue->>DB: set the status of current job to `FINISHED` and `finished_at` to `now()`
      else [Worker has non-zero exit status]
        Queue->>DB: set the status of current job to `FAILED`
      end

      break when any of the steps above fails
          Queue-->DB: set the status of the current job to `FAILED`
      end

    end

    participant API as QFieldCloud App