Turėjome 2 maišus žolės, 75 mescaline tablečių unix aplinką, docker saugyklą ir užduotį įgyvendinti docker pull ir docker push komandas be docker kliento.
UPD:
Klausimas: Kam visa tai?
Atsakymas: Produkto apkrovos testavimas (NEnaudojamas bash, scenarijai pateikiami edukaciniais tikslais). Buvo nuspręsta nenaudoti docker kliento norint sumažinti papildomus sluoksnius (protingose ribose) ir atitinkamai emuliuoti didesnę apkrovą. Dėl to buvo pašalinti visi „Docker“ kliento sistemos vėlavimai. Tiesiogiai ant gaminio gavome gana švarią apkrovą.
Straipsnyje buvo naudojamos GNU įrankių versijos.
Pirmiausia išsiaiškinkime, ką daro šios komandos.
Taigi kam naudojamas docker pull? Pagal
„Ištraukite vaizdą arba saugyklą iš registro“.
Ten taip pat randame nuorodą į
Iš čia galime suprasti, kad docker vaizdas yra tam tikrų sluoksnių rinkinys, kuriame yra informacija apie naujausius vaizdo pakeitimus, o tai, be abejo, yra tai, ko mums reikia. Toliau žiūrime
Jame rašoma taip:
„Vaizdas“ yra JSON aprašo ir atskirų sluoksnių failų derinys. Vaizdo > ištraukimo procesas yra susijęs su šių dviejų komponentų gavimu.
Taigi pirmasis žingsnis pagal dokumentaciją yra „Vaizdo manifesto traukimas".
Žinoma, mes to nešaudysime, bet mums reikia duomenų iš jo. Toliau pateikiamas prašymo pavyzdys: GET /v2/{name}/manifests/{reference}
"Pavadinimas ir nuorodos parametras identifikuoja vaizdą ir yra būtini. Nuorodoje gali būti žyma arba santrauka."
Mūsų dokerių saugykla yra įdiegta vietoje, pabandykime įvykdyti užklausą:
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/manifests/1.1.1" -H "header_if_needed"
Atsakydami gauname json, iš kurio šiuo metu mus domina tik gelbėjimo linijos, tiksliau, jų maišos. Gavę juos, galime peržiūrėti kiekvieną ir įvykdyti šią užklausą: „GET /v2/{name}/blobs/{digest}“
„Prieiga prie sluoksnio bus apribota pagal saugyklos pavadinimą, tačiau registre ji unikaliai identifikuojama pagal santrauką.
„Digest“ šiuo atveju yra maiša, kurią gavome.
Pabandykime
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer
Pažiūrėkime, kokį failą galiausiai gavome kaip pirmąjį gelbėjimosi ratą.
file firstLayer
tie. bėgiai yra tar archyvai, juos išpakavus atitinkama tvarka gausime vaizdo turinį.
Parašykime nedidelį bash scenarijų, kad visa tai būtų galima automatizuoti
#!/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
Dabar galime jį paleisti su norimais parametrais ir gauti reikiamo vaizdo turinį
./script.sh dirName “http://localhost:8081/link/to/docker/registry” myAwesomeImage 1.0
2 dalis – dokerio stūmimas
Tai bus šiek tiek sudėtingesnė.
Pradėkime iš naujo nuo
Išstudijavę dokumentaciją, atsisiuntimo procesą galime padalyti į kelis etapus:
- Proceso inicijavimas – „POST /v2/{repoName}/blobs/uploads/“
- Gelbėjimo juostos įkėlimas (naudosime monolitinį įkėlimą, t. y. siunčiame visą gelbėjimo liniją) – „PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
Turinio ilgis: {sluoksnio dydis}
Turinio tipas: programa / okteto srautas
Dvejetainių duomenų sluoksnis“. - Įkeliamas aprašas – „PUT /v2/{repoName}/manifests/{reference}“.
Tačiau dokumentacija praleidžia vieną žingsnį, be kurio niekas neveiks. Monolitiniam pakrovimui, taip pat daliniam (smulkintam), prieš pakraunant bėgį, turite atlikti PATCH užklausą:
PATCH /v2/{repoName}/blobs/uploads/{uuid}
Turinio ilgis: {size of chunk}
Turinio tipas: programa / okteto srautas
{Layer Cunk Binary Data}".
Priešingu atveju negalėsite peržengti pirmojo taško, nes... Vietoj laukiamo atsakymo kodo 202 gausite 4xx.
Dabar algoritmas atrodo taip:
- Inicijavimas
- Patch bėgelis
- Turėklo pakrovimas
- Įkeliamas manifestas
Atitinkamai 2 ir 3 taškai bus kartojami tiek kartų, kiek reikės įkelti eilučių.
Pirma, mums reikia bet kokio vaizdo. Naudosiu archlinux:latest
docker pull archlinux
Dabar išsaugokime jį vietoje tolesnei analizei
docker save c24fe13d37b9 -o savedArch
Išpakuokite gautą archyvą į dabartinį katalogą
tar xvf savedArch
Kaip matote, kiekviena gelbėjimo linija yra atskirame aplanke. Dabar pažiūrėkime į gauto manifesto struktūrą
cat manifest.json | json_pp
Nedaug. Pažiūrėkime, pagal kurį manifestą reikia įkelti
Akivaizdu, kad esamas manifestas mums netinka, todėl sukursime savo su blackjack ir kurtizanėmis, gelbėjimosi linijomis ir konfigūracijomis.
Mes visada turėsime bent vieną konfigūracijos failą ir daugybę gelbėjimo linijų. 2 schemos versija (dabartinė rašymo metu), mediaType bus palikta nepakeista:
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
Sukūrę pagrindinį manifestą, turite jį užpildyti tinkamais duomenimis. Norėdami tai padaryti, naudojame bėgio objekto json šabloną:
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
},
Pridėsime jį prie kiekvieno bėgio manifesto.
Tada turime išsiaiškinti konfigūracijos failo dydį ir pakeisti aprašo šaknis tikrais duomenimis
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
Dabar galite pradėti atsisiuntimo procesą ir išsaugoti uuid, kuris turėtų būti pridedamas prie visų vėlesnių užklausų.
Visas scenarijus atrodo maždaug taip:
#!/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
galime naudoti paruoštą scenarijų:
./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0
UPD:
Ką mes gavome dėl to?
Pirma, tikri duomenys analizei, nes testai vykdomi blazemeter, o duomenys apie docker kliento užklausas nėra labai informatyvūs, skirtingai nuo grynų HTTP užklausų.
Antra, perėjimas leido apie 150% padidinti virtualių naudotojų skaičių įkeliant dokerį ir gauti vidutinį atsako laiką 20–25% greičiau. „Docker“ atsisiuntimo atveju mums pavyko padidinti vartotojų skaičių 500%, o vidutinis atsako laikas sumažėjo apie 60%.
Ačiu už dėmesį.
Šaltinis: www.habr.com