Teníem 2 bosses d'herba, 75 tauletes de mescalina entorn Unix, un dipòsit docker i la tasca d'implementar les ordres docker pull i docker push sense un client docker.
ACTUALITZACIÓ:
Pregunta: per a què serveix tot això?
Resposta: prova de càrrega del producte (NO utilitza bash, els scripts es proporcionen amb finalitats educatives). Es va decidir no utilitzar el client Docker per reduir capes addicionals (dins de límits raonables) i, en conseqüència, emular una càrrega més gran. Com a resultat, es van eliminar tots els retards del sistema del client Docker. Vam rebre una càrrega relativament neta directament sobre el producte.
L'article utilitzava versions de GNU d'eines.
Primer, anem a esbrinar què fan aquestes ordres.
Aleshores, per a què serveix Docker pull? D'acord amb
"Extreu una imatge o un repositori d'un registre".
També hi trobem un enllaç a
A partir d'aquí podem entendre que una imatge docker és un conjunt de determinades capes que contenen informació sobre els últims canvis de la imatge, que és evidentment el que necessitem. A continuació ens mirem
Diu el següent:
"Una "imatge" és una combinació d'un manifest JSON i fitxers de capes individuals. El procés d'extracció d'una > imatge se centra en la recuperació d'aquests dos components".
Així que el primer pas segons la documentació és "Tirant d'un manifest d'imatge".
Per descomptat, no el dispararem, però en necessitem les dades. El següent és un exemple de sol·licitud: GET /v2/{name}/manifests/{reference}
"El nom i el paràmetre de referència identifiquen la imatge i són obligatoris. La referència pot incloure una etiqueta o un resum."
El nostre repositori Docker es desplega localment, intentem executar la sol·licitud:
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/manifests/1.1.1" -H "header_if_needed"
Com a resposta, rebem json del qual actualment només ens interessen les línies de vida, o més aviat els seus hash. Un cop rebuts, podem revisar-los i executar la següent sol·licitud: "GET /v2/{name}/blobs/{digest}"
"L'accés a una capa es limitarà pel nom del dipòsit, però s'identifica únicament al registre mitjançant un resum".
digest en aquest cas és el hash que hem rebut.
Intentant
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer
Vegem quin tipus d'arxiu vam rebre finalment com a primera línia de vida.
file firstLayer
aquells. els rails són arxius tar, desempaquetant-los en l'ordre adequat obtindrem el contingut de la imatge.
Escrivim un petit script bash perquè tot això es pugui automatitzar
#!/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
Ara podem executar-lo amb els paràmetres desitjats i obtenir el contingut de la imatge requerida
./script.sh dirName “http://localhost:8081/link/to/docker/registry” myAwesomeImage 1.0
Part 2 - Docker push
Això serà una mica més complicat.
Comencem de nou amb
Després d'estudiar la documentació, podem dividir el procés de descàrrega en diversos passos:
- Inicialització del procés - "POST /v2/{repoName}/blobs/uploads/"
- Càrrega d'una línia de vida (utilitzarem una càrrega monolítica, és a dir, enviarem cada línia de vida en la seva totalitat) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
Durada del contingut: {mida de la capa}
Tipus de contingut: aplicació/stream d'octets
Capa de dades binàries". - S'està carregant el manifest - "PUT /v2/{repoName}/manifests/{reference}".
Però la documentació perd un pas, sense el qual res funcionarà. Per a la càrrega monolítica, així com per a la parcial (en trossos), abans de carregar el rail, heu de realitzar una sol·licitud PATCH:
"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Durada del contingut: {mida del tros}
Tipus de contingut: aplicació/stream d'octets
{Layer Chunk Binary Data}".
En cas contrari, no podreu avançar més enllà del primer punt, perquè... En lloc del codi de resposta esperat 202, rebreu 4xx.
Ara l'algorisme sembla:
- Inicialització
- Raíl de pegat
- Carregant la barana
- S'està carregant el manifest
Els punts 2 i 3, respectivament, es repetiran tantes vegades com calgui carregar el nombre de línies.
En primer lloc, necessitem qualsevol imatge. Faré servir archlinux:latest
docker pull archlinux
Ara desem-lo localment per a una anàlisi posterior
docker save c24fe13d37b9 -o savedArch
Descomprimiu l'arxiu resultant al directori actual
tar xvf savedArch
Com podeu veure, cada línia de vida es troba en una carpeta separada. Vegem ara l'estructura del manifest que hem rebut
cat manifest.json | json_pp
No gaire. Vegem quin manifest es necessita per carregar, segons
Evidentment, el manifest existent no ens convé, així que ens en farem el nostre amb blackjack i cortesanes, línies de vida i configuracions.
Sempre tindrem almenys un fitxer de configuració i una sèrie de línies de vida. Esquema versió 2 (actual en el moment d'escriure), mediaType es deixarà sense canvis:
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
Després de crear el manifest bàsic, heu d'omplir-lo amb dades vàlides. Per fer-ho, utilitzem la plantilla json de l'objecte rail:
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
},
L'afegirem al manifest de cada rail.
A continuació, hem d'esbrinar la mida del fitxer de configuració i substituir els talons del manifest per dades reals
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
Ara podeu iniciar el procés de descàrrega i desar-vos un uuid, que hauria d'acompanyar totes les sol·licituds posteriors.
L'script complet sembla una cosa així:
#!/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
podem utilitzar un script ja fet:
./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0
ACTUALITZACIÓ:
Què hem obtingut com a resultat?
En primer lloc, dades reals per a l'anàlisi, ja que les proves s'executen en blazemeter i les dades de les sol·licituds dels clients docker no són gaire informatives, a diferència de les sol·licituds HTTP pures.
En segon lloc, la transició ens va permetre augmentar el nombre d'usuaris virtuals per a la càrrega de Docker en un 150% i obtenir un temps de resposta mitjà entre un 20 i un 25% més ràpid. Per a la descàrrega de Docker, hem aconseguit augmentar el nombre d'usuaris en un 500%, mentre que el temps de resposta mitjà es va reduir en un 60%.
Gràcies per la seva atenció.
Font: www.habr.com