Отже, продовжуємо нашу подорож з міграцією GitLab до себе в Kubernetes. Див. попередні частини:
- GitLab: компоненти, архітектура, інфраструктура та запуск з Helm-чарту в Minikube
- GitLab: Helm-чарт values, залежності та деплой у Kubernetes з AWS S3
- GitLab: міграція даних з GitLab cloud та процес backup-restore у self-hosted версії в Kubernetes
В цілому – все працює, і вже готуємося переносити репозиторії, останнє ( 🙂 ) що залишилось зробити – це моніторинг.
Зміст
GitLab та Prometheus
Документація по моніторингу GitLab:
У нас в Kubernetes кластері розгорнутий свій Prometehus за допомогою Kube Prometheus Stack (далі – KPS) та його Prometheus Operator.
GitLab вміє запускати власний Prometheus, якому відразу налаштовує збір метрик з усіх подів та сервісів, які мають аннотацію gitlab.com/prometheus_scrape=true
.
Крім того, всі поди та сервіси мають аннотацію prometheus.io/scrape=true
, але KPS не вміє працювати з аннотаціями, див. документацію:
The prometheus operator does not support annotation-based discovery of services
Тож маємо два варіанти збору метрик:
- вимкнути Promethus самого GitLab, та через ServiceMonitor-и збирати метрики з компонентів відразу в KPS Prometheus – але тоді всім компонентам доведеться включати ServiceMonitor (і не всі їх мають, тож деякі доведеться додавати вручну через окремі маніфести)
- або ми можемо лишити “вбудований” Prometehus, в якому вже все налаштовано, і через Prometheus federation просто збирати потрібні нам метрики до KPS Prometheus
В другому випадку ми будемо витрачати зайві ресурси на роботу додаткового Prometheus, але знімаємо з себе необхідність в додатковій конфігурації чартів самого GitLab та Prometheus з KPS.
Налаштування Prometheus federation
Документація – Federation.
Спочатку, перевіримо налаштування Prometheus самого GitLab – чи є метрики і які є джоби.
Знаходимо Prometheus Service:
[simterm]
$ kk -n gitlab-cluster-prod get svc gitlab-cluster-prod-prometheus-server NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE gitlab-cluster-prod-prometheus-server ClusterIP 172.20.194.14 <none> 80/TCP 27d
[/simterm]
Відкриваємо до нього доступ:
[simterm]
$ kk -n gitlab-cluster-prod port-forward svc/gitlab-cluster-prod-prometheus-server 9090:80
[/simterm]
Заходимо в браузері на http://localhost:9090, переходимо в Status > Configuration, та дивимось які там є джоби:
Далі ще є job_name: kubernetes-service-endpoints
та job_name: kubernetes-services
, але ніяких метрик по ним зараз нема:
Джоби prometheus
та kubernetes-apiservers
нам не потрібні, бо це лише ганяти зайві метрики в KPS Prometheus: в job=prometheus
метрики по самому GitLab Prometheus, в job=kubernetes-apiservers
– дані по Kubernetes API, які Prometheus KPS збирає і так.
Перевіримо, що метрики в GitLab Prometheus взагалі є. Візьмемо, наприклад, метрику sidekiq_concurrency
, див. GitLab Prometheus metrics:
Далі налаштовуємо федерацію – в values
Kube Prometheus Stack в блоці prometheus
додаємо additionalScrapeConfigs
, де вказуємо ім’я джоби, шлях для federation
, в params
– задаємо match
, за яким з GitLab Prometheus вибираємо тільки потрібні нам метрики, а в static_configs
задаємо таргет – GitLab Prometheus Service URL:
... additionalScrapeConfigs: - job_name: 'gitlab_federation' honor_labels: true metrics_path: '/federate' params: 'match[]': - '{job="kubernetes-pods"}' - '{job="kubernetes-service-endpoints"}' - '{job="kubernetes-services"}' static_configs: - targets: ["gitlab-cluster-prod-prometheus-server.gitlab-cluster-prod:80"] ...
Деплоїмо, та перевіряємо Targets в KPS Prometheus:
І за хвилину-дві перевіряємо чи пішли метрики до Prometheus KPS:
Метрики GitLab
Тепер, як маємо метрики в нашому Prometheus, давайте поглянемо що взагалі можно і треба моніторити в GitLab.
По-перше – це ресурси Kubernetes, але про них поговоримо, коли будемо створювати власний Grafana dashboard.
Але ще у нас є компоненти самого GitLab, які мають власні метрики:
- PostgreSQL: моніториться власним експортером
- KeyDB/Redis: моніториться власним експортером
- Gitaly: віддає метрики сам, включені по дефолту, див. values
- Runner: віддає метрики сам, виключені по дефолту, див. values
- Shell: віддає метрики сам, виключені по дефолту, див. values
- Registry: віддає метрики сам, виключені по дефолту, див. values
- Sidekiq: віддає метрики сам, включені по дефолту, див. values
- Toolbox && backups: нічого по метрикам, див. values
- Webservice: віддає метрики сам, включені по дефолту, див. values
- додатково метрики від workhorse, виключені по дефолту, див. values
Також є GitLab Exporter з власними метриками – values.
На сторінці GitLab Prometheus metrics є багато метрик, але не всі, тож має сенс пройтись руками по подах, та переглянути метрики прямо з сервісів.
Наприклад, у Gitaly є метрика gitaly_authentications_total
, якої нема в документації.
Відкриваємо доступ до порту з метриками (є у його values):
[simterm]
$ kk -n gitlab-cluster-prod port-forward gitlab-cluster-prod-gitaly-0 9236:9236
[/simterm]
І перевіряємо їх:
[simterm]
$ curl localhost:9236/metrics # HELP gitaly_authentications_total Counts of of Gitaly request authentication attempts # TYPE gitaly_authentications_total counter gitaly_authentications_total{enforced="true",status="ok"} 5511 ...
[/simterm]
Далі – список цікавих (на мій власний погляд) метрик з компонентів, які можна буде потім використати для побудови Grafana dashboards per GitLab service та алертів.
Gitaly
Тут метрики:
gitaly_authentications_total
: Counts of of Gitaly request authentication attemptsgitaly_command_signals_received_total
: Sum of signals received while shelling outgitaly_connections_total
: Total number of connections to Gitalygitaly_git_protocol_requests_total
: Counter of Git protocol requestsgitaly_gitlab_api_latency_seconds_bucket
: Latency between posting to GitLab’s `/internal/` APIs and receiving a responsegitaly_service_client_requests_total
: Counter of client requests received by client, call_site, auth version, response code and deadline_typegitaly_supervisor_health_checks_total
: Count of Gitaly supervisor health checksgrpc_server_handled_total
: Total number of RPCs completed on the server, regardless of success or failuregrpc_server_handling_seconds_bucket
: Histogram of response latency (seconds) of gRPC that had been application-level handled by the server
Runner
Тут метрики:
gitlab_runner_api_request_statuses_total
: The total number of api requests, partitioned by runner, endpoint and statusgitlab_runner_concurrent
: The current value of concurrent settinggitlab_runner_errors_total
: The number of caught errorsgitlab_runner_jobs
: The current number of running buildsgitlab_runner_limit
: The current value of concurrent settinggitlab_runner_request_concurrency
: The current number of concurrent requests for a new jobgitlab_runner_request_concurrency_exceeded_total
: Count of excess requests above the configured request_concurrency limit
Shell
Тут чомусь не працює ендпоінт метрик, не став копатись:
[simterm]
$ kk -n gitlab-cluster-prod port-forward gitlab-cluster-prod-gitlab-shell-744675c985-5t8wn 9122:9122 Forwarding from 127.0.0.1:9122 -> 9122 Forwarding from [::1]:9122 -> 9122 Handling connection for 9122 E0311 09:36:35.695971 3842548 portforward.go:407] an error occurred forwarding 9122 -> 9122: error forwarding port 9122 to pod 51856f9224907d4c1380783e46b13069ef5322ae1f286d4301f90a2ed60483c0, uid : exit status 1: 2023/03/11 07:36:35 socat[10867] E connect(5, AF=2 127.0.0.1:9122, 16): Connection refused
[/simterm]
Registry
Тут метрики:
registry_http_in_flight_requests
: A gauge of requests currently being served by the http serverregistry_http_request_duration_seconds_bucket
: A histogram of latencies for requests to the http serverregistry_http_requests_total
: A counter for requests to the http serverregistry_storage_action_seconds_bucket
: The number of seconds that the storage action takesregistry_storage_rate_limit_total
: A counter of requests to the storage driver that hit a rate limit
Sidekiq
Тут метрики:
- Jobs:
sidekiq_jobs_cpu_seconds
: Seconds of CPU time to run Sidekiq jobsidekiq_jobs_db_seconds
: Seconds of DB time to run Sidekiq jobsidekiq_jobs_gitaly_seconds
: Seconds of Gitaly time to run Sidekiq jobsidekiq_jobs_queue_duration_seconds
: Duration in seconds that a Sidekiq job was queued before being executedsidekiq_jobs_failed_total
: Sidekiq jobs failedsidekiq_jobs_retried_total
: Sidekiq jobs retriedsidekiq_jobs_interrupted_total
: Sidekiq jobs interruptedsidekiq_jobs_dead_total
: Sidekiq dead jobs (jobs that have run out of retries)sidekiq_running_jobs
: Number of Sidekiq jobs runningsidekiq_jobs_processed_total
: (from gitlab-exporter)
- Redis:
sidekiq_redis_requests_total
: Redis requests during a Sidekiq job executiongitlab_redis_client_exceptions_total
: Number of Redis client exceptions, broken down by exception class
- Queue (from gitlab-exporter):
sidekiq_queue_size
sidekiq_queue_latency_seconds
- Misc:
sidekiq_concurrency
: Maximum number of Sidekiq jobs
Webservice
Трохи про сервіси:
- Action Cable: is a Rails engine that handles websocket connections – див. Action Cable
- Puma: is a simple, fast, multi-threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications – див. GitLab Puma
Тут метрики:
- Database:
gitlab_database_transaction_seconds
: Time spent in database transactions, in secondsgitlab_sql_duration_seconds
: SQL execution time, excluding SCHEMA operations and BEGIN / COMMITgitlab_transaction_db_count_total
: Counter for total number of SQL callsgitlab_database_connection_pool_size
: Total connection pool capacitygitlab_database_connection_pool_connections
: Current connections in the poolgitlab_database_connection_pool_waiting
: Threads currently waiting on this queue
- HTTP:
http_requests_total
: Rack request counthttp_request_duration_seconds
: HTTP response time from rack middleware for successful requestsgitlab_external_http_total
: Total number of HTTP calls to external systemsgitlab_external_http_duration_seconds
: Duration in seconds spent on each HTTP call to external systems
- ActionCable:
action_cable_pool_current_size
: Current number of worker threads in ActionCable thread poolaction_cable_pool_max_size
: Maximum number of worker threads in ActionCable thread poolaction_cable_pool_pending_tasks
: Number of tasks waiting to be executed in ActionCable thread poolaction_cable_pool_tasks_total
: Total number of tasks executed in ActionCable thread pool
- Puma:
puma_workers
: Total number of workerspuma_running_workers
: Number of booted workerspuma_running
: Number of running threadspuma_queued_connections
: Number of connections in that worker’s “to do” set waiting for a worker threadpuma_active_connections
: Number of threads processing a requestpuma_pool_capacity
: Number of requests the worker is capable of taking right nowpuma_max_threads
: Maximum number of worker threads
- Redis:
gitlab_redis_client_requests_total
: Number of Redis client requestsgitlab_redis_client_requests_duration_seconds
: Redis request latency, excluding blocking commands
- Cache:
gitlab_cache_misses_total
: Cache read missgitlab_cache_operations_total
: Cache operations by controller or action
- Misc:
user_session_logins_total
: Counter of how many users have logged in since GitLab was started or restarted
Workhorse
Про сервіс: GitLab Workhorse is a smart reverse proxy for GitLab, див. GitLab Workhorse.
Тут метрики:
gitlab_workhorse_gitaly_connections_total
: Number of Gitaly connections that have been establishedgitlab_workhorse_http_in_flight_requests
: A gauge of requests currently being served by the http servergitlab_workhorse_http_request_duration_seconds_bucket
: A histogram of latencies for requests to the http servergitlab_workhorse_http_requests_total
: A counter for requests to the http servergitlab_workhorse_internal_api_failure_response_bytes
: How many bytes have been returned by upstream GitLab in API failure/rejection response bodiesgitlab_workhorse_internal_api_requests
: How many internal API requests have been completed by gitlab-workhorse, partitioned by status code and HTTP methodgitlab_workhorse_object_storage_upload_requests
: How many object storage requests have been processedgitlab_workhorse_object_storage_upload_time_bucket
: How long it took to upload objectsgitlab_workhorse_send_url_requests
: How many send URL requests have been processed
Ух… Багацько.
Але було цікаво і корисно, щоб більш-менш поринути в те, що взагалі відбувається всередині GitLab кластеру.
Grafana GitLab Overview dashboard
Ну і останнім – побудуємо власну дашборду для GitLab, хоча є багато готових ось тут>>>, можна з них брати приклади запитів та панелей.
Для самих компонентів GitLab мабуть потім можна буде створити окрему, а поки що хочеться на одному екрані бачити що відбувається з подами, воркер-нодами Kubernetes, та загальную інформацю про сервіси GitLab і їхній статус.
Що нам цікаво?
З ресурсів Kubernetes:
- pods: рестарти, pendings
- PVC: зайнято/вільно місця на дисках, IOPS
- CPU/Memory по подах та CPU throttling (якби у подів були ліміти, по-дефолту нема)
- network: in/out bandwich, errors rate
Крім того – хотілося бі мати перед очима статус компонентів GitLab, дані по базі даних, Redis та якусь статистику по HTTP/Git/SSH.
Я вже трохи маю досвід з побудування подібних “обзорних” дашборд, тож в принципі маю уяву як воно буде виглядати, але інколи має сенс набросати схему розміщення блоків олівцем на папері, а потім вже будувати саму борду.
Чисто для мене – бажано, щоб всі дані були на одному екрані/моніторі – потім зручно відразу бачити все, що треба.
Колись давно, коли ще ходив до офісу, це виглядало так – load testing нашого першого Kubernetes-кластеру на колишній роботі:
Поїхали.
Variables
Щоб мати змогу вивести інформацю по конкретному компоненту кластера – додамо змінну component.
Значення формуємо за запитом до kube_pod_info
:
label_values(kube_pod_info{namespace="gitlab-cluster-prod", pod!~".*backup.*"}, pod)
З якої отримаємо лейблу pod
, і потім регуляркою /^([^\d]+)-/
вирізаємо все до цифр:
А далі можемо використовувати $component
, щоб отримати тільки потрібні поди.
Статус компонентів GitLab
Тут досить просто: знаємо кількість подів кожного сервісу. Рахуємо їх, та виводимо UP/DEGRADED/DOWN.
На прикладі Webservice – використовуємо такий запит:
sum(kube_pod_info{namespace="gitlab-cluster-prod", pod=~"gitlab-cluster-prod-webservice-.+"})
Створюємо панель з типом Stat, отримуємо кількість подів:
Задаємо Text mode = Value:
Unit = number:
Створюємо Value mapping:
У нас наразі 2 поди в Deployment, тож якщо буде нуль – то пишему DOWN, якщо тільки один – то DEGRADED, ну а 2 і більше – то ОК, UP.
Повторюємо для всіх сервісів:
Pods status та кількість WorkerNodes
Друге, за чим важливо стежити – це статуси подів та кількість EC2 у AWS EC2 AutoScale групі, бо маємо виділенний node pool під GitLab кластер.
Pod restarts table
Для Pod restarts використаємо тип Table:
Запит:
sum(delta(kube_pod_container_status_restarts_total{namespace="gitlab-cluster-prod", pod=~"$component.*"}[5m])) by (pod)
І ставимо тип Table:
Додаємо Value mappins – в залежності від значення в колонці рестартів ячейка буде змінювати колір:
В Override ховаємо колонку Time, поле Value називаємо Restarts, змінюємо колір колонки Pod та її ім’я:
Результат:
Pods status graph
Далі, виведомо графік статусів подів – рестарти, Pending, etc.
Для статусу використаємо:
sum(avg(kube_pod_status_phase{namespace="gitlab-cluster-prod", phase!="Succeeded", pod=~"$component.*"}) by(namespace, pod, phase)) by(phase)
Для відображення рестартів:
sum(delta(kube_pod_container_status_restarts_total{namespace="gitlab-cluster-prod", pod=~"$component.*"}[5m]))
Результат:
Cluster Autoscaler Worker Nodes
Тут трохи цікавіше: треба порахувати всі Kubernetes Worker Nodes, на яких є поди GitLab, але метрики від самого Cluster AutoScaler на мають лейбли типу “namespace”, тож використаємо метрику kube_pod_info
, яка має лейбли namespace
та node
, і по сумі node
дізнаємось кількість EC2 інстансів:
count(count(kube_pod_info{namespace="gitlab-cluster-prod", pod!~"logical-backup.+"}) by (node))
Для Max nodes довелося значення задавати вручну, але навряд чи воно буде часто змінюватись.
В Thresholds задаємо значення, коли треба напрягатись, нехай буде 10, і включаємо Show thresholds = As filled regions and lines, щоб бачити його на графіку:
Результат:
І все разом виглядає так:
CPU та Memory by Pod
CPU by Pod
Рахуємо % від доступного CPU по кількості ядер. Тут цю кількість теж задав руками, знаючи тип ЕС2, але можна пошукати метрики типу “cores allocatable”:
sum(rate(container_cpu_usage_seconds_total{namespace="gitlab-cluster-prod", container!="POD",pod!="", image=~"", pod=~"$component.*"}[5m]) / 2 * 100) by (pod)
Не пам’ятаю вже, звідки сам запит – але результат у kubectl top pod
підтверджує дані – перевіримо на поді з Sidekiq:
І top:
121 millicpu з 2000 доступних (2 ядра) це:
[simterm]
>>> 121/2000*100 6.05
[/simterm]
На графіку 5,43 – виглядає ок.
В Legend переносимо список вправо, та включаємо Values = Last, щоб сортувати по значеннях:
Результат:
Memory by Pod
Тут рахуємо по container_memory_working_set_bytes
, налаштування таблиці аналогічні:
Доречі, можна було вивести % від доступної пам’яті на ноді, але нехай краще буде в “чистих” байтах.
Або можна додати Threshold с максимум 17179869184 байт – але тоді не так добре буде видно графікі з подів.
І разом маємо таке:
Статистка по дисках
Gitaly PVC used space
Що хотілося б по-перше – бачити вільне місце на диску Gitaly, де будуть всі репозиторії, та загальну статистку по записам-читанню на дисках.
Запити брав з якоїсь дефолтної дашборди з комплекту Kube Prometheus Stack.
Для отримання % зайнятого місця на Gitaly використовуємо запит:
100 - ( kubelet_volume_stats_available_bytes{namespace="gitlab-cluster-prod", persistentvolumeclaim="repo-data-gitlab-cluster-prod-gitaly-0"} / kubelet_volume_stats_capacity_bytes{namespace="gitlab-cluster-prod", persistentvolumeclaim="repo-data-gitlab-cluster-prod-gitaly-0"} * 100 )
Та тип Gauge, Unit – Percent 0-100, і додаємо Thresholds:
Disc IOPS
Додамо operations per second на дисках, запит теж десь з готових борд брав:
ceil(sum by(pod) (rate(container_fs_reads_total{job="kubelet", metrics_path="/metrics/cadvisor", container!="", device=~"(/dev/)?(mmcblk.p.+|nvme.+|rbd.+|sd.+|vd.+|xvd.+|dm-.+|dasd.+)", namespace="gitlab-cluster-prod", pod=~"$component.*"}[$__rate_interval]) + rate(container_fs_writes_total{job="kubelet", metrics_path="/metrics/cadvisor", container!="", namespace="gitlab-cluster-prod", pod=~"$component.*"}[$__rate_interval])))
Сумуємо по подах, у Legend знову додаємо Values = Last, щоб мати змогу сортування:
Disc Throughput
Тут все в принципі аналогічно, тільки інший запит:
sum by(pod) (rate(container_fs_reads_bytes_total{job="kubelet", metrics_path="/metrics/cadvisor", container!="", device=~"(/dev/)?(mmcblk.p.+|nvme.+|rbd.+|sd.+|vd.+|xvd.+|dm-.+|dasd.+)", namespace="gitlab-cluster-prod", pod=~"$component.*"}[$__rate_interval]) + rate(container_fs_writes_bytes_total{job="kubelet", metrics_path="/metrics/cadvisor", container!="", namespace="gitlab-cluster-prod", pod=~"$component.*"}[$__rate_interval]))
І все разом:
Networking
Ще мабуть буде корисно бачити що з мережею – помилки, та рейти In/Out.
Received/Transmitted Errors
Додамо Gauge, де будемо виводити % помилок – container_network_receive_errors_total
, який рахуємо за запитом:
sum(rate(container_network_receive_errors_total{namespace="gitlab-cluster-prod"}[5m])) / sum(rate(container_network_receive_packets_total{namespace="gitlab-cluster-prod"}[5m])) * 100
Та аналогічно – для Transmitted:
sum(rate(container_network_transmit_errors_total{namespace="gitlab-cluster-prod"}[5m])) / sum(rate(container_network_transmit_packets_total{namespace="gitlab-cluster-prod"}[5m])) * 100
Network Bandwidth Bytes/second
Тут рахуємо кількість байт в секунду на кожному поді – container_network_receive_bytes_total
та container_network_transmit_bytes_total
:
Network Packets/second
Аналогічно, тільки з метриками container_network_receive_packets_total
/container_network_transmit_packets_total
:
Не впевнений, що воно буде корисно, але поки що нехай буде.
Webservice HTTP statistic
Для загальної картини – додамо трохи даних по HTTP-запитам на Webservice.
HTTP requests/second
Використаємо метрику http_requests_total
:
Додамо Override, щоб змінити колір для даних по 4хх
та 5хх
кодам:
Webservice HTTP request duration
Тут можна було б побудувати Heatmap використовуєчи http_request_duration_seconds_bucket
, але як на мене – то звичайний графік по типам запитів буде кращий:
sum(increase(http_request_duration_seconds_sum{kubernetes_namespace="gitlab-cluster-prod"}[5m])) by (method) / sum(increase(http_request_duration_seconds_count{kubernetes_namespace="gitlab-cluster-prod"}[5m])) by (method)
Але можна й Heatmap:
sum(increase(http_request_duration_seconds_bucket[10m])) by (le)
Взагалі, по метрикам з типом Histogram можна робити досить багато цікавого, хоча я якось не користувався ними.
Див:
- Creating Grafana Dashboards for Node.js Apps on Kubernetes
- How to visualize Prometheus histograms in Grafana
- Introduction to histograms and heatmaps
Статистика сервісів GitLab
Ну й на останнє – трохи даних по компонентам самого GitLab. В процессі роботи вже щось точно буду міняти, бо поки він не сильно використовується – то й не дуже зрозуміло, що саме заслуговує уваги.
Але з того, що поки приходить в голову – це Sidekiq та його джоби, Redis, PostgreSQL, GitLab Runner.
Sidekiq Jobs Errors rate
Запит:
sum(sidekiq_jobs_failed_total) / sum(sidekiq_jobs_processed_total) * 100
GitLab Runner Errors rate
Запит:
sum(gitlab_runner_errors_total) / sum(gitlab_runner_api_request_statuses_total) * 100
GitLab Redis Errors rate
Запит:
sum(gitlab_redis_client_exceptions_total) / sum(gitlab_redis_client_requests_total) * 100
Gitaly Supervisor errors rate
Запит:
sum(gitaly_supervisor_health_checks_total{status="bad"}) / sum(gitaly_supervisor_health_checks_total{status="ok"}) * 100
Database transactions latency
Запит:
sum(rate(gitlab_database_transaction_seconds_sum[5m])) by (kubernetes_pod_name) / sum(rate(gitlab_database_transaction_seconds_count[5m])) by (kubernetes_pod_name)
User Sessions
Запит:
sum(user_session_logins_total)
Git/SSH failed connections/second – by Grafana Loki
А тут використаємо значення, отримані з Loki – рейт помилок “kex_exchange_identification: Connection closed by remote host“, в Loki це виглядає так:
Див. Grafana Loki: можливості LogQL для роботи з логами та створення метрик для алертів.
В панелі вказуємо Data source = Loki, та використовуємо sum()
і rate()
для отримання значень:
Налаштовуємо Thresholds та Overrides, і маємо такий графік:
І взагалі вся борда тепер виглядає так:
Побачимо, як воно буде далі, і що стане у нагоді, а що буде видалитись, та що можна буде додати ще.
Ну і самі алерти теж треба буде зробити.