Цели уровня обслуживания — опыт Google (перевод главы книги Google SRE)

image

SRE (Site Reliability Engineering) — подход к обеспечению доступности веб-проектов. Считается фреймворком для DevOps и говорит как добиться успеха в применение DevOps-практик. В этой статье перевод Главы 4 Service Level Objectives книги Site Reliability Engineering от Google. Этот перевод я готовил самостоятельно и полагался на собственный опыт понимания процессов мониторинга. В телеграм-канале monitorim_it и прошлом посте на Хабре я публиковал также перевод 6 главы этой же книги о целях уровня обслуживания.

Перевод по катом. Приятного чтения!

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

Мы используем свои интуицию, опыт и осознаём желание пользователей иметь представление об индикаторах уровня обслуживания (SLI), целях уровня обслуживания (SLO) и соглашении об уровне обслуживания (SLA). Эти измерения описывают основные метрики, которые мы хотим контролировать и на которые будем реагировать, если не сможем предоставить ожидаемое качество сервиса. В конечном счете, выбор подходящих показателей помогает управлять правильными действиями, если что-то пойдет не так, а также дает уверенность команде SRE в здоровье сервиса.

В этой главе описывается подход, который мы используем для борьбы с проблемами метрического моделирования, метрического отбора и метрического анализа. Большая часть объяснений будет без примеров, поэтому мы будем использовать сервис Шекспир, описанный в примере его реализации (поиск по произведениям Шекспира), чтобы проиллюстрировать основные моменты.

Терминология уровня обслуживания

Многие читатели, вероятно, знакомы с концепцией SLA, но термины SLI и SLO заслуживают тщательного определения, поскольку в общем случае термин SLA перегружен и имеет ряд значений в зависимости от контекста. Для ясности, мы хотим отделить эти значения.

Индикаторы

SLI является индикатором уровня обслуживания — тщательно определенной количественной мерой одного из аспектов предоставляемого уровня обслуживания.

Для большинства сервисов в качестве ключевого SLI считают задержку запроса — сколько времени требуется для возврата ответа на запрос. Другие распространенные SLI включают частоту ошибок, часто выражаемую как долю всех полученных запросов, и пропускную способность системы, обычно измеряемую в запросах в секунду. Измерения часто агрегируются: сырые данные сначала собираются, а затем преобразуются в скорость изменения, среднее значение или процентиль.

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

Другим видом SLI, важным для SRE, является доступность или часть времени, в течение которого сервисом можно пользоваться. Часто определяется как доля успешных запросов, иногда называемых выработкой. (Продолжительность срока службы — вероятность того, что данные будут храниться в течение длительного периода времени, ещё важна и для систем хранения данных.) Хотя доступность на 100% невозможна, доступность близкая к 100% часто достижима, значения доступности выражаются в виде количества «девяток» процента доступности. Например, доступность 99% и 99,999% может быть обозначена как «2 девятки» и «5 девяток». Текущая заявленная цель доступности Google Compute Engine — «три с половиной девятки» или 99,95%.

Цели

SLO — это цель уровня обслуживания: целевое значение или диапазон значений для уровня обслуживания, который измеряется SLI. Нормальным значением для SLO является «SLI ≤ целевого значения» или «нижняя граница ≤ SLI ≤ верхняя граница». Например, мы можем решить, что мы вернем результаты поиска по произведениям Шекспира «быстро», приняв в виде SLO значение средней задержки запроса поиска меньшее 100 миллисекунд.

Выбор подходящего SLO — сложный процесс. Во-первых, вы не всегда можете выбрать конкретное значение. Для внешних входящих HTTP-запросов к вашему сервису метрика количества запросов в секунду (QPS) в основном определяется желанием ваших пользователей посетить ваш сервис, и вы не можете установить SLO для этого.

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

Опять же, это более неоднозначно, чем может показаться на первый взгляд: совсем выбрасывать QPS из расчёта не стоит. Дело в том, что связка QPS и задержка сильно связаны друг с другом: более высокий QPS часто приводит к большим задержкам, и обычно сервисы испытывают резкое снижение производительности при достижении определенного порога нагрузки.

Выбор и публикация SLO задаёт пользовательские ожидания о том, как будет работать сервис. Эта стратегия может уменьшить необоснованные жалобы на владельца сервиса, например, на его медленную работу. Без явного SLO пользователи часто создают свои собственные ожидания относительно желаемой производительности, которые могут быть никак не связаны с мнением людей, проектирующих и управляющих сервисом. Такой расклад может привести к завышенным ожиданиям от сервиса, когда пользователи ошибочно полагают, что сервис будет более доступным, чем он есть на самом деле и вызывать недоверие, когда пользователи считают, что система менее надежная, чем она есть на самом деле.

Соглашения

Cоглашение об уровне обслуживания — это явный или неявный контракт с вашими пользователями, который включает в себя последствия наступления (или отсутствия) SLO, которые в них содержатся. Последствия наиболее легко распознаются, когда они являются финансовыми — скидка или штраф, — но они могут принимать другие формы. Легкий способ рассказать о разнице между SLO и SLA заключается в том, чтобы спросить «что произойдет, если SLO не будут выполнены?». Если нет явных последствий, вы почти наверняка смотрите на SLO.

SRE обычно не участвует в создании SLA, поскольку SLA тесно связаны с решениями для бизнеса и продуктов. SRE, однако, участвует в оказании помощи при предотвращении последствий неудачных SLO. Они также могут помочь определить SLI: очевидно, должен быть объективный способ измерения SLO в соглашении или возникнут разногласия.

Google Search — пример важного сервиса, который не имеет SLA для общественности: мы хотим, чтобы все пользовались Поиском как можно более эффективно, но мы не подписали контракт со всем миром. Тем не менее, все еще есть последствия, если поиск недоступен — недоступность приводит к падению нашей репутации, а также к снижению доходов от рекламы. У многих других сервисов Google, таких как Google for Work, есть явные соглашения об уровне обслуживания с пользователями. Независимо от того, имеет ли конкретная услуга SLA, важно определить SLI и SLO и использовать их для управления службой.

