Докер: теги Docker и Docker Hub

Понимание тегов изображений Docker и публикация изображений в Docker Hub

В этом уроке мы обсудим, как создавать и управлять тегами, связанными с образами Docker. Затем мы собираемся опубликовать образец образа Docker в Docker Hub и использовать его на отдельной машине.

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

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

docker-version-app/ 
├── .dockerignore
├── .gitignore
├── Dockerfile
└── version.txt

Наш образ Docker будет выглядеть очень просто. У нас будет файл Dockerfile и version.txt файл, содержащий строку версии. В Dockerfile есть инструкция CMD, которая печатает содержимое version.txt файла. Поскольку эта операция не требует сложной настройки, мы будем использовать образ alpine:3.12.2 в качестве родительского образа для нашего образа Docker.

Теперь поговорим о создании образа Docker. На предыдущих уроках мы узнали, что для создания образа Docker мы используем команду $ docker build PATH, где находится каталог PATH, в котором находится наш контекст сборки. Если мы не используем флаг --file или -f с этой командой, наш Dockerfile должен находиться в этом каталоге. Итак, давайте создадим образ Docker.

Изначально у нас не было загруженных локально образов Docker. После запуска команды $ docker build Docker извлекает родительский образ из реестра Docker (docker.io) и создает образ, читая инструкции в Dockerfile. Когда мы снова запускаем команду $ docker images, мы видим наше изображение с идентификатором 223b43496b9c (указанным внутри столбца IMAGE ID). Мы можем использовать этот идентификатор в таких командах, как $ docker run <id> или $ docker rmi <id>, чтобы выполнить какое-либо действие с этим изображением.

Теперь давайте сосредоточим наше внимание на столбцах REPOSITORY и TAG. В столбце REPOSITORY отображается имя, связанное с образом Docker, а в TAG отображается тег, связанный с образом Docker.

Думайте об образе Docker как о стороннем (зависимом) пакете. Подобно тому, как пакет должен иметь уникальное имя и версию, поскольку образы Docker могут быть общедоступными, REPOSITORY и столбец TAG выполняет ту же работу по маркировке образа Docker, чтобы пользователь мог загрузить правильный образ с соответствующей версией из реестра Docker. .

Имя репозитория может иметь форму <name> или <username>/<name>, где <name> - уникальное имя вашего образа Docker, а <username> - имя пользователя вашей учетной записи в Docker Hub. Docker Hub - это общедоступная веб-платформа для публичного или частного обмена образами Docker. Если вы хотите опубликовать образ в Dockerhub, ваш образ Docker должен иметь имя репозитория в формате <username>/<name>.

Значение столбца TAG отображает тег, связанный с образом Docker. У этого значения нет согласованного формата. Это должно различать образы Docker с одинаковыми именами, поэтому вы можете использовать любой формат, который вам нравится, но я обычно использую формат Семантическая версия, например 1.0.0.

Если вы видите репозиторий и тег, связанные с нашим образом Docker, для обоих столбцов отображается <none>. Причина в том, что мы не указали значения для этих столбцов при выполнении команды $ docker build.

💡 Образы Docker с именем <none> называются Висячими изображениями. Вы можете использовать флаг --filter "dangling=true" с $ docker images, чтобы перечислить все висячие образы, присутствующие на вашем локальном компьютере.

Чтобы указать значение для репозитория и тега изображения, мы используем флаг --tag или -t с командой $ docker build. Формат значения этого флага - <repository>:<tag>. Если мы оставим часть :<tag>, Docker будет использовать значение latest для части <tag> неявно.

💡 Флаг --tag действительно сбивает с толку, поскольку мы указываем не только значение для столбца TAG, но и для столбца REPOSITORY. Вот почему <repository>:<tag> иногда называют тегом или именем, так что несите меня, если это вас запутает.

