Imali smo 2 vreće trave, 75 meskalin tableta unix okruženje, docker repozitorijum i zadatak implementacije docker pull i docker push komandi bez docker klijenta.
UPS:
Pitanje: Čemu sve ovo?
Odgovor: Testiranje učitavanja proizvoda (NE koristi bash, skripte su date u obrazovne svrhe). Odlučeno je da se docker klijent ne koristi za smanjenje dodatnih slojeva (u razumnim granicama) i, shodno tome, emuliranje većeg opterećenja. Kao rezultat toga, sva sistemska kašnjenja Docker klijenta su uklonjena. Dobili smo relativno čisto opterećenje direktno na proizvodu.
Članak je koristio GNU verzije alata.
Prvo, hajde da shvatimo šta ove komande rade.
Dakle, za šta se koristi docker pull? Prema
"Izvucite sliku ili spremište iz registra".
Tamo nalazimo i link do
Odavde možemo shvatiti da je docker slika skup određenih slojeva koji sadrže informacije o najnovijim promjenama na slici, što nam je očito potrebno. Dalje ćemo pogledati
Piše sljedeće:
"Slika" je kombinacija JSON manifesta i pojedinačnih datoteka slojeva. Proces povlačenja > slike se usredotočuje na preuzimanje ove dvije komponente."
Dakle, prvi korak prema dokumentaciji je “Izvlačenje manifesta slike".
Naravno, nećemo ga snimati, ali su nam potrebni podaci iz njega. Slijedi primjer zahtjeva: GET /v2/{name}/manifests/{reference}
"Naziv i parametar reference identificiraju sliku i obavezni su. Referenca može uključivati oznaku ili sažetak."
Naše docker spremište je raspoređeno lokalno, hajde da pokušamo izvršiti zahtjev:
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/manifests/1.1.1" -H "header_if_needed"
Kao odgovor, dobijamo json od kojeg nas trenutno zanimaju samo linije spašavanja, odnosno njihove hešove. Nakon što ih primimo, možemo proći kroz svaki i izvršiti sljedeći zahtjev: "GET /v2/{name}/blobs/{digest}"
“Pristup sloju će biti ograničen imenom spremišta, ali se jedinstveno identificira u registru pomoću sažetka.”
digest u ovom slučaju je hash koji smo primili.
Pokušavam
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer
Hajde da vidimo kakav smo fajl konačno dobili kao prvi spas.
file firstLayer
one. rails su tar arhive, raspakujući ih odgovarajućim redoslijedom dobićemo sadržaj slike.
Hajde da napišemo malu bash skriptu kako bi se sve ovo moglo automatizovati
#!/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
Sada ga možemo pokrenuti sa željenim parametrima i dobiti sadržaj tražene slike
./script.sh dirName “http://localhost:8081/link/to/docker/registry” myAwesomeImage 1.0
Dio 2 - docker push
Ovo će biti malo komplikovanije.
Počnimo ponovo sa
Nakon proučavanja dokumentacije, proces preuzimanja možemo podijeliti u nekoliko koraka:
- Inicijalizacija procesa - "POST /v2/{repoName}/blobs/uploads/"
- Učitavanje linije spasavanja (koristit ćemo monolitno otpremanje, tj. šaljemo svaku liniju spasa u cijelosti) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
Dužina sadržaja: {veličina sloja}
Content-Type: aplikacija/oktet-tok
Binarni podaci sloja". - Učitavanje manifesta - "PUT /v2/{repoName}/manifests/{reference}".
Ali dokumentaciji nedostaje jedan korak, bez kojeg ništa neće raditi. Za monolitno opterećenje, kao i za djelomično (u komadima), prije utovara šine, morate izvršiti PATCH zahtjev:
"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Dužina sadržaja: {veličina komada}
Content-Type: aplikacija/oktet-tok
{Layer Chunk Binary Data}".
U suprotnom, nećete moći da se pomerite dalje od prve tačke, jer... Umjesto očekivanog koda odgovora 202, dobit ćete 4xx.
Sada algoritam izgleda ovako:
- Inicijalizacija
- Patch rail
- Utovar rukohvata
- Učitavanje manifesta
Tačke 2 i 3, respektivno, će se ponoviti onoliko puta koliko je potrebno učitati redove.
Prvo, trebamo bilo koju sliku. Koristiću archlinux:latest
docker pull archlinux
Sada ga spremimo lokalno za dalju analizu
docker save c24fe13d37b9 -o savedArch
Raspakirajte rezultujuću arhivu u trenutni direktorij
tar xvf savedArch
Kao što vidite, svaka linija života nalazi se u zasebnom folderu. Pogledajmo sada strukturu manifesta koji smo dobili
cat manifest.json | json_pp
Ne mnogo. Hajde da vidimo koji je manifest potreban za učitavanje, prema
Očigledno nam postojeći manifest ne odgovara, pa ćemo napraviti svoj s blackjackom i kurtizanama, spasilačkim užetom i konfigovima.
Uvijek ćemo imati barem jednu konfiguracijsku datoteku i niz linija spasavanja. Shema verzija 2 (aktualna u vrijeme pisanja), mediaType će ostati nepromijenjen:
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
Nakon kreiranja osnovnog manifesta, morate ga popuniti važećim podacima. Da bismo to učinili, koristimo json predložak željezničkog objekta:
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
},
Dodaćemo ga u manifest za svaku šinu.
Zatim moramo saznati veličinu konfiguracijske datoteke i zamijeniti stubove u manifestu stvarnim podacima
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
Sada možete pokrenuti proces preuzimanja i spremiti sebi uuid, koji bi trebao pratiti sve naredne zahtjeve.
Kompletna skripta izgleda otprilike ovako:
#!/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
možemo koristiti gotovu skriptu:
./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0
UPS:
Šta smo dobili kao rezultat?
Prvo, pravi podaci za analizu, pošto se testovi izvode u blazemeteru, a podaci o zahtjevima docker klijenta nisu baš informativni, za razliku od čistog HTTP zahtjeva.
Drugo, tranzicija nam je omogućila da povećamo broj virtuelnih korisnika za docker upload za oko 150% i dobijemo prosječno vrijeme odgovora 20-25% brže. Za preuzimanje docker-a uspjeli smo povećati broj korisnika za 500%, dok je prosječno vrijeme odgovora smanjeno za oko 60%.
Hvala na pažnji.
izvor: www.habr.com