[Kubernetes, Серверное администрирование, Системное администрирование] Практические истории из наших SRE-будней. Часть 2
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Утекло уже немало воды с момента публикации нашего последнего очерка об интересных случаях из практики обслуживания Kubernetes-кластеров и запускаемых в нём сервисов/приложений. Хотим представить очередную серию приключений инженеров «Фланта» в неспокойных водах большой (и не очень) веб-разработки.
История №1. Kafka и переменные от Docker’a в K8s
Работа в системах, построенных на контейнерах, постепенно становится стандартом отрасли и многие инструменты уже довольно плотно закрепились в этой среде. Однако по-прежнему можно найти забавные (а иногда и не очень) баги, связанные с интеграцией в инфраструктуру Docker/Kubernetes.
Однажды нам потребовалось развернуть стандартную связку ZooKeeper + Kafka для Sentry, ну и, возможно, других компонентов в будущем. Выглядит не сложно: берем за основу тяжелочитаемый Helm-чарт Kafka из всем известного проекта на GitHub, обрезаем всё лишнее, добавляем своё под нужды проекта.
Пробуем запускать — получаем первую ошибку:
===> Configuring ...
port is deprecated. Please use KAFKA_ADVERTISED_LISTENERS instead.
Странное сообщение — ок, добавим:
env:
- name: KAFKA_ADVERTISED_LISTENERS
value: {{ printf "%s:%s" .Chart.Name .Values.port._default }}
Ситуация повторяется. Возможно, имеет смысл посмотреть, кто и почему это пишет? И тогда встает главный вопрос: какой образ мы используем и что там есть? Чарт из крупнейшего репозитория основан на использовании довольно популярного образа confluentinc/cp-kafka, который имеет широкофункциональный entrypoint (как и у многих других образов Confluent Community). Если же изучить, что и в какой последовательности запускается, можно найти скрипт, в котором и таится источник проблем:
if [[ -n "${KAFKA_PORT-}" ]]
then
echo "port is deprecated. Please use KAFKA_ADVERTISED_LISTENERS instead."
exit 1
fi
Вроде бы всё ясно, но ведь мы эту переменную (да и вообще ничего похожего) не объявляли!..
Однако, если взглянуть на реально объявленные переменные окружения внутри контейнера, обнаружится, что такая переменная действительно существует… как и некоторые другие, подобного ей формата:
root@kafka-0:/# env |grep KAFKA_PORT
KAFKA_PORT_9092_TCP_PORT=9092
KAFKA_PORT_9092_TCP_PROTO=tcp
KAFKA_PORT=tcp://192.168.252.192:9092
KAFKA_PORT_9092_TCP=tcp://192.168.252.192:9092
KAFKA_PORT_9092_TCP_ADDR=192.168.252.192
Всё дело в том, что kubelet при старте pod’а добавляет набор переменных, формируемых на базе создаваемых сервисов, — это один из способов найти адрес необходимого сервиса, что кратко описано в документации K8s.
Один из самых очевидных способов закрыть вопрос — просто сменить имя сервиса в Kubernetes на что-то более явное (а главное — отличное от лаконичного kafka).
Пробуем, запускаем:
$ kubectl -n sentry logs sentry-kafka-0
===> ENV Variables ...
...
SENTRY_KAFKA_PORT=tcp://192.168.224.150:9092
...
===> Configuring ...
===> Running preflight checks ...
===> Check if /var/lib/kafka/data is writable ...
Поздравляем! Вы великолепны!
История №2. Как скромные 100 байт стали причиной больших проблем
Мы любим ClickHouse и используем эту БД во многих проектах как основную базу для сбора статистики. А еще мы храним там логи K8s-кластеров, что очень удобно и эффективно — для этого даже создали специализированный инструмент loghouse. Так что нас не удивишь тем, что на практике могут периодически возникают аварии, связанные с ClickHouse, причем разной степени тяжести… но этот случай — особенный.
У нас в обслуживании уже много лет есть кластер ClickHouse довольно внушительных размеров: 5 шардов, 10 железных узлов по 45 Тб данных на каждой. В кластере много replicated-таблиц, а обслуживает репликацию кластер ZooKeeper из 3 узлов на виртуальных машинах.
Однажды ночью намертво завис один из гипервизоров, на которых запущены виртуалки ZooKeeper’а. Было принято решение перезагрузить гипервизор, после чего кластер ZK потерял целостность, перестал собираться и обслуживать ClickHouse. Первая мысль — «что-то побилось», поэтому возникла необходимость восстановить метаданные из снапшота.
Сказано — сделано! Однако ZooKeeper при попытке собраться, выбрать лидера и разлить снапшот начинает испытывать проблемы:
2020-07-03 18:19:50,681 [myid:2] - WARN [QuorumPeer[myid=2](plain=[0:0:0:0:0:0:0:0]:2181)(secure=disabled):Follower@129] - Exception when following the leader
java.net.SocketTimeoutException: Read timed out
....
Если пробуем запускать без снапшота и создавать таблички в ClickHouse, то ситуация не лучше:
CREATE TABLE ctest.stor1 (`date` Date, `created` DateTime, `value` Int32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/stor1', '{replica}', date, value, 8192);
....
Received exception from server (version 19.14.3):
Code: 999. DB::Exception: Received from 127.0.0.1:9000. Coordination::Exception. Coordination::Exception: All connection tries failed while connecting to ZooKeeper. Addresses: 192.168.110.60:2181, 192.168.110.61:2181, 192.168.110.62:2181
....
Code: 209, e.displayText() = DB::NetException: Timeout exceeded while reading from socket (192.168.110.62:2181): while receiving handshake from ZooKeeper (version 19.14.3.3 (official build)), 192.168.110.62:2181
(Connection loss).
Любопытно: ведь мы не меняли никаких настроек… ни на серверах, ни в конфигурации самих CH и ZK. Но косвенно данные проблемы указывают на проблемы с сетью, к тому же, они явно завязаны на ZK.
Не буду вдаваться в излишнюю детализацию всех наших действий, предпринятых для обнаружения источника проблем, — достаточно лишь сказать, что понервничали прилично. Итогом же этих исследований стал… самый обычный MTU!
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:e5:6d:fc brd ff:ff:ff:ff:ff:ff
inet 192.168.110.60/24 brd 192.168.110.255 scope global ens3
valid_lft forever preferred_lft forever
Всё дело в том, что сеть на виртуалках для ZK создавалась довольно давно — в 2018 году, сразу после выхода Ubuntu 18.04 и тогда был задействован еще не ставший по-настоящему привычным netplan. А он по умолчанию выставляет MTU в 1500 байт. В то же время, как многие знают, значение MTU по умолчанию в Hetzner vSwitch — 1400 байт.
Итог: использование кластера до сих пор не требовало пакетов подобного размера, так что и проблема ни разу не всплывала, а сейчас разница в 100 байт оказалась фатальной. При уменьшении MTU кластер завёлся и вернулся к работе без каких-либо проблем (а мы продолжили устранять последствия).
Бонус!
При восстановлении метаданных в ZK мы пробовали (помимо прочего) вручную пересоздавать таблицы и обнаружили интересную особенность.
Такой формат создания таблицы (когда ключ партиционирования и первичный ключ указываются прямо в секции ENGINE) начиная с 20 версии ClickHouse является устаревшим (deprecated):
CREATE TABLE ctest.stor1foo (`date` Date, `created` DateTime, `value` Int32)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/stor1foo', '{replica}', date, value, 8192);
В актуальных версиях ClickHouse правильным является такой вариант:
CREATE TABLE ctest.stor1bar
(
`date` Date,
`created` DateTime,
`value` Int32
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/stor1bar', '{replica}')
PARTITION BY date
ORDER BY value
SETTINGS index_granularity = 8192
Приглядитесь: по сути оба варианта делают совершенно идентичную работу и создают одинаковые таблицы с одинаковыми параметрами. Но давайте посмотрим, какие метаданные генерируют эти команды:
[zk: localhost:2181(CONNECTED) 5] get /clickhouse/tables/1/stor1foo/metadata
metadata format version: 1
date column: date
sampling expression:
index granularity: 8192
mode: 0
sign column:
primary key: value
granularity bytes: 10485760
[zk: localhost:2181(CONNECTED) 6] get /clickhouse/tables/1/stor1bar/metadata
metadata format version: 1
date column:
sampling expression:
index granularity: 8192
mode: 0
sign column:
primary key: value
data format version: 1
partition key: date
granularity bytes: 10485760
На выходе есть различие в полях date column и partition key, хотя входные данные, на первый взгляд, одинаковы, что делает несовместимыми метаданные и таблицы в CH, созданные с разным форматированием запроса.
История №3. Как был перегружен K8s-кластер… а на самом деле — перегрет
Однажды от клиента поступило обращение о сбоях в работе одного из сервисов. При осмотре pod’ов этого сервиса выяснилось, что pod’ы периодически перезапускаются и толком не работают.
Сервис был запущен на нескольких узлах K8s, нагрузка на CPU у которых была просто колоссальной. Рост нагрузки произошел на разных узлах не одновременно, но очень близко друг к другу по времени. Ситуация была столь плоха, что в какой-то момент kubelet попросту перестал отвечать.
При этом на проблемных узлах потребление процессора ресурсами кластера было не очень велико: львиная доля приходилась на kidle_inj, т.е. ядро постоянно запускало idle-процесс.
$ dmesg -T | grep intel_powercl
[Sat Jun 13 06:29:00 2020] intel_powerclamp: Start idle injection to reduce power
[Sat Jun 13 06:29:02 2020] intel_powerclamp: Stop forced idle injection
[Sat Jun 13 06:29:05 2020] intel_powerclamp: Start idle injection to reduce power
[Sat Jun 13 06:29:07 2020] intel_powerclamp: Stop forced idle injection
[Sat Jun 13 06:29:08 2020] intel_powerclamp: Start idle injection to reduce power
Этот кластер построен на bare metal, т.е. железных узлах. Возник законный вопрос: что с их температурой?
$ sensors
coretemp-isa-0000
Adapter: ISA adapter
Package id 0: +92.0°C (high = +80.0°C, crit = +100.0°C)
Core 0: +90.0°C (high = +80.0°C, crit = +100.0°C)
Core 1: +91.0°C (high = +80.0°C, crit = +100.0°C)
Core 2: +92.0°C (high = +80.0°C, crit = +100.0°C)
Core 3: +91.0°C (high = +80.0°C, crit = +100.0°C)
90 градусов!
Самое время написать в техподдержку дата-центра с запросом проверить машины, а в то же время искать любые возможности, куда переключить нагрузку.
Ответом техподдержки стало сообщение о необходимости отключения машин на 30 минут для проведения работ. Чтобы согласиться на это, мы отключили (drain’ом) первый проблемный узел. А в результате проведённых операций получили такой ответ ДЦ:
Dear Client,
unfortunately the CPU fan was faulty, we've replaced this and started the server again.
Что и требовалось доказать! Дальнейшие шаги понятны: поочередный вывод узлов из кластера и координация наших действий с сотрудниками в ДЦ, меняющими кулеры. Ситуация с нагрузкой буквально на глазах приходит в норму.
История №4. Ода pg_repack для PostgreSQL
Обычно мы не оказываем услуги DBA. Да, мы можем посмотреть на вашу базу, создать пару индексов, оптимизировать запросы. Зачастую такие вещи происходят просто по стечению обстоятельств: заметили какую-то странность, пошли смотреть внимательно и что-то нашли (всегда можно что-то найти, если смотреть внимательно).
Однажды наш дежурный заметил рост долгих транзакций на базе клиента. Точнее, его зафиксировала система мониторинга: плановая задача очистки неактуальных данных из базы стала занимать всё больше времени, из-за чего попала под триггер долгих транзакций.
Несмотря на предпринятый простой шаг для решения первоначальной проблемы, о которой сообщил мониторинг, стало ясно, что требуется более внимательный анализ ситуации.
Для этого мы с помощью модуля pgstattuple оценили фрагментированность таблиц запросами:
SELECT schemaname,
tablename,
dead_tuple_count(stats) AS dead_tuples,
pg_size_pretty(dead_tuple_len(stats)) AS dead_space,
pg_size_pretty(free_space(stats)) AS free_space,
last_autovacuum,
last_autoanalyze
FROM
(SELECT pgt.schemaname AS schemaname,
pgt.tablename AS tablename,
pgstattuple(pgt.schemaname || '.' || pgt.tablename)
AS stats,
uts.last_autovacuum AS last_autovacuum,
uts.last_autoanalyze AS last_autoanalyze
FROM pg_tables AS pgt
LEFT JOIN pg_stat_user_tables
AS uts
ON pgt.schemaname = uts.schemaname
AND pgt.tablename = uts.relname
WHERE pgt.schemaname NOT IN ('repack','pg_catalog')) AS r
ORDER BY dead_tuples DESC;
… и фрагментированность индексов:
SELECT schemaname,
indexname,
tablename,
dead_tuple_count(stats) AS dead_tuples,
pg_size_pretty(dead_tuple_len(stats)) AS dead_space,
pg_size_pretty(free_space(stats)) AS free_space
FROM
(SELECT pgt.schemaname AS schemaname,
pgt.indexname AS indexname,
pgt.tablename AS tablename,
pgstattuple(pgt.schemaname || '.' || pgt.indexname) AS stats
FROM pg_indexes AS pgt
WHERE pgt.schemaname NOT IN ('repack',
'pg_catalog')
AND pgt.indexname NOT IN ('some',
'important',
'triggers')) AS r
ORDER BY dead_tuples DESC;
В процессе этого исследования были выявлены фрагментированные таблицы и индексы в огромном количестве. Решение — выполнить pg_repack и параллельно зачистить ряд дублирующихся индексов.
Операция repack’а на БД размером 620 Гб заняла целую ночь и утро. Результатом стал выигрыш около 150 Гб дискового пространства и сопутствующее «причесывание» базы в сочетании с небольшим ростом производительности. Ближайший план — миграция с PostgreSQL v11 на v12, так как в нём применены техники для снижения фрагментирования данных, а значит — проблема станет меньше нам мешать.
Вывод банален: следите за базами, оптимизируйте с умом и не превращайте их в лапшу, наполненную бесполезными индексами.
Заключение
Даже привычные инструменты зачастую могут преподносить сюрпризы (как приятные, так и не очень), что, несомненно, делает нашу и без того насыщенную работу еще интереснее. А что подобного случалось у вас? У нас же историй еще много, так что до встречи в следующей части!
P.S.
Читайте также в нашем блоге:
- «6 практических историй из наших SRE-будней»;
- «Из жизни с Kubernetes: Как мы выносили СУБД (и не только) из review-окружений в статическое»;
- «Из жизни с Kubernetes: Как HTTP-сервер испанцев не жаловал»;
- «6 занимательных системных багов при эксплуатации Kubernetes [и их решение]».
===========
Источник:
habr.com
===========
Похожие новости:
- [Информационная безопасность, Системное администрирование, Сетевые технологии, Сетевое оборудование] 2. NGFW для малого бизнеса. Распаковка и настройка
- [Open source, Openshift, Виртуализация, Разработка под Linux] OKD4 – общедоступный релиз уже здесь
- [DevOps, Серверное администрирование, Системное администрирование] Использование переменных Grafana для большей интерактивности дашбордов (перевод)
- [Apache, Big Data, Kubernetes] Запускаем Apache Spark на Kubernetes
- [PostgreSQL] Настройка continious бекапов PostgreSQL
- [DevOps, Kubernetes, Серверное администрирование, Системное администрирование] Запускаем Keycloak в HA режиме на Kubernetes (перевод)
- [Kubernetes, Информационная безопасность, Конференции, Машинное обучение] 23 июля приглашаем на онлайн-митап Hot Backend
- [DevOps, PostgreSQL, Администрирование баз данных] MCS и Postgres Professional запускают облачный сервис управляемой базы данных Postgres Pro
- [Машинное обучение] Как быстро и просто ускорить доступ к API приложениям?
- [IT-компании, Системное администрирование, Софт] Microsoft возобновляет выпуск необязательных обновлений для Windows 10 and Windows Server
Теги для поиска: #_kubernetes, #_servernoe_administrirovanie (Серверное администрирование), #_sistemnoe_administrirovanie (Системное администрирование), #_kubernetes, #_docker, #_clickhouse, #_postgresql, #_flant (Флант), #_blog_kompanii_flant (
Блог компании Флант
), #_kubernetes, #_servernoe_administrirovanie (
Серверное администрирование
), #_sistemnoe_administrirovanie (
Системное администрирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:54
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Утекло уже немало воды с момента публикации нашего последнего очерка об интересных случаях из практики обслуживания Kubernetes-кластеров и запускаемых в нём сервисов/приложений. Хотим представить очередную серию приключений инженеров «Фланта» в неспокойных водах большой (и не очень) веб-разработки. История №1. Kafka и переменные от Docker’a в K8s Работа в системах, построенных на контейнерах, постепенно становится стандартом отрасли и многие инструменты уже довольно плотно закрепились в этой среде. Однако по-прежнему можно найти забавные (а иногда и не очень) баги, связанные с интеграцией в инфраструктуру Docker/Kubernetes. Однажды нам потребовалось развернуть стандартную связку ZooKeeper + Kafka для Sentry, ну и, возможно, других компонентов в будущем. Выглядит не сложно: берем за основу тяжелочитаемый Helm-чарт Kafka из всем известного проекта на GitHub, обрезаем всё лишнее, добавляем своё под нужды проекта. Пробуем запускать — получаем первую ошибку: ===> Configuring ...
port is deprecated. Please use KAFKA_ADVERTISED_LISTENERS instead. Странное сообщение — ок, добавим: env:
- name: KAFKA_ADVERTISED_LISTENERS value: {{ printf "%s:%s" .Chart.Name .Values.port._default }} Ситуация повторяется. Возможно, имеет смысл посмотреть, кто и почему это пишет? И тогда встает главный вопрос: какой образ мы используем и что там есть? Чарт из крупнейшего репозитория основан на использовании довольно популярного образа confluentinc/cp-kafka, который имеет широкофункциональный entrypoint (как и у многих других образов Confluent Community). Если же изучить, что и в какой последовательности запускается, можно найти скрипт, в котором и таится источник проблем: if [[ -n "${KAFKA_PORT-}" ]]
then echo "port is deprecated. Please use KAFKA_ADVERTISED_LISTENERS instead." exit 1 fi Вроде бы всё ясно, но ведь мы эту переменную (да и вообще ничего похожего) не объявляли!.. Однако, если взглянуть на реально объявленные переменные окружения внутри контейнера, обнаружится, что такая переменная действительно существует… как и некоторые другие, подобного ей формата: root@kafka-0:/# env |grep KAFKA_PORT
KAFKA_PORT_9092_TCP_PORT=9092 KAFKA_PORT_9092_TCP_PROTO=tcp KAFKA_PORT=tcp://192.168.252.192:9092 KAFKA_PORT_9092_TCP=tcp://192.168.252.192:9092 KAFKA_PORT_9092_TCP_ADDR=192.168.252.192 Всё дело в том, что kubelet при старте pod’а добавляет набор переменных, формируемых на базе создаваемых сервисов, — это один из способов найти адрес необходимого сервиса, что кратко описано в документации K8s. Один из самых очевидных способов закрыть вопрос — просто сменить имя сервиса в Kubernetes на что-то более явное (а главное — отличное от лаконичного kafka). Пробуем, запускаем: $ kubectl -n sentry logs sentry-kafka-0
===> ENV Variables ... ... SENTRY_KAFKA_PORT=tcp://192.168.224.150:9092 ... ===> Configuring ... ===> Running preflight checks ... ===> Check if /var/lib/kafka/data is writable ... Поздравляем! Вы великолепны! История №2. Как скромные 100 байт стали причиной больших проблем Мы любим ClickHouse и используем эту БД во многих проектах как основную базу для сбора статистики. А еще мы храним там логи K8s-кластеров, что очень удобно и эффективно — для этого даже создали специализированный инструмент loghouse. Так что нас не удивишь тем, что на практике могут периодически возникают аварии, связанные с ClickHouse, причем разной степени тяжести… но этот случай — особенный. У нас в обслуживании уже много лет есть кластер ClickHouse довольно внушительных размеров: 5 шардов, 10 железных узлов по 45 Тб данных на каждой. В кластере много replicated-таблиц, а обслуживает репликацию кластер ZooKeeper из 3 узлов на виртуальных машинах. Однажды ночью намертво завис один из гипервизоров, на которых запущены виртуалки ZooKeeper’а. Было принято решение перезагрузить гипервизор, после чего кластер ZK потерял целостность, перестал собираться и обслуживать ClickHouse. Первая мысль — «что-то побилось», поэтому возникла необходимость восстановить метаданные из снапшота. Сказано — сделано! Однако ZooKeeper при попытке собраться, выбрать лидера и разлить снапшот начинает испытывать проблемы: 2020-07-03 18:19:50,681 [myid:2] - WARN [QuorumPeer[myid=2](plain=[0:0:0:0:0:0:0:0]:2181)(secure=disabled):Follower@129] - Exception when following the leader
java.net.SocketTimeoutException: Read timed out .... Если пробуем запускать без снапшота и создавать таблички в ClickHouse, то ситуация не лучше: CREATE TABLE ctest.stor1 (`date` Date, `created` DateTime, `value` Int32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/stor1', '{replica}', date, value, 8192);
.... Received exception from server (version 19.14.3): Code: 999. DB::Exception: Received from 127.0.0.1:9000. Coordination::Exception. Coordination::Exception: All connection tries failed while connecting to ZooKeeper. Addresses: 192.168.110.60:2181, 192.168.110.61:2181, 192.168.110.62:2181 .... Code: 209, e.displayText() = DB::NetException: Timeout exceeded while reading from socket (192.168.110.62:2181): while receiving handshake from ZooKeeper (version 19.14.3.3 (official build)), 192.168.110.62:2181 (Connection loss). Любопытно: ведь мы не меняли никаких настроек… ни на серверах, ни в конфигурации самих CH и ZK. Но косвенно данные проблемы указывают на проблемы с сетью, к тому же, они явно завязаны на ZK. Не буду вдаваться в излишнюю детализацию всех наших действий, предпринятых для обнаружения источника проблем, — достаточно лишь сказать, что понервничали прилично. Итогом же этих исследований стал… самый обычный MTU! 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:e5:6d:fc brd ff:ff:ff:ff:ff:ff inet 192.168.110.60/24 brd 192.168.110.255 scope global ens3 valid_lft forever preferred_lft forever Всё дело в том, что сеть на виртуалках для ZK создавалась довольно давно — в 2018 году, сразу после выхода Ubuntu 18.04 и тогда был задействован еще не ставший по-настоящему привычным netplan. А он по умолчанию выставляет MTU в 1500 байт. В то же время, как многие знают, значение MTU по умолчанию в Hetzner vSwitch — 1400 байт. Итог: использование кластера до сих пор не требовало пакетов подобного размера, так что и проблема ни разу не всплывала, а сейчас разница в 100 байт оказалась фатальной. При уменьшении MTU кластер завёлся и вернулся к работе без каких-либо проблем (а мы продолжили устранять последствия). Бонус! При восстановлении метаданных в ZK мы пробовали (помимо прочего) вручную пересоздавать таблицы и обнаружили интересную особенность. Такой формат создания таблицы (когда ключ партиционирования и первичный ключ указываются прямо в секции ENGINE) начиная с 20 версии ClickHouse является устаревшим (deprecated): CREATE TABLE ctest.stor1foo (`date` Date, `created` DateTime, `value` Int32)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/stor1foo', '{replica}', date, value, 8192); В актуальных версиях ClickHouse правильным является такой вариант: CREATE TABLE ctest.stor1bar
( `date` Date, `created` DateTime, `value` Int32 ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/stor1bar', '{replica}') PARTITION BY date ORDER BY value SETTINGS index_granularity = 8192 Приглядитесь: по сути оба варианта делают совершенно идентичную работу и создают одинаковые таблицы с одинаковыми параметрами. Но давайте посмотрим, какие метаданные генерируют эти команды: [zk: localhost:2181(CONNECTED) 5] get /clickhouse/tables/1/stor1foo/metadata
metadata format version: 1 date column: date sampling expression: index granularity: 8192 mode: 0 sign column: primary key: value granularity bytes: 10485760 [zk: localhost:2181(CONNECTED) 6] get /clickhouse/tables/1/stor1bar/metadata metadata format version: 1 date column: sampling expression: index granularity: 8192 mode: 0 sign column: primary key: value data format version: 1 partition key: date granularity bytes: 10485760 На выходе есть различие в полях date column и partition key, хотя входные данные, на первый взгляд, одинаковы, что делает несовместимыми метаданные и таблицы в CH, созданные с разным форматированием запроса. История №3. Как был перегружен K8s-кластер… а на самом деле — перегрет Однажды от клиента поступило обращение о сбоях в работе одного из сервисов. При осмотре pod’ов этого сервиса выяснилось, что pod’ы периодически перезапускаются и толком не работают. Сервис был запущен на нескольких узлах K8s, нагрузка на CPU у которых была просто колоссальной. Рост нагрузки произошел на разных узлах не одновременно, но очень близко друг к другу по времени. Ситуация была столь плоха, что в какой-то момент kubelet попросту перестал отвечать. При этом на проблемных узлах потребление процессора ресурсами кластера было не очень велико: львиная доля приходилась на kidle_inj, т.е. ядро постоянно запускало idle-процесс. $ dmesg -T | grep intel_powercl
[Sat Jun 13 06:29:00 2020] intel_powerclamp: Start idle injection to reduce power [Sat Jun 13 06:29:02 2020] intel_powerclamp: Stop forced idle injection [Sat Jun 13 06:29:05 2020] intel_powerclamp: Start idle injection to reduce power [Sat Jun 13 06:29:07 2020] intel_powerclamp: Stop forced idle injection [Sat Jun 13 06:29:08 2020] intel_powerclamp: Start idle injection to reduce power Этот кластер построен на bare metal, т.е. железных узлах. Возник законный вопрос: что с их температурой? $ sensors
coretemp-isa-0000 Adapter: ISA adapter Package id 0: +92.0°C (high = +80.0°C, crit = +100.0°C) Core 0: +90.0°C (high = +80.0°C, crit = +100.0°C) Core 1: +91.0°C (high = +80.0°C, crit = +100.0°C) Core 2: +92.0°C (high = +80.0°C, crit = +100.0°C) Core 3: +91.0°C (high = +80.0°C, crit = +100.0°C) 90 градусов! Самое время написать в техподдержку дата-центра с запросом проверить машины, а в то же время искать любые возможности, куда переключить нагрузку. Ответом техподдержки стало сообщение о необходимости отключения машин на 30 минут для проведения работ. Чтобы согласиться на это, мы отключили (drain’ом) первый проблемный узел. А в результате проведённых операций получили такой ответ ДЦ: Dear Client,
unfortunately the CPU fan was faulty, we've replaced this and started the server again. Что и требовалось доказать! Дальнейшие шаги понятны: поочередный вывод узлов из кластера и координация наших действий с сотрудниками в ДЦ, меняющими кулеры. Ситуация с нагрузкой буквально на глазах приходит в норму. История №4. Ода pg_repack для PostgreSQL Обычно мы не оказываем услуги DBA. Да, мы можем посмотреть на вашу базу, создать пару индексов, оптимизировать запросы. Зачастую такие вещи происходят просто по стечению обстоятельств: заметили какую-то странность, пошли смотреть внимательно и что-то нашли (всегда можно что-то найти, если смотреть внимательно). Однажды наш дежурный заметил рост долгих транзакций на базе клиента. Точнее, его зафиксировала система мониторинга: плановая задача очистки неактуальных данных из базы стала занимать всё больше времени, из-за чего попала под триггер долгих транзакций. Несмотря на предпринятый простой шаг для решения первоначальной проблемы, о которой сообщил мониторинг, стало ясно, что требуется более внимательный анализ ситуации. Для этого мы с помощью модуля pgstattuple оценили фрагментированность таблиц запросами: SELECT schemaname,
tablename, dead_tuple_count(stats) AS dead_tuples, pg_size_pretty(dead_tuple_len(stats)) AS dead_space, pg_size_pretty(free_space(stats)) AS free_space, last_autovacuum, last_autoanalyze FROM (SELECT pgt.schemaname AS schemaname, pgt.tablename AS tablename, pgstattuple(pgt.schemaname || '.' || pgt.tablename) AS stats, uts.last_autovacuum AS last_autovacuum, uts.last_autoanalyze AS last_autoanalyze FROM pg_tables AS pgt LEFT JOIN pg_stat_user_tables AS uts ON pgt.schemaname = uts.schemaname AND pgt.tablename = uts.relname WHERE pgt.schemaname NOT IN ('repack','pg_catalog')) AS r ORDER BY dead_tuples DESC; … и фрагментированность индексов: SELECT schemaname,
indexname, tablename, dead_tuple_count(stats) AS dead_tuples, pg_size_pretty(dead_tuple_len(stats)) AS dead_space, pg_size_pretty(free_space(stats)) AS free_space FROM (SELECT pgt.schemaname AS schemaname, pgt.indexname AS indexname, pgt.tablename AS tablename, pgstattuple(pgt.schemaname || '.' || pgt.indexname) AS stats FROM pg_indexes AS pgt WHERE pgt.schemaname NOT IN ('repack', 'pg_catalog') AND pgt.indexname NOT IN ('some', 'important', 'triggers')) AS r ORDER BY dead_tuples DESC; В процессе этого исследования были выявлены фрагментированные таблицы и индексы в огромном количестве. Решение — выполнить pg_repack и параллельно зачистить ряд дублирующихся индексов. Операция repack’а на БД размером 620 Гб заняла целую ночь и утро. Результатом стал выигрыш около 150 Гб дискового пространства и сопутствующее «причесывание» базы в сочетании с небольшим ростом производительности. Ближайший план — миграция с PostgreSQL v11 на v12, так как в нём применены техники для снижения фрагментирования данных, а значит — проблема станет меньше нам мешать. Вывод банален: следите за базами, оптимизируйте с умом и не превращайте их в лапшу, наполненную бесполезными индексами. Заключение Даже привычные инструменты зачастую могут преподносить сюрпризы (как приятные, так и не очень), что, несомненно, делает нашу и без того насыщенную работу еще интереснее. А что подобного случалось у вас? У нас же историй еще много, так что до встречи в следующей части! P.S. Читайте также в нашем блоге:
=========== Источник: habr.com =========== Похожие новости:
Блог компании Флант ), #_kubernetes, #_servernoe_administrirovanie ( Серверное администрирование ), #_sistemnoe_administrirovanie ( Системное администрирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:54
Часовой пояс: UTC + 5