cat mindflow.txt > /dev/null – Telegram
cat mindflow.txt > /dev/null
180 subscribers
14 photos
87 links
Поток сознания о программировании, технологиях, жизни и вообще
Download Telegram
В рамках погружения в увлекательный мир оркестрации решил посмотреть, зачем же Kubernetes-у зависимость от Docker-а как прослойки для работы с контейнеризацией, когда Docker сам по себе вполне себе оркестратор со своим мертворожденным swarm. И оказывается она вовсе и не нужна, просто так сложилось исторически. Containerd, который отвечает именно за контейнеризацию в докере сгодится в качестве такой прослойки и для Kubernetes. И оказывается там уже несколько лет как существует формальная спецификация на API такой прослойки - Kubernetes CRI. И есть реализация прослойки даже лайтовее, чем containerd, и имя ей cri-o. В общем, решил я самообразования ради сам написать что-то подобное, так что встречайте - conman - the container manager!

https://iximiuz.com/en/posts/conman-the-container-manager-inception/
О моем восприятии Docker-а

Для тех, кто мог подумать, что я, по каким-то причинам, пытаюсь своими постами выставить Docker в негативном свете. Это далеко не так. Docker - это именно тот проект, с которого началась массовая популярность контейнеров. Docker - это исторически первая удачная упаковка разрозненных фич контейнирзации (Linux namespaces, cgroups, etc) в законченный продукт. Вклад Docker в эту область индустрии просто неоценим. Но у этой медали есть и обратная сторона... Я вижу как минимум 2 проблемы и постоянно пытаюсь обратить на них внимание коллег по цеху.

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

Во-вторых, everything out of the box подход к проблеме контейнеризации, принесший докеру такой стремительный первоначальный успех (что может быть проще, чем yum install docker; docker build .; docker run), оказался и его слабой стороной. Docker старается решить проблемы запуска контейнеров (docker run/exec), управления образами (docker build/push/pull), оркестрирования (docker compose/swarm) и пр. Но далеко не все клиенты Docker заинтересованы сразу во всех его возможностях, в то время, как реализация всех этих фич в одном куске софта налагает как архитектурные (большой демон, управляющий другим демоном), так и операционные (демон еще и запущен под root) издержки. К счастью, кроме Docker существуют и друге проекты, решающие как целиком задачу контейнеризации, так и ее отдельные изолированные подзадачи (podman для управления контейнерами; runc для создания и запуска контейнеров; buildah и skopeo для работы с образами; containerd и cri-o как реализации container runtime для Kubernetes; Kubernetes - как оркестратор; в конце концов kata containers как альтернативный подход к изоляции). И, возвращаясь к первому пункту, для того, чтобы осмысленно начать пользоваться всеми преимуществами этих проектов, необходимо четко представлять зоны отвественности каждого из них.

На мой взгляд, самая сильная сторона Docker сейчас в его применимости в процессе разработки. Установить и начать использовать Docker на локальной машине - проще простого (для контраста попробуйте развернуть локальный Kubernetes cluster). Но если создание образов и выполнение контейнеров в Docker не будет совместимо с конечным окружением, где эти контейнеры выполняются в бою, Docker потеряет и свое последнее преимущество. Благо сейчас каждый из аспектов в этой области стараются завернуть в формальную спецификацию (OCI runtime spec, OCI image spec, Kubenetes CRI spec, etc), и Docker принимает активное участие в этих начинаниях.
Практикующий 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, но не наоборот.