5. Дополнительные возможности

5.1. Дополнительные возможности конфигурации Стандарт

Примечание

Инструкции данного раздела не выполняются в рамках первичной установки частей Компонента. Необходимость выполнения действий данного раздела определяется в процессе эксплуатации Компонента.

5.1.1. Установки опциональных приложений

Серверы сбора, хранения и индексирования логов устанавливаются независимо от наличия или отсутствия других приложений.

Конкретные средства логирования и мониторинга не входят в состав данного решения и выбираются в соответствии с требованиями конкретного ведомства.

Обязательно необходимо установить, как минимум один из серверов базы данных Postgres (ADP), ADB (Greenplum), ADQM (Clickhouse) или ADG (Tarantool).

Обязательно необходимо установить программное обеспечение CМЭВ3-адаптер для работы со СМЭВ:

Агент СМЭВ4 не входит в состав данного решения и устанавливается отдельно, согласно Руководству администратора СМЭВ4.

5.1.2. Материлиазованные представления

Материализованное представление — это набор записей, который является результатом исполнения SELECT-запроса.

Подробная документация о мат.представлениях представлена в документации Prostore.

Материализованное представление позволяет предварительно вычислить результат запроса и сохранить его для будущего использования.

SELECT-запрос, на котором строится представление, может обращаться к данным одной или нескольких логических баз данных.

Материализованное представление строится на основе данных одной СУБД хранилища (далее — СУБД-источник), а его данные размещаются в других СУБД.

Это позволяет создавать инсталляции, где одна СУБД служит полноценным хранилищем исходных данных, а остальные СУБД отвечают за быструю выдачу данных по запросам чтения.

Материализованное представление помогает ускорить запросы к данным в следующих случаях:

  • если представление содержит результаты сложного запроса, который на исходных данных выполняется дольше;

  • если запросы к представлению возвращают значительно меньше данных, чем запросы к исходным данным;

  • если запросы относятся к категории, которую СУБД хранилища, где размещены данные представления, выполняет более эффективно, чем СУБД-источник (например, ADG быстрее всех из поддерживаемых СУБД обрабатывает чтение по ключу).

Материализованное представление дает доступ к актуальным и архивным данным. Чтение горячих данных из представления недоступно: это позволяет избежать чтения изменений, загруженных из СУБД-источника только частично.

Данные материализованного представления хранятся аналогично данным логических таблиц — в физических таблицах хранилища, которые автоматически создаются при создании представления (см. Рисунок - 5.3).

Связи материализованного представления с физическими таблицами

Рисунок - 5.3 Связи материализованного представления с физическими таблицами

Система поддерживает целостность данных материализованных представлений, размещенных в СУБД-приемнике, периодически синхронизируя их с СУБД-источником (см. Рисунок - 5.4).

Синхронизация материализованных представлений

Рисунок - 5.4 Синхронизация материализованных представлений

Подробная информация об операциях над мат.представлениями изложена в документации Prostore.

Загрузка и обновление данных недоступны для материализованных представлений.

Примечание

Информацию о DDL-запросе, создавшем представление, можно получить с помощью запроса GET_ENTITY_DDL.

Примечание

По умолчанию система ведет статистику обработки запросов к данным логических сущностей. Для получения статистики, выполните запрос GET_ENTITY_STATISTICS..

При запросе или выгрузке данных из материализованного представления можно указать момент времени, по состоянию на который запрашиваются данные. Если момент времени не указан, система возвращает (выгружает) данные, актуальные на момент последней синхронизации представления, иначе — данные, актуальные на запрашиваемый момент времени.

При запросе или выгрузке данных на указанный момент времени может оказаться, что материализованное представление отстало от СУБД-источника и не содержит запрошенные данные. В этом случае система перенаправляет запрос к исходным таблицам СУБД-источника (см. раздел Маршрутизация запросов к материализованным представлениям). Перенаправленный запрос может выполняться дольше, однако это позволяет получить данные, полностью актуальные на указанный момент времени.

Синхронизация материализованных представлений

Система периодически проверяет, нужно ли синхронизировать материализованные представления окружения с СУБД-источником. Периодичность проверки настраивается в конфигурации системы с помощью параметра MATERIALIZED_VIEWS_SYNC_PERIOD_MS; по умолчанию проверка запускается раз в 5 секунд.

Примечание

При необходимости синхронизацию материализованных представлений можно отключить, установив значение параметра MATERIALIZED_VIEWS_SYNC_PERIOD_MS равным 0.

Проверка материализованных представлений запускается по таймеру. Другие события (например, создание представления или загрузка данных) не запускают проверку представлений. При срабатывании таймера система проверяет, появились ли в СУБД-источнике дельты, закрытые после последней синхронизации и, если такие дельты появились, система синхронизирует материализованные представления с СУБД-источником.

Примечание

Материализованное представление, основанное на таблицах из разных логических баз данных, синхронизируется при наличии новых дельт в основной логической базе данных — в той, которой принадлежит представление.

Количество одновременно синхронизируемых представлений задается в конфигурации системы с помощью параметра MATERIALIZED_VIEWS_CONCURRENT. По умолчанию одновременно синхронизируется максимум два представления, а остальные, если они есть, ожидают следующего цикла проверки.

Данные представления синхронизируются отдельно по каждой закрытой дельте — с полным сохранением изменений, выполненных в этих дельтах. В каждой дельте для материализованного представления рассчитывается и сохраняется результат запроса, указанного при создании этого представления. Таким образом, материализованное представление имеет такой же уровень историчности данных, как и исходные логические таблицы, на которых построено представление.

Если системе не удалось синхронизировать материализованное представление, она делает несколько повторных попыток. Максимальное количество таких попыток регулируется параметром конфигурации MATERIALIZED_VIEWS_RETRY_COUNT. По умолчанию система делает до 10 попыток. Если количество попыток исчерпано, но представление так и не удалось синхронизировать, система прекращает попытки синхронизировать это представление. В случае перезапуска системы счетчики попыток синхронизации обнуляются, и система снова пытается синхронизировать представления, которые остались несинхронизированными.

Примечание

Статусы синхронизации материализованных представлений можно посмотреть с помощью запроса CHECK_MATERIALIZED_VIEW .

Пример синхронизации материализованного представления

Рассмотрим пример со следующими условиями:

  • логическая БД marketing содержит логическую таблицу sales и материализованное представление sales_by_stores;

  • логическая БД содержит две дельты:

    • дельта 0: в таблицу sales загружено две записи (с идентификаторами 100 и 101);

    • дельта 1: в таблицу sales загружено еще две записи (с идентификаторами 102 и 103);

  • материализованное представление sales_by_stores содержит результат агрегации и группировки данных таблицы sales и построено на основе следующего запроса:

CREATE MATERIALIZED VIEW marketing.sales_by_stores (
      store_id INT NOT NULL,
      product_code VARCHAR(256) NOT NULL,
      product_units INT NOT NULL,
      PRIMARY KEY (store_id, product_code)
      )
      DISTRIBUTED BY (store_id)
      DATASOURCE_TYPE (adg)
      AS SELECT store_id, product_code, SUM(product_units) FROM marketing.sales
       WHERE product_code <> 'ABC0001'
       GROUP BY store_id, product_code
      DATASOURCE_TYPE = 'adb'

На рисунках ниже (см Рисунок - 5.5 и Рисунок - 5.6) показан порядок синхронизации материализованного представления sales_by_stores. В каждой дельте рассчитывается и сохраняется сумма по столбцу product_units таблицы sales с группировкой по столбцам store_id и product_code. При этом неважно, когда было создано материализованное представление: до дельты 0, после дельты 1 или в какой-то момент между этими дельтами.

Состояние данных на момент дельты 0

Рисунок - 5.5 Состояние данных на момент дельты 0

Состояние данных на момент дельты 1

Рисунок - 5.6 Состояние данных на момент дельты 1

5.1.3. Маршрутизация запросов к материализованным представлениям

Запросы к данным материализованных представлений проходят все этапы маршрутизации, описанные выше, и затем — дополнительные этапы:

  1. Если для материализованного представления не указано ключевое слово FOR SYSTEM_TIME, запрос направляется в СУБД, где размещены данные этого представления. Из представления выбираются данные, актуальные на момент его последней синхронизации.

  2. Иначе, если ключевое слово FOR SYSTEM_TIME указано, система проверяет, есть ли в представлении данные за запрашиваемый момент времени:

    • Если в запросе есть ключевое слово DATASOURCE_TYPE, а данных за запрашиваемый момент времени в представлении нет, в ответе

    возвращается исключение.

    • Если в запросе нет ключевого слова DATASOURCE_TYPE:
      • Если данные есть в представлении, запрос направляется в СУБД, где размещены данные этого представления.

      • Иначе запрос направляется к исходным таблицам СУБД-источника, на которых построено представление.

Примечание

В запросах к материализованным представлениям доступны не все выражения с ключевым словом FOR SYSTEM_TIME Подробнее см. в секции Доступность значений FOR SYSTEM_TIME раздела SELECT

5.1.4. Миграция из Bare metal варианта установки в Kubernetes

В процессе миграции необходимо отделить модули, которым предстоит переехать в Kubernetes от тех, которые остаются в Bare Metal режиме инсталляции.

Миграции подлежат модули Компонента «Витрина данных» и модули Prostore.

СУБД остается вне Kubernetes.

Для мигрирующего модуля оформляется K8S deployment, конфигурация application.yml и logback.xml размещаются в K8S configmap. При смене версии модуля необходимо актуализировать конфигурацию application.yml в соответствии с новой версией документации.

Альтернативно, вместо использования application.yml конфигурировать приложение можно через переменные окружения K8S контейнера.

Сервис исполнения запросов может работать корректно только в рамках одного пода.

Для модулей, имеющих HTTP-интерфейс, дополнительно формируется K8S service, обеспечивающий маршрутизацию к экземплярам модулей.

На диаграмме (см. Рисунок - 5.7) представлена миграция модуля исполнения запросов и Prostore.

Миграция в Kubernetes

Рисунок - 5.7 Миграция в Kubernetes

Для миграции модуля в его корневой директории необходимо создать манифест файлы с инструкциями:

  • deployment;

  • service;

  • configmap.

Примеры создания манифест файлов приведены ниже.

Создать объекты из манифест файлов в Kubernetes можно при помощи утилиты kubectl:

kubectl apply -f <FILE_NAME>

5.1.4.1. Примеры инструкций по развертыванию Prostore в Kubernetes

Пример создания файла deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: prostore
  namespace: dtm-dev
  uid: 46d6e239-427e-4dad-a988-4ce44a53b75e
  resourceVersion: '630322324'
  generation: 39
  creationTimestamp: '2023-03-03T07:36:03Z'
  labels:
    app.kubernetes.io/instance: prostore
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: prostore
    app.kubernetes.io/version: 6.7.0
    helm.sh/chart: prostore-0.2.0
    k8slens-edit-resource-version: v1
  annotations:
    deployment.kubernetes.io/revision: '35'
    helm.sh/template: 1.0.1
    meta.helm.sh/release-name: prostore
    meta.helm.sh/release-namespace: dtm-dev
  selfLink: /apis/apps/v1/namespaces/dtm-dev/deployments/prostore
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/instance: prostore
      app.kubernetes.io/name: prostore
  template:
    metadata:
      creationTimestamp: null
      labels:
        app.kubernetes.io/instance: prostore
        app.kubernetes.io/name: prostore
      annotations:
        checksum/config: 2d5e69a4edfcbaf92ee27d05855c797f1a825c16c08d78b82db62da024cc7b1d
        helm.sh/template: 1.0.1
        kubectl.kubernetes.io/restartedAt: '2023-11-24T09:25:44Z'
        rollout: QMnGFyw4ilxm
    spec:
      volumes:
        - name: logs-q
          emptyDir: {}
        - name: logs-s
          emptyDir: {}
        - name: logback-q
          configMap:
            name: prostore.config
            items:
              - key: logback-q.xml
                path: logback.xml
            defaultMode: 420
        - name: logback-s
          configMap:
            name: prostore.config
            items:
              - key: logback-s.xml
                path: logback.xml
            defaultMode: 420
        - name: fluent-bit-config-q
          configMap:
            name: prostore.config
            items:
              - key: parsers.conf
                path: parsers.conf
              - key: fluent-bit-q.conf
                path: fluent-bit.conf
            defaultMode: 420
        - name: fluent-bit-config-s
          configMap:
            name: prostore.config
            items:
              - key: parsers.conf
                path: parsers.conf
              - key: fluent-bit-s.conf
                path: fluent-bit.conf
            defaultMode: 420
      containers:
        - name: prostore
          image: registry.gosuslugi.local/dtm-dev/query-execution:6.8.1
          command:
            - java
            - '-XX:MaxRAMPercentage=80.0'
            - '-jar'
            -  dtm-query-execution-core.jar
          args:
            - '--logging.config=logback.xml'
          ports:
            - name: http-q
              containerPort: 9090
              protocol: TCP
            - name: metrics-q
              containerPort: 8080
              protocol: TCP
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            - name: POD_IP
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: spec.nodeName
            - name: ADB_HOST
              value: 10.81.0.99
            - name: ADB_MPPW_DEFAULT_MESSAGE_LIMIT
              value: '1000'
            - name: ADB_MPPW_FDW_TIMEOUT_MS
              value: '2000'
            - name: ADB_MPPW_USE_ADVANCED_CONNECTOR
              value: 'true'
            - name: ADB_NAME
            - name: ADB_PASS
              value: dtm
            - name: ADB_USERNAME
              value: dtm
            - name: ADP_HOST
              value: postgres
            - name: ADP_PASS
              value: dtm
            - name: ADP_PORT
              value: '5432'
            - name: ADP_USERNAME
              value: dtm
            - name: ADP_MAX_POOL_SIZE
              value: '4'
            - name: KAFKA_JET_POLL_DURATION_MS
              value: '1000'
            - name: KAFKA_JET_POLL_BUFFER_SIZE
              value: '1000'
            - name: KAFKA_JET_DB_BUFFER_SIZE
              value: '3000'
            - name: ADP_EXECUTORS_COUNT
              value: '4'
            - name: ADP_REST_START_LOAD_URL
              value: http://kafka-postgres-writer:8096/newdata/start
            - name: ADP_REST_STOP_LOAD_URL
              value: http://kafka-postgres-writer:8096/newdata/stop
            - name: ADP_MPPW_CONNECTOR_VERSION_URL
              value: http://kafka-postgres-writer:8096/versions
            - name: ADP_MPPR_QUERY_URL
              value: http://kafka-postgres-reader:8094/query
            - name: ADP_MPPR_CONNECTOR_VERSION_URL
              value: http://kafka-postgres-reader:8094/versions
            - name: CORE_PLUGINS_ACTIVE
              value: ADP
            - name: DTM_NAME
              value: dev
            - name: EDML_CHANGE_OFFSET_TIMEOUT_MS
              value: '180000'
            - name: EDML_DATASOURCE
              value: ADP
            - name: EDML_DEFAULT_CHUNK_SIZE
              value: '500'
            - name: EDML_FIRST_OFFSET_TIMEOUT_MS
              value: '180000'
            - name: KAFKA_BOOTSTRAP_SERVERS
              value: kafka-0.kafka-headless:9092
            - name: KAFKA_JET_WRITERS
              value: http://kafka-jet-writer:8080
            - name: KAFKA_STATUS_EVENT_ENABLED
              value: 'true'
            - name: KAFKA_STATUS_EVENT_TOPIC
              value: status.event
            - name: KAFKA_STATUS_EVENT_WRITE_OPERATIONS_ENABLED
              value: 'true'
            - name: >-
               LOGGING_LEVEL_RU_DATAMART_PROSTORE_QUERY_EXECUTION_CORE_BASE_SERVICE
             value: warn
            - name: TZ
              value: Europe/Moscow
            - name: ZOOKEEPER_DS_ADDRESS
              value: zookeeper-0.zookeeper-headless:2181
            - name: ZOOKEEPER_KAFKA_ADDRESS
              value: zookeeper-0.zookeeper-headless:2181
          resources:
            limits:
              cpu: '1'
              memory: 4Gi
            requests:
              cpu: 125m
            memory: 128Mi
          volumeMounts:
            - name: logs-q
              mountPath: /app/logs
            - name: logback-q
              mountPath: /app/logback.xml
              subPath: logback.xml
          livenessProbe:
            httpGet:
              path: /actuator/health
              port: metrics-q
              scheme: HTTP
            initialDelaySeconds: 20
            timeoutSeconds: 5
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: metrics-q
              scheme: HTTP
            initialDelaySeconds: 20
            timeoutSeconds: 5
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: Always
        - name: fluent-bit-q
          image: registry.gosuslugi.local/proxy-docker.io/fluent/fluent-bit:1.9.6
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            - name: POD_IP
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: spec.nodeName
          resources:
            limits:
              cpu: 100m
              memory: 256Mi
            requests:
              cpu: 100m
              memory: 256Mi
          volumeMounts:
            - name: logs-q
              mountPath: /app/logs
            - name: fluent-bit-config-q
              mountPath: /fluent-bit/etc/
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      securityContext: {}
      imagePullSecrets:
        - name: registry.gosuslugi.local
      schedulerName: default-scheduler
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

