Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Docker – руководство для начинающих

Оригинал: Docker - Beginner's tutorial
Автор: German Jaber
Дата публикации: 28 / 01 / 2015
Перевод: Н.Ромоданов
Дата перевода: март 2015 г.

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

Прежде чем мы продолжим, позвольте мне прояснить ситуацию. По состоянию на сейчас (4 января 2015 г.) Docker работает только на Linux, он не может нативно функционировать в Windows или в OSX. Позже я буду говорить об архитектуре Docker и все станет очевидным. Так что если вы хотите иметь Docker на платформе, которая не является системой Linux, то вы должны запустить Linux в виртуальной машине.

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

Что Docker может сделать для вас?

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

  • Изоляции зависимостей приложения
  • Создания образа приложения и его тиражирования
  • Создания готового для запуска приложения, которое легко распространять
  • Возможности легкого и быстрого масштабирования отдельных экземпляров приложения
  • Тестирования приложения и освобождения затраченных ресурсов после завершения тестирования

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

Что конкретно делает Docker?

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

Двумя наиболее важными сущностями в Docker являются образы (images) и контейнеры (containers). Помимо них, также важны ссылки (links) и тома (volumes). Давайте начнем с образов.

Образы

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

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

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

У образов есть уникальный идентификатор и уникальная пара параметров - уникальное удобочитаемое название и тег. Образы могут назваться, например, ubuntu:latest, ubuntu:precise, django:1.6, django:1.7 и т.д.

Контейнеры

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

Давайте перейдем к примеру: вы можете загрузить образ ubuntu (есть общедоступный репозиторий, который называется реестром docker registry), модифицировать его, установив Gunicorn и ваше приложение Django со всеми его зависимостями, а затем из этого образа создать контейнер, при запуске которого будет запускаться ваше приложение.

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

У контейнеров есть одна особенность, которая существенно отличает их от виртуальных машин; они спроектированы так, что запускают только один процесс, они также не полностью эмулируют среду (если это то, что вам требуется, то воспользуйтесь LXC). Вы можете запустить экземпляр runit или экземпляр supervisord и в результате получить несколько процессов, но в действительности это не нужно (по моему скромному мнению).

Должен ли это быть единый процесс или несколько процессов является темой отдельной важной дискусии. Вы должны знать, что разработчики Docker приложили много усилий для реализации "подхода с одним процессом для одного контейнера", и что единственный случай, когда у вас действительно нет никакого другого варианта, кроме как использовать более одного процесса, это когда с целью отладки запускается что-то вроде SSH для получения доступа к контейнеру, ботает в целях отладки, однако с помощью команды docker exec эта проблема решается.

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

Тома

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

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

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

Ссылки

Ссылки (links) являются еще одной очень важной частью Docker.

Всякий раз, когда контейнер запущен, ему назначается случайный приватный адрес IP, а другие контейнеры могут пользоваться этим адресом IP для соединения с данным контейнером. Это важно по двум причинам: во-первых, контейнерам предоставляется возможность обращаться друг к другу, во-вторых, у контейнеров есть общая локальная сеть. У меня однажды возникла проблема, когда я на одной и той же машине запустил два контейнера elasticsearch для двух различных клиентов, но не поменял имя кластера, задаваемое по умолчанию, и два сервера elasticsearch быстро создали непредусмотренный кластер. Есть некоторые ограниченные возможности межконтейнерного взаимодействия; подробности смотрите в документации Docker, описывающей сетевое взаимодействие

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

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

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

Переносимость образов Docker

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

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

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

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

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

Как Docker делает то, что он должен делать?

Два понятия: группы управления (cgroups) и объединяемые файловые системы (union filesystems). Docker использует группы управления для изоляции контейнеров, а объединяемую файловую систему - для хранения образов и создания практически эфемерных контейнеров.

Cgroups

Эта особенность ядра Linux, которая позволяет выполнять следующие два действия:

  1. Ограничивать использование ресурсов (оперативная память, процессор) отдельными группами процессов в Linux (process groups)
  2. Для групп процессов создавать идентификаторы PID, ОТС, IPC, сеть, пользователя и монтировать пространства имен

