Уймин - про разработку
162 subscribers
3 photos
37 links
Авторский канал про backend-разработку. Подробнее - в закрепллённом сообщении.

Личиный аккаунт: @maksimuimin
Download Telegram
Добро пожаловать на канал!

Меня зовут Максим, я Ведущий разработчик в VK, работаю над доставкой электронных писем в проекте Почта Mail.ru. Я разрабатываю ПО, которое работает 24/7 под нагрузкой 1.000.000 писем/мин.

За 5 лет в компании я прошёл путь Intern → Junior → Middle → Senior Lead, а в целом разрабатываю ПО где-то 9 лет.

На канале я делюсь практическим опытом backend-разработки. Каждый пост - краткая выжимка из профессионального опыта, который можно перенять и применить в работе.

3 моих лучших поста:
- 10 советов сделают твой код лучше https://t.me/uimindev/31
- Как поменять раскладку? https://t.me/uimindev/29
- Пусть баги ищут роботы, а не человек https://t.me/uimindev/44

Другие мои любимые посты, список постоянно обновляется: t.me/uimindev/37

Приятного чтения!
Уймин - про разработку pinned «Добро пожаловать на канал! Меня зовут Максим, я Ведущий разработчик в VK, работаю над доставкой электронных писем в проекте Почта Mail.ru. Я разрабатываю ПО, которое работает 24/7 под нагрузкой 1.000.000 писем/мин. За 5 лет в компании я прошёл путь Intern…»
Анализируем соединения netstat

Работа с сетевыми соединениями лежит в основе серверной разработки. Важность этой темы сложно переоценить, однако ни в универе, ни на курсах дополнительного образования никто толком не рассказывал, как отлаживать работу с сетью 😅

Для меня открытием стала утилита netstat, она позволяет ответить на вопросы:
- Какая программа куда ходит по сети?
- Какая программа какой порт слушает?

Дисклеймер 1: приведённые команды работают на Linux и основаны на GNU-версии утилиты. На MacOS стоит BSD-версия netstat, он сильно отличается от GNU, поэтому на маке мои команды работать не будут 😞 Вместо netstat можно использовать lsof.

Дисклеймер 2: чтобы видеть PID’ы и имена программ в выхлопе netstat, нужно чтобы юзер, выполняющий команду, был овнером сокета или суперюзером. В общем, для лучшей наглядности выполняйте команду от рута 😉

1️⃣ Вывести список всех активных TCP-соединений:
netstat -natp


2️⃣ Вывести список процессов, которые слушают порт (готовы принимать входящие TCP-соединения):
netstat -natp | fgrep LISTEN


или то же самое, но дополнительно вывести имена столбцов в табличке:
netstat -natp | head -2 && netstat -natp | fgrep LISTEN


3️⃣ Вывести список установленных соединений по процессу (как входящих, так и исходящих):
netstat -natp | fgrep ESTABLISHED | fgrep <pid | program name>


4️⃣ Какой на машине есть UDP-трафик:
netstat -naup


А как вы дебажите работу с сетью? Напишите в комменты!
Ставьте огонёк, если было полезно 🔥

#hardskills #tools #Linux #networks #debug
Что такое контейнеры и зачем они нужны? (часть 1)

Есть 2 группы бэкендеров: те, кто ещё ничего не знает о контейнерах, и те, кто применяет их везде, где только можно. Что это за технология и почему стала такой популярной? 🧐

Контейнер - это изолированный процесс ОС.

Это простое для запоминания определение, но оно мало что говорит о сути технологии. Предлагаю рассмотреть основные свойства контейнеров, чтобы семантика слова “изолированный” стала понятнее:

1️⃣ Контейнер видит только своё поддерево файлов, см. chroot

2️⃣ Все файлы, с которыми работает контейнер, находятся во временной директории, которая автоматически удалится после завершения контейнеризованного процесса, см. mktemp

3️⃣ У контейнера свой набор переменных окружения, см. execve

4️⃣ Контейнер можно (и нужно!) запускать от имени непривилегированного пользователя, см. setuid/setgid

5️⃣ Даже если контейнер запущен от рута, у него часто ограничен набор разрешённых системных вызовов, см. capabilities

6️⃣ PID контейнеризованного процесса всегда равен 1, при этом изнутри контейнера не видны другие процессы - только дочерние процессы контейнера, см. pid_namespaces

7️⃣ На работу с сетью тоже накладываются ограничения - контейнер может иметь свои фаерволы, выделенный IP, доступ к сетевым девайсам сервера может быть ограничен белым списком, hostname контейнера может отличаться от хостовой машины, см. network_namespaces, uts_namespaces

8️⃣ Ещё на контейнер могут быть наложены ограничения на потребление основных хардварных ресурсов сервера: оперативную память, и процессорное время, см. cgroups

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

