Контейнеризация понятным языком: от самых азов до тонкостей работы с Kubernetes
Чем контейнеры отличаются от виртуальных машин, почему Docker настолько популярен, что такое Kubernetes и в чём его преимущества и недостатки. В интервью АйТиБороде СТО «Слёрма» Марсель Ибраев и старший инженер Southbridge Николай Месропян рассказали о контейнеризации понятным языком. Мы перевели интервью в текст для тех, кому лень смотреть.
Мне не лень смотреть, мне лень читать
Разница между контейнеризацией и виртуализацией
Что такое виртуализация?
Виртуализация появилась как средство уплотнения окружений на одном и том же железе. Сначала программный продукт выполнялся на железном сервере. Потом, чтобы иметь возможность поселять в одно и то же железо больше клиентов, чтобы максимально полно утилизировать производительные мощности, придумали виртуализацию. Теперь на одном и том же железе можно держать несколько окружений. В зависимости от среды, опять же. Есть полностью проприетарные решения, такие как vmware vsphere, есть опенсорсные решения, как QEMU KVM, на основе которого Red Hat делает свой коммерческий гипервизор — Red Hat Virtualization. На платформе Windows есть Hyper-V.
Благодаря виртуализации мы получаем возможность более полно утилизировать ресурсы железа. Но при этом речь идёт о полной изоляции: в виртуальной машине полностью изолированное ядро, все библиотеки, строго ограниченные ресурсы по процессору и памяти.
Ядра разделяются физически или можно виртуально разделить там одно физическое ядро на несколько при виртуализации?
Если у вас на хосте один процессор, то в виртуальной машине вы два иметь не можете. Ресурсы хоста можно делить произвольным образом между всеми виртуальными машинами. Либо статично, выделяя под конкретную виртуальную машину один, два, три процессора. Либо динамически, чтобы использовались просто свободные ресурсы в нужное время.
Что такое контейнеры и контейнеризация, и чем отличаются?
Детали зависят от операционной системы, на которой выполняется контейнер, но вообще контейнер делит с хостом ядро, пространство памяти ядра, и своё у контейнера только пользовательское окружение. Первая широко распространенная технология контейнеризации в Linux — это OpenVZ, которая потом превратилась в коммерческий продукт Virtuozzo. Мы много работали и работаем с OpenVZ. У нас клиентские окружения жили в контейнерах OpenVZ, пока мы не перешли на более современные технологии.
То есть контейнер от виртуальной машины отличается только тем, что в контейнере общее адресное пространство?
Нет. Виртуальная машина изолируется полностью средствами процессора (технологии Intel, AMD, VMX).
Контейнер работает на ядре хостовой операционной системы и использует для изоляции возможности не железа, а операционной системы, так называемое пространство имён. Если мы говорим о Docker, как о наиболее распространённой сейчас технологии виртуализации, используются так называемые cgroups в ядре Linux.
Контейнер — это продолжение виртуализации? То есть это технология, которая является преемником виртуализации?
Нет. Они ни в коем случае не конкурируют. Они занимают совершенно разные ниши в использовании.
Тогда почему их постоянно сравнивают? И постоянно есть вопрос, что лучше виртуализация или контейнеризация?
С моей точки зрения сравнить контейнеризацию и виртуализацию нельзя. Это сравнение теплого с мягким.
Где лучше использовать виртуализацию, а где контейнеризацию? Для как разработчика нет разницы: и то, и то используется для развертывания приложений. Два-три приложения ты фигачишь контейнером. Ты можешь виртуальных машин столько создать, и в каждой из них запустить своё приложение. В чем разница для обычных девелоперов?
Для тебя виртуальная машина — это обычная изолированная операционная система, целиком: своё ядро, свой init, systemd и так далее. Чем она отличается от контейнера с точки зрения потребления ресурсов? Тем, что она полностью занимает все ресурсы, под неё выделенные. То есть, есть механизмы, когда можно динамически, то есть в зависимости от потребления процессами внутри виртуальной машины, освобождать память на хосте или занимать её. Но это всё полумеры.
Виртуальная машина — это полностью готовая операционная система. Для человека, который с ней работает изнутри, она вообще ничем не отличается от железного компьютера. С помощью специальных инструментов можно выяснить, мы на железе или на виртуальном окружении, но для любого работающего на ней ПО разницы нет никакой.
Если мы говорим о Docker (а в рамках разговора мы не сможем обсудить все варианты контейнеризации), то он рассчитан на то, что в одном контейнере работает одно приложение.
Возвращаясь к твоему первому вопросу, разница вот в чём. Допустим, если у тебя на хосте Linux или VMware, то виртуальная машина у тебя может быть Windows. Если у тебя в контейнере Linux, то у тебя и снаружи Linux. Потому что мы в первую очередь пользуемся для изоляции не средствами железа, не средствами гипервизора, а средствами операционной системы — cgroups и namespace.
Почему контейнеры разворачиваются быстрее? Потому что они маленькие, содержат в себе там одно приложение? Почему быстрее развернуть контейнер, нежели зафигачить, законфигурировать?
Виртуалка сама по себе большая, так как содержит целую операционную систему. Если нам нужно развернуть виртуальную машину, то нужно нести с собой и ядро, и всё пользовательское окружение, и какой-то запас места (потому что динамически оно с хостом шариться в общем случае не может). Я не видел линуксовую виртуальную машину весом меньше 10 Гб, и это без данных. Потом к ней еще нужно прицепить диски для данных, в зависимости от того, что будет внутри.
Если говорить о контейнерах, есть разные дистрибутивы, в том числе специально созданные для контейнеризации, тот же Alpine Linux, который в голом виде весит 20 или 50 Мб в зависимости от версии. То есть ничего не весит, собственно говоря.
Виртуалка тянет полностью всю операционку, а когда Docker создаешь, ты тянешь только какие-то небольшие пакеты?
Нет. Чтобы создать Docker-контейнер ты должен собрать образ. Ты берёшь какой-то базовый образ, тот же Alpine, CentOS или Ubuntu. В него с помощью специальных команд зашиваешь свое приложение и выгружаешь уже туда, где оно будет работать.
То есть все равно ты в контейнере используешь полноценную операционку? Вот тот же образ Alpine Linux.
Она может быть сильно порезаной по сравнению с операционной системой, которую ты засовываешь в виртуальную машину.
Но потенциально ты можешь и полноценный Linux запустить в контейнере?
Потенциально да, можешь.
Но смысла в этом, наверное, нет.
В этом совершенно нет никакого смысла, потому что хорошей практикой при использовании Docker считается один контейнер — одно приложение.
Один контейнер? А это не слишком жирно использовать для одного приложения, ну пусть и урезанную, но операционную систему?
Когда нужна изоляция — это не слишком жирно.
Понял. Есть ли какие-то еще инструменты, которые позволяют сделать что-то похожее на контейнеризацию, но не контейнеризация?
Контейнеризация сама по себе использует механизмы изоляции, которые предоставляет ядро. Если делать что-то другое, то это тоже получится контейнеризация.
Почему Docker захватил весь рынок? Вот ты говорил, что было решение какое-то изначально в Linux?
Нет. Оно занимало и занимает совершенно другую нишу. Docker захватил весь рынок в первую очередь потому, что он первым начал использовать технологии namespace и cgroups для, так сказать, народа. Понизил порог вхождения в эти технологии до того, чтобы можно было выйти на широкий рынок, на широкого пользователя.
Docker предоставляет общий интерфейс через фактически одну команду к массе возможностей. То есть из единого командного интерфейса мы управляем всеми нюансами создания контейнеров, их запуска, монтирования томов в них, запуска процессов у них — всё что угодно.
А как тут обстоит дело с дебагом твоего кода, логированием и всем остальным? Со стороны кажется, что это сложновато: нужно залезть внутрь какого-то контейнера, который представляет из себя урезанную операционку…
Когда работаешь с контейнерами, в принципе не обязательно думать, что это операционка, не операционка. Там начинается другой мир. Да, к нему надо привыкнуть, но с дебагом, логированием проблем нет никаких, потому что хорошим тоном считается писать все логи в stdout/stderr контейнера, а не в файлики внутри него.
Docker-контейнер знаменит тем, что он одноразовый. Он запустился, а после того, как ты контейнер удаляешь, — если ты специально никаких мер не предпринимал, чтобы сохранить в нём данные, — у тебя всё удаляется. Поэтому все логи обычно пишут в stdout/stderr, средствами Docker или внешних утилит экспортируют их в ElasticSearch, ClickHouse или какие-то другие системы хранения логов и централизованно уже с ними работают. В первую очередь потому, что контейнеров много. Контейнеров в сетапах могут быть десятки, сотни, тысячи и десятки тысяч.
Как правило, они весьма короткоживущие. Если мы сетапим железные сервер или виртуалку, они могут работать годами, то контейнер живёт до обновления образа максимум. Поэтому контейнеров много, они сравнительно короткоживущие, эфемерные, непостоянные. И поэтому всё, что нужно хранить вне них, нужно хранить специальными методами.
Что насчет контейнеризации в Windows? Насколько я помню, там если не всё очень плохо, то не всё так просто, как на Linux.
Там, действительно, очень сложно. Я ни в коем случае не Windows-админ, знаком поверхностно. Но насколько я знаю, нативная контейнеризация в Windows есть. Есть средства изоляции и по ресурсам, и по пространствам имен, сетевые пространства имен, для памяти, под файлы и так далее. То есть можно Windows запустить как контейнер Windows. Это Windows Server Containerization, если я не ошибаюсь (Windows-админы, не обессудьте).
Но если мы говорим о том, чтобы запускать Docker в Windows, то здесь начинаются пляски. Docker — это технология Linux, потому что использует специфические средства для изоляции, для создания контейнеров.
Когда контейнер выполняется, он не представляет собой некий образ. Когда выполняется виртуальная машина — это образ, внутри которого своя файловая система, свои разделы, где всё это нарезано и всё это варится. Когда выполняется контейнер, для операционной системы это просто набор ограничений. Когда мы смотрим на процесс виртуальной машины с хоста, мы видим один процесс. В винде это Microsoft Hyper-V, в Linux это QEMU KVM, в vSphere это тоже один процесс. Когда мы смотрим с хоста на контейнер, то видим дерево процессов.
Но почему мы образы передаем друг другу? Я приложение запаковываю в Docker, и мы девелоперы передаём друг другу образы.
Образ — это то, из чего контейнер запускается. А с точки зрения хоста — это дерево процессов, которые ограничены через встроенные средства ограничения, то есть через namespace и cgroups. Это я к тому, что Docker по своей сути линуксовый.
А почему нельзя было сделать универсальное решение, чтобы оно и для Linux, и для винды работала? Там нет общих API или в Linux есть что-то, чего нет…
Ядро-то разное.
Архитектура разная, да?
Да, API Windows и API Linux — это совершенно разные вещи. По той же причине нет нативного Docker для macOS. Потому что используются средства изоляции линуксового ядра.
Я думал, что ядра macOS и Linux очень похожи.
Нет. macOS больше UNIX-like, нежели Linux. Потому что, как известно GNU — is not UNIX (рекурсивный акроним). А macOS внутри более, так сказать, близка к юниксам. И там нет таких механизмов, как в Linux. Они развиваются независимо.
Docker и для Windows, и для macOS — это чужеродное тело, которое запускается в линуксовой виртуалке.
Получается, чтобы запустить контейнер, нужно запустить еще и виртуалку?
Мы запускаем линуксовую виртуалку, а уже в ней мы уже запускаем эти контейнеры. Docker Desktop скрывает от пользователя все сложные процессы, но внутри все равно остаются всякие. Ну не то, чтобы это очень неэффективно. Если вам нужно разрабатывать что-то под Docker, но у вас только Windows или только macOS, то это позволяет работать, да. Но в продакшене с нагрузками так ничего толком не запустишь.
Я понял, что ты в основном с Linux работаешь, но вдруг ты слышал про WSL (Windows Subsystem for Linux)?
Разумеется, я на OpenNET читаю об этом всём и удивляюсь.
А может ли эта штука запустить контейнеры нативно? Я просто не знаю, она тоже под виртуалкой?
Насколько я понимаю, WSL — это Wine наоборот. То есть трансляция вызовов API в нативные для винды. Если у нас Wine — это трансляция вызовов виндового API для ядра Linux, то WSL — это наоборот. И поэтому средств изоляции там ядерных линуксовых нет. Поэтому увы, увы.
Про оркестрацию
Скажем, у нас микросервисная архитектура, и не одно приложение, а много всего: 10, 20, 40, 100 микросервисов. Руками их конфигурировать совсем не прикольно. Как с этим разбираются?
Да, это вполне типовая ситуация. Сейчас особенно, потому что стильно, модно, молодежно. Постепенно приложение обрастает логикой, микросервисов становится больше и больше. И одного Docker, и даже Docker Compose уже становится мало. Ну и плюс ко всему, наверное, еще хочется какую-то отказоустойчивость, чтобы это на нескольких серверах работало. Возможно, какой-то Service Discovery и прочее.
Постепенно компания утыкается в потолок, когда им нужно свежее и очень продуктивное решение. И здесь, конечно, нужен оркестратор контейнеров. То есть такой тип программного обеспечения, который управляет всеми микросервисами, смотрит за ними, чинит, переносит с машины на машину, строит сеть и в целом является такой входной точкой во всю инфраструктуру проекта.
Марсель Ибраев, СТО «Слёрм»
Docker Compose не позволяет нам ничего делать? Ведь это тоже средство управления несколькими контейнерами.
Docker Compose, как минимум, не позволяет запускать проект на нескольких серверах. То есть это все равно все-таки история про одну ноду. Поэтому, да.
ОК. Что придумано? Что есть сейчас, чтобы это все делать?
Сразу нужно сказать, что инфраструктурный стандарт — это всё-таки Kubernetes. Штука, которая в свое время была произведена в Google. И Google по зову сердца, по доброте своей решил поделиться с миром.
Есть ещё ряд решений, например, Docker Swarm, Mesos, OpenShift и другие, но всё-таки наиболее популярен и пользуется спросом Kubernetes. Компании, которые задумываются о том, что им нужен оркестратор, в первую очередь смотрят на Kubernetes.
Где обычно применяются оркестраторы, в частности Kubernetes?
Да, это очень важный вопрос. Дело в том, что Kubernetes все проблемы не решает. Компания работает, работает, у них всё плохо (как правило, плохо с процессами) они такие: «Блин, Kubernetes классная штука. Давайте её себе поставим — у нас всё сразу станет хорошо!» Но становится сильно хуже.
К Kubernetes нужно подходить уже осознанно. Работу с Kubernetes стоит рассматривать, когда у вас действительно большое количество микросервисов, когда есть определённые требования к уровню доступности вашего сервиса, когда над одной частью приложения трудятся несколько команд, когда нужна инфраструктура для автоматизации деплоя и тестов. На этом этапе да, действительно стоит задуматься о Kubernetes.
Если вы небольшая компания или если у вас монолит, и вы такие: «Сейчас мы его в куб засунем и все станет хорошо!» Нет, не станет.
Kubernetes работает только с контейнерами Docker и с их оркестрацией?
Kubernetes работает с контейнерами, но не только с Docker. У Kubernetes есть такая штука, которая называется Container Runtime Interface. В принципе, все системы контейнеризации, которые сейчас есть и которые поддерживают Container Runtime Interface, могут работать с Kubernetes. Например, rkt.
Сейчас возникло движение энтузиастов, которые выкорчевывают Docker из Kubernetes и используют что-то другое. Потому что Docker тоже не без проблем. Главная проблема Docker — это его демон, который имеет свойство зависать, особенно при большой нагрузке. Но зачем демон, если у нас уже есть Kubernetes, есть достаточно зрелая инфраструктура и нам надо просто какое-то место для запуска контейнеров.
Дополнительный демон по сути не нужен. В эту сторону движение сейчас активно идёт, но, я думаю, дойдёт не быстро. Устоявшееся мнение, что контейнеры равно Docker, будет держаться долго.
А что может быть использовано вместо Docker более оптимальным путем?
Оптимально пока сложно сказать, потому что у конкурентов Docker есть свои минусы. К примеру, containerd не имеет нормального средства управления им. К слову, с версии 1.11, кажется, под капотом Docker containerd и работает. По сути, сейчас Docker выполняет роль обёртки над containerd, а там containerd, а внутри ещё runC, если уж совсем углубляться.
Кто-то говорит про Podman: делайте просто алиас Podman — Docker, и можно сразу работать. Но тоже есть свои нюансы, поэтому мы в том числе пока работаем с Docker.
Расскажи подробнее, как вообще Kubernetes работает? Что у него происходит под капотом? Для начала уточним, Kubernetes — это сервис или это какое-то ПО, которое можно ставить на сервера, или это и то и то?
Ну это ПО, да. И при этом ПО, которое сейчас очень активно развивается и предоставляется облачными провайдерами как сервис. При этом ничто не мешает его поставить на железные серверы.
Всегда нужно держать в голове, что Kubernetes — это в первую очередь оркестратор контейнеров. Когда вы это понимаете, то вы понимаете, для чего он нужен.
Kubernetes состоит из нескольких компонентов, которые выполняют каждый свою роль (подробно о них ещё поговорим). Из этого вытекает две особенности:
- Компоненты друг друга не аффектят. Если один упал, то все остальные могут продолжить работать.
- Эти компоненты работают по pull-модели. Нет никакого центрального компонента, командира, который всем раздаёт команды, а как сдох, так все не знают что делать. Каждый компонент выполняет свою часть работы: сделал, завершил. Если он умер, ну значит этот кусочек не выполнится.
Теперь по поводу самих компонентов. Основной компонент — это API-сервер. Это просто апишка, REST API (разработчики понимают, о чём речь): управление с помощью http-запросов, версионирование кстати тоже. Очень важно, что там есть версия API, при обновлении мы можем на эти версии API завязываться и за счёт этого обновляться менее болезненно. Есть API, с которым работают все: и клиент (мы, как оператор кластера), и компоненты остальные в том числе.
API-сервер работает с хранилищем, которое представляет из себя просто etcd. Etcd — это key-value хранилище, то есть «ключ-значение». Вот и API-сервер — это единственный компонент, который с этим хранилищем взаимодействует.
Это какая-то разработка команды kubernetes?
Нет, это отдельная штука, очень древняя.
А почему её у Redis нет, например?
У Redis есть проблемы с многопоточностью, есть проблемы с кластеризацией (хотя они ее постепенно решают). А etcd штука древняя, все детские болезни там уже вылечены, и она достаточно такая отказоустойчивая.
Кстати, это хороший показатель, что если разработчики Kubernetes уже начиная с первых версий используют etcd, то, наверное, у себя его тоже можно использовать как key-value в кластер-режиме.
API-сервер единственный, кто с etcd работает, он записывает, считывает информацию. А в etcd у нас хранится всё. Там наши настройки кластера, манифесты — всё, что мы делаем.
Мы как клиент хотим что-то создать, запустить приложение, и мы эту информацию передаем в API-сервер. Мы непосредственно это не делаем, конечно, там есть такая утилита, которая называется kubectl. С её помощью мы управляем всем кластером, делаем все операции, в том числе и запускаем приложения. Передаем yaml-манифест, где у нас в декларативном формате описано, как должно выглядеть приложение в кластере. Вот мы это передаем. Оно сохраняется в etcd и следующие компоненты постоянно смотрят в API-сервер.
Если немного углубиться, там есть подписка на событие и они по сути watch’ат. То есть никакого DDoS’а самого себя там нет. Следующий компонент, который берёт эту историю в работу — это kube-controller-manager. По сути, мозг кластера Kubernetes. В него вшиты множество контроллеров: node-controller, endpoint-controller. Практически у всех абстракций, которые есть в Kubernetes, есть контроллер, и он вшит в этот бинарь. Эти контроллеры занимаются просто контролем вот этой абстракции: смотрят, есть ли новые, нужно ли что-то удалить и так далее.
Давай на примере. Если продолжать говорить о приложении, то контроллер, который отвечает за какое-то конкретное приложение, точнее за его манифест, за его абстракцию — он видит, что мы что-то хотим создать, запустить. И он выполняет соответствующую работу, а именно дописывает манифест в etcd, обновляет информацию. Тут, конечно, без некоторого углубления нормально не объяснишь. Но есть такая абстракция, которая называется ReplicaSet. Она позволяет запускать приложение в нескольких инстансах. Через нее мы можем увеличивать, уменьшать количество реплик. И все здорово.
Это балансировка нагрузки?
Это просто контроль за количеством инстансов одного и того же приложения.
А зачем?
Чтобы иметь возможность в случае чего скейлить свое приложение или скейлить обратно. То есть хотим в три инстанса реплики — просто пишем три — у нас три инстанса.
Ну это очень похоже на балансировку нагрузок.
Балансировкой уже занимается другая абстракция, которая уже трафик распределяет на вот эти три инстанса.
То есть они в принципе могут в паре работать?
Да. Они в паре и работают.
ReplicaSet не только создаёт реплики, она ещё и следит, чтобы их действительно было три. Причем не больше, не меньше.
Инстансы, которые запускает ReplicaSet, называются подами. В подах и работает наше приложение (про поды мы ещё поговорим).
И вот как раз, когда мы создаем, например, ReplicaSet, у нас есть такой ReplicaSet controller в этом контроллер-менеджере, который описание подов для ReplicaSet генерирует, и туда же, грубо говоря, в etcd через API-сервер скидывает.
Потом подключается следующий компонент. После того, как мы поняли, какое приложение нам нужно из скольких инстансов запускать, оно вот в etcd хранится этот манифест. Далее у нас идет такой компонент, который называется scheduler. Его роль достаточно проста. Он решает, на каких серверах это приложение надо запускать. Но делает это не просто так, у него есть свои алгоритмы принятия решения.
Ну в частности, он смотрит на ресурсы, то есть сколько ресурсов на ноде, если мы для этого приложения запрашиваем 1 ГБ ОЗУ, а на ноде только 512 свободны, он туда не отправляет.
Под приложением ты понимаешь Docker-контейнер с приложением?
Да, контейнер.
Контейнер с приложением каким-то.
Да.
Технологии scheduler«а несколько сложнее, если будет интересно, то можем туда углубиться. В целом, у него есть некоторый ряд алгоритмов, он выставляет очки каждой ноде и условно та нода, которая больше очков набрала, туда приложение и уходит на запуск.
Это сказывается на стабильности работы системы в целом? Правильно я понимаю, если у нас есть какой-то нестабильный сервак, который может там валиться очень часто, то у него будет меньше очков.
На стабильность он не смотрит. Он смотрит в первую очередь на ресурсы. Какой смысл отправлять приложение на запуск туда, где их недостаточно. Смотрит ещё на priority class — это такая штука, с помощью которой мы можем задать приоритет.
Например, в кластере два окружения: продакшн и стейджинг. Конечно, продакшн более важен. И для них мы priority class выставляем высокий. Для стейджинга мы можем поставить поменьше. Когда происходит авария, Kubernetes понимает, что часть серверов отвалилась (за это будет отвечать Node Controller, который контролирует жизнь нод), он принимает решение, что надо те поды, которые там были, запустить в живых серверах. Scheduler будет запускать в первую очередь поды продакшена.
Интересно, что если поды продакшена не лезут, то поды стейджинга будут убиваться и на место их будут запускаться поды продакшена.
А если не хватит под продакшн места?
Если не хватит, ну сорян. Поды будут висеть в pending, то есть ждать, когда появятся ресурсы. И scheduler назначает… Если на такой низкий уровень опуститься, то в манифесте пода есть специальное поле, которое называется nodeName — имя ноды. И вот пока scheduler не принял решение — оно пустое. Scheduler говорит, что вот этот под, вот это приложение нужно запускать там на Node №2, и он эту информацию передает, API-сервер это записывает в etcd и в это поле вносит это имя. А далее в работу вступает последний компонент всей этой схемы, который называется kubelet.
Kubelet — это компонент своего рода «node agent», то есть агент, который запущен на всех серверах кластера. Он тоже постоянно в API-сервер смотрит. И он видит, что появился под, то есть приложение, у которого в поле «имя сервера» написано его имя, там, где он работает. Он такой: «Ага! Значит его нужно у себя запустить!» Он видит, что у него запущено, и что от него хотят. Он передает Docker API, из манифеста считывает, что там конкретно нужно запустить, и говорит Docker, какой контейнер нужно запустить.
Kubelet, получается, замена Docker демона?
Вот в том то и дело, что не замена, к сожалению. Поэтому вот эти альтернативы и изобретаются, поэтому туда люди идут, потому что Docker демон висит. Но по сути да, он общается с Docker демоном по API, но без него вполне можно было обойтись. Причем он не просто их запускает, он постоянно смотрит за статусом, и статус этот передаёт в API-сервер.
Хелс-чек такой?
В том числе хелс-чек тоже делает Kubelet. И мы постоянно видим, какой статус у наших приложений в реальном времени. Вот то, что там сейчас пулинг образа идёт, что там сейчас она запускается, вот он на раннинг — все хорошо, все запустилось. И вот только на этом этапе у нас физический запуск произошёл. То есть всё вот это — это подготовка.
Ноды — это всегда сервер или это может быть кластер серверов?
Ноды — это место, где запущен Kubelet.
Это может быть виртуалка, как я понимаю?
Да, может быть виртуалка.
Ты сказал, что в результате этих действий мы получаем физически развёрнутое приложение. Kubelet посылает какие-то свои статусы, либо он просто stdout контейнера фигачит? К чему этот вопрос. Потому что, если у тебя приложение в stdout выдает логи, какой-то дебаг kubernetes как-то умеет это в одно место собирать и предоставлять в удобочитаемом виде, или это не его обязанность вообще?
В твоем вопросе, два вопроса скрыты. Статус самого контейнера (жив или не жив) — берёт из Docker. Функционал приложения (работает ли оно) — вот эти дебаг, логи, какие-то хелс-чеки — это все тоже делает Kubelet, но для этого надо несколько строчек в манифест добавить и сказать, как именно проверять.
На данный момент поддерживается три возможности проверять приложение:
- http-get — это http-запрос в контейнер на инпоинт, и мы видим, работает оно, не работает, отвечает, не отвечает. С 200 по 399 код — это ок, если 301 даже редирект — это ок. Если 404 — это не ок. 500 тем более.
- exec — мы внутрь контейнера делаем какой-то запрос, какую-то команду, проваливаемся. Например, select 1, проверяем, всё ли нормально с базой.
- tcp socket — Kubelet просто проверяет доступные сокеты. Если все хорошо, то все хорошо.
Есть три типа проверки контейнеров: это liveness, readiness и startup пробы.
Liveness проба — это контроль за жизнью приложения. Постоянно Kubelet смотрит, ходит и смотрит. Там гибкие настройки, можно написать, как часто ходить, как проверять и так далее.
Readiness проба — проверяет, а готово ли приложение принимать трафик. Потому что разные истории могут быть, это могут быть разные инпоинты у приложения. Мы проверяем, работает ли приложение, готово ли оно принимать трафик.
Startup проба — это больше для легаси таких историй, которые очень долго поднимаются, молотятся в самом начале. То есть очень долго инициализируются. И startup проба проверяет, запустилось ли вообще приложение.
На каком размере архитектуры может работать Kubernetes? Может ли он контролировать сразу миллион инстансов?
Насколько я помню из документации, это 5000+, что ли, нод. На одной ноде по умолчанию можно запустить 110 подов, 110 экземпляров приложения.
Под — это экземпляр одного и того же приложения? Или могут быть два разных контейнера на одном серваке и это будет два разных пода?
Под — это абстракция, в которой запускается приложение. Тут важно понять, что это не какая-то физическая оболочка, не какой-то процесс ещё один, это скорее именно абстракция, с которой работает Kubernetes.
Kubernetes не умеет работать с контейнерами. Мы не можем сказать: «заскейль нам вот это приложение в трёх контейнерах». Мы можем только сказать: «заскейль нам в трех подах». В поде может быть как один контейнер, так и несколько. То есть мы можем туда запихнуть, например, nginx и php-fpm в связке, и они будут скейлится по два контейнера в связке.
Но тут надо понимать, что хорошая практика — засовывать в контейнер неделимые части приложения. Всё-таки, если 2–3 контейнера надо засовывать, то может, стоит ещё поиграться с логикой приложения. Обычно один контейнер засовывают и там есть еще второй, который сам запускается, это pause контейнер, который держит сетевой namespace, чтобы все контейнеры в поде были в одном namespace, и чтобы всё хорошо работало.
Это первая часть беседы. Во второй будет про хранение данных в Kubernetes и про Ansible. Не пропустите!
Вопросы задавал Лекс АйТиБорода iamitbeard