Make. Build. Break. Reflect.
947 subscribers
119 photos
1 video
1 file
124 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
#пятница #всратость

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

- вход в кубер через SSO, OIDC, VPN со временем сессии 2 часа, ролевая модель строже и острее, чем шутки в 1937 году, девелоперы могут быть только в UI ArgoCD с read-only, а каждый чих логирует SIEM, и если ты случайно нажал F5 чаще, чем раз в 5 секунд, срабатывает правило "Potential Brute Force" и тебя блочат.

- никакого интернета, только внутренние ресурсы. Везде distroless или SBOM, имаджи сканирует Trivy, депенданси бот обновляет версии. Вайтлист на реджистри, OPA. Три контура container registry, с ручной перекладкой имаджей и immutable тегами. Три контура CICD с разделением доступов по командам и подпись образа через Cosign.

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

- инфобеза угорает по файрволам между immutable Talos нодами и натягивает Istio везде, даже на глобусы. В системе логируется буквально всё: ивенты, ивенты ивентов, даже команды от дебаг контейнеров типа ls. Собственно и kubectl debug не работает, когда ты пытаешься запустить busybox, ведь OPA его блокирует, потому что не в вайтлисте. Все логи не просто собираются, а дублируются в три разных S3-бакета, каждый из которых зашифрован своим KMS-ключом, который ротируется каждые 2 часа

- всё обвешано киверно политиками, от замены пути к имаджам, до принудительного обмазывания капабилитис. Уже оператор Trivy сканирует в рантайме имаджи, пофиг, что в CI/CD уже сканировалось.

- повсеместно селинукс, секьюрити контекст, рутлес, капабилитис.
Всем запрещено монтирование секрета куба, доступ к нему только по заявке и аппрувом Его Самого.

- все коммиты в git проходят через 4 уровня ревью: обычный code review, security review, compliance review и финальный ревью от инфобеза, где они проверяют, не закодировал ли ты в YAML отступах какие-нибудь вредоносные секреты.
Каждый мердж в мастер требует подписи руководителя департамента

- пароль от БД не знает даже дба - оператор сам создает пароль и хранит в волте, доступ только у оператора, пароль инжектится напрямую в апп, даже в секретах не узнать. Все проходят PCI DSS v4, SOC2 и даже DRP в случае нападения казаков.

- всюду SAST, DAST, DU HAST, CIS, SCA, хоть и не все понимают что это и зачем, но покорно используют

- админы опеншифта и кубера общаются между собой исключительно через вебхуки и admission-контроллеры

- каждый месяц проходишь тесты на полиграфе, доказывая, что никаких закладок не делал, ты всего-лишь пилил пайплайны. Вокруг датацентра ров с акулами, колючей проволокой в башкирском мёде, вертолёты и собственная армия с малой авиацией. При выдаче USB-токена при старте работы проходишь квест убегания от служебных собак, тренировка на скорость и выносливость, ведь слабаки нам не нужны.
.
..
...
А в это время в Перми, весело щебеча, словно воробушек, операционистка Валентина делает "клац-клац" фотокамерой с телефона, сливая все данные клиентов на экране монитора её панели оператора, за сумасшедшие 3500 рублей, которые ей пообещали анонимы в телеграме, наплевав на DLP, ведь она об этом даже и не знала.
.
..
...
Инфобеза тратит ещё миллиард денег, чтобы провести расследование и внезапно доказывает, что виноваты девопсы, опять всё сделали не так. Ведь так оно и есть. Во всём везде и всегда виноваты девопсы.
15🤣32😁7🔥6💯61
#мысли #kubernetes

Выходные закончились, новый год успешно встречен, яма 100+ уровня и 240+ парагон взяты, а все данжи и боссы зачищены в 11 сезоне Diablo 4, а это значит пора готовиться к работе. И что у нас впереди? А впереди у нас очередная версия кубера.

Чем больше читаю про изменения куба в 1.35, тем больше жалею всех людей, кто работает в экосистеме Kubernetes.
Это просто невероятно, как кубер развивается ежеквартально/ежегодно.
Как всё это помнить, знать, отвечать на собеседовании, траблшутить, когда так часто и многое меняется (в рамках одного года - четырёх релизов).

Заморочился и подцепил через MCP/context7/etc всё, что связано с кубом, но даже последние версии LLM не всегда корректно отвечают на вопросы по кубернетису, особенно всё, что связано с lifecycle и security.
Пришлось написать собственный MCP для работы с компонентами k8s в git и пару tools/scripts для аналитики.

Чтобы всё знать и понимать на уже глубоком уровне требуется не только читать официальную документацию, но так же и сторонние ресурсы, где авторы детально разбирают многие механизмы, рисуют диаграммы, приводят примеры манифестов.
Ну типа
- https://www.redhat.com/en/blog/kubernetes-deep-dive-api-server-part-1
- https://www.ivinco.com/blog/a-deep-dive-into-kubernetes-networking
- https://learnkube.com/kubernetes-long-lived-connections
- https://www.opcito.com/blogs/advanced-kubernetes-security-posture-management-deep-dive
- https://blog.devops.dev/mastering-kubernetes-architecture-a-deep-dive-for-devops-and-platform-engineers-f0376c319d45
Все не буду перечислять, это лишь пример.

