Skip to content

Instantly share code, notes, and snippets.

@shreve
Last active July 14, 2022 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shreve/1a8cab41ecc344c1fbc2b4b0ca0745ac to your computer and use it in GitHub Desktop.
Save shreve/1a8cab41ecc344c1fbc2b4b0ca0745ac to your computer and use it in GitHub Desktop.
Docker layer size report
"""
Docker layer size report
python docker_layer_sizes.py <image>
Returns the size of each layer of the image and the command that created it.
This is a decorative layer on top of the docker history command.
"""
import subprocess
import sys
import re
from pygments import highlight
from pygments.styles import get_style_by_name
from pygments.lexers import DockerLexer
from pygments.formatters import Terminal256Formatter
INDENT = 8
LEXER = DockerLexer()
FORMATTER = Terminal256Formatter(style=get_style_by_name("rrt"))
MULTISPACE = re.compile(r" {2,}")
NOP_PREFIX = re.compile(r".*#\(nop\) +", re.DOTALL)
class Command:
shell = ""
def __init__(self, string):
self.size, self.instr = string.split("\t", 1)
if self.size == "0B":
self.size = ""
if self.cmd == "SHELL":
self.__class__.shell = self.text[1:-1]
@property
def cmd(self):
return self.instr.split(" ", 1)[0]
@property
def text(self):
return self.instr.split(" ", 1)[1]
def __repr__(self):
instr = re.sub(NOP_PREFIX, "", self.instr)
instr = re.sub(MULTISPACE, " ", instr)
if self.cmd != "SHELL":
instr = instr.replace(f"RUN {self.__class__.shell}", "RUN")
instr = instr.replace(self.__class__.shell, "RUN")
instr = instr.replace(" RUN", "\n" + " " * (INDENT + 3))
instr = highlight(instr, LEXER, FORMATTER).strip()
instr = [x.strip() for x in instr.split("&&")]
instr = f"\n{'':>12}&&".join(instr)
return f"{self.size:>6} {instr}"
def data(image) -> str:
"""Ask docker for our data."""
proc = subprocess.run(
[
"docker",
"history",
"--no-trunc",
"--format",
"{{.Size}}\t{{.CreatedBy}}",
image,
],
check=True,
capture_output=True,
)
return [Command(line) for line in proc.stdout.decode("utf-8").strip().split("\n")][
::-1
]
def main(image):
"""Run the program."""
output = data(image)
for line in output:
print(line)
if __name__ == "__main__":
main(sys.argv[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment