Meil oli 2 koti rohtu, 75 tabletti meskalina unix keskkonnas, docker hoidla ja ülesanne teostada käske docker pull ja docker push ilma docker kliendita.

UPD:
Küsimus: Milleks see kõik on?
Vastus: Toote koormustestimine (MITTE bash'i vahendite abil, skriptid on esitatud hariduse eesmärkidel). Docker kliendi mittekasutamise otsustamisel püüti vähendada lisavahekihte (mõistlike piiride piires) ja seega jäljendada suuremat koormust. Tulemuseks oli, et eemaldati kõik süsteemi viivitused docker kliendist. Saime suhteliselt puhta koormuse otse tootest.
Artiklis kasutati GNU versioone.
Alustuseks selgitame, mida need käsud teevad.
Nii et milleks kasutatakse docker pull? Vastavalt :
"Tõmba pilt või hoidla registrist".
Seal leiame lingi .

Sealt saame aru, et docker image on komplekt teatud kihtidest, mis sisaldavad teavet viimaste muudatuste kohta pildis, mis on meile ilmselgelt vajalik. Edasi vaatame .
Siin öeldakse järgmist:
"Pilt" on JSON manifesti ja individuaalsete kihifailide kombinatsioon. Pildi tõmbamise protsess keskendub nende kahe komponendi hankimisele."
Nii et esimene samm vastavalt dokumentatsioonile on "Pildi manifesti tõmbamine”.
Kuid me ei kavatse seda tõmmata, kuid andmed sellest on meile vajalikud. Edasi toome näite päringust: GET /v2/{name}/manifests/{reference}
"Nimi ja viitamisparameeter tuvastavad pildi ja on kohustuslikud. Viitamine võib sisaldada silti või väljakuulutust."
Meie docker hoidla on kohalikult üles seatud, proovime teha päringu:
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/manifests/1.1.1" -H "header_if_needed"
Vastusena saame json-i, millest meid huvitavad hetkel ainult kihid, täpsemalt nende hash'id. Saades need, saame igaühe kohta edasi minna ja teostada järgmise päringu: "GET /v2/{name}/blobs/{digest}"
"Kihile pääsemine on piiratud hoidla nime järgi, kuid tuvastatakse registris unikaalselt hash'i kaudu."
Hash on antud juhul see hash, mille me saime.
Katsume
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer![]()
vaatame, milline fail me lõpuks esimese kihina saime.
file firstLayer
st. kihid on tar-arhive, mille õigesti järjestatud lahtipakkimise korral saame pildi sisu.
Kirjutame väikese bash'i skripti, et seda kõike automatiseerida.
#!/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 0Nüüd saame selle soovitud parameetritega käivitada ja vajalikku imaget saada.
.\/script.sh dirName "http:\/\/localhost:8081\/link\/to\/docker\/registry" myAwesomeImage 1.0Osa 2 - docker push
Siin läheb natuke keerulisemaks.
Alustame jälle . Nii et peame laadima üles iga kihi, looma vastava manifeesti ja laadima selle ka üles. Näib lihtne.
Dokumentatsiooni uurides saame laadimisprotsessi jagada mitmeks sammuks:
- Protsessi algatamine - "POST \/v2\/{repoName}\/blobs\/uploads\/"
- Kihi üleslaadimine (kasutame monoliitset laadimist, ehk iga kihi saadame tervikuna) - "PUT \/v2\/{repoName}\/blobs\/uploads\/{uuid}?digest\={digest}
Content-Length: {layeri suurus}
Content-Type: application\/octet-stream
Kihi binaarsed andmed". - Manifeesti üleslaadimine - "PUT \/v2\/{repoName}\/manifests\/{reference}".
Kuid dokumentatsioonist jääb puudu üks samm, ilma milleta midagi ei õnnestu. Nii monoliitse kui ka osalise (chunked) laadimise puhul peab enne kihi laadimist teostama PATCH-päringu:
"PATCH \/v2\/{repoName}\/blobs\/uploads\/{uuid}
Content-Length: {chunki suurus}
Content-Type: application\/octet-stream
{Layer Chunk Binary Data}".
Vastasel juhul ei saa te edasi liikuda esimese punktini, sest oodatud vastuskoodi 202 asemel saate 4xx.
Nüüd näeb algoritm välja järgmine:
- Algatamine
- Kihi patchimine
- Kihi üleslaadimine
- Manifeesti üleslaadimine
Punktid 2 ja 3 korduvad vastavalt nii palju kordi, kui kihte on vaja üles laadida.
Alustuseks on meil vaja mingit imaget. Kasutan archlinux:latest
docker pull archlinux
Nüüd salvestame selle endale lokaalselt edasiseks analüüsiks.
docker save c24fe13d37b9 -o savedArch
Lahti pakkime saadud arhiivi praegusesse kausta.
tar xvf savedArch
Nagu näeme, on iga kiht eraldi kaustas. Vaadakem nüüd manifeesti struktuuri, mille saime.
cat manifest.json | json_pp
Pole palju. Uurime, milline manifeet on üleslaadimiseks vajalik, vastavalt .

Ilmselgelt ei sobi olemasolev manifeet, seega teeme oma mustandiga, kus on mustad kaardid ja proovid.
Meil on alati vähemalt üks config-fail ja kihi massiiv. Skeemi versioon on 2 (aktuaalne artikli kirjutamise hetkel), mediaType jätame muutmata:
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.jsonPärast põhimanifeesti loomist on vajalik see täita kehtivate andmetega. Selleks kasutame kihi json-objekti mall:
{
"mediaType": "application\/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
},seda lisame manifeesti iga kihi jaoks.
Jätkusuutlikult peame teadma konfigureerimisfaili suurust ja asendama manifestis plokid reaalse teabega.
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFileNüüd saame algatada laadimisprotsessi ja salvestada uuid, millega peavad kõik järgmised päringud kaasas olema.
Täielik skript näeb välja umbes selline:
#!/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
saame kasutada valmis skripti:
./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0UPD:
Mida me tulemusena saime?
Esiteks, reaalsed andmed analüüsi jaoks, kuna testid käivitatakse blazemeter'is ja Docker kliendi päringud ei ole kuigi informatiivsed võrreldes puhaste HTTP päringutega.
Teiseks, üleminek võimaldas meil suurendada virtuaalsete kasutajate arvu Docker'i üleslaadimisel umbes 150% ja saada keskmine vastusaeg 20-25% kiiremini. Docker'i allalaadimisel õnnestus suurendada kasutajate arvu 500%, keskmine vastusaeg langes samas umbes 60%.
Aitäh tähelepanu eest.
Allikas: habr.com
