Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Материал переведен. Ссылка на оригинальную статью
Вам поручили спроектировать инфраструктуру на основе контейнеров? И вы, скорее всего, понимаете, какую пользу могут принести контейнеры разработчикам, архитекторам и командам эксплуатации. Вы уже что-то читали о них и с нетерпением ждете возможности более подробно изучить эту технологию. Однако перед погружением в обсуждение архитектуры и развертывание контейнеров в продакшн-окружении необходимо знать три важные вещи:
Все приложения, включая контейнерные, используют ядро базовой ОС.
Ядро предоставляет приложениям API через системные вызовы (system calls).
Важны версии этого API, так как это "клей", который обеспечивает детерминированное взаимодействие между пространством пользователя (user space) и пространством ядра (kernel space).
Иногда контейнеры рассматриваются как виртуальные машины, но важно отметить, что, в отличие от виртуальных машин, ядро является единственным слоем абстракции между программами и ресурсами, к которым необходим доступ. Давайте посмотрим почему.
Все процессы выполняют системные вызовы (system calls):
А поскольку контейнеры тоже являются процессами, они также выполняют системные вызовы:
Итак, у нас есть понимание, что такое процесс, и что контейнеры — это тоже процессы. Но что насчет файлов и программ, находящихся внутри контейнера? Эти файлы и программы находятся в так называемом пользовательском пространстве (user space). При старте контейнера в память загружается программа из образа контейнера. Но программе, запущенной в контейнере, по-прежнему необходимо выполнять системные вызовы в пространстве ядра (kernel space). Важна возможность детерминированного взаимодействия пользовательского пространства и пространства ядра.
Пользовательское пространство
Под пользовательским пространством понимается весь код операционной системы, который находится вне ядра. Большинство Unix-подобных операционных систем (включая Linux) поставляются с разнообразными предустановленными утилитами, средствами разработки и графическими инструментами — это все приложения пространства пользователя.
Приложения могут быть написаны на C, Java, Python, Ruby и других языках программирования. В мире контейнеров эти программы обычно поставляются в формате образа контейнера, такого как Docker. Когда вы запускаете в контейнере образ Red Hat Enterprise Linux 7 из Red Hat Registry, то используете предварительно настроенное минимальное пользовательское пространство Red Hat Enterprise Linux 7, содержащее такие утилиты, как bash, awk, grep и yum (для возможности установки дополнительного программного обеспечения).
docker run -i -t rhel7 bash
Все пользовательские приложения (и контейнеризированные и нет) при работе используют различные данные, но где эти данные хранятся? Какие-то данные поступают из регистров процессора и внешних устройств, но чаще они хранятся в памяти и на диске. Приложения получают доступ к данным, выполняя специальные запросы к ядру — системные вызовы. Например, такие как выделение памяти (для переменных) или открытие файла. В памяти и файлах часто хранится конфиденциальная информация, принадлежащая разным пользователям, поэтому доступ к ним должен запрашиваться у ядра с помощью системных вызовов.
Пространство ядра
Ядро обеспечивает абстракцию для безопасности, оборудования и внутренних структур данных. Например, системный вызов open()
используется для получения дескриптора файла в Python, C, Ruby и других языках программирования. Вряд ли бы вы хотели, чтобы ваша программа работала с XFS на уровне битов, поэтому ядро предоставляет системные вызовы и работает с драйверами. Фактически этот системный вызов настолько распространен, что является частью библиотеки POSIX .
На следующем рисунке обратите внимание, что bash выполняет вызов getpid()
, который возвращает его собственный идентификатор процесса. А команда cat запрашивает доступ к /etc/hosts
с помощью вызова файла open()
. В следующей статье мы рассмотрим, как это работает в мире контейнеров, а пока обратите внимание, что часть кода находится в пользовательском пространстве, а часть — в ядре.
Обычные программы пользовательского пространства для выполнения работы постоянно выполняют системные вызовы, например:
ls
ps
top
bash
Некоторые программы, выполняемые в пользовательском пространстве, почти напрямую отображаются на системные вызовы, например:
chroot
sync
mount/umount
swapon/swapoff
Копнув на один уровень глубже, можно привести примеры системных вызовов, которые выполняются перечисленными выше программами. Обычно они вызываются через такие библиотеки, как glibc
, или через интерпретатор (Ruby, Python) или через Java Virtual Machine.
open (files)
getpid (processes)
socket (network)
Типичная программа получает доступ к ресурсам ядра через несколько слоев абстракции, как показано на следующем рисунке:
Чтобы получить представление о том, какие системные вызовы доступны в ядре Linux, смотрите man-страницу syscalls
. Интересно заметить, что я запускаю эту команду на своем ноутбуке с Red Hat Enterprise Linux 7, но использую контейнер Red Hat Enterprise Linux 6, чтобы посмотреть, какие были изменения в системных вызовах:
docker run -t -i rhel6-base man syscalls
SYSCALLS(2) Linux Programmer’s Manual SYSCALLS(2)
NAME
syscalls - Linux system calls
SYNOPSIS
Linux system calls.
DESCRIPTION
The system call is the fundamental interface between an application and the kernel.
System call Kernel Notes
------------------------------------------------------------------------------
_llseek(2) 1.2
_newselect(2)
_sysctl(2)
accept(2)
accept4(2) 2.6.28
access(2)
acct(2)
add_key(2) 2.6.11
adjtimex(2)
afs_syscall(2) Not implemented
alarm(2)
alloc_hugepages(2) 2.5.36 Removed in 2.5.44
bdflush(2) Deprecated (does nothing) since 2.6
bind(2)
break(2) Not implemented
brk(2)
cacheflush(2) 1.2 Not on i386
Обратите внимание, что по информации из man какие-то системные вызовы (также известные как интерфейсы) были убраны, а какие-то добавлены. Линус Торвальдс и другие уделяют большое внимание тому, чтобы поведение системных вызовов было понятным и стабильным. В Red Hat Enterprise Linux 7 (ядро 3.10) доступно 382 системных вызова. Время от времени добавляются новые, а некоторые объявляются устаревшими. Это следует учитывать при рассмотрении жизненного цикла вашей контейнерной инфраструктуры и приложений, которые будут в ней работать.
Заключение
Есть несколько важных моментов, которые нужно знать про пользовательское пространство и пространство ядра:
Приложения содержат бизнес-логику, но используют системные вызовы.
После компиляции программы набор используемых системных вызовов встраивается в бинарный файл (в языках более высокого уровня это интерпретатор или JVM).
Контейнеры не абстрагируют (не отменяют) необходимость того, чтобы пользовательское пространство и пространство ядра использовали одинаковый набор системных вызовов.
В мире контейнеров пользовательское пространство упаковывается (bundle) и развертывается на различных хостах от ноутбуков до продуктивных серверов.
В ближайшие годы могут появиться проблемы.
Со временем будет сложно гарантировать, что контейнер, созданный сегодня, будет работать завтра. Представьте, что наступил 2024 год (возможно, наконец-то появятся настоящие ховерборды), а у вас все еще работает контейнеризованное приложение в проде для которого требуется пользовательское пространство Red Hat Enterprise Linux 7. Как безопасно обновить хост с контейнерами и инфраструктуру? Будет ли контейнеризированное приложение одинаково хорошо работать на новых хостах, доступных на рынке?
Во второй части этой серии (оригинал на англ. Architecting Containers Part 2: Why the User Space Matters) мы рассмотрим, как взаимосвязь между пространством пользователя и пространством ядра влияет на архитектурные решения и что можно сделать, чтобы минимизировать проблемы.
Перевод материала подготовлен в рамках курса "Administrator Linux. Professional". Всех желающих приглашаем на открытый урок «Использование VPN туннелей в Linux».
В ходе этого вебинара:
— узнаем, что такое VPN;
— познакомимся с основными видами VPN и сравним их;
— разберем варианты конфигурации OpenVPN, попробуем понять разницу между ними;
— познакомимся с WireGuard, сравним его производительность с OpenVPN.
• РЕГИСТРАЦИЯ НА ВЕБИНАР •