Образи і контейнери Docker в картинках

Переклад поста Visualizing Docker Containers and Images, від новачка до новачків, автор на простих прикладах пояснює базові сутності і процеси у використанні docker.

Якщо ви не знаєте, що таке Docker або не розумієте, як він співвідноситься з віртуальними машинами або з інструментами configuration management, то цей пост може здатися трохи складною.

Пост призначений для тих, хто намагається освоїти docker cli, зрозуміти, чим відрізняється контейнер і образ. Зокрема, буде пояснена різниця між контейнером і запущеним контейнером.

docker container

В процесі освоєння потрібно уявити собі деякі лежать в основі деталі, наприклад, шари файлової системи UnionFS. Протягом останньої пари тижнів я вивчав технологію, я новачок в світі docker, командний рядок docker здалася мені досить складною для освоєння.

По-моєму, розуміння того, як технологія працює зсередини — кращий спосіб швидко освоїти новий інструмент і правильно його використовувати. Часто нова технологія розробляє нові моделі абстракцій і привносить нові терміни й метафори, які можуть бути як ніби зрозумілі на початку, але без чіткого розуміння ускладнюють подальше використання інструменту.

Хорошим прикладом є Git. Я не міг зрозуміти Git, поки не зрозумів його базову модель, включаючи trees, blobs, commits, tags, tree-ish та інше. Я думаю, що люди, які не розуміють нутрощі Git, не можуть майстерно використовувати цей інструмент.

Визначення способу (Image)

Візуалізація образу представлена нижче у двох видах. Образ можна визначити як «сутність» або «загальний вигляд» (union view) стека шарів тільки для читання.

docker_image

Зліва ми бачимо стек шарів для читання. Вони показані тільки для розуміння внутрішнього пристрою, вони доступні поза запущеного контейнера на хост-системі. Важливо те, що вони доступні тільки для читання (иммутабельны), а всі зміни відбуваються у верхньому шарі стека. Кожен шар може мати одного батька, батько теж має батьків і і. д. Шар верхнього рівня може бути використаний як UnionFS (AUFS в моєму випадку з docker) і представлений у вигляді єдиної read-only файлової системи, в якій відображені всі шари. Ми бачимо цю «сутність» образу на малюнку праворуч.

Якщо ви захочете подивитися на ці верстви в первозданному вигляді, ви можете знайти їх в файловій системі на хост-машині. Вони не видно безпосередньо з запущеного контейнера. На моїй хост-машині я можу знайти образи в /var/lib/docker/aufs.

# sudo tree -L 1 /var/lib/docker/
/var/lib/docker/
├── aufs
├── containers
├── graph
├── init
├── linkgraph.db
├── repositories-aufs
├── tmp
├── trust
└── volumes

7 directories, 2 files

Визначення контейнер (Container)

Контейнер можна назвати «сутністю» стека шарів з верхнім шаром для запису.

docker_container

На малюнку вище показано приблизно те ж саме, що на зображенні про спосіб, крім того, що верхній шар доступний для запису. Ви могли помітити, що це визначення нічого не говорить про те, запущений контейнер чи ні, і це неспроста. Поділ контейнерів на запущені і не запущені усунула плутанину в моєму розумінні.

Контейнер визначає лише шар для запису нагорі образу (стека шарів для читання). Він не запущений.

Визначення запущеного контейнера

Запущений контейнер — це «загальний вигляд» контейнера для читання-запису та його ізольованого простору процесів. Нижче зображено контейнер у своєму просторі процесів.

docker_container_running

Ізоляція файлової системи забезпечується технологіями рівня ядра, cgroups, namespaces та інші, дозволяють докеру бути такою перспективною технологією. Процеси в просторі контейнера можуть змінювати, видаляти або створювати файли, які зберігаються у верхньому шарі для запису. Дивіться зображення:

docker_touch_file

Щоб перевірити це, виконайте команду на хост-машині:

docker run ubuntu touch happiness.txt

Ви можете знайти новий файл в шарі для запису на хост-машині, навіть якщо контейнер не запущений.

# find / -name happiness.txt
/var/lib/docker/aufs/diff/860a7b...889/happiness.txt

Визначення шару образу (Image layer)

Нарешті, ми визначимо шар образу. Зображення нижче являє шар образу і дає нам зрозуміти, що шар — це не просто зміни у файловій системі.

docker_layer

Метадані — додаткова інформація про шарі, яка дозволяє докеру зберігати інформацію під час виконання та під час збирання. Обидва види шарів (для читання і для запису) містять метадані.

docker_container_metadata

Крім того, як ми вже згадували раніше, кожен шар містить вказівник на батька, використовуючи id (на зображенні батьківські шари внизу). Якщо шар не вказує на батьківський шар, значить він нагорі стека.

docker_image_metadata

Розташування метаданих
На даний момент (я розумію, що розробники docker можуть пізніше змінити реалізацію), метадані шарів образів (для читання) знаходяться у файлі з іменем «json» у теці /var/lib/docker/graph/id_слоя:

/var/lib/docker/graph/e809f156dc985.../json

де «e809f156dc985...» — урізаний id шару.

З'єднаємо всі разом

Тепер, давайте подивимося на команди, ілюстровані зрозумілими малюнками.

docker create <image-id>
До:
docker create input

Після:
docker create output

Команда 'docker create' додає шар для запису наверх стека шарів, знайденого за <image-id>. Команда не запускає контейнер.

docker create

docker start <container-id>
До:
docker create output

Після:
docker start output

Команда 'docker start' створює простір процесів навколо шарів контейнера. Може бути тільки один простір процесів на один контейнер.

