需求
項(xiàng)目通過(guò)docker容器化后领突,由于種種原因暖璧,項(xiàng)目部署后存在未被容器使用的鏡像,占用磁盤空間攘须。因此漆撞,需要查找未被容器使用的docker鏡像殴泰,并將它們從部署腳本中去除。項(xiàng)目中容器數(shù)量龐大浮驳,手工查詢費(fèi)時(shí)費(fèi)力且易出錯(cuò)悍汛,故尋求自動(dòng)化方式查找出未使用鏡像
分析
- 最好能通過(guò)docker自身命令查找出未使用的容器。
docker images --filter dangling=true能查找出untagged images,如下
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
<none> <none> 8abc22fbb042 4 weeks ago 0 B
但實(shí)際項(xiàng)目中未使用的docker鏡像大多是有tag的至会,只是鏡像加載后沒有使用而已离咐。故通過(guò)docker images --filter dangling=true無(wú)法查找出全部未使用的鏡像
- 自己寫腳本解決
1.docker ps -a獲取所有容器信息,其中包含它們所使用的鏡像名稱
2.對(duì)所有容器奉件,docker history container_id宵蛀,查找到它們的基礎(chǔ)鏡像(由于docker鏡像的分層復(fù)用特性,下層的基礎(chǔ)鏡像不會(huì)占用額外的空間县貌,故無(wú)需清理)
3.對(duì)1/2中所有的鏡像取并集术陶,并去重
4.docker images查找出所有鏡像,并排查3中得到的鏡像煤痕,便是未使用的鏡像
python腳本
find_unused_images.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
IMAGES_COMMAND = 'docker images'
PS_COMMAND = 'docker ps -a'
HISTORY = 'docker history %s'
RE = re.compile(r'\s+\s+')
def exec_command(command):
result = os.popen(command)
return result.readlines()
class Image(object):
def __init__(self, image_info):
self.split_info = RE.split(image_info)
self.image_id = self.split_info[2]
self._generate_image_name()
self.size = self.split_info[-1]
def _generate_image_name(self):
tag = self.split_info[1]
repo = self.split_info[0]
self.name = repo if tag == 'latest' else repo + ':' + tag
def get_related_images(self):
image_ids = [RE.split(history)[0] for history in exec_command(HISTORY % self.image_id)[1:] if
RE.split(history)[0] != '<missing>']
return filter(None, [ImageUtil.get_image_by_id(image_id) for image_id in image_ids])
def __repr__(self):
return 'id:%s name:%s size:%s' % (self.image_id, self.name, self.size)
def __hash__(self):
return hash(self.image_id)
def __eq__(self, other):
return self.image_id == other.image_id
def __ne__(self, other):
return not self.__eq__(other)
class ImageUtil(object):
all_images = [Image(image_info) for image_info in exec_command(IMAGES_COMMAND)[1:]]
@classmethod
def get_image_by_id(cls, image_id):
try:
return filter(lambda img: img.image_id == image_id, cls.all_images)[0]
except IndexError:
return ''
@classmethod
def get_image_by_name(cls, name):
try:
return filter(lambda img: img.name == name, cls.all_images)[0]
except IndexError:
return ''
class Container(object):
def __init__(self, container_info):
self.split_info = RE.split(container_info)
image_name = self.split_info[1]
self.image = ImageUtil.get_image_by_name(image_name)
class ContainerUtil(object):
all_containers = [Container(container_info) for container_info in exec_command(PS_COMMAND)[1:]]
@classmethod
def get_used_images(cls):
used_images = [container.image for container in cls.all_containers if container.image]
related_images = []
for used_image in used_images:
related_images.extend(used_image.get_related_images())
used_images.extend(related_images)
return used_images
if __name__ == '__main__':
all_images = ImageUtil.all_images
images_used_by_container = ContainerUtil.get_used_images()
unused_images = set(all_images) - set(images_used_by_container)
print('unused images')
for image in unused_images:
print(image)
依賴
- python2.7(python3.x略作修改也可使用)
- root用戶
用法
將find_unused_images.py腳本放至任意目錄梧宫,執(zhí)行
python find_unused_images.py
便可打印出所有未使用的鏡像以及它們的大小
unused images
id:e111a70eee6a name:celery size:216MB