Vi hadde 2 poser med gress, 75 meskalin-tabletter unix-miljø, et docker-lager og oppgaven med å implementere docker pull og docker push-kommandoene uten en docker-klient.
OPP:
Spørsmål: Hva er alt dette til for?
Svar: Lasttesting av produktet (IKKE ved bruk av bash, skriptene er gitt for pedagogiske formål). Det ble besluttet å ikke bruke docker-klienten til å redusere flere lag (innenfor rimelige grenser) og følgelig emulere en høyere belastning. Som et resultat ble alle systemforsinkelser til Docker-klienten fjernet. Vi fikk en relativt ren belastning direkte på produktet.
Artikkelen brukte GNU-versjoner av verktøy.
La oss først finne ut hva disse kommandoene gjør.
Så hva brukes docker pull til? I følge
"Trekk et bilde eller et depot fra et register".
Der finner vi også en link til
Herfra kan vi forstå at et docker-bilde er et sett med visse lag som inneholder informasjon om de siste endringene i bildet, som åpenbart er det vi trenger. Neste ser vi på
Det står følgende:
"Et "bilde" er en kombinasjon av et JSON-manifest og individuelle lagfiler. Prosessen med å trekke et bilde er sentret rundt å hente disse to komponentene."
Så det første trinnet i henhold til dokumentasjonen er "Å trekke et bildemanifest".
Selvfølgelig vil vi ikke skyte det, men vi trenger dataene fra det. Følgende er et eksempel på en forespørsel: GET /v2/{name}/manifests/{reference}
"Navnet og referanseparameteren identifiserer bildet og er obligatoriske. Referansen kan inneholde en kode eller sammendrag."
Docker-lageret vårt er distribuert lokalt, la oss prøve å utføre forespørselen:
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 mottar vi json som vi for øyeblikket kun er interessert i livslinjene, eller snarere hashen deres. Etter å ha mottatt dem, kan vi gå gjennom hver enkelt og utføre følgende forespørsel: "GET /v2/{name}/blobs/{digest}"
"Tilgang til et lag vil bli lukket av navnet på depotet, men identifiseres unikt i registeret etter sammendrag."
Digest i dette tilfellet er hashen vi mottok.
Prøver
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer
La oss se hva slags fil vi til slutt mottok som første livline.
file firstLayer
de. skinner er tjærearkiver, pakker vi dem ut i riktig rekkefølge får vi innholdet i bildet.
La oss skrive et lite bash-skript slik at alt dette kan automatiseres
#!/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
Nå kan vi kjøre den med de ønskede parameterne og få innholdet i det nødvendige bildet
./script.sh dirName “http://localhost:8081/link/to/docker/registry” myAwesomeImage 1.0
Del 2 - docker push
Dette blir litt mer komplisert.
La oss starte på nytt med
Etter å ha studert dokumentasjonen, kan vi dele ned nedlastingsprosessen i flere trinn:
- Prosessinitialisering - "POST /v2/{repoName}/blobs/uploads/"
- Opplasting av en livline (vi vil bruke en monolitisk opplasting, dvs. vi sender hver livline i sin helhet) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
Innhold-lengde: {size of layer}
Innholdstype: applikasjon/oktettstrøm
Lag binære data". - Laster inn manifestet - "PUT /v2/{repoName}/manifests/{reference}".
Men dokumentasjonen går glipp av ett trinn, uten noe vil ingenting fungere. For monolittisk lasting, så vel som for delvis (chunked), før lasting av skinnen, må du utføre en PATCH-forespørsel:
"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Innholdslengde: {size of chunk}
Innholdstype: applikasjon/oktettstrøm
{Layer Chunk Binary Data}".
Ellers vil du ikke kunne gå utover det første punktet, fordi... I stedet for forventet svarkode 202 vil du motta 4xx.
Nå ser algoritmen slik ut:
- Initialisering
- Patch rail
- Lasting av rekkverket
- Laster inn manifestet
Henholdsvis punkt 2 og 3 gjentas så mange ganger som antall linjer må lastes.
Først trenger vi et hvilket som helst bilde. Jeg vil bruke archlinux:latest
docker pull archlinux
La oss nå lagre det lokalt for videre analyse
docker save c24fe13d37b9 -o savedArch
Pakk ut det resulterende arkivet i gjeldende katalog
tar xvf savedArch
Som du kan se, er hver livline i en egen mappe. La oss nå se på strukturen til manifestet vi mottok
cat manifest.json | json_pp
Ikke mye. La oss se hvilket manifest som trengs for å laste, ifølge
Det er klart at det eksisterende manifestet ikke passer oss, så vi lager vårt eget med blackjack og kurtisaner, livlinjer og konfigurasjoner.
Vi vil alltid ha minst én konfigurasjonsfil og en rekke livslinjer. Scheme versjon 2 (gjeldende i skrivende stund), mediaType vil forbli uendret:
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
Etter å ha opprettet det grunnleggende manifestet, må du fylle det med gyldige data. For å gjøre dette bruker vi json-malen til rail-objektet:
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
},
Vi vil legge det til manifestet for hver skinne.
Deretter må vi finne ut størrelsen på konfigurasjonsfilen og erstatte stubbene i manifestet med ekte data
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
Nå kan du starte nedlastingsprosessen og lagre deg selv en uuid, som skal følge med alle påfølgende forespørsler.
Det komplette skriptet ser omtrent slik ut:
#!/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 bruke et ferdig skript:
./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0
OPP:
Hva fikk vi som resultat?
For det første, ekte data for analyse, siden testene kjøres i blazemeter og dataene om docker-klientforespørsler ikke er veldig informative, i motsetning til rene HTTP-forespørsler.
For det andre tillot overgangen oss å øke antallet virtuelle brukere for docker-opplasting med omtrent 150 % og få gjennomsnittlig responstid 20–25 % raskere. For docker-nedlasting klarte vi å øke antall brukere med 500 %, mens gjennomsnittlig responstid gikk ned med omtrent 60 %.
Takk for oppmerksomheten.
Kilde: www.habr.com