Так много теории — теперь к опыту.

Индикаторы на практике

Учитывая, что мы сделали вывод, что важно выбрать подходящие показатели для измерения уровня обслуживания, как вы теперь узнаете, какие показатели имеют значение для сервиса или системы?

О чем вы и ваши пользователи заботитесь

Не нужно использовать каждый показатель в качестве SLI, который вы можете отслеживать в системе мониторинга; понимание того, что пользователи хотят от системы, поможет выбрать несколько показателей. Выбор слишком большого количества индикаторов затрудняет должный уровень внимания к важным показателям, в то время как выбор небольшого количества может оставить значительные куски вашей системы без внимания. Обычно мы используем несколько ключевых индикаторов для оценки и понимания состояния системы.

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

  • Пользовательские фронтэнд-системы, такие как интерфейсы поиска сервиса Шекспир из нашего примера. Они должны быть доступны, не иметь задержек и обладать достаточной пропускной способностью. Соответственно, можно задать вопросы: можем ли мы ответить на запрос? Сколько времени потребовалось, чтобы ответить на запрос? Сколько запросов может быть обработано?
  • Системы хранения. Для них важна низкая задержка ответа, доступность и долговечность. Соответствующие вопросы: сколько времени требуется для чтения или записи данных? Можем ли мы получить доступ к данным по запросу? Доступны ли данные, когда они нам нужны? См. главу 26 Целостность данных: то, что вы читаете, это то, что вы записали, для детального разбора этих вопросов.
  • Системы с большими данными, такие как конвейеры обработки данных, зависят от пропускной способности и задержки обработки запроса. Соответствующие вопросы: сколько данных обрабатывается? Сколько времени требуется, чтобы данные продвигались от приёма запроса до выдачи ответа? (Некоторые части системы могут также иметь задержки на отдельных этапах.)

Сбор индикаторов

Многие индикаторы уровня сервиса наиболее естественно собирать на стороне сервера, используя систему мониторинга, такую как Borgmon (см. Главу 10 Практика оповещений по данным временных рядов) или Prometheus, или просто периодически анализируя логи, выявляя HTTP ответы со статусом 500. Тем не менее, некоторые системы должны быть снабжены сбором метрик на стороне клиента, так как отсутствие мониторинга на стороне клиента может привести к пропуску ряда проблем, которые затрагивают пользователей, но не влияют на показатели на стороне сервера. Например, сосредоточенность на задержке ответа бэкэнда нашего тестового приложения с поиском по произведениям Шекспира может привести к задержке обработки запросов на стороне пользователя из-за проблем с JavaScript: в этом случае измерение того, сколько времени займет обработка страницы в браузере, является лучшим показателем.

Агрегация

Для простоты и удобства использования мы часто агрегируем сырые измерения. Это необходимо делать осторожно.

Некоторые показатели кажутся простыми, например, количество запросов в секунду, но даже это, очевидное, прямое измерение неявно агрегирует данные по времени. Является ли измерение полученным конкретно один раз в секунду или это измерение усреднено по количеству запросов в течение минуты? Последний вариант может скрыть гораздо более высокое мгновенное количество запросов, которые сохраняются всего на несколько секунд. Рассмотрим систему, которая обслуживает 200 запросов в секунду с четными номерами и 0 в остальное время. Константа в виде среднего значения в 100 запросов в секунду и вдвое большая мгновенная нагрузка — совсем не одно и то же. Точно так же усреднение задержек запросов может показаться привлекательным, но скрывает важную деталь: вполне возможно, что большинство запросов будут быстрыми, но среди них окажется много запросов, которые будут медленными.

Большинство показателей лучше рассматривать как распределения, а не средние. Например, для SLI задержки некоторые запросы будут обрабатываться быстро, в то время как некоторые всегда будут занимать больше времени, иногда намного больше. Простое среднее может скрывать эти длительные задержки. На рисунке приведен пример: хотя типичный запрос обслуживается примерно 50 мс, 5% запросов в 20 раз медленнее! Мониторинг и оповещения, основанные только на средней задержке, не показывают изменений в поведении в течение дня, когда на самом деле существуют заметные изменения в длительности обработки некоторых запросов (самая верхняя строка).

image
50, 85, 95, и 99 процентили задержки системы. Ось Y приведена в логарифмическом формате.

Использование процентилей для индикаторов позволяет увидеть форму распределения и его характеристики: высокий уровень процентиля, такой как 99 или 99,9, показывает наихудшее значение, а на 50 процентиле (также известном как медиана) можно увидеть наиболее частое состояние метрики. Чем больше дисперсия времени отклика, тем сильнее длительные запросы влияют на пользовательский опыт. Эффект усиливается при высокой нагрузке при наличии очередей. Исследования пользовательского опыта показали, что люди обычно предпочитают более медленную систему с высокой дисперсией времени отклика, поэтому некоторые команды SRE сосредотачиваются только на высоких процентильных значениях, исходя из того, что если поведение метрики на 99,9 процентиле является хорошим — большинство пользователей не будут испытывать проблем.

Примечание по статистическим ошибкам

Обычно мы предпочитаем работать с процентилями, а не со средним (средним арифметическим) набором значений. Это позволяет рассматривать более дисперсные значения, которые часто имеют существенно отличающиеся (и более интересные) характеристики, чем среднее. Из-за искусственного характера вычислительных систем значения метрик часто искажаются, например, ни один запрос не может получить ответ менее чем за 0 мс, а тайм-аут в 1000 мс означает, что успешных ответов со значениями, превышающими таймаут, не может быть. В результате мы не можем принять, что среднее и медианы могут быть одинаковы или близки друг к другу!

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

Стандартизировать индикаторы

Мы рекомендуем стандартизировать общие характеристики для SLI, чтобы не приходилось рассуждать о них каждый раз. Любая функция, которая удовлетворяет стандартным шаблонам, может быть исключена из спецификации отдельного SLI, например:

  • Интервалы агрегирования: «усредненный за 1 минуту»
  • Области агрегирования: «Все задачи в кластере»
  • Как часто проводятся измерения: «Каждые 10 секунд»
  • Какие запросы включены: «HTTP GET из заданий мониторинга черного ящика»
  • Как данные получены: «Благодаря нашему мониторингу, измеренному на сервере»,
  • Задержка доступа к данным: «Время до последнего байта»

Чтобы сэкономить усилия, создайте набор многоразовых шаблонов SLI для каждой общей метрики; они также упрощают для всех понимание того, что означает определенный SLI.

Цели на практике

Начните с размышлений (или узнайте!), о чем заботятся ваши пользователи, а не о том, что вы можете измерить. Часто то, что беспокоит ваших пользователей, трудно или невозможно измерить, так что в итоге вы приблизитесь к их потребностям. Однако, если вы просто начнете с того, что легко измерить, вы получите менее полезные SLO. В результате мы иногда обнаруживали, что первоначальное определение желаемых целей и дальнейшая работа с конкретными индикаторами работает лучше, чем выбор индикаторов, а затем достижение целей.

Определите цели

Для максимальной ясности, должно быть определено как измеряются SLO и условия, при которых они действительны. Например, мы можем сказать следующее (вторая строка такая же, как первая, но использует SLI-значения по умолчанию):

  • 99% (усредненные в течение 1 минуты) вызовов Get RPC будут завершены менее чем за 100 мс (измеряется на всех серверах backend).
  • 99% вызовов Get RPC будут завершены менее чем за 100 мс.

Если форма кривых производительности важна, вы можете указать несколько целей SLO:

  • 90% вызовов Get RPC выполнились менее чем за 1 мс.
  • 99% вызовов Get RPC выполнились менее чем за 10 мс.
  • 99.9% вызовов Get RPC выполнились менее чем за 100 мс.

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

  • 95% запросам клиентов важна пропускная способность. Установите подсчёт RPC-вызовов выполнявшихся <1 с.
  • 99% клиентам важна величина задержки. Установите подсчёт RPC-вызовов с трафиком <1 кБ и выполнявшихся <10 мс.

Нереалистично и нежелательно настаивать на том, что SLO будут выполняться в 100% случаев: это может снизить темпы ввода нового функционала и деплоймента, потребовать дорогостоящих решений. Вместо этого лучше разрешить бюджет ошибок — долю разрешённого простоя системы и отслеживать это значение ежедневно или еженедельно. Возможно, высшее руководство будет хотеть ежемесячную или ежеквартальную оценку. (Бюджет ошибок — это просто SLO для сравнения с другим SLO).

Долю нарушения SLO можно сравнить с бюджетом ошибок (см. Главу 3 и раздел «Мотивация для бюджетов ошибок»), при этом значение разницы используется в качестве входа в процесс, который решает, когда разворачивать новые релизы.

Выбор плановых значений

Выбор плановых значений (SLO) не является чисто технической деятельностью из-за интересов продукта и бизнеса, которые должны быть отражены в выбранных SLI, SLO (и, возможно, SLA). Аналогичным образом, может потребоваться обмен информацией по поводу вопросов, связанных с укомплектованием штата персонала, временем выхода на рынок, наличием оборудования и финансированием. SRE должен быть частью этого разговора и помогать разобраться с рисками и жизнеспособностью различных вариантов. Мы прикинули несколько вопросов, которые могут помочь обеспечить более продуктивное обсуждение:

Не выбирайте цель, основанную на текущей производительности
Хотя понимание достоинств и пределов системы имеет важное значение, адаптация показателей без рассуждений может блокировать вас в поддержке системы: потребуются героические усилия для достижения целей, которые не может быть достигнуты без существенной реорганизации.

Будьте проще
Сложные расчёты SLI могут скрыть изменения в производительности системы и усложнят поиск причины проблемы.

Избегайте абсолюта
Хотя возникает соблазн иметь систему, которая может принимать неограниченно растущую нагрузку без увеличения значения задержки — это требование нереально. Система, которая подходит к таким идеалам, вероятно, заставить потратить много времени на проектирование и создание, обойдётся дорого в эксплуатации окажется слишком хороша для ожиданий пользователей, которым хватило бы и меньшего.

Используйте как можно меньшее количество SLO
Выберите достаточное количество SLO, чтобы обеспечить хорошее покрытие атрибутов системы. Защитите SLO, которые вы выбрали: если вы никогда не сможете выиграть спор о приоритетах, указав конкретную SLO, вероятно, не стоит рассматривать эту SLO. Однако, не все атрибуты системы поддаются SLO: трудно подсчитать уровень пользовательского восторга при помощи SLO.

Не гонитесь за совершенством
Вы всегда можете уточнить определения и цели SLO с течением времени, когда узнаете больше о поведении системы под нагрузкой. Лучше начинать с плавающей цели, которую вы со временем уточните, чем выбирать слишком строгую цель, которая должна быть ослаблена, когда вы обнаружите, что это недостижимо.

SLO могут и должны стать основным драйвером в определении приоритетов работы для SRE и разработчиков продуктов, поскольку они отражают заботу о пользователях. Хорошее SLO — это полезный инструмент принуждения команды разработчиков. Но плохо продуманная SLO может привести к расточительной работе, если команда прилагает героические усилия для достижения чрезмерно агрессивной SLO или плохому продукту, если SLO слишком низкое. SLO — это мощный рычаг, используйте его с умом.

Контролируйте измерения

SLI и SLO являются ключевыми элементами, используемыми для управления системами:

  • Мониторьте и измеряйте SLI системы.
  • Сравните SLI со SLO и решите нужны ли действия.
  • Если требуется действие, выясните, что должно произойти, чтобы достичь цели.
  • Выполните это действие.

