Обложка канала

Хмельной Девопс. Страница 3

Будничный хаос и мрак при поддержке ИТ систем.

  • Хмельной Девопс

    Если вдруг кто пропустил (что маловероятно, но все же). Советую обратить внимание на отличную утилитку от hashicorp - terraform (https://www.terraform.io).

    С ее помощью вы сможете полностью описать инфраструктуру, которое использует ваше приложение. Дроплеты на ДО и инстансы ЕС2, dns записи в облачных и локальных сервисах, сервисы в pagerduty, deploy keys на гитхабе, почтовые домены в mailgun.org. В общем теперь все внешние сервисы, которые мы настраивали руками, можно описать с помощью terraform'а и запушить все в гит. Тем самым подойти практически вплотную к понятию infrastructure-as-a-code.

    P.S. Список провайдеров внушает: https://www.terraform.io/docs/providers/index.html
  • Хмельной Девопс

    Давно я ничего интересного не публиковал, но при этом число подписчиков выросло. Парадокс 🙂А нет, вру - статья же вышла на tproger.ru 🙂 В общем-то за это время ничего технически интересного и не происходило. Вот только вчера написал сервачок на golang'е, который round robin'ом записи из базы отдает. Но думаю, эта информация не достойна отдельной публикации. Так что сегодня я просто хотел поделиться с вами собственными мыслями и проектами, которые мы активно толкаем.

    Вертика. Это какой-то треш, простите меня, пожалуйста. Как только я вижу счет или офер от вертики я тут же начинаю нервно кусать локти. Ну как так-то? 60 000$ / Tb. Но цена после первого отказа сокращается на 20%, после второго на 40%, а после третьего скидка доходит до 80%. Непонятно зачем себя так мучить, если можно просто поставить кликхаус и закинуть все данные туда. Что мы собственно говоря и сделали (практически ;))

    С удивлением открыл для себя два крутых сайта: dnsperf.com и cdnperf.com - внутри сравнение между популярными Cloud решениями по dns и cdn, соответственно. Благодаря им, узнал, что cloudflare вообще в топе по DNS - решили как раз смигрировать на него, тем более enterprise ценник там на порядок интереснее, чем у Oracle (DynDNS).

    А еще, мы в этом месяце, стартанули онлайн проектик по мониторингу - https://checkops.com. Позволяет писать свои скрипты мониторинга на python'е. Остался вопрос с маркетингом 🙂Если надумаете потестить - пингуйте, очень хочу услышать фидбек. Даже если он и разнесет идею в пух и прах.

    Из нового еще запускаем свой курс по девопс, поэтому я начал проводить вебинары. В начале было ужасно стремно, но потом втянулся и начал получать кайф. Кстати, в связи с этим, я подумал что было бы прикольно записать какие-нибудь простые видосики на разные темы: Основы работы сетей, Как работает DNS, Как работает HTTP/SMTP/etc. Что скажете? Если вдруг вам идея понравилась, то заходите и предлагайте о чем рассказать - https://goo.gl/forms/w4J811agiSrNtvii2, я соберу все ответы и попробую успеть записать первое видео на следующей неделе.

    Так же очень хочется презентовать два новых проекта, которые мы планируем запустить в ближайшее время. Но пока не могу описать их подробно. Единственное что могу сказать, что один из них будет игрой - квесты в сети интернет, а-ля escape the room, только для айтишников. А про второй напишу отдельно на следующей неделе.

    Вот такие вот новости, друзья. Всем хороших выходных без алертов!
  • Хмельной Девопс

    Занялся я тут консолидацией всех используемых сервисов. Ну вы понимаете, каждый проект использует кучу внешних SaaS решений, таких как pagerduty, amazon, google cloud, digital ocean, mailgun, sendgrid, 1password и так далее и так далее. Обычно к таким сервисам подключается техническая дебетовая карта, с которой постоянно списывают деньги. И я пришел к мысли, что вообще было бы неплохо видеть ежемесячные расходы по каждому сервису.

    Но тут возникает проблема - каждый сервис биллит по-разному, в разное время и присылает инвойсы в pdf формате. Да и сидеть вручную вбивать информацию из инвойсов не наш метод. Немного гугления привело меня к прикольной утилитке, которой я и хочу с вами поделиться - https://github.com/m3nu/invoice2data. Под капотом pdftotext и система шаблонов, используя которые утилитка парсит инвойсы и выдает результат в виде invoice date, number, total amount.

    В итоге будущая схема выглядит следующим образом:
    1. В каждом сервисе указываем один и тот же email для инвойсов - [email protected]
    2. Подключаем на этот адрес fetchmail
    3. При скачивании письма, вложенный pdf передается на вход invoice2data.
    4. Результат invoice2data сохраняется в базу, отправляется в слак и еще куда угодно.

    Если вдруг я сейчас изобретаю велосипед, то прошу кинуть в меня ссылкой на сервис, который решит мои проблемы. Спасибо!
  • Реклама

  • Хмельной Девопс

    История одного запроса или переезд с mssql на postgresql.

    В табличках начали пропадать данные, поиск по логам и прочим источникам указал на причину - unlogged таблицы после креша полностью транкейтятся. Причиной креша оказался OOM. Ну ладно, подумали мы и увеличили память. Ровно через день история повторилась. Причем крешилось все на запросе update к не очень большой табличке.

    Первое подозрение было на триггеры. Отключили, запустили, упало. Памяти оно отжирало будь здоров (видно на скриншоте). Начали копать плотнее. Ну точнее решили не гадать, а посмотреть на execution plan:

    DB=# explain update table1 set column = True from table1 t1 join table2 t2 on t2.id = t1.id where t1.id < 100000 and t2.column in (0, 4) and t12bool;

    Update on table1 (cost=159112.16..2527607960.95 rows=202168723468 width=48)
    -> Nested Loop (cost=159112.16..2527607960.95 rows=202168723468 width=48)
    -> Seq Scan on table1 (cost=0.00..199305.87 rows=8276887 width=35)
    -> Materialize (cost=159112.16..268192.87 rows=24426 width=12)

    ... skipped ...


    WTF?! 200 000 000 000 строк?! Как оно вообще работало до этого (спойлер: видимо не работало). После глубокого и вдумчивого чтения документации переписали запрос (не я, спасибо доброму и очень умному человеку!):

    DB=# explain update table1 t1 set column = True from table2 t2 where t2.id = t1.id and t1.id < 100000 and t2.column in (0, 4) and t2.bool;

    Update on synonyms s (cost=159112.16..268440.74 rows=24426 width=42)
    -> Hash Join (cost=159112.16..268440.74 rows=24426 width=42)
    Hash Cond: (s.entity_id = e.id)

    ... skipped ...


    Ура! 24 000 строк выглядят намного лучше 200 млрд. И как результат - никаких проблем с памятью, да и запрос выполняется за секунды. Кому интересно, ответ кроется в документации (кто бы мог подумать!) - https://www.postgresql.org/docs/10/static/sql-update.html:

    Note that the target table must not appear in the from_list, unless you intend a self-join


    В общем, друзья, смотрите на свои запросы через призму explain и читайте документацию.
  • Хмельной Девопс

  • Хмельной Девопс

    Никому не рекомендую сталкиваться с системой разграничения прав в постгресе. Но если уж столкнулись, то моя заметка может вам помочь не сойти с ума.
    Итак, несколько поинтов, как выдавать права:
    1. Права выдавайте через роли, иначе, после сотого раза grant select on all tables in schema public to user, есть вероятность запутаться. Создаем роль create role schema_read
    2. Назначаем права на текущие таблицы и последовательности в нужной схеме (возьмем public, к примеру):
    grant all privileges on all tables in schema public to schema_read
    grant all privileges on all sequences in schema public to schema_read
    3. А теперь ход конем. При создании новой таблицы, она получает права из default privileges, посмотреть их можно с помощью \ddp запроса в постгресе. Добавим-ка нашей роли права на чтение всех новых таблиц и последовательностей:
    alter default privileges in schema public grant select on tables to schema_read;
    alter default privileges in schema public grant select on sequences to schema_read;
    4. Ну и добавляем нашего пользователя в группу schema_read:
    grant schema_read to user

    Чуть не забыл, если у вас не public схема, то выдайте еще права роли на ее использование:
    grant usage on schema schema_name to schema_read
  • Хмельной Девопс

    Для nginx вышел очень крутой модуль - mirror (http://nginx.org/en/docs/http/ngx_http_mirror_module.html). Наконец-то можно перестать использовать прокси костыли для этой задачи. Или вообще tcp mirroring tools. Единственное, чего не хватает на данном этапе, так это зеркалирования не всего трафика, а лишь части. Надеюсь эту фичу допилят в ближайшее время. А пока я пошел убивать стейджинг :)
  • Хмельной Девопс

    Интересный был сегодня денек. Очевидно, что РКН пошел в разнос и наша прокся попала под удар. Хорошо что был план Б с быстрым переездом на digital ocean. Плохо, что у digital ocean не заработал load balancer при более 50 одновременных подключениях.
    Но хорошие люди посоветовали присмотреться к Azure, что мы собственно говоря и сделали. Немного про переезд - сначала временное решение, далее вдумчивое чтение доков, настройка load balancer'а, available set и прочих интересных вещей. И вот мы снова обслуживаем 26 000 пользователей.
    Если интересно как поднять похожую схему на Azure - welcome - https://medium.com/@vozerov/microsoft-azure-loadbalancer-setup-8c0392ca29c3, написал для себя чтобы не забыть. Но вдруг кому-то еще пригодится.
  • Хмельной Девопс

    Жизнь девопса, инфраструктурщика, системного администратора неразрывно связана со стрессом. Алерты, жалобы на неработоспособность, трудно диагностируемые проблемы. Но все эти моменты ничто, по сравнению с ощущением полета, когда ты запустил новый проект и он заработал. Сразу и навсегда. Когда бекенд автоматически масштабируется, когда везде стоят healthcheck'и, когда есть нормальный мониторинг.
    Сегодня как раз тот самый день, день когда наша прокся для телеграм обслуживает ~ 15 000 сессий. Предлагаю и вам прочувствовать тот кайф, который возникает от отлично проделанной работы: http://telegram.airpush.com.
    P.S. Не реклама, в настройке принимал участие лично я 🙂
  • Хмельной Девопс

    Хороший скрипт для смены лидера в кафке: https://gist.github.com/miguno/87d5b2411e3f93e80866
    Скрипт генерирует json, который описывает как должны распологаться партиции в класетере - на каких брокерах. Допустим, вам необходимо отключить на некоторое время broker id 2 для замены дисков. Запускаем скрипт:

    ./kafka-move-leadership.sh --broker-id 2 --first-broker-id 1 --last-broker-id 3 --zookeeper localhost:2181 > ~/ld.json

    Скрипт сгенерирует инструкции для kafka-reassign-partitions, чтобы broker id 2 не был лидер ни для одной из партиций. После чего можно применить изменения:

    ./kafka/bin/kafka-reassign-partitions.sh --zookeeper localhost:2181 --reassignment-json-file ld.json --execute

    После завершения операции можно спокойно выключать кафку на maintenance - лидером она больше не является.
  • Хмельной Девопс

    Решил почитать changelog'и кликхауса и наткнулся на утилитку для проверки конфига nginx: https://github.com/yandex/gixy, сижу второй час и правлю ошибки.
    Есть очень забавные штуки про default_server и хедер Host к примеру, никогда бы до такого не додумался - https://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html. Или почему нельзя использовать alias, если location не заканчивается слешем (/) - https://github.com/yandex/gixy/blob/master/docs/en/plugins/aliastraversal.md.
    В общем советую пробежаться по своим хостам, будет весело, гарантирую.
  • Хмельной Девопс

    Недавно ресерчили почему nginx возвращает 400 на некоторые запросы. Ну как некоторые - порядка 300 rps 400ых ответов. Есть два способа разобраться в ситуации детально:

    1. Скомпилировать nginx с lua и добавить вывод в лог request_body, response_body и headers:

    lua_need_request_body on;

    set $request_headers "";
    set $resp_body "";

    body_filter_by_lua '
    local resp_body = string.sub(ngx.arg[1], 1, 1000)
    ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_body
    if ngx.arg[2] then
    ngx.var.resp_body = ngx.ctx.buffered
    end

    local h = ngx.req.get_headers()
    local request_headers = ""

    for k, v in pairs(h) do
    request_headers = request_headers .. k .. ":" .. v
    end

    ngx.ctx.buffered = (ngx.ctx.buffered or "") .. request_headers
    if ngx.arg[2] then
    ngx.var.request_headers = ngx.ctx.buffered
    end

    ';


    Далее определяем новый log:

    log_
    format bodylog '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" $request_time '
    '<"$request_body"> <"$resp_body"> <"$request_headers">';

    И
    используем в access_log.

    2. Второй способ завязан на tcpdump:

    tcpdump -i eth0 -s 0 -A 'host x.x.x.x and tcp dst port 80 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354)'

    Выгл
    ядит примерно так:

    03:07:06.943965 IP x.x.x.x.YYYY > server.domain.com.http: Flags [P.], seq 495715572:495715769, ack 2181877155, win 8192, length 197: HTTP: POST /?api=key&name=value HTTP/1.1
    [email protected] .1.P........P. .rl..POST /?api=key&name=value HTTP/1.1
    Host: server.domain.com
    User-Agent: curl/7.54.0
    Accept: */*
    Content-Length: 6
    Content-Type: application/x-www-form-urlencoded

    {json}
  • Хмельной Девопс

    Отличная новость для системных администоров и всего интернета. Let's Encrypt начал поддерживать выпуск wildcard-сертификатов.

    Что это значит?

    Сертификаты безопасности (SSL/TLS-сертификаты) — магический математический артефакт. Он защищает сайты двумя способами: 1. шифрует соединение читателей с сайтом в интернете, так что никто* не может подсмотреть, какие страницы открывает человек или данные он передает через формы сайта 2. позволяет подтвердить**, что между сайтом и читателем не влез злоумышленник, который меняет часть сообщений на другие (например, без SSL злоумышленник мог бы перехватить и изменить ваше банковское поручение).

    Система безопасности сейчас построена так, что для создания (выпуска) сертификата нужна третья сторона, так называемый центр сертификации (Certificate Authority). Это бизнес и чуваки собирают деньги за каждый сертификат. Вдобавок, каждый выпуск сертификата это ручной процесс (благо он происходит раз в год или реже).

    Несколько лет назад группа людей и организаций (посмотрите About, там много звездных имен) решили, что для общей безопасности нам нужно сделать выпуск сертификатов бесплатным и автоматизированным. И это сработало! Вот безумный график их роста, а вот тут исследование показывает, что 30% пользователей COMODO перешли на Let's Encrypt.

    Зачем нужны wildcard'ы?

    Дело в том, что порой у сайтов есть много поддоменов (то, что перед именем сайта через точку, www.meduza.io, monitor.meduza.io, specials.meduza.io, например). Как защитить эти сайты? Можно выпустить по отдельному сертификату для каждого этого поддомена. Так многие и делают, но есть ситуации, когда гораздо лучше заказать один (wildcard) сертификат *.meduza.io, который можно использовать на любых поддоменах Медузы.

    Раньше LE не умел выпускать wildcart-сертификаты, а теперь научился.

    Это был один из последних*** сильных аргументов, чем коммерческие Certificate Authority лучше Let's Encrypt.

    Ура!

    * Ходят слухи, что у NSA есть способы
    ** У злоумышленников есть 101 способ обвести вас вокруг пальца. Математику мы делать научились, а интерфейсы, которые бы помогли не попасться на удочку мошенника — нет.
    *** Есть ситуации, в которых сертификаты от LE не годятся, но если вы в этой ситуации — то скорее всего знаете, что делать.
  • Хмельной Девопс

    Вспомнилась история, как я лоад балансеры настраивал.
    Схемка примитивная до безобразия - два лоад балансера с nginx на борту и keepalived для переключения в случае падений.
    Все радовало глаз, кроме load average. Запросов примерно 10k rps overall. И я все время грешил на lua, который использовался в nginx для выбора нужного апстрима. Выглядит это примерно так:


    access_by_lua '
    -- Try POST first
    ngx.req.read_body();
    local args, err = ngx.req.get_post_args();

    if next(args) == nil
    then
    -- Since post is empty, use GET
    args, err = ngx.req.get_uri_args();
    end

    local uri = ngx.var.uri;
    local matched = string.find(uri, "needed-method");
    local some_arg = args["some_arg"];

    if (matched ~= nil and (nil == some_arg or "" == some_arg or string.find(some_arg, "^arg-value$")))
    then
    ngx.exec("@proxy");
    end
    ';


    Ну а ниже уже определяем location @proxy и направляем запрос куда надо. К слову, очень удобный способ при миграции. К примеру, переписываете вы api с python на golang. Используя lua и подход указанный выше вы очень легко сможете проксировать часть запросов (функционал которых уже реализован) на golang. Причем принимать решение о проксировании вы сможете на основне внутренних данных запроса. Тогда переезжать можно будет постепенно, а не разом все переносить.

    Возвращаемся к load average. Гитхаб оказался завален жалобами на nginx lua high cpu usage и во всех тредах советовали немного отпрофилировать nginx. Чем я собственно и занялся. Скачал, собрал, запустил - https://github.com/openresty/openresty-systemtap-toolkit#sample-bt. Получил карту вызовов системных функций и что увидел? Правильно! lua оказалась не при делах. Корень зла крылся в ssl 🙂 Кто-бы мог подумать.

    Ну и разрешилось все достаточно легко - добавил ssl_session_cache и нагрузка резко пошла вниз. На скриншоте два идентичных load balancer'а. Справа - после добавления ssl-related настроек, слева - первоначальный вариант.

    Ну и конечно вывод - гипотезы надо проверять.

    Ах да, чуть не забыл полезную ссылочку на утилитку для генерации ssl конфига: https://mozilla.github.io/server-side-tls/ssl-config-generator/
  • Хмельной Девопс

    Абсолютно случайно узнал про Amazon GovCloud Region. Он изолирован как физически, так и на уровне сети от других регионов Amazon. Так же у них абсолютно отдельный authentication stack, который (что логично), отделен от amazon.com. Еще из интересного - им управляют отдельные администраторы, каждый из которых является проверенным гражданином Америки.
    Более детально о различиях можно почитать здесь - https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/govcloud-differences.html
  • Реклама

  • Хмельной Девопс

    И, в продолжении темы про nginx. Немного запоздал с новостью про nginx server_push, но все же - отличная технология, которая позволяет отправлять клиентам контент без их запроса. Тем самым сразу можно закинуть css/js/картинки клиенту, который запрашивает index.html. Ведь мы знаем что они ему понадобятся. Потестирую на этой недельке и поделюсь результатами. Proof - https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/
  • Хмельной Девопс

    На выходных отваливался nginx-vts-exporter. Prometheus периодически кричал о том, что он не может к нему подключиться. Начали изучать и увидели, что nginx-vts-exporter отдает примерно 95 Мб данных на каждый запрос. Дальнейший разбор привел к очень интересным запросам, найденным в access_log'е nginx:

    27.97.240.25 - - [26/Feb/2018:08:05:35 -0800] "\xEF6\xF0\xF1p\xDC\xD8\x9A\xDE\xD6\xF6" 400 166 "-" "-" "-" - 0.710

    И таких запросов очень много. И символы всегда разные. Из-за них забивались метрики вида http_request_count{status="400", method="\xEF6\xF0\xF1p\xDC\xD8\x9A\xDE\xD6\xF6"}.

    Единственное что пришло мне в голову - что кто-то пытается на http порт подключаться с SSL хендшейком. Родился примерно такой тест:

    $ curl https://app-lb1.domain.com:80/
    curl: (35) Unknown SSL protocol error in connection to app-lb1.domain.com:-9847

    Но в логах, естественно были одинаковые символы для всех запросов - SSL Handshake начинался одинаково, что логично.

    x.x.x.x - - [26/Feb/2018:08:18:35 -0800] "\x16\x03\x01\x00\xC6\x01\x00\x00\xC2\x03\x03Z\x943[\x93\xDF\xA9R\xFD\x8Dg\xBDV/\xEBjY\x97L\xC0\xB5\xED\x5C\xB5\x18zd\x8BR\xA6\x8C\x0B\x00\x00D\x00\xFF\xC0,\xC0+\xC0$\xC0#\xC0" 400 166 "-" "-" "-" - 0.229
    x.x.x.x - - [26/Feb/2018:08:18:42 -0800] "\x16\x03\x01\x00\xC6\x01\x00\x00\xC2\x03\x03Z\x943bor\xF1\xE3_t\xB3\x96\x9C\xB3\x5CZWW(~@j\xB8\x04\xD9\x8E\xA6\xB9\xBB\xAF\x14\x1D\x00\x00D\x00\xFF\xC0,\xC0+\xC0$\xC0#\xC0" 400 166 "-" "-" "-" - 0.215

    Не знаю что это было, но в итоге просто начали возвращать 403 ошибку сразу, если метод не входил в whitelist (GET, POST, HEAD, PUT, DELETE, OPTIONS).
  • Хмельной Девопс

    Как обходят active/passive DPI - https://nnm-club.me/forum/viewtopic.php?p=9516088#9516088 с описанием работы первого и второго.
    И от того же автора утилита для проверки вашего провайдера - https://github.com/ValdikSS/blockcheck
    Запустив в первый раз я даже немного расстерялся - все проверки были пройдены успешно. А потом я вспомнил про включенный vpn. Как видно из скриншота - мне достаточно добавить перенос строки в начало запроса и сайт успешно откроется.
    Вообще сетевые технологии до сих пор меня поражают. В свое время я практически добрался до CCNP, но все же разбить пакет изменив window size и тем самым обмануть DPI - это очень круто.
    В любом случае всем советую завести свой vpn сервер на digital ocean.