Архив метки: docker

🐳 Популярные ошибки в конфигурации, которые делают контейнерные приложения уязвимыми для атак

С учетом того, что сегодня число интернет-пользователей поражает воображение, а сами веб-приложения выполняют больше задач, чем когда-либо в прошлом, масштабирование, поддержка и разработка крупных веб-приложений стали серьезной проблемой для команд DevOps.

Теперь, когда приложения масштабируются в нескольких публичных облаках с использованием различных технологических стеков, для их поддержки и развертывания требуются современные решения.

Итак, мы продолжаем статью 🦊 Современное состояние безопасности CI/CD и как предотвратить распространенные ошибки

Среди них популярны контейнерные приложения, обычно использующие такие технологии, как Docker.

Дальнейшее масштабирование стало возможным с появлением таких технологий, как Kubernetes.

Контейнерные приложения позволяют DevOps-командам поддерживать контейнеры с определенными конфигурациями и версиями приложений.

Эта практика также позволяет командам DevOps реплицировать их столько раз, сколько необходимо, и все это в автоматическом режиме.

Сочетание таких технологий, как Kubernetes (которая сделала развертывание, масштабирование и обслуживание контейнерных приложений очень простым) и Docker, зарекомендовало себя как решение для удовлетворения требований современных приложений, что привело к значительному росту их популярности в последнее время.

Однако внедрение новых технологий всегда чревато новыми ошибками и уязвимостями.

Поэтому такой риск требует такого же внимания, как и ручное развертывание, чтобы избежать наплыва уязвимостей, проникающих в ваше контейнерное и автоматизированное развертывание.

Какие популярные ошибки в конфигурации делают контейнерные приложения уязвимыми для атак?

Наиболее распространенным источником уязвимостей в таких технологиях, как Docker, Kubernetes и других технологиях автоматизации, таких как SaltStack, Ansible и Puppet, являются устаревшие версии программного обеспечения, а также отсутствие процедур усиления безопасности и надлежащего анализа конфигурации.

Права

Одна из самых основных форм неправильной конфигурации в Docker связана с повышением прав пользователя.

В таких случаях контейнеры Docker, запускаемые от имени root, представляют гораздо более серьезную угрозу безопасности, чем контейнеры, запускаемые не от имени root.

🐳 Почему процессы в контейнерах Docker не должны запускаться от имени Root

Если контейнер запускается под пользователем root и существуют уязвимости безопасности для данной версии Docker, то, например, в прошлом существовали уязвимости, которые позволяли злоумышленникам проникнуть на хост через уязвимое или неправильно сконфигурированное программное обеспечение, запущенное в самом контейнере.

🐳 Что такое Docker без root (rootless)?

Такое уязвимое программное обеспечение также позволяло злоумышленникам выйти из контейнера и проникнуть на хост-сервер, на котором запущен контейнер.

Именно поэтому настоятельно рекомендуется запускать контейнеры под управлением пользователей с нужным количеством разрешений и доступа.

Docker поддерживает работу в режиме пользователя rootless/non-root, что позволяет значительно повысить уровень безопасности, конфигурацию которого можно увидеть в официальном руководстве по Docker.

Безопасность данных/конфигурации

Место хранения данных – важный момент при работе с контейнерами, такими как Docker, как и знание того, что хранение данных внутри контейнеров часто имеет больше недостатков, чем преимуществ.

Настоятельно рекомендуется хранить любые пользовательские данные вне контейнера; в случае обнаружения уязвимостей контейнер можно уничтожить, обновить и развернуть из чистого состояния.

Это позволяет автоматизировать работу с CVE, поскольку данные не находятся внутри контейнера.

Хранение учетных данных – еще одна частая ошибка конфигурации.

Как и в случае с хранением данных, рекомендуется избегать хранения учетных данных внутри самого контейнера, чтобы в случае возникновения уязвимости контейнер можно было легко обновить и снова запустить без утечки учетных данных.

Монтирование данных вне контейнера легко выполняется с помощью Docker.

Рассмотрим следующую команду Docker run:

docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started