Контейнер - довольно элегантная абстракция, наверное именно поэтому они стали так популярны

Однако, когда начинаешь рассматривать свойства контейнеров внимательно, выясняется, что это довольно сложная технология, обеспеченная множеством хитрых механизмов ОС ⚙️

В части 2 рассмотрим преимущества и недостатки технологии и обсудим где её можно применять. Подпишитесь на канал, чтобы не пропустить!

А вы используете контейнеры? Напишите об этом в комментарии 👇
Ставьте огонёк, если узнали что-то новое 🔥

#theory #containers #tools #Linux
Что такое контейнеры и зачем они нужны? (часть 2)

В части 1 мы рассмотрели, что такое контейнеры и какими свойствами они обладают. Вторую часть предлагаю начать с того, чтобы кратко рассмотреть жизненный цикл контейнера. Допустим, мы решили запустить какое-то свое ПО в контейнере, с чего нам начать? 🧐

1️⃣ Нам нужен имейдж. Имейдж - это архив, в который мы запакуем информацию об окружении (файлы, переменные окружения и т.п.), в котором будет запущено наше ПО. Сборка имейджа - первый этап жизненного цикла контейнера.

2️⃣ Имейдж надо отправить в реджистри. Реджистри - это файловый сервер, который хранит библиотеку собранных имейджей.

3️⃣ На машине, на которой мы хотим запустить контейнер, надо скачать имейдж из реджистри.

4️⃣ Теперь контейнер можно запустить. Как мы узнали из первой части, чтобы это сделать, надо выполнить десяток разных системных вызовов. Было бы чрезвычайно утомительно делать это каждый раз вручную, поэтому в нашей истории появляется специальная программа, которая сделает это за нас - контейнерный рантайм. Даём ему на вход имейдж, получаем на выходе запущенный контейнер. О, и ещё хорошая новость: имейджи и разные контейнерные рантаймы совместимы между собой благодаря стандартам OCI, давайте скажем коллегам спасибо 🤝

Плюсы контейнеров:

Поставляем ПО на продакшен сразу вместе с окружением.

↳ Деньги - единый подход к поставке ПО должен снижать стоимость эксплуатации.

Контейнеры изолированы, поэтому не конфликтуют между собой;

↳ Снова деньги - изолированные контейнеры можно “плотнее” набивать на сервера, так повышается утилизация железа ⇒ снова снижается стоимость эксплуатации ПО.

Безопасность - повышение изоляции компонентов усложняет проникновение злоумышленника в систему.

💡 Именно из-за экономической эффективности, как мне кажется, IT-бизнес по всему миру драйвит переход на контейнерные и облачные технологии.

Минусы контейнеров:

Не все приложения готовы к контейнерам - если вам нужно хранить состояние на диске (например, вы хотите запустить СУБД), или нужен какой-то специфичный девайс (например, GPU), или вы хотите выполнять экзотические системные вызовы - заехать в контейнеры может быть проблематично. Btw, вот тут есть список лучших практик, чтобы с контейнерами не было проблем.

Сложность системы сильно возрастает. Вся та изоляция, накрученная вокруг ПО, усложняет жизнь не только “злоумышленнику”, но и разработчику с админом. Когда что-то ломается, разобраться в этом - отдельный квест.

Необходимость в оркестрации - чтобы максимизировать экономический эффект от внедрения контейнеров, надо вместе с ними внедрять системы оркестрации, такие как Kubernetes. Это ещё сильнее усложняет систему.

В заключение, давайте рассмотрим варианты использования контейнеров:

☁️ Облачные инсталляции ПО

💻 Разработка ПО на локальном компьютере - контейнеры относительно удобно можно тестировать локально. Это работает как под Linux, так и под MacOS, и даже на Windows можно попробовать.

📦 Распространение ПО для разработчиков - если вы хотите поделиться своим ПО с другими программистами, то можно собрать его, запаковать в имейдж и выложить в реджистри с открытым доступом - так чтобы поиграть с вашим приложением не надо будет заморчиваться со сборкой из исходников под свою ОС.

Как вы думаете, какое будущее ждёт контейнерные технологии? Напишите об этом в комментарии 👇
Ставьте огонёк, если контейнеры топ 🔥

#theory #containers #tools #Linux
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Паттерн early exit

Какие задачи стоят перед программистом, когда он реализует какую-то функцию? Обеспечить корректность алгоритма, при минимальной сложности кода. Именно в этом помогает паттерн early exit.

📍 В чём суть: когда в вашем алгоритме появляется оператор ветвления (if, switch и т.п.), короткую ветвь вычислений надо обработать перед длинной ветвью.

ℹ️ Пример:
- Без early exit
if user.isAuthorized() {
// Do
// some
// business
// logic
} else {
return errors.New("403 Anauthorized")
}

- То же самое, но с early exit:
if !user.isAuthorized() {
return errors.New("403 Anauthorized")
}
// Do
// some
// business
// logic


У early exit подхода есть ряд преимуществ:

1️⃣ Снижается вложенность операторов - это упрощает чтение кода.

2️⃣ Повышается фокус на основной путь выполнения функции - часто, когда дочитываешь длинную (вероятно, главную) ветвь выполнения кода, уже забываешь, зачем выше было ветвление и в чём смысл короткой ветви. Early exit позволяет быстро прочитать короткую ветвь и забыть о ней, сфокусировавшись на длинной ветви. В итоге, программисту нужно меньше “оперативной памяти” чтобы прочитать код 😊

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

4️⃣ Если в вашем языке нет defer'ов и сборки мусора, разновидность early exit упрощает корректную деаллокацию ресурсов. Пример:
void foo() {
void *a = malloc(1024);
if (!a)
goto out_a;

void *b = malloc(1024);
if (!b)
goto out_b;

void *c = malloc(1024);
if (!c)
goto out_c;

/*
* Метки позволяют деаллоцировать только те ресурсы,
* которые были успешно аллоцированы
*/

free(c);

out_c:
free(b);

out_b:
free(a);

out_a:
return;
}

^ такой подход особенно популярен в ядре Linux: раз, два, три. Думаю, авторы этого кода что-то знают о программировании 😉

💡 Итого: при использовании операторов ветвления короткие ветви алгоритма следует обрабатывать перед длинными. Этот подход называется early exit. Он позволяет писать более простой и корректный код.

Ставь огонёк, если используешь early exit в своей работе 🔥

#hardskills #coding #pattern #bestpractice #codereading
Please open Telegram to view this post
VIEW IN TELEGRAM
Анализируем файловые дескрипторы lsof

Про подсистему ввода/вывода поговорили, а как это дебажить опять никто не сказал 🤡 Давайте исправлять!

📂 Когда мы открываем файл (пайп, сетевое соединение, и т.д.), операционная система даёт нам файловый дескриптор - это неотрицательное число, которое идентифицирует открытый файл. В дальнейшем, при каждой операции над этим файлом мы будем передавать файловый дескриптор, чтобы ОС могла понять, над каким файлом производится операция.

⚙️ В пространстве ядра у каждого процесса есть таблица открытых файлов. Файловый дескриптор - это по сути номер строчки в этой таблице, т.е. ключ. По ключу хранится значение - контекст открытого файла. В этой структуре хранятся, например:
- позиция чтения/записи (это похоже на положение курсора в тексте);
- флаги, с которыми открыт файл (только для чтения/для чтения и записи, блокирующий/неблокирующий режим и т.п.);
- примитивы для блокировок;
- и прочие переменные, необходимые для операций над открытыми файлами.

🔧 К чему я всё это написал: в UNIX есть замечательная утилита - lsof. Она работает с таблицей открытых файлов и может помочь в отладке операций ввода/вывода. Разберём несколько рецептов:

1️⃣ Вывести таблицу открытых файлов процесса:
lsof -p "<PID>"

☝️ Настоятельно рекомендую выполнить эту команду прямо сейчас, если вы первый раз слышите об lsof. Там куча всего интересного:
- Список открытых файлов на диске - логи, рабочая директория процесса, /dev/null и т.п.
- Список подгруженных разделяемых библиотек
- Список установленных сетевых соединений (btw, lsof может быть реальной альтернативой netstat, при чём под MacOS по моим наблюдениям lsof работает так же, как и под Linux)
В общем, lsof даёт много полезной инфы.

2️⃣ Вывести список процессов, работающих с файлом:
lsof "/path/to/file"


🤔 С какими проблемами может помочь lsof?

 Проверка доступа к файлам и сетевым ресурсам. Когда процесс “должен куда-то ходить” или наоборот “не должен куда-то ходить”, lsof подскажет - пытается он или нет.

 Отладка ошибки Too many open files - размер таблицы открытых файлов ограничен, из-за чего при открытии нового файла (или установке соединения) может возникать такая ошибка. lsof подскажет, имеет ли место утечка файловых дескрипторов (это когда программист close забыл позвать) и какие именно ФД утекли. Или наоборот, при анализе lsof можно провалидировать, что всё ок, и нужно просто поднять лимит.

 Отладка разделяемых библиотек - lsof покажет, какие либы процесс подгрузил, чтобы программист смог провалидировать, всё ли в этом списке в рамках ожиданий.

💎 В общем, lsof - невероятно полезная утилита. Меня самого она несколько раз очень сильно выручала. Надеюсь, теперь она будет полезна и вам 😉

Ставь огонёк, если lsof - топ 🔥

#hardskills #tools #Linux #fs #networks #debug