Kodėl verta susipažinti su Docker technologija
Prisimenu, kaip prieš keletą metų kolega pasakojo apie savo košmarą – projektas puikiai veikė jo kompiuteryje, bet production serveryje viskas byrėjo. „Works on my machine” tapo mūsų komandos vidinių pokštų dalimi, kol neaptikome Docker. Šiandien ši technologija tapo tokia įprasta, kad naujų projektų kūrimas be konteinerizacijos atrodo keistai.
Docker iš esmės išsprendžia amžiną problemą – kaip užtikrinti, kad aplikacija veiks vienodai visur: kūrėjo nešiojamame kompiuteryje, testuotojo aplinkoje, staging serveryje ir galutinėje production sistemoje. Konteineriai įpakuoja ne tik jūsų kodą, bet ir visas priklausomybes, bibliotekas, konfigūracijas – viską, ko reikia programai veikti.
Praktiškai tai reiškia, kad galite užmiršti situacijas, kai vienas komandos narys dirba su PHP 7.4, kitas su 8.1, o serveris turi 7.2. Visi naudoja tą patį konteinerį su ta pačia versija. Skamba paprasta, bet tai kardinaliai keičia darbo eigą.
Kaip Docker veikia ir kuo skiriasi nuo virtualių mašinų
Daugelis pradedančiųjų klausia – ar Docker nėra tas pats kas VirtualBox ar VMware? Trumpas atsakymas: ne, ir tai yra gera žinia. Virtualios mašinos emuliuoja visą operacinę sistemą su branduoliu, draiversiais ir viskuo kitu. Tai reiškia, kad jei turite 3 VM, jūs faktiškai paleidžiate 3 pilnas OS kopijas. Resursų ėdikai, tiesą sakant.
Docker konteineriai veikia kitaip – jie dalijasi host sistemos branduoliu, bet turi izoliuotą failų sistemą, procesus ir tinklo sąsają. Vienas konteineris gali užimti vos keliasdešimt megabaitų, kai VM paprastai reikia kelių gigabaitų. Paleidimas užtrunka sekundes, ne minutes.
Techniškai Docker naudoja Linux kernel funkcijas kaip namespaces ir cgroups. Namespaces užtikrina izoliaciją – kiekvienas konteineris mato tik savo procesus, failų sistemą, tinklo interfeisus. Cgroups kontroliuoja resursų paskirstymą – kiek CPU, RAM ar disko I/O gali naudoti konteineris.
Praktinis pavyzdys: jūsų web projektas gali turėti atskirą konteinerį PHP aplikacijai, atskirą MySQL duomenų bazei, dar vieną Redis cache, ir viskas veiks jūsų laptope naudodamas mažiau resursų nei viena VM su visa šita krūva.
Pirmieji žingsniai: Dockerfile ir image kūrimas
Dockerfile – tai receptas, kaip sukurti jūsų aplikacijos image. Panagrinėkime realų pavyzdį PHP/Laravel projektui:
FROM php:8.2-fpm
WORKDIR /var/www/html
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY . .
RUN composer install --no-dev --optimize-autoloader
RUN chown -R www-data:www-data /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
Kas čia vyksta? Pradedame nuo bazinio PHP 8.2 FPM image (FROM). Nustatome darbo direktoriją (WORKDIR). Įdiegiame sisteminius paketus, kurių reikia mūsų aplikacijai. Įjungiame reikalingas PHP ekstensijas. Nukopijuojame Composer ir mūsų kodą. Įdiegiame PHP priklausomybes. Nustatome teises ir atidarome portą.
Svarbus patarimas: nenaudokite COPY . . komandos be .dockerignore failo. Sukurkite .dockerignore ir įtraukite node_modules, vendor, .git ir kitus nereikalingus katalogus. Kitaip jūsų image bus milžiniškas ir build procesas lėtas.
Kai Dockerfile paruoštas, sukuriate image komanda:
docker build -t mano-projektas:latest .
Taškas gale reiškia, kad Dockerfile yra dabartiniame kataloge. Parametras -t suteikia pavadinimą ir tag’ą jūsų image.
Docker Compose: orkestravimas vietiniam developmentui
Vienas konteineris – gerai, bet realūs projektai retai apsiriboja vienu servisu. Čia į pagalbą ateina Docker Compose. Tai įrankis, leidžiantis aprašyti ir paleisti kelių konteinerių aplikacijas.
Štai docker-compose.yml pavyzdys tipiniam LEMP (Linux, Nginx, MySQL, PHP) stackui:
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./:/var/www/html
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php
- mysql
php:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./:/var/www/html
environment:
- DB_HOST=mysql
- DB_DATABASE=laravel
- DB_USERNAME=root
- DB_PASSWORD=secret
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: laravel
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
mysql-data:
Paleidžiate viską viena komanda: docker-compose up -d. Parametras -d reiškia detached režimą – konteineriai veiks fone.
Patogumas akivaizdus: naujas komandos narys klonuoja repo, paleidžia docker-compose up, ir po minutės turi veikiančią development aplinką. Jokio „įdiek MySQL, sukonfigūruok PHP, įjunk extensijas” maratono.
Svarbu suprasti volumes koncepciją. Eilutė ./:/var/www/html reiškia, kad jūsų lokalus kodas yra „įmontuotas” į konteinerį. Keičiate failą savo IDE – pakeitimai iš karto matomi konteineryje. Duomenų bazės volume (mysql-data) užtikrina, kad duomenys išliks net perkrovus konteinerius.
Realūs scenarijai ir sprendimų būdai
Kaip dirbti su duomenų baze
Dažna problema: kaip importuoti duomenų bazės dump’ą į MySQL konteinerį? Keletas būdų:
Pirmas – tiesiogiai per docker exec:
docker exec -i mysql-container mysql -uroot -psecret laravel < dump.sql
Antras – per docker-compose:
docker-compose exec mysql mysql -uroot -psecret laravel < dump.sql
Trečias – automatizuotas per docker-entrypoint-initdb.d. MySQL image automatiškai vykdo .sql failus iš šio katalogo pirmą kartą paleidžiant konteinerį:
mysql:
image: mysql:8.0
volumes:
- ./docker/mysql/init:/docker-entrypoint-initdb.d
- mysql-data:/var/lib/mysql
Debugging ir logai
Kai kažkas neveikia, pirmiausia žiūrite logus:
docker-compose logs -f php
Parametras -f (follow) rodo logus realiu laiku. Norite pamatyti paskutines 100 eilučių?
docker-compose logs --tail=100 php
Kartais reikia „įlįsti" į konteinerį ir pažiūrėti, kas viduje:
docker-compose exec php bash
Dabar esate konteineryje ir galite vykdyti bet kokias komandas, tikrinti failus, testuoti konfigūraciją.
Performance optimizavimas Mac ir Windows sistemose
Jei dirbate Mac ar Windows, pastebėsite, kad volumes gali būti lėti. Tai žinoma problema, nes Docker šiose sistemose veikia per virtualizacijos sluoksnį. Sprendimai:
Naudokite cached ar delegated režimus:
volumes:
- ./:/var/www/html:cached
Arba išvis nenaudokite volumes development metu – kopijuokite kodą į image ir naudokite hot reload įrankius. Tai greitesnis, bet mažiau patogus variantas.
Dar vienas triukas – naudokite named volumes vendor ir node_modules katalogams:
volumes:
- ./:/var/www/html:cached
- vendor:/var/www/html/vendor
- node_modules:/var/www/html/node_modules
Taip šie katalogai lieka konteineryje ir nėra sinchronizuojami su host sistema, kas gerokai pagreitina darbą.
Multi-stage builds ir production optimizavimas
Development ir production aplinkos reikalavimai skiriasi. Development reikia debug įrankių, hot reload, source maps. Production reikia minimalaus image dydžio, saugumo, greičio.
Multi-stage builds leidžia turėti vieną Dockerfile, kuris kuria skirtingus image variantus:
# Build stage
FROM node:18 AS frontend-builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM php:8.2-fpm-alpine
WORKDIR /var/www/html
RUN apk add --no-cache \
mysql-client \
&& docker-php-ext-install pdo_mysql opcache
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY composer*.json ./
RUN composer install --no-dev --no-scripts --optimize-autoloader
COPY . .
COPY --from=frontend-builder /app/public/build ./public/build
RUN chown -R www-data:www-data /var/www/html
USER www-data
EXPOSE 9000
CMD ["php-fpm"]
Pastebėkite alpine variantą – tai minimalus Linux distribucija, dėl kurios image gali būti 5-10 kartų mažesnis. Taip pat naudojame USER direktyvą – konteineris veiks ne root teisėmis, kas saugiau.
Production docker-compose.yml paprastai naudoja pre-built images iš registry:
services:
php:
image: registry.example.com/mano-projektas:${VERSION}
restart: unless-stopped
environment:
- APP_ENV=production
CI/CD integracija ir automatizavimas
Docker puikiai dera su CI/CD pipeline'ais. GitLab CI pavyzdys:
stages:
- build
- test
- deploy
build:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
test:
stage: test
script:
- docker-compose -f docker-compose.test.yml up -d
- docker-compose -f docker-compose.test.yml exec -T php vendor/bin/phpunit
- docker-compose -f docker-compose.test.yml down
deploy:
stage: deploy
script:
- ssh user@server "docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
- ssh user@server "docker-compose up -d"
only:
- main
Kiekvienas commit sukuria naują image su unikaliu tag'u (commit SHA). Testai vykdomi atskirame docker-compose.test.yml aplinkoje. Jei testai praeina ir tai main branch – automatiškai deplojiname.
Svarbus patarimas: naudokite image registry (Docker Hub, GitLab Container Registry, AWS ECR). Nekurkite image tiesiogiai production serveryje – tai lėta ir nesaugu. Sukurkite CI/CD sistemoje, pushinkite į registry, production serveryje tik pullinkite ir paleiskite.
Saugumas ir best practices
Docker saugumas – plati tema, bet keletas esminių dalykų:
Nenaudokite latest tag production. Jis nuolat keičiasi, ir nežinosite, kokią versiją tiksliai paleidote. Naudokite konkretų tag'ą ar commit SHA.
Reguliariai atnaujinkite base images. Seni image gali turėti saugumo spragų. Įrankiai kaip Trivy ar Snyk gali skenuoti jūsų images ir perspėti apie pažeidžiamumus:
docker run aquasec/trivy image mano-projektas:latest
Nenaudokite root user konteineryje. Visada pridėkite:
RUN addgroup -g 1000 appuser && adduser -D -u 1000 -G appuser appuser
USER appuser
Nekopijuokite sensitive informacijos į image. .env failai, API raktai, slaptažodžiai – visa tai turėtų būti perduodama per environment variables arba secrets management sistemas (Docker secrets, Kubernetes secrets, AWS Secrets Manager).
Naudokite .dockerignore:
.git
.env
node_modules
vendor
*.log
.idea
.vscode
Ribokite konteinerių resursus docker-compose.yml:
services:
php:
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
memory: 512M
Ką daryti, kai viskas sudėta ir veikia
Dabar, kai suprantate Docker pagrindus, keletas patarimų kaip išlaikyti viską tvarkingoje būsenoje. Pirmiausia – reguliariai valykite nebenaudojamus resursus. Docker turi tendenciją kaupti senus images, sustabdytus konteinerius, neprisijungusias networks:
docker system prune -a --volumes
Ši komanda išvalys viską, kas nebenaudojama. Būkite atsargūs su --volumes – tai ištrins ir duomenų volumes, kurie nėra prijungti prie veikiančių konteinerių.
Dokumentuokite savo Docker setup. README.md turėtų turėti aiškias instrukcijas, kaip paleisti projektą. Naujas komandos narys neturėtų spėlioti, kokias komandas vykdyti. Pavyzdys:
# Projekto paleidimas
1. Klonuokite repo
2. Nukopijuokite .env.example į .env
3. Paleiskite: docker-compose up -d
4. Įvykdykite migrations: docker-compose exec php php artisan migrate
5. Atidarykite: http://localhost
Monitorinkite konteinerių būseną production. Įrankiai kaip Portainer, cAdvisor ar Prometheus + Grafana padės sekti resursų naudojimą, logus, konteinerių health status.
Svarbiausia – Docker nėra sidabrinė kulka. Tai įrankis, kuris išsprendžia konkrečias problemas: aplinkos konsistenciją, deployment paprastumą, resursų efektyvumą. Bet jis nepakeis blogo kodo, neišspręs architektūrinių problemų, nepadarys lėtos aplikacijos greitos.
Pradėkite mažai – vienas projektas, paprastas setup. Eksperimentuokite, darykite klaidas, mokykitės. Docker dokumentacija yra puiki, community aktyvus, Stack Overflow pilnas atsakymų. Po kelių savaičių pastebėsite, kad jau nebegalvojate apie „works on my machine" problemas, o fokusatės į tai, kas iš tiesų svarbu – kurti gerą produktą.