В этом примере данные хранятся вне контейнера по пути /etc/todos – таким образом, контейнер работает независимо, позволяя создавать, уничтожать или перемещать контейнер, сохраняя данные как есть и в отдельном пути.

Автоматизация безопасности

Такие технологии, как Ansible, SaltStack и Puppet, используются для автоматизации задач, которые необходимо выполнять на большом количестве серверов.

Эти технологии используют файлы конфигурации, называемые “плейбуки”, которые содержат информацию о том, что и где должно быть выполнено.

Для работы этих плейбуков необходим доступ к серверам через SSH или аналогичный доступ на уровне консоли.

Однако выполнение этих  задачот имени root и/или хранение паролей root в открытом виде в этих конфигурациях может привести к инцидентам, связанным с безопасностью, в случае утечки конфигураций.

Рассмотрим следующий пример.

Чтобы установить MySQL с помощью Ansible:

- hosts: webservers

  user: vagrant

  sudo: true

  vars_files:

    - vars.yml



  tasks:

    - name: Install MySQL

      action: apt pkg=$item state=installed

      with_items:

        - mysql-server-core-5.5

        - mysql-client-core-5.5

        - libmysqlclient-dev

        - python-mysqldb

        - mysql-server

        - mysql-client



    - name: Start the MySQL service

      action: service name=mysql state=started



    - name: Remove the test database

      mysql_db: name=test state=absent



    - name: Create deploy user for mysql

      mysql_user: user="deploy" host="%" password={{mysql_root_password}} priv=*.*:ALL,GRANT



    - name: Ensure anonymous users are not in the database

      mysql_user: user='' host=$item state=absent

      with_items:

        - 127.0.0.1

        - ::1

        - localhost



    - name: Copy .my.cnf file with root password credentials

      template: src=templates/.my.cnf dest=/etc/mysql/my.cnf owner=root mode=0600



    - name: Update mysql root password for all root accounts

      mysql_user: name=root host={{item}} password={{mysql_root_password}}

      with_items:

        - 127.0.0.1

        - ::1

        - localhost

Как показано выше, мы можем использовать Ansible для установки определенной версии MySQL (5.5), запуска службы, удаления всех тестовых баз данных, добавления пользователя, удаления всех анонимных пользователей, копирования существующего файла my.cnf и обновления пароля root.

Поскольку пароль root MySQL, различная другая конфиденциальная информация (например, версия MySQL) и имя развернутого пользователя MySQL хранятся прямо в конфигурационном файле, безопасность становится насущной проблемой.

Раскрытие конфигурации/файлов

Конфигурация/файлы Docker могут быть идеальной демонстрацией такого рода неправильной конфигурации.

Давайте рассмотрим пример базового официального файла Docker compose (docker-compose.yml), используемого для установки WordPress с MySQL:

version: "3.9"



services:

  db:

    image: mysql:5.7

    volumes:

      - db_data:/var/lib/mysql

    restart: always

    environment:

      MYSQL_ROOT_PASSWORD: somewordpress

      MYSQL_DATABASE: wordpress

      MYSQL_USER: wordpress

      MYSQL_PASSWORD: wordpress



  wordpress:

    depends_on:

      - db

    image: wordpress:latest

    volumes:

      - wordpress_data:/var/www/html

    ports:

      - "8000:80"

    restart: always

    environment:

      WORDPRESS_DB_HOST: db

      WORDPRESS_DB_USER: wordpress

      WORDPRESS_DB_PASSWORD: wordpress

      WORDPRESS_DB_NAME: wordpress

volumes:

  db_data: {}

  wordpress_data: {}

Здесь мы видим, что MySQL 5.7 установлен из последнего образа WordPress, доступного на Docker Hub, а также пароль корня MySQL, имя базы данных, имя пользователя и пароль пользователя.

Поскольку конфигурация контейнера Docker перечисляется в виде обычного/человекочитаемого текста, включая пароли к базе данных, пути к данным и файлам, необходимо убедиться не только в надежности хранения файлов, но и в том, что любая часть автоматизированных процессов развертывания обеззараживается при использовании для развертывания контейнеров.

Обнаружение подобных проблем с конфигурацией позволяет blue team предотвратить утечку конфиденциальных данных, которые впоследствии могут быть использованы для осуществления более сложных атак на облачную инфраструктуру.

Раскрытие Kubernetes

Rancher – это мультикластерная платформа оркестровки с открытым исходным кодом, которая позволяет операционным командам развертывать, управлять и обеспечивать безопасность корпоративных Kubernetes.

Это программное обеспечение для оркестрации часто становится жертвой неправильной конфигурации; например, учетные данные администратора по умолчанию часто полностью раскрываются, что, в свою очередь, используется злоумышленниками.

Раскрытие консоли Kubernetes

Консоль Kubernetes (или дашборд) является важной частью настройки Kubernetes.

Она позволяет получить полный обзор всех контейнеров, управляемых кластером Kubernetes, включая их состояние, использование памяти, других ресурсов, а также различные функции управления.

Раскрытие этой консоли может привести к различным типам атак.

В прошлом открытые консоли использовались для организации майнинга криптовалюты, что может привести к финансовым потерям и замедлению работы приложений из-за потребления ресурсов процессами майнинга криптовалюты.

Раскрытие Kubernetes Kustomization

Инструмент Kubernetes Kustomization используется для настройки объектов Kubernetes с помощью файла “Kustomization”.

Со страницы проектов Kustomize позволяет настраивать необработанные, свободные от шаблонов YAML-файлы для различных целей, оставляя исходный YAML нетронутым и пригодным для использования как есть.

В свою очередь, такая возможность настройки конфигураций делает новые файлы Kustomization довольно мощными, позволяя объединять различные существующие файлы конфигурации Kubernetes.

Однако доступ к этим файлам может привести к утечке большого количества конфиденциальных данных из вашей организации, поэтому очень важно обеспечить постоянную безопасность и дезинфекцию этих файлов.

А как насчет уязвимостей программного обеспечения для оркестрации контейнеров?

Правильная настройка этих инструментов необходима для обеспечения безопасности развертывания контейнерных приложений.

В конце концов, недостатки безопасности в этих инструментах могут быть как простыми, например, приборная панель позволяет обходить аутентификацию, так и сложными, например, приборная панель имеет уязвимости безопасности, позволяющие проводить инъекционные атаки shell.

Учитывая  уязвимость CVE-2020-16846, затрагивающую SaltStack Salt, программное обеспечение для управления инфраструктурой и оркестровки контейнеров, CVE позволяла осуществлять атаки shell injection при отправке определенных веб-запросов к API SaltStack.

Проще говоря, поскольку уязвимости возможны в стеке управления вашей контейнерной инфраструктурой, безопасность всего вашего контейнерного приложения, вероятно, также находится под угрозой.

Заключение

Наряду с неизбежным переходом на контейнерные приложения, используемые в организациях для обеспечения более простого управления, быстрого развертывания и эффективного масштабирования веб-приложений, возникают определенные критические проблемы безопасности, которые часто упускаются из виду при рассмотрении различных преимуществ (а иногда и сложностей) управления этими кластерами контейнеров.

Хотя контейнеры доказали, что они помогают обеспечить согласованность, сократить время развертывания и другие проблемы, связанные с версиями и конфигурациями программного обеспечения для разработчиков и производственных инстансов, они также создают извечную проблему обеспечения правильной и безопасной конфигурации.

Это особенно актуально для конфигурационных файлов, содержащих детали, касающиеся каждого аспекта развертываемого программного обеспечения: пути к данным, пароли баз данных и другие учетные данные доступа.

 



2022-09-09T13:33:57
Закрытие уязвимостей

Как очистить логи запущенных контейнеров Docker

Долго работающие контейнеры Docker могут быстро накапливать большое количество логов.

Они потребляют емкость хранилища и снижают производительность при доступе к данным и их фильтрации.

Хотя Docker содержит встроенные инструменты для просмотра логов, в нем нет встроенного механизма для их очистки.

В этой статье вы узнаете, как очистить логи запущенных контейнеров Docker, не перезапуская и не заменяя их.

Вы также увидите некоторые методы более эффективной работы с большими журналами, сохраняя при этом старые данные. Читать

🐳 Почему процессы в контейнерах Docker не должны запускаться от имени Root

Процессы в контейнере Docker не должны запускаться от имени root.

Безопаснее запускать приложения от имени пользователя, не являющегося root, которое вы указываете в Dockerfile или при использовании docker run.

Это минимизирует риск, так как уменьшает поверхность атаки для любых угроз в вашем контейнере.

В этой статье вы узнаете об опасностях, связанных с запуском контейнерных приложений от имени root.

Вы также увидите, как создать пользователя, не являющегося root, и настроить namespacing в ситуациях, когда это невозможно.

Почему запуск от имени Root опасен?

По умолчанию контейнеры запускаются от имени root.

Демон Docker запускается от имени root на вашем хосте, и запущенные контейнеры также будут запускаться от имени root.

Хотя может показаться, что root внутри контейнера является независимым пользователем, на самом деле это то же самое, что и учетная запись root на вашем хосте.

Разделение обеспечивается только механизмами изоляции контейнеров Docker.

Нет прочной физической границы; ваш контейнер – это еще один процесс, запущенный пользователем root на ядре вашего хоста.

Это означает, что уязвимость в вашем приложении, среде выполнения Docker или ядре Linux может позволить злоумышленникам выйти из контейнера и выполнить операции с правами root на вашей машине.

Существуют некоторые встроенные средства защиты, которые снижают риск возникновения такой ситуации.

Root внутри контейнера является непривилегированным и имеет ограниченные возможности. Это не позволяет контейнеру использовать команды системного администрирования, если вы вручную не добавите возможности или не используете привилегированный режим при запуске контейнеров.

Несмотря на эти меры, разрешение запуска приложений от имени root остается опасным.

Так же, как вы ограничиваете использование root в традиционной среде, неразумно без необходимости использовать его в контейнерах.

Вы создаете слишком привилегированную среду, которая дает злоумышленникам больше возможностей в случае взлома.

Запуск контейнеризированных приложений от имени пользователя, не являющегося привилегированным пользователем

Наилучшей практикой для контейнерных приложений является запуск от имени обычного пользователя.

Большинству программ не нужен root-доступ, поэтому смена пользователя обеспечивает немедленный уровень защиты от взлома контейнера.

Вы должны создать новую учетную запись пользователя как один из последних этапов в вашем Dockerfile.

Этого можно добиться с помощью инструкции USER:

FROM base-image:latest

RUN apt install demo-package

USER demo-user:demo-group

ENTRYPOINT ["demo-binary"]

Контейнеры, запущенные с этого образа, будут работать под именем demo-user.

Пользователь будет членом группы demo-group.

Вы можете опустить имя группы, если вам не нужно, чтобы пользователь состоял в группе:

USER demo-user

Вместо имен можно указать идентификатор пользователя (UID) и идентификатор группы (GID):

USER 950:950

Выделение известных UID и GID обычно является самым безопасным способом.

Это предотвращает сопоставление пользователя в контейнере с чрезмерно привилегированной учетной записью хоста.

USER часто указывается как предпоследняя ступень в Dockerfile.

Это означает, что вы все еще можете выполнять операции, требующие root, на более ранних этапах сборки образа.

Инструкция apt install в приведенном выше примере имеет законную потребность в root.

Если бы инструкция USER была размещена выше, apt был бы запущен от имени demo-user, который не имел бы необходимых прав.

Поскольку инструкции Dockerfile относятся только к сборкам образов, а не к запущенным контейнерам, безопасно оставить изменение пользователя на более поздний период в Dockerfile.

Изменение пользователя, под которым запускается контейнер, может потребовать обновления разрешений на файлы и папки, к которым он обращается.

Установите права на все пути, которые будут использоваться вашим приложением:

COPY initial-config.yaml /data/config.yaml



USER demo-user:demo-group

RUN chown demo-user:demo-group /data

В этом примере каталог /data должен принадлежать demo-user, чтобы приложение могло вносить изменения в свой конфигурационный файл.

Предыдущий оператор COPY скопирует файл от имени root.

Можно воспользоваться сокращенным вариантом, используя флаг –chown вместе с copy:

COPY --chown=demo-user:demo-group initial-config.yaml /data/config.yaml

В этом примере каталог /data должен принадлежать demo-user, чтобы приложение могло вносить изменения в свой конфигурационный файл.

Предыдущий оператор COPY скопирует файл от имени root.

Можно воспользоваться сокращенным вариантом, используя флаг –chown вместе с copy:

$ docker run -d --user demo-user:demo-group demo-image:latest

$ docker run -d --user demo-user demo-image:latest

$ docker run -d --user 950:950 demo-image:latest

Флаг –user запускает процесс контейнера от имени указанного пользователя.

Он менее безопасен, чем инструкция Dockerfile USER, поскольку вам придется применять его отдельно к каждой команде docker run.

Лучшим вариантом для регулярно используемых образов является создание собственного производного образа, в котором можно установить новую учетную запись пользователя:

FROM image-that-runs-as-root:latest

USER demo-user

$ docker build . -t image-that-now-runs-as-non-root:latest

Изменение пользователя стороннего образа может вызвать проблемы: если контейнер ожидает запуска от имени root или нуждается в доступе к путям файловой системы, принадлежащим root, вы увидите ошибки при использовании приложения.

Вы можете попробовать вручную изменить права на пути, вызывающие проблемы.

Кроме того, проверьте, есть ли у поставщика поддерживаемый метод запуска приложения с непривилегированной учетной записью пользователя.

Работа с приложениями, которые должны запускаться от имени root

Разделение имен пользователей – это техника работы с приложениями, которым нужны некоторые привилегии root.

Она позволяет сопоставить root внутри контейнера с пользователем, не являющимся root, на вашем хосте.

Имитированный root в контейнере имеет необходимые привилегии, но вынос не даст root-доступа на хост.

Ремаппинг пространства имен активируется путем добавления поля userns-remap в файл /etc/docker/daemon.json:

{

    "userns-remap": "default"

}

Использование default в качестве значения для userns-remap инструктирует Docker автоматически создать нового пользователя на вашем хосте под именем dockremap.

Root в контейнерах будет отображаться на dockremap на вашем хосте.

При желании можно указать существующего пользователя и группу, используя комбинацию UID/GID или имя пользователя/имя группы:

{

    "userns-remap": "demo-user"

}

Перезапустите демон Docker после применения изменений:

sudo service docker restart

Если вы используете nsuser-remap: default, пользователь dockremap теперь должен существовать на вашем хосте:

id dockremap
uid=140(dockremap) gid=119(dockremap) groups=119(dockremap)

Пользователь также должен появиться в файлах идентификаторов /etc/subuid и /etc/subgid:

$ dockremap:231500:65535

Пользователю выделен диапазон из 65 535 подчиненных идентификаторов, начиная с 231500.

В пространстве имен пользователей идентификатор 231500 сопоставлен с 0, что делает его корневым пользователем в ваших контейнерах.

Будучи UID с высоким номером, 231500 не имеет привилегий на хосте, поэтому атаки на взлом контейнеров не смогут нанести такой большой ущерб.

Все запущенные вами контейнеры будут работать с измененным пространством имен пользователей, если вы не откажетесь от этого с помощью docker run –userns=host.

Механизм работает путем создания каталогов с разнесенными именами внутри /var/lib/docker, которые принадлежат подчиненным UID и GID пользователя с разнесенными именами:

$ sudo ls -l /var/lib/docker/231500.231500



total 14

drwx------ 5 231500 231500 13 Jul 22 19:00 aufs

drwx------ 3 231500 231500 13 Jul 22 19:00 containers

...

Сопоставление имен пользователей – это эффективный способ повысить изоляцию контейнеров, избежать выходов из них и сохранить совместимость с приложениями, которым требуются привилегии root.

Однако есть некоторые недостатки: эта функция лучше всего работает на свежем экземпляре Docker, тома, смонтированные с хоста, должны иметь настроенные разрешения, а некоторые драйверы внешних хранилищ вообще не поддерживают сопоставление пользователей.

Перед использованием этой опции следует изучить документацию.

Заключение

Запуск контейнерных приложений от имени root представляет собой риск для безопасности.

