Meillä oli 2 pussillista ruohoa, 75 meskaliinitablettia unix-ympäristö, docker-varasto ja tehtävänä toteuttaa telakointiaseman veto- ja työntökomennot ilman docker-asiakasta.
UPD:
Kysymys: Mitä varten tämä kaikki on?
Vastaus: Tuotteen kuormitustestaus (EI käyttämällä bashia, skriptit toimitetaan opetustarkoituksiin). Docker-asiakasta päätettiin olla käyttämättä lisäkerrosten vähentämiseen (kohtuullisissa rajoissa) ja vastaavasti suuremman kuormituksen emulointiin. Tämän seurauksena kaikki Docker-asiakkaan järjestelmäviiveet poistettiin. Saimme suhteellisen puhtaan kuorman suoraan tuotteeseen.
Artikkelissa käytettiin työkalujen GNU-versioita.
Selvitetään ensin, mitä nämä komennot tekevät.
Joten mihin docker pullia käytetään? Mukaan
"Vedä kuva tai arkisto rekisteristä".
Sieltä löytyy myös linkki
Tästä voimme ymmärtää, että Docker-kuva on joukko tiettyjä kerroksia, jotka sisältävät tietoa viimeisimmistä muutoksista kuvassa, mitä tietysti tarvitsemme. Seuraavaksi katsomme
Siinä lukee seuraavaa:
""Kuva" on yhdistelmä JSON-luettelosta ja yksittäisistä tasotiedostoista. Kuvan vetäminen keskittyy näiden kahden komponentin hakemiseen."
Joten ensimmäinen askel dokumentaation mukaan on "Kuvamanifestin vetäminen".
Emme tietenkään ammu sitä, mutta tarvitsemme sen tiedot. Seuraava on esimerkkipyyntö: GET /v2/{name}/manifests/{reference}
"Nimi ja viiteparametri tunnistavat kuvan ja ovat pakollisia. Viite voi sisältää tunnisteen tai tiivistelmän."
Docker-varastomme on otettu käyttöön paikallisesti, yritetään suorittaa pyyntö:
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/manifests/1.1.1" -H "header_if_needed"
Vastauksena saamme jsonin, josta olemme tällä hetkellä kiinnostuneita vain elinlinjoista tai pikemminkin niiden tiivisteistä. Saatuamme ne, voimme käydä jokaisen läpi ja suorittaa seuraavan pyynnön: "GET /v2/{name}/blobs/{digest}"
"Pääsy tasoon suojataan arkiston nimellä, mutta se tunnistetaan yksilöllisesti rekisterissä tiivistelmällä."
Digest on tässä tapauksessa saamamme hash.
Yritetään
curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer
Katsotaan millaisen tiedoston lopulta saimme ensimmäiseksi pelastusköydeksi.
file firstLayer
nuo. kiskot ovat terva-arkistoja, purkamalla ne sopivassa järjestyksessä saamme kuvan sisällön.
Kirjoitetaan pieni bash-skripti, jotta kaikki tämä voidaan automatisoida
#!/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
Nyt voimme ajaa sen halutuilla parametreilla ja saada tarvittavan kuvan sisällön
./script.sh dirName “http://localhost:8081/link/to/docker/registry” myAwesomeImage 1.0
Osa 2 - telakkatyöntö
Tämä tulee olemaan hieman monimutkaisempi.
Aloitetaan uudestaan
Dokumentaation tutkimisen jälkeen voimme jakaa latausprosessin useisiin vaiheisiin:
- Prosessin alustus - "POST /v2/{repoName}/blobs/uploads/"
- Pelastusköyden lataaminen (käytämme monoliittista latausta, eli lähetämme jokaisen pelastusköyden kokonaisuudessaan) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
Sisällön pituus: {kerroksen koko}
Sisältötyyppi: sovellus/oktettivirta
Layer Binary Data". - Ladataan luetteloa - "PUT /v2/{repoName}/manifests/{reference}".
Mutta dokumentaatiosta puuttuu yksi vaihe, jota ilman mikään ei toimi. Monoliittinen kuorma, samoin kuin osittainen (palastettu), ennen kiskon lastausta on suoritettava PATCH-pyyntö:
"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Sisällön pituus: {size of chunk}
Sisältötyyppi: sovellus/oktettivirta
{Layer Chunk Binary Data}".
Muuten et voi siirtyä ensimmäisen pisteen pidemmälle, koska... Odotetun vastauskoodin 202 sijasta saat 4xx.
Nyt algoritmi näyttää tältä:
- Alustus
- Patch kisko
- Kaiteen lastaus
- Ladataan luetteloa
Kohdat 2 ja 3 toistetaan niin monta kertaa kuin rivejä on ladattava.
Ensinnäkin tarvitsemme minkä tahansa kuvan. Käytän archlinux:latest
docker pull archlinux
Tallennetaan se nyt paikallisesti lisäanalyysiä varten
docker save c24fe13d37b9 -o savedArch
Pura tuloksena oleva arkisto nykyiseen hakemistoon
tar xvf savedArch
Kuten näet, jokainen pelastusköysi on erillisessä kansiossa. Katsotaanpa nyt saamamme manifestin rakennetta
cat manifest.json | json_pp
Ei paljon. Katsotaan, mikä manifesti on ladattava, sen mukaan
Ilmeisesti olemassa oleva manifesti ei sovi meille, joten teemme omamme blackjackilla ja kurtisaaneilla, pelastuslinjoilla ja asetuksilla.
Meillä on aina vähintään yksi asetustiedosto ja joukko elinlinjoja. Kaavan versio 2 (nykyinen kirjoitushetkellä), mediaType jätetään ennalleen:
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
Perusluettelon luomisen jälkeen sinun on täytettävä se kelvollisilla tiedoilla. Käytämme tähän rail-objektin json-mallia:
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": ${layersSizes[$i]},
"digest": "sha256:${layersNames[$i]}"
},
Lisäämme sen jokaisen kiskon manifestiin.
Seuraavaksi meidän on selvitettävä konfigurointitiedoston koko ja korvattava luettelon tyngät todellisilla tiedoilla
sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
Nyt voit aloittaa latausprosessin ja tallentaa itsellesi uuid-tunnuksen, jonka pitäisi liittää kaikkiin myöhempiin pyyntöihin.
Koko skripti näyttää suunnilleen tältä:
#!/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
voimme käyttää valmiita skriptiä:
./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0
UPD:
Mitä saimme tuloksena?
Ensinnäkin todellista dataa analysoitavaksi, koska testit ajetaan blazemeterissä ja docker-asiakaspyyntöjen tiedot eivät ole kovin informatiivisia, toisin kuin puhtaat HTTP-pyynnöt.
Toiseksi siirron ansiosta pystyimme lisäämään virtuaalisten käyttäjien määrää docker-latauksessa noin 150 % ja saada keskimääräinen vasteaika 20-25 % nopeammaksi. Docker-latauksen osalta onnistuimme kasvattamaan käyttäjien määrää 500 %, kun taas keskimääräinen vasteaika lyheni noin 60 %.
Kiitos huomiota.
Lähde: will.com