С таким подходом и быстрым развитием уже через 3-4 года я смогу и не узнать тот кубер, что был в 2019, когда я впервые в него потыкал. Впрочем, может оно и к лучшему.

- - -
* спасибо славным ребятам из Фланта, которые квартал за кварталом и год за годом отлично пишут заметки про изменения новых версий ❤️
👍18🔥42
#devops #security #nginx #tls #privacy

Тихо и незаметно (для меня) NGINX 1.29.4 официально добавил поддержку Encrypted Client Hello (ECH).
Для инженеров, которые следят за приватностью трафика, это важная новость.

Что такое ECH и почему это важно.
ECH - расширение TLS 1.3, которое шифрует полностью весь ClientHello, включая Server Name Indication (SNI).
Раньше SNI передавался открытым текстом: любой наблюдатель на сети видел, к какому домену вы коннектитесь, даже если содержимое соединения зашифровано. ECH устраняет этот пробел.


ECH использует два ClientHello:
- ClientHelloOuter - видимый, с "dummy" SNI
- ClientHelloInner - зашифрованный, с реальным SNI и другими параметрами

Шифрование происходит через HPKE (Hybrid Public Key Encryption).
Если сервер не может расшифровать inner, он завершает handshake с outer и сообщает клиенту актуальный ключ для повторной попытки.

Важные требования:
- DNS-over-HTTPS обязателен - без него публикация ключа в DNS небезопасна
- Частая ротация - рекомендуется ротировать ключи каждый час (как Cloudflare)
- HTTPS-записи -должны быть корректно настроены с параметром ech=

Для инженера это значит:
- провайдер или DPI-бокс не увидит, куда идёт трафик из вашей инфраструктуры
- метаданные перестают быть источником утечек
- комплаенс с требованиями privacy-by-design становится проще

Что уже работает:

- NGINX принимает директиву ssl_ech_file /path/to/ech-keys.pem
В контексте http/server - она указывает PEM‑файл с ECHConfig и ключами, необходимыми для включения TLS 1.3 ECH в shared‑режиме.
​- поддержка HTTP и Stream модулей
​- ротация ключей через несколько файлов и nginx -s reload - директива ssl_ech_file может указываться несколько раз для разных файлов, что позволяет операционно ротировать ECH‑ключи.
​- мониторинг через переменные $ssl_ech_status и $ssl_ech_outer_server_name - они дают статус обработки ECH

Чего мне пока не хватает для продакшена:
- стабильная версия OpenSSL с ECH
ECH поддерживается только в экспериментальном бранче feature/ech OpenSSL.
Хотя OpenSSL 3.5 LTS вышел в апреле 2025, ECH не вошел в основной релиз и требует сборки из специального бранча.
- отсутствие интеграции с инфраструктурой
Let’s Encrypt и другие CA пока не выдают ECH‑ключи через ACME‑протокол - механизм не стандартизирован. Обычные TLS‑сертификаты работают с ECH без изменений, но получить и автоматически обновить ECHConfig‑ключи придётся вручную.
Публикация ключей в DNS (через HTTPS/SVCB‑записи) требует ручного управления или кастомных скриптов.
- ограниченная поддержка клиентами
Хотя Chrome и Firefox поддерживают ECH, некоторые корпоративные библиотеки еще не обновлены.

Как же это плюсы для для девопс-команд?
1) Privacy-focused сервисы🔥🔥🔥
server {
listen 443 ssl http2;
ssl_ech_file /etc/nginx/ech-keys.pem;
# Теперь SNI не виден провайдеру!
}


2) Мультитенант платформы
- скрываете имена клиентских доменов от внешних наблюдателей
- полезно при shared infrastructure с чувствительными данными

3) Корпоративные VPN-шлюзы 🔥🔥🔥
- скрываете конечные точки внутри корпоративной сети
- провайдер видит только коннект к VPN-шлюзу, не к конкретным сервисам

Что делал я для домашней лаборатории:
git clone -b feature/ech https://github.com/openssl/openssl.git
./config --prefix=/opt/openssl-ech && make && sudo make install

./configure --with-openssl=/opt/openssl-ech \
--with-http_ssl_module \
--with-stream_ssl_module
make && sudo make install

/opt/openssl-ech/bin/openssl ech_keygen -out /etc/nginx/ech-keys.pem

+ крайне важно не забыть добавить DNS публикацию ECH-ключей.
Ключи ECH не работают изолированно - их обязательно нужно опубликовать в DNS через HTTPS-записи (SVCB/HTTPS-тип).
Без этого клиенты не узнают, что сервер поддерживает ECH.
_443._https.example.com. IN HTTPS 1 . ech=AEj+DQBE..
314🆒2👍1🤯1
Для продакшена предполагаю будет так:
- подождать OpenSSL 4.0 (ориентировочно апрель 2026)
- дождаться стабильного релиза NGINX с ECH (сейчас, как я понял, это не стейбл, а мейнлайн)
- подготовить скрипты ротации ключей
- протестировать с реальными клиентами (A/B)
- мониторить логи через $ssl_ech_status для выявления проблем

