В первой части цикла статей мы изучили, чем отличается пространство пользователя от пространства ядра. Во второй – поняли, почему пространство пользователя так важно для разработчиков, администраторов и архитекторов. В этой статье мы осветим несколько важных примеров, при которых выбор пространства пользователя может влиять на развертывание и поддержание приложения.
Хотя есть много способов воздействия и влияния на ваше приложение для некой контейнерной архитектуры, пространство пользователя предоставляет инструменты, про которые часто забывают:
языковая исполняющая среда;
инструменты для отладки и управления.
Контейнеры приложений против контейнеров с суперпривилегиями
Сначала давайте определим два основных типа рабочей нагрузки – контейнеры приложений и контейнеры с суперпривилегиями (SPC) . Для каждого из них требуется более или менее детерминированная связь с базовым хостом контейнера, а также безопасный доступ к нему.
Контейнеры приложений (или контейнеры служб ) предоставляют инструменты для приложений и пакетной обработки: например, веб-серверы с Python или Ruby, JVM, инструментами Hadoop или HPC. Контейнеры приложений – это то, что разработчики пытаются перенести в производственную среду или в кластер, чтобы удовлетворить потребности бизнеса.
Контейнеры можно рассматривать как процессы, которые выполняются в собственном пользовательском пространстве с дополнительной изоляцией. Многие не осознают, что большую часть этой изоляции можно удалить для выполнения различных задач системного администрирования.
Контейнеры – это действительно просто процессы, которые монтируют отдельный образ диска, включенный пространствами имен ядра. Контейнеры служб обычно работают с SELinux и включенными CGroups , в то время как суперпривилегированные контейнеры часто работают без этих ограничений.
SPC предоставляют инструменты для управления и отладки других контейнеров или самого хоста контейнера. Например, SPC можно использовать для загрузки модулей ядра, прослушивания сетевого трафика или для запуска SystemTap . Суперпривилегированные контейнеры аналогичны предоставлению системным администраторам и старшим разработчикам доступа sudo (root) для поддержки приложений.
Разработчики и системные администраторы сталкиваются с разными проблемами
У контейнеров приложений и супер привилегированных контейнеров разные предназначения и для их разработки требуется различный подход, но хватит лирики… как это влияет на ваше приложение?
Это влияет на принятия решений о том, как создавать, отлаживать, управлять и развертывать ваше приложение в разных средах – от рабочего ноутбука до промышленного сервера. Выбор и стандартизация единого пользовательского пространства контейнера (цепочки инструментов) в разных средах позволит программистам, системным администраторам и архитекторам упростить взаимодействие между разработкой и эксплуатацией, ускоряя развертывание. Развитие навыков работы с одним общим стеком инструментов облегчает общение (между разработчиками и командой эксплуатации), обучение и устранение неполадок (если что-то пойдет не так).
Контейнеры приложений
Пользовательское пространство влияет на приложения, потому что именно в нем расположены все языковые среды выполнения. Пользовательское пространство упаковывается в образ контейнера и совместно используется разработчиками, архитекторами и системными администраторами. Когда разработчик или архитектор запускает новый проект, они выбирают пространство пользователя (образ контейнера) в зависимости от того, какие языковые среды выполнения и приложения доступны. Доступность приложения является ключевым критерием при выборе платформы, будь то внутри или вне контейнера.
Пока разработчики и архитекторы приложений больше думают о том, какие языковые среды выполнения им доступны, системные администраторы больше озабочены безопасностью и сроками поддержки платформы.
Разработчики озадачены развертыванием своего кода и данных. Пользователей интересует доступ к этим приложениям
Помните, что когда создаются контейнеры приложений, они – все еще процессы с добавленной в ядро дополнительной изоляцией. Это означает, что к ним применяется большинство обычных правил. Их все еще нужно патчить, улучшать и управлять ими после запуска.
Упакованная в образ контейнера среда выполнения приложения и его зависимостей упрощает совместное использование кода. Когда все приложение и все его зависимости упакованы в образ контейнера, ускоряется процесс совместной работы, начиная от разработки микросервисов внутри компании, до проверки домашнего проекта потенциального разработчика, с которым вы проводите собеседование.
Среда разработки и серверы
Red Hat Software Collections предоставляет доступ к последним стабильным версиям (как в виде RPM, так и в виде контейнерных образов) языков с открытым исходным кодом, баз данных (например, Ruby, Python, Node.js, Passenger, GCC, Ruby, MySQL, MongoDB, PHP и т.д.) и других утилит и серверов (Varnish, httpd, nginx). Стоит отметить, что наиболее популярные коллекции программного обеспечения Red Hat теперь доступны в виде образов контейнеров через Red Hat Customer Portal.
Кроме того, последние версии сертифицированных Red Hat образов контейнеров сторонних производителей доступны через Red Hat Federated Registry . Разработчики, системные администраторы и архитекторы могут использовать эти образы от независимых поставщиков программного обеспечения (ISV) для упрощения разработки, развертывания и эксплуатации готовых решений – все они сертифицированы для использования с экосистемой контейнерных хостов и платформ Red Hat.
Red Hat предоставляет разработчикам доступ к новым языковым средам и серверам через Red Hat Software Collections и сертифицированные приложения ISV. Для системных администраторов есть доступ к инструментам с помощью контейнера rhel-tools . Все они распространяются либо через Red Hat Container Registry, либо через Red Hat Federated Registry.
Интроспекция
Выполняя свои обязанности, разработчики, архитекторы и системные администраторы вносят изменения в пользовательское пространство. Рассматривая каждый слой образа как часть цепочки поставок, такие форматы, как Docker, предоставляют инструменты для быстрого и простого определения изменений, внесенных в предыдущий образ контейнера (пользовательское пространство).
Хотите понять из чего собран образ? В этом нам частично помогут метаданные:
FROM rhel7 MAINTAINER Scott McCarty <smccarty@redhat.com> RUN yum update -y;yum clean all # update the image
Так как образы Docker строятся в виде слоев, мы можем определить, что изменилось между этим слоем и лежащим под ним. Обратите внимание, что последний слой образа был обновлен неделю назад.
docker history rhel7-updated IMAGE CREATED CREATED BY SIZE ff664e850f37 1 weeks ago /bin/sh -c yum update -y 632.9 MB 775344f011a7 1 weeks ago /bin/sh -c #(nop) MAINTAINER Scott McCarty sm 0 B edf056c07122 1 weeks ago bash 111 MB d822d9962e7c 1 months ago
Мы даже можем определить, какие именно файлы изменялись между образом и запущенным контейнером. Это особенно полезно при переносе устаревших приложений в контейнеры. Выполняющий миграцию инженер может определить, где приложение вносит изменения в пользовательское пространство. Это позволит ему легче разделить логи, данные, файлы конфигурации и код – и разместить компоненты на правильном устройстве хранения (например, на внешнем по отношению к контейнеру хранилище).
docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fd99f8f10a9a rhel7-updated:latest "bash" 11 seconds ago Exited (0) 2 seconds ago hubristic_stallman Notice that the Bash history file has been updated: docker diff fd99f8f10a9a C /root A /root/.bash_history
На самом деле мы лишь поверхностно изучаем ценность интроспекции. Представьте насколько удобным может оказаться этот инструментарий при выполнении миграций через несколько лет после того, как создавшие проект разработчик покинет его.
Упрощенное развертывание приложений
В то время как перенос приложения в контейнер обычно выполняется как отдельный проект (один раз), запуск приложений – происходит довольно часто. Команда atomic run позволяет разработчикам встроить сложную логику запуска в метаданные образа контейнера, чтобы ее не нужно было указывать каждый раз при запуске контейнера. Используя метку RUN
, запуск можно упростить до:
atomic run ntpd
вместо
docker run -d -n ntpd —cap_add SYS_TIME ntp
При этом логика аккуратно сохраняется в Dockerfile
, который можно контролировать по версиям:
FROM rhel7 RUN yum -y install ntp; yum -y clean all LABEL RUN="docker run -d --name=ntpd --cap-add=SYS_TIME --cap-add=NET_BIND_SERVICE IMAGE /usr/sbin/ntpd -d" CMD /usr/sbin/ntpd
Жизненный цикл
Создание образа контейнера на пользовательском пространстве RHEL7 обеспечивает десятилетний жизненный цикл приложения. В течение этого времени исправления безопасности и ошибок предоставляются в рамках подписки на RHEL. Также Red Hat будет предоставлять новые образы контейнеров в дополнение к RPM, что позволит клиентам по усмотрению обновлять свои образы контейнеров.
Большинство дистрибутивов Linux позволяют важным частям программного обеспечения получать обновления основных версий. Например, Fedora старается обновлять ядро как можно быстрее (rebasing). Fedora стремится к инновациям, что замечательно, но иногда это может привести к поломкам. Red Hat Enterprise Linux использует методику обратного переноса (вместо rebasing) патчей к определенным версиям библиотек и двоичных файлов в пользовательском пространстве. Например, версия веб-сервера Apache в Red Hat Enterprise Linux 7 по-прежнему 2.4.6, но команда разработчиков будет тщательно следить за ним для обеспечения безопасности и исправления ошибок. Это дает администраторам уверенность в том, что образы контейнеров будут работоспособны в течение многих лет. В этом случае конфигурационные файлы и другое зависящее от Apache ПО будут продолжать работать даже после обновления образа контейнера для применения обновлений безопасности и исправления ошибок.
Простая строка FROM
в вашем Dockerfile отразится на том, сколько сил ваши разработчики и администраторы потратят при обновлении и модернизации в течение жизненного цикла приложения. Более того, поскольку эти контейнеры будут работать на различном оборудовании – от ноутбуков программистов до производственных серверов, поддержка и жизненный цикл являются ключевыми факторами при принятии решения о том, какой дистрибутив использовать в образе контейнера.
Контейнеры с супер привилегиями (SPC)
После запуска приложений в контейнерной инфраструктуре (и особенно после запуска этих приложений в производство), скорее всего потребуется пересмотреть способы устранения неполадок, сканирования, резервного копирования и управления приложениями, хостами и данными.
При использовании суперпривилегированных контейнеров важность отношений между пользовательским пространством и ядром становится очевидной. SPC выполняются с привилегиями, аналогичными обычному процессу, принадлежащему и выполняемому пользователем root. Инструментарий внутри SPC часто напрямую взаимодействует со структурами данных ядра и файловой системой хоста – такими как стек TCP, список процессов, список пользователей и т.д. Контейнер RHEL Tools делает многие из этих операций довольно простыми.
Системные администраторы занимаются управлением, обслуживанием, устранением неполадок и сканированием контейнеров.
SPC также показывают насколько легко развернуть системное ПО и утилиты, особенно если они нужны администратору лишь временно для устранения неполадок. После того, как администратор закончит работу, утилиты можно легко и просто удалить. Чтобы сделать эту операцию безболезненной, компания Red Hat создала специальный контейнер инструментов под названием Atomic Tools Container . Мы продемонстрируем его использование в некоторых из следующих разделов.
А сейчас рассмотрим некоторые дополнительные случаи использования SPC.
Отладка контейнеров и контейнерных хостов
После того как приложение запущено и работает, как устранить происходящие в контейнере неполадки? Как определить, в чем проблема – в контейнере или в хосте контейнера? Ядро размещается и работает на хосте, но приложения выполняются в контейнере, поэтому следует подходить к процессу отладки комплексно. Первым шагом для администратора является определение того, где именно прячется проблема – в хосте или в контейнере?
Для начала администратор может войти по ssh на хост контейнера и запустить набор утилит… как обычно. Более удобный способ – запустить эти утилиты из суперпривилегированного контейнера, что позволяет легко удалить их впоследствии и не загрязнять контейнерный хост кучей программ, файлов и конфигураций.
Отслеживание системных вызовов
Очень полезно просматривать, какими системными вызовами приложение обращается к ядру, но как это сделать, если приложение находится в контейнере? Процесс почти идентичен подходу, который используется и вне контейнера: сначала мы вызываем список процессов, затем подключаемся к нему с помощью команды strace . Основное отличие заключается в том, что мы должны разрешить контейнеру видеть таблицу процессов ядра хоста:
docker run -it --pid=host registry.access.redhat.com/rhel7/rhel-tools ps -ef | grep yum root 35114 34439 1 03:42 ? 00:00:01 /usr/bin/python /usr/bin/yum install docker run -it --pid=host --privileged registry.access.redhat.com/rhel7/rhel-tools strace -p 35114 Process 35114 attached
Примеры команд (выше) сложно запомнить, в них используется много дополнительных ключей, поэтому вы можете использовать команду atomic , чтобы упростить ее до следующей:
atomic run rhel-tools ps -ef | grep yum root 12993 12974 5 19:56 ? 00:00:07 /usr/bin/python /usr/bin/yum upd root 13405 12880 0 19:59 ? 00:00:00 grep --color=auto yum atomic run rhel7/rhel-tools strace -p 12993 Process 12993 attached read(0,
Сканирование сетей
Другой распространенный сценарий – сканирование сети. Как мы можем перехватить исходящие из контейнера пакеты? Как обычно, обратившись к TCP-стеку ядра.
Сначала узнайте IP-адрес контейнера:
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3ab5aa19b1be rhel7 "httpd" 2 hours ago Up About a minute clever_payne Output: docker inspect 3ab5aa19b1be | grep IPAddress "IPAddress": "172.17.0.13", "SecondaryIPAddresses": null,
Теперь просканируем траффик, но используя SPC:
atomic run --spc rhel7/rhel-tools tcpdump -Xs 1514 -i any -n host 172.17.0.13 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on any, link-type LINUX_SLL (Linux cooked), capture size 1514 bytes 07:12:28.521162 IP 172.17.42.1.60854 > 172.17.0.13.http: Flags [S], seq 648584050, win 14600, options [mss 1460,sackOK,TS val 1956311198 ecr 0,nop,wscale 7], length 0 0x0000: 4500 003c 6bec 4000 4006 4c9f ac11 2a01 E..<k.@.@.L...*. 0x0010: ac11 000d edb6 0050 26a8 9b72 0000 0000 .......P&..r.... 0x0020: a002 3908 825f 0000 0204 05b4 0402 080a ..9.._.......... 0x0030: 749a f09e 0000 0000 0103 0307 t...........
SystemTap
Вы сталкивались с тем, что после обновления приложения снижалась его производительность? Представьте, что вы обновляете хосты контейнеров (особенно ядро), а производительность приложения падает. Как устранить такую проблему? Начать следует с определения времени выполнения системных вызовов в разных версиях ядра. SystemTap может это сделать, но как установить его в контейнерной среде?
Служба поддержки Red Hat уже решила эту проблему – с помощью этой статьи в базе знаний Red Hat можно легко запустить SystemTap в контейнере. После того, как у вас будет создан образ контейнера с stap и отладочными символами для установленного ядра, запустить SystemTap не составит труда, поскольку теперь ваше пространство пользователя и ядро работают вместе:
stap -e ‘probe kernel.function(«generic_make_request») { print_backtrace() }’
Еще можно измерить скорость системных вызовов в ядре с помощью скрипта syscalltimes , изменив команду stap на:
docker run —privileged -v /lib/modules:/lib/modules —tty=true —interactive=true stap stap $P_VERBOSE_STR -w «$P_PROCESS_STR» -e ‘
Затем скрипт выполняется как обычно, но весь код запускается из суперпривилегированного контейнера:
./syscalltimes.sh -t System Call Count Total ns Avg ns Min ns Max ns write 103 458792 4454 999 42182 readlink 4 26277 6569 3460 9715 set_tid_address 2 2094 1047 1019 1075 execve 2 218808 109404 108172 110636 access 10 44503 4450 2312 8385 getpeername 4 3804 951 752 1208 close 500 294474 588 340 17796 wait4 6 200706487 33451081 601 113411404 ftruncate 4 24773 6193 2823 9584 setpgid 4 4822 1205 718 1743 mprotect 60 131753 2195 977 5733 accept4 4 14854 3713 1880 5663 epoll_create1 2 4841 2420 2359 2482
Сбор данных из контейнеров
В контейнер RHEL Atomic Tools было вложено много труда, чтобы разработать такие инструменты, как kdump , abrt , sosreport , инструменты для анализа файлов ядра и т.д.
Руководство по Atomic Tools (англ).
Сканирование или управление контейнерами и контейнерными хостами
Образы следует сканировать в целях безопасности. Аналогичную методику можно использовать для агентов резервного копирования, мониторинга и даже протоколирования.
Для проверки вашего хоста или контейнера можно запустить Docker CIS Benchmark . Также команда запуска значительно упрощена с помощью метки atomic RUN
:
atomic run fatherlinux/docker-bench-security
Подробнее про сканирование контейнеров можно прочитать в этой статье(англ).
Заключение
Чтобы проще понять разницу между контейнерами приложений и SPC, стоит посмотреть на них со стороны рабочей нагрузки. Контейнеры приложений поддерживают бизнес-логику, а супер привилегированные контейнеры (SPC) – приложения и инфраструктуру.
При выборе образов контейнеров и хостов контейнеров необходимо продумать множество моментов. Решите, какие приложения вам нужны, а также как вы будете их поддерживать и устранять неполадки в течение всего жизненного цикла продукта.
Когда операционная система разделяется на ядро (контейнерный хост) и пространство пользователя (образ контейнера), многие из тех же функций поддержки должны быть продуманы по-другому, но объем работы в целом почти не меняется.