Last active
July 11, 2024 10:35
-
-
Save zxkane/23de226fee8806ee0ed8c05136972ce0 to your computer and use it in GitHub Desktop.
get size of layers of docker image. Inspired by this post(https://ops.tips/blog/inspecting-docker-image-without-pull/).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
source ./library | |
# Entry point of the script. | |
# If makes sure that the user supplied the right | |
# amount of arguments (image_name and image_tag) | |
# and then performs the main workflow: | |
# 1. retrieve the image digest | |
# 2. retrieve the size of layers for | |
# that image. | |
main() { | |
check_args "$@" | |
local image=$1 | |
local tag=$2 | |
local manifests=$(get_image_tag_layers $image $tag) | |
formatOutput "$image" "$tag" "$manifests" | |
totalSize "$image" "$tag" "$manifests" | |
} | |
# Makes sure that we provided (from the cli) | |
# enough arguments. | |
check_args() { | |
if (($# != 2)); then | |
echo "Error: | |
Two arguments must be provided - $# provided. | |
Usage: | |
$0 <image> <tag> | |
Aborting." | |
exit 1 | |
fi | |
} | |
totalSize(){ | |
local image=$1 | |
local tag=$2 | |
local manifests=$3 | |
echo "Calculate the total size of image. | |
IMAGE: $image | |
TAG: $tag | |
" >&2 | |
for manifest in $(jq -r '.[] | @text' <<<"$manifests"); do | |
local platform=$(jq -r '.platform | @text' <<< "$manifest") | |
local digest=$(jq -r '.digest' <<< "$manifest") | |
local layers=$(jq -r '.layers' <<< "$manifest") | |
echo " Digest: $digest | |
Platform: $platform | |
TOTAL_SIZE: $(jq -r 'reduce (.[]|.size) as $item (0; . + $item)' <<< "$layers") | |
" >&2 | |
done | |
} | |
# Run the entry point with the CLI arguments | |
# as a list of words as supplied. | |
main "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Address of the registry that we'll be | |
# performing the inspections against. | |
# This is necessary as the arguments we | |
# supply to the API calls don't include | |
# such address (the address is used in the | |
# url itself). | |
readonly REGISTRY_ADDRESS="${REGISTRY_ADDRESS:-https://registry-1.docker.io}" | |
readonly REGISTRY_AUTH_ADDRESS="${REGISTRY_AUTH_ADDRESS:-https://auth.docker.io}" | |
# Retrieve the layers of image with manfiest info. | |
# note.: $image must be the full image name without | |
# the registry part, e.g.: `nginx` should | |
# be named `library/nginx`. | |
get_image_tag_layers() { | |
local image=$1 | |
local tag=$2 | |
manifest_lists=$(get_manifest_list $image $tag) | |
local SPE="" | |
echo "[" | |
for manifest in $(jq -r '.[] | @text' <<< "$manifest_lists"); do | |
local platform=$(jq -r '.platform' <<< "$manifest") | |
local digest=$(jq -r '.digest' <<< "$manifest") | |
echo $SPE | |
echo "{ | |
\"platform\": $platform, | |
\"layers\": $(get_layers_by_digest $image $digest), | |
\"digest\": \"$digest\" | |
}" | |
SPE="," | |
done | |
echo "]" | |
} | |
get_layers_by_digest() { | |
local image=$1 | |
local digest=$2 | |
echo "Retrieving image layers. | |
IMAGE: $image | |
Digest: $digest | |
" >&2 | |
manifest=$(curl \ | |
--silent -m 10 --retry 10 \ | |
--header "Accept: application/vnd.docker.distribution.manifest.v2+json" \ | |
--header "Authorization: Bearer $(get_token $image)" \ | |
"$REGISTRY_ADDRESS/v2/$image/manifests/$digest") | |
schemaVersion=$(jq -r '.schemaVersion' <<< "$manifest") | |
if [ "$schemaVersion" == "2" ]; then | |
jq -r '.layers' <<< "$manifest" | |
else | |
local SPE="" | |
echo "[" | |
for blob in $(jq -r '.fsLayers | .[] | .blobSum' <<< "$manifest"); do | |
echo $SPE | |
echo "{ \"digest\": \"$blob\", \"size\": $(get_blob_size $image $blob)}" | |
SPE="," | |
done | |
echo "]" | |
fi | |
} | |
get_manifest_list() { | |
local image=$1 | |
local tag=$2 | |
echo "Retrieving image manifest list. | |
IMAGE: $image | |
TAG: $tag | |
" >&2 | |
manifestOutput=$(curl \ | |
--silent -m 10 --retry 10 \ | |
--header "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \ | |
--header "Authorization: Bearer $(get_token $image)" \ | |
"$REGISTRY_ADDRESS/v2/$image/manifests/$tag") | |
manifests=$(jq -r 'if .manifests != null then .manifests else .schemaVersion end' <<<"$manifestOutput") | |
if [ "$manifests" == "1" ]; then | |
echo "[ { | |
\"platform\": { | |
\"architecture\": \"$(jq -r '.architecture' <<<"$manifestOutput")\", | |
\"os\": \"linux\" | |
}, | |
\"digest\": \"$tag\" | |
} | |
]" | |
else | |
echo "$manifests" | |
fi | |
} | |
# Retrieve the size of blob. | |
get_blob_size(){ | |
local image=$1 | |
local blob=$2 | |
echo "Retrieving blob size. | |
IMAGE: $image | |
Blob: $blob | |
" >&2 | |
blobLocation=$(curl \ | |
--silent -I -m 10 --retry 10 \ | |
--header "Authorization: Bearer $(get_token $image)" \ | |
"$REGISTRY_ADDRESS/v2/$image/blobs/$blob" \ | |
| tr -d '\r' | sed -En 's/^[Ll]ocation: (.*)/\1/p') | |
curl \ | |
--silent -I -m 10 --retry 5 \ | |
"$blobLocation" \ | |
| tr -d '\r' | sed -En 's/^[Cc]ontent-[Ll]ength: (.*)/\1/p' | |
} | |
#Refresh current token str if necessary. | |
refresh_token_str() { | |
local image=$1 | |
local current=${2:-''} | |
if [ -z "$current" ]; then | |
echo "Retrieving Docker Hub token. | |
IMAGE: $image | |
" >&2 | |
curl \ | |
--silent -m 10 --retry 5 \ | |
"$REGISTRY_AUTH_ADDRESS/token?scope=repository:$image:pull&service=registry.docker.io" | |
else | |
issuedAt=$(jq -re '.issued_at' <<< "$current") | |
expiresIn=$(jq -re '.expires_in' <<< "$current") | |
expiresTimestamp=$(date -d "$issuedAt +$(($expiresIn - 30)) seconds" +'%s') | |
nowTimestamp=$(date +'%s') | |
if [ $nowTimestamp -ge $expiresTimestamp ]; then | |
refresh_token_str "$image" | |
else | |
echo "Reusing current Docker Hub token. | |
IMAGE: $image | |
" >&2 | |
echo "$current" | |
fi | |
fi | |
} | |
TOKEN_FILE=$(mktemp) | |
remove_token_files() { | |
rm $TOKEN_FILE | |
} | |
trap remove_token_files EXIT | |
# Retrieves a token that grants access to the | |
# registry.docker.io (or any docker registry) access | |
# to pull a specific image. | |
# | |
# note.: the token that we retrieve is valid only | |
# for that image. | |
# note.: we get the token from `auth.docker.io` and | |
# not `registry.docker.io`. | |
# usage.: get_token "library/nginx" | |
# | |
get_token(){ | |
local image=$1 | |
local token_str=$(head -n 1 $TOKEN_FILE) | |
token_str=$(refresh_token_str "$image" "$token_str") | |
echo "$token_str" > "$TOKEN_FILE" | |
jq -re '.token' <<< "$token_str" | |
} | |
get_tags() { | |
local image=$1 | |
echo "Retrieving image tags. | |
IMAGE: $image | |
" >&2 | |
curl \ | |
--silent -m 10 --retry 10 \ | |
--header "Accept: application/vnd.docker.distribution.manifest.v2+json" \ | |
--header "Authorization: Bearer $(get_token $image)" \ | |
"$REGISTRY_ADDRESS/v2/$image/tags/list" | |
} | |
formatOutput() { | |
local image=$1 | |
local tag=$2 | |
local manifests=$3 | |
echo "Format the manifests output. | |
IMAGE: $image | |
TAG: $tag | |
" >&2 | |
for manifest in $(jq -r '.[] | @text' <<<"$manifests"); do | |
local platformArch=$(jq -r '.platform.architecture' <<< "$manifest") | |
local platformOs=$(jq -r '.platform.os' <<< "$manifest") | |
local platformVariant=$(jq -r '.platform.variant' <<< "$manifest") | |
local platformVersion=$(jq -r '.platform["os.version"]' <<< "$manifest") | |
local digest=$(jq -r '.digest' <<< "$manifest") | |
local layers=$(jq -r '.layers' <<< "$manifest") | |
layersArray=$(jq -r '.[] | [.digest, .size] | @csv ' <<< "$layers") | |
if [ -n "$layersArray" ]; then | |
for k in $layersArray; do | |
echo "$tag,$digest,$platformArch,$platformOs,$platformVariant,$platformVersion,$k" | |
done | |
else | |
echo "$tag,unknown,unknown,unknown,unknown,unknown,unknown,0" | |
fi | |
done | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is good and thanks but honestly i chose to go with Dive