Вывод (для меня):
ECH в NGINX - это важный первый шаг, но не готовое решение для продакшена.
Пока это инструмент для пионеров, которые готовы собирать из сорсов и тестировать в лабораторных условиях.

Рекомендация (для остальных):
Начинайте изучать технологию, готовьте тестовые стенды, но не спешите в прод.
Когда выйдет OpenSSL 4.0 и основные клиенты добавят поддержку - ECH станет must-have для privacy-conscious инфраструктуры.

А сейчас это хорошая возможность изучить что-то новое.

* Дополнительный материал:
- https://blog.nginx.org/blog/nginx-open-source-1-29-3-and-1-29-4
- https://nginx.org/en/docs/http/ngx_http_ssl_module.html
- https://developers.cloudflare.com/ssl/edge-certificates/ech/
- https://blog.cloudflare.com/encrypted-client-hello/
- https://datatracker.ietf.org/doc/draft-ietf-tls-esni/25/
1👍15
#cloudfront #aws #troubleshooting #одинденьизжизни

Однажды меня попросили сделать перенос старого SPA (Single Page Application)-приложения на новый S3 bucket в CloudFront.
Но не просто перенос, а хитрый: часть путей должна идти на новое приложение, а часть - оставаться на старом.
Legacy страницы типа /welcome, /login-start, /login-end пока не переписаны в новом коде, их надо сохранить.
А всё остальное - на свежий app-v2.
По сути частичная миграция на новое приложение, не ломая старое.

Казалось бы, что может пойти не так? 😁