docker run <image-id>
До:
docker run input

Після:
docker run output

Один з перших питань, яке задають люди (я теж задавав): «В чому різниця між «docker start' і 'docker run'?» Одна з початкових цілей цього посту — пояснити цю тонкість.

docker run

Як ми бачимо, команда 'docker run' знаходить образ, створює контейнер поверх нього і запускає контейнер. Це зроблено для зручності і приховує деталі двох команд.

Продовжуючи порівняння з освоєнням Git, я скажу, що 'docker run' дуже схожа на 'git pull'. Так само, як і 'git pull' (який об'єднує 'git fetch' і 'git merge'), команда 'docker run' об'єднує дві команди, які можуть використовуватися незалежно. Це зручно, але спочатку може ввести в оману.

docker ps
docker ps

Команда 'docker ps' виводить список запущених контейнерів на вашої хост-машині. Важливо розуміти, що в цей список входять тільки запущені контейнери, не запущені контейнери приховані. Щоб переглянути список всіх контейнерів, потрібно використовувати наступну команду.

docker ps -a
docker ps -a

Команда 'docker ps -a', де 'a' — скорочення від 'all' виводить список всіх контейнерів, незалежно від їх стану.

docker images
image

Команда 'docker images' виводить список образів верхнього рівня (top-level images). Фактично, нічого особливого не відрізняє образ від шару для читання. Тільки ті образи, які мають приєднані контейнери або ті, що були отримані за допомогою pull, вважаються образами верхнього рівня. Це розходження потрібно для зручності, так як за кожним чином верхнього рівня може бути безліч шарів.

docker images -a
docker images -a

Команда 'docker images -a' виводить всі образи на хост-машині. Це фактично список всіх шарів для читання в системі. Якщо ви хочете побачити всі верстви одного образу, скористайтеся командою 'docker history'.

docker stop <container-id>
До:
docker stop input

Після:
docker stop output

Команда 'docker stop' посилає сигнал SIGTERM запущеному контейнера, що м'яко зупиняє всі процеси в просторі процесів контейнера. В результаті ми отримуємо не запущений контейнер.

docker kill <container-id>
До:
docker kill input

Після:
docker kill output

Команда 'docker kill' посилає сигнал SIGKILL, що негайно завершує всі процеси в поточному контейнері. Це майже те ж саме, що натиснути Ctrl+\ в терміналі.

docker pause <container-id>
До:
docker pause input

Після:
docker pause output

На відміну від 'docker stop' і 'docker kill', які посилають справжні UNIX сигнали процесів контейнера, команда 'docker pause' використовують спеціальну можливість cgroups для заморозки запущеного простору процесів. Подробиці можна прочитати тут, якщо коротко, відправлення сигналу Ctrl+Z (SIGTSTP) не достатньо, щоб заморозити всі процеси в просторі контейнера.

docker rm <container-id>
До:
docker rm input

Після:
docker rm output

Команда 'docker rm' видаляє шар для запису, який визначає контейнер на хост-системі. Повинна бути запущена на зупиненому контейнерах. Видаляє файли.

docker rmi <container-id>
До:
docker rmi input

Після:
docker rmi output

Команда 'docker rmi' видаляє шар для читання, який визначає «сутність» образу. Вона видаляє образ з хост-системи, але образ усе ще може бути отриманий з репозиторію через 'docker pull'. Ви можете використовувати 'docker rmi' тільки для шарів верхнього рівня (чи образів), для видалення проміжних шарів потрібно використовувати 'docker rmi -f'.

docker commit <container-id>
До:
docker commit running containerабо docker commit container

Після:
docker commited layer

Команда 'docker commit' бере верхній рівень контейнера, той, що для запису і перетворює його в шар для читання. Це фактично перетворює контейнер (незалежно від того, чи запущений він) незмінний образ.

docker commit

docker build
До:
Dockerfile dockerfiledocker image

Після:
docker image
З багатьма іншими шарами.

Команда 'docker build' цікава тим, що запускає цілий ряд команд:
docker build

На зображенні вище ми бачимо, як команда build використовує значення інструкції FROM файлу Dockerfile як базовий образ після чого:

1) запускає контейнер (create і start)
2) змінює шар для запису
3) робить commit
На кожній ітерації створюється новий шар. При виконанні 'docker build' може створюватися безліч шарів.

docker exec <running-container-id>
До:
docker running container

Після:
docker exec

Команда 'docker exec' застосовується до запущеного контейнера, запускає новий процес всередині простору процесів контейнера.

docker inspect <container-id> | <image-id>
До:
docker inspect containerабо docker inspect image

Після:
metadata

Команда 'docker inspect' отримує метадані верхнього шару контейнера або образу.

docker save <image-id>
До:
docker save input

Після:
docker save output

Команда 'docker save' створює один файл, який може бути використаний для імпорту образу на інший хост-систему. На відміну від команди 'export', вона зберігає всі шари та їх метадані. Може бути застосована тільки до образів.

docker export <container-id>
До:
docker export input

Після:
docker export output

Команда 'docker export' створює tar архів з вмістом файлів контейнера, в результаті виходить папка, придатна для використання поза docker. Команда прибирає шари та їх метадані. Може бути застосована тільки для контейнерів.

docker history <image-id>
До:
docker history input

Після:
docker history output

Команда 'docker history' приймає <image-id> і рекурсивно виводить список всіх верств-батьків образу (які теж можуть бути образами)

Підсумок

Я сподіваюся, вам сподобалася ця візуалізація контейнерів і образів. Є багато інших команд (pull, search, restart, attach та інші), які можуть або не можуть бути пояснені моїми порівняннями.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.