cat mindflow.txt > /dev/null – Telegram
cat mindflow.txt > /dev/null
180 subscribers
14 photos
87 links
Поток сознания о программировании, технологиях, жизни и вообще
Download Telegram
Практикующий software engineer может эволюционировать в двух направлениях - generalist и specialist.

Specialist подразумевает постоянное углубление знаний в какой-либо одной предметной области, технологии или языке. Если вам нужно знать, как выжать максимальную производительность из Кассандры, вы идете именно к специалисту.

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

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

Как известно, каждой задаче - свой инструмент. Всегда можно забивать гвозди микроскопом, но редко это будет эффективно. То же справедливо и для программистских задач. А у generalist-а их тьма и все они разные. Поэтому я долго думал над минимальным набором языков, эффективным как с точки зрения затрат на его изучение, так и количества областей, которые он будет покрывать. И, после почти 10 лет в индустрии, мой список выглядит так: Python, Go, C/Rust, JavaScript/TypeScript.

https://twitter.com/iximiuz/status/1191622134646411267?s=09
Mirantis купили Docker Enterprise и теперь Swarm официально мертв. Хотя пару лет поддержки для существующих клиентов, пока все не переедут на Kubernetes, все ещё обещают.

https://thenewstack.io/mirantis-acquires-docker-enterprise/
Что можно узнать, построив flamegraph на основе вывода cloc для кодовой базы Kubernetes https://iximiuz.com/en/posts/kubernetes-repository-on-flame/. Всего за 5 минут, без регистрации и смс.
Подвел итоги нет, не года, но десятилетия бытия программистом на полный рабочий день (а иногда и ночь, и почти всегда - выходные и праздники). Статья получилась техническая, с фокусом на мои личные best practices, выработанные за эти годы.

https://iximiuz.com/en/posts/my-10-years-of-programming-experience/?utm_medium=social&utm_source=tchannel
Об отказоустойчиости и cloud-native

Что делать, если что-то перестало работать? Первое правило инженера - выключить и снова включить (aka перезагрузить). А теперь посмотрите, во что превращается серверная разработка. Сплошные микросервисы, состоящие из большого числа эфемерных контейнеров. Если раньше мы мерились, у кого uptime сервера больше (потому что ребут очень часто был болью), то сейчас мы соревнуемся, кому проще прибить и перезапустить контейнер. Да еще и chaos monkey выпускаем, которые только тем и занимаются, что киляют процессы, ожидая, что сервис сам себя восстановит. Получается, что индустрия начала перманентно применять правило "выключи/включи" и это должно привести к качественному увеличению отказоустойчивости наших систем. Причинно-следственная связь правда не ясна. То ли Docker с Kubernetes-ом и AWS ECS нам позволили осуществить этот переход, то ли их для того и сделали, чтобы этот переход свершился...
conman - [the] container manager

В рамках моего pet project по созданию Kubernetes CRI-совместимого менеджера контейнеров (читай, клона containerd или cri-o) я наконец-то добрался до первых интерактивных результатов. Использовали docker run -i? Это примерно о том, как реализовать такую же фичу самостоятельно. На подходе docker run -i -t 🤓

https://twitter.com/iximiuz/status/1219011765943533570?s=20

О предыдущих этапах можно прочитать тут:

- conman - [the] container manager: inception https://iximiuz.com/en/posts/conman-the-container-manager-inception/
- Implementing Container Runtime Shim: runc https://iximiuz.com/en/posts/implementing-container-runtime-shim/
- Implementing Container Runtime Shim: First Code https://iximiuz.com/en/posts/implementing-container-runtime-shim-2/
В продолжение темы интерактивных контейнеров, описал свои приключения с реализацией этой функции в conman:

https://iximiuz.com/en/posts/implementing-container-runtime-shim-3/

#Go #Rust #Containers
И действительно, полностью согласен с результатами этого исследования. Книг про разработку или около-разработку вокруг очень много, но лишь малая часть из них по-настоящему стоящая и неустаревающая классика. Приятно осознавать, что в Top-5 выборки из исследования попали 4 книги из моего личного Top-5. А вот Clean Code by Robert Martin я, к своему стыду, все еще не прочитал.

https://threadreaderapp.com/thread/1229731043332231169.html
С этим вашим карантином работы почему-то стало больше, а свободного времени - заметно меньше. Так что создание собственного контента пришлось пока поставить на паузу. К счастью, кому-то все еще удается писать статьи. И вот одна новая и действительно классная:

"Surprising Things About Working at Well-Known Tech Unicorns"

