Где работают контейнеры: на серверах, виртуальных машинах или в облаке
Что проверяют
Понимаете ли вы, что контейнер это не отдельная машина, а процесс на ядре Linux с изоляцией, поэтому вопрос «где» сводится к «где есть подходящее ядро». Для Java-разработчика следом почти всегда идёт вопрос, как JVM видит лимиты памяти и CPU внутри контейнера.
Как отвечать
Короткий ответ: везде, и эти варианты не исключают друг друга. Контейнер использует ядро хоста, изоляцию дают namespaces, лимиты ресурсов дают cgroups. Дальше по средам:
- физический сервер: рантайм (
containerd, CRI-O, Docker Engine) прямо на железе. Нет накладных расходов гипервизора, но все контейнеры делят одно ядро, и уязвимость в нём задевает всех; - виртуальная машина: самый частый вариант. Ноды Kubernetes в облаке почти всегда ВМ, контейнеры внутри получают второй слой изоляции. Docker Desktop на macOS и Windows тоже запускает лёгкую Linux-ВМ, потому что своего Linux-ядра там нет;
- облако: управляемый Kubernetes (EKS, GKE, AKS, Yandex Managed Service for Kubernetes) или бессерверные контейнеры вроде Cloud Run, AWS Fargate, Yandex Serverless Containers, где ВМ от вас скрыта. Под капотом там часто микро-ВМ вроде Firecracker или песочница gVisor.
Для Java добавьте практическую часть. Современные JDK понимают cgroups: -XX:+UseContainerSupport включён по умолчанию с JDK 10 и бэкпортирован в 8u191, и размер кучи считается от лимита контейнера. По умолчанию MaxRAMPercentage равен 25, поэтому при лимите 1 ГБ куча получится около 256 МБ. Обычно его поднимают примерно до 75, оставляя место под metaspace, стеки потоков и off-heap.
Где спотыкаются
Называют контейнер «лёгкой виртуалкой». Своего ядра у него нет, поэтому он стартует быстро, но изолирован слабее ВМ.
Противопоставляют облако и виртуальные машины, хотя в облаке контейнеры в подавляющем большинстве случаев работают именно на ВМ.
Не знают, как JVM ведёт себя в контейнере: ставят -Xmx равным лимиту пода и получают OOMKilled, потому что память процесса Java не ограничивается кучей.