【问题标题】:How to get the list of dependent child images in Docker?如何获取 Docker 中依赖的子镜像列表?
【发布时间】:2016-08-03 16:57:10
【问题描述】:

我正在尝试删除图像,但我得到:

# docker rmi f50f9524513f  
Failed to remove image (f50f9524513f): Error response from daemon: conflict: unable to delete f50f9524513f (cannot be forced) - image has dependent child images

这是 docker 版本:

# docker version
Client:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 21:49:11 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 21:49:11 2016
 OS/Arch:      linux/amd64

但没有额外的信息:

# docker images --format="raw" | grep f50f9524513f -C3

repository: debian
tag: 8
image_id: f50f9524513f
created_at: 2016-03-01 18:51:14 +0000 UTC
virtual_size: 125.1 MB

repository: debian
tag: jessie
image_id: f50f9524513f
created_at: 2016-03-01 18:51:14 +0000 UTC
virtual_size: 125.1 MB

如何获得它声称拥有的依赖子图像

没有具有该图像 ID 的正在运行或已停止的容器。

【问题讨论】:

  • 我想知道为什么 docker 团队不能提供原生的方式来做到这一点?

标签: linux docker containers


【解决方案1】:

如果你没有大量的图像,总是有蛮力的方法:

for i in $(docker images -q)
do
    docker history $i | grep -q f50f9524513f && echo $i
done | sort -u

【讨论】:

  • 我认为这是“最好的”解决方案。您可以将其扩展一点,并通过将echo $1 更改为更丑陋(但仍然蛮力)docker images | grep $i(在 docker 版本中比在图像中使用--filter 标志更便携)来更清楚地说明什么是什么身份证)
  • 您可以将-q 标志添加到docker history 命令以加快执行速度
【解决方案2】:

简答: Here is a python3 script 列出了依赖的 docker 镜像。

长答案: 您可以使用以下内容查看在相关图像之后创建的所有图像的图像 ID 和父 ID:

docker inspect --format='{{.Id}} {{.Parent}}' \
    $(docker images --filter since=f50f9524513f --quiet)

您应该能够查找父 id 以 f50f9524513f 开头的图像,然后查找 那些 等的子图像。但是 .Parent isn’t what you think. ,因此在大多数情况下,您需要在上面指定 docker images --all 才能使其正常工作,然后您还将获得所有中间层的图像 ID。

这是一个更有限的 python3 脚本,用于解析 docker 输出并进行搜索以生成图像列表:

#!/usr/bin/python3
import sys

def desc(image_ids, links):
    if links:
        link, *tail = links
        if len(link) > 1:
            image_id, parent_id = link
            checkid = lambda i: parent_id.startswith(i)
            if any(map(checkid, image_ids)):
                return desc(image_ids | {image_id}, tail)
        return desc(image_ids, tail)
    return image_ids


def gen_links(lines):
    parseid = lambda s: s.replace('sha256:', '')
    for line in reversed(list(lines)):
        yield list(map(parseid, line.split()))


if __name__ == '__main__':
    image_ids = {sys.argv[1]}
    links = gen_links(sys.stdin.readlines())
    trunc = lambda s: s[:12]
    print('\n'.join(map(trunc, desc(image_ids, links))))

如果您将其保存为desc.py,您可以按如下方式调用它:

docker images \
    | fgrep -f <(docker inspect --format='{{.Id}} {{.Parent}}' \
        $(docker images --all --quiet) \
        | python3 desc.py f50f9524513f )

或者只是使用the gist above,它做同样的事情。

【讨论】:

  • 如果它们都不以期望字符开头怎么办?这是否表明可能存在错误?我正在使用 Docker for Mac beta,FWIW,所以这不会让我感到惊讶。
  • 要么是错误,要么意味着有问题的图像没有任何孩子。
  • 这并不能真正回答原始问题。这显示了在相关图像之后创建的内容,这可能取决于或可能不取决于发布者试图删除的图像。 Simon Brady 的回答可以解决问题,至少对于小样本图像。
  • @penguincoder 这就是 python 脚本的用途。
  • 像@neverfox 一样,这个答案对我不起作用。不过,西蒙·布雷迪(Simon Brady)在下面的回答确实有效。
【解决方案3】:

安装dockviz 并从树形视图中的图像 id 中跟随分支:

go get github.com/justone/dockviz
$(go env GOPATH)/bin/dockviz images --tree -l

【讨论】:

  • 最好先做sudo apt-get update ; sudo apt-get install golang-go; export GOPATH=$HOME/.go
  • 可通过brew install dockviz在macOS上使用
  • 使用 Ubuntu 时要小心。它通常使用过时的存储库进行 go
  • 显示了一棵漂亮的树,但有问题的图像在那棵树中没有孩子,即使这是 docker 所说的。
【解决方案4】:

如果有人对 bash 解决方案感兴趣,我已经创建了一个带有 shell 脚本的 gist 来打印出 docker 映像的后代树:

#!/bin/bash
parent_short_id=$1
parent_id=`docker inspect --format '{{.Id}}' $1`

