如何解析Docker图像名称?

BMi*_*tch 14 docker docker-registry

在执行docker push或拉动图像时,Docker如何确定图像名称中是否存在注册服务器,或者它是默认注册表(例如Docker Hub)上的路径/用户名?

我从1.1图像规范中看到以下内容:

标签

标记用于将描述性的,用户指定的名称映射到任何单个图像ID.标签值仅限于字符集[a-zA-Z_0-9].

知识库

在公共前缀(名称组件:)之前分组的标签集合.例如,在标记为my-app:3.1.4的图像中,my-app是名称的Repository组件.存储库名称由斜杠分隔的名称组件组成,可选地以DNS主机名为前缀.主机名必须遵循标准DNS规则,但可能不包含_字符.如果存在主机名,则可以选择后跟以下格式的端口号:8080.名称组件可能包含小写字符,数字和分隔符.分隔符定义为句点,一个或两个下划线,或一个或多个破折号.名称组件不能以分隔符开头或结尾.

对于DNS主机名,是否需要使用点完全限定,或者"my-local-server"是否为有效的注册表主机名?对于名称组件,我看到句点有效,这意味着"team.user/appserver"是有效的图像名称.如果注册表服务器在端口80上运行,因此图像名称中的主机名上不需要端口号,则主机名和注册表服务器上的路径之间似乎存在歧义.我很好奇Docker如何解决这种模棱两可的问题.

BMi*_*tch 15

TL; DR:主机名必须在第一个之前包含.dns分隔符或:端口分隔符/,否则代码假定您需要默认注册表.


经过一些挖掘代码后,我遇到了以下分发/ 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
Run Code Online (Sandbox Code Playgroud)

实际的实现是通过distribution/reference/regexp.go中的正则表达式.

但是通过一些挖掘和挖掘,我发现除了正则表达式之外还有另一个检查(如果你不包含a .或者,你会得到一个大写主机名的错误:).我在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
}
Run Code Online (Sandbox Code Playgroud)

对我来说,重要的部分是检查第一个if语句中的第一个.:之前/.使用它,主机名从第一个之前拆分/,如果没有它,整个名称将传递给默认的注册表主机名.

  • 这很棒。简而言之,我会尝试这样记住它:“[[host:port/]registry/]component[:tag][@digest]”。 (3认同)