Хотя это легко упустить из виду, изоляция, обеспечиваемая контейнерами, недостаточно сильна, чтобы полностью отделить пользователей ядра от пользователей контейнера.

Root в контейнере – это тот же root, что и root на вашем хосте, поэтому успешная компрометация может обеспечить контроль над вашей машиной.

Как автор образа, вы должны включить инструкцию USER в свой Dockerfile, чтобы ваше приложение запускалось без root.

Пользователи образа могут отменить эту инструкцию с помощью docker run –user, чтобы назначить определенные UID и GID. Это поможет смягчить ситуацию, когда образ обычно использует root.

Вы можете еще больше усилить безопасность, удалив все возможности из контейнера с помощью –cap-drop=ALL, а затем включив в белый список те, которые необходимы, с помощью флагов –cap-add.

Сочетание этих методов позволит запустить ваше приложение от имени пользователя, не являющегося root, с минимальным набором необходимых привилегий, что повысит уровень безопасности.

см. также:



2022-08-18T15:00:27
Закрытие уязвимостей

Установка Docker в Debian 11

Docker — одна из самых популярных программ для контейнеризации. В терминологии Docker, контейнеры – это стандартная единица программного обеспечения, которая упаковывает код и все его зависимости, для того чтобы приложение можно было быстро и надежно запустить на разных операционных системах и в разных вычислительных средах.

С технической точки зрения контейнер — это запущенный процесс (наподобие процессов в операционных системах) который изолирован от других процессов и имеет доступ к ресурсам компьютера. В данной статье будет рассмотрена установка Docker в Debian 11, а также запуск первого приложения. Читать

Установка Docker в Ubuntu

Docker — флагман рынка ПО для контейнеризации и микросервисов, появившийся в 2013 году и до сих пор остающийся основным игроком. Для его использования в Ubuntu не требуется установки отдельной ВМ, т.к. он использует возможности ядра.
Читать

Docker Tips: Очистите свою машину от хлама /Как удалить старые и не используемые образы Docker

Сегодня мы поговорим о том, как Docker использует дисковое пространство хостовой машины, а также разберемся в том, как это пространство освободить от ошметков неиспользуемых образов и контейнеров.







Общее потребление




Docker – крутая штука, наверное сегодня мало кто в этом сомневается. Всего несколько лет назад этот продукт предоставил нам совершенно новый способ построения, доставки и запуска любого окружения, позволяя значительно сэкономить ресурсы процессора и оперативной памяти. В дополнение к этому (а для кого-то это будет даже самым важным) Docker позволил нам невероятно упростить и унифицировать управление жизненным циклом используемых рабочих сред.




Однако, за все эти прелести современной жизни приходится платить. Когда мы запускаем контейнеры, скачиваем или создаем собственные образы, разворачиваем сложные экосистемы, нам приходится платить. И платим мы, в том числе, дисковым пространством.




Если вы никогда не задумывались о том, сколько же места реально занято на вашей машине Docker’ом, то можете быть неприятно удивлены выводом этой команды:




$ docker system df







Здесь отображено использование диска Docker’ом в различных разрезах:







Готов поспорить, что уже после этого простого перечисления вы горите желанием почистить диск от мусора и вернуть к жизни драгоценные гигабайты (прим. перев.: особенно, если за эти гигабайты вы ежемесячно перечисляете арендную плату).




Использование диска контейнерами




Каждый раз при создании контейнера на хостовой машине в каталоге /var/lib/docker создается несколько файлов и каталогов, среди которых стоит отметить следующие:







Давайте представим себе систему, на которой установлен девственно чистый Docker, ни разу не участвовавший в запуске контейнеров и сборке образов. Его отчет об использовании дискового пространства будет выглядеть так:




$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         0          0          0B         0B
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B




Запустим какой-нибудь контейнер, например, NGINX:




$ docker container run --name www -d -p 8000:80 nginx:1.16




Что происходит с диском:







$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          2B         0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B




Судя по выводу, у нас еще нет пространства, которое мы могли бы высвободить. Так как 2 байта это совершенно несерьезно, давайте представим, что наш NGINX неожиданно для всех написал куда-то 100 Мегабайт данных и создал внутри себя файл test.img именно такого размера.




$ docker exec -ti www 
  dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]




Снова исследуем использование дискового пространства на хосте. Мы увидим, что контейнер (containers) занимает там 100 Мегабайт.




$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          104.9MB    0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B




Думаю, ваш пытливый мозг уже задается вопросом, где же находится наш файл test.img. Давайте его поищем:




$ find /var/lib/docker -type f -name test.img
/var/lib/docker/overlay2/83f177...630078/merged/test.img
/var/lib/docker/overlay2/83f177...630078/diff/test.img




Не вдаваясь в подробности можно отметить, что файл test.img удобно расположился на уровне чтения-записи, управляемом драйвером overlay2. Если же мы остановим наш контейнер, то хост подскажет нам, что это место, в принципе, можно высвободить:




# Stopping the www container
$ docker stop www

# Visualizing the impact on the disk usage
$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          0          104.9MB    104.9MB (100%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B




Как мы можем это сделать? Удалением контейнера, которое повлечет за собой очистку соответствующего пространства на уровне чтения-записи.




С помощью следующей команды вы можете удалить все установленные контейнеры одним махом и очистить ваш диск от всех созданных ими на уровне чтения-записи файлов:




$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2

Total reclaimed space: 104.9MB




Итак, мы высвободили 104,9 Мегабайта удалением контейнера. Но так как мы уже не используем скачанный ранее образ, то он тоже становится кандидатом на удаление и высвобождение наших ресурсов:




$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          0          126M       126M (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B




Внимание: до тех пор, пока образ используется хотя бы одним контейнером, вы не сможете использовать этот трюк.




Субкоманда prune, которую мы использовали выше, дает эффект только на остановленных контейнерах. Если мы хотим удалить не только остановленные, но и запущенные контейнеры, следует использовать одну из этих команд:




# Historical command
$ docker rm -f $(docker ps –aq)

# More recent command
$ docker container rm -f $(docker container ls -aq)




Заметки на полях: если при запуске контейнера использовать параметр —rm, то при его остановке будут высвобождено все дисковое пространство, которое он занимал.




Использование диска образами




Несколько лет назад размер образа в несколько сотен мегабайт был совершенно нормальным: образ Ubuntu весил 600 Мегабайт, а образ Microsoft .Net – несколько Гигабайт. В те лохматые времена скачивание одного только образа могло нанести большой урон вашему свободному месту на диске, даже если вы расшаривали уровни между образами. Сегодня – хвала великим – образы весят намного меньше, но даже в этом случае можно быстро забить имеющиеся ресурсы, если не принимать некоторых мер предосторожности.




Есть несколько типов образов, которые напрямую не видны конечному пользователю:







$ docker image ls -f dangling=true
REPOSITORY  TAG      IMAGE ID         CREATED             SIZE
none      none   21e658fe5351     12 minutes ago      71.3MB




Удалить их можно следующим способом:




$ docker image rm $(docker image ls -f dangling=true -q)




Мы можем использовать также субкоманду prune:




$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
deleted: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
deleted: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
deleted: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
deleted: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f

Total reclaimed space: 12.9kB




Если мы вдруг захотим удалить вообще все образы (а не только dangling) одной командой, то можно сделать так:




$ docker image rm $(docker image ls -q)




Использование диска томами




Тома (volumes) применяются для хранения данных за пределами файловой системы контейнера. Например, если мы хотим сохранить результаты работы какого-либо приложения, чтобы использовать их как-то еще. Частым примером являются базы данных.




Давайте запустим контейнер MongoDB, примонтируем к нему внешний по отношению к контейнеру том, и восстановим из него бэкап базы данных (у нас он доступен в файле bck.json):




# Running a mongo container
$ docker run --name db -v $PWD:/tmp -p 27017:27017 -d mongo:4.0

# Importing an existing backup (from a huge bck.json file)
$ docker exec -ti db mongoimport 
  --db 'test' 
  --collection 'demo' 
  --file /tmp/bck.json 
  --jsonArray




Данные будут находиться на хостовой машине в каталоге /var/lib/docker/volumes. Но почему не на уровне чтения-записи контейнера? Потому что в Dockerfile образа MongoDB каталог /data/db (в котором MongoDB по умолчанию хранит свои данные) определен как том (volume).







Заметки на полях: многие образы, в результате работы которых должны создаваться данные, используют тома (volumes) для сохранения этих самых данных.




Когда мы наиграемся с MongoDB и остановим (а может даже и удалим) контейнер, том не будет удален. Он продолжит занимать наше драгоценное дисковое пространство до тех пор, пока мы явно не удалим его такой командой:




$ docker volume rm $(docker volume ls -q)




Ну или мы можем использовать уже знакомую нам субкоманду prune:




$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da

Total reclaimed space: 25.82GB
luc@saturn:~$




Использование диска для кэша сборки образов




В Docker 18.09 процесс создания образов претерпел некоторые изменения благодаря инструменту BuildKit. С помощью этой штуки увеличивается скорость процесса, оптимизируется управление хранением данных и безопасностью. Здесь мы не будем рассматривать все детали этого замечательного инструмента, остановимся лишь нам том, как он затрагивает вопросы использования дискового пространства.




Предположим, что у нас есть совершенно простое приложение Node.Js:







$ cat index.js
var express = require('express');
var util    = require('util');
var app = express();
app.get('/', function(req, res) {
  res.setHeader('Content-Type', 'text/plain');
  res.end(util.format("%s - %s", new Date(), 'Got Request'));
});
app.listen(process.env.PORT || 80);




$ cat package.json
    {
      "name": "testnode",
      "version": "0.0.1",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "dependencies": {
        "express": "^4.14.0"
      }
    }




Dockerfile для сборки образа выглядит так:




FROM node:13-alpine
COPY package.json /app/package.json
RUN cd /app && npm install
COPY . /app/
WORKDIR /app
EXPOSE 80
CMD ["npm", "start"]




Давайте соберем образ обычным способом, без использования BuildKit:




$ docker build -t app:1.0 .




Если мы проверим использование дискового пространства, то увидим, что место занимают только базовый образ (node:13-alpine) и конечный образ (app:1.0):




TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         2          0          109.3MB    109.3MB (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B




Давайте соберем вторую версию нашего приложения, уже с использованием BuildKit. Для этого нам лишь необходимо установить переменную DOCKER_BUILDKIT в значение 1:




$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .




Если мы сейчас проверим использование диска, то увидим, что теперь там участвует кэш сборки (buid-cache):




$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         2          0          109.3MB    109.3MB (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    11         0          8.949kB    8.949kB




Для его очистки воспользуемся следующей командой:




$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Deleted build cache objects:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi

Total reclaimed space: 8.949kB




Очистить все!




Итак, мы рассмотрели очистку дискового пространства, занятого контейнерами, образами и томами. В этом нам помогает субкоманда prune. Но ее можно использовать и на системном уровне docker, и она очистит все, что только сможет:




$ docker system prune
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache

Are you sure you want to continue? [y/N]




Если вы по каким-либо причинам экономите дисковое пространство на машине с Docker, то периодический запуск этой команды стоит ввести в привычку.




====================================================================




Так как в Docker более менее адекватный механизм удаления старых образов и контейнеров появился в версии 1.13: PR 26108 (за счет параметра prune который удаляет все старые контейнеры volume без контейнеров и образа без контейнеров), но зная что с каждой новой версией кол-во багов и проблем ростет, я лично не рискую обновляться, потому использую такие механизмы:




Удаление всех не используемых images




docker rmi $(docker images --filter "dangling=true" -q --no-trunc)




Удаление контейнеров в статусе “exited




docker rm $(docker ps -qa --no-trunc --filter "status=exited")




Удаление не используемых volume




docker volume ls -qf "dangling=true" | xargs docker volume rm




Все это можно совместить в один алиас для bash окружения:




alias docker-clean=' 
  docker ps --no-trunc -aqf "status=exited" | xargs docker rm ; 
  docker images --no-trunc -aqf "dangling=true" | xargs docker rmi ; 
  docker volume ls -qf "dangling=true" | xargs docker volume rm'




Источник: