Домашнее задание: Необходимо развернуть окружение с веб-сайтом с использованием базы данных, настроить постоянное хранение и создать сетевое окружение. Проверить получившийся контейнер и образ на безопасность. В отчете привести скриншоты и описать последовательность действий. Разобрать вывод сканеров безопасности и предложить меры по их исправлению.
Утилиты для проверки docker-контейнера
Share for dojo and Ubuntu Virtual Image - \\10.114.6.6\shared
https://hackmd.io/@0x41/SDL_Docker
Литература:
Для начала найдем интересующий нас образ (первая команда) и установим его:
Для запуска контейнера в фоновом режиме необходимо указать параметр -d. По умолчанию Docker запускает последнюю доступную версию. Если требуется определенная версия, она может быть указана в виде тега, например, версия 3.2 будет
Для просмотра информации о запущенных в фоне контейнерах есть команда:
Так же имеется две полезные команды. Первая выводит дополнительную информацию о контейнере. Вторая выводит его логи.
Redis установлен, но не доступен вне контейнера. 6379 — используемый порт Redis. Для проброса порта имеется такая команда -p <host-port>:<container-port>
. Пример:
По умолчанию порт на хосте сопоставлен с 0.0.0.0, что означает доступ со всех IP-адресов. Вы можете указать конкретный IP-адрес при определении сопоставления портов, например, -p 127.0.0.1:6379:6379
Как хранить данные и не потерять их при переустановке контейнера
Если открыть документацию по Redis для Docker , то увидим информацию по хранению данных. Данный образ хранит её в /data .
Любые данные, которые необходимо сохранить на Docker хосте, а не внутри контейнера redis, должны храниться в /opt/docker/data/redis, что задается отдельным параметром: docker run -d --name redisMapped -v /opt/docker/data/redis:/data redis
Взаимодействие внутри контейнера
Ранее мы использовали -d для выполнения контейнера в отдельном фоновом состоянии.
Без указания этого контейнер будет работать на переднем плане.
Если нужно взаимодействовать с контейнером (например, для доступа к оболочке bash), нужно добавить опции -it.
Примеры:
docker run ubuntu ps
запустит контейнер ubuntu и выполнит команду ps. docker run -it ubuntu bash
даст доступ к bash shell внутри контейнера.
Цель урока: создать веб сайт со статичной страницей в контейнере.
Docker images начинаются с базового image(образа), который включает в себя зависимости платформы для приложения. Этот базовый образ определяется как команда в Dockerfile, который является списком инструкций (команд), описывающих как развернуть приложение.
В примерах будет использован как базовый образ NGINX версии Alpine (настроенный веб сервер в дистрибутиве linux alpine).
Так же создайте простой index.html файл в папке, из которой идет работа.
Создадим Dockerfile:
Первая строчка определяет базовый образ. Вторая строчка копирует контент текущей папки во внутрь контейнера (наш index.html).
Для сборки используется команда build. Она принимает несколько параметров. Например, параметр -t позволяет указать имя для и тег для изображения (используется часто как номер версии). Пример:
docker build -t webserver-image:v1 .
Для просмотра списка изображений используйте команду:
docker images
Запускаем контейнер с пробросом 80 порта.
docker run -d -p 80:80 webserver-image:v1
Теперь можно проверить — работает ли наш сайт? Воспользуемся утилитой curl.
curl dockerDocker, лекция 3. Сборка образов
Как собрать образ, основываясь на собственных требованиях.
Для этой лекции контейнер будет запускать статическое HTML-приложение с использованием веб-сервера Nginx.
Имя компьютера, на котором запущен контейнер: Docker. Если вы хотите получить доступ к какой-либо из служб — используйте docker вместо localhost или 0.0.0.0.
Образы Docker создаются на основе Dockerfile. Dockerfile определяет все шаги, необходимые для создания образа Docker с приложением, настроенным и готовым к запуску в качестве контейнера. Сам образ содержит все, от операционной системы до зависимостей и конфигурации, необходимых для запуска приложения.
Образ позволяет переносить его между различными средами и быть уверенным, что он заработает в любой из них.
Dockerfile позволяет пользователям расширять существующие изображения вместо создания с нуля. Основываясь на существующем образе, нужно только определить шаги по настройке приложения. Базовые образы могут быть основными установками операционной системы или настроенными системами, которые просто нуждаются в дополнительных настройках.
Все образы Docker начинаются с базового образа. Базовый образ — это те же изображения из реестра Docker, которые используются для запуска контейнеров. Наряду с именем образа мы также можем включить тег, чтобы указать, какую конкретную версию мы хотим, по умолчанию это последняя версия.
Эти базовые образы используются в качестве основы для запуска вашего приложения. Например, в этом уроке мы требуем, чтобы NGINX был настроен и запущен в системе, прежде чем мы сможем развернуть наши статические HTML-файлы. Поэтому мы хотим использовать NGINX в качестве базового образа.
Dockerfile — это простые текстовые файлы с командой в каждой строке. Чтобы определить базовый образ, нужно использовать инструкцию FROM <image-name>: <tag>.
Добавим образ в Dockerfile.
Важно: заманчиво использовать тег :lastest, однако есть вероятность построить образ не на той версии, которую бы хотелось. Для исключения ошибок в работе приложения рекомендуется использовать конкретную версию.
Определив базовый образ, нам нужно запустить различные команды для настройки нашего образа. Есть много команд, которые могут помочь с этим, две главные команды это COPY и RUN.
RUN <команда> позволяет вам выполнить любую команду, как в командной строке, например, установить различные пакеты приложений или выполнить команду сборки. Результаты RUN сохраняются в образе, поэтому важно не оставлять ненужные или временные файлы на диске, так как они будут включены в образ.
COPY <src> <dest> позволяет копировать файлы из каталога, содержащего Dockerfile, в образ контейнера. Это чрезвычайно полезно для исходного кода и ресурсов, которые вы хотите развернуть внутри своего контейнера.
Для примера создадим новый файл index.html, который добавим в контейнер. На следующей строке после команды FROM созданного ранее dockerfile добавим команду команду COPY, чтобы скопировать index.html в каталог с именем /usr/share/nginx/html
С настроенным образом Docker и определившись, какие порты будут открыты, нужно указать команду, которая запускает приложение.
С настроенным образом Docker и определением, какие порты мы хотим сделать доступными, теперь нам нужно определить команду, которая запускает приложение.
Строка CMD в Dockerfile определяет команду по умолчанию, запускаемую при запуске контейнера. Если команде требуются аргументы, рекомендуется использовать массив, например:
Массив будет объединен вместе и итоговая команда ниже будет выполнена:
Ещё пример — передадим команду nginx. По-умолчанию демон NGINX будет выключен:
CMD ["nginx", "-g", "daemon off;"]
Файлы скопированы в наш образ, теперь нужно определить, на каком порте приложение должно быть доступно.
Используя команду EXPOSE \<port>, нужно сообщить Docker, какие порты должны быть открыты. Вы можете определить несколько портов в одной команде, например, EXPOSE 80 433 или EXPOSE 7000-8000. Добавим в наш файл конфигурации:
После написания Dockerfile нужно использовать команду docker build, чтобы превратить его в образ. Команда build принимает каталог, содержащий Dockerfile, выполняет шаги и сохраняет образ в вашем локальном Docker Engine. Если произошел сбой из-за ошибки, сборка прекращается.
Итоговый dockerfile этой лекции приведен ниже. Это nginx версии 1.11, в который скопирована html страница и выключен демон веб-службы.
Итак, выполняем команду docker build для создания образа. Можно дать образу понятное имя, используя опцию -t \<name>.
Не стоит забывать о команде docker images для просмотра списка образов.
После успешного создания образа можно запустить контейнер так же, как это описано в первой лекции.
NGINX предназначен для работы в качестве фоновой службы, поэтому ему нужно включить опцию -d. Чтобы сделать веб-сервер доступным, нужно привязать его к порту 80, используя -p 80:80. Например:
Теперь можно получить доступ к запущенному веб-серверу по имени хоста docker. После запуска контейнера команда curl -i http: // docker вернет наш index.html через NGINX и созданный нами образ.
В этой лекции продолжается изучение того, как создавать и развертывать приложения в виде контейнера Docker. Предыдущий сценарий охватывал развертывание статического HTML-сайта. В этой лекции рассматривается, как развернуть приложение Node.js в контейнере.
Среда настроена с доступом к личному экземпляру Docker, а код для приложения Expressjs по умолчанию находится в рабочем каталоге.
Имя компьютера, на котором запущен Docker, называется Docker. Если нужно получить доступ к какой-либо службе, следует использовать hostname docker вместо localhost или 0.0.0.0.
Как описано в предыдущей лекции, все образы начинаются с базового изображения, в идеале максимально приближенного к желаемой конфигурации. Для Node.js есть готовые образы с тегами для каждой выпущенной версии.
Образ для node 10.0 это node:10-alpine. Это образ на основе alpine linux, который меньше и более обтекаема, чем официальное изображение.
Помимо базового образа также необходимо создать базовые каталоги, из которых запускается приложение. Используя RUN \<command>, можно выполнять команды, как если бы они выполнялись из командной оболочки. Используя mkdir, можно создавать каталоги, из которых будет выполняться приложение. Сейчас идеальным каталогом был бы /src/app, поскольку пользователь среды имеет доступ для чтения/записи к этому каталогу.
Можно определить рабочий каталог, используя WORKDIR <каталог>, чтобы гарантировать, что все будущие команды будут выполняться из каталога нашего приложения.
В нашем Dockerfile определим выше описанное:
Базовый образ и местоположение приложения настроены. Следующим этапом является установка зависимостей, необходимых для запуска приложения. Для Node.js это означает запуск установки NPM.
Чтобы свести время сборки к минимуму, Docker кэширует результаты выполнения строки в файле Docker для использования в будущей сборке. Если что-то изменилось, то Docker сделает недействительными текущую и все последующие строки, чтобы убедиться, что все обновлено.
Сделаем так, чтобы npm install запускался только в том случае, если что-то в нашем файле package.json изменилось. Если ничего не изменилось, то продолжим использовать версию кеша для ускорения развертывания. Используя COPY package.json <dest>, можно сделать кеш команды RUN npm install недействительным, если файл package.json изменился. Если файл не изменился, то кэш не будет признан действительным, и будут использованы кэшированные результаты команды установки npm.
Добавим описанные выше действия в dockerfile:
Пример package.json:
Создадим необходимые шаги в Dockerfile, чтобы завершить развертывание приложения, как это было на прошлой лекции.
Скопируем файлы для развертывания, в Dockerfile следует использовать COPY. <dest dir>.
После того, как исходный код скопирован, порты, к которым требуется доступ к приложению, определяются с помощью EXPOSE <порт>.
Наконец, приложение готово к установке зависимостей. При использовании Node.js следует использовать один хитрый прием: запустить команду npm. Параметр start описан в «scripts» — «start» файла package.json. Ниже приведен пример и краткое описание — почему так.
После того, как мы установили наши зависимости с помощью npm install, мы хотим скопировать остальную часть исходного кода нашего приложения. Разделение установки зависимостей и копирование исходного кода позволяет нам использовать кеш при необходимости.
Если мы скопируем наш код перед запуском npm install, он будет запускаться каждый раз, так как наш код изменился бы. Копируя просто package.json, мы можем быть уверены, что кеш становится недействительным только тогда, когда содержимое нашего пакета изменилось. Итоговый dockerfile:
Запускаем
И проверяем:
Образы docker должны быть спроектированы так, чтобы их можно было переносить из одной среды в другую без внесения каких-либо изменений или необходимости восстановления. Следуя этому шаблону, вы можете быть уверены, что если он работает в одной среде, например, в промежуточной, то он будет работать в другой среде, например в рабочей среде.
С помощью Docker переменные среды могут быть определены при запуске контейнера. Например, для приложений Node.js вы должны определить переменную среды для NODE_ENV при запуске.
Используя опцию -e, вы можете установить имя и значение -e NODE_ENV = production
В этом сценарии мы рассмотрим, как можно оптимизировать Dockerfile, используя OnBuild инструкции.
Среда лекции настроена с помощью примера приложения Node.js, однако подходы могут быть применены к любому изображению. Имя компьютера, на котором запущен Docker, называется Docker. Если нужно получить доступ к какой-либо из служб, тогда используйте docker вместо localhost или 0.0.0.0.
В то время как Dockerfile выполняется в порядке сверху вниз, с помощью OnBuild вы можете запустить инструкцию, которая будет выполнена позже, когда образ используется в качестве основы для другого образа.
В результате вы можете отложить выполнение, чтобы оно зависело от приложения, которое вы создаете, например, от файла package.json приложения.
Ниже приведен файл Docker Node.js OnBuild. В отличие от сценария предыдущей лекции команды приложения имеют префикс ONBUILD.
В результате образ будет построен, но команды приложения с пометкой ONBUILD не будут выполняться до тех пор, пока построенный образ не будет использован в качестве базового. Затем они будут выполнены как часть сборки базового образа.
Мы имеем всю логику для копирования кода, установки зависимостей и запуска приложения. Для примера добавим ещё один элемент — откроем порт.
Преимущество создания образов OnBuild состоит в том, что наш Dockerfile теперь намного проще и может легко использоваться повторно в нескольких проектах без необходимости повторного выполнения одних и тех же шагов, что сокращает время сборки.
Пример такого dockerfile:
Для начала — сборка первого контейнера из базового docker образа.
Теперь выполним команду сборки встроенного образа, который основан на предыдущем:
Сборка первого контейнера заняла около 2 минут при наличии хорошего интернета. Сборка второго контейнера заняла пару секунд.
Как обычно можно проверить доступность сервиса в контейнере командой:
Как можно игнорировать попадание определенных файлов в образ Docker, что может представлять угрозу безопасности и увеличивать время сборки.
Чтобы не допустить ошибочного включения в изображения конфиденциальных файлов или каталогов, нужно добавить файл с именем .dockerignore.
Пример
Dockerfile копирует рабочий каталог в образ Docker. В результате это может включать потенциально конфиденциальную информацию, такую как файл паролей, которым мы хотели бы управлять за пределами изображения. Добавим файл passwords.txt с нашим условным паролем, а так же напишем Dockerfile:
Соберем образ и посмотрим файлы в нем (сюрприз — файл passwords.txt будет в контейнере):
Следующая команда включит passwords.txt в файл .dockerignore и гарантирует, что он случайно не окажется в контейнере.
Файл игнорирования поддерживает каталоги и регулярные выражения для определения ограничений, очень похожих на .gitignore у git.
Этот файл также можно использовать для уменьшения времени сборки, что будет рассмотрено позже. Соберем образ, теперь файла с паролем быть не должно.
Если вам нужно использовать пароли как часть команды RUN, вам нужно скопировать, выполнить и удалить файлы как часть одной команды RUN. Внутри образа сохраняется только конечное состояние контейнера Docker.
Файл .dockerignore может гарантировать, что конфиденциальные данные не будут включены в образ Docker. Однако его также можно использовать для улучшения времени сборки образа.
Для примера создадим файл в 100 МБ, например big-temp-file.img и соберем образ. Это уже займет намного больше времени, чем обычно.
Так же целесообразно игнорировать каталоги .git вместе с зависимостями, которые загружаются /создаются в образе, такими как node_modules. Они никогда не используются приложением, работающим в Docker-контейнере, а просто добавляют издержки процессу сборки.
Таким же образом мы использовали файл .dockerignore для исключения конфиденциальных файлов, мы можем использовать его для исключения файлов, которые мы не хотим отправлять в сборку образа Docker.
Контейнеры могут выполнять разные функции. Это может быть контейнер, создающий и хранящий данные, например, база данных. Так же это могут быть данные, требующие дополнительной конфигурации или SSL-сертификации.
Один из подходов, который был ранее, заключается в использовании опции -v <host-dir>: <container-dir> для сопоставления каталогов. Другой подход заключается в использовании контейнеров данных, о которых сегодня и пойдет речь.
Контейнеры данных — это контейнеры, единственная цель которых заключается в том, чтобы быть местом для хранения/управления данными.
Как и другие контейнеры, они управляются хост-системой. Однако они не запускаются при выполнении команды docker ps.
Чтобы создать контейнер данных, мы сначала создаем контейнер с понятным именем для использования в будущем. Будем использовать busybox в качестве базы, так как он небольшой и легкий на тот случай, если мы захотим перенести контейнер на другой хост.
При создании контейнера мы также используем опцию -v, чтобы определить, где другие контейнеры будут считывать/сохранять данные.
Теперь, когда контейнер установлен, можно копировать файлы из локального клиентского каталога в контейнер.
Для копирования файлов в контейнер нужно использовать команду docker cp. Следующая команда скопирует файл config.conf в наш dataContainer и каталог config.
Теперь контейнер данных имеет конфиг файл и сейчас можно ссылаться на контейнер при запуске зависимых контейнеров, требующих этот файл конфигурации.
Используя опцию —volumes-from <container>, можно использовать тома монтирования из других контейнеров. В примере ниже запустим контейнер Ubuntu, который имеет ссылку на контейнер данных. Когда мы выведем каталог конфигурации, он покажет файлы из прикрепленного контейнера.
Если каталог /config уже существует, то имя тома будет переопределено. Так же можно привязать несколько томов.
Если мы хотим переместить контейнер данных на другой компьютер, мы можем экспортировать его в файл .tar.
Для импорта контейнера данных назад в Docker используйте команду:
Как разрешить нескольким контейнерам взаимодействовать друг с другом: как подключить хранилище данных (в данном случае Redis) к приложению, запущенному в отдельном контейнере.
Наиболее распространенным сценарием подключения к контейнерам является приложение, подключающееся к хранилищу данных. Ключевым аспектом при создании link является имя контейнера.
Настроим:
Для подключения к исходному контейнеру используется команда —link <container-name|id>:<alias> при запуске нового контейнера. container-name|id относится к исходному контейнеру, который определен ранее (redis), а alias определяет понятное имя хоста.
Устанавливая alias, мы разделяем, как наше приложение настроено на инфраструктуру. Это означает, что конфигурацию приложения не нужно менять, поскольку она подключена к другим средам.
Рассмотрим на примере. Вызывем контейнер Alpine, который связан с redis-сервером. Определим псевдоним как Redis. Когда link будет создан, Docker сделает две вещи.
Во-первых, Docker установит некоторые переменные окружения на основе ссылки на контейнер. Эти переменные среды дают возможность ссылаться на порты и IP-адреса, через известные имена.
Можно вывести все переменные окружения, добавив команду env. Например, создадим link и выведем env:
Во-вторых, Docker обновит файл HOSTS контейнера с записью для исходного контейнера с тремя именами: оригинал, alias и hash-id . Можно вывести запись host контейнеров, используя cat /etc/hosts
Пример вывода:
После создания link, можно пропинговать исходный контейнер так же, как если бы это был сервер, работающий в сети.
Создав link, приложения могут подключаться и взаимодействовать с исходным контейнером обычным способом, независимо от того, что обе службы работают в контейнерах.
Используем простое приложение на node.js, которое будет подключаться к redis:
В итоге получим работающее приложение, которое можно проверить http запросом:
Таким же образом можно подключать контейнеры к приложениям и к своим собственным инструментам CLI.
Эта команда запустит инстанс redis-cli и подключится к серверу по alias:
Для проверки работы можно вывести все данные из redis командой:
KEYS *
Как создать сеть docker, которая позволяет контейнерам взаимодействовать по сети. Так же будет рассмотрен встроенный dns сервер, добавленный в docker 1.10.
У Docker есть два подхода к работе в сети. Первый — это рассмотренный в прошлой лекции link, который обновляет /etc/hosts и переменные среды, чтобы позволить контейнерам подключаться.
Альтернативный подход заключается в создании Docker-сети, к которой подключены контейнеры. Сеть имеет атрибуты, аналогичные физической сети.
Первым шагом будет создание сети с использованием CLI. Эта сеть позволит нам прикрепить несколько контейнеров, которые смогут подключаться друг к другу.
Создадим сеть с желаемым именем:
Теперь при запуске контейнера можно использовать ключ —net, чтобы определить подключение к сети. Например:
В отличие от использования link, docker-сеть ведет себя как традиционные сети, где узлы могут быть подключены / отключены.
Первое отличие, это то, что Docker больше не назначает переменные среды и не обновляет файл hosts для контейнеров. Можно вывести две команды ниже и увидеть, что упоминаний других контейнеров нет.
Вместо этого контейнеры могут связываться через встроенный DNS-сервер. Этот DNS-сервер назначается всем контейнерам как IP 127.0.0.11 и устанавливается в файле resolv.conf. Проверим это командой:
Когда контейнеры пытаются получить доступ к другим контейнерам через имя, такое как Redis, DNS-сервер возвращает IP-адрес контейнера. В этом случае полное имя Redis будет redis.backend-network.
Docker поддерживает одновременное подключение сетей и контейнеров к нескольким сетям.
Например, давайте создадим отдельную сеть с приложением Node.js, которое связывается с существующим экземпляром Redis. Создадим сеть:
Используем команду подключения существующего контейнера к сети:
Если запустить тестовое приложение, то оно сможет обмениваться данными с контейнером reddis.
Link по-прежнему поддерживаются при использовании Docker Network и предоставляют способ определения псевдонима для имени контейнера. Это даст контейнеру дополнительное имя DNS-записи и способ обнаружения. При использовании —link встроенный DNS гарантирует, что результат поиска будет только в том контейнере, где используется —link.
Другой подход заключается в предоставлении псевдонима при подключении контейнера к сети.
Следующая команда подключит экземпляр Redis к внешней сети с псевдонимом db:
Когда контейнеры пытаются получить доступ к сервису через имя db, ими будет получен IP-адрес контейнера Redis.
Создав сети, можно использовать CLI для изучения деталей. Выведем список сетей.
Так же можно посмотреть какие контейнеры подключены и их IP.
А теперь отключим контейнер redis от сети frontend-network:
Docker Volumes позволяет обновлять контейнеры, перезагружать машины и обмениваться данными без потери данных. Это важно при обновлении базы данных или версий приложения.
Тома Docker создаются и назначаются при запуске контейнеров. Тома позволяют сопоставить каталог хоста с контейнером для обмена данными.
Тома являются двунаправленными. Это позволяет получать доступ к данным, хранящимся на хосте, из контейнера. Это также означает, что данные, сохраненные процессом внутри контейнера, сохраняются и на хосте.
В этом примере Redis будет использоваться для сохранения данных. Запустим контейнер Redis и создадим том данных с помощью параметра -v. Это указывает, что любые данные, сохраненные в контейнере в каталоге /data, должны сохраняться на хосте в каталоге /docker/redis-data.
Запишем данные в redis командой:
Если команда выполнена успешна, то в папке /docker/redis-data будет создан aof файл.
Так же путь на хосте можно использовать для других контейнеров. Например, сделаем контейнер с ubuntu, который будет заниматься резервным копированием.
Тома, сопоставленные с хостом, отлично подходят для сохранения данных. Однако, чтобы получить доступ к ним из другого контейнера, вам нужно знать точный путь, что может привести к опечаткам и другим ошибкам.
Альтернативный подход заключается в использовании —volumes-from. Параметр отображает тома из исходного контейнера в запускаемый контейнер.
Теперь сопоставляем том нашего контейнера Redis с контейнером Ubuntu. Каталог /data существует только в контейнере Redis, однако с помощью -volumes-from контейнер Ubuntu может получить доступ к данным.
Монтирование томов дает контейнеру полный доступ на чтение и запись к каталогу. Можно указать разрешения только для чтения каталога, добавив ro к монтированию.
ВАЖНО: если контейнер попытается изменить данные в каталоге, произойдет ошибка: Read-only file system.
Когда запускается контейнер, Docker будет отслеживать потоки out и error процесса и будет делать их доступными через клиент.
Для вывода логов используется команда
По-умолчанию журналы Docker выводятся с использованием json-file logger, т.е. вывод хранится в файле JSON на хосте. Это может привести к переполнению диска файлами логов. Можно изменить драйвер логов для перемещения логов в другое место.
Драйвер журнала syslog запишет все журналы контейнеров в центральный жулнал на хосте docker. Этот лог драйвер хорош, когда syslog собирается и агрегируется внешним ПО.
Команда ниже переведет логи контейнера в syslog.
Если попытаться получить доступ к журналам через клиент — будет ошибка FATA[0000] . docker logs поддерживается только драйвером json-file.
Для отключения логов достаточно прописать в —log-driver параметр none:
Как узнать текущий конфиг?
Команда inspect позволяет определить конфигурацию ведения журнала для контейнера, в формате можно указать раздел LogConfig. Пример:
Как и любой процесс, контейнеры могут зависать. В этой лекции узнаем как сохранить контейнеры в рабочем состоянии, и автоматически перезапустить их в случае неожиданного сбоя.
Docker считает любые контейнеры в exit кодом не равным 0 как crashed. По-умолчанию crashed контейнеры остаются остановленными. Для перезапуска используется:
Не забываем для диагностики смотреть ps и логи:
В зависимости от вашего сценария перезапуск сбойного процесса может исправить проблему. Docker может автоматически повторить попытку запуска контейнера определенное количество раз, прежде чем он прекратит попытки.
Опция —restart=on-failure:# позволяет указать, сколько раз Docker должен повторить попытку. В приведенном ниже примере Docker перезапустит контейнер три раза перед остановкой:
Наконец, Docker всегда может перезапустить сбойный контейнер, в этом случае Docker будет продолжать попытки, пока контейнер явно не получит команду остановиться.
Когда контейнеры запущены в продакшене важно добавлять дополнительные метаданные, которые помогут в управлении ими. Например: версия запущенного в контейнера кода, список приложений или права пользователей.
Метаданные управляются Docker Labels, которые позволяют установить пользовательские метаданные для контейнера или образа, которые могут быть потом отфильтрованы или просто прочитаны.
Label может быть прикреплен к контейнеру когда он запускается командой docker run. Контейнер может иметь несколько labels.
Для добавления label используется команда -l. В примере ниже устанавливается label с именем user с значением.
Если вы добавляете несколько label, они могут быть получены из внешнего файла. Файл должен иметь label в каждой строке, а затем они будут прикреплены к работающему контейнеру.
Для использования файла используется команда —label-file=<имя_файла>.
И прикрепим:
С образами всё работает похожим способом, только определение label происходит в Dockerfile.
Для добавления label с именем vendor нужно добавить строчку ниже в Dockerfile:
Если необходимо добавить несколько значений, то запись нужно добавлять такого вида:
Для просмотра добавленной информации используется docker inspect. Для примера был создан контейнер rd и образ katakoda-label-example. Чтобы получить label контейнера rd выполним:
Однако это не удобно — выдается вся информация о контейнере, а нам нужна только маленькая часть информации. Для этого есть параметр -f, который позволяет фильтровать JSON ответ. В примере ниже выберем только label.
С просмотром информации образов отличий почти нет, только вместо .Config.Labels используется .ContainerConfig.Labels:
Если так получилось, что у образа нет имени — можно использовать вместо имени <none>.
Когда речь идет не о паре контейнеров, а о сотнях, то такой вариант получения информации не очень удобен. Для этого есть фильтрация контейнеров ключом —filter для docker ps.
А для образов:
Важно помнить что поиск чувствителен к регистру текста. Поэтому лучше не использовать большие буквы.
Данная команда может понадобиться для, например, пометки продакшн или тестового Docker.
В дальнейших уроках будет подробнее показаны примеры конфигурации самого Docker, но пока только про label:
В этой лекции рассмотрим, как можно использовать веб-сервер NGINX для балансировки запросов между двумя контейнерами, запущенными на хосте.
В Docker есть два основных способа взаимодействия контейнеров друг с другом. Первый — через links, которые конфигурируют контейнер с переменными среды и изменением host файла, позволяя им общаться. Второй — использование шаблонов Service Discovery, в котором используется информация, предоставленная третьими сторонами, в этом случае это будет API докера.
В шаблоне Service Discovery приложение использует стороннюю систему для определения местоположения целевой службы. Например, если наше приложение хочет общаться с базой данных, оно сначала спросит API, каков IP-адрес базы данных. Этот шаблон позволяет вам быстро перенастроить и масштабировать вашу архитектуру с повышенной отказоустойчивостью, чем фиксированные пути.
Имя компьютера, на котором запущен Docker, называется Docker. Для доступа к службам используется имя docker вместо localhost или 0.0.0.0.
Мы хотим запустить службу NGINX, которая может динамически обнаруживать и обновлять свою конфигурацию баланса нагрузки при загрузке новых контейнеров. К счастью, это уже существует и называется nginx-proxy.
Nginx-proxy принимает HTTP-запросы и передает их в соответствующий контейнер на основе имени хоста запроса. Это прозрачно для пользователя и происходит без каких-либо дополнительных расходов ресурсов.
При запуске прокси-контейнера необходимо настроить три параметра. Первый — это привязка контейнера к порту 80 на хосте с использованием -p 80:80. Это гарантирует, что все HTTP-запросы обрабатываются прокси.
Второе — смонтировать файл docker.sock. Это соединение с демоном Docker, работающим на хосте, которое позволяет контейнерам получать доступ к его метаданным через API. Nginx-прокси использует это для прослушивания событий, а затем обновляет конфигурацию NGINX на основе IP-адреса контейнера. Монтирование файла работает так же, как и для каталогов, используя -v /var/run/docker.sock:/tmp/docker.sock:ro. Мы указываем: ro, чтобы ограничить доступ только для чтения.
Наконец, мы можем установить необязательный -e DEFAULTHOST = <домен>. Если запрос приходит и не указывает на определенные хосты, то выбирается указанный контейнер, в котором запрос будет обработан. Это позволяет вам запускать несколько веб-сайтов с разными доменами на одном компьютере с помощью отката на известный веб-сайт.
Попробуем на примере:
Поскольку мы используем DEFAULT_HOST, любые поступающие запросы будут направлены в контейнер, которому назначен HOST proxy.example.
Вы можете сделать запрос к веб-серверу, используя curl http://docker. Поскольку у нас нет контейнеров, он вернет ошибку 503.
Nginx-прокси теперь прослушивает события, которые Docker вызывает при запуске / остановке. Был создан пример веб-сайта katacoda/docker-http-server, который возвращает имя контейнера, на котором он работает. Это позволяет нам проверить, что наш прокси работает должным образом. Внутренне это приложение PHP и Apache2, прослушивающее порт 80.
Чтобы Nginx-прокси начал отправлять запросы в контейнер, нужно указать переменную среды VIRTUAL_HOST. Эта переменная определяет домен, откуда будут поступать запросы, и должен обрабатываться контейнером.
В этом примере установим наш HOST равным DEFAULT_HOST, чтобы он принимал все запросы.
Если выполнить запрос к нашему прокси с помощью curl http://docker, тогда запрос будет обработан контейнером с сайтом.
Теперь мы успешно создали контейнер для обработки наших HTTP-запросов. Если мы запустим второй контейнер с тем же VIRTUAL_HOST, то nginx-proxy настроит систему в циклическом сценарии с балансировкой нагрузки. Это означает, что первый запрос будет отправлен в один контейнер, второй запрос — во второй контейнер, а затем будет повторяться по кругу. Нет ограничений на количество работающих узлов.
Если мы выполним запрос к нашему прокси с помощью curl http://docker, тогда запрос будет обработан нашим первым контейнером. Второй HTTP-запрос вернет другое имя компьютера, что означает, что он был обработан нашим вторым контейнером.
Хотя nginx-proxy автоматически создает и настраивает NGINX для нас, если вам интересно, как выглядит окончательная конфигурация, вы можете вывести полный файл конфигурации с помощью docker exec, как показано ниже.
При работе с несколькими контейнерами может быть сложно управлять запуском, а также настройкой переменных и ссылок. Чтобы решить эту проблему, в Docker имеется инструмент под названием Docker Compose для управления оркестровкой, запуском контейнеров.
Посетите https://docs.docker.com/compose/install/ для получения инструкций о том, как установить docker compose в вашей локальной среде.
Docker Compose основан на файле docker-compose.yml. Этот файл определяет все контейнеры и параметры, необходимые для запуска набора кластеров. Cвойства отображаются в зависимости от того, как вы используете команды docker run, теперь хранятся в системе контроля версий и совместно используются с вашим кодом.
Формат файла основан на YAML (Yet Another Markup Language).
В этом сценарии у нас есть приложение Node.js, которое требует подключения к Redis. Для начала нам нужно определить наш файл docker-compose.yml для запуска приложения Node.js.
Учитывая приведенный выше формат, в файле необходимо присвоить имя контейнера «web» и установить для свойства сборки текущий каталог. Мы рассмотрим другие свойства в следующих шагах. Итого:
Docker Compose поддерживает все свойства, которые можно определить с помощью Docker Run.
Чтобы связать два контейнера вместе, укажите свойство links и перечислите необходимые соединения. Например, следующее будет ссылаться на контейнер источника redis, определенный в том же файле, и присвоить псевдониму то же имя. Так же пробросим порты.
Дополнительная информация тут — https://docs.docker.com/compose/compose-file/
На предыдущем шаге мы использовали Dockerfile в текущем каталоге в качестве базы для нашего контейнера. На этом этапе мы хотим использовать существующий образ из Docker Hub в качестве второго контейнера.
Формат YAML достаточно гибок, чтобы определять несколько контейнеров в одном файле.
С созданным файлом docker-compose.yml вы можете запускать все приложения с помощью одной команды up. Если вы хотите вызвать один контейнер, вы можете использовать up <имя>.
Аргумент -d указывает, что контейнеры запускаются в фоновом режиме, аналогично тому, как это используется в Docker run.
Docker Compose не только может управлять начальными контейнерами, но также предоставляет способ управления всеми контейнерами с помощью одной команды.
Например, чтобы увидеть детали запущенных контейнеров, вы можете использовать docker-compose ps.
Для доступа ко всем журналам через один поток вы используете docker-compose logs.
Другие команды следуют той же схеме. Получите их, набрав docker-compose
Поскольку Docker Compose понимает, как запускать контейнеры вашего приложения, его также можно использовать для масштабирования количества работающих контейнеров.
Параметр масштаба позволяет указать службу, а затем количество экземпляров, которые вы хотите. Если это число превышает количество уже запущенных экземпляров, он запустит дополнительные контейнеры. Если число меньше, то остановит ненужные контейнеры. Увеличим количество web контейнеров.
Так же ничего не мешает уменьшить их количество:
Как и при запуске приложения, для остановки набора контейнеров вы можете использовать команду docker-compose stop.
Чтобы удалить все контейнеры, используйте команду docker-compose rm.
Docker, лекция 16. Метрики контейнера с помощью Docker Stats
При запуске контейнеров в продакшн важно следить за показателями выполнения: загрузка ЦП и памяти. Это нужно для того, чтобы убедиться, что они контейнеры ведут себя так, как ожидалось. Эти метрики также могут помочь диагностировать проблемы, если они возникают.
В этом сценарии мы рассмотрим встроенные метрики, предоставляемые Docker, которые могут предоставить дополнительную информацию по работающим контейнерам.
В среде этого урока есть контейнер, работающий под именем nginx. Вы можете найти статистику для контейнера с помощью команды:
Команда запускает окно терминала, которое самостоятельно обновляется данными из контейнера.
Для того, чтобы прервать работу команды нажмите CTRL+C.
Встроенный Docker позволяет вам предоставлять несколько имен или идентификаторов и отображать их статистику в одном окне.
Сейчас в среде работают три подключенных контейнера. Для просмотра статистики по всем этим контейнерам вы можете использовать pipes и xargs. Pipe передает выходные данные одной команды на вход другой, в то время как xargs позволяет вам предоставлять входные данные в качестве аргументов команды. Пример:
Данная команда выведет все контейнеры и выведет их статистику.
В этом уроке будет рассказано как использовать функциональность многоэтапной сборки для создания меньших, более оптимизированных образов.
Эта функция идеально подходит для развертывания таких языков, как Golang, в качестве контейнеров. Имея многоэтапные сборки, на первом этапе можно собрать двоичный файл Golang, используя в качестве основы более крупный образ Docker. На втором этапе вновь созданный двоичный файл может быть развернут с использованием намного меньшего базового образа. Конечный результат — оптимизированный образ Docker.
Функция Multi-Stage позволяет одному Dockerfile содержать несколько этапов, чтобы получить желаемый оптимизированный Docker образ.
Первый этап будет содержать шаги для сборки двоичного файла с использованием контейнера разработки, второй будет оптимизирован и не будет включать инструменты разработки.
Удаляя средства разработки в рабочем образе, вы уменьшаете вероятность атаки и сокращаете время развертывания.
Начнем с развертывания HTTP-сервера Golang. В настоящее время используется двухэтапный подход Docker Build. В этом сценарии будет создан новый файл Docker, который позволяет создавать образ с помощью одной команды.
И наш Dockerfile.multi:
И запустим:
Результатом будут два образа. Один без тега, который использовался для первого этапа, а второй, меньший, — наш итоговый образ. Размер первого образа — 293МБ, размер второго — 12МБ.
Если появляется ошибка, COPY —from=0 /build/out /app/ Unknown flag, то это означает, что вы используете старую версию Docker без Multi-Stage поддержки и вам нужно обновится.
Теперь можно запустить приложение и проверить его доступность:
В этом уроке описано как использовать параметры —format для симпатичной печати вывода из Docker PS и Docker Inspect.
Формат docker ps можно отформатировать так, чтобы отображалась только информация, которая нужна именно сейчас.
Стандартная команда docker ps выводит имя, используемый образ, команду, время работы и информацию о порте.
Чтобы ограничить отображаемые столбцы, используйте параметр — format. Параметр позволяет красиво печатать данные контейнеров с использованием синтаксиса шаблона Go.
Пример ниже выведет: <Имя> контейнер использует образ <имя>.
Поскольку используются шаблоны Go, то это включает вспомогательные функции, такие как таблица.
Однако параметр format позволяет поддерживать отображение данных, которые уже доступны с помощью команды docker ps. Если вы хотите включить дополнительную информацию, такую как IP-адрес контейнера, данные должны быть доступны через docker inspect.
К счастью, Docker Inspect также поддерживает красивую печать результатов через шаблон Go. Идентификаторы контейнеров из Docker PS могут быть переданы в Docker Inspect.
Параметр format может получить доступ ко всей информации о контейнере. Ниже приведен пример перечисления всех IP-адресов для работающих контейнеров.
Docker укорачивает циклы разработки программного обеспечения и его развертывания, давая возможность поставлять код быстрее, чем раньше. Однако в комплекте вы также получаете связанные с безопасностью проблемы, о которых следует знать.
Под катом описаны 5 распространенных сценариев, при которых развертывание Docker-образов ведет к появлению вопросов, связанных с безопасностью, а также представлены замечательные инструменты и приведены рекомендации, которые могут оказаться полезными при их решении.
Начнем с вопроса, заложенного, пожалуй, в саму природу Docker: подлинность образов. Те, кто хотя бы некоторое время пользовался Docker, прекрасно знают, что контейнеры можно создавать на основе практически любых образов, независимо от того, загружены ли они из официального репозитория или получены от третьих лиц.
В результате мы оказываемся перед огромным выбором. Если вам не нравится контейнер, потому что он не удовлетворяет вашим требованиям, его можно заменить на другой. Но безопасно ли это?
Давайте рассмотрим вопрос с точки зрения программиста. При написании приложения можете ли вы доверять любому коду, независимо от того, кем он написан, даже если библиотека предлагается менеджером пакетов вашего языка программирования? Или все же стоит рассматривать не проанализированный вами код с определенной долей недоверия?
Думаю, что если безопасность вам хоть немного важна, перед интеграцией чужого кода в свое приложение нужно должным образом этот код проверить. Или я неправ?
Такая же доля скепсиса должна присутствовать и при работе с Docker-контейнерами. Если разработчик или организация вам незнакомы, можно ли быть уверенным, что выбранный контейнер не содержит скомпрометированных бинарных файлов или другого вредоносного содержимого? Думаю, вряд ли.
Учитывая вышеизложенное, я бы посоветовал сделать три вещи.
Используйте частные (private) и доверенные (trusted) репозитории
В частности, я рекомендую официальные репозитории на Docker Hub.
Там есть базовые образы для:
Помимо всего прочего, Docker Hub выделяется тем, что образы в нем всегда сканируются с помощью службы проверки безопасности (Security Scanning Service) Docker.
Для тех, кто ранее не слышал об этом сервисе, привожу выдержку из документации:
Docker Cloud и Docker Hub могут сканировать образы в частных репозиториях на предмет наличия уязвимостей, а также формировать отчет о результатах проверки для каждого тега образа.
При использовании официальных репозиториев вы можете быть уверены, что загруженные оттуда контейнеры безопасны и не содержат вредоносный код.
Эта функция доступна как на всех платных, так и на бесплатных тарифных планах, но во втором случае лишь в течение ограниченного периода времени. Если у вас платный тариф, обязательно воспользуйтесь этим сервисом.
Вы также можете создать частный (private) репозиторий для использования внутри организации.
Используйте Docker Content Trust
Еще одним полезным инструментом является Docker Content Trust. Эта функция была представлена сравнительно недавно — в Docker Engine 1.8. Она позволяет издателям заверять созданные ими Docker-образы.
Процитирую статью, посвященную выпуску соответствующего релиза, за авторством Diogo Mónica — ведущего специалиста по безопасности компании Docker:
Docker Engine перед загрузкой в удаленный репозиторий локально подписывает образ закрытым ключом издателя. Когда пользователь загружает этот образ, Docker Engine, используя публичный ключ издателя, удостоверяет, что будет запущен образ, идентичный созданному издателем, и в него не были внесены изменения третьими лицами.
В итоге этот сервис защищает от подделывания образов, атак повторного воспроизведения (replay attacks) и компрометации ключей. Я настоятельно рекомендую прочитать соответствующую статью и официальную документацию.
Docker Bench Security
Не так давно я начал использовать инструмент под названием Docker Bench Security, который:
Проводит проверку на основе десятков зарекомендовавших себя методов, относящихся к развертыванию Docker-контейнеров в production.
Этот инструмент основан на рекомендациях CIS Docker 1.13 Benchmark и проводит проверки в следующих областях:
Для установки этого инструмента скопируйте репозиторий:
Затем сделайте cd docker-bench-secutity и выполните следующую команду:
В итоге будет создан и запущен контейнер, который, в свою очередь, начнет проверять хост и его контейнеры. На рисунке ниже представлен примерный вывод, генерируемый этим инструментом.
Посмотрев на рисунок, легко убедиться, что список проверок и их результаты представлены в удобной для понимания форме. В моем случае, как видите, требуются некоторые улучшения.
Особенно мне приглянулась возможность автоматизации. То есть проверки можно включить в процесс непрерывной интеграции и таким образом постоянно контролировать уровень безопасности контейнеров.
Эта проблема появилась вместе с первыми системами контроля доступа, но продолжает быть актуальной и по сей день. При этом неважно, устанавливаете ли вы, например, дистрибутив Linux на голое железо или в качестве гостевой операционной системы в виртуальной машине.
Работа с Docker-контейнерами кардинально не улучшает безопасность. К тому же Docker добавляет новый уровень сложности, и граница между гостевой системой и хостом теперь не так четко различима.
В плане Docker я обращаю особое внимание на 2 вещи:
контейнеры, работающие в привилегированном режиме;
избыточные привилегии для контейнеров.
Начнем с первого пункта. Контейнер в Docker может быть запущен с ключом privileged, который дает ему дополнительные привилегии.
Процитируем документацию:
предоставляет контейнеру все разрешения и снимает все ограничения, наложенные cgroup-контроллером устройства.
Другими словами, контейнер может делать практически все то же, что и хост. Этот флаг сделали для особых случаев, например, для запуска Docker внутри Docker.
Если наличие этой возможности не заставило вас задуматься, я буду крайне удивлен или даже обеспокоен. На самом деле, если у вас не тот самый особый случай, тогда я и не знаю, зачем вам вообще может понадобиться эта опция. Если все же необходимо ее использовать, будьте предельно осторожны.
Процитирую Armin Braun:
Если вы используете привилегированные контейнеры, относитесь к ним так же, как и к любым другим процессам, запущенным от имени суперпользователя.
Но даже если вы не используете контейнеры с опцией --privileged, у некоторых из них могут быть избыточные привилегии. По умолчанию Docker запускает контейнеры с довольно ограниченным набором возможностей. Это, однако, можно изменить путем редактирования профиля. В зависимости от вашего хостинг-провайдера, включая DigitalOcean, sloppy.io, dotCloud и Quay.io, эти настройки могут отличаться от идущих по умолчанию. Если же вы развернули Docker самостоятельно, проверка привилегий, с которыми запускаются ваши контейнеры, не менее важна.
Каков бы ни был ваш хостинг-провайдер, следует прислушаться к руководству по безопасности Docker, в котором говорится:
Пользователям рекомендуется отключить все разрешения (capabilities), за исключением тех, которые точно необходимы их процессам.
Ответьте на следующие вопросы:
Какое сетевое соединение нужно вашему приложению?
Нужен ли ему доступ к raw-сокетам?
Будет ли оно работать по UDP?
Если нет, отключите все эти разрешения. А нужно ли вашему приложению то, что по умолчанию запрещено? Если да, предоставьте необходимое.
Для управления разрешениями контейнеров используются опции –cap-drop и –cap-add.
Предположим, что вашему приложению не нужно изменять разрешения процесса и привязывать привилегированные порты, но необходимо выгружать и загружать модули ядра.
Соответствующие разрешения можно настроить следующим образом:
Более подробную информацию об этих опциях вы можете найти в разделе документации «Runtime privilege and Linux capabilities».
Итак, у вас есть удостоверенный издателем образ и уменьшенный набор привилегий контейнера. Но насколько этот образ безопасен? Например, какие разрешения получит злоумышленник, завладев доступом к контейнеру? Или другими словами: насколько вы обезопасили свои контейнеры? Если атакующему удастся взломать контейнер, сможет ли он проникнуть куда-то еще? Если да, то с этим надо что-то делать.
Благодаря пространствам имен (namespaces) и контрольным группам (cgroups) Docker даже с настройками по умолчанию становится с каждым релизом все более безопасным, но вам необязательно полагаться только на эти функции. Можно сделать шаг вперед и воспользоваться дополнительными механизмами обеспечения безопасности Linux, такими как AppArmor, SELinux, grsecurity и Seccomp.
Все они являются зрелыми и хорошо протестированными инструментами, которые в состоянии усилить безопасность вашей системы. Если вы с ними еще не знакомы, вот их краткое описание.
AppArmor
AppArmor — программный инструмент упреждающей защиты, основанный на политиках безопасности (известных также как профили (англ. profiles)), которые определяют, к каким системным ресурсам и с какими привилегиями может получить доступ то или иное приложение. В AppArmor включен набор стандартных профилей, а также инструменты статического анализа и инструменты, основанные на обучении, позволяющие ускорить и упростить построение новых профилей. — Источник: Wikipedia.
SELinux (англ. Security-Enhanced Linux — Linux с улучшенной безопасностью) — реализация системы принудительного контроля доступа, которая может работать параллельно с классической избирательной системой контроля доступа. — Источник: Wikipedia.
Grsecurity — это проект для Linux, который включает в себя некоторые связанные с безопасностью улучшения, включая принудительный контроль доступа, рандомизацию ключевых локальных и сетевых информативных данных, ограничения /proc и chroot() jail, контроль сетевых сокетов, контроль возможностей и добавочные функции аудита.
Типичной областью применения являются web-серверы и системы, которые принимают удаленные соединения из сомнительных мест, такие как серверы, которые обеспечивают shell-доступ для пользователей. — Источник: Wikipedia.
seccomp (сокращение от secure computing mode) — это механизм обеспечения безопасности в ядре Linux. Он был помещен в основную ветку ядра Linux начиная с версии 2.6.12, которая была выпущена 8 марта 2005. Seccomp позволяет процессу перейти в «безопасное» состояние, из которого он не может выйти и в котором способен выполнить лишь ограниченный набор системных вызовов: exit(), sigreturn(), а также read() и write() к уже открытым файловым дескрипторам. При попытке выполнения других системных вызовов процесс будет завершен ядром с помощью SIGKILL. Таким образом, этот механизм не виртуализирует системные ресурсы, а практически полностью изолирует от них запущенный процесс. — Источник: Wikipedia
Рабочие примеры и более глубокое освещение представленных технологий выходит за рамки этой статьи. Однако я настоятельно рекомендую изучить их более подробно и использовать при построении инфраструктуры.
Каковы потребности вашего приложения? Может быть, ему нужно не более 50 МБ памяти? Тогда зачем позволять больше? Выполняет ли ваше приложение интенсивную обработку данных, для которой требуется 4 и более процессорных ядер? Тогда выделите необходимые ресурсы, но не более того.
Если анализ, профилирование и эталонное тестирование являются частью вашего процесса непрерывной разработки, информация о требуемых ресурсах уже должна быть под рукой.
В результате при развертывании контейнеров необходимо убедиться, что им выданы только необходимые ресурсы. Для этого в Docker предусмотрены следующие опции:
Пример ниже демонстрирует использование некоторых из этих параметров в конфигурационном файле Docker compose, взятом из официальной документации:
Более подробную информацию можно получить, воспользовавшись интерактивной справкой Docker или обратившись к разделу документации “Runtime constraints on resources”.
Последним из рассматриваемых в этой статье аспектов безопасности будет поверхность атаки, увеличение которой является следствием особенностей работы Docker. Это может случиться в любой IT-системе, однако проблема дополнительно усугубляется эфемерностью инфраструктуры, основанной на контейнерах.
Поскольку Docker позволяет создавать, разворачивать и удалять контейнеры очень быстро, бывает сложно уследить, какие точно приложения разворачиваются в вашей системе. Это, в свою очередь, ведет к потенциалу по увеличению поверхности атаки.
Не уверены по поводу статистики развертывания в организации? Задайте себе следующие вопросы:
Какие приложения развернуты в организации в данный момент?
Надеюсь, что вы не слишком обеспокоились, обдумывая ответы на эти вопросы. В любом случае давайте рассмотрим действия, которые можно предпринять.
Внедрите журнал аудита и соответствующее логирование
Помимо журналов аудита (audit trail) приложений, учитывающих время создания и активации учетной записи пользователя, последнего обновления пароля и т. д., рассмотрите внедрение журнала аудита контейнеров, создаваемых и разворачиваемых в организации.
Эта система не обязана быть очень сложной, но она должна фиксировать следующие события:
Большинство инструментов непрерывной разработки должны поддерживать регистрацию этой информации, что может быть реализовано напрямую в инструменте либо с помощью пользовательских скриптов, написанных на языке программирования по вашему выбору.
В дополнение рассмотрите создание оповещений, которые должны отправляться на email или в IRC, Slack, HipChat и т. п. Благодаря этому дополнительному уровню безопасности сотрудники получают информацию о выполненных развертываниях, что делает процесс более прозрачным. Теперь то, что не должно было произойти, уже не сможет пройти незамеченным.
Я не говорю, что вы не должны доверять сотрудникам и коллегам, но все же лучше быть в курсе происходящего.
Прошу понять меня правильно. Здесь важно не переусердствовать и не погрязнуть в создании множества новых процессов контроля. Этим можно добиться лишь потери времени на ненужную работу и утраты всех тех преимуществ, которые дают контейнеры.
При этом по крайней мере обдумывание поставленных вопросов, регулярная переоценка системы на их основе, а также способность принимать решения с их учетом должны помочь в уменьшении вероятности создания неизвестных поверхностей атаки в ваших системах.
Итак, мы рассмотрели 5 вопросов, связанных с безопасностью Docker, а также поговорили о возможных решениях. Надеюсь, что если вы переходите на Docker, раздумываете о таком шаге или уже это сделали, то предложенный материал поможет в усилении безопасности вашей контейнерной инфраструктуры.
Docker — это замечательная технология. Мне бы хотелось, чтобы она появились намного раньше. Теперь Docker с нами, и я надеюсь, что изложенная информация поможет вам построить работу с этим инструментом таким образом, чтобы он функционировал исключительно в ваших интересах, не подкидывая неприятные сюрпризы в плане безопасности.