Например, если шаг 2 показывает, что время ожидания запроса увеличивается, и нарушит SLO через несколько часов, если ничего не будет сделано, шаг 3 может включать в себя тестирование гипотезы о том, что нагрузка на серверы привязана к процессорам и добавление новых серверов распределит нагрузку. Без SLO вы не знали бы нужно ли (или когда) принимать меры.

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

Чтобы установить реалистичные ожидания для ваших пользователей, используйте одну или обе следующие тактики:

  • Сохраняйте запас прочности. Используйте более жесткую внутреннюю SLO, чем объявленная пользователям. Это даст вам возможность реагировать на проблемы, прежде чем они станут видимыми извне. Буфер SLO также позволяет иметь запас прочности при установке релизов, которые влияют на производительность системы и обеспечить простоту обслуживания системы без необходимости разочаровывать пользователей даунтаймом.
  • Не перевыполняйте ожиданий пользователей. Пользователи основываются на том, что вы предлагаете, а не на ваших словах. Если фактическая производительность вашего сервиса намного лучше, чем заявленная SLO, пользователи будут полагаться на текущую производительность. Вы можете избежать чрезмерной зависимости, преднамеренно отключая систему или ограничивая производительность при небольших нагрузках.

Понимание того, насколько хорошо система отвечает ожиданиям, помогает решить, следует ли инвестировать ускорение системы и делать её доступнее и более устойчивой. В качестве альтернативы, если сервис слишком хорошо работает, некоторое время работы персонала должно быть потрачено на другие приоритеты, такие как погашение технического долга, добавление новых функций или ввод в эксплуатацию новых продуктов.

Соглашения на практике

Создание SLA требует, чтобы бизнес- и юридические команды определяли последствия и штрафы за его нарушение. Роль SRE заключается в том, чтобы помочь им понять вероятные трудности с выполнением SLO, содержащимися в SLA. Большая часть рекомендаций по созданию SLO также применима для SLA. Целесообразно быть консервативным в том, что вы обещаете пользователям, поскольку чем их больше, тем сложнее изменить или удалить SLA, которые кажутся неразумными или трудными для выполнения.

Спасибо, что прочитали перевод до конца. Подписывайтесь на мой телеграм-канал о мониторинге monitorim_it и блог на Медиуме.

ссылка на оригинал статьи https://habr.com/ru/post/484578/

Нет клещам! Растения против переносчиков болезни Лайма

Сегодня я написал в своем канале заметку про отпугивание клещей растительными заграждениями. А потом подумал, и решил продублировать на хабр. Пусть она и небольшая по размеру, и не совсем подходит под характерные для меня лонгриды. Но ведь не у всех есть телеграм. И именно сейчас лучшее время для поиска и закупки семян различных растительных репеллентов и «клещегонов». Поэтому сегодня под катом — про биобарьеры против боррелиозного клеща. Из каких растений их лучше сделать!


Когда в очередной раз на даче пришлось извлекать из жены, брата и тещи порцию клещей, я наконец-то задумался, что пора бы создать какой-то заслон от этих гостей из леса…Мне раньше по долгу службы мне приходится консультировать людей по созданию биобарьеров (в частности против комаров). Но на мой взгляд для всех владельцев дач гораздо более опасным явлением является клещ. Тот самый, который внутри переносит боррелиоз (болезнь Лайма).

Уверен, многие отмахнуться, дескать мы уже все обработали и закупились акарицидными средствами (чаще всего на основе одного и того же компонента — перметрина). Но клещи реагируют на изменение ситуации гораздо оперативнее людей и приобретают иммунитет. Так что “надежды юношей питают”, не уверен, что к болезни Лайма применимо понятие плацебо 🙂

Что же делать? А вот планировать и реализовывать на дачах/загородных домах биобарьеры (или точнее фитобарьеры). Это обычные посевы растений, обладающих отпугивающим эффектом против Ixodes ricinus (собачий клещ), Ixodes persulcatus (таежный клещ) — распространенных в Европе видов клещей, переносящих возбудителей бореллиоза.

Весной посадил (по согласованию с женой естественно) вдоль забора рядок правильных растений — и весь летне-осенний сезон спи спокойно, не опасаясь что из ближайшего леса придет клещ (или собака на себе принесет). Кстати, задумался над темой фитоинсектицидов я с подачи Cenzo, в обсуждениях "озоновой статьи".

Что же нужно сажать и сеять ?

Чаще всего подходящими акарицидными и репелентными свойствами обладают растения семейств Астровые, Яснотковые, Пасленовые, Вербеновые. Из официально признанных индивидуальных соединений-репеллентов, выделенных из растений этих семейств, следует упомянуть соединение 2-ундеканон. Соединение это присутствует в таких растениях как дикорастущий томат, садовая клубника, или декоративная хауттюйния сердецвидная.

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

Что касается растительных генераторов репеллентов, которые можно вырастить на приусадебном участке, то наиболее выраженным эффектом по отпугиванию клещей обладают эфирные масла лимонной травы и ландыша.

Притом их репеллентная активность сравнима с эталонным химическим репеллентом N,N-диэтил-м-толуамидом. Основным клещегонным компонентом в эфирных маслах этих растений являются цитронеллол, гераниол и эвгенол, фенетиловый спирт.

Высокой активностью против черноногого клеща обладают эфирные масла такого распространенного декоративного растения как котовник, или кошачья мята.

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

Ну и если не привязываться к царству растений, можно упомянуть единственный одобренный EPA гриб-биоинсектицид — Metarhizium brunneum (или Metarhizium anisopliae). Грибок этот встречается в почвах по всему миру, а также доступен в виде лиофилизированной биомассы. Но наши “лесники” по старинке обочины травят непонятно чем, скорее всего отпугивателем толуамидом или перметрином. Вместо того, чтобы засеять один раз обочины правильными растениями. Поэтому, видимо, и статистика соответствующая…

В общем, надеюсь информация будет полезна и, главное, подтолкнет хотя бы кого-то к активным действиям. Готовь сани летом, а телегу зимой — гласит народная мудрость. Все-таки зимние месяцы — это оптимальное время для планирования того, что будет расти на приусадебном участке. Если недалеко лес — не экономьте на себе, выделите небольшой участок под биобарьер, не стоит надеяться что “если вдруг клещ-доксициклин поможет”.

На сим все, заметка краткая, но имхо информативная. Традиционно советую не ждать статьи на хабре, а подписаться на авторский телеграм-канал и читать все в InstantView 🙂

Сергей Бесараб (Siarhei Besarab)

ссылка на оригинал статьи https://habr.com/ru/post/484574/

ASP.NET MVC — Entity Framework, MySQL и использование Dependency Resolver для выбора репозитория

Legacy технологии

Предупреждение: ASP.NET MVC уже устарел. Рекомендуется использовать ASP.NET Core. Но если вам интересно, то читайте.

Решил немного расширить предыдущую статью про ASP.NET MVC и MySQL. В ней речь шла про работу с MySQL в ASP.NET MVC не через практически стандартный ORM Entity Framework (EF), а с помощью прямого доступа к СУБД через ADO.NET. И была приведена реализация этого метода доступа. И хотя метод устаревший и не рекомендуемый к использованию, но иногда полезен: например, в высоконагруженных приложениях или когда разработчик сталкивается с ситуацией, когда ORM не может сгенерировать корректно работающий SQL-запрос. И иногда можно совмещать в приложении оба способа — и через ORM и через ADO.NET. В итоге я подумал, и решил дописать приложение: добавив в него реализацию репозитория для Entity Framework и сделать выбор из них зависимым от параметра приложения с помощью Dependency Resolver.

Весь код можно взять вот по этому адресу, ниже этот код будет частично презентован с небольшими ссылками и пояснениями по отношению к предыдущему проекту. А здесь можно посмотреть на работу приложения.

Изменяем проект

1. Для использования Entity Framework с MySQL мы должны установить библиотеку MySQL.Data.EntityFramework (можно, конечно, и другую, просто эта от Oracle — владельца MySQL).


Она потянет за собой MySQL.Data и собственно EntityFramework. В файл web.config внесены изменения:

<entityFramework>   <providers>       <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.EntityFramework, Version=8.0.19.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />   </providers> </entityFramework> 

С MySQL.Data возникла интересная коллизия — поскольку MySQL.Data.EntityFramework потребовал версии MySQL.Data не ниже 8.0.19, то он обновился… и проект перестал работать. Стала возникать ошибка:

Не удалось загрузить файл или сборку «Ubiety.Dns.Core» либо одну из их зависимостей. Невозможно проверить подпись строгого имени. Возможно, сборка была изменена или построена с отложенной подписью, но не полностью подписана правильным закрытым ключом. (Исключение из HRESULT: 0x80131045)

Видимо в версию MySQL.Data 8.0.19 была добавлена не подписанная сборка Ubiety.Dns.Core. Пришлось в проект ещё и включить этот компонент через Nuget. Ошибка пропала.

2. Кроме этого для реализации внедрения зависимостей добавим в проект Ninject — контейнер внедрения зависимостей (DI).

3. Немного изменим структуру проекта: файлы репозитория вынесем в отдельный каталог Repository и создадим в нём еще подкаталоги ADO.NET (перенесём туда имеющиеся файлы LanguagesRepository.cs и UsersRepository.cs) и EF (тут будут файлы репозитория для Entity Framework).

4. Кроме этого в файл web.config в раздел appConfig добавлен параметр приложения: <add key="ConnectionMethod" value="ADO.NET" />. Приложение будет принимать два значения: «Entity Framework» или «ADO.NET». В файл Base.cs добавил ссылку на этот параметр:

public static string ConnectionMethod {     get     {         return System.Configuration.ConfigurationManager.AppSettings["ConnectionMethod"];     } }

Entity Framework и MySQL – репозитарий

Добавим в каталог Repository\EF файл DbContext.cs с классом EFDbContext:

public class EFDbContext : DbContext {     public EFDbContext() : base(Base.ConnectionString)     { }     public DbSet<UserClass> Users { get; set; }     public DbSet<LanguageClass> Languages { get; set; } }

В нём мы определяем используемую строку подключения к СУБД и наборы данных Users и Languages.

Добавляем файл LanguagesRepository.cs с классом LanguagesRepositoryEF:

public class LanguagesRepositoryEF : ILanguagesRepository {     private EFDbContext context = new EFDbContext();       public IList<LanguageClass> List()     {         return context.Languages.OrderBy(x => x.LanguageName).ToList();     } }

И файл UsersRepository.cs с классом UsersRepositoryEF:

public class UsersRepositoryEF : IUsersRepository {     private EFDbContext context = new EFDbContext();       public IList<UserClass> List()     {          return context.Users.ToList();     }       public IList<UserClass> List(string sortName, SortDirection sortDir, int page, int pageSize, out int count)     {         count = context.Users.Count();         if (sortName != null) return context.Users.OrderByDynamic(sortName, sortDir).Skip((page - 1) * pageSize).Take(pageSize).ToList();         else return context.Users.OrderBy(o => o.UserID).Skip((page - 1) * pageSize).Take(pageSize).ToList();     }       public bool AddUser(UserClass user)     {         user.Language = context.Languages.Find(user.Language.LanguageID);         if (user.Language != null && context.Users.Add(user) != null)         {             try { context.SaveChanges(); }             catch (System.Exception ex) {}         }         return user.UserID > 0;     }       public UserClass FetchByID(int userID)     {         UserClass user = null;         try { user = context.Users.Find(userID); }         catch (System.Exception ex) { }         return user;     }       public bool ChangeUser(UserClass user)     {         bool result = false;         user.Language = context.Languages.Find(user.Language.LanguageID);         if (user.Language != null)         {             UserClass olduser = context.Users.Find(user.UserID);             if (olduser != null)             {                 olduser.Email = user.Email;                 olduser.Loginname = user.Loginname;                 olduser.Language = user.Language;                 olduser.SupporterTier = user.SupporterTier;                 try { result = context.SaveChanges() > 0; }                 catch (System.Exception ex) { }             }         }         return result;     }       public bool RemoveUser(UserClass user)     {         bool result = false;         UserClass olduser = context.Users.Find(user.UserID);         if (olduser != null) context.Users.Remove(olduser);         try { result = context.SaveChanges() > 0; }         catch (System.Exception ex) { }         return result;     } }

Видно, что размер файла явно короче подобного для ADO.NET — ORM делает за нас «грязную» работу — создает SQL-запросы самостоятельно.

Однако, я столкнулся с парой моментов, которые прокатывали в реализации ADO.NET, но не работают в EF.

Первый, что пришлось внести изменение в файл UserClass.cs (в каталоге Domain): добавить еще одно поле для нормальной работы связи таблиц Users и Languages:

[HiddenInput(DisplayValue = false)] public int? LanguageID { get; set; } 

И второй — оказалось что поля в MySQL типа Enum не работают через EF. Скорее всего причина этого в том, что перечисление в коде является целочисленным значением, а вот из БД значения через EF читаются как текст (если в запросе из MySQL читать значения поля типа enum MySQL возвращает как раз текстовые значения этого перечисления). И если в версии для ADO.NET я могу это обойти с помощью конструкции CAST(u.SupporterTier AS UNSIGNED) as SupporterTier, то с EF такая метаморфоза оказалась для меня непреодолимой — ни один из пробуемых вариантов не подошёл. Ну и поскольку технология Code First поле типа Enum генерирует в виде поля типа INT, то пришлось в БД поменять тип поля SupporterTier:

CHANGE COLUMN `SupporterTier` `SupporterTier` INT(4) UNSIGNED NOT NULL DEFAULT '1' ;

Выбор репозитория с помощью параметра приложения

Воспользуемся внедрением через конструктор, прямо как написано в учебнике. Во-первых, нам надо создать интерфейсы для нашего общего репозитария: создаем файл LanguagesRepository.cs в каталоге Repository с содержимым:

public interface ILanguagesRepository {     IList<LanguageClass> List(); } 

И файл UsersRepository.cs с содержимым:

public interface IUsersRepository {     IList<UserClass> List();       IList<UserClass> List(string sortName, SortDirection sortDir, int page, int pageSize, out int count);       bool AddUser(UserClass user);       UserClass FetchByID(int userID);       bool ChangeUser(UserClass user);       bool RemoveUser(UserClass user); } 

Ну и наследуем соответствующие классы от этих интерфейсов:

public class LanguagesRepositoryADO : ILanguagesRepository public class UsersRepositoryADO : IUsersRepository public class LanguagesRepositoryEF : ILanguagesRepository public class UsersRepositoryEF : IUsersRepository 

Ну и в контроллер UsersController вносим добавления, которые позволят ему работать с этими интерфейсами:

private ILanguagesRepository repLanguages; private IUsersRepository repUsers;   public UsersController(ILanguagesRepository langsParam, IUsersRepository usersParam)  {      repLanguages = langsParam;     repUsers = usersParam; } 

И в контроллере изменяем места обращения к объектам этих классов на объекты repLanguages и repUsers, соответственно. Но нам потребуется передавать экземпляры классов репозиториев через конструктор контроллера, что, конечно, неудобно. Чтобы этого избежать, нам нужно сильное колдунство типа Dependency Resolver (DR). И для этого мы будем использовать Ninject:

Регистрируем DR в файле Global.asax.cs в методе Application_Start:

DependencyResolver.SetResolver(new NinjectDependencyResolver()); 

Создадим файл NinjectDependencyResolver.cs в каталоге Infrastructure с классом NinjectDependencyResolver (унаследовавшего от интерфейса IDependencyResolver):

public class NinjectDependencyResolver : IDependencyResolver      {          private IKernel kernel;          public NinjectDependencyResolver()          {              kernel = new StandardKernel();              AddBindings();          }          public object GetService(Type serviceType)          {              return kernel.TryGet(serviceType);          }          public IEnumerable<object> GetServices(Type serviceType)          {              return kernel.GetAll(serviceType);          }          private void AddBindings()          {              if (Domain.Base.ConnectionMethod == "Entity Framework")             {                 kernel.Bind<ILanguagesRepository>().To<LanguagesRepositoryEF>();                 kernel.Bind<IUsersRepository>().To<UsersRepositoryEF>();             }             else             {                 kernel.Bind<ILanguagesRepository>().To<LanguagesRepositoryADO>();                 kernel.Bind<IUsersRepository>().To<UsersRepositoryADO>();             }         }     } 

И получается, что единственное место, в котором определяеся какой метод работы с СУБД используется (напрямую, через ADO.NET или через Entity Framework) это метод AddBindings в классе NinjectDependencyResolver. Настоящая магия, если не знать как это работает.

В методе AddBindings в зависимости от значения параметра приложения «ConnectionMethod» происходит связка интерфейсов ILanguagesRepository и IUsersRepository с конкретными классами реализующими методы интерфейсов. Поскольку при старте приложения мы зарегистрировали DR как объект класса NinjectDependencyResolver, а в классе мы указали привязку интерфейсов репозиториев к конкретному классу, то при запросе фреймворка MVC на создание объекта контроллера UsersController, Ninject при анализе класса обнаружит, что он требует реализацию интерфейсов ILanguagesRepository и IUsersRepository и создаст экземпляры конкретных классов и передаст их в конструктор контроллера (через DR и фрейворк MVC).

Итого

Приложение теперь поддерживает и метод доступа к СУБД через ORM Entity Framework. При этом метод доступа через ADO.NET никуда не делся и выбирается при запуске приложения по параметру, для чего было использован метод внедрения зависимости через конструктор контроллера с помощью библиотеки Ninject.

P.S. И напоследок: посмотреть, как работает данный проект можно по этому адресу. А вот тут можно скачать весь проект. Ну и до кучи — ссылка на мой блог.

ссылка на оригинал статьи https://habr.com/ru/post/484582/

Скрипт добавления серверов из Google Cloud в config ssh

Аннотация. Статья про очень простой скрипт, формирующий из списка серверов когфиг для ssh Linux. Проверено на Ubunta 18, используется Goodle Cloud SDK, Python 2.7, Bash.

После резкого увеличения количества серверов, с которыми приходится работать, понял, хранилище паролей и CMDB уже не дают такого оперативного доступа, как в те времена, когда просто помнил все ip и реквизиты наизусть. Может быть и потому, что CMDB мы до конца еще так и не осилили. Но тем не менее решать проблему быстрого доступа по SSH к большому количеству серверов как-то надо.

Дальше — с точки зрения терминала Linux (выполнялось на Ubunta 18). Возможно, работает и в других дистрибутивах и, вероятно, даже есть аналог на Windows — не смотрел.

Главные требования:

  • Легко повторять. Администраторов несколько
    и нужна возможность настроить одинаково у всех. Плюс к тому допускаем удаленную работу — хоть у каждого ноутбук, но ситуация, когда работаешь не за своим привычным "давно настроенным и отлаженным" компьютером бывает.
  • Сервера добавляются, удаляются, меняют адреса. Это должно учитываться.

Для этого решил использовать alias хостов в настройках ssh, список серверов получать через gcloud cli клиент GCP, а автоматизировать все это с помощью Python 2.7 (потому что он в Ubuntu был по умолчанию и я решил его изучить для работы с данными). Сам скрипт с описанием под катом.

Постановка задачи

Идея была такая: вести список связи "алиасов" серверов и их актуальных адресов таким образом, чтобы при установке подключения можно было использовать только имя. При этом сам список периодически актуализировать из GCP по API.

В итоге задач стояло несколько:

  1. Подключение к хосту набирая только запоминающееся его имя (обычно имена мы формируем по строгим правилам, поэтому достаточно знать клиента и систему, чтобы почти наверняка угадать имя сервера). Имена могут быть реальными (если сервер полностью создан нами) или "алиасами" внутри нашей cmdb (если сервер создан клиентом и имя присвоено по правилам клиента). В любом случае, имя запомнить проще, чем адрес. Адрес еще и меняется.
  2. Получение списка актуальных хостов с действующими адресами. Т.к. основной площадкой является GCP и 90% серверов там, то данная задача будет решаться через их API, а не средствами мониторинга.
  3. Поиск по именам хостов. Их хоть и проще запомнить, но количество растет, а память не безгранична 🙂 Да и новым администраторам так проще.
  4. Обновление связки алиас — адрес только при изменении адреса. Это необходимо для того, чтобы отказаться от полного переписывания всех связок. Это позволит хранить связки не только для серверов GCP. Пока что эта задача не решена 🙁

Описание решения

Алиас для подключения

Тут все довольно просто и решено не мной. Утилита ssh в Linux поддерживает настройку алиасов в конфиг файле.

Расположение файла: ~.ssh/config

Формат файла мы использовали очень банальный, но на самом деле возможности там намного шире:

HOST  алиас_сервера    HostName  ip_или_имя-сервера

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

Дальнейшие шаги

  1. Использовать разные ключи для подключения к разным группам хостов. Немного паранойи с точки зрения безопасности не повредит.
  2. Обеспечить резервирование этого файла для защиты от случайных изменений и ошибок скрипта обновления.

Поиск по именам хостов

Тут решение принадлежит совершенно не мне, а Ben Lobaugh и описано в его статье.
Коротко говорят, использовал sed, который для простоты запуска (уж очень длинное правило в параметрах) сокращено через alias:

alias sshhosts="sed -rn ‘s/^\s*Host\s+(.*)\s*/\1/ip’ ~/.ssh/config"

Если надо найти хост по части названия, то этот скрипт отлично дополняется утилитой grep и в итоге получается так:

sshhost | grep 'Искомая-часть-названия-хоста'

Есть и другие варианты, например, как описано тут.

Автозаполнение имени хоста

Спасибо за решение onix74 !
Добавить в ~/.bash_completion строку:

complete -W "$(grep "^Host " ~/.ssh/config | grep -v "\*" | sed 's/[^ ]* *\(.*\)/\1/')" ssh

Для команды ssh будет работать автозаполнение по списку алиасов серверов. Т.е. можно использовать ssh для подстановки имени ssh-сервера. Работает в bash.

Дальнейшие шаги

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

Получить список хостов из GCP

В этом шаге все довольно просто, если не считать настройки параметров (хотя и тут дело опыта). Решил использовать GCloud SDK для получения данных, т.к. пока пользуюсь им редко, но предполагаю, что когда-то это позволит мне все меньше использовать графический интерфейс и значительно упростить рутину администрирования. Поэтому основной аргумент — получение опыта.
Вероятно, то же самое можно было бы сделать используя curl и REST API GCP, и тогда решение было бы более универсальным (т.к. GCloud SDK требуют отдельной установки и инициализации, которые не так уж сложны, но все равно требуются).

Для получения нужной информации пришлось использовать формат json. Хотя это существенно упростило дальнейшую обработку полученного ответа, но заставило немного поломать голову с настройкой параметров форматирования в SDK.
В итоге получил такую команду:

gcloud compute instances list --filter="status:running" --format="json(name, status, networkInterfaces[].accessConfigs[])"

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

Итоговый результат прилетает в json:

[ ---...----   {     "name": "название-сервера",     "networkInterfaces": [       {         "accessConfigs": [           {             "kind": "compute#accessConfig",             "name": "External NAT",             "natIP": "ip-адрес",             "networkTier": "PREMIUM",             "type": "ONE_TO_ONE_NAT"           }         ]       }     ],     "status": "RUNNING"   }, ----...--- ]

Дальнейшие шаги

Найти формат, вывод которого позволит минимизировать или вообще устранить последующую обработку.

Скрипт создания списка алиасов по данным из GCP

Скрипт написал на python 2.7 по двум причинам:

  1. я решил изучить его для работы с данными;
  2. python 2.7 стоит по умолчанию на большинстве систем под управлением linux — не будет проблем с использованием в других местах. Учитывая, что даже win сейчас позволяет поставить вторую систему и использовать Ubuntu как терминал.

Алгоритм следующий:

  1. Получаем список серверов из Google Cloud с помощью SDK, инициируя выполнение команды в консоли из python.
  2. Разбираем полученный JSON в понятные python типы.
  3. На выход отдаем строки в формате, необходимом для настройки ssh (при необходимости выход скрипта перенаправляется в .ssh/config или в какой-то промежуточный файл).

Посмотреть скрипт

#!/usr/bin/python import commands import json  sComputeListOutput = commands.getoutput('gcloud compute instances list --filter="status:running" --format="json(name, status, networkInterfaces[].accessConfigs[])" ') s = json.loads(sComputeListOutput) for server in s:     print 'HOST ',server['name']     print '   HostName ',server['networkInterfaces'][0]['accessConfigs'][0]['natIP']

Обновление только измененных данных

Эта задача пока в работе. Планирую читать файл ssh/config в рамках того же скрипта, сравнивать с полученными значениями и потом записывать весь результат обратно. Или генерировать новый файл отдельно и проводить сравнение, используя какой-нибудь diff — это позволит вручную подтверждать все изменения.

ссылка на оригинал статьи https://habr.com/ru/post/484500/

Зачем нужен менеджер в IT проекте и что будет происходить когда его нет

Роль ПМ-а — она есть всегда, и если не поручена отдельному человеку с нужной подготовкой, то перераспределяется.

Кому?

  1. Всем членам команды в равной степени.
  2. Одному члену команды готовому совмещать это со своей первичной ролью.
  3. Человеку извне, который в процессе толком не участвует, но как-бы управляет.

Все эти варианты вполне реальны, и встречаются на практике, в особенности в молодых компаниях, в которых еще нет структуры и процессов.

Держим сразу в уме вопросы:

  • Кто общается с клиентом?
  • Кто держит в уме всю картину проекта? А лучше документирует её.
  • Кто организовывает процесс?


1. Роль менеджера распределяется всем — и при этом команда средняя по опыту — будет тяжело. Люди не будут знать что делать, и придется много времени митинговать. Совокупная стоимость их часов на обсуждение быстро уйдет в небеса. И не факт что будет достигнут компромисс.

Также общаться с клиентом надо уметь — даже с адекватным, с неадекватным это сложнее в разы. Кто-то не терпелив к вопросам и утверждениям несведущего заказчика, кто-то не понимает совсем его бизнес и зачем ему нужен продукт. Селфтаскинг и прочие радости тоже чреваты проблемами, да и просто не любят разработчики этим заниматься — а значит делают это посредственно. Не забываем что кто-то должен контролировать всё.

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

2. Один из программистов — сходу, он сможет адекватно понять бизнес-требования и перевести на понятный язык своим бойцам? Ведь согласитесь, человек то что не понимает, то пропускает мимо ушей — а не понимает в бизнесе средний разработчик много вещей, так как не его сфера совсем, и часто просто неинтересно. Дальше своим ребятам он объяснит коротко — что надо, каждый опять понимает по-своему и результат — полный провал. Потому что, опрашивать клиента надо уметь, объяснять другим тоже надо уметь, и с людьми притираться и склеивать команду тоже необходимо.

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

3. Человек глубоко не погружающийся в проект — наихудший вариант имхо. Часто у него просто нет времени на это, он подхватил заказ по случаю через связи, деньги хорошие, нашел команду технарей — и дело пошло. Параллельно у него еще, как правило, есть занятие поважнее.

В итоге технари без понятия что надо делать, и не переспрашивают — что-то пилят. Он сам понятия не имеет что они разрабатывают, изредка проверяет, но в основном на тест ничего не выходит. Про заказчика вообще молчу. Так происходит потому, что на такие проекты в 80% случаев попадают фрилансеры-одиночки, которые не умеют и/или не хотят работать совместно с кем-то, и не особо умеют делать что-то сложное, где в одиночку не справиться, потому что на биржах таких проектов практически нет. Соответственно никакой самоорганизованной команды крутых ребят не будет. Будет группа ничего не понимающих одиночек. Повезло если к ним попадет лидер, который сможет их сплотить. И то это не сразу и без гарантий.

Так зачем ПМ?

Первое — держать в уме всю картину, и документировать её вовремя с проектной точки зрения. Членам команды нужно прорабатывать детали, и проект как единое целое от них часто ускользает. Сложно постоянно двигаться от частного к общему и обратно. А вот менеджер обязан это делать, и отвечать в меру своей компетенции на все вопросы.

Второе — быть единым контактным лицом для всех. Вся информация должна проходить через него, иначе сразу начнется бардак. Заказчик договорится с отдельным разработчиком, или предъявит ему баги, тот никому не скажет, баги касаются и других и пошло-поехало.

Плюс претензии — менеджер он еще и громоотвод, он выслушивает, принимает все претензии, и в конструктивной форме передает их команде, зная на кого и как воздействовать. Нередки случаи, когда камни летят в конкретного программиста, а тот от первичного шока начинает делать глупости: говорить лишнее, валить на других, нападать в ответ, убегать.

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

Кстати без профильных знаний в управлении это делать достаточно сложно.

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

ссылка на оригинал статьи https://habr.com/ru/post/484584/