如何在Docker中获取依赖子映像的列表?


123

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

# 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的容器。


9
我想知道为什么Docker团队无法提供本地方法来做到这一点?
Amalgovinus

Answers:


54

简短的答案: 这是一个python3脚本,其中列出了相关的docker映像。

长答案: 您可以看到在有问题的图像之后创建的所有图像的图像ID和父ID,如下所示:

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

您应该能够查找具有父ID的图像(以f50f9524513f开头),然后查找这些图像的子图像,等等。 但这 .Parent 不是您的想法。,因此在大多数情况下,您需要在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 )

或只是使用上面的要点,即可完成相同的操作。


2
如果它们都不以期望的字符开头怎么办?这是否表明可能存在错误?我使用的是Docker for Mac beta(FWIW),所以这不足为奇。
neverfox

这是一个错误,还是意味着该图片没有任何子级。
Aryeh Leib Taurog

2
这并不能真正回答原始问题。这显示了在相关图像之后创建的内容,该内容可能取决于也可能不取决于张贴者试图删除的图像。西蒙·布雷迪(Simon Brady)的答案就可以解决这个问题,至少可用于较小的图像样本大小。
penguincoder

2
@penguincoder是python脚本的作用。
Aryeh Leib Taurog

就像@neverfox一样,这个答案对我不起作用。不过,西蒙·布雷迪(Simon Brady)的回答确实有效。
标记

91

如果您没有大量的图像,那么总有一种蛮力的方法:

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

1
我认为这是“最佳”解决方案。您可以通过将其更改echo $1为更丑陋(但仍然是蛮横的)docker images | grep $i(在docker 版本中比--filter在Image Id中使用标志更可移植),将其扩展一点,并使其更清楚一些,这是什么?
Jon

15

安装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
loretoparisi

3
可通过brew install dockviz
rcoup

使用Ubuntu时请小心。它通常使用过时的存储库进行访问
Qohelet

7

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

#!/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" ""

尽管此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能无效。- 点评来源
Juan Cruz Soler

我只是在格式化代码时遇到了麻烦,所以我放弃了,感谢您的参与。Jan
Michal Linhard

2

这是我为了保留最终的“图像”而做的(层,实际上-这就是让我失望的原因,因为我刚刚进入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

好的答案,但是负载只能用于从“ docker save”创建的映像。“
docker

2

根据肮脏迈克尔·霍夫曼的答案,如果您没有大量图像,则可以使用以下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]++'

2

这是一个基于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中提供了要点


1

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

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

现在,可能有一连串的相关子图像,因此您可能必须不断重复以找到最后一个子图像。


0

您可以通过以下Docker目录删除与父子关系无关的Docker映像

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

在此目录中,您可以找到Docker映像,因此可以删除所需的内容。


我正在尝试使用docker API做出可移植的命令,假设设备映射器和docker引擎(例如与docker swarm相对)使该解决方案成为不可移植的解决方案。在其他进程(包括docker守护程序)可能正在使用文件系统的情况下删除文件系统中的文件也是有风险的。
nicocesar

在macOS上呢?
安东尼·孔

1
*“警告”会在以后删除和拉出容器时导致多个错误。切勿/var/lib/docker直接用手直接删除目录
vladkras

0

怎么样:

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格式的条件,但也许可以作为此用例的筛选器。
ingyhere

0

我做了一些准备,以递归的方式找到孩子,他们的回购标签,并打印他们在做什么:

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- *。


-6

我也面临着同样的问题。下面的理想步骤可解决该问题。

停止所有正在运行的容器

码头工人停止$(docker ps -aq) 删除所有容器

泊坞窗rm $(docker ps -aq) 删除所有图像

泊坞窗rmi $(泊坞窗图像-q)


该答案不符合发问者的要求。
安库洛里亚

请停止评论人们可能复制和粘贴并做有害事情的事情。
nicocesar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.