Задача звучит просто:
- /* > новый bucket (app-v2)
- /welcome*, /login-start*, /login-end*, /help-bot* > старый bucket
- /assets/*, /sdk.js > старый bucket

Открываю Terraform, добавляю новый origin для app-v2 bucket.
Меняю default_cache_behavior на новый origin.
Добавляю ordered_cache_behavior для legacy путей на старый bucket.
Terraform plan - всё как задумано.
Apply. Жду. Готово.

Иду проверять.
- https://stage-app.example.com/ - работает, новое приложение грузится.
- https://stage-app.example.com/?appId=xxx - работает, редиректит.
- https://stage-app.example.com/welcome - ...

"Server Error. An unexpected error happened."


Чо. 🤡

Первая мысль - кэш CloudFront. Делаю invalidation. Жду. Проверяю.
Та же херня.

Вторая мысль - может origin неправильный?
Проверяю напрямую S3 bucket:
curl -I "https://stage-app.example.com.s3-website-us-east-1.amazonaws.com/index.html"
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 4755

Bucket работает. index.html есть. 4755 байт. Всё ок.

Третья мысль - может custom_error_response мешает?
В CloudFront есть глобальный обработчик 404 ошибок, который возвращает /index.html.
Думаю: "А, наверное S3 возвращает 404, а CloudFront берёт index.html с дефолтного origin!"
Убираю custom_error_response. Apply. Проверяю.
Та же херня. Даже хуже - теперь вместо "Server Error" просто "Not found".

Возвращаю custom_error_response. Сижу, чешу репу.

Ладно, поехали курлить по-взрослому.
curl -sI "https://stage-app.example.com/welcome"
HTTP/2 200
content-type: text/html
x-cache: Hit from cloudfront

Стоп. 200? Не 404? CloudFront отдаёт 200 и HTML?
Смотрю body:
curl -s "https://stage-app.example.com/welcome" | head -20

Да, это HTML старого приложения. webpackJsonstatham@app/web. Всё верно.
Так почему "Server Error" в браузере?

Если HTML грузится, значит проблема в JavaScript.
Приложение падает после загрузки.
Открываю DevTools > Network.

И тут я вижу ЭТО:
/static/js/main.a1b2c3d4.chunk.js → content-type: text/html 
/static/css/main.e5f6g7h8.chunk.css → content-type: text/html

JS файлы возвращают HTML вместо JavaScript.
Браузер пытается выполнить HTML как JavaScript.
Приложение падает с "Server Error".
Сука. 😁

Проверяю напрямую S3:
curl -sI "https://stage-app.example.com.s3-website-us-east-1.amazonaws.com/static/js/main.a1b2c3d4.chunk.js"
HTTP/1.1 200 OK
Content-Type: text/javascript
Content-Length: 1080675

S3 отдаёт правильно! 1MB JavaScript!

А через CloudFront:
curl -sI "https://stage-app.example.com/static/js/main.a1b2c3d4.chunk.js"
HTTP/2 200
content-type: text/html
x-cache: Error from cloudfront

CloudFront отдаёт HTML. И x-cache: Error from cloudfront. Красота.

Теперь понятно что происходит.
Сажусь рисовать на планшете путь запроса (а я всегда рисую).

Проблема:
1. Браузер > /welcome
2. CloudFront матчит /welcome* > origin: OLD bucket
3. S3 не находит файл /welcome (это SPA route, не файл)
4. S3 возвращает error_document = index.html
5. Браузер получает HTML старого приложения

6. HTML содержит: <script src="./static/js/main.a1b2c3d4.chunk.js">
7. Браузер резолвит ./static/... относительно /welcome
8. Браузер запрашивает /static/js/main.a1b2c3d4.chunk.js

9. CloudFront матчит /* (default) > origin: app-v2 bucket
10. В app-v2 bucket НЕТ файла main.a1b2c3d4.chunk.js!
11. S3 возвращает 404
12. CloudFront custom_error_response > /index.html
13. Браузер получает index.html (HTML) вместо JavaScript
14. JavaScript парсер: "че за херня, это не JS"
15. Приложение: "Server Error"🐒
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
#cloudfront #aws #troubleshooting #одинденьизжизни

Вот оно.
Два разных SPA с разными static assets в одном CloudFront distribution.
Legacy paths идут на старый bucket, но их ./static/* резолвится как /static/* и летит на default origin - новый bucket.
А там этих файлов нет.

Классика жанра: симптомы в одном месте, причина в другом.
Ошибка "Server Error" от JavaScript, а проблема в CloudFront routing.
Логи чистые, метрики зелёные, status code 200 - всё отлично, только ничего не работает.

Для визуалов:
ДО (работало):
══════════════════════════════════════════
CloudFront


┌─────────┐
│ OLD S3 │ ← всё идёт сюда
│ bucket │
└─────────┘

ПОСЛЕ (сломалось):
══════════════════════════════════════════
CloudFront

┌───────────┴───────────┐
▼ ▼
┌─────────┐ ┌─────────┐
│ OLD S3 │ │ NEW S3 │ ← default
│ bucket │ │ bucket │
└─────────┘ └─────────┘
/welcome* /* (всё остальное)
/login-* включая /static/*


Ну или вот так, что не работало (красиво только на ПК):
  Browser                     CloudFront                        S3
| | |
| GET /welcome | |
|------------- req ---------->| |
| | /welcome* --> OLD bucket |
| |------------- req ---------->| OLD bucket
| | | (app-v1)
| |<------------ res -----------| index.html
|<----------- res ------------| HTML + <script> |
| | |
| GET /static/js/main.js | |
|------------- req ---------->| |
| | /* (default) --> NEW |
| |------------- req ---------->| NEW bucket
| | | (app-v2)
| | |
| | main.js NOT FOUND! |
| |<------------ 404 -----------|
| | |
| | custom_error_response |
| | 404 --> /index.html |
| |------------- req ---------->| NEW bucket
| |<------------ res -----------| index.html
|<----------- res ------------| HTML (not JS!) WRONG! | (app-v2!)
| | |
| "Server Error" | |
| JS parser: WTF?! | |


Мораль:
Нельзя без дополнительной логики безопасно смешать два SPA с разными static assets в одном CloudFront distribution, если они используют пересекающиеся пути (/static/*) и простой path-based routing.
Относительные пути (./static/...) превращаются в абсолютные (/static/...) и летят на default origin.
И не всегда виноват девопс, что криво пилит редирект или бихейвиор. 😀

Решения:
- отдельные CloudFront distributions для старого и нового приложения
- Cloudflare Workers / Lambda@Edge для умного роутинга
- добавить /static/* behavior на старый bucket (но тогда сломается новое приложение)
- не переключать default, тестировать новое приложение на отдельном домене

Я выбрал вариант 4 (вроде, уже не помню) и пошёл пить кофе.

А RFC переписали, убрав требование "переключить трафик через один CloudFront".
Иногда лучшее решение - не решать задачу в лоб.
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍3
Make. Build. Break. Reflect.
#пятница #байки #troubleshooting Задолго до работы в айти я был простым советским инженером спутниковой связи. Тарелки, антенны, модемы, кабели, бесконечные командировки. В то время случалось масса странных историй. Мы разрабатывали спутниковое оборудование…
#байки #пятница #tcp #networking

Когда-то давно я работал в компании, которая делала железки для спутниковой связи.
Не те, что вы вешаете на дачу для ТВ, а серьёзные штуки - морские терминалы VSAT, Inmarsat-модемы для судов, станции для нефтянки в тайге.
Короче, интернет там, где провода не дотягиваются.

И вот первое, что узнаёшь в этой индустрии:
всё, что ты знал о сетях - забудь. 🙃

⚠️ Важно: всё, что ниже - про геостационарные спутники (GEO).
Это классический VSAT, Inmarsat, морская и промышленная связь.

Starlink, OneWeb и прочие LEO-созвездия - совсем другая история.
Там спутники летают на 550 км вместо 36 000, RTT составляет 20-40 мс, и проблемы с TCP практически отсутствуют.
Но это технология 2020-х, а я работал в эпоху, когда GEO был единственным вариантом.


Геостационарный спутник висит на высоте ~36 000 км.
Сигнал от твоего модема до спутника и обратно - это примерно 72 000 км.
Дальше сигнал летит на наземную станцию (HUB), там обрабатывается, и только потом идёт в интернет.
А ответ - всё в обратном порядке.
                        🛰 СПУТНИК
(36 000 км)
/ \
/ \
↗️ / \ ↘️
/ ~36 000 км \
/ в каждую \
/ сторону \
/ \
┌──────────────┐ ┌──────────────┐
│ ТВОЙ МОДЕМ │ │ HUB │
│ (корабль, │ │ (наземная │
│ платформа) │ │ станция) │
└──────────────┘ └──────┬───────┘

│ ③ оптика

┌──────────────┐
│ ИНТЕРНЕТ │
└──────────────┘


Суммарное расстояние: ~144 000 км (минимум, на экваторе).
Скорость света в вакууме: 299 792 км/с.
RTT = 144000 / 299792 ≈ 480 мс (только свет!)


Добавь обработку на спутнике, на HUB'е, в модеме - получаешь 600-800 мс RTT.
И это не баг. Это физика. Скорость света не обманешь.

Классический звонок от клиента:
- Алло, вы нам поставили модем на 10 мегабит, а у нас файлы качаются со скоростью 500 килобит!
- А какой размер файла?
- 100 мегабайт!
- А пинг какой?
- 700 миллисекунд...
- (вздох) Сейчас объясню.


И начинается лекция по TCP, которую никто не хотел слышать.
Но вам я расскажу.

Bandwidth-Delay Product: труба, которую надо заполнить
TCP работает так: отправил данные > ждёшь ACK > отправляешь ещё.
Сколько данных можно "запихнуть" в сеть до получения первого ACK, ограничено TCP Window.

А сколько данных должно лететь одновременно, чтобы канал был загружен?
Это называется BDP (Bandwidth-Delay Product):
BDP = Bandwidth × RTT

Считаем для нашего случая (все каналы 10 Мбит/с):
LAN (RTT 1 мс):
BDP = 10 000 бит ≈ 1.25 КБ
WAN (RTT 50 мс):
BDP = 500 000 бит ≈ 62.5 КБ
Спутник (RTT 700 мс):
BDP = 7 000 000 бит ≈ 875 КБ

Видишь разницу?

Чтобы полностью загрузить 10 Мбит спутниковый канал, надо держать почти мегабайт данных "в полёте" одновременно.
А дефолтное TCP окно в старой винде - 64 КБ. 😭

Математика боли
Максимальный throughput TCP (без потерь) приблизительно равен:
Throughput ≈ Window Size / RTT

Подставляем:
Window = 64 КБ = 524 288 бит
RTT = 0.7 с
Throughput = 524288 / 0.7 ≈ 749 000 бит/с ≈ 730 Кбит/с

730 Кбит/с на 10 Мбит канале.

Семь процентов утилизации.

Клиент платит за 10 мегабит, а получает 730 килобит.
И это не наш модем плохой. Это TCP.
Хотя клиент думает обратно. 😀

А давайте потюним TCP!
Конечно, никто ж до этого не догадался же, ага.
Ладно, мы ж не дураки. Знаем про sysctl, знаем про window scaling.

На Linux:
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
sysctl -w net.ipv4.tcp_window_scaling=1
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥3
Make. Build. Break. Reflect.
#пятница #байки #troubleshooting Задолго до работы в айти я был простым советским инженером спутниковой связи. Тарелки, антенны, модемы, кабели, бесконечные командировки. В то время случалось масса странных историй. Мы разрабатывали спутниковое оборудование…
#байки #пятница #tcp #networking

На Windows (реестр, куда ж без него):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
TcpWindowSize = 65535 (или больше с window scaling)
Tcp1323Opts = 3 (timestamps + window scaling)


Помогает? Да, частично.
Но есть нюансы:
- Обе стороны должны поддерживать большие окна.
А сервер где-нибудь в Китае - фиг знает как настроен.
- Потери убивают всё.
При 1% потерь и 700 мс RTT скорость падает катастрофически.
- Slow Start
TCP начинает медленно и наращивает окно раз в RTT. При 700 мс это оооочень долго.

Короче, тюнинг помогает, но не спасает. Нужен костыль уровня "бог".

TCP Acceleration: красивый обман
И тут на сцену выходит PEP - Performance Enhancing Proxy.
В народе - TCP Accelerator. Маркетологи любят это слово.

Идея гениальна в своей наглости.
БЕЗ PEP (честный TCP):

Client Satellite (700ms) Server
│ │
│───── DATA ───────────────────────────────>│
│ 700 ms │
│<─────────────────────────────────── ACK ──│
│ 700 ms │
│───── DATA ───────────────────────────────>│
│ Total: очень медленно │


С PEP (хитрый обман):

Client Local PEP Satellite Remote PEP Server
│ │ 700ms │ │
│── DATA ─>│ │ │
│<── ACK ──│ (мгновенно!) │ │
│── DATA ─>│ │ │
│<── ACK ──│ │ │
│── DATA ─>│ │ │
│<── ACK ──│─────── DATA ───────────>│ │
│ │ (много данных сразу!) │── DATA ──>│
│ │ │<── ACK ───│
│ │ │ │
│ Client думает, На самом деле данные │
│ что всё уже ещё летят по спутнику, │
│ доставлено! но клиент уже шлёт ещё! │

Как это работает:
1. На стороне клиента стоит модем с PEP (наша железка).
2. На стороне HUB'а - ответная часть PEP.
3. Клиент отправляет TCP-пакет.
4. Локальный PEP сразу отвечает ACK, не дожидаясь ответа с сервера.
5. Клиент думает "ура, данные доставлены!" и шлёт ещё.
6. Тем временем PEP буферизирует данные и гонит их по спутнику *своим протоколом* (оптимизированным под high latency).
7. Удалённый PEP получает данные и уже по-честному передаёт серверу.

По сути, мы разрываем TCP-сессию на два локальных сегмента с низким RTT, а между ними гоним трафик специальным протоколом.

Клиент видит: "пинг 700 мс, но скорость 10 мегабит!" 🎉
Магия? Нет, честный обман. 😁

Конечно, есть ограничения:
- Память не бесконечна.
Каждая "ускоренная" сессия жрёт буфер. Модем на 100 сессий - это одно, на 10 000 - совсем другие деньги.
- При потере связи - боль.
Если спутник моргнул, а PEP уже насобирал мегабайт данных "в кредит" - это всё надо переслать. А клиент уже уверен, что данные доставлены.
- Шифрование.
HTTPS, VPN, IPsec - PEP не может влезть в сессию, не может подменить ACK. Приходится либо терминировать SSL на PEP (что не всегда возможно), либо PEP работает только на транспортном уровне и не так эффективен.
- Оборудование на обеих сторонах.
Если у тебя PEP только на модеме, а на HUB'е нет - толку мало. Поэтому это работает в контролируемых сетях VSAT.

Вместо морали
Работая со спутниковой связью, начинаешь по-другому смотреть на сети.
Когда твой пинг 700 мс, ты понимаешь, что TCP придумали для LAN.
Твой клиент на корабле посреди Тихого океана жалуется на скорость, ты не можешь сказать "перезагрузите роутер".
Тебе надо найти решение.

Если вы думали, что всё знаете о сетях и TCP просто представьте:
Вы на нефтяной платформе в Северном море.
С ноутбуком 2005 года.
И Windows XP.
И TCP-окном в 64 КБ.
И RTT 800 мс.
А на улице идёт дождь, который дарит помехи.
И надо срочно скачать огромный файл.

Вы знаете о TCP и сетях не всё 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
👍37🔥54
#aws

Есть у меня личный паблик EKS (да, я плачу ~93-95 долларов ежемесячно).

Решил я обновить версию 1.33 на версию 1.34:
- control plane отлично обновился
- одна нод группа на t3.micro обновилась так же на отлично
- вторая нод группа из одной ноды на t3.small почему-то пыталась обновится до 1.35.😁

При том, что официально 1.35 в AWS ещё нет.

Повторные ручные попытки дернуть апгрейд приводят лишь к этой ошибке.

Хорошо,что это не прод на работе.
Спасибо хоть на этом.

- - -
Ждём 1.35 на днях?
Please open Telegram to view this post
VIEW IN TELEGRAM
😁121
#пятница #всратость #javascript

Заходят как-то в бар арифметика с плавающей точкой и неявное приведение типов...



Баланс отпуска (PTO) на JS:
- когда "1.41" - "1" почему-то превращается в "15.92".
Обожаю этот язык, не меняйте ничего!1!!
🤣10🙈4😁2
#victorialogs #victoriametrics #grafana #troubleshooting

Когда простая задача превращается в тупик:
миграция дашбордов с Loki на VictoriaLogs

Самобичевание, часть 1 из 2.

Вот уже много лет я работаю.
За плечами - десятки пройденных боёв с Kubernetes, облаками, мониторингом и коллегами.
Поел не одну ложку коричневой субстанции.
Казалось, что в области observability я видывал всё.
Но одна простая задача недавно поставила меня в тупик настолько основательно, что я до сих пор негодую на себя.

Как всё начиналось
У нас на работе есть собственная система observability - довольно сложная, с десятками компонентов, которые менялись со временем.
Сначала были одни инструменты, потом другие, затем поменялась концепция хранения, добавился мульти-тенантный доступ и так далее.
И я попал на задачу в самый неподходящий момент: когда нужно было переделать дашборды и для клиентов (с их интерфейсом и логами), и для внутренней техподдержки SRE.

Задача звучала тривиально:
- поменять дата-сорс в Grafana с Loki на VictoriaLogs
- параллельно часть метрик из VictoriaMetrics тоже переехала в VictoriaLogs - веб-серверный компонент стало удобнее хранить именно там
- переделать существующие панели, сохранив при этом изоляцию данных: чтобы клиенты не видели чужие логи, а права доступа работали корректно

Казалось, пара дней - и готово.

Когда тупик оказался глубже, чем я думал
Я провозился с этой задачей больше недели.
Почему?
Потому что оказалось, что я не знал многого.

Да нихера я не знал, начиная с базовой архитектуры:
- как работает индексация полей в VictoriaLogs (спойлер: индексируются все поля, не только лейблы как в Loki, плюс есть отдельные _stream_fields для ускорения типичных фильтров)
- как агенты отправляют данные и что такое _stream_fields
- что такое тенанты (AccountID, ProjectID) и как они задаются через HTTP-заголовки
- какие эндпоинты существуют (/select/logsql/query для запросов, /insert/loki/api/v1/push для записи)

Самое сложное - это понимание метрик в логах.
Когда берёшь запрос, не ясно: эта метрика уже распарсена из JSON на этапе ingestion, или её нужно парсить прямо в запросе?

Оказалось, что VictoriaLogs по умолчанию автоматически парсит JSON при записи данных (начиная с версии v1.16.0).
При Loki-совместимом ingestion VictoriaLogs пытается распарсить JSON из поля сообщения (message / _msg) и разложить его ключи в отдельные поля, которые затем индексируются.
Это можно отключить флагом -loki.disableMessageParsing или параметром disable_message_parsing=1 в URL.

А для парсинга в запросах есть свои операторы LogsQL:
- unpack_json - распаковка JSON
- unpack_logfmt - распаковка logfmt
- extract и extract_regexp - извлечение по паттерну
- fields pipe - работа с полями

Но вот сюрприз номер два: оказалось, что в Grafana можно вообще не парсить через LogsQL.
Есть Grafana Transformations - extractFields с format: "json" - и он достаёт поля из JSON прямо в браузере. 🤡
Это работает, но жрёт ресурсы на клиенте и ломает голову: где парсить - в запросе или в трансформации?

Примеры запросов, с которыми пришлось работать:
{tenant_id="${tenant_id}"} stream:"stderr" "[crit]" | stats count() as total
{tenant_id="${tenant_id}", region=~"${region}"} eventType:kubernetes-event

Тут видно:
- фильтрация по stream полям (stream:"stderr")
- поиск по тексту ("[crit]", "[emerg]")
- stats pipe с count() as total
- переменные Grafana (${tenant_id}, ${region})

И каждый раз для новой панели нужно было решать:
парсить в LogsQL через unpack_json или в Grafana через transformations?
Использовать stats pipe или range запрос?
Какой подход быстрее, а какой уложит VictoriaLogs ошибками 502 и 504?
Спойлер: я раз 30 уложил на лопатки виктория сторадж.
👍6
#victorialogs #victoriametrics #grafana #troubleshooting

Самобичевание, часть 2 из 2.

Особенности Grafana, о которых я забыл
Потом всплыли нюансы самой Grafana.
В плагине VictoriaLogs для Grafana (victoriametrics-logs-datasource) есть несколько queryType:
- Raw Logs - для логовой панели (лента логов, tailing)
- Range - для time series и графиков по времени, под капотом ходит в /select/logsql/stats_query_range
- Stats - для агрегатов без временной развёртки (одно число/набор чисел по фильтру)

И вот тут началась жесть с выбором правильного queryType:
- queryType: "range" - для time series графиков
- queryType: "stats" - для агрегаций через | stats count() as total
- queryType: "logs" - для сырых логов

Где-то нужен range-запрос, где-то stats.
Где-то метрика уже доступна как число в отдельном поле, а где-то её нужно извлекать из JSON.
И если ошибёшься с queryType - панель просто пустая, без ошибок.
Сидишь и гадаешь: запрос кривой или тип панели неправильный? 😭

Разница между _stream_fields (лейблы потока, быстрая фильтрация) и обычными полями лога оказалась критичной:
- фильтровать по stream field - быстро и дёшево
- по log field - медленнее, особенно если JSON-парсинг выполняется в запросе

Выводы и самоанализ
Когда я осмотрел свои коммиты, увидел десятки итераций:
- сначала делал парсинг в запросах через unpack_json, потом переключился на Grafana transformations, потом понял - лучше хранить поля структурированно при ingestion
- менял queryType с range на stats и обратно, потому что панели то пустые, то не те данные
- возился с мульти-тенантностью: настраивал лейблы типа tenant_id в запросах и флаги изоляции
- оптимизировал запросы со stats count() as total чтобы не гонять по всей базе полнотекстовый поиск
- добавлял фильтры по stream:"stderr" вместо поиска по всем логам
Полная фигня, если честно. 🤡

Это сильно меня опечалило:
- либо я отстал от технологий и мои знания Prometheus/Grafana устарели и надо срочно учить весь VM стек
- либо я переоценил себя при оценке задачи
- либо это действительно сложная задача, которую я недооценил
Скорее всего, правда посередине.

VictoriaLogs - не просто замена Loki, это другой инструмент с другой философией:
- Loki индексирует только лейблы потока, текст сообщений не индексируется - regex по message сканирует содержимое
- VictoriaLogs индексирует все поля логов, так что по ним можно быстро фильтровать и искать. При этом стрим-поля через _stream_fields дополнительно помогают с компрессией и ускоряют типичные фильтры
- LogsQL ≠ LogQL - это разные языки запросов, хоть и похожие
- Высокая кардинальность (trace_id, user_id, ip) в VictoriaLogs гораздо менее болезненна, чем в Loki, если хранить такие вещи как обычные поля, а не stream fields. Loki сильно страдает, если тащить user_id/trace_id в лейблы

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

И это я ещё пропускаю часть про VMrules и recording rules, которые пришлось добавить для некоторых панелей....😭

Задача - теперь для меня не сложная. Ну я максимум часа 2-3 на подобное потрачу.
Проблема в том, что я переоценил свои силы и знания (которых и не было).
В общем надо учиться и читать документацию, прежде, чем приступать к задаче.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥1
#kubernetes #мысли

Сижу, разбираюсь с Crossplane и провайдерами для Azure.
- https://docs.crossplane.io/latest/
- https://marketplace.upbound.io/providers (да, мне понравились upbound)
Надо было кое-что найти, а обычный лист выдаёт сотни ответов без грепа.
Глянул сколько у нас всего кастом кайндов, а там..

Ах этот безумный-безумный мир.
kubectl api-resources --verbs=list | grep -v customresourcedefinition | wc -l

513
kubectl get crd -o name | wc -l

498

В наших кластерах CRD станет скоро больше, чем базовых сущностей кубернетиса.
Безумие, абсолютное безумие.

В целом можно на собеседовании/в баре с коллегами по девопс-цеху меряться:
- У нас в кластере 498 CRD, а сколько у вас? 😁

Скоро это будет показывать насколько зрелый проект, мощная команда и глубокое погружение в экосистему кубера (надеюсь нет).

Очередной раз не завидую всем молодым специалистам, кому надо будет учить этот кубер.
Please open Telegram to view this post
VIEW IN TELEGRAM
💯10😱4
1.png
431.8 KB
#devops #git

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

Последние месяцы для работы со схемами/диаграммами вместо привычных ресурсов (draw.io excalidraw.com diagrams.mingrammer.com) перешёл на связку:
- любой AI ассистент, любая модель
- markdown файл https://www.markdownguide.org/
- mermaid фреймворк https://mermaid.js.org/

Поддержка mermaid в MD файлах уже достаточно давно, а основные инструменты - VSCode, GitHub, GitLab, Cursor умеют это отображать для человека и в IDE, и в веб-браузере.
В некоторых случаях надо ставить плагин, но в некоторых уже работает и так.

Как это работает:
- я прошу ассистента нарисовать некую схему. Схема может быть обычная flowchart, может быть state, sequence или что-то другое
- в основном README.md или CLAUDE.md появляется блок кода, который отлично лежит в md файле и при этом
- - схема видна в IDE через preview режим
- - схема видна в браузере на страницах GitHub/GitLab
- - схема понятна любой нейронке
- самое главное, это прозрачно и для меня и нейронка понимает что хочет, чтобы изменение кода было корректным
- ну и всё это в git репозитории

Рандомный пример промпта:
Нарисуй flowchart в mermaid, который показывает, как Pod с toleration scheduling'ится на tainted node в Kubernetes


Рандомный пример кода (в файлах формата *.md)
graph TB
subgraph "Node Pool"
N1[Node 1<br/>No Taint]
N2[Node 2<br/>Taint: dedicated=app:NoSchedule]
N3[Node 3<br/>Taint: dedicated=app:NoSchedule]
end

subgraph "Pods"
P1[Pod A<br/>No Toleration]
P2[Pod B<br/>Toleration: dedicated=app]
P3[Pod C<br/>No Toleration]
end

P1 -->|Can Schedule| N1
P1 -.->|Cannot Schedule| N2
P1 -.->|Cannot Schedule| N3

P2 -->|Can Schedule| N1
P2 -->|Can Schedule| N2
P2 -->|Can Schedule| N3

P3 -->|Can Schedule| N1
P3 -.->|Cannot Schedule| N2
P3 -.->|Cannot Schedule| N3

style N1 fill:#E8F5E9,stroke:#4CAF50,stroke-width:2px,color:#1B5E20
style N2 fill:#FCE4EC,stroke:#E91E63,stroke-width:2px,color:#880E4F
style N3 fill:#FCE4EC,stroke:#E91E63,stroke-width:2px,color:#880E4F
style P1 fill:#E3F2FD,stroke:#2196F3,stroke-width:2px,color:#0D47A1
style P2 fill:#E3F2FD,stroke:#2196F3,stroke-width:2px,color:#0D47A1
style P3 fill:#E3F2FD,stroke:#2196F3,stroke-width:2px,color:#0D47A1

Результат на скриншоте (GitHub + Cursor IDE).
Сами промпт/схема просто рандомно-мусорная, лишь для примера.

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

Связка мне нравится, я думаю многие это используют.
Если ещё не пробовали - начните.

Мы итак многие инженерные вещи переусложнили.
Хочется хотя бы с диаграммами и схемами сделать всё проще.
5👍196