get_kids() {
    local parent_id=$1
    docker inspect --format='ID {{.Id}} PAR {{.Parent}}' $(docker images -a -q) | grep "PAR ${parent_id}" | sed -E "s/ID ([^ ]*) PAR ([^ ]*)/\1/g"
}

print_kids() {
    local parent_id=$1
    local prefix=$2
    local tags=`docker inspect --format='{{.RepoTags}}' ${parent_id}`
    echo "${prefix}${parent_id} ${tags}"

    local children=`get_kids "${parent_id}"`

    for c in $children;
    do
        print_kids "$c" "$prefix  "
    done
}

print_kids "$parent_id" ""

【讨论】:

  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
  • 我只是在格式化代码时遇到了问题,所以我放弃了,谢谢你一月这样做
  • 嘿@MichalLinhard!我发现你的 sn-p 非常有用。我已将其包含在github.com/PHPExpertsInc/DockerUpgrade/blob/master/… 中,如果您可以授予我(或所有人!)此代码的 MIT 许可证,将不胜感激!
【解决方案5】:

根据slushyMichael Hoffman 的回答,如果您没有大量图像,您可以使用这个shell 函数:

docker_image_desc() {
  for image in $(docker images --quiet --filter "since=${1}"); do
    if [ $(docker history --quiet ${image} | grep ${1}) ]; then
      docker_image_desc "${image}"
    fi
  done
  echo "${1}"
}

然后调用它使用

docker_image_desc <image-id> | awk '!x[$0]++'

