Продолжение предыдущей статьи - добавляем поддержку async/await в наш самописный event loop https://micromind.me/en/posts/from-callback-hell-to-async-await-heaven/?utm_medium=social&utm_source=tchannel
Iximiuz
Explaining async/await in 200 lines of code - Ivan Velichko
What are the callback alternatives? From callback to promises. From promises to async/await. How to implement async/await with generators.
У Python очень богатая стандартная библиотека. Собрал в одном месте десяток способов запустить TCP и/или HTTP сервер с использованием только стандартных средств.
https://micromind.me/ru/posts/over-9000-ways-to-make-web-server-in-python/?utm_medium=social&utm_source=tchannel
https://micromind.me/ru/posts/over-9000-ways-to-make-web-server-in-python/?utm_medium=social&utm_source=tchannel
micromind.me
9001 способ создать веб-сервер на Python
Дополняемый список способов запустить веб-сервер на Python: socketserver, http.server, asyncio, wsgiref, etc...
Хоть практической пользы в этом видео наверное и нет, это интервью просто обязаны посмотреть все, кто имеет хоть какое-то отношение к программированию. Кен Томпсон: "К счастью, моя жена в тот момент уехала в трехнедельный отпуск с нашим годовалым ребенком, и - неделя, неделя, еще неделя - и получился Unix" https://youtu.be/EY6q5dv_B-o?t=1357
YouTube
Ken Thompson interviewed by Brian Kernighan at VCF East 2019
In the 1960s-1970s, Ken Thompson co-invented the UNIX operating system along with Dennis Ritchie at Bell Labs. He also worked on the language B, the operating system Plan 9, and the language Go. He and Ritchie won the Turing Award. He now works at Google.…
Как написать свой HTTP-сервер с нуля https://micromind.me/ru/posts/writing-python-web-server-part-3/?utm_medium=social&utm_source=tchannel. Продолжение серии статей про разработку веб-сервера на Python.
Iximiuz
Пишем свой веб-сервер на Python: протокол HTTP - Ivan Velichko
В статье рассматривается пошаговая реализация простого HTTP/1.1 сервера на Python
Минутка занимательной этимологии. Возможно, это оказалось новостью только для меня, но глагол "to boot" в компьютерном мире означает далеко не просто "загружать что-либо". Достаточно взглянуть на список значений - увольнять, надевать ботинки, ударить сапогом, помогать https://translate.google.com/#view=home&op=translate&sl=en&tl=ru&text=boot. В компьютерный же мир это слово пришло скорее из более длинной его формы "bootstrap", имеющей в настоящее время значение "get (oneself or something) into or out of a situation using existing resources". То есть справиться с задачей исключительно своими собственными ресурсами, без внешнего воздействия, а в компьютерном мире - ввода дополнительных данных. Вероятно, все началось пару сотен лет назад с гиперболы "pull oneself over a fence by one's bootstraps" (https://en.wikipedia.org/wiki/Bootstrapping), означающей "перебраться через изгородь, потянув самого себя за задники ботинок (bootstrap-ы)", американского варианта подвига барона Мюнхгаузена, вытянувшего себя из болота за свои же волосы.
Google
Google Translate
Google's service, offered free of charge, instantly translates words, phrases, and web pages between English and over 100 other languages.
Docker несколько лет был для меня не более, чем инструментом, чёрным ящиком с чёрной магией внутри, позволяющим запустить своё приложение в воспроизводимой среде. А на самом деле, стоило всего лишь потыкать в него палочкой, чтобы осознать, что, например, image - это не более, чем папка с файлами, обычно с корневой файловой системой какой-нибудь версии Linux.
Аналогичная история с Linux OS. Какая часть сервера - это Linux kernel, а какая - userland? Как происходит загрузка ОС? И опять же, просто нужно было собрать свой дистрибутив и запустить его на виртуальной машине.
https://micromind.me/en/posts/from-docker-container-to-bootable-linux-disk-image/?utm_medium=social&utm_source=tchannel
Аналогичная история с Linux OS. Какая часть сервера - это Linux kernel, а какая - userland? Как происходит загрузка ОС? И опять же, просто нужно было собрать свой дистрибутив и запустить его на виртуальной машине.
https://micromind.me/en/posts/from-docker-container-to-bootable-linux-disk-image/?utm_medium=social&utm_source=tchannel
Используешь себе какой-нибудь линуксовый тул каждый день и беды не знаешь. Но вдруг накрывает, и становится очень интересно, как он работает под капотом. Вот сегодня, например, настала очередь
Вот так за 5 секунд мы узнали, что пора читать про AF_NETLINK сокеты https://en.wikipedia.org/wiki/Netlink.
ip из iproute2. Стандартно полез гуглить исходники и уж было начал их курить... В общем, не знаю, почему мне пришло это в голову только сейчас, но... Куда быстрее просто обернуть вызов ip в strace:strace -e trace=network,file ip addr
execve("/usr/sbin/ip", ["ip", "addr"], [/* 35 vars */]) = 0
...
socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE) = 3
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [32768], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [1048576], 4) = 0
setsockopt(3, SOL_NETLINK, 11, [1], 4) = -1 ENOPROTOOPT (Protocol not available)
bind(3, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0
getsockname(3, {sa_family=AF_NETLINK, pid=4727, groups=00000000}, [12]) = 0
sendto(3, ..., 40, 0, NULL, 0) = 40
recvmsg(3, ..., MSG_PEEK|MSG_TRUNC) = 3752
...
open("/etc/iproute2/group", O_RDONLY) = 4
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:26:10:60 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0
valid_lft 76662sec preferred_lft 76662sec
inet6 fe80::5054:ff:fe26:1060/64 scope link
valid_lft forever preferred_lft forever
Вот так за 5 секунд мы узнали, что пора читать про AF_NETLINK сокеты https://en.wikipedia.org/wiki/Netlink.
Откуда есть пошла iptables заморская https://iximiuz.com/en/posts/laymans-iptables-101/?utm_medium=social&utm_source=tchannel #netfilter #linux #iptables #ip
Iximiuz
Illustrated introduction to Linux iptables
What are iptables chains, rules, policies, and tables? Describe iptables in layman's terms.
Что такое "операционная система Linux"? Это ядро (Linux kernel, бинарник в несколько мегабайт, хитрая прослойка между железом и софтом) и пользовательское пространство (user space, десяки или сотни мегабайт разных бинарников утилит, помогающих работать с ядром). Ядро всегда одно, отличается лишь версиями, а пользовательских пространств много (Debian, Ubuntu, CentOS, Alpine, busybox, кастомные сборки и т.п.).
Что такое "контейнеризация"? Это способ виртуализации на уровне операционной системы. В частности, это когда на запущенной Linux ОС можно положить разные пользовательские пространства в разные папки и потом запустить их выполняться. В качестве ядра будет использовано ядро родительской ОС, но программы, выполняющиеся в разных контейнерах, будут думать, что каждая из них имеет свою собственную ОСь вокруг. Изменения, сделанные в рамках одного контейнера, могут быть скрыты от остальных контейнеров и родительской ОС. Как это работает? Ядро Linux поддерживает т.н. namespaces, то есть способ изоляции ресурсов. Например, Process ID namespace позволяет иметь несколько деревьев процессов, начинающихся каждое со своего PID 1 и все это в рамках одного и того же экземпляра выполняющегося ядра. Также существуют network, mount, user ID и прочите изолированные пространства. Системный вызов clone(), являющийся основой канонического fork() принимает флаги, позволяющие изолировать новый запускаемый процесс в различных пространствах. Зачем это нужно? Здорово уметь запускать на одной железке как можно больше изолированных окружений, это круто повышает утилизацию ресурсов (альтернативный подход - запускать полностью излоированные ОСи на одной и той же железке - обычно менее эффективен [см. kata containers], правда и более безопасен).
Что такое Docker? Когда-то давно Docker был большим демоном-монолитом (и клиентом, со всеми знакомым нам CLI API), решающим комплексную задачу запуска и обслуживания контейнеров. Что же именно это значит? Когда на диске уже есть папка с набором файлов какого-либо user space, остается лишь создать нужные namespace и запустить бинарник, указанный как entry point в качестве процесса с PID 1. Но после запуска необходимо мониторить состояние контейнера, возможно перезапускать его при падении, очищать ресусры при остановке и т.п. Папку с user space на диске тоже нужно создать умело. Многие контейнеры используют на 100% схожий user space, отличаясь лишь полезной нагрузкой (кто-то хочет запускать nginx в debian:latest, кто-то свой node.js app но тоже в debian:latest и т.п.). Так как речь идет о десятках и сотнях мегабайт, неплохо бы уметь реиспользовать повторяющиеся части. Я могу выделить следующие основные куски Docker-а: 1) обслуживание жизненного цикла контейнеров (запуск, мониторинг состояния, очистка ресурсов после остановки) 2) работа с образами (images), т.е. жонглирование всеми этими большими папками, в частности загрузка их из реестров по сети 3) мощный API для запуска и запроса состояния контейнеров и образов. Так как каждый из этапов вполне себе самодостаточный, а релизить все синхронно - сложно, в какой-то момент Docker решили распилить на runc (запускатор контейнеров, консольная утилита, один запус == один контейнер), containerd (демон, делает все, чтобы runc мог запускать новые контейнеры, в частности работу с образами) и dockerd ("лицевой" демон, общающийся с containerd, чтобы мы могли исползьовать вот эти все удобные плюшки Docker-а в командной строке, через docker client). С момента распила каждый из проектов живет своей отдельной жизнью. Например, runc используется и другими менеджерами контейнеров (см. cri-o), а его интерфейс стандартизирован и он является канонической реалзиацией Container Runtime Specification.
Что такое "контейнеризация"? Это способ виртуализации на уровне операционной системы. В частности, это когда на запущенной Linux ОС можно положить разные пользовательские пространства в разные папки и потом запустить их выполняться. В качестве ядра будет использовано ядро родительской ОС, но программы, выполняющиеся в разных контейнерах, будут думать, что каждая из них имеет свою собственную ОСь вокруг. Изменения, сделанные в рамках одного контейнера, могут быть скрыты от остальных контейнеров и родительской ОС. Как это работает? Ядро Linux поддерживает т.н. namespaces, то есть способ изоляции ресурсов. Например, Process ID namespace позволяет иметь несколько деревьев процессов, начинающихся каждое со своего PID 1 и все это в рамках одного и того же экземпляра выполняющегося ядра. Также существуют network, mount, user ID и прочите изолированные пространства. Системный вызов clone(), являющийся основой канонического fork() принимает флаги, позволяющие изолировать новый запускаемый процесс в различных пространствах. Зачем это нужно? Здорово уметь запускать на одной железке как можно больше изолированных окружений, это круто повышает утилизацию ресурсов (альтернативный подход - запускать полностью излоированные ОСи на одной и той же железке - обычно менее эффективен [см. kata containers], правда и более безопасен).
Что такое Docker? Когда-то давно Docker был большим демоном-монолитом (и клиентом, со всеми знакомым нам CLI API), решающим комплексную задачу запуска и обслуживания контейнеров. Что же именно это значит? Когда на диске уже есть папка с набором файлов какого-либо user space, остается лишь создать нужные namespace и запустить бинарник, указанный как entry point в качестве процесса с PID 1. Но после запуска необходимо мониторить состояние контейнера, возможно перезапускать его при падении, очищать ресусры при остановке и т.п. Папку с user space на диске тоже нужно создать умело. Многие контейнеры используют на 100% схожий user space, отличаясь лишь полезной нагрузкой (кто-то хочет запускать nginx в debian:latest, кто-то свой node.js app но тоже в debian:latest и т.п.). Так как речь идет о десятках и сотнях мегабайт, неплохо бы уметь реиспользовать повторяющиеся части. Я могу выделить следующие основные куски Docker-а: 1) обслуживание жизненного цикла контейнеров (запуск, мониторинг состояния, очистка ресурсов после остановки) 2) работа с образами (images), т.е. жонглирование всеми этими большими папками, в частности загрузка их из реестров по сети 3) мощный API для запуска и запроса состояния контейнеров и образов. Так как каждый из этапов вполне себе самодостаточный, а релизить все синхронно - сложно, в какой-то момент Docker решили распилить на runc (запускатор контейнеров, консольная утилита, один запус == один контейнер), containerd (демон, делает все, чтобы runc мог запускать новые контейнеры, в частности работу с образами) и dockerd ("лицевой" демон, общающийся с containerd, чтобы мы могли исползьовать вот эти все удобные плюшки Docker-а в командной строке, через docker client). С момента распила каждый из проектов живет своей отдельной жизнью. Например, runc используется и другими менеджерами контейнеров (см. cri-o), а его интерфейс стандартизирован и он является канонической реалзиацией Container Runtime Specification.
Почему даже после распила на части Docker - все еще монолит? Одна из причин в том, что containerd - это демон, да еще и требующий root (ибо он запускает runc, которому нужен root для создания namespaces). Но работа с образами не требует ни демона ни тем более root. Однако, в случае Docker альтернативы (пока?) нет. Если хочется скачать какой-либо image из сети или собрать свой - придется запускать containerd. К счастью, ребятам из Red Hat это не понравилось и они решили запилить свой тулсет контейнеризации с блекджеком и барышнями. Так, запустить контейнер, используя докеро-подобный CLI можно утилитой podman (https://github.com/containers/libpod), которая под капотом использует опять же runc. Собрать свой образ или модифицировать существующий можно утилитой buildah (https://github.com/containers/buildah), которая под капотом испоьзует крутую либу https://github.com/containers/storage, которую также, например, использует демон cri-o, реализующий Kubernetes Container Runtime Interface (CRI). На мой взгляд, альтернативная (от Docker-вселенной) реализация Red Hat-ом всех этих контейнерных дел получилась куда более гибкой и структурированной. Но Docker все еще очень сильный игрок, благодаря исторически сформировавшемуся полчищу конечных пользователей.
GitHub
GitHub - containers/podman: Podman: A tool for managing OCI containers and pods.
Podman: A tool for managing OCI containers and pods. - containers/podman
Подводя итог нескольких месяцев, проведенных за изучением мира контейнеров и их оркестрирования, написал обзорную статью про экосистему контейнеризации, попробовав структурировать и описать взаимосвязи наиболее заметных проектов в области (OCI specs, runc, containerd, moby, cri-o, podman, etc) https://iximiuz.com/en/posts/journey-from-containerization-to-orchestration-and-beyond/?utm_medium=social&utm_source=tchannel
Iximiuz
Journey From Containerization To Orchestration And Beyond
What's the difference between dockerd and containerd? Why Kubernetes CRI was introduced? What is a container runtime?
Кратенько о том, как Perl и PHP не успевают за реалиями серверной разработки.
Оба языка используют схожий подход к обработке HTTP запросов. Являясь по своей натуре однопоточными (как с точки зрения реализации интерпретатора, так и из-за отсутствия примитивов синхронизации вроде mutex-ов в спецификации языков), они вынуждены назначать выделенный процесс на каждый входящий HTTP-запрос. Таким образом, для обработки 100 одновременных HTTP-запросов на одной машине необходимо иметь 100 процессов-воркеров с запущенным интерпретатором PHP или Perl. 101й запрос будет вынужден находиться в очереди на обработку, пока один из 100 активных запросов не будет завершен. Каждый процесс-воркер может занимать от сотни мегабайт до нескольких гигабайт RAM в resident set size (!), и это без учета объема обрабатываемых в запросе данных. Таким образом, если на гипотетическом сервере у нас 8 ядер и 16 GB RAM, получится запустить в среднем 16-32 воркеров (при memory utilization стремящейся к 100%, что в реальном мире недопустимо), т.е. иметь 16-32 одновременных запроса.
Оба языка используют схожий подход к обработке HTTP запросов. Являясь по своей натуре однопоточными (как с точки зрения реализации интерпретатора, так и из-за отсутствия примитивов синхронизации вроде mutex-ов в спецификации языков), они вынуждены назначать выделенный процесс на каждый входящий HTTP-запрос. Таким образом, для обработки 100 одновременных HTTP-запросов на одной машине необходимо иметь 100 процессов-воркеров с запущенным интерпретатором PHP или Perl. 101й запрос будет вынужден находиться в очереди на обработку, пока один из 100 активных запросов не будет завершен. Каждый процесс-воркер может занимать от сотни мегабайт до нескольких гигабайт RAM в resident set size (!), и это без учета объема обрабатываемых в запросе данных. Таким образом, если на гипотетическом сервере у нас 8 ядер и 16 GB RAM, получится запустить в среднем 16-32 воркеров (при memory utilization стремящейся к 100%, что в реальном мире недопустимо), т.е. иметь 16-32 одновременных запроса.
В мире микросервисов и сторонних API редкий обработчик HTTP запроса обходится без десятка [под-]запросов к соседним сервисам. Это, очевидно, заставляет воркеры ожидать I/O, вместо того, чтобы заниматься непосредственно обработкой (т.е. считать что-то на CPU). Да, сетевое взаимодействие в PHP и Perl можно сделать асинхронным (в Perl даже есть промисы), но, в общем случае, эти [под-]запросы могут иметь зависимость по данным, делающую невозможным их конкурентную отправку. Таким образом, процессы-воркеры будут значительную часть времени находиться в ожидании ответов от сторонних сервисов. Что приводит, к неприятной ситуации - overutilization of RAM and underutilization of CPU.
Для сравнения, языки с нативной поддержкой асинхронной обработки запросов (JS/NodeJS асинхронный до мозга костей, Go обычно выделяет по горутине на процесс, в Python есть asynio или gevent-подобные штуки) не требуют оверхеда в виде выделенного процесса на каждый HTTP-запрос, делая возможным иметь тысячи одновременных HTTP-запросов на одной машине. И в конечном итоге позволяют иметь более сбалансированную утилизацию ресурсов сервера.
Про аккуратность и запуск новых процессов в Linux
Когда запускается новый процесс, его потоки ввода/вывода STD(IN|OUT|ERR) определяются родительским процессом. Например, делая
Когда мы запускаем новый процесс программно (fork + exec), мы можем указать его STD(IN|OUT|ERR) файловые дескрипторы (`man dup`). Но по умолчанию форкнутый процесс просто наследует потоки родителя.
Когда мы запускаем новый процесс программно (fork + exec) и хотим дождаться его завершения, нам всего лишь нужно вызвать
Когда мы хотим прочитать то, что запущенный нами процесс печатает в свой STDOUT, можно создать pipe (`man pipe`) и указать его в качестве соответствующего файлового дескриптора для нового процесса, а затем начать читать из этого pipe в родительском процессе.
Когда процесс форкает другой процесс, а затем сам завершается, потоки, унаследованне форком не будут закрыты.
А теперь представьте ситуацию - мы запускаем (fork + exec) процесс (назовем его стартер), который в свою очередь запускает новый процесс (назовем его демон), а сам завершается печатая статус на экран. И в нашем коде мы хотим а) дождаться завершения стартера б) прочитать статус из его STDOUT.
Оказывается, если стартер-процесс не переназначил STD(IN|OUT|ERR) демона,
Вывод - стартеры должны быть умными =) А самое смешное, что вездесущий runc (https://github.com/opencontainers/runc) - пример неумного стартера. Пруф https://github.com/cri-o/cri-o/blob/8a43af20119ad8fe1ffebb4128d8938134eaaeb1/conmon/conmon.c#L1336-L1342. Это умный стартер неумного стартера.
Когда запускается новый процесс, его потоки ввода/вывода STD(IN|OUT|ERR) определяются родительским процессом. Например, делая
cat - в консоли, родительским процессом будет командный интерпретатор (`bash`, zsh, etc), а его (интерпретатора) STD(IN|OUT|ERR) уже связаны с терминалом. cat же просто унаследует тот же набор.Когда мы запускаем новый процесс программно (fork + exec), мы можем указать его STD(IN|OUT|ERR) файловые дескрипторы (`man dup`). Но по умолчанию форкнутый процесс просто наследует потоки родителя.
Когда мы запускаем новый процесс программно (fork + exec) и хотим дождаться его завершения, нам всего лишь нужно вызвать
waitid() (`man waitpid`) в родительском процессе.Когда мы хотим прочитать то, что запущенный нами процесс печатает в свой STDOUT, можно создать pipe (`man pipe`) и указать его в качестве соответствующего файлового дескриптора для нового процесса, а затем начать читать из этого pipe в родительском процессе.
Когда процесс форкает другой процесс, а затем сам завершается, потоки, унаследованне форком не будут закрыты.
А теперь представьте ситуацию - мы запускаем (fork + exec) процесс (назовем его стартер), который в свою очередь запускает новый процесс (назовем его демон), а сам завершается печатая статус на экран. И в нашем коде мы хотим а) дождаться завершения стартера б) прочитать статус из его STDOUT.
Оказывается, если стартер-процесс не переназначил STD(IN|OUT|ERR) демона,
waitpid() нам благополучно сообщит, что стартер завершился, но вот блокирующая операция чтение из pipe на нашей стороне приведет к зависанию нашего процесса до окончания выполнения (в общем случае долгоживущего) демона. И это вряд ли то, что нам нужно. Фактически, демон наследует STDOUT стартера и, несмотря на то, что стартер уже давно завершился, файловый дескриптор останется открытым в течение всей жизни демона.Вывод - стартеры должны быть умными =) А самое смешное, что вездесущий runc (https://github.com/opencontainers/runc) - пример неумного стартера. Пруф https://github.com/cri-o/cri-o/blob/8a43af20119ad8fe1ffebb4128d8938134eaaeb1/conmon/conmon.c#L1336-L1342. Это умный стартер неумного стартера.
DevOps можно сравнивать с Agile. DevOps определяет не конкретные техники, а скорее общую философию, что разработка (Dev) и эксплуатация (Ops) должны быть тесно связаны. Но не говорит, как. SRE же уместно сравнивать с конкретной Agile методологией, такой как scrum или kanban. SRE отвечает на вопрос как именно практиковать DevOps культуру в отдельно взятой компании. Вспоминается каламбур
Более того, раз уж мы упомянули Agile, то на мой взгляд, DevOps зачастую конфликтует с конкретными реализациями последнего. Если у вас в спринте (или на kanban доске) нет задач по Ops, но при этом кандидатам на собеседовании вы заявляете, что практикуете DevOps - где-то что-то не сходится. Ops должен быть представлен как равноправный аспект ежедневной работы.
https://twitter.com/iximiuz/status/1181177923610513409?s=09
class SRE implements DevOps от Google.Более того, раз уж мы упомянули Agile, то на мой взгляд, DevOps зачастую конфликтует с конкретными реализациями последнего. Если у вас в спринте (или на kanban доске) нет задач по Ops, но при этом кандидатам на собеседовании вы заявляете, что практикуете DevOps - где-то что-то не сходится. Ops должен быть представлен как равноправный аспект ежедневной работы.
https://twitter.com/iximiuz/status/1181177923610513409?s=09
Twitter
Ivan Velichko
Taxonomically, DevOps is like Agile, and SRE is like scrum (or kanban). By the way, if your Agile implementation focuses solely on development and you claim to have a DevOps culture as well, you are doing something wrong. #DevOps #SRE #agile
В рамках погружения в увлекательный мир оркестрации решил посмотреть, зачем же 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/
https://iximiuz.com/en/posts/conman-the-container-manager-inception/
Iximiuz
conman - [the] Container Manager: Inception
dockerd vs. containerd. What is a container manager and what is a container runtime? Learn the difference with a deep dive into a container manager implementation.
О моем восприятии 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 принимает активное участие в этих начинаниях.
Для тех, кто мог подумать, что я, по каким-то причинам, пытаюсь своими постами выставить 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
Specialist подразумевает постоянное углубление знаний в какой-либо одной предметной области, технологии или языке. Если вам нужно знать, как выжать максимальную производительность из Кассандры, вы идете именно к специалисту.
Generalist же скорее должен постоянно повышать свой технический кругозор и делать упор на обобщении и применении общих инженерных практик в различных областях, задачах и технологиях. Применимость тут разная, например, в стартапах, где нужно уметь все и сразу.
Мне по душе именно обобщение, так как я всегда склонен подмечать одинаковые паттерны, какую бы задачу и на каком стеке я ни решал, и переносить известные решения на новые предметные области.
Как известно, каждой задаче - свой инструмент. Всегда можно забивать гвозди микроскопом, но редко это будет эффективно. То же справедливо и для программистских задач. А у generalist-а их тьма и все они разные. Поэтому я долго думал над минимальным набором языков, эффективным как с точки зрения затрат на его изучение, так и количества областей, которые он будет покрывать. И, после почти 10 лет в индустрии, мой список выглядит так: Python, Go, C/Rust, JavaScript/TypeScript.
https://twitter.com/iximiuz/status/1191622134646411267?s=09
Twitter
Ivan Velichko
My choice of programming languages: - Python: noscripting, prototyping, data analysis - Go: server side, tools, containerization and alike - C: reading systems stuff, playing with OS capabilities - Rust: systems programming, performance-critical tools - Ja…
Mirantis купили Docker Enterprise и теперь Swarm официально мертв. Хотя пару лет поддержки для существующих клиентов, пока все не переедут на Kubernetes, все ещё обещают.
https://thenewstack.io/mirantis-acquires-docker-enterprise/
https://thenewstack.io/mirantis-acquires-docker-enterprise/
The New Stack
Mirantis Acquires Docker Enterprise
Mirantis, the cloud consulting company with OpenStack roots and a more recent focus on Kubernetes, has acquired Docker’s enterprise business,
Что можно узнать, построив flamegraph на основе вывода cloc для кодовой базы Kubernetes https://iximiuz.com/en/posts/kubernetes-repository-on-flame/. Всего за 5 минут, без регистрации и смс.
Iximiuz
Kubernetes Repository On Flame
Analyze Kubernetes codebase using cloc and FlameGraph