На этот раз мы используем флаг -t version с командой $ docker build для создания нового образа. Теперь, когда мы перечисляем изображения, он показывает образ Docker с тем же идентификатором, который был у нас раньше, но со значением REPOSITORY version и TAG значением latest. Docker использовал значение latest для TAG, поскольку мы не упомянули его в значении -t.

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

💡 Обратите внимание, что в столбце CREATED отображается 26 minutes ago, что означает, что мы действительно не создали новое изображение. Это все еще старое изображение, но с собственным именем (repo: tag). Та же самая логика применима, если мы попытаемся создать новое изображение с точно таким же содержанием.

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

С флагом -t version:1.0.0 мы создали новый образ Docker со значением TAG 1.0.0. Однако, если вы видите столбец IMAGE ID, он также ссылается на старое изображение, поскольку его содержимое остается прежним.

Ссылаясь на образ Docker в такой команде, как $ docker run <image> или $ docker inspect <image>, мы можем использовать имя образа вместо его идентификатора для значения <image>. Общий формат значения <image> при использовании имени изображения - <repository>:<tag>, то же самое, что и --tag.

Например, $ docker inspect version:1.0.0 отобразит информацию об изображении с идентификатором 223b43496b9c. Если мы опустим часть :<tag>, по умолчанию будет заменено :latest.

Теперь давайте изменим содержимое version.txt файла, заменив текст v1.0.0 на v1.0.1, и создадим новую сборку.

Теперь, когда содержимое создаваемого образа Docker отличается от старого, IMAGE ID будет другим. Поскольку мы не указали значение для флага --tag, Docker создал зависший образ. На этот раз дадим изображению собственное имя.

На этот раз мы назвали изображение version:1.0.1. Однако посмотрите изображение с тегом latest, оно по-прежнему относится к старой сборке. Это происходит потому, что latest в мире Docker не означает последнюю версию образа, поэтому он может ввести в заблуждение новичков. Посмотрим на проблему.

Когда мы создаем контейнер из version изображения (version:latest неявно), мы обычно ожидаем, что нацелимся на последнее (самое последнее) изображение. Однако для Docker latest - это просто тег по умолчанию и больше для него ничего не значит. Таким образом, тег version:latest не имеет другого значения, чем version:1.0.0 или version:bla-bla.

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

Итак, давайте восстановим предыдущее изображение с тегом version:latest.

В приведенном выше случае мы создали новый образ (без изменения содержимого) с именем version:latest, хотя мы оставили :latest часть, чтобы Docker мог творить чудеса. Поскольку тег version:latest уже существует, Docker просто укажет тег version:latest на изображение с идентификатором 0c102369f408.

Теперь, когда мы создаем контейнер из version изображения (version:latest неявно), мы попадаем в самое последнее изображение. Это доказывает, что ответственность за управление последними образами лежит на нас, разработчиках.

Поскольку теги можно легко перемещать, Docker дает нам команду $ docker tag для создания тега без создания образа с флагом --tag.

$ docker tag <image> <repo>:<tag>

<image> - это изображение, на которое должен ссылаться новый тег. Мы можем использовать идентификатор изображения для этого значения или значение <repo>:<tag> изображения, где часть :tag является необязательной и по умолчанию будет :latest. В качестве <repo>:<tag> значения команды мы можем указать все, что захотим. Здесь :<tag> также является необязательным и по умолчанию будет :latest.

Давайте создадим новую сборку с версией 1.0.2, изменив значение в version.txt файле. Но на этот раз мы воспользуемся командой $ docker tag для перемещения тега version:latest.

Как видно из результатов выше, новый образ Docker имеет идентификатор 6001cd23b419 с именем version:1.0.2. Поскольку тег version:latest указывал на более раннее изображение, нам пришлось переместить его. Используя команду $ docker tag, мы вручную переместили его, чтобы указать на новую версию.

Поскольку теги Docker представляют собой просто ярлыки, удаление тега не приводит к удалению изображения, если только это не последний тег, который ссылается на изображение. У нас нет специальной команды для снятия тегов с образа Docker, и мы используем ту же старую команду $ docker rmi для удаления тега.

Внимательно следите за приведенными выше командами и их выводом. У нас был образ Docker с идентификатором 6001cd23b419, на который указывают два тега. Таким образом, удаление этого изображения с помощью команды $ docker rmi <id> не сработает, если не используется флаг -f или --force.

Команда $ docker rmi version:1.0.2 только немаркированное (удалило тег) изображение с идентификатором 6001cd23b419. Теперь, когда у этого изображения остался только один тег (version:latest), $ docker rmi version:latest (:latest не является обязательным) удаляет тег вместе с изображением.

Публикация образа в Docker Hub

Docker Hub - это общедоступный репозиторий для обмена образами Docker. Когда вы запускаете команду $ docker pull <image> или используете инструкцию FROM <image> в Dockerfile, Docker по умолчанию ищет образы в Docker Hub (если они не присутствуют локально).

Чтобы делиться изображениями в Docker Hub, нам нужна учетная запись в Docker Hub. Поэтому вам следует зарегистрироваться на https://hub.docker.com и использовать удобное имя пользователя при регистрации, потому что это имеет значение. Мое имя пользователя в Docker Hub - thatisuday, поэтому любое изображение, которое я хотел бы опубликовать в Docker Hub, должно начинаться с префикса thatisuday/, что означает, что когда я помечаю изображение, это будет thatisuday/<repo>:<tag>.

После регистрации нам нужно настроить репозиторий. Репозиторий похож на репозиторий GitHub, который содержит множество версий (тегов) одного и того же изображения. Щелкните ссылку Repositories, а затем кнопку Create Repository.

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

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

$ docker push [OPTIONS] NAME[:TAG]

Мы публикуем образ Docker с нашего локального компьютера в Docker Hub с помощью команды $ docker push. Как обсуждалось ранее, при отправке образа Docker в Docker Hub имя тега действительно имеет значение, поскольку нет другого способа указать Docker, в какой репозиторий его отправить. Следовательно, когда мы отправляем образ Docker с именем thatisuday/version:tag, Docker предполагает, что мы хотим отправить образ в version репозиторий пользователя с именем пользователя thatisuday.

Поскольку любой аноним может злонамеренно опубликовать образ Docker в любом репозитории, для выполнения команды $ docker push требуется предварительная авторизация. Для этого воспользуемся командой $ docker login. Эта команда запрашивает имя пользователя и пароль для входа в вашу учетную запись Docker Hub.

Как только мы увидим сообщение Login Suceeded, мы можем опубликовать изображение. Но сначала нам нужно изображение с правильным тегом. Итак, давайте создадим новый тег из наших предыдущих сборок, чтобы удовлетворить критериям Docker Hub.

Сначала мы создали два новых тега, а именно. thatisuday/version:1.0.0 и thatisuday/version:latest с именем репозитория thatisuday/version из изображения с идентификатором 223b43496b9c, и мы использовали имя version:1.0.0 в команде $ docker tag для ссылки на него.

Когда мы выполняем команду $ docker push <username>/<repo>:<tag>, только тег (image) с именем <username>/<repo>:<tag> помещается в Docker Hub. Если мы опустим часть :<tag>, будут вставлены все теги с именем <username>/<repo>. Поскольку в нашем случае мы не учли часть :tag, будут добавлены теги 1.0.0 и latest.

Мы можем подтвердить это, посмотрев опубликованные теги в разделе Tags and Scans. Мы видим, что теги latest и 1.0.0 там вверху. Теперь, когда мы опубликовали наш первый образ Docker, давайте попробуем его. Я бы предпочел другую машину с установленным Docker для загрузки и игры с нашим образом Docker, но в этом нет необходимости. Мы можем использовать контейнер docker-in-docker.

Докер-в-Докере

Можно запустить движок Docker внутри контейнера Docker. Образ docker:dind (dind означает docker-in-docker) позволяет нам создать контейнер с установленным Docker. Мы можем создать контейнер из этого образа и поиграть с командами Docker.

Заставить официальный docker:dind образ работать немного громоздко, и вы можете следовать инструкциям по установке со страницы эта. Однако это изображение основано на изображении jpetazzo / dind, которое сейчас устарело. Однако он по-прежнему работает, и я буду использовать его для запуска контейнера docker-in-docker. Чтобы узнать больше о docker-in-docker, следите за публикацией this.

$ docker run --rm --privileged -it jpetazzo/dind

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

Чтобы загрузить образ Docker из Docker Hub, мы используем команду $ docker pull. Команда $ docker pull thatisuday/version извлечет version:latest изображение из version репозитория учетной записи, идентифицированной с thatisuday именем пользователя. Если мы используем $ docker pull thatisuday/version:1.0.0, он будет извлекать изображение с тегом 1.0.0, хотя в нашем случае он будет указывать на то же изображение.

Команда $ docker run thatisuday/version ищет образ thatisuday/version:latest локально. Если он не может найти его локально, он будет загружен на лету, а затем из него будет создан контейнер, поэтому предыдущий шаг на самом деле не нужен. Мы можем убедиться, что изображение thatisuday/version:latest - это 1.0.0, посмотрев на вывод v1.0.0 в консоли (игнорируйте другие INFO строки).

💡 Если в репозитории Docker Hub нет изображения с тегом :latest, $ docker run thatisuday/version приведет к ошибке. Следовательно, если ваши пользователи хорошо информированы, необходимо иметь тег latest.

Давайте создадим новый тег thatisuday/version:1.0.1, указывающий на version:1.0.1 изображение, которое есть на нашем локальном компьютере. Давайте также переместим thatisuday/version:latest, чтобы указать на это изображение. Затем мы отправим только что созданные теги в репозиторий Docker Hub.

На этот раз мы добавляем определенные теги вместо всех тегов thatisuday/version изображения. Поскольку между изображением 1.0.0 и 1.0.1 есть лишь незначительные различия, Docker выталкивает только новые слои в образе. Поскольку теги latest и 1.0.1 относятся к одному и тому же изображению (, следовательно, к одним и тем же слоям), новые слои для тега latest не добавляются.

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

Теперь вернемся к нашему контейнеру DIND и снова запустим команду $ docker run thatisuday/version. Каких результатов вы ожидаете?

Поскольку у нас уже есть образ thatisuday/version:latest, Docker больше не будет извлекать его из реестра, поэтому мы все еще видим результат v1.0.0. Чтобы загрузить новую версию, нам нужно снова вытащить этот тег (image) с помощью команды $ docker pull.

Теперь, когда мы вытаскиваем тег thatisuday/version:latest, старое изображение, которое раньше было тегом latest, становится болтающимся. Но теперь мы можем увидеть результат v1.0.1, когда мы создали контейнер из thatisuday/version:latest изображения.

Секреты и уловки

  • Если вы хотите удалить тег или репозиторий из Docker Hub, вы сможете сделать это из раздела Tags репозитория.
  • Если вы хотите опубликовать все теги для всех репозиториев, используйте флаг --all-tags или -a с командой $ docker push.
  • Если вы делаете репозиторий частным, вы должны добавить в репозиторий участников, которые будут загружать ваше изображение из раздела репозитория Collaborators. Им нужно будет войти в систему с помощью команды $ docker login, прежде чем они смогут использовать команду $ docker pull.
  • Если вы хотите поделиться образом Docker в частном порядке, вы можете использовать команду $ docker save, чтобы сохранить образ Docker в виде файла tarball. Позже кто-то может использовать команду $ docker import для импорта образа Docker из этого файла tarball или URL, который указывает на этот tarball.