Vi hade 2 påsar med gräs, 75 mescaline tabletter unix-miljö, ett docker-förråd och uppgiften att implementera docker pull och docker push-kommandon utan en docker-klient.
UPD:
Fråga: Vad är allt detta till för?
Svar: Lasttestning av produkten (inte använder bash, skripten tillhandahålls för utbildningsändamål). Det beslutades att inte använda docker-klienten för att minska ytterligare lager (inom rimliga gränser) och följaktligen emulera en högre belastning. Som ett resultat togs alla systemförseningar från Docker-klienten bort. Vi fick en relativt ren last direkt på produkten.
Artikeln använde GNU-versioner av verktyg.
Låt oss först ta reda på vad dessa kommandon gör.
Så vad används docker pull till? Enligt
"Dra en bild eller ett arkiv från ett register".
Där hittar vi också en länk till
Härifrån kan vi förstå att en docker-bild är en uppsättning av vissa lager som innehåller information om de senaste ändringarna i bilden, vilket uppenbarligen är vad vi behöver. Därefter tittar vi på
Det står följande:
"En "bild" är en kombination av ett JSON-manifest och enskilda lagerfiler. Processen att dra en bild är centrerad kring att hämta dessa två komponenter."
Så det första steget enligt dokumentationen är "Dra ett bildmanifest".
Naturligtvis kommer vi inte att skjuta det, men vi behöver data från det. Följande är ett exempel på begäran: GET /v2/{name}/manifests/{reference}
"Namnet och referensparametern identifierar bilden och är obligatoriska. Referensen kan innehålla en tagg eller sammanfattning."
Vårt dockningsförråd är utplacerat lokalt, låt oss försöka utföra begäran:
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/manifests/1.1.1" -H "header_if_needed"
Som svar får vi json från vilken vi för närvarande bara är intresserade av livlinorna, eller snarare deras hash. Efter att ha tagit emot dem kan vi gå igenom var och en och utföra följande begäran: "GET /v2/{name}/blobs/{digest}"
"Åtkomst till ett lager kommer att grindas av namnet på förvaret men identifieras unikt i registret genom sammanfattning."
digest i det här fallet är hash som vi fick.
Låt oss försöka
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer
Låt oss se vilken typ av fil vi äntligen fick som första livlina.
file firstLayer
de där. räls är tjärarkiv, packar vi upp dem i lämplig ordning får vi innehållet i bilden.
Låt oss skriva ett litet bash-skript så att allt detta kan automatiseras
#!/bin/bash -eu
downloadDir=$1
# url as http://localhost:8081/link/to/docker/registry
url=$2
imageName=$3
tag=$4
# array of layers
layers=($(curl -s -X GET "$url/v2/$imageName/manifests/$tag" | grep -oP '(?<=blobSum" : ").+(?=")'))
# download each layer from array
for layer in "${layers[@]}"; do
echo "Downloading ${layer}"
curl -v -X GET "$url/v2/$imageName/blobs/$layer" --output "$downloadDir/$layer.tar"
done
# find all layers, untar them and remove source .tar files
cd "$downloadDir" && find . -name "sha256:*" -exec tar xvf {} ;
rm sha256:*.tar
exit 0
Nu kan vi köra den med önskade parametrar och få innehållet i den önskade bilden
./script.sh dirName “http://localhost:8081/link/to/docker/registry” myAwesomeImage 1.0
Del 2 - hamnarknuff
Det här blir lite mer komplicerat.
Låt oss börja igen med
Efter att ha studerat dokumentationen kan vi dela upp nedladdningsprocessen i flera steg:
- Processinitiering - "POST /v2/{repoName}/blobs/uploads/"
- Ladda upp en livlina (vi använder en monolitisk uppladdning, dvs. vi skickar varje livlina i sin helhet) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
Innehållslängd: {storlek på lager}
Innehållstyp: applikation/oktettström
Lager binära data". - Laddar manifestet - "PUT /v2/{repoName}/manifests/{referens}".
Men dokumentationen missar ett steg, utan vilket ingenting kommer att fungera. För monolitisk laddning, såväl som för partiell (chunked), innan du laddar skenan, måste du utföra en PATCH-begäran:
"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Innehållslängd: {size of chunk}
Innehållstyp: applikation/oktettström
{Layer Chunk Binary Data}".
Annars kommer du inte att kunna gå längre än den första punkten, eftersom... Istället för den förväntade svarskoden 202 får du 4xx.
Nu ser algoritmen ut så här:
- initiering
- Patchskena
- Laddar ledstången
- Laddar manifestet
Punkterna 2 respektive 3 kommer att upprepas så många gånger som antalet rader behöver laddas.
Först behöver vi vilken bild som helst. Jag kommer att använda archlinux:latest
docker pull archlinux
Låt oss nu spara det lokalt för vidare analys
docker save c24fe13d37b9 -o savedArch
Packa upp det resulterande arkivet i den aktuella katalogen
tar xvf savedArch
Som du kan se finns varje livlina i en separat mapp. Låt oss nu titta på strukturen för manifestet vi fick
cat manifest.json | json_pp
Inte mycket. Låt oss se vilket manifest som behövs för att ladda, enligt
Uppenbarligen passar det befintliga manifestet inte oss, så vi kommer att göra vårt eget med blackjack och kurtisaner, livlinor och konfigurationer.
Vi kommer alltid att ha minst en konfigurationsfil och en rad livlinor. Schema version 2 (nuvarande i skrivande stund), mediaType kommer att lämnas oförändrad:
echo ‘{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": config_size,
"digest": "config_hash"
},
"layers": [
’ > manifest.json
Efter att ha skapat det grundläggande manifestet måste du fylla det med giltiga data. För att göra detta använder vi json-mallen för rail-objektet:
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
},
Vi kommer att lägga till det i manifestet för varje skena.
Därefter måste vi ta reda på storleken på konfigurationsfilen och ersätta stubbarna i manifestet med riktiga data
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
Nu kan du initiera nedladdningsprocessen och spara dig själv en uuid, som bör följa med alla efterföljande förfrågningar.
Det fullständiga skriptet ser ut ungefär så här:
#!/bin/bash -eux
imageDir=$1
# url as http://localhost:8081/link/to/docker/registry
url=$2
repoName=$3
tag=$4
manifestFile=$(readlink -f ${imageDir}/manifestCopy)
configFile=$(readlink -f $(find $imageDir -name "*.json" ! -name "manifest.json"))
# calc layers sha 256 sum, rename them accordingly, and add info about each to manifest file
function prepareLayersForUpload() {
info_file=$imageDir/info
# lets calculate layers sha256 and use it as layers names further
layersNames=($(find $imageDir -name "layer.tar" -exec shasum -a 256 {} ; | cut -d" " -f1))
# rename layers according to shasums. !!!Set required amount of fields for cut command!!!
# this part definitely can be done easier but i didn't found another way, sry
find $imageDir -name "layer.tar" -exec bash -c 'mv {} "$(echo {} | cut -d"/" -f1,2)/$(shasum -a 256 {} | cut -d" " -f1)"' ;
layersSizes=($(find $imageDir -name "*.tar" -exec ls -l {} ; | awk '{print $5}'))
for i in "${!layersNames[@]}"; do
echo "{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
}," >> $manifestFile
done
# remove last ','
truncate -s-2 $manifestFile
# add closing brakets to keep json consistent
printf "nt]n}" >> $manifestFile
}
# calc config sha 256 sum and add info about it to manifest
function setConfigProps() {
configSize=$(ls -l $configFile | awk '{print $5}')
configName=$(basename $configFile | cut -d"." -f1)
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
}
#prepare manifest file
prepareLayersForUpload
setConfigProps
cat $manifestFile
# initiate upload and get uuid
uuid=$(curl -s -X POST -I "$url/v2/$repoName/blobs/uploads/" | grep -oP "(?<=Docker-Upload-Uuid: ).+")
# patch layers
# in data-binary we're getting absolute path to layer file
for l in "${!layersNames[@]}"; do
pathToLayer=$(find $imageDir -name ${layersNames[$l]} -exec readlink -f {} ;)
curl -v -X PATCH "$url/v2/$repoName/blobs/uploads/$uuid"
-H "Content-Length: ${layersSizes[$i]}"
-H "Content-Type: application/octet-stream"
--data-binary "@$pathToLayer"
# put layer
curl -v -X PUT "$url/v2/$repoName/blobs/uploads/$uuid?digest=sha256:${layersNames[$i]}"
-H 'Content-Type: application/octet-stream'
-H "Content-Length: ${layersSizes[$i]}"
--data-binary "@$pathToLayer"
done
# patch and put config after all layers
curl -v -X PATCH "$url/v2/$repoName/blobs/uploads/$uuid"
-H "Content-Length: $configSize"
-H "Content-Type: application/octet-stream"
--data-binary "@$configFile"
curl -v -X PUT "$url/v2/$repoName/blobs/uploads/$uuid?digest=sha256:$configName"
-H 'Content-Type: application/octet-stream'
-H "Content-Length: $configSize"
--data-binary "@$configFile"
# put manifest
curl -v -X PUT "$url/v2/$repoName/manifests/$tag"
-H 'Content-Type: application/vnd.docker.distribution.manifest.v2+json'
--data-binary "@$manifestFile"
exit 0
vi kan använda ett färdigt skript:
./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0
UPD:
Vad fick vi som resultat?
För det första, verklig data för analys, eftersom testen körs i blazemeter och data om docker-klientförfrågningar inte är särskilt informativa, till skillnad från rena HTTP-förfrågningar.
För det andra tillät övergången oss att öka antalet virtuella användare för dockningsuppladdning med cirka 150 % och få en genomsnittlig svarstid 20-25 % snabbare. För dockernedladdning lyckades vi öka antalet användare med 500 %, medan den genomsnittliga svarstiden minskade med cirka 60 %.
Tack för er uppmärksamhet.
Källa: will.com