Как анализируются имена имен Docker?

Когда вы делаете docker push или вытягиваете изображение, как Docker определяет, есть ли сервер реестра в имени изображения или это путь/имя пользователя в реестре по умолчанию (например, Docker Hub)?

Я вижу следующее из спецификации изображения 1.1:

Тег

Тег служит для сопоставления описательного имени пользователя с любым идентификатором изображения. Значения тегов ограничены набором символов [a-zA-Z_0-9].

вместилище

Коллекция тегов, сгруппированных под общим префиксом (компонент имени перед :). Например, в изображении, помеченном именем my-app: 3.1.4, my-app является компонентом репозитория имени. Имя репозитория состоит из разделенных словом компонентов имени, необязательно с префиксом имени хоста DNS. Имя хоста должно соответствовать стандартным правилам DNS, но не может содержать _ символов. Если присутствует имя хоста, он может сопровождаться номером порта в формате 8080. Компоненты имени могут содержать строчные символы, цифры и разделители. Сепаратор определяется как период, один или два символа подчеркивания или один или несколько тире. Компонент имени может не начинаться или заканчиваться разделителем.

Для имени хоста DNS необходимо ли полностью обладать точками, или является ли "мой-локальный-сервер" действительным именем реестра? Для компонентов имени я считаю периоды действительными, что означает, что "team.user/appserver" является допустимым именем изображения. Если сервер реестра запущен на порту 80, и поэтому номер порта не требуется для имени хоста в имени изображения, похоже, что будет некоторая неопределенность между именем хоста и пути на сервере реестра. Мне любопытно, как Докер разрешает эту двусмысленность.

Ответ 1

TL; DR: имя хоста должно содержать . разделитель dns или разделитель : port перед первым /, в противном случае код предполагает, что вам нужен реестр по умолчанию.


Немного покопавшись в коде, я наткнулся на дистрибутив /reference/reference.go со следующим:

// Grammar
//
//  reference                       := name [ ":" tag ] [ "@" digest ]
//  name                            := [hostname '/'] component ['/' component]*
//  hostname                        := hostcomponent ['.' hostcomponent]* [':' port-number]
//  hostcomponent                   := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
//  port-number                     := /[0-9]+/
//  component                       := alpha-numeric [separator alpha-numeric]*
//  alpha-numeric                   := /[a-z0-9]+/
//  separator                       := /[_.]|__|[-]*/
//
//  tag                             := /[\w][\w.-]{0,127}/
//
//  digest                          := digest-algorithm ":" digest-hex
//  digest-algorithm                := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
//  digest-algorithm-separator      := /[+.-_]/
//  digest-algorithm-component      := /[A-Za-z][A-Za-z0-9]*/
//  digest-hex                      := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value

Реальная реализация этого осуществляется через регулярное выражение в дистрибутиве /reference/regexp.go.

Но, немного покопавшись, я обнаружил, что есть еще одна проверка помимо этого регулярного выражения (вы получите ошибки с именем хоста в верхнем регистре, если вы не включите . Или :. И я отследил фактическое разделение имени до следующего в docker/distribution/reference/normalize.go:

// splitDockerDomain splits a repository name to domain and remotename string.
// If no valid domain is found, the default domain is used. Repository name
// needs to be already validated before.
func splitDockerDomain(name string) (domain, remainder string) {
    i := strings.IndexRune(name, '/')
    if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
        domain, remainder = defaultDomain, name
    } else {
        domain, remainder = name[:i], name[i+1:]
    }
    if domain == legacyDefaultDomain {
        domain = defaultDomain
    }
    if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
        remainder = officialRepoName + "/" + remainder
    }
    return
}

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

Ответ 2

Спецификация изображения на https://github.com/moby/moby/blob/master/image/spec/v1.1.md была обновлена, чтобы сообщить, что теги ограничены 128 символами.

Тема с пиаром находится здесь https://github.com/docker/distribution/issues/2248

Некоторый код на Ruby находится здесь https://github.com/cyber-dojo/runner-stateless/blob/master/src/image_name.rb

Некоторые тесты Ruby находятся здесь https://github.com/cyber-dojo/runner-stateless/blob/master/test_server/image_name_test.rb

Ответ 3

Этот синтаксис в размещенном коде наверху только подтверждает мою озадаченность.
Возьмите это имя в качестве примера: "domain.name.or.repo.com/some_img:1.0.0". Мне кажется, что может быть два пути разрешения.

  1. hostname = domain.name.or.repo.com
    repo = some_img
    тег = 1.0.0

  2. имя хоста = ''
    repo = domain.name.or.repo.com/some_img
    тег = 1.0.0

Не экспериментируя, как мы можем определить, какой из них правильный?