Пример создания файла service:

apiVersion: v1
kind: Service
metadata:
name: prostore
spec:
ports:
- name: jdbc
port: 9090
protocol: TCP
targetPort: jdbc
selector:
app.kubernetes.io/instance: prostore
app.kubernetes.io/name: prostore
sessionAffinity: None
type: ClusterIP

Пример создания файла configmap:

# В STDOUT выводит в простом "читаемом" формате
# В FILE_FLUENT выводит в logfmt формате с полями для внутреннего пользования стенда разработки и тестирования
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-logback
data:
logback.xml: |
<configuration>
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
     <layout class="ch.qos.logback.classic.PatternLayout">
          <pattern>
          <Pattern>
          %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
          </Pattern>
          </pattern>
     </layout>
     </appender>
     <appender name="FILE_FLUENT" class="ch.qos.logback.core.rolling.RollingFileAppender">
     <file>/fluent-bit/logs/log.log</file>
     <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <fileNamePattern>/fluent-bit/logs/log.%d{yyyy-MM-dd}.log</fileNamePattern>
          <maxHistory>1</maxHistory>
          <totalSizeCap>1GB</totalSizeCap>
     </rollingPolicy>
     <append>false</append>
     <layout class="ch.qos.logback.classic.PatternLayout">
          <pattern>
          <Pattern>
          @timestamp="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX, UTC}" level=%level threadName="%thread" logger="%logger" message="%replace(%replace(%m){'\n','\\n'}){'\"','\''}" exception="%replace(%replace(%ex){'\"','\''}){'\n','\\n'}%nopex" \n
          </Pattern>
          </pattern>
     </layout>
     </appender>
     <root level="debug" additivity="false">
     <appender-ref ref="STDOUT"/>
     <appender-ref ref="FILE_FLUENT"/>
     </root>
</configuration>

Пример создания файла configmap для Fluentbit:

apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config-demo
data:
fluent-bit.conf: |
[SERVICE]
     Flush        1
     Log_Level    info
     Daemon       off
     Parsers_File /fluent-bit/etc/parsers.conf
[INPUT]
     Name              tail
     Path              /fluent-bit/logs/log.log
     Tag               services
     Buffer_Chunk_Size 400k
     Buffer_Max_Size   6MB
     Mem_Buf_Limit     6MB
     Parser            logfmt
     Refresh_Interval  20
[FILTER]
     Name   record_modifier
     Match  *
     Record hostname "${HOSTNAME}"
     Record serviceName "${DEPLOYMENTUNIT}"
[OUTPUT]
     Name  forward
     Match *
     host  demo-dtm-vector01.ru-central1.internal
     port  24228
parsers.conf: |
[PARSER]
     Name        logfmt
     Format      logfmt
scripts.lua: ""

5.2. Дополнительные возможности конфигурации Лайт

Необходимость выполнения действий данного раздела определяется в процессе эксплуатации Компонента.

5.2.1. Логирование

Сбор лог-файлов Компонента, c записями о событиях производится с помощью Graylog, через утилиту полнотекстового поиска и аналитики Elasticsearch, которая позволяет в режиме реального времени хранить, искать и анализировать большие объемы данных.

При запуске Graylog автоматически конфигурирует Elasticsearch.

Для передачи сообщений в Graylog используется Filebeat.

Просмотр записей лог-файлов доступен через веб-интерфейс Graylog (см. Рисунок - 5.8) по адресу http://0.0.0.0:9010/ (авторизация: admin/somepasswordpepper).

Просмотр записей лог-файлов в Graylog

Рисунок - 5.8 Просмотр записей лог-файлов в Graylog

Каждая запись в таблице содержит следующую информацию:

  1. Уровень логирования;

  2. Дата и время события в формате yyyy-mm-dd hh:mm:ss;

  3. Имя узла, на котором произошло событие.

5.2.2. Проверка версии компонентов

Версии используемых компонентов Компонента можно проверить с помощью запроса CHECK_VERSIONS.