Visokie dockeriai

Posted: 2021-04-02 in Pezalai
Žymos:, , ,

Šitas įrašas skirtas kompiuterastams, linuxoidams ir man pačiam, kaip užrašėlis/išorinė atmintis. Jei negirdėjote, kas yra Dockeris ir apie Linux esate girdėję tik kad toks daiktas egzistuoja, gal praleiskite 🙂

Linux architektūra yra tikrai labai įdomi. Na, dėl to dar, kad ją pažįstu. Tikrai nesakau, kad Windows yra neįdomi sistema, bet ji iš esmės visiškai kitokia. Ten irgi yra tikrų nuostabybių ir aš nesu religiškai nusiteikęs apskritai prieš jokias OS: visos turi savų pritaikymų ir visos yra savaip geros ar blogos.

Linux man yra patogiausia sistema dėl darbo pobūdžio, nes ši OS yra paplitusi labiausiai pasaulyje. Nekalbam čia apie eilinių vartotojų kompiuterius, o serverius, visokius mažus ir įterptinius (embedded) įrenginius. Kadangi aš su visokiais sąlyginai smulkiais ir įterptiniais įrenginiais dirbu, man visi Linux įrankiai savaime yra patogiau: daug ką galiu pasileisti tiesiai savo kompiuteryje, nereikia virtualių mašinų ar atskirų aplinkų daugumai darbų atlikti.

Bet su visais šiais smagumais atsiranda ir toks reikalas, kaip kompiuterio OS „užsiteršimas“ arba koks nors durnas versijų nesuderinamumas. Labai lengva savo kompiuteryje prisidiegti visokių tuo metu reikalingų paketų, kuriuos paskui pamiršti pravalyt arba pradedi nebesigaudyt, ko tau nebereikia.

Čia į pagalbą ateina Docker – konteinerizavimo įrankis. Jis egzistuoja jau seniai, o netiesioginu pavidalu – dar seniau. Kai dar nebuvo išrastas Dockeris, seni linuxoidai naudodavosi visokiais chroot, ip namespace ir dar krūva kitų ganėtinai egzotinių priemonių. Dockeris, nors gali greitai pasidaryti labai sudėtingas, iš esmės yra jūzerio-lūzerio priemonė greitai padaryti kokią sudėtingą programinės įrangos izoliaciją. Sakykim, norite paleisti kokią nors programą ar visą jų rinkinį izoliuotai nuo savo sistemos ar su kitomis bibliotekų versijomis, bet nenorite kurti tokio gargaro, kaip virtualkė. Dockeris tą padaro kiek paprasčiau: izoliuoja visokias bibliotekas, tačiau išnaudoja tą patį OS branduolį. Kaip ir virtualkė, bet kaip ir ne.

Aišku, jei jūsų kompiuteryje sukasi kokia senesnė Linux versija, o reikia kažko iš naujesnės – su Dockeriu šita užduotėlė tampa gan lengva. Arba atvirkščiai: reikia paleisti kažką nusenusio su specifinėm versijom. Arba tiesiog kažką paranojiškai izoliuoti, pavyzdžiui paleisti izoliuotą naršyklę per VPN, tačiau tik ją vieną, o ne viską, kas jums svarbu: paštas, kokie nors IP keitimui jautrūs korporatyviniai prisijungimai ir panašiai. Na, naršyklę ir su tinklo namespace gan lengva sutvarkyti, bet gal norisi ją dar izoliuoti ir nuo įprastų nustatymų ar tiesiog neleisti prie pagrindinių savo kompiuterio failų prieiti.

Aš Dockerį naudoju jau seniai. Tiksliai nepamenu, kiek. Smagu ir kad mano NAS palaiko Docker konteinerius, tai su jų pagalba esu prisileidęs įvairių paties NAS nepalaikomų smagybių. Beveik Dockerių spiečius jau.

Šiais laikais tiems visiems spiečiams valdyti yra ir specialių įnagių, tokių kaip Kubernetes, MicroK8s ir dar visokios velniavos. Man to neprireikia, bet va Dockerio konteineriukus naudoju mielai ir per daug galvos nesukdamas, dažnai visokiems eksperimentams.

Taigi mano asmeniniai (gykiniai, t.y. ne darbiniai) panaudos atvejai:

  • Naujesnių programų paleidimas, nes paprastai užsisėdžiu su viena LTS versija dvejus/ketverius metus. Labai tingiu perinstaliuot viską.
  • Nuolat naudojamų programų lengvesnis atnaujinimas su visokiom ten bibliotekų versijom ir pan.
  • Kitais būdais nepaleidžiamų programų ant NAS paleidimas.
  • Koks nors paranojiškas izoliavimas (minėtos naršyklės per VPN ar prieigų apribojimai).
  • Eksperimentai su AI ir šiaip visokia egzotika.

Kur gauti

Yra toks Docker Hub, panašiai, kaip github. Ten pilna visokių paruoštukų. Jei radote jums patinkamą, užtenka paleisti komandą, kuri partįs atitinkamai suformuotą rinkinį (image). O tada galima paleisti to rinkinio konteinerį ir kažką su juo daryti.

Kitas būdas, kurį dažniausiai pats naudoju, – susikurti savo Dockerfile. Tai yra failas su instrukcijomis, kaip sukurti jums reikalingą rinkinį. Pirma eilutė visada nurodo būsimo rinkinio/konteinerio pagrindą. Pagrindas gali būti tam tikra Linux versija, pvz. ubuntu:16.04 arba jau pusiau paruošta aplinka, pvz. su Python3 python3:latest. Toliau keliauja kitos instrukcijos, kurios sudiegia jums reikalingas bibliotekas ir programas, sukuria vartotojų aplinką (jei to reikia) ir panašiai.

Naujesnės programos

Čia nėra daug ką aiškint. Kai Linux distribucija nusensta, net ir LTS, joje besivoliojantys programų paketai irgi nusensta. Prieš kelerius metus, prieš atsinaujinimą, man prireikė pakarpyti H265 vaizdo įrašą, o mano tuometinė distribucijos naujausia ffmpeg versija dar tokio formato nesuprato. Tai viskas buvo yzy: pasidariau tuo metu naujausio Ubuntu pagrindu Dockerio konteinerį su ffmpeg, pasileidau pririšęs prie reikiamo katalogo ir netgi prikabinau kompiuterio jūzerių konfigūraciją, kad konteinerio shell’as būtų praktiškai pririštas prie normalios mano aplinkos.

Čia mano senas Dockerfile, kuriame aš tik pakeičiau Ubuntu versiją į šiuo metu naujausią:

FROM ubuntu:21.04

RUN apt-get update \
        && apt-get install -y ffmpeg

Tik tiek! Aišku, Ubuntu pagrindo partempimas užtruks ir užims kelis šimtus megabaitų diske. O šito rinkinio partempimui ir naudojimui aš dar šalia pasidėjau shell skriptuką:

#!/bin/bash -ex

tag=ffmpeg-21-04
params="--rm -v /media/Multimedia/trans:/trans -w /trans -u"$(id -u):$(id -g)" -v /etc/group:/etc/group:ro -v /etc/passwd:/etc/passwd:ro -v /etc/shadow-:/etc/shadow:ro ${tag}"

docker build --tag=${tag} .

docker run -it $params bash

Paleidžiant konteinerį jam gali tekti perduoti daugybę parametrų. Šiuo atveju aš nurodžiau, kokiame kataloge ruošiuosi su konteineriu terliotis (/media/Multimedia/trans) ir koks katalogas jį atitiks konteinerio viduje (/trans). Toliau su -w nurodoma, kad tai bus einamasis darbinis katalogas konteineriui startavus. Dar, kad būtų visai paprasta, prikabinu konteineriui ir pagrindinės OS failus, turinčius savyje informaciją apie visus vartotojus. :ro nurodo, kad tie failai bus read-only, konteineryje besisukančios programos jų negalės pakeisti. Einamasis vartotojas nustatomas į tą, iš kurio konteineris paleidžiamas.

Lengvesnis atnaujinimas

Dauguma konteinerių turi vadinamą latest žymę, t.y. jie atnaujinami nuolat. Tas labai gerai, kai kažkokia programa leidžiama būtent iš konteinerio ir ją reikia atnaujinti. Tiesiog nudobiamas konteineris, išvalomas, startuojamas iš naujo ir dažniausiai nauja rinkinio versija partempiama automagiškai. Priklauso, žinoma, nuo paleidimo skripto: ar jis kaskart perkuria rinkinį, ar konteineris kaskart perkuriamas ir taip toliau. Čia kaip visada, prasideda niuansai – kuo giliau į mišką, tuo daugiau medžių. Praėjusio pavyzdžio skriptukas kaskart bando perkurt konteinerį, nors su fiksuota Ubuntu versija tas nebūtina. Taip pat konteinerio paleidimo parametruose yra –rm, kas reiškia, jog konteineris pabaigęs savo darbą bus ištrintas, Dockeris jo „neatsimins“.

Šiuo būdu aš gan lengvai ir neskausmingai atsinaujinu Home Assistant, kuris dirba už mano trobos protą. Tas „neskausmingai“, tiesa, ne visada suveikia 😀 Kartą buvau užtempęs ir nepastebėjau, kad Home Assistant iš pagrindo pakeitė savo UI. Su nauju rinkiniu konteineris pasileido, bet visas vaizdas buvo išsilakstęs. Teko perkurti namo proto „snukelį“ per naują. Na, bet nereikia rūpintis, kad būtų reikiamos OS bibliotekos, pitono versijos ir kitos ten priklausomybės – Docker rinkinyje viskas yra.

Įvairovė NAS

Kadangi vis dar vargstu su savo QNAP NAS, tai trumpai apie jį. Minėtas ffmpeg skriptas numigravo būtent į NAS, nes ten laikomi vaizdo įrašai, kuriuos kartais su ffmpeg ir apdoroju. Dažniausiai tai būna karpymas ir jungimas nenaudojant perkodavimo, tad NAS procesoriuko tam visai pakanka, nereikia failų siuntinėti tinklu pirmyn-atgal. Neradau kito patogaus būdo ffmpeg įsidiegti į NAS, nebent per Entware-ng. Bet tas projektas jau senstelėjęs ir ne viskas puikiai veikia, kaip norėtųsi, ffmpeg ten irgi pagyvenus versija. Tad Docker šiuo atveju labai gerai. Be to, nenorint terliotis su shell, QNAP turi visai neblogą grafinę konteinerių tvarkyklę ir eiliniams žmonėms su jos pagalba kur kas paprasčiau pasikurti nuosavą spiečių. Ten pas mane sukasi Home Assistant, Transmission, SMTP agentas, Mosquitto, Gitolite ir dar keli niekučiai. Viskas tvarkingai, viskas nuosavuose konteineriuose, grožis ir gėris. Be konteinerių ant NAS būtų didelė problema pasileisti tai, ko norisi.

Kai apsėda paranoja

Yra tinklapių, kuriuose lankausi ir man visai ne gėda prisipažinti, kad nenoriu jiems rodyti jokių savo IP, kukių ar apskritai kad mane kažkaip sektų. Ir tai nėra nei piratiniai, nei pornografiniai tinklapiai, anei noras pakomentuoti kažkokį pornalų šūdą. Pastaruoju metu vis tenka užsirauti ant kokių nors amerikoniškų tinklapių, kurie tingi pas save diegti GDPR reikalavimus ir paprasčiausiai pasako, kad europiečiams nieko nerodys (403 Forbidden).

Šiam atvejui labai gerai Dockeris su kokiu nors VPN – OpenVPN gargaru ar WireGuard (jei duoda koks provideris). Kiek sudėtingiau yra paleisti iš Dockerio naršyklę, nes tai yra grafinė programa. Bet visa gudrybė yra tiesiog nustatyti reikiamus aplinkos kintamuosius ir „primontuoti“ reikiamus failus – panašiai, kaip /etc/passwd atveju.

Štai Dockerfile pavyzdys, kuriame sukištas Firefox ir OpenVPN:

FROM ubuntu:20.04

RUN apt-get update
RUN apt-get install -y sudo curl gnupg openvpn

ENV TZ=Europe/Vilnius
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get install -y firefox

RUN groupadd -g 1000 darauble
RUN useradd -d /home/darauble -s /bin/bash -m darauble -u 1000 -g 1000
RUN echo "darauble ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

USER darauble
ENV HOME /home/darauble

Reikia įsidiegti sudo, kad būtų galima paleidinėti OpenVPN su root. curl naudingas, kai reikia parsitįsti ar nusisiurbti kažką naudojant VPN. Kam man reikėjo gnupg, dabar nebepamenu.

Laiko juostos nustatymas padarytas tam, kad diegiamos Firefox priklausomybės nesustabdytų Docker rinkinio kūrimo – ten kažkodėl negalima nurodyti laiko juostos, nereaguoja shell.

Šitame paranojiškame konteineryje neduodu skaityti /etc failų, o sukuriu vartotoją su tuo pačiu ID, kaip ir mano pagrindinis kompiuteryje, jam nurodau namų katalogą ir vuolia – galima leisti konteinerį.

#!/bin/bash
TAG=ff-paranoid

docker build -t ${TAG} .

docker run -it --rm --privileged -v /media/Docker/${TAG}/home:/home/darauble -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY -h $HOSTNAME -v $HOME/.Xauthority:/home/sp/.Xauthority ${TAG} /bin/bash

Čia iš pagrindinės OS „primontuoju“ specifinį katalogą, kaip konteinerio vartotojo naminį. Ten naršyklė gali pasidėti kukius, aš galiu padėti VPN kredencialus, o jei reikia viską išvalyti – tai to katalogo turinį ištrynus viskas vėl ok. Norint, kad tarp konteinerio paleidimų niekas neišsisaugotų, to naminio katalogo prijungti nereikia iš viso.

Toliau perduodamas $DISPLAY, kad programa galėtų prieiti prie grafinės posistemės. Tam taip pat prijungiamas ir .X11-unix failas.

Paleidus skriptą patenkama į konteinerio bash. Ten su ampersandu paleidžiam Firefox (kad suktųsi fone), o paskui normaliai paleidžiam OpenVPN su reikiamais nurodymais. Konteineris palenda po VPN ir galime ramiai sau naršyti.

Panašų dalyką galima pasidaryti ir su tinklo namespace. Tačiau naršyklės tai neizoliuos.

Eksperimentai visokie

Be abejo, Docker yra visiškai nerealus, kai daromi visokie eksperimentai ir reikia prisikišti į kompiuterį krūvą tik laikinai reikalingo softo. Tai jei laikinai – geriau nekišti iš viso.

Neseniai darėm su dukrike tokį eksperimentą. Ji sako man: kaži kaip užsieniečiams skamba lietuvių kalba? Na, skamba kažkaip, bet kadangi mes tą kalbą suprantam, negalim išskirti jos specifinio burblenimo, automatiškai įsijungia parseris smegenyse. Jei girdim kokią nors visai svetimą kalbą, išskiriam tam tikras fonemas, akcentus ir galim netgi pamėginti tą kalbą pamėgdžioti – nors jos kalbėtojai išsižvengtų bile kaip.

Tad mintis kilo tokia: pasikurti kokį AI frameworką, sukišti jam kokio nors gražiai kalbančio diktoriaus balso pavyzdžių, apmokyti ir paskui paleisti generatorių, kuris sukurtų fonetiškai panašų garsą, bet be žodžių ir sakinių struktūros. Tam yra WaveNet frameworkai, jie sugeba šią užduotį atlikti. Naudojami gražiam teksto į balsą vertimui ar dirbtinei muzikai kurti.

Iš karto pasakau, kad eksperimentas nepavyko – reikėjo per daug neuroninį tinklą mokyti, mano kompiuteriu būtų reikėję tai daryti kelias savaites. Po dviejų parų jis vis dar buvo neapsimokęs, tad reikalą užmečiau, nors kažkokius veblenimus su triukšmais jau generavo. Apskritai buvo kur kas įdomiau pakurti konteinerį, o jo Dockerfile gavosi va toks:

FROM tensorflow/tensorflow:1.15.5-py3

ENV TZ=Europe/Vilnius
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get update
RUN apt-get -y install git libsndfile1

RUN pip install librosa

RUN groupadd -g 1000 darauble
RUN useradd -d /home/darauble -s /bin/bash -m darauble -u 1000 -g 1000
RUN echo "darauble ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

USER darauble
ENV HOME /home/darauble
CMD /bin/bash

Jau „naminiam“ kataloge dar reikia parsitempti frameworką:

git clone https://github.com/ibab/tensorflow-wavenet

Jį dar teko taisyti, nes kai kurios bibliotekos buvo nuevoliucionavę ir pasikeitę jų API. Dėl šios priežasties geriau visokius githubo sorsus kišti ne į Dockerfile, o prisidėti išoriškai.

Niuansų buvo dar ir dėl to, kad reikėjo būtent Python3 ir jam pritaikytos specifinės senos Tensorflow versijos – su naujausiom WaveNet frameworkas lūžo ir neveikė. sudo man ten irgi kažkam prireikė, jau nebepamenu.

Kitas panašiai durnas eksperimentas buvo su juodai-baltų nuotraukų spalvinimo frameworku. Kažkoks bičas kadaise apmokė Torch frameworką su krūva spalvotų nuotraukų ir jų nespalvotais atitikmenimis, kad kitas nespalvotas nuotraukas pamatęs neuroninis tinklas jas padažytų. Neveikia tas reikalas taip jau puikiai, yra šiais laikais geresnių priemonių, kurios nuotraukas nuspalvina su keliais pelės pabaksnojimais ir spalvų parinkimais. Na, bet norėjosi pabandyti ir dar palyginti su online servisais, kurie tai irgi daro automagiškai (tiems servisams lankyti visai praverčia paranojiškasis konteineris).

Su nuotraukų spalvinimu išbandžiau du frameworkus: Richard Zhang colorization ir gan smarkiai paplitusį bei labai protingą DeOldify. colorization mažiau apdorotas ir ne taip gerai skirtas eiliniams vartotojams. Ilgai vargau, kol jam susirankiojau tinkamą Dockerfile, nes tai kažkas neveikė, tai versijos netiko, tai dar kodą reikėjo taisyt – jis ketverius metus neprižiūrėtas. Rezultatai irgi labai nenudžiugino.

DeOldify tuo tarpu turi ir konteinerių aprašymus, ir krūvą paruoštukų, ir dar paleidžia Web serverį, į kurį galima padėt savo nuotraukas ir gauti spalvotus rezultatus.

Kad tik tas serveris veiktų…

Liūdna dalis buvo ta, kad konteineris neveikė: lūždavo su core dump ir keista klaida apie kažkokias nepalaikomas instrukcijas. Pasirodė, kad konteineris pagrįstas specifine PyTorch versija, kuri mano 10-ies metų kompiuteriui per nauja – t.y. kompiuterio procesorius per senas ir nepalaiko kai kurių mašininio kodo instrukcijų! Pasisekė tik tiek, kad pasendinus PyTorch versiją viskas suveikė ir dabar galiu spalvinti nespalvotas nuotraukas.

Pvz. tokias, kaip ši:

Sophia Loren, Jayne Mansfield | Darau, blė

DeOldify visai smagiai nuspalvino šitas damas, netgi lūpytes paraudonino:

Sophia Loren, Jayne Mansfield | Darau, blė

Tiesa, Web serverio taip ir nesusitvarkiau, nesupratau, kas ten jam negerai. Tiksliau – tingėjau gilintis. Tiesiog pasirašiau naują Python skriptuką, kuris tiesiai paverčia nespalvotus paveikslėlius spalvotais ir tiek, be jokių ten upload/download nesąmonių. Kam man to reikia vietiniam kompiutery?

Mano paredaguotas DeOldify Dockerfile nuo originalo skiriasi tik keliom eilutėm. Pirmojoj pakeičiau PyTorch versiją:

FROM nvcr.io/nvidia/pytorch:18.12-py3

Tada dar kažkodėl nesuveikė [[ ! -f /data/models/ColorizeArtistic_gen.pth ]] komanda. Pakeičiau ją tiesiog į test ! -f /data/models/ColorizeArtistic_gen.pth.

