ФОП: податки для нубасів, ч2 – “ІТ- Експорт – документи до надходження”
0 (0)

4 Жовтня 2022

Ще одна історія, яка добре демонструє те, як взагалі відбувається взаємодія між ІТ-фахівцем та замовниками.

Отже, мені на ФОП-рахунок в Укрсіб банку прийшли гроші – перша зарплата на новому проекті, після чого я отримав такого собі “листа щастя”:

Що трапилося?

  • на ФОП-рахунок прийшли гроші
  • банк гадки не має – хто та чому мені їх відправив, але він має звітувати перед Податковою службою в разі, якщо у тої до мене виникнуть питання (“Звідки гроші, Галя?!“)

Тож банк звертається до мене, як власника банківского рахунку, щоб я надав документи, які підтвержують, що я отримав ці гроші як сплату за мої послуги, і що я сплачу за них податок.

Для цього банк потребує відправити їм лист-роз’яснення – на підставі чого ці гроші прийшли.

Для того, щоб врешті-решт таки отримати свої гроші – потрібно два документи:

  • договір з проектом – він буде підставою для оформленя Інвойсу
  • інвойс за серпень – він буде підставою для зарахування вам грошей

Договір про надання послуг / Service Agreement

Мій договір з проектом виглядає якось так:

Найменування ВЕД

В Договорі варто звернути увагу на пункт 1.2:

1.2. Виконавець є суб’єктом підприємницької
діяльності-фізичною особою з видом діяльності:
консультування з питань інформатизації.

Бо саме він має відповідати вашому Виду економічної діяльності (Найменування ВЕД), який вказано для вашого ФОП.

Перевірити його можно в Кабінеті платника податків – https://cabinet.tax.gov.ua:

Та перевіряємо ВЕД:

Реквізити сторін

Крім того, в Договорі мають бути вказани ваші реквізити, по яких (за якими?) компанія буде нараховувати вам гроші, та має право це робити виключно згідно цього пункту:

І – важливо – назва компаніі, з рахунку якої прийшли гроші – вочевидь, вона має співпадати з назвою компаніі в Договорі.

Рахунок – оферта / Invoice

Другим – потрібно знайти інвойс, за яким вам нарахували гроші – його вам має вислати компанія-замовник. Мій виглядає ось так:

Знов-таки – увага на Опис – саме там вказана послуга, за яку ви отримали гроші – і вона знов-таки має співпадати з вашим ВЕД.

Відповідь банку та отримання грошей

І тепер, коли знайшли всі документи – клікаємо на Відповісти на запит:

Прикріпляємо два документи – Договір та Інвойс:

У полі “Додаткова інформація” – вказуємо:

“У відповідь на “ІТ- Експорт – документи до надходження NNNN.73 USD 31.08.2022″ повідомляємо, що кошти надійшли як оплата згідно договору №NNNN від 22.08.2022. Документи надаємо разом з відповіддю.”

Тобто роз’яснюємо банку – на підставі чого ви отримали гроші.

Monobank та зміна реквізитів

В Монобанку все це наче якось простіше відбувається, принаймні після першої зарплати на рахунок Моно мені ніхто не писав, все обробилося автоматично:

І зверніть увагу на SWIFT Code – MT103 – виявляється, там ціла система, див. MT103.

Доречі, коли я змінював рахунок ФОП з Укрсібу на Моно, то до основного Договіру з Проектом треба було підписати додаток, в якому зазначалися нові реквізити – саме на підставі його Проект може далі робити переводи.

Слава Податковій службі України!))

Loading

Apache Druid: PostgreSQL в ролі metadata storage, та заміна ZooKeeper на Kubernetes Extensions
0 (0)

23 Вересня 2022

Продовжимо серію постів про Apache Druid. В першій частині розібралися з самім Друідом – його архітектурою та моніторингом, в другій частині – розгорнули кластер PostgreSQL та налаштували його моніторинг.

Наступні задачі:

Почнемо з PostgreSQL.

Налаштування Apache Druid з PostgreSQL

Див. PostgreSQL Metadata Store та Metadata Storage.

PostgreSQL users

Повертаємося до файлу manifests/minimal-master-replica-svcmonitor.yaml, з якого створювали PostgreSQL кластер – додаємо юзера druid та базу druid:

...
  users:
    zalando:  # database owner
    - superuser
    - createdb
    foo_user: []  # role for application foo
    druid:       
    - createdb
  databases:
    foo: zalando  # dbname: owner
    druid: druid
...

Оновлюємо кластер:

[simterm]

$ kubectl apply -f maniapplyminimal-master-replica-svcmonitor.yaml

[/simterm]

Отримуємо пароль юзера druid:

[simterm]

$ kubectl -n test-pg get secret druid.acid-minimal-cluster.credentials.postgresql.acid.zalan.do -o 'jsonpath={.data.password}' | base64 -d
Zfqeb0oJnW3fcBCZvEz1zyAn3TMijIvdv5D8WYOz0Y168ym6fXahta05zJjnd3tY

[/simterm]

Відкриваємо порт до PostgreSQL-мастера:

[simterm]

$ kubectl -n test-pg port-forward acid-minimal-cluster-0 6432:5432 
Forwarding from 127.0.0.1:6432 -> 5432
Forwarding from [::1]:6432 -> 5432

[/simterm]

Підключаємося:

[simterm]