https://blog.pragmaticengineer.com/surprising-things-about-working-at-tech-unicorns/
Все знают, что [Docker] контейнеры - это не виртуальные машины, что они нужны для упакови приложения и его зависимостей для последующего совместного запуска тьмы таких же коробочек на одном Linux сервере и т.д. и т.п. В то же время, чуть ли не каждый первый пример работы с Docker упоминает имя какого-нибудь стандартного дистрибутива Linux (debian, centos, ubuntu, alpine, busybox, etc) и бесцеремонно пользуется (GNU?) утилитами из него. В итоге у вновь прибывших случается некоторый когнитивный диссонанс. Если контейнер - это не виртуальная машина, то почему он ходит и крякает как утка выглядит как что-то, внутри чего работает полноценная операционная система? В общем, попробовал разложить все по полочкам в этой короткой заметке:

https://iximiuz.com/en/posts/not-every-container-has-an-operating-system-inside/

Важно: для сохранения душевного равновесия во время прочтения, нужно четко отличать Linux kernel (голое ядро) от операцинной системы (ядро + user-land обвязка в виде либ, утилит и конфигов) и дистрибутива (конкретная комбинация ядра и обвязки).

#Docker #Linux #Containers
И снова про контейнеры....

Термин container довольно сильно перегружен. Для разных людей и в зависимости от контекста он может означать разные вещи (systemd-nspawn, lxc/lxd, docker, rkt, OCI-complaint runtime, kata containers, etc). Но основная цель контейнеризации остается неизменной - это эффективная упаковка и изолированное выполнение пользовательских приложений, реализуемая на уровне операционной системы (а по факту - Linux). Эта идея и технология существует довольно давно, но по-настоящему популярной ей удалось стать именно благодаря удобной реализации от Docker.

Так хорошо всем знакомые docker build|push|pull|run позволили обычным смертными программистами воспользоваться всеми преимуществами контейнеризации без выполнения сложных последовательностей команд и тонкой конфигурации. Но, у такого коробочного решения оказалась и обратная сторона. Повседневное использование docker run -it ubuntu bash (или написание Dockerfile-ов, начинающихся с `FROM centos`) может привести к тому, что контейнер начнет казаться чем-то мало отличимым от полноценной операционной системы, спрятанной внутри некоторой коробочки виртуализации, созданной докером на вашем хосте.

На самом же деле, почти всегда контейнер - это лишь изолированный (namespaces) и ограниченный с точки зрения потребляемых ресурсов (cgroups) и доступных действий (capabilities, seccomp, AppArmor) процесс (или группа процессов). Такой же, как и все остальные процессы на вашем Linux хосте. [1] Но если контейнер - это обычный процесс, то для его запуска нам нужен всего лишь один единственный выполняемый файл! Т.е. можно создать контейнер с нуля FROM scratch (а не FROM debian|alpine|centos|etc) и добавить в пустую директорию (bundle) лишь один бинарник, который затем будет выполнен в изолированном окружении. Вполне себе валидный вариант использования, особенно для статических сборок, как в Go.

Если же программа внутри контейнера хочет иметь полноценную файловую структуру, что-то вроде того, что мы видим, выполняя ls /, то мы добавляем в bundle директорию все необходимые файлы. Заметили, что до сих пор мы ни разу не упомянули images? Только директории и исполняемые файлы, только хардкор!

Так для чего же нужны все эти _images_? Оказывается, основная задача, решаемая с помощью images - это эффективная сборка и распространение контейнеров, а не их запуск. [2] Images - суть tar архивы с файловой системой внутри. Перед запуском контейнера такой архив распаковывается во временную директорию, которая затем становится bundle директорией контейнера. Но если у вас на хосте есть 10 кастомных образов, каждый из которых основывается на базовом образе debian (100+ MB), будет ли это означать, что они займут как минимум 1 GB на диске? К счастью - нет. Образы сохраняются слоями (layers). Базовый образ debian формирует один такой слой и этот слой - неизменяемый. На этот слой затем ссылаются все остальные кастомные образы. Отлично экономит место на диске нужное, для хранения образов. Что, если нам нужно запустить 100 экземпляров одного и того же контейнера? Нужно ли нам создать 100 копий bundle директории? Да! Займут ли они x100 от размера исходного образа? К счастью - нет! Опять же, спасибо слоям, мы можем создавать директории bundle монтирую слои один на другой с помощью какой-либо из реализаций union mount, например overlayfs. Все слои, кроме самого верхнего временного слоя, окажутся неизменяемыми и поэтому могут быть безопасно использованы совместно разными контейнерами. Все изменения файловой системы, сделанные любым из экземпляров контейнера будут сохранены в самом верхнем временном слое, который после остановки контейнера будет просто удален. Отлично экономит место на диске, нужное для запуска контейнеров.
Итак, мы знаем, что images на самом деле не являются необходимым компонентом для контейнеров. Но на самом деле ситуация еще более "вывернутая" по сравнению с привычным нам вариантом использования docker build -> docker run. Для того, чтобы собрать образ, необходимо запускать контейнеры! [3] Обычно, мы их не видим, потому что docker (podman, buildah и т.п.) делает всю грязную работу за нас. Но, если в Dockerfile-е есть инструкция RUN это означает, что в процессе сборки образа будет происходить запуск промежуточных контейнеров. Задача таких контейнеров - выполнить команды из RUN в изолированном окружении. Как обычно, для создания таких контейнеров все промежуточные слои будут смонтированы для создания временной bundle директории. Все изменения файловой системы сделанные командами из инструкции RUN будут сохранены во временном слое, который затем будет сохранен, как один из слоев собираемого нами образа. Отличное и далеко не всегда явное использование технологии контейнеров!

Make code, not war!

[1] https://iximiuz.com/en/posts/not-every-container-has-an-operating-system-inside/
[2] https://iximiuz.com/en/posts/you-dont-need-an-image-to-run-a-container/
[3] https://iximiuz.com/en/posts/you-need-containers-to-build-an-image/
Крутая интерактивная визуализация Cloud-native проектов landscape.cncf.io. С разделением на уровни наконец-то стало понятно, что CNCF пытается под одной крышей собрать проекты, охватывающие полный цикл разработки, начиная от Provisioning и затем через Runtime (containers, stroage, and network) и Orchestration до Application Definition and Development.

Все элементы на карте кликабельны!
TIL: если вы вдруг оказались на Linux хосте или внутри контейнера, где нет ни ip, ни ifconfig утилит, а очень хочется узнать сколько там сетевых интерфейсов и как они называются - смело делайте ls /sys/class/net.

#Linux
TIL: pushd and popd - это как комбинация cd и cd -, только с полноценной историей переходов.

#Linux
Откуда есть пошел YAML заморский...

На самом деле, понятия не имею. Но воюя в очередной раз с хитрым синтаксисом какого-нибудь условного nginx.conf у меня всегда возникает вопрос - ну почему бы просто не описать конфигурацию в JSON? Та же история, когда пишешь какой-нибудь приложение, и наступает пора сделать его конфигурируем. Если все можно описать как композицию объектов, то зачем выдумывать кастомные форматы? Положим все конфиги в JSON! Но когда дело доходит до редактирования JSON, все вот эти скобочки, кавычки, отсутствие комментариев - это прямо ух... И так мы приходим к YAML. Который по сути JSON на стероидах и без лишнего шума (кавычек и скобочек). By the way, JSON - это валидный YAML, но не наоборот.
Так, я потратил суммарно уже целых 6 минут (за два дня), чтобы активировать здесь комментарии к постам... А что, если комментарии можно писать только к новому контенту?!
Не знаю как дела обстоят сейчас, но лет 5-7 назад на собеседованиях было ультра-модно спрашивать про архитектурные шаблоны. Абстрактные фабрики, синглтоны, вот это все. Возможно это и хорошая разминка для ума, но ведь архитектурные шаблоны - не самоцель. Они - средство. А цель - это адекватная архитектура приложения. Не супер-сложная (чтобы коллеги не приходили в ужас от количества реверансов и условностей при чтении/написании кода), не полное ее отсутствие (никто не любит big ball of mud катающийся в тарелке со спагетти), а сбалансированная (т.е. с умеренным использованием абстракций и инкапсуляции), симметричная (все компоненты примерно одного размера, нет god objects и армии анемичных недо-компонентов) и обладающая слабым зацеплением (aka loose coupling, обожаю российскую терминологию) структура программы.

Достигнуть такой адекватной архитектуры помогает следование некоторым принципам. Например, мы должны стараться производить код с loose coupling и high cohesion (нет, даже не буду стараться перевести на русский эти термины). Еще один замечательный свод принципов - это SOLID. Четыре из пяти его постулатов применимы и за пределами объектно-ориентированной парадигмы. Любой компонент системы должен стараться не брать на себя слишком много (single-responsibility principle), быть легко расширяемым без необходимости копаться по локоть в его кишках (open–closed principle), обладать лаконичным и недвусмысленным интерфейсом (interface segregation principle) и не зависеть от конкретных реализаций соседних компонентов (dependency inversion principle). Заметьте, я умышленно не употреблял слово код в прошлом предложении, потому что более высокоуровневые компоненты (микросервисы, очереди задач, API и пр.) также должны удовлетворять этим принципам, чтобы результирующая система оказалась адекватной.

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

Навеяно недавней статьей Кента Бека https://medium.com/@kentbeck_7670/monolith-services-theory-practice-617e4546a879.