Spalvinimo skriptukas:

# import the necessary packages
import os
import sys
import requests
import ssl
from flask import Flask
from flask import request
from flask import jsonify
from flask import send_file


from app_utils import download
from app_utils import generate_random_filename
from app_utils import clean_me
from app_utils import clean_all
from app_utils import create_directory
from app_utils import get_model_bin
from app_utils import convertToJPG

from os import path
import torch

import fastai
from deoldify.visualize import *
from pathlib import Path
import traceback


del os.environ["CUDA_VISIBLE_DEVICES"]


if __name__ == '__main__':
	model_directory = '/data/models/'
	create_directory(model_directory)
	artistic_model_url = "https://data.deepai.org/deoldify/ColorizeArtistic_gen.pth"

	# only get the model binay if it not present in /data/models
	get_model_bin(
		artistic_model_url, os.path.join(model_directory, "ColorizeArtistic_gen.pth")
	)

	image_colorizer = get_image_colorizer(artistic=True)

	print("PRADEDAM...")

	input_path = sys.argv[1]
	image_colorizer.plot_transformed_image(path=input_path, figsize=(20,20), render_factor=30, display_render_factor=True, compare=False)

	print("Spalvinimas baigtas!")

Aptikau, kad po kiekvieno konteinerio paleidimo iš naujo šitas skriptukas partempia apie 80 MB sveriantį failą iš interneto. Po to nebekartoja iki kito konteinerio paleidimo. Tad aš jį parsitempiau pats ir paleisdamas konteinerį nurodau, iš kur tą failą imti. Susikūriau pradinių ir jau nuspalvintų paveiksliukų katalogus ir juos taip pat pakišau konteineriui. Tad dabar paleidžiu konteinerį su tokiu skriptuku:

#!/bin/bash
DEOLDIFY_DIR=/share/KOPIJOS/DeOldify

docker run -it --rm \
	-v ${DEOLDIFY_DIR}/spalvinimas.py:/data/spalvinimas.py \
	-v ${DEOLDIFY_DIR}/cache/resnet34-333f7ec4.pth:/root/.cache/torch/hub/checkpoints/resnet34-333f7ec4.pth \
	-v ${DEOLDIFY_DIR}/images:/images \
	-v ${DEOLDIFY_DIR}/result_images:/data/result_images \
	deoldify /bin/bash

Tai va tokie reikalai su tais dockeriais. Reikėtų kažką tokio su visokiom nežinomom priklausomybėm tiesiog pagrindinėj OS susiinstaliuoti – sveikas pražuvęs.

Komentarai
  1. Rimas B. parašė:

    Sakai vargsti su QNAP… Tai tada manasis Synology būtų tikras prakeiksmas, bet ačiū dievui Docker’is yra. Kažkada bandžiau tiesiai Home Assistant’ą suinstaliuot, tai vos per langą neišmečiau – Synology’je viskas ne “kaip pas žmones“, Python’as ir tas kitur sėdi. Bet tada pabandžiau Docker’į ir korta smagiai paėjo.

  2. eenamas parašė:

    Supratau, kad nieko nesupratau :). Labai daug paukščių kalbos :). Gal kada nors :).

  3. Raspberry Pi parašė:

    […] Darau Blė straipsnis apie Dockerius […]

Parašykite komentarą

Įveskite savo duomenis žemiau arba prisijunkite per socialinį tinklą:

WordPress.com Logo

Jūs komentuojate naudodamiesi savo WordPress.com paskyra. Atsijungti /  Pakeisti )

Google photo

Jūs komentuojate naudodamiesi savo Google paskyra. Atsijungti /  Pakeisti )

Twitter picture

Jūs komentuojate naudodamiesi savo Twitter paskyra. Atsijungti /  Pakeisti )

Facebook photo

Jūs komentuojate naudodamiesi savo Facebook paskyra. Atsijungti /  Pakeisti )

Connecting to %s

Brukalų kiekiui sumažinti šis tinklalapis naudoja Akismet. Sužinokite, kaip apdorojami Jūsų komentarų duomenys.