$ psql -U druid -h localhost -p 6432
Password for user druid: 
psql (14.5, server 13.7 (Ubuntu 13.7-1.pgdg18.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

druid=>

[/simterm]

Перевіряємо зміст бази – поки що пусто:

[simterm]

druid-> \dt
Did not find any relations.

[/simterm]

Apache Druid metadata.storage config

Використовуємо той же файл druid-operator/examples/tiny-cluster.yaml, з якого розгортали кластер Apache Druid (див. Запуск Druid Cluster).

Зараз маємо в ньому конфіг для DerbyDB, яка зберігає дані на локальному диску:

...
    druid.metadata.storage.type=derby
    druid.metadata.storage.connector.connectURI=jdbc:derby://localhost:1527/druid/data/derbydb/metadata.db;create=true
    druid.metadata.storage.connector.host=localhost
    druid.metadata.storage.connector.port=1527
    druid.metadata.storage.connector.createTables=true
...

Для PostgreSQL нам теж треба вказати connectURI, отже знаходимо його Kubernetes Service:

[simterm]

$ kubectl -n test-pg get svc
NAME                                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
acid-minimal-cluster                       ClusterIP   10.97.188.225   <none>        5432/TCP   14h

[/simterm]

Та редагуємо маніфест – видаляємо або коментимо строки з Derbi, та додаємо новий конфіг:

...
    # Extensions
    #
    druid.extensions.loadList=["druid-kafka-indexing-service", "postgresql-metadata-storage", "druid-kubernetes-extensions"]
...
    # Metadata Store
    #druid.metadata.storage.type=derby
    #druid.metadata.storage.connector.connectURI=jdbc:derby://localhost:1527/druid/data/derbydb/metadata.db;create=true
    #druid.metadata.storage.connector.host=localhost
    #druid.metadata.storage.connector.port=1527
    #druid.metadata.storage.connector.createTables=true

    druid.metadata.storage.type=postgresql
    druid.metadata.storage.connector.connectURI=jdbc:postgresql://acid-minimal-cluster.test-pg.svc.cluster.local/druid
    druid.metadata.storage.connector.user=druid
    druid.metadata.storage.connector.password=Zfqeb0oJnW3fcBCZvEz1zyAn3TMijIvdv5D8WYOz0Y168ym6fXahta05zJjnd3tY
    druid.metadata.storage.connector.createTables=true
...

Оновлюємо кластер Druid:

[simterm]

$ kubectl -n druid apply -f examples/tiny-cluster.yaml

[/simterm]

Перевіримо дані в базі Постгре:

[simterm]

druid-> \dt
               List of relations
 Schema |         Name          | Type  | Owner 
--------+-----------------------+-------+-------
 public | druid_audit           | table | druid
 public | druid_config          | table | druid
 public | druid_datasource      | table | druid
 public | druid_pendingsegments | table | druid
 public | druid_rules           | table | druid
 public | druid_segments        | table | druid
 public | druid_supervisors     | table | druid

[/simterm]

Найс!

Якщо треба мігрувати дані з Derby на Postgre – див. Metadata Migration.

Далі – позбавимося необхідності в ZooKeeper.

Налаштування Druid Kubernetes Service Discovery

Документація по модулю – тут>>>.

Повертаємося до druid-operator/examples/tiny-cluster.yaml, та оновлюємо конфіг – відключаємо ZooKeeper, додаємо новий екстешен druid-kubernetes-extensions і додаткові параметри:

...
    druid.extensions.loadList=["druid-kafka-indexing-service", "postgresql-metadata-storage", "druid-kubernetes-extensions"]
    ...
    druid.zk.service.enabled=false
    druid.serverview.type=http
    druid.coordinator.loadqueuepeon.type=http
    druid.indexer.runner.type=httpRemote
    druid.discovery.type=k8s

    # Zookeeper
    #druid.zk.service.host=tiny-cluster-zk-0.tiny-cluster-zk
    #druid.zk.paths.base=/druid
    #druid.zk.service.compress=false
...

Оновлюємо:

[simterm]

$ kubectl -n druid apply -f examples/tiny-cluster.yam

[/simterm]

Druid RBAC Role

Додаємо RBAC Role та RoleBinding, інакше будемо мати помилки авторізації по типу такої:

ERROR [org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcherbroker] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher – Error while watching node type [BROKER]
org.apache.druid.java.util.common.RE: Expection in watching pods, code[403] and error[{“kind”:”Status”,”apiVersion”:”v1″,”metadata”:{},”status”:”Failure”,”message”:”pods is forbidden: User \”system:serviceaccount:druid:default\” cannot watch resource
\”pods\” in API group \”\” in the namespace \”druid\””,”reason”:”Forbidden”,”details”:{“kind”:”pods”},”code”:403}

Создаємо маніфест із документації:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: druid-cluster
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - configmaps
  verbs:
  - '*'
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: druid-cluster
subjects:
- kind: ServiceAccount
  name: default
roleRef:
  kind: Role
  name: druid-cluster
  apiGroup: rbac.authorization.k8s.io

Створюємо нові ресурси в неймспейсі Друіда:

[simterm]

$ kubectl -n druid apply -f druid-serviceaccout.yaml 
role.rbac.authorization.k8s.io/druid-cluster created
rolebinding.rbac.authorization.k8s.io/druid-cluster created

[/simterm]

І за хвилину перевіряємо логи:

[simterm]

...
2022-09-21T17:01:15,916 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Starting NodeRoleWatcher for [HISTORICAL]...
2022-09-21T17:01:15,916 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Started NodeRoleWatcher for [HISTORICAL].
2022-09-21T17:01:15,916 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider - Created NodeRoleWatcher for nodeRole [HISTORICAL].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider - Creating NodeRoleWatcher for nodeRole [PEON].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Starting NodeRoleWatcher for [PEON]...
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Started NodeRoleWatcher for [PEON].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider - Created NodeRoleWatcher for nodeRole [PEON].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider - Creating NodeRoleWatcher for nodeRole [INDEXER].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Starting NodeRoleWatcher for [INDEXER]...
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Started NodeRoleWatcher for [INDEXER].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider - Created NodeRoleWatcher for nodeRole [INDEXER].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider - Creating NodeRoleWatcher for nodeRole [BROKER].
2022-09-21T17:01:15,917 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Starting NodeRoleWatcher for [BROKER]...
2022-09-21T17:01:15,918 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider$NodeRoleWatcher - Started NodeRoleWatcher for [BROKER].
2022-09-21T17:01:15,918 INFO [main] org.apache.druid.k8s.discovery.K8sDruidNodeDiscoveryProvider - Created NodeRoleWatcher for nodeRole [BROKER].
...

[/simterm]

Готово.

Loading

PostgreSQL: запуск в Kubernetes з PostgreSQL Operator та моніторинг з Prometheus
0 (0)

21 Вересня 2022

Отже, Друід ми запустили – див. Apache Druid: огляд, запуск в Kubernetes та моніторинг з Prometheus. Поки що в дефолтному вигляді, тобто в ролі сторейджа для метаданих використовується локальна база Apache Derby.

Далі будемо переключати Друід на PostgreSQL, ще згодом – прибирати звідти ZooKeeper.

Ну а для початку – запустимо кластер PostgreSQL в Kubernetes, додамо PostgreSQL Exporter для Promethues, та налаштємо збір метрік.

Запускати знову будемо в Minikube, для PostgreSQL використаємо Zalando Operator, а Експортер будемо запускати у вигляді sidecar container.

Глибоко копати в Оператор поки що не будемо, хоча він дуже цікавий, тож якось з ним побавимося. Наразі, нам треба лише його моніторинг.

Документація – Administrator Guide.

Запуск PostgreSQL оператора

Створюємо неймспейс:

[simterm]

$ kubectl create ns postgres-operator
namespace/postgres-operator created

[/simterm]

Додаємо Хельм-репозіторій:

[simterm]

$ helm repo add postgres-operator-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator

[/simterm]

Встановлюємо сам оператор:

[simterm]

$ helm -n postgres-operator install postgres-operator postgres-operator-charts/postgres-operator

[/simterm]

За бажанням додаємо WebUI для оператора:

[simterm]

$ helm repo add postgres-operator-ui-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator-ui
$ helm -n postgres-operator install postgres-operator-ui postgres-operator-ui-charts/postgres-operator-ui

[/simterm]

Перевіряємо поди:

[simterm]

$ kubectl -n postgres-operator get pods
NAME                                    READY   STATUS    RESTARTS   AGE
postgres-operator-649799f4bd-dz5bl      1/1     Running   0          82s
postgres-operator-ui-5cfff55c65-v4bjj   1/1     Running   0          22s

[/simterm]

Вікриваємо собі доступ до Сервісу веб-морди Оператора:

[simterm]

$ kubectl port-forward svc/postgres-operator-ui 8081:80
Forwarding from 127.0.0.1:8081 -> 8081
Forwarding from [::1]:8081 -> 8081

[/simterm]

Перевіряємо:

Тут ми нічого робити не будемо – візьмемо готові приклади конфігу кластера.

Запуск PostgreSQL кластеру

Клонуємо репозіторій:

[simterm]

$ https://github.com/zalando/postgres-operator.git
$ cd postgres-operator/

[/simterm]

В каталозі manifests є декілька прикладів, візьмемо собі manifests/minimal-master-replica-svcmonitor.yaml – він описує неймспейс, кластер-юзерів-бази, два Service та два ServiceMonitors + Sidecars с Prometheus Exporter.

Застосовуємо його:

[simterm]

$ kubectl apply -f manifests/minimal-master-replica-svcmonitor.yaml
namespace/test-pg created
postgresql.acid.zalan.do/acid-minimal-cluster created
service/acid-minimal-cluster-svc-metrics-master created
service/acid-minimal-cluster-svc-metrics-replica created
servicemonitor.monitoring.coreos.com/acid-minimal-cluster-svcm-master created
servicemonitor.monitoring.coreos.com/acid-minimal-cluster-svcm-replica created

[/simterm]

Перевіряєм кластер:

[simterm]

$ kk -n test-pg get postgresql
NAME                   TEAM   VERSION   PODS   VOLUME   CPU-REQUEST   MEMORY-REQUEST   AGE     STATUS
acid-minimal-cluster   acid   13        2      1Gi                                     2m21s   Running

[/simterm]

Та його поди:

[simterm]

$ kk -n test-pg get po
NAME                     READY   STATUS    RESTARTS   AGE
acid-minimal-cluster-0   2/2     Running   0          37s
acid-minimal-cluster-1   1/2     Running   0          24s

[/simterm]

У кожного є своя роль, яка задається в лейблах – spilo-role=master або spilo-role=replica.

PostgreSQL users

Див. доки тут>>> та тут>>>.

Юзери описуються в блоці – сюрпрайз – users:

[simterm]

$ kubectl -n test-pg get postgresql -o yaml
...
    users:
      foo_user: []
      zalando:
      - superuser
      - createdb
...

[/simterm]

І для кожного з них створюється окремий Kubernetes Secret:

[simterm]

$ kk -n test-pg get secret
NAME                                                                 TYPE     DATA   AGE
foo-user.acid-minimal-cluster.credentials.postgresql.acid.zalan.do   Opaque   2      38m
postgres.acid-minimal-cluster.credentials.postgresql.acid.zalan.do   Opaque   2      38m
standby.acid-minimal-cluster.credentials.postgresql.acid.zalan.do    Opaque   2      38m
zalando.acid-minimal-cluster.credentials.postgresql.acid.zalan.do    Opaque   2      38m

[/simterm]

Які потім мапляться в поди через змінні:

[simterm]

$ kubectl -n test-pg get statefulsets acid-minimal-cluster -o yaml
...
      - env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        - name: POSTGRES_USER
          value: postgres
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              key: password
              name: postgres.acid-minimal-cluster.credentials.postgresql.acid.zalan.do
...

[/simterm]

Ну і перевіримо.

Отримаємо пароль:

[simterm]

$ kubectl -n test-pg get secret postgres.acid-minimal-cluster.credentials.postgresql.acid.zalan.do -o 'jsonpath={.data.password}' | base64 -d
CcWdAaqvPA8acxwIpVyM8UHkds2QG3opC3KD7rO1TxITQ1q31cwYLTswzfBeTVsN

[/simterm]

Відкриваємо порт:

[simterm]

$ kubectl -n test-pg port-forward acid-minimal-cluster-0 6432:5432

[/simterm]

Логінимося, та перевіряємо бази:

[simterm]

$ psql -U postgres -h localhost -p 6432
Password for user postgres: 
psql (14.5, server 13.7 (Ubuntu 13.7-1.pgdg18.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# \l
                                  List of databases
   Name    |   Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+-----------+----------+-------------+-------------+-----------------------
 bar       | bar_owner | UTF8     | en_US.utf-8 | en_US.utf-8 | 
 foo       | zalando   | UTF8     | en_US.utf-8 | en_US.utf-8 | 
 postgres  | postgres  | UTF8     | en_US.utf-8 | en_US.utf-8 | 
 template0 | postgres  | UTF8     | en_US.utf-8 | en_US.utf-8 | =c/postgres          +
           |           |          |             |             | postgres=CTc/postgres
 template1 | postgres  | UTF8     | en_US.utf-8 | en_US.utf-8 | =c/postgres          +
           |           |          |             |             | postgres=CTc/postgres
(5 rows)

postgres=#

[/simterm]

PostgreSQL Prometheus Exporter

Див. Sidecar definitions.

Сайдкар ми вже маємо – доданий із маніфесту, і в кожному поді наразі маємо два контейнери – самого PostgreSQL, та його Експортера:

[simterm]

$ kk -n test-pg get po acid-minimal-cluster-0 -o jsonpath='{.spec.containers[*].name}'
postgres exporter

[/simterm]

Глянемо чи є там метріки – відкриваємо порт:

[simterm]

$ kubectl -n test-pg port-forward svc/acid-minimal-cluster-svc-metrics-master 9187:9187
Forwarding from 127.0.0.1:9187 -> 9187
Forwarding from [::1]:9187 -> 9187

[/simterm]

І бачимо, що нічого не бачимо, а кластер взагалі “мертвий” – pg_up == 0:

[simterm]

$ curl -s localhost:9187/metrics | grep pg_ | grep -v '#'
pg_exporter_last_scrape_duration_seconds 1.00031302
pg_exporter_last_scrape_error 1
pg_exporter_scrapes_total 9
pg_up 0

[/simterm]

Чому – тому що експортер має мати дані доступу, тобто логін-пароль.

В конфізі сайдкару додаємо нові змінні, див. Environment Variables:

...
      env:
      - name: "DATA_SOURCE_URI"
        value: "$(POD_NAME)/postgres?sslmode=require"
      - name: "DATA_SOURCE_USER"
        value: "$(POSTGRES_USER)"
      - name: "DATA_SOURCE_PASS"
        value: "$(POSTGRES_PASSWORD)"
      - name: "PG_EXPORTER_AUTO_DISCOVER_DATABASES"
        value: "true"
...

Тобто, Оператор створює StatefulSet, в якому задає змінні POSTGRES_USER та POSTGRES_PASSWORD, які ми використовуємо для сайдкару, щоб задати його власні змінні.

Зберігаємо, оновлюємо:

[simterm]

$ kubectl apply -f manifests/minimal-master-replica-svcmonitor.yaml

[/simterm]

Перевіряємо змінні в самому поді:

[simterm]

$ kubectl -n test-pg get po acid-minimal-cluster-0 -o yaml
...
  - env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.namespace
    - name: POSTGRES_USER
      value: postgres
    - name: POSTGRES_PASSWORD
      valueFrom:
        secretKeyRef:
          key: password
          name: postgres.acid-minimal-cluster.credentials.postgresql.acid.zalan.do
    - name: DATA_SOURCE_URI
      value: $(POD_NAME)/postgres?sslmode=require
    - name: DATA_SOURCE_USER
      value: $(POSTGRES_USER)
    - name: DATA_SOURCE_PASS
      value: $(POSTGRES_PASSWORD)
    - name: PG_EXPORTER_AUTO_DISCOVER_DATABASES
      value: "true"
...

[/simterm]

Та знову перевіряємо мектріки в експортері:

[simterm]

$ curl -s localhost:9187/metrics | grep pg_ | grep -v '#' | tail -5
pg_stat_replication_pg_current_wal_lsn_bytes{application_name="acid-minimal-cluster-0",client_addr="172.17.0.17",server="acid-minimal-cluster-1:5432",slot_name="182",state="streaming"} 1.52655344e+08
pg_stat_replication_pg_wal_lsn_diff{application_name="acid-minimal-cluster-0",client_addr="172.17.0.17",server="acid-minimal-cluster-1:5432",slot_name="182",state="streaming"} 0
pg_stat_replication_reply_time{application_name="acid-minimal-cluster-0",client_addr="172.17.0.17",server="acid-minimal-cluster-1:5432",slot_name="182",state="streaming"} 1.663625745e+09
pg_static{server="acid-minimal-cluster-1:5432",short_version="13.7.0",version="PostgreSQL 13.7 (Ubuntu 13.7-1.pgdg18.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0, 64-bit"} 1
pg_up 1

[/simterm]

pg_up == 1 – йай! Іт воркс!

Prometehus ServiceMonitors

Відкриваємо порт для доступу до самого Prometheus:

[simterm]

$ kubectl -n monitoring port-forward svc/kube-prometheus-stack-prometheus 9090:9090
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090

[/simterm]

Перевіряємо Status > Service Discovery – PostgreSQL тут не бачимо:

СервісМонітори також були створені із маніфесту:

[simterm]

$ kubectl -n test-pg get servicemonitor
NAME                                AGE
acid-minimal-cluster-svcm-master    65m
acid-minimal-cluster-svcm-replica   65m

[/simterm]

Повторюємо “грязний хак”, як робили для Друіда – додаємо в них label "release": "kube-prometheus-stack", чекаємо хвилину-дві, и перевіряємо ще раз:

Та маємо метріки в графіках:

Готово.

Loading

ФОП: податки для нубасів, ч1 – вступ та відкриття рахунків Моно
0 (0)

18 Вересня 2022

На новому проекті бухгалтерію свого ФОП маю вести сам, отже, “Валєра! Настало твайо врємя!” – нарешті є прекрасний шанс поринути в деталі ведення власного ФОП, бо раніше за мене це робила бухгалтерія компанії.

Ну а раз така тема, а деталей там буде досить – то вирішив ще й нову рубрику на RTFM завести, а так як тема стосується тільки України та її податкової системи, то й мова постів в цій рубриці буде виключно українська.

ФОП та банки

Отже, що маємо наразі: у меня є ФОП 3 групи, 5% податку, подача декларації – раз на квартал, плюс щорічна декларація. Про групи та деклараціх якось напишу окремо, як буду подавати.

Між іншим, в Дії мають бути всі ваши документи в тому числі и по ФОП, де можна глянути деталі, наприклад – групу, бо до цього моменту я її навіть не знав 🙂

Для цього переходимо в Послуги – Податки ФОП, та при потребі – Детальна інформація (там же можна включити більше тесну інтеграцію с деякими банками, але про це теж згодом):

UkrSib Bank vs Monobank

Щодо банків та рахунків – маю відкритий колись давно ФОП-рахунок в UkrSib Bank, до якого є нарікання, наприклад операції з рахунками ФОП тільки в банківські дні та час, або необхідності відправляти на продаж валюту  до 13.00, щоб отримати кошти від продажу в той самий день, а не наступний.

Чому Моно? Бо по-перше – миттєві операції по рахунках, по друге, як кажуть – там набагато простіше виконувати всякі операціі по податкам, плюс тісна інтеграція з Дією. Але, нажаль, немає і не буде веб-морди. Будемо звикати. Доречі – є якийсь Бухалтерський кабінет, саме як WebUI, в якому мають бути доступні як раз операціі з податками та звітами.

Що буду робити – відкрию Моно ФОП в гривні, до нього – в USD, і передам IBAN нового долларового рахунку бухгалтерії нового проекту.

Перша компенсація (такий собі замінник слову “зарплатня”) тут буде на старий рахунок ФОП в Укрсибі (де мав радість знайомства с оформленням нових контрактів, хочется окремо записати), а вже всі наступні платежі підуть на новий Моно-рахунок.

Відкриття ФОП в Моно

Тут насправді особо нема й чого писати, бо як й усе в Монобанку – процесс досить тривіальний.

Вид економічної діяльності ФОП

Єдине, на що варто звернути увагу, це опис вашої діяльності – взагалі, Моно має витягти її автоматично, начебто з Дії, але можно описати вручну.

Він має співпадати з вашим Видом економічної діяльності. Якщо не знаєте його – то можно перевірити в Свідоцтві платника єдиного податку, або скористатися такими сервісами як https://opendatabot.ua, або його телегоботом – @OpenDataUABot:

Ну і сам процесс на кшалт “кілька разів кликнути “Окей, згода, погнали далі”. Нажаль, скріни з рос. версії:


Після відкриття рахунку в гривні – з’явиться можливість відкрити другий рахунок, в долларах. Там процесс ще простіший.

І нарешті маємо два рахунки – долларовий, на який приходить “експортна виручка” aka salary:

Та гривнєвий, на який буде переводитися гривня, виручена за продаж тієї самої експортної виручки, і з якого можна буде платити податки та переводити гроші на свої картки:

Далі треба буде потикати “Ліміти обігу ФОП”, ну і можно будет починати платити податки та подавати звіти.

Loading

Apache Druid: огляд, запуск в Kubernetes та моніторинг з Prometheus
0 (0)

18 Вересня 2022

Apache Druid – колонкова база даних, орієнтована на роботу з великими обсягами даних, що поєднує в собі можливості та переваги Time-Series Database, Data Warehouse та пошукової системи.

Загальне завдання – налаштувати моніторинг кластера Druid в Kubernetes, для чого спочатку подивимося, що це взагалі таке і як воно все працює, а потім запустимо Друїд і помацаємо його руками.

Для запуску самого Druid у Kubernetes використовуємо druid-operator, для збору метрик – druid-exporter, для моніторингу – Kubernetes Prometheus Stack, а запускати все це будемо у Minikube.

Огляд Apache Druid

Головна фіча Друїда особисто для мене це те, що система розроблялася для використання в хмарах типу AWS та Kubernetes, тому має чудові можливості зі скейлінгу, зберігання даних та відновлення роботи у разі падіння одного з сервісів.

Крім того, Друїд це:

  • колонковий формат зберігання даних: для обробки завантажується лише необхідна таблиця, що значно збільшує швидкість обробки запитів. Крім того, для швидкого сканування та агрегації даних Друїд оптимізує сховище колонки відповідно до її типу даних.
  • масштабована розподілена система: кластера Друїда можуть розташовуватися на десятках і сотнях окремих серверів
  • паралельна обробка даних: Друїд може обробляти кожен запит паралельно на незалежних інстансах
  • self-healing та self-balancing: у будь-який момент можна додати або видалити нові ноди в кластер, при цьому кластер сам виконує балансування запитів та перемикання між ними без будь-якого даунтайму. Більше того, Друїд дозволяє без даунтайму виконувати і оновлення версій
  • власне, cloud-native и fault-tolerant архитектура: Друїд від початку спроектований до роботи у розподілених системах. Після отримання нових даних, він відразу ж копіює їх у свій Deep Storage, в ролі якого можуть бути сервіси зберігання даних типу Amazon S3, HDFS або мережна файлова система, і дозволяє відновити дані навіть у тому випадку, якщо всі друїдні сервери впадуть. Якщо впала лише частина серверів Друїда – вбудована реплікація даних дозволить продовжувати виконання запитів (вже сверблять руки повбивати його ноди, і подивитися на реакцію Друїда – але не цього разу)
  • ідекси для швидкого пошуку: Друїд використовує стислі Bitmap-індекси, дозволяючи виконувати швидкий пошук по кількох колонках
  • Time-based partitioning: за замовчуванням Друїд розділяє дані у часі, дозволяючи створювати додаткові сегменти з інших полях. В результаті, при виконанні запитів з тимчасовими проміжками будуть використані лише необхідні сегменти даних

Архітектура та компоненти Druid

Загальна архітектура:

Або так:

Див. Processes and servers.

Three-server configuration – Master, Query, Data

Друїд має декілька внутрішніх сервісів, що використовуються для роботи з даними, і ці сервіси можна групувати за трьома типами серверів – Master, Query і Data, див. Pros and cons of colocation.

Master servers – управління додаванням нових даних та доступністю (ingest/indexing) – відповідають за запуск нових завдань щодо додавання нових даних та координацію доступності даних:

  • Coordinator: відповідає за розміщення сегментів даних на конкретні ноди з Historical процесами
  • Overlord: відповідають за розміщення завдань з додавання даних на Middle Managers і за координацію передачі створених сегментів у Deep Store

Query servers – управління запитами від клієнтів

  • Broker: отримує запити від клієнтів, визначає які з Historical або Middle Manager процесів/нід містять необхідні сегменти, і з вихідного запиту клієнта формує та відправляє підзапит (sub-query) кожному з цих процесів, після чого отримує від них відповіді, формує з них загальну відповідь з даними для клієнта, і надсилає йому.
    При цьому Historical відповідають на підзапити, які відносяться до сегментів даних, які вже зберігаються в Deep Store, а Middle Manager – відповідають на запити, які відносяться до нещодавно отриманих даних, які ще знаходяться в пам’яті і не відправлені в Deep Store
  • Router: опціональний сервіс, який надає загальний API для роботи з Брокерами, Оверлордами та Координаторами, хоча їх можна використовувати і безпосередньо. Крім того, Router надає Druid Console – WebUI для роботи з кластером, побачимо її трохи згодом

Data servers – керують додаванням нових даних у кластер і зберігають дані для клієнтів:

  • Historical: “головні робочі конячки” (с) Друїда, які обслуговують сховище та запити до “історичних” даних, тобто. тим, які вже знаходяться в Deep Store – завантажують звідти сегменти на локальний диск хоста, на якому працює Historical процес, та формують відповіді за цими даними для клієнтів
  • Middle Manager: обробляє додавання нових даних у кластер – читають дані із зовнішніх джерел та створюють з них нові сегменти даних, які потім завантажуються у Deep Store
    • Peons: за додавання даних до кластера може відповідати кілька завдань. Для логування та ізоляції ресурсів Middle Manager створює Peons, які є окремими процесами JVM і відповідають за виконання конкретного завдання від Middle Manager. Запускається завжди на тому ж хості, де запущено процес Middle Manager, який породив Peon-процес
  • Indexer: альтернатива Middle Manager і Peon – замість створення окремої JVM на кожне завдання від Middle Manager, Indexer виконує їх в окремих потоках своєї JVM.
    Розробляється як простіша альтернатива Middle Manager і Peon, поки є експерементальною фічею, але в майбутньому замінить Middle Manager і Peon

Окрім внутрішніх процесів, для роботи Друїд також використовує зовнішні сервіси – Deep storage, Metadata storage та ZooKeeper:

  • deep storage: використовується для зберігання всіх даних, доданих у систему і є розподіленим сховищем, доступним кожному серверу Друїда. Це можуть бути Amazon S3, HDFS або NFS
  • metadata storage: використовується для зберігання внутрішніх метаданих, таких як інформація про використання сегментів та поточні завдання. У ролі такого сховища можуть бути класичні СУДБ типу PostgreSQL чи MySQL
  • ZooKeeper: використовується для service discovery та координації роботи сервісів (у Kubernetes замість нього можна використовувати druid-kubernetes-extensions, спробуємо також, але в інший раз)

Druid data flow

Подивимося, як виконується додавання даних (Data ingest) та відповіді клієнтам (Data query, Query Answering).

Data ingest

Додавання даних можна розділити на два етапи: у першому, інстанси Middle Manager запускають завдання з індексування, ці завдання створюють сегменти даних і відправляють їх у Deep Store, а в другому – Historical інстанси завантажують сегменти даних із Deep store, щоб використовувати їх при формуванні відповіді на запити клієнтів.

Перший етап – отримання даних із зовнішніх джерел, індексування та створення сегментів даних, та їх завантаження в Deep store:

При цьому в процесі створення сегментів даних вони вже доступні для виконання запитів по них.

Другий етап – Coordinator опитує Metadata store у пошуках нових сегментів. Як тільки процес Coordinator-а їх знаходить, він вибирає інстанс Historical, який повинен завантажити сегмент з Deep store, щоб він став доступним для обробки запитів:

Coordinator опитує Metadata Store у пошуках нових сегментів. Як тільки він їх знаходить, Coordinator вибирає інстанс Historical, який має завантажити сегмент із Deep store. Коли сегмент завантажений – Historical готовий використовувати його для обробки запитів

Data query

Клієнти надсилають запити до Broker безпосередньо або через Router.

Коли такий запит отримано, Broker визначає, які процеси обслуговують необхідні сегменти даних.

Якщо дані вимагають сегментів одночасно і з Deep store (якісь старі), і з Middle Manager (які отримуємо прямо зараз зі стриму), то Брокер формує та надсилає окремі підзапити до Historical та Middle Manager, де кожен з них виконає свою частину та поверне дані Брокера. Брокер їх агрегує і відповідає клієнту з фінальним результатом.

Далі, спробуємо цю справу розгорнути і потикати гілочкою, а потім зверху додамо моніторингу.

Запуск Druid в Kubernetes

Запускаємо Minikube, додаємо пам’яті і ЦПУ, щоб спокійно запускати всі поди, тут виділяємо 24 RAM і 8 ядер – там Java, най їсть:

[simterm]

$ minikube start --memory 12000 --cpus 8
😄  minikube v1.26.1 on Arch "rolling"
✨  Automatically selected the virtualbox driver
👍  Starting control plane node minikube in cluster minikube
🔥  Creating virtualbox VM (CPUs=8, Memory=12000MB, Disk=20000MB) ...
🐳  Preparing Kubernetes v1.24.3 on Docker 20.10.17 ...
    ▪ Generating certificates and keys ...
    ▪ Booting up control plane ...
    ▪ Configuring RBAC rules ...
🔎  Verifying Kubernetes components...
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

[/simterm]

Перевіряємо:

[simterm]

$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

[/simterm]

І ноди:

[simterm]

$ kubectl get node
NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   41s   v1.24.3

[/simterm]

Переходимо до встановлення Apache Druid operator.

Установка Druid Operator

Створюємо Namespace для оператора:

[simterm]

$ kubectl create namespace druid-operator
namespace/druid-operator created

[/simterm]

Встановлюємо оператор у цей неймспейс:

[simterm]

$ git clone https://github.com/druid-io/druid-operator.git 
$ cd druid-operator/
$ helm -n druid-operator install cluster-druid-operator ./chart

[/simterm]

Перевіряємо под:

[simterm]

$ kubectl -n druid-operator get pod
NAME                                     READY   STATUS    RESTARTS   AGE
cluster-druid-operator-9c8c44f78-8svhc   1/1     Running   0          49s

[/simterm]

Переходимо до створення кластера.

Запуск Druid Cluster

Створюємо неймспейс:

[simterm]

$ kubectl create ns druid
namespace/druid created

[/simterm]

Встановлюємо ZooKeeper:

[simterm]

$ kubectl -n druid apply -f examples/tiny-cluster-zk.yaml
service/tiny-cluster-zk created
statefulset.apps/tiny-cluster-zk created

[/simterm]

Для створення тестового кластера використовуємо конфіг із прикладів – examples/tiny-cluster.yaml.

Створюємо кластер:

[simterm]

$ kubectl -n druid apply -f examples/tiny-cluster.yaml
druid.druid.apache.org/tiny-cluster created

[/simterm]

Перевіряємо ресурси:

[simterm]

$ kubectl -n druid get all
NAME                                    READY   STATUS              RESTARTS   AGE
pod/druid-tiny-cluster-brokers-0        0/1     ContainerCreating   0          21s
pod/druid-tiny-cluster-coordinators-0   0/1     ContainerCreating   0          21s
pod/druid-tiny-cluster-historicals-0    0/1     ContainerCreating   0          21s
pod/druid-tiny-cluster-routers-0        0/1     ContainerCreating   0          21s
pod/tiny-cluster-zk-0                   1/1     Running             0          40s

NAME                                      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                      AGE
service/druid-tiny-cluster-brokers        ClusterIP   None         <none>        8088/TCP                     21s
service/druid-tiny-cluster-coordinators   ClusterIP   None         <none>        8088/TCP                     21s
service/druid-tiny-cluster-historicals    ClusterIP   None         <none>        8088/TCP                     21s
service/druid-tiny-cluster-routers        ClusterIP   None         <none>        8088/TCP                     21s
service/tiny-cluster-zk                   ClusterIP   None         <none>        2181/TCP,2888/TCP,3888/TCP   40s

NAME                                               READY   AGE
statefulset.apps/druid-tiny-cluster-brokers        0/1     21s
statefulset.apps/druid-tiny-cluster-coordinators   0/1     21s
statefulset.apps/druid-tiny-cluster-historicals    0/1     21s
statefulset.apps/druid-tiny-cluster-routers        0/1     21s
statefulset.apps/tiny-cluster-zk                   1/1     40s

[/simterm]

Чекаємо, поки піди перейдуть у Running (хвилин 5 зайняло), і прокидаємо порт до Druid Router:

[simterm]

$ kubectl port-forward svc/druid-tiny-cluster-routers 8888:8088 -n druid
Forwarding from 127.0.0.1:8888 -> 8088
Forwarding from [::1]:8888 -> 8088

[/simterm]

Відкриваємо Druid Console – http://localhost:8888:

Можна заглянути в Druid Services – побачимо ті ж сервіси, що бачили у вигляді Kubernetes-подів:

Ось тут є непогане коротке відео з прикладом завантаження тестових даних – заради інтересу можна пройтися.

Переходимо до встановлення Prometheus.

Установка Kube Prometheus Stack

Створюємо неймспейс:

[simterm]

$ kubectl create ns monitoring
namespace/monitoring created

[/simterm]

Встановлюємо KPS:

[simterm]

$ helm -n monitoring install kube-prometheus-stack prometheus-community/kube-prometheus-stack
NAME: kube-prometheus-stack
LAST DEPLOYED: Tue Sep 13 15:24:22 2022
NAMESPACE: monitoring
STATUS: deployed
...

[/simterm]

Перевіряємо поди:

[simterm]

$ kubectl -n monitoring get po
NAME                                                        READY   STATUS              RESTARTS   AGE
alertmanager-kube-prometheus-stack-alertmanager-0           0/2     ContainerCreating   0          22s
kube-prometheus-stack-grafana-595f8cff67-zrvxv              3/3     Running             0          42s
kube-prometheus-stack-kube-state-metrics-66dd655687-nkxpb   1/1     Running             0          42s
kube-prometheus-stack-operator-7bc9959dd6-d52gh             1/1     Running             0          42s
kube-prometheus-stack-prometheus-node-exporter-rvgxw        1/1     Running             0          42s
prometheus-kube-prometheus-stack-prometheus-0               0/2     Init:0/1            0          22s

[/simterm]

Чекаємо кілька хвилин, поки всі стануть Running і відкриваємо собі доступ до Prometheus:

[simterm]

$ kubectl -n monitoring port-forward svc/kube-prometheus-stack-prometheus 9090:9090
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090

[/simterm]

Відкриваємо собі доступ до Grafana:

[simterm]

$ kubectl -n monitoring port-forward svc/kube-prometheus-stack-grafana 8080:80
Forwarding from 127.0.0.1:8080 -> 3000
Forwarding from [::1]:8080 -> 3000

[/simterm]

Grafana dashboard

Отримуємо пароль від Grafana – знаходимо її Secret:

[simterm]

$ kubectl -n monitoring get secret | grep graf
kube-prometheus-stack-grafana                                  Opaque               3      102s

[/simterm]

І отримуємо значення:

[simterm]

$ kubectl -n monitoring get secret kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
prom-operator

[/simterm]

Для Grafana є готова community дашборда, поки додамо її, а взагалі потім зробимо свою.

Відкриваємо в браузері localhost:8080, логінимся, переходимо в Dashboards > Import:

Вказуємо ID 12155, завантажуємо:

ВибираємоPrometheus як datasource:

Отримуємо таку борду, але поки що без даних:

Запуск Druid Exporter

Клонуємо репозиторій:

[simterm]

$ cd ../
$ git clone https://github.com/opstree/druid-exporter.git
$ cd druid-exporter/

[/simterm]

Перевіряємо Druid Router Service – нам треба його повне ім’я, щоб налаштувати Exporter:

[simterm]

$ kubectl -n druid get svc
NAME                              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                      AGE
...
druid-tiny-cluster-routers        ClusterIP   None         <none>        8088/TCP                     4m3s
...

[/simterm]

Встановлюємо експортер в неймспейсmonitoring, в параметрі druidURL вказуємо URL сервісу Router нашого Друїд-кластера, дивилися вище, його порт, включаємо створення Kubernetes ServiceMonitor та неймспейс, в якому у нас працює Prometheus (monitoring):

[simterm]

$ helm -n monitoring install druid-exporter ./helm/ --set druidURL="http://druid-tiny-cluster-routers.druid.svc.cluster.local:8088" --set druidExporterPort="8080" --set logLevel="debug" --set logFormat="text" --set serviceMonitor.enabled=true --set serviceMonitor.namespace="monitoring"
NAME: druid-exporter
LAST DEPLOYED: Tue Sep 13 15:28:25 2022
NAMESPACE: monitoring
STATUS: deployed
...

[/simterm]

Відкриваємо доступ до Exporter Service:

[simterm]

$ kubectl -n monitoring port-forward svc/druid-exporter-prometheus-druid-exporter 8989:8080
Forwarding from 127.0.0.1:8989 -> 8080
Forwarding from [::1]:8989 -> 8080

[/simterm]

Перевіряємо метрики:

[simterm]

$ curl -s localhost:8989/metrics | grep -v '#' | grep druid_
druid_completed_tasks 0
druid_health_status{druid="health"} 1
druid_pending_tasks 0
druid_running_tasks 0
druid_waiting_tasks 0

[/simterm]

Окей – вже щось, хоча поки що мало. Додаємо збір даних у Prometheus, а потім додамо ще метрик.

Prometheus ServiceMonitor

Перевіряємо Prometheus Service Discovery – тут повинні бути всі наші СервісМонітори:

Перевірити, чи створено Druid ServiceMonitor в Kubernetes :

[simterm]

$ kubectl -n monitoring get servicemonitors
NAME                                             AGE
druid-exporter-prometheus-druid-exporter         87s
...

[/simterm]

Перевіряємо ресурс Prometheus - йогоserviceMonitorSelector:

[simterm]

$ kubectl -n monitoring get prometheus -o yaml | yq .items[].spec.serviceMonitorSelector.matchLabels
{
  "release": "kube-prometheus-stack"
}

[/simterm]

Редагуємо Druid ServiceMonitor:

[simterm]

$ kubectl -n monitoring edit servicemonitor druid-exporter-prometheus-druid-exporter

[/simterm]

Додаємо лейблу release: kube-prometheus-stack, щоб Prometheus почав з цього ServiceMonitor збирати метрики:

Чекаємо на хвилину-дві, перевіряємо монітори ще раз:

Перевіряємо метрики:

 

Окей – тепер у нас є Druid Cluster, є Prometheus, який збирає метрики – залишилося налаштувати ці самі метрики.

Apache Druid Monitoring

Налаштування моніторингу Друїда включає Emitters і Monitors: емітери “пушать” метрики з Друїда “назовні”, а монітори визначають те, які саме метрики будуть доступні. При цьому для деяких метрик потрібно включати додаткові розширення.

Див:

Усі сервіси мають загальні метрики, наприклад query/time, та свої власні, наприклад для Broker є метрика sqlQuery/time, які можуть бути задані для конкретного сервісу через його runtime.properties.

Druid Metrics Emitters

Щоб включити “емітинг” метрик – редагуємо examples/tiny-cluster.yaml та в common.runtime.properties додаємо:

druid.emitter=http
druid.emitter.logging.logLevel=debug
druid.emitter.http.recipientBaseUrl=http://druid-exporter-prometheus-druid-exporter.monitoring.svc.cluster.local:8080/druid

Зберігаємо, оновлюємо кластер:

[simterm]

$ kubectl -n druid apply -f examples/tiny-cluster.yaml 
druid.druid.apache.org/tiny-cluster configured

[/simterm]

Чекаємо хвилину-дві, щоб піди перезапустилися і почали збиратися метрики, перевіряємо метрики в Експортері:

[simterm]

$ curl -s localhost:8989/metrics | grep -v '#' | grep druid_ | head
druid_completed_tasks 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-brokers-0:8088",metric_name="avatica-remote-JsonHandler-Handler/Serialization",service="druid-broker"} 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-brokers-0:8088",metric_name="avatica-remote-ProtobufHandler-Handler/Serialization",service="druid-broker"} 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-brokers-0:8088",metric_name="avatica-server-AvaticaJsonHandler-Handler/RequestTimings",service="druid-broker"} 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-brokers-0:8088",metric_name="avatica-server-AvaticaProtobufHandler-Handler/RequestTimings",service="druid-broker"} 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-brokers-0:8088",metric_name="jetty-numOpenConnections",service="druid-broker"} 1
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-coordinators-0:8088",metric_name="compact-task-count",service="druid-coordinator"} 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-coordinators-0:8088",metric_name="compactTask-availableSlot-count",service="druid-coordinator"} 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-coordinators-0:8088",metric_name="compactTask-maxSlot-count",service="druid-coordinator"} 0
druid_emitted_metrics{datasource="",host="druid-tiny-cluster-coordinators-0:8088",metric_name="coordinator-global-time",service="druid-coordinator"} 2

[/simterm]

Появились druid_emitted_metrics – чудово.

У них у лейбліexported_service вказується з якого сервісу метрика отримана, а в metric_name – яка саме метрика.

Перевіряємо в Prometheus:

Чудово!

Але хочеться більше за метрик – “Need more metrics, my lord!” (c)

Druid Monitors

Наприклад, хочеться знати споживання CPU кожним із процесів/сервісом.

Щоб мати можливість бачити всі системні метрики (без Prometheus Node Exporter) – включаємо org.apache.druid.java.util.metrics.SysMonitor, а для даних по роботі JVM – додамо org.apache.druid.java.util.metrics.JvmMonitor.

У той же файл examples/tiny-cluster.yaml до блоку common.runtime.properties додаємо:

...
druid.monitoring.monitors=["org.apache.druid.java.util.metrics.SysMonitor", "org.apache.druid.java.util.metrics.JvmMonitor"]
...

Зберігаємо, оновлюємо кластер, чекаємо на рестарт подів, і через пару хвилин перевіряємо метрики:

Інша справа!

І дашборда в Графані:

Чудово.

У Running/Failed Tasks у нас все ще No Data, тому що нічого не запускали. Якщо завантажити тестові дані із вже згаданого відео – то з’являться та ці метрики.

Залишилося насправді ще багато чого:

  • додати PostgreSQL як Metadata store
  • налаштувати збір метрик з нього
  • протестувати роботу без ZooKeeper (druid-kubernetes-extensions)
  • потестуватиPrometheus Emitter
  • спробувати Indexer замість Middle Manager
  • налаштувати збір логів (куди? поки не придумали, але швидше за все Promtail && Loki)
  • ну і власне – зібрати нормальну дашборду для Графани

А вже маючи моніторинг можна буде поганяти якісь тести навантаження і погратися з тюнінгом продуктивності кластера, але це все вже в наступних частинах.

Посилання по темі

Loading

AWS: VPC Flow Logs – знайомство та аналітика з CloudWatch Logs Insights
0 (0)

10 Червня 2022

AWS VPC Flow Logs – сервіс Amazon, який дозволяє логувати інформацію про трафік між мережевими інтерфейсами у AWS VPC. Далі, ці логи можуть бути передані у AWS CloudWatch Logs для подальшого аналізу, при цьому логування трафіку ніяк не впливає на швидкість роботи мережі.

Коротко розглянемо основні поняття, доступні опції та налаштуємо Flow Logs для VPC з передачею даних для аналізу у CloduWatch Logs.

Опис VPC Flow Logs

Логи можуть бути включені для цілої VPC, підмережі, або конкретного інтерфейсу. При включенні для всієї VPC – логування буде включено для всіх мережевих інтерфейсів цієї мережі.

Сервіси, для яких можна використовувати Flow Logs:

  • Elastic Load Balancing
  • Amazon RDS
  • Amazon ElastiCache
  • Amazon Redshift
  • Amazon WorkSpaces
  • NAT gateways
  • Transit gateways

Дані будуть записані у вигляді flow log records, які представляють собою текстовий запис з заданними полями.

Use Cases – приклади використання

Що можна відстежити за допомогою Flow logs?

  • спрацьовування правил SecuirtyGroup/Network Access List – заблоковані запити будуть відмічені як REJECTED
  • те, заради чого логі впроваджуємо ми – отримати картину трафіку між VPC та сервісами, щоб зрозуміти хто найбільше трафіку споживає, де і скільки cross-AZ трафіку тощо
  • моніторинг віддалених логінів у систему – стежити за портами 22 (SSH), 3389 (RDP)
  • відстеження сканування портів

Flow Log record – поля

Кожен запис у логах містить дані про IP-трафіку, отриманому за aggregation interval і є рядком з полями, розділені пробілами, де кожне поле містить інформацію про передачу даних, наприклад – Source IP, Destination IP і протокол.

За замовчуванням використовується такий формат:

${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status}

Див. таблицю Available fields в документації – все у колонці Version 2 включено у default format. Інші версії доступні у Custom Format.

При створенні Flow Logs, ми можемо використовувати дефолтний формат або створити свій – розглянемо його трохи нижче:

Обмеження VPC Flow Logs

  • не можна використовувати з інстансами EC2-Classic
  • не можна створити логі для VPC-пірингів, якщо вони ведуть до VPC в іншому акаунті
  • після створення лога – не можна змінити його конфігурацію чи формат записів
  • якщо в інтерфейсу кілька IPv4-адрес, і траїфк відправляється до одного з secondary-адрес, то в полі dstaddr буде відображено primary-адресу; щоб отримати оригінальну адресу – використовуйте поле pkt-dstaddr
  • якщо трафік відправлений з або на мережевий інтерфейс, поля srcaddr та dstaddr будуть містити його primary private IPv4 адресу; щоб отримати оригінальну адресу – використовуйте поле pkt-dstaddr та pkt-dstaddr

Також, враховуйте, що:

  • не логуються записи до DNS Amazon, але пишуться, якщо використовується свій DNS
  • не логгується трафік на та з адреси 169.254.169.254 для отримання метаданих EC2-інстансу
  • не логується трафік між мережевим інтерфейсом EC2 та інтерфейсом AWS Network Load Balancer

Див. всі обмеження на Flow log limitations.

Створення VPC Flow Log

Для створення нам потрібно вказати:

  • ресурс, логи якого будемо писати – VPC, підмережа або конкретний мережевий інтерфейс
  • тип трафіку, який логуємо (accepted traffic, rejected traffic або all traffic)
  • і куди писатимемо дані – в кошик, або CloudWatch Logs

Поки подивимося, що вийде з CloudWatch Logs, а наступного разу – спробуємо візуалізувати у Kibana або Grafana.

CloudWatch Logs Log Group

Створюємо Log Group:

Створення IAM Policy та IAM Role

Для того, щоб сервіс Flow Logs міг писати в наш CloudWatch – налаштовуємо йому права доступу.

Переходимо в AWS IAM, створюємо IAM Policy та IAM Role.

Починаємо з Policy:

Додаємо саму політику:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

Зберігаємо:

Створюємо роль.

Переходимо в IAM Roles, створюємо нову, вибираємо тип EC2:

Знаходимо створену вище політику, підключаємо:

Задаємо ім’я, зберігаємо:

Переходимо до Role Trust relationshitp (див. AWS: IAM AssumeRole – описание, примеры), редагуємо – змінюємо значення поля Service на vpc-flow-logs.amazonaws.com:

Вказуємо:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "vpc-flow-logs.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Зберігаємо:

VPC – включення Flow Logs

І нарешті переходимо до включення логів – знаходимо потрібну VPC, клікаємо Flow Logs > Create:

Задаємо ім’я, Filter, Interval:

У Destination вибираємо CloudWatch Logs, вказуємо створені раніше Log Group та IAM Role:

Format – залишаємо Default.

Перевіряємо Status:

І за декілька хвилин – дані пішли:

У лог-групі з’явився перший стрим з ім’ям Elastic Network Interface, з якого знімаються дані:

CloudWatch Logs Insights

Швидко подивимося, що нам доступно в Logs Insights.

Клікаєм Queries для отримання підказки щодо синтаксису запитів:

Наприклад, отримаємо топ-15 хостів за кількістю пакетів:

Або за обсягом надісланих даних:

stats sum(bytes) as BytesSent by srcAddr, dstAddr
| sort BytesSent desc

Окей, а що з іншими форматами?

Наприклад, хочеться побачити напрямок запиту (egress/ingress) та значення поля pkt-dstaddr.

VPC Flow Log – Custom format

Див. приклади на сторінціFlow log record examples.

Використовуємо такий формат:

region vpc-id az-id subnet-id instance-id interface-id flow-direction srcaddr dstaddr srcport dstport pkt-srcaddr pkt-dstaddr pkt-src-aws-service pkt-dst-aws-service traffic-path packets bytes action

У CloudWatch Logs створюємо новий Log group, назвемо його bttrm-eks-dev-1-21-vpc-fl-custom, не забуваємо про retention, щоб дані не лежали вічно (ну і взагалі не забуваємо, що CloudWatch не самий дешевий сервіс):

Повертаємось до VPC, знаходимо потрібну мережу, створюємо новий Flow Log, назвемо його bttrm-eks-dev-1-21-vpc-fl-custom:

Вибираємо Custom Format та поля, які хочемо записувати. При цьому враховуйте, що порядок полів у логах буде такий, який ви використовуєте під час вибору полів.

Тобто, якщо першим клікнути на “region” – то й у логах він буде йти першим:

Виходить так:

${region} ${vpc-id} ${az-id} ${subnet-id} ${instance-id} ${interface-id} ${flow-direction} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${pkt-srcaddr} ${pkt-dstaddr} ${pkt-src-aws-service} ${pkt-dst-aws-service} ${traffic-path} ${packets} ${bytes} ${action}

Flow Log Custom format та CloudWatch Logs Insights

Але якщо ми тепер ми зайдемо в Logs Insights, і спробуємо будь-який із попередніх запитів – отримаємо зовсім не ті поля, які хотіли:

Тобто, дані бачимо, але як розбити поля по колонках?

Ми навряд чи будемо щільно користуватися CloudWatch Logs, швидше за все в продакшені дані підуть у S3 і потім в ELK (logz.io), тому докладно тут зупинятись не буду, проте принцип роботи подивимося.

CloudWatch Logs за замовчуванням створює кілька мета-полів, які ми можемо використовувати у запитах:

  • @message: “сирі” дані – все повідомлення в text
  • @timestamp: час події
  • @logStream: ім’я стриму

Для Custom format, щоб сформувати поля, використовуємо команду parse, якою передаємо поле @message з усім змістом, а потім парсимо його по полях, які розділені пробілами:

parse @message "* * * * * * * * * * * * * * * * * * *" 
| as region, vpc_id, az_id, subnet_id, instance_id, interface_id, 
| flow_direction, srcaddr, dstaddr, srcport, dstport, 
| pkt_srcaddr, pkt_dstaddr, pkt_src_aws_service, pkt_dst_aws_service, 
| traffic_path, packets, bytes, action 
| sort start desc

Тут кількість “*” в @message має бути рівним кількості імен полів, які ми задаємо – ${vpc-id} і т.д.

Крім того, імена полів не повинні містити тире. Тобто, оригінальне ім’я поля ${vpc-id} для виведення імені колонки вказуємо як vpc_id (або vpcID – кому який формат більше подобається).

Перевіряємо:

Інша справа!

Окрім parse, ми можемо використовувати такі команди, як filter, display, stats. Див. усі в CloudWatch Logs Insights query syntax.

Приклади Logs Insights

Ну і спробуємо щось зобразити, наприклад – отримати всі заблоковані запити SecuirtyGroup/Network Access List – вони будуть відзначені як REJECTED.

До нашого запиту:

parse @message "* * * * * * * * * * * * * * * * * * * * * *" 
| as start, end, region, vpc_id, az_id, subnet_id, instance_id, interface_id, 
| flow_direction, srcaddr, dstaddr, srcport, dstport, protocol, 
| pkt_srcaddr, pkt_dstaddr, pkt_src_aws_service, pkt_dst_aws_service, 
| traffic_path, packets, bytes, action

Додамо:

  • filter action="REJECT"
  • stats count(action) as redjects by srcaddr
  • sort redjects desc

Тут:

  • фільтруємо по дії над пакетом – вибираємо все REJECTED
  • рахуємо кількість записів по полю action, вибираючи за IP-адресою джерела, виводимо в колонці redjects
  • і сортуємо по колонці redjects

Тобто повністю запит зараз буде:

parse @message "* * * * * * * * * * * * * * * * * * *" 
| as region, vpc_id, az_id, subnet_id, instance_id, interface_id, 
| flow_direction, srcaddr, dstaddr, srcport, dstport, 
| pkt_srcaddr, pkt_dstaddr, pkt_src_aws_service, pkt_dst_aws_service, 
| traffic_path, packets, bytes, action 
| filter action="REJECT" 
| stats count(action) as redjects by srcaddr 
| sort redjects desc

Отримуємо:

Ми також можемо використовувати негативні фільтри та комбінувати умови фільтра з операторами and/or.

Наприклад, прибрати з виводу всі IP, що починаються з 162.142.125 – додаємо filter (srcaddr not like "162.142.125."):

...
| filter action="REJECT"
| filter (srcaddr not like "162.142.125.")
| stats count(action) as redjects by srcaddr
| sort redjects desc

Див. Sample queries.

І додамо фільтр на вибірку тільки вхідних запитів – flow_direction == ingress:

...
| filter action="REJECT"
| filter (srcaddr not like "162.142.125.") and (flow_direction like "ingress")
| stats count(action) as redjects by flow_direction, srcaddr, dstaddr, pkt_srcaddr, pkt_dstaddr
| sort redjects desc

Отримуємо топ відкинутих запитів – спрацювало правило SecurityGroup або VPC Network Access List.

І дивимося – що за IP у dstaddr – кому йшов пакет, який був заблокований?

Переходимо в EC2 > Network Interfaces, шукаємо Private IP:

Знаходимо “Elastic IP address owner“:

LoadBalancer.

Якщо адреса не знаходиться в AWS – можливо, це якийсь ендпоінт у Kubernetes – шукаємо наприклад так:

[simterm]

$ kubectl get endpoints --all-namespaces | grep 10.1.55.140
dev-ios-check-translation-ns                     ios-check-translation-backend-svc                    10.1.55.140:3000                                                     58d
dev-ios-check-translation-ns                     ios-check-translation-frontend-svc                   10.1.55.140:80                                                       58d

[/simterm]

Загалом на це все.

У наступній частині – налаштуємо збір логів в AWS S3, потім будемо їх звідти збирати в ELK, і там спробуємо зробити візуалізацію та алерти.

Посилання по темі

VPC Flow Logs

CloudWatch Logs

Loading