In his talk, Max Mazur a DevOps Engineer at Grid Dynamics, shares his experience deploying to production despite unexpected loads using the example of the web application (RTB). There you can find specific cases of using MySQL and resolving solutions. Technology stack: Linux, MySQL, PHP, Nginx, Kafka, Redis, Gearman
4. Содержание
• Коротко о проекте
• Deployment plan который мы придумали
• Особенности MySQL 5.7 (некоторые частные случаи)
• О проблемах (и решениях конечно же)
• Выводы которые я сделал для себя
7. Пару-тройку слов о проекте
• Есть web-приложение (оно как-то связано с рекламой). Умные люди называют это RTB
8. Пару-тройку слов о проекте
• Есть web-приложение (оно как-то связано с рекламой). Умные люди называют это RTB
• Я участвовал в deployment в production (в роли "пожарной команды")
9. Пару-тройку слов о проекте
• Есть web-приложение (оно как-то связано с рекламой). Умные люди называют это RTB
• Я участвовал в deployment в production (в роли "пожарной команды")
• И хочу рассказать о том как это происходило и с каким проблемами пришлось столкнуться
10. Пару-тройку слов о проекте
• Есть web-приложение (оно как-то связано с рекламой). Умные люди называют это RTB
• Я участвовал в deployment в production (в роли "пожарной команды")
• И хочу рассказать о том как это происходило и с каким проблемами пришлось столкнуться
• Хотя таких рассказов есть over 100500 надеюсь мой тоже будет полезным
11. Пару-тройку слов о проекте
• Есть web-приложение (оно как-то связано с рекламой). Умные люди называют это RTB
• Я участвовал в deployment в production (в роли "пожарной команды")
• И хочу рассказать о том как это происходило и с каким проблемами пришлось столкнуться
• Хотя таких рассказов есть over 100500 надеюсь мой тоже будет полезным
• Собрал все "грабли" какие только были J
15. Постановка задачи
• Сроки горят
• Нужно быстро подготовить окружение для production
• Починить что сломается
16. Постановка задачи
• Сроки горят
• Нужно быстро подготовить окружение для production
• Починить что сломается
• Что не сломаются – сломать, а потом тоже починить
37. А теперь шутки в стороны
1. Имеется три сервер All-in-One в разных регионах
38. А теперь шутки в стороны
1. Имеется три сервер All-in-One в разных регионах
2. На них уже пустили часть "боевого" трафика
39. А теперь шутки в стороны
1. Имеется три сервер All-in-One в разных регионах
2. На них уже пустили часть "боевого" трафика
3. Нужно выкатить мульти-серверную конфигурацию
40. А теперь шутки в стороны
1. Имеется три сервер All-in-One в разных регионах
2. На них уже пустили часть "боевого" трафика
3. Нужно выкатить мульти-серверную конфигурацию
4. Несколько регионов и несколько разных ролей. Что-то около 15 серверов
51. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
52. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
3. Модифицировать код (не сложно)
53. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
3. Модифицировать код (не сложно)
4. Остановить трафик (nginx: return 204)
54. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
3. Модифицировать код (не сложно)
4. Остановить трафик (nginx: return 204)
5. Включить binlog и GTID
55. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
3. Модифицировать код (не сложно)
4. Остановить трафик (nginx: return 204)
5. Включить binlog и GTID
6. mysqldump ... (с серверов 2 и 3 – часть таблиц)
56. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
3. Модифицировать код (не сложно)
4. Остановить трафик (nginx: return 204)
5. Включить binlog и GTID
6. mysqldump ... (с серверов 2 и 3 – часть таблиц)
7. Запустить трафик
57. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
3. Модифицировать код (не сложно)
4. Остановить трафик (nginx: return 204)
5. Включить binlog и GTID
6. mysqldump ... (с серверов 2 и 3 – часть таблиц)
7. Запустить трафик
8. Все запросы попадут в binlog и slave node сможет их проиграть
58. MySQL: Миграция базы
1. Совсем без downtime не вышло
2. ALTER TABLE ... ADD COLUMN `region` NOT NULL ...
3. Модифицировать код (не сложно)
4. Остановить трафик (nginx: return 204)
5. Включить binlog и GTID
6. mysqldump ... (с серверов 2 и 3 – часть таблиц)
7. Запустить трафик
8. Все запросы попадут в binlog и slave node сможет их проиграть
9. Главное что бы binlog не успел отротейтится
59. MySQL: GTID
gtid-mode = on
a61678ba488942799e5845ba840af334:1
Server UUID Transaction Number
62. Binlog Format
1. binlog_format = STATEMENT
UPDATE mytable SET x=123 WHERE id=1
UPDATE mytable SET time=NOW() WHERE id=1
UPDATE mytable SET field=uuid() WHERE id=1
63. Binlog Format
1. binlog_format = STATEMENT
UPDATE mytable SET x=123 WHERE id=1
UPDATE mytable SET time=NOW() WHERE id=1
UPDATE mytable SET field=uuid() WHERE id=1
2. binlog_format = ROW
Binary diff: {x:123}
64. Binlog Format
1. binlog_format = STATEMENT
UPDATE mytable SET x=123 WHERE id=1
UPDATE mytable SET time=NOW() WHERE id=1
UPDATE mytable SET field=uuid() WHERE id=1
2. binlog_format = ROW
Binary diff: {x:123}
binlog_row_image = minimal
minial – результат изменения
full – полная копия строки до и после
noblob - full но без BLOB J
65. MySQL – времена изменились
binlog_group_commit_sync_delay
log_slave_updates
slave-parallel-workers
slave-parallel-type = LOGICAL_CLOCK / DATABASE
binlog_do_db / binlog_do_table
replicate-do-db / replicate-do-table
68. MySQL: New Master
1. Запустить mysql: systemctl start mysql
2. Залить дамп mysql < dump_file.sql
69. MySQL: New Master
1. Запустить mysql: systemctl start mysql
2. Залить дамп mysql < dump_file.sql
3. CHANGE MASTER
MASTER_HOST='something',
MASTER_USER= ...
FOR CHANNEL="name_of_channel";
70. MySQL: New Master
1. Запустить mysql: systemctl start mysql
2. Залить дамп mysql < dump_file.sql
3. CHANGE MASTER
MASTER_HOST='something',
MASTER_USER= ...
FOR CHANNEL="name_of_channel";
4. FOR CHANNEL="master_in_region_2";
71. MySQL: New Master
1. Запустить mysql: systemctl start mysql
2. Залить дамп mysql < dump_file.sql
3. CHANGE MASTER
MASTER_HOST='something',
MASTER_USER= ...
FOR CHANNEL="name_of_channel";
4. FOR CHANNEL="master_in_region_2";
5. Примерно 30-40 минут на то что бы скачать данные измененные на "старых" серверах.
89. Спасибо мониторингу
• Zabbix
• И он даже был настроен
• И новые сервера добавлены (автоматически)
• Мониторились логи на предмет ошибок приложения
90. Спасибо мониторингу
• Zabbix
• И он даже был настроен
• И новые сервера добавлены (автоматически)
• Мониторились логи на предмет ошибок приложения
• O проблеме узнали из мониторинга, а не от клиента
92. PHP Fatal error
PHP Fatal error:
Uncaught exception
'PredisConnectionConnectionException'
with message
'Connection timed out [tcp://redis-host:6379]'
93. PHP Fatal error
• Никаких ошибок кроме этой
PHP Fatal error:
Uncaught exception
'PredisConnectionConnectionException'
with message
'Connection timed out [tcp://redis-host:6379]'
94. PHP Fatal error
• Никаких ошибок кроме этой
• Логи со стороны Redis девственно чисты
PHP Fatal error:
Uncaught exception
'PredisConnectionConnectionException'
with message
'Connection timed out [tcp://redis-host:6379]'
95. PHP Fatal error
• Никаких ошибок кроме этой
• Логи со стороны Redis девственно чисты
• В сети проблем не нашли
PHP Fatal error:
Uncaught exception
'PredisConnectionConnectionException'
with message
'Connection timed out [tcp://redis-host:6379]'
96. PHP Fatal error
• Никаких ошибок кроме этой
• Логи со стороны Redis девственно чисты
• В сети проблем не нашли
• Корреляция с нагрузкой? Сложно сказать.
PHP Fatal error:
Uncaught exception
'PredisConnectionConnectionException'
with message
'Connection timed out [tcp://redis-host:6379]'
98. Что делать? Ну как обычно
1. Покрутить крутилки в ядре, проверить лимиты
net.core.somaxconn
net.ipv4.tcp_tw_reuse
ulimit
99. Что делать? Ну как обычно
1. Покрутить крутилки в ядре, проверить лимиты
net.core.somaxconn
net.ipv4.tcp_tw_reuse
ulimit
2. Обновить Redis
100. Что делать? Ну как обычно
1. Покрутить крутилки в ядре, проверить лимиты
net.core.somaxconn
net.ipv4.tcp_tw_reuse
ulimit
2. Обновить Redis
3. Отключить flush на диск в Redis
101. Что делать? Ну как обычно
1. Покрутить крутилки в ядре, проверить лимиты
net.core.somaxconn
net.ipv4.tcp_tw_reuse
ulimit
2. Обновить Redis
3. Отключить flush на диск в Redis
4. Ничего не помогло
102. Что делать? Ну как обычно
1. Покрутить крутилки в ядре, проверить лимиты
net.core.somaxconn
net.ipv4.tcp_tw_reuse
ulimit
2. Обновить Redis
3. Отключить flush на диск в Redis
4. Ничего не помогло
5. На тестовом окружении не воспроизводится (я не смог воспроизвести)
103. Что делать? Ну как обычно
1. Покрутить крутилки в ядре, проверить лимиты
net.core.somaxconn
net.ipv4.tcp_tw_reuse
ulimit
2. Обновить Redis
3. Отключить flush на диск в Redis
4. Ничего не помогло
5. На тестовом окружении не воспроизводится (я не смог воспроизвести)
6. Ошибки продолжают сыпаться в лог
104. Что делать? Ну как обычно
1. Покрутить крутилки в ядре, проверить лимиты
net.core.somaxconn
net.ipv4.tcp_tw_reuse
ulimit
2. Обновить Redis
3. Отключить flush на диск в Redis
4. Ничего не помогло
5. На тестовом окружении не воспроизводится (я не смог воспроизвести)
6. Ошибки продолжают сыпаться в лог
7. Мой любимый вид проблем (но нет)
106. По колесам постучал, капот открыл-закрыл ...
• Каждый запрос открывает сессию к Redis
107. По колесам постучал, капот открыл-закрыл ...
• Каждый запрос открывает сессию к Redis
• 100500 новых сессий постоянно появляются и завершаются
108. По колесам постучал, капот открыл-закрыл ...
• Каждый запрос открывает сессию к Redis
• 100500 новых сессий постоянно появляются и завершаются
• Redis однопоточный
109. По колесам постучал, капот открыл-закрыл ...
• Каждый запрос открывает сессию к Redis
• 100500 новых сессий постоянно появляются и завершаются
• Redis однопоточный
• Ставим прокси à twemproxy
110. По колесам постучал, капот открыл-закрыл ...
• Каждый запрос открывает сессию к Redis
• 100500 новых сессий постоянно появляются и завершаются
• Redis однопоточный
• Ставим прокси à twemproxy
• [tcp://redis-host:6387] à [unix://var/run/twemproxy.sock]
111. По колесам постучал, капот открыл-закрыл ...
• Каждый запрос открывает сессию к Redis
• 100500 новых сессий постоянно появляются и завершаются
• Redis однопоточный
• Ставим прокси à twemproxy
• [tcp://redis-host:6387] à [unix://var/run/twemproxy.sock]
• twemproxy.yaml
listen: /var/run/twemproxy/redis.sock
servers:
- redis-host:6379:1
115. PHP FPM: Продолжение приключений
php-fpm.sock failed (11: Resource temporarily unavailable)
while connecting to upstream nginx error
116. PHP FPM: Продолжение приключений
php-fpm.sock failed (11: Resource temporarily unavailable)
while connecting to upstream nginx error
• pm = ondemand
Плохо, очень плохо
117. PHP FPM: Продолжение приключений
php-fpm.sock failed (11: Resource temporarily unavailable)
while connecting to upstream nginx error
• pm = ondemand
Плохо, очень плохо
• pm = dynamic
Ничуть не лучше
118. PHP FPM: Продолжение приключений
php-fpm.sock failed (11: Resource temporarily unavailable)
while connecting to upstream nginx error
• pm = ondemand
Плохо, очень плохо
• pm = dynamic
Ничуть не лучше
• pm = static
Путь силы
119. PHP FPM: Продолжение приключений
php-fpm.sock failed (11: Resource temporarily unavailable)
while connecting to upstream nginx error
• pm = ondemand
Плохо, очень плохо
• pm = dynamic
Ничуть не лучше
• pm = static
Путь силы
fork() – это "дорогая" операция.
Когда запросы приходят внезапно и их много
на fork() уже нет времени.
124. Kafka
• Используется для отложенных запросов к MySQL (сгладить нагрузку)
• Просто "труба"
• Да вы наверно и так все про Kafka знаете
125. Kafka
• Используется для отложенных запросов к MySQL (сгладить нагрузку)
• Просто "труба"
• Да вы наверно и так все про Kafka знаете
• У нас было несколько topic-ов (10 или около того)
126. Kafka
• Используется для отложенных запросов к MySQL (сгладить нагрузку)
• Просто "труба"
• Да вы наверно и так все про Kafka знаете
• У нас было несколько topic-ов (10 или около того)
• Мониторинг был (JMX)
127. Kafka
• Используется для отложенных запросов к MySQL (сгладить нагрузку)
• Просто "труба"
• Да вы наверно и так все про Kafka знаете
• У нас было несколько topic-ов (10 или около того)
• Мониторинг был (JMX)
Случайно заметили что данные в базе
отстают на сутки.
Пришлось разбираться почему.
128. Отстают данные это как?
mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2017-01-10 11:48:25 |
+---------------------+
1 row in set (0.00 sec)
mysql> SHOW FULL PROCESSLISTG
************************ 5. row ************************
Id: 1907564
<skipped>
Info: INSERT INTO ... WHERE TIMESTAMPT='2017-01-09 10:33:15'
133. Kafka
• Первым делом – завели lag по топикам на мониторинг
• Сколько consumer-ов?
134. Kafka
• Первым делом – завели lag по топикам на мониторинг
• Сколько consumer-ов?
• Оказалось что один на topic
135. Kafka
• Первым делом – завели lag по топикам на мониторинг
• Сколько consumer-ов?
• Оказалось что один на topic
• Непорядок – запустили 20. И "внезапно" увидели что topic-и все с одной partition
136. Kafka
• Первым делом – завели lag по топикам на мониторинг
• Сколько consumer-ов?
• Оказалось что один на topic
• Непорядок – запустили 20. И "внезапно" увидели что topic-и все с одной partition
• Тут коллеги из зала должны смеяться. И спросить на что рассчитывали.
137. Kafka
• Первым делом – завели lag по топикам на мониторинг
• Сколько consumer-ов?
• Оказалось что один на topic
• Непорядок – запустили 20. И "внезапно" увидели что topic-и все с одной partition
• Тут коллеги из зала должны смеяться. И спросить на что рассчитывали.
• kafka-topics.sh
--alter
--zookeeper zookeeper:2181
--partitions <много>
--topic test
138. Kafka (слайд с примерами для тех кто будет читать а не слушать)
$ git clone https://github.com/wurstmeister/kafka-docker.git
$ docker-compose -f docker-compose-single-broker.yml up
Внутри контейнера
$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --group 1
$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --group 1
$ kafka-topics.sh --alter --zookeeper zookeeper:2181 --partitions 4 --topic test
$ kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group 1
TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID
test 0 14 14 0 consumer-1-53 ... /127.0.0.1 consumer-1
test 1 7 7 0 consumer-1-6d ... /127.0.0.1 consumer-1
Читать тут: https://www.safaribooksonline.com/library/view/kafka-the-definitive/9781491936153/ch04.html
142. Kafka
• Вроде бы все нормально но lag продолжает увеличиваться
• Пришлось смотреть в код
143. Kafka
• Вроде бы все нормально но lag продолжает увеличиваться
• Пришлось смотреть в код
• Был расстроен увидев там set_time_limit(60)
144. Kafka
• Вроде бы все нормально но lag продолжает увеличиваться
• Пришлось смотреть в код.
• Был расстроен увидев там set_time_limit(60)
• Вместо работы – вечный ребаланс.
Иногда повторное вычитывание если скрипт
убивался до того как делал commit
145. Kafka
• Вроде бы все нормально но lag продолжает увеличиваться
• Пришлось смотреть в код.
• Был расстроен увидев там set_time_limit(60)
• Вместо работы – вечный ребаланс.
• Убрал set_time_limit(60)
146. Kafka
• Вроде бы все нормально но lag продолжает увеличиваться
• Пришлось смотреть в код.
• Был расстроен увидев там set_time_limit(60)
• Вместо работы – вечный ребаланс.
• Убрал set_time_limit(60)
Через полтора часа пришел OOM Killer и все мне рассказал.
"Собака была бешеной, пришлось пристрелить"
147. Kafka
• Вроде бы все нормально но lag продолжает увеличиваться
• Пришлось смотреть в код.
• Был расстроен увидев там set_time_limit(60)
• Вместо работы – вечный ребаланс.
• Убрал set_time_limit(60)
• Утечку искал не я но в конце концов встроили ограничитель на число обработанных
сообщений
151. Kafka
• Тут должно было стать все хорошо
• Но не стало – lag продолжает увеличиваться (хотя и медленнее чем раньше)
152. Kafka
• Тут должно было стать все хорошо
• Но не стало – lag продолжает увеличиваться (хотя и медленнее чем раньше)
• Но все таки не рассасываться
153. Kafka
• Тут должно было стать все хорошо
• Но не стало – lag продолжает увеличиваться (хотя и медленнее чем раньше)
• Но все таки не рассасываться
• Кто виноват и что делать?
154. Kafka
• Тут должно было стать все хорошо
• Но не стало – lag продолжает увеличиваться (хотя и медленнее чем раньше)
• Но все таки не рассасываться
• Кто виноват и что делать?
• Объявили виноватой Kafka (мол не дает быстро вычитать данные)
155. Kafka
• Тут должно было стать все хорошо
• Но не стало – lag продолжает увеличиваться (хотя и медленнее чем раньше)
• Но все таки не рассасываться
• Кто виноват и что делать?
• Объявили виноватой Kafka (мол не дает быстро вычитать данные)
• Пришлось написать тестовый consumer на Java - вычитывает мгновенно
156.
157. Kafka
• Тут должно было стать все хорошо
• Но не стало – lag продолжает увеличиваться (хотя и медленнее чем раньше)
• Но все таки не рассасываться
• Кто виноват и что делать?
• Объявили виноватой Kafka (мол не дает быстро вычитать данные)
• Пришлось написать тестовый consumer на Java - вычитывает мгновенно
• Начали профайлить код consumer на PHP и смотреть куда уходит время
158. Kafka
• Тут должно было стать все хорошо
• Но не стало – lag продолжает увеличиваться (хотя и медленнее чем раньше)
• Но все таки не рассасываться
• Кто виноват и что делать?
• Объявили виноватой Kafka (мол не дает быстро вычитать данные)
• Пришлось написать тестовый consumer на Java - вычитывает мгновенно
• Начали профайлить код consumer на PHP и смотреть куда уходит время
• Да ТЕПЕРЬ я понимаю что с этого стоило б начать
161. Kafka
• Тут же выяснили что уперлись в MySQL
• Тут же выяснили то Disk Utilization у нас ВНЕЗАПНО не мониторилась
162. Kafka
• Тут же выяснили что уперлись в MySQL
• Тут же выяснили то Disk Utilization у нас ВНЕЗАПНО не мониторилась
• Опечатка: никто не застрахован
163. Kafka
• Тут же выяснили что уперлись в MySQL
• Тут же выяснили то Disk Utilization у нас ВНЕЗАПНО не мониторилась
• Опечатка: никто не застрахован
• После того как расширили диск IOPSов стало хватать lag начал уменьшаться
164. Kafka
• Тут же выяснили что уперлись в MySQL
• Тут же выяснили то Disk Utilization у нас ВНЕЗАПНО не мониторилась
• Опечатка: никто не застрахован
• После того как расширили диск IOPSов стало хватать lag начал уменьшаться
• А потом опять расти!
165. Kafka
• Тут же выяснили что уперлись в MySQL
• Тут же выяснили то Disk Utilization у нас ВНЕЗАПНО не мониторилась
• Опечатка: никто не застрахован
• После того как расширили диск IOPSов стало хватать lag начал уменьшаться
• А потом опять расти!
• CPU Credits на Kafka закончились – t2.<some instance size>
167. Kafka
• Тут же выяснили что уперлись в MySQL
• Тут же выяснили то Disk Utilization у нас ВНЕЗАПНО не мониторилась
• Опечатка: никто не застрахован
• После того как расширили диск IOPSов стало хватать lag начал уменьшаться
• А потом опять расти!
• CPU Credits на Kafka закончились – t2.<some instance size>
• Пришлось увеличить instance size
168. Все хорошо
• Все хорошо
• Все очень хорошо – нагрузку держим
• Моя работа закончена
171. Выводы
1. Планируйте нагрузку.
Нагрузочное тестирование позволило б предсказать большую часть проблем. К
сожалению это не всегда просто, особенно когда есть зависимости на внешние сервисы.
Иногда это банально стоит денег.
172. Выводы
1. Планируйте нагрузку.
Нагрузочное тестирование позволило б предсказать большую часть проблем. К
сожалению это не всегда просто, особенно когда есть зависимости на внешние сервисы.
Иногда это банально стоит денег.
2. Мониторинг это важно.
Проверьте мониторинг.
Те ли метрики Вы собираете?
И все ли нужные метрики?
Не менее важно хранить исторические данные что б видеть куда движемся и
предсказать проблемы.
173. Выводы
1. Планируйте нагрузку.
Нагрузочное тестирование позволило б предсказать большую часть проблем. К
сожалению это не всегда просто, особенно когда есть зависимости на внешние сервисы.
Иногда это банально стоит денег.
2. Мониторинг это важно.
Проверьте мониторинг.
Те ли метрики Вы собираете?
И все ли нужные метрики?
Не менее важно хранить исторические данные что б видеть куда движемся и
предсказать проблемы.
3. Архитектура?
174. Выводы
1. Планируйте нагрузку.
Нагрузочное тестирование позволило б предсказать большую часть проблем. К
сожалению это не всегда просто, особенно когда есть зависимости на внешние сервисы.
Иногда это банально стоит денег.
2. Мониторинг это важно.
Проверьте мониторинг.
Те ли метрики Вы собираете?
И все ли нужные метрики?
Не менее важно хранить исторические данные что б видеть куда движемся и
предсказать проблемы.
3. Архитектура?
175. Выводы
DevOps это не только CI/CD, но и взаимодействие между командами.
Программист: Наш код работает.
Админ: Сервер настроен правильно.
Крайнего не найти.