【讨论】:

    【解决方案6】:

    这是一个基于 Python API (pip install docker) 的解决方案,它递归地列出后代及其标签(如果有),根据关系的深度(子、孙等)增加缩进:

    import argparse
    import docker
    
    def find_img(img_idx, id):
        try:
            return img_idx[id]
        except KeyError:
            for k, v in img_idx.items():
                if k.rsplit(":", 1)[-1].startswith(id):
                    return v
        raise RuntimeError("No image with ID: %s" % id)
    
    def get_children(img_idx):
        rval = {}
        for img in img_idx.values():
            p_id = img.attrs["Parent"]
            rval.setdefault(p_id, set()).add(img.id)
        return rval
    
    def print_descendants(img_idx, children_map, img_id, indent=0):
        children_ids = children_map.get(img_id, [])
        for id in children_ids:
            child = img_idx[id]
            print(" " * indent, id, child.tags)
            print_descendants(img_idx, children_map, id, indent=indent+2)
    
    def main(args):
        client = docker.from_env()
        img_idx = {_.id: _ for _ in client.images.list(all=True)}
        img = find_img(img_idx, args.id)
        children_map = get_children(img_idx)
        print_descendants(img_idx, children_map, img.id)
    
    if __name__ == "__main__":
        parser = argparse.ArgumentParser(description=__doc__)
        parser.add_argument("id", metavar="IMAGE_ID")
        main(parser.parse_args())
    

    例子:

    $ python find_dep_img.py 549afbf12931
     sha256:913d0981fdc7d2d673f2c8135b7afd32ba5037755e89b00432d3460422ba99b9 []
       sha256:0748dbc043b96ef9f88265c422e0267807f542e364b7a7fadf371ba5ee082b5d []
         sha256:6669414d2a0cc31b241a1fbb00c0ca00fa4dc4fa65dffb532bac90d3943d6a0a []
           sha256:a6441e7d9e92511608aad631f9abe8627033de41305c2edf7e03ee36f94f0817 ['foo/bar:latest']
    

    我已将其作为要点发布在 https://gist.github.com/simleo/10ad923f9d8a2fa410f7ec2d7e96ad57

    【讨论】:

      【解决方案7】:

      这是获取依赖于父图像的子图像列表的简单方法:

      image_id=123456789012
      
      docker images -a -q --filter since=$image_id |
      xargs docker inspect --format='{{.Id}} {{.Parent}}' |
      grep $image_id
      

      这将生成一个子/父图像 ID 列表,例如(为了简洁而截断):

      sha256:abcdefghijkl sha256:123456789012
      

      左边是子图像ID,右边是我们试图删除的父图像ID,但它是说“图像有依赖的子图像”。这告诉我们abcdefghijkl 是依赖于123456789012 的孩子。所以我们需要先docker rmi abcdefghijkl,然后你可以docker rmi 123456789012

      现在,可能有一系列依赖的子图像,因此您可能必须不断重复才能找到最后一个子图像。

      【讨论】:

      • .. 并且链可能比您使用这种逐步方法的时间长很多:)
      【解决方案8】:

      这就是我为了保存我的最终“图像”所做的(层,真的 - 这让我感到厌烦,因为我刚刚进入 docker 构建)。

      我得到了整个“......不能被强迫......”的消息。我意识到我无法删除我不需要的图像,因为它们不是由“docker commit”创建的真正独立的图像。我的问题是,我在基本图像和最终图像之间有几个图像(或图层),只是试图清理就是我遇到关于孩子和父母的错误/警告的地方。

      1. 我将最终图像(或图层,如果您愿意的话)导出到一个 tarball。
      2. 然后我删除了所有我想要的图像,包括我的最终图像 - 我将它保存到一个 tarball 中,所以虽然我不确定我是否能够使用它,但我只是在试验。
      3. 然后我跑了docker image load -i FinalImage.tar.gz。输出类似于:

      7d9b54235881: Loading layer [==================================================>]  167.1MB/167.1MB
      c044b7095786: Loading layer [==================================================>]  20.89MB/20.89MB
      fe94dbd0255e: Loading layer [==================================================>]  42.05MB/42.05MB
      19abaa1dc0d4: Loading layer [==================================================>]  37.96MB/37.96MB
      4865d7b6fdb2: Loading layer [==================================================>]  169.6MB/169.6MB
      a0c115c7b87c: Loading layer [==================================================>]    132MB/132MB
      

      加载的图像 ID:sha256:82d4f8ef9ea1eab72d989455728762ed3c0fe35fd85acf9edc47b41dacfd6382

      现在,当我使用 'docker image ls' 列出时,我只有原始基础图像和我之前保存到 tarball 的最终图像。

      [root@docker1 ~]# docker image ls
      REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
      httpd               import              82d4f8ef9ea1        3 days ago          747MB
      centos              httpd               36540f359ca3        5 weeks ago         193MB
      

      我的系统现在“干净”了。我只有我想要的图像。 我什至毫无问题地删除了基础映像。

      [root@docker1 ~]# docker rmi 36540f359ca3
      Untagged: centos:httpd
      Untagged:     centos@sha256:c1010e2fe2b635822d99a096b1f4184becf5d1c98707cbccae00be663a9b9131
      Deleted: sha256:36540f359ca3b021d4b6a37815e9177b6c2bb3817598979ea55aee7ecc5c2c1f
      

      【讨论】:

      • 很好的答案,但 load 只应该用于从“docker save”创建的图像上。 “docker export”的正确恢复是“docker import”
      【解决方案9】:

      您可以通过以下Docker目录删除Docker镜像,无论父子关系如何

      /var/lib/docker/image/devicemapper/imagedb/content/sha256
      

      在这个目录中你可以找到 Docker 镜像,所以你可以删除你想要的。

      【讨论】:

      • 我正在尝试使用 docker API 制作可移植的命令,假设设备映射器和 docker 引擎(例如,与 docker swarm 相对)使其成为不可移植的解决方案。删除文件系统中的文件也是有风险的,而其他进程(包括 docker 守护进程)可能正在使用它。
      • 它在macos上的什么位置?
      • *WARNING" 这可能会导致以后删除和拉取容器时出现多个错误。切勿直接手动删除/var/lib/docker 中的目录
      【解决方案10】:

      怎么样:

      ID=$(docker inspect --format="{{.Id}}" "$1")
      IMAGES=$(docker inspect --format="{{if eq \"$ID\" .Config.Image}}{{.Id}}{{end}}" $(docker images --filter since="$ID" -q))
      echo $(printf "%s\n" "${IMAGES[@]}" | sort -u)
      

      它将打印带有sha256:前缀的子图像ID。
      我还有以下内容,其中附加了名称:

      IMAGES=$(docker inspect --format="{{if eq \"$ID\" .Config.Image}}{{.Id}}{{.RepoTags}}{{end}}" $(docker images --filter since="$ID" -q))
      

      • ID=获取图片的完整id
      • IMAGES= 获取将此图像列为Image 的所有子图像
      • echo... 删除重复项并回显结果

      【讨论】:

      • 我喜欢 Go 格式的条件句,但也许它们更适合作为这个用例的过滤器。
      【解决方案11】:

      我做了这个来递归地找到孩子,他们的回购标签,并打印他们在做什么:

      docker_img_tree() {
          for i in $(docker image ls -qa) ; do
              [ -f "/tmp/dii-$i"  ] || ( docker image inspect $i > /tmp/dii-$i)
              if grep -qiE 'Parent.*'$1 /tmp/dii-$i ; then
                  echo "$2============= $i (par=$1)"
                  awk '/(Cmd|Repo).*\[/,/\]/' /tmp/dii-$i | sed "s/^/$2/"
                  docker_img_tree $i $2===
              fi
          done
      }
      

      如果您的主机不安全,请不要忘记删除 /tmp/dii-*。

      【讨论】:

        【解决方案12】:

        我也面临同样的问题。以下是解决问题的闲置步骤。

        停止所有正在运行的容器

        docker 停止 $(docker ps -aq) 删除所有容器

        docker rm $(docker ps -aq) 删除所有图片

        docker rmi $(docker images -q)

        【讨论】:

        • 此答案不符合提问者要求。
        • 请停止评论人们可能会复制和粘贴并做有害事情的事情。
        猜你喜欢
        • 2017-08-26
        • 1970-01-01
        • 1970-01-01
        • 2019-01-24
        • 1970-01-01
        • 1970-01-01
        • 2021-10-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多