Ключевым понятием здесь является пространство имен (namespace). Например, пространство имен идентификаторам PID позволяет процессам в нем использовать идентификаторы PID, изолированные и независимые от основного пространства имен PID, поэтому у вас в пространстве имен PID может быть свой собственный процесс init с PID, равным 1. Аналогично для всех остальных пространств имен. Вы можете использовать группы управления cgroups для того, чтобы создать среду, в которой процессы могут быть выполняться изолированно от остальной части ОС, но ключевой особенностью здесь является то, что процессы в этой среде используют уже загруженное и работающее ядро, поэтому накладные расходы в значительной степени такие же, как и при запуске другого процесса. Chroot для групп управления cgroups, как я это вижу, является нечто общим, что объединяет Халка, Бейна и Венома.

Объединяемые файловые системы

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

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

По состоянию на 4 января 2015, в Docker можно в объединенных файловых системах пользоваться aufs, btrfs и средствами отображения устройств (device mapper).

Образы

Позвольте мне представить вам образ postgresql:

[{
    "AppArmorProfile": "",
    "Args": [
        "postgres"
    ],
    "Config": {
        "AttachStderr": true,
        "AttachStdin": false,
        "AttachStdout": true,
        "Cmd": [
            "postgres"
        ],
        "CpuShares": 0,
        "Cpuset": "",
        "Domainname": "",
        "Entrypoint": [
            "/docker-entrypoint.sh"
        ],
        "Env": [
            "PATH=/usr/lib/postgresql/9.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "LANG=en_US.utf8",
            "PG_MAJOR=9.3",
            "PG_VERSION=9.3.5-1.pgdg70+1",
            "PGDATA=/var/lib/postgresql/data"
        ],
        "ExposedPorts": {
            "5432/tcp": {}
        },
        "Hostname": "6334a2022f21",
        "Image": "postgres",
        "MacAddress": "",
        "Memory": 0,
        "MemorySwap": 0,
        "NetworkDisabled": false,
        "OnBuild": null,
        "OpenStdin": false,
        "PortSpecs": null,
        "StdinOnce": false,
        "Tty": false,
        "User": "",
        "Volumes": {
            "/var/lib/postgresql/data": {}        
        },
        "WorkingDir": ""
    },
    "Created": "2015-01-03T23:56:12.354896658Z",
    "Driver": "devicemapper",
    "ExecDriver": "native-0.2",
    "HostConfig": {
        "Binds": null,
        "CapAdd": null,
        "CapDrop": null,
        "ContainerIDFile": "",
        "Devices": null,
        "Dns": null,
        "DnsSearch": null,
        "ExtraHosts": null,
        "IpcMode": "",
        "Links": null,
        "LxcConf": null,
        "NetworkMode": "",
        "PortBindings": null,
        "Privileged": false,
        "PublishAllPorts": false,
        "RestartPolicy": {
            "MaximumRetryCount": 0,
            "Name": ""
        },
        "SecurityOpt": null,
        "VolumesFrom": [
            "bestwebappever.dev.db-data"
        ]
    },
    "HostnamePath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hostname",
    "HostsPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hosts",
    "Id": "6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da",
    "Image": "aaab661c1e3e8da2d9fc6872986cbd7b9ec835dcd3886d37722f1133baa3d2db",
    "MountLabel": "",
    "Name": "/bestwebappever.dev.db",
    "NetworkSettings": {
        "Bridge": "docker0",
        "Gateway": "172.17.42.1",
        "IPAddress": "172.17.0.176",
        "IPPrefixLen": 16,
        "MacAddress": "02:42:ac:11:00:b0",
        "PortMapping": null,    
        "Ports": {
            "5432/tcp": null
        }
    },
    "Path": "/docker-entrypoint.sh",
    "ProcessLabel": "",
    "ResolvConfPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/resolv.conf",
    "State": {
        "Error": "",
        "ExitCode": 0,
        "FinishedAt": "0001-01-01T00:00:00Z",
        "OOMKilled": false,
        "Paused": false,
        "Pid": 21654,
        "Restarting": false,
        "Running": true,
        "StartedAt": "2015-01-03T23:56:42.003405983Z"
    },
    "Volumes": {
        "/var/lib/postgresql/data": "/mnt/docker/vfs/dir/5ac73c52ca86600a82e61279346dac0cb3e173b067ba9b219ea044023ca67561",
        "postgresql_data": "/mnt/docker/vfs/dir/abace588b890e9f4adb604f633c280b9b5bed7d20285aac9cc81a84a2f556034"
    },
    "VolumesRW": {
        "/var/lib/postgresql/data": true,
        "postgresql_data": true
    }
}
]

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

Контейнеры

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

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

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

Ниже показан контейнер для bestwebappever:

[{
    "AppArmorProfile": "",
    "Args": [],
    "Config": {
        "AttachStderr": true,
        "AttachStdin": false,
        "AttachStdout": true,
        "Cmd": [
            "/sbin/my_init"
        ],
        "CpuShares": 0,
        "Cpuset": "",
        "Domainname": "",
        "Entrypoint": null,
        "Env": [
            "DJANGO_CONFIGURATION=Local",
            "HOME=/root",
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "TALPOR_ENVIRONMENT=local",
            "TALPOR_DIR=/opt/bestwebappever"
        ],
        "ExposedPorts": {
            "80/tcp": {}
        },
        "Hostname": "44a87fdaf870",
        "Image": "talpor/bestwebappever:dev",
        "MacAddress": "",
        "Memory": 0,
        "MemorySwap": 0,
        "NetworkDisabled": false,
        "OnBuild": null,
        "OpenStdin": false,
        "PortSpecs": null,
        "StdinOnce": false,
        "Tty": false,
        "User": "",
        "Volumes": {
            "/opt/bestwebappever": {}
        },
        "WorkingDir": "/opt/bestwebappever"
    },
    "Created": "2015-01-03T23:56:15.378511619Z",
    "Driver": "devicemapper",
    "ExecDriver": "native-0.2",
    "HostConfig": {
        "Binds": [
            "/home/german/bestwebappever/:/opt/bestwebappever:rw"
        ],
        "CapAdd": null,
        "CapDrop": null,
        "ContainerIDFile": "",
        "Devices": null,
        "Dns": null,
        "DnsSearch": null,
        "ExtraHosts": null,
        "IpcMode": "",
        "Links": [
            "/bestwebappever.dev.db:/bestwebappever.dev.app/db",
            "/bestwebappever.dev.redis:/bestwebappever.dev.app/redis"
        ],
        "LxcConf": null,
        "NetworkMode": "",
        "PortBindings": {
            "80/tcp": [
                {
                    "HostIp": "",
                    "HostPort": "8887"
                }
            ]
        },
        "Privileged": false,
        "PublishAllPorts": false,
        "RestartPolicy": {
            "MaximumRetryCount": 0,
            "Name": ""
        },
        "SecurityOpt": null,
        "VolumesFrom": [
            "bestwebappever.dev.requirements-data"
        ]
    },
    "HostnamePath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hostname",
    "HostsPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hosts",
    "Id": "44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4",
    "Image": "b84804fac17b61fe8f344359285186f1a63cd8c0017930897a078cd09d61bb60",
    "MountLabel": "",
    "Name": "/bestwebappever.dev.app",
    "NetworkSettings": {
        "Bridge": "docker0",
        "Gateway": "172.17.42.1",
        "IPAddress": "172.17.0.179",
        "IPPrefixLen": 16,
        "MacAddress": "02:42:ac:11:00:b3",
        "PortMapping": null,
        "Ports": {
            "80/tcp": [
                {
                    "HostIp": "0.0.0.0",
                    "HostPort": "8887"
                }
            ]
        }
    },
    "Path": "/sbin/my_init",
    "ProcessLabel": "",
    "ResolvConfPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/resolv.conf",
    "State": {
        "Error": "",
        "ExitCode": 0,
        "FinishedAt": "0001-01-01T00:00:00Z",
        "OOMKilled": false,
        "Paused": false,
        "Pid": 21796,
        "Restarting": false,
        "Running": true,
        "StartedAt": "2015-01-03T23:56:47.537259546Z"
    },
    "Volumes": {
        "/opt/bestwebappever": "/home/german/bestwebappever",
        "requirements_data": "/mnt/docker/vfs/dir/bc14bec26ca311d5ed9f2a83eebef872a879c9e2f1d932470e0fd853fe8be336"
    },
    "VolumesRW": {
        "/opt/bestwebappever": true,
        "requirements_data": true
    }
}
]

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

Совсем кратко пошаговые действия

Итак, первый шаг: установите Docker.

Утилитам командной строки, имеющимся в Docker, для того, чтобы они работали, нужны права суперпользователя. Для того, чтобы по каждому поводу не обращаться к команде sudo, вы можете добавить в группу docker своего пользователя.

Второй шаг: давайте загрузим образ из общедоступного реестра с помощью следующей команды:

$> docker pull ubuntu:latest
ubuntu:latest: The image you are pulling has been verified
3b363fd9d7da: Pull complete
.....<большой фрагмент выданных данных пропущен>.....
8eaa4ff06b53: Pull complete
Status: Downloaded newer image for ubuntu:latest
$>

В этом общедоступном реестре есть образы почти для всего, что вам, возможно, потребуется: Ubuntu, Fedora, Postgresql, MySQL, Jenkins, Elasticsearch, Redis и т.д. Разработчики Docker поддерживают в общедоступном реестре лишь несколько образов, но большая часть того, что вы можете получить оттуда, поступают от пользователей, которые публикуют там свои творения.

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

Третий шаг: смотрим список ваших образов:

$> docker images
REPOSITORY  TAG     IMAGE ID      CREATED     VIRTUAL SIZE
ubuntu      latest  8eaa4ff06b53  4 days ago  192.7 MB

Четвертый шаг: создаем контейнер из этого образа.

$> docker run --rm -ti ubuntu /bin/bash
root@4638a40c2fbb:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root......
root@4638a40c2fbb:/# exit

Краткий список того, что вы сделали с помощью этой последней команды:

  • --rm: сообщает Docker-у удалять контейнер каждый раз, как только произойдет выход из процесса. Хорошая возможность при тестировании, позволяющая избежать беспорядка
  • -ti: указывает Dockerу создать псевдотерминал и предоставить его для использования в интерактивном режиме. Это возможность для доступа к контейнеру и она хорошо подходит для быстрого прототипирования и различных игр, но при использовании контейнера в производственных условиях вы не должны включать эти флаги.
  • ubuntu: это образ, на основе котором мы будем создавать контейнер
  • /bin/bash: команда запуска контейнера, и поскольку мы начали работу в интерактивном режиме, то будет выдано приглашение для работы с контейнером

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

Теперь давайте запустим контейнер в фоновом режиме:

$> docker run -d ubuntu ping 8.8.8.8
31c68e9c09a0d632caae40debe13da3d6e612364198e2ef21f842762df4f987f
$>

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

$> docker ps
CONTAINER ID IMAGE         COMMAND         CREATED        STATUS        PORTS  NAMES
31c68e9c09a0 ubuntu:latest "ping 8.8.8.8"  2 minutes ago  Up 2 minutes         loving_mcclintock

Он существует, ему автоматически присвоено удобное для восприятия имя. Теперь давайте посмотрим, что происходит внутри контейнера:

$> docker exec -ti loving_mcclintock /bin/bash
root@31c68e9c09a0:/# ps -aux|grep ping
root 1 0.0 0.0 6504 636 ? Ss 20:46 0:00 ping 8.8.8.8
root@31c68e9c09a0:/# exit

То, что мы только что сделали, это выполнили программу внутри контейнера и в данном случае это была программа /bin/bash. Флаги -ti служит той же цели, что при запуске docker, так что мы просто переходим в контейнере во внутрь оболочки.

Подведем итог

Подведем итог. Есть много другого, что мы здесь не рассмотрели, но это выходит за рамки данной статьи.

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

Базовая структура Docker:

Для дальнейшего чтения:

Интересные ссылки:

Полезные проекты и ссылки: