Docker va Podman: amaliy ishlash
Linux containerlari va Namespaces va cgroups maqolalarida container kernel darajasida aslida nima ekani ko'rib chiqildi: OCI runtime spec’iga muvofiq yaratilgan, namespaces va cgroups bilan o'ralgan jarayon. Bu maqolada o'sha nazariya kundalik amaliyotga aylanadi: image qurish, container ishga tushirish, uning ma’lumotlarini saqlab qolish va bularning hammasini administrator amalda eng ko'p duch keladigan ikki vosita — Docker va uning daemonsiz muqobili Podman — bilan bajarish.
Nimalarni o'rganasiz
Bu maqola oxirida siz quyidagilarni qila olasiz:
docker run,docker psvadocker logsbilan container’ni ishga tushirish, tekshirish va to'xtatish;- minimal
Dockerfileyozish va undan image qurish; - nomlangan volume’lar va bind mount’lar bilan container ma’lumotlarini qayta ishga tushirishlardan keyin ham saqlab qolish;
- Docker’ning daemon modeli va Podman’ning daemonsiz, rootless modeli o'rtasidagi arxitekturaviy farqni tushuntirish;
- keng tarqalgan Docker buyruqlarini minimal o'zgarishlar bilan Podman’ga ko'chirish.
Docker: daemon’ga asoslangan model
Docker dockerd deb ataladigan uzoq ishlaydigan fon jarayoni sifatida ishlaydi. docker buyrug'ining o'zi faqat bu daemon bilan Unix socket orqali muloqot qiladigan klient. Docker yaratadigan har bir container’ni aslida terminalga yoziladigan docker binary’si emas, daemon ishga tushiradi, kuzatadi va o'chiradi.
Ubuntu’da Docker o'rnatish
Qulaylik skripti Docker’ning rasmiy repozitoriysini qo'shadi va engine’ni o'rnatadi. Foydalanuvchingizni docker guruhiga qo'shish keyinchalik docker ni sudo siz ishga tushirish imkonini beradi. Guruh a’zoligi kuchga kirishi uchun yangi login sessiyasi (yoki newgrp docker) kerak.
Warning
docker guruhiga a’zolik amalda hostda root huquqiga teng. Daemon root sifatida ishlaydi va -v /:/host bilan ishga tushirilgan container butun host fayl tizimini o'z ichiga mount qilishi, istalgan faylni o'qishi va unga qayta yozishi mumkin — hech qanday qo'shimcha tasdiqlash bosqichi yo'q. Bu guruhga faqat ishonchli foydalanuvchilarni qo'shing va unga oddiy, past imtiyozli guruh a’zoligi sifatida qaramang.
Birinchi container’ingiz
--rm container tugagach uni avtomatik o'chiradi. Bu bir martalik sinovlar uchun foydali, chunki usiz to'xtatilgan container’lar diskda cheksiz to'planib boraveradi.
-iinteraktiv kiritish uchun stdin’ni ochiq saqlaydi;-tpseudo-terminal ajratadi, shunda shell prompt’i va qatorni tahrirlash odatdagidek ishlaydi.
Container’larni boshqarish
docker ps faqat ishlab turgan container’larni ko'rsatadi, -a esa to'xtatilganlarini ham qo'shadi. Tugagan container yo'qolmaydi — u aniq o'chirilmaguncha diskda obyekt sifatida mavjud bo'lib qoladi.
stop ishlab turgan jarayonni tugatish uchun SIGTERM (kutish muddatidan keyin esa SIGKILL) yuboradi. rm esa endi to'xtatilgan container obyektining o'zini, jumladan uning yoziladigan qatlamini o'chiradi. Hali ishlab turgan container’da docker rm ni ishga tushirish -f qo'shilmasa, muvaffaqiyatsiz tugaydi. -f dan foydalanishdan oldin to'xtab o'ylashga arziydi — u jarayonni muloyim to'xtatmasdan majburan o'ldiradi.
Fonda ishga tushirish va portni ochish
-dcontainer’ni ajratilgan (detached) holda, fonda ishga tushiradi;-p 8080:80hostdagi8080portni container ichidagi80portga moslashtiradi — hostning8080portiga keladigan trafik container’ning80portiga uzatiladi.
docker logs container’ning stdout/stderr’ini o'qiydi — bu jarayon bevosita oldingi plandagi terminalda ishlaganida ko'radigan o'sha chiqish. Shuning uchun container’lashtirilgan ilova har doim ichki faylga emas, stdout/stderr’ga log yozishi kerak. Linux loglari maqolasi bu tamoyilni ilova tomonidan ko'rib chiqadi.
Ishlab turmaydigan container’ni debug qilish
Amalda eng ko'p uchraydigan Docker muammosi — va intervyularda eng ko'p beriladigan Docker troubleshooting savoli — ishga tushib, darhol to'xtab qoladigan container. Diagnostika taxmin emas, tizimli jarayon va u hech qachon docker run ni qayta ishga tushirib umid qilishdan boshlanmaydi:
Bo'sh docker ps container allaqachon yo'qligini anglatadi. docker ps -a uni exit status’i bilan ko'rsatadi va bu birinchi haqiqiy ishora:
Exited (1) — container asosiy jarayonining exit kodi. U Exit kodlar va xatolarni qayta ishlash maqolasidagi har qanday shell buyrug'ining exit kodi kabi o'qiladi: 0 jarayon oxirigacha ishlaganini va shunchaki boshqa qiladigan ishi qolmaganini anglatadi (tugagan batch vazifa — xato emas), har qanday noldan farqli qiymat jarayon muvaffaqiyatsiz tugaganini anglatadi. Xususan Exited (137) jarayon SIGKILL (128 + 9) olganini bildiradi. Bu ko'pincha kernel’ning OOM killer’i uni xotira cheklovidan oshgani uchun to'xtatganini anglatadi va Namespaces va cgroups maqolasidagi memory.events dagi oom_kill hisoblagichi bilan solishtirib tekshiriladi.
Exit kod jarayon muvaffaqiyatsiz tugaganini aytadi. docker logs esa allaqachon tugagan container’da ham nega ekanini aytadi:
Traceback (most recent call last):
File "/app/app.py", line 3, in <module>
ModuleNotFoundError: No module named 'requests'
Hali ham ishlab turgan, lekin noto'g'ri ishlayotgan container uchun docker exec uning mavjud namespace’lari ichida interaktiv shell ochadi. Bu kundalik debug qilishning kirish nuqtasi va Namespaces va cgroups maqolasida ostida ko'rsatilgan o'sha nsenter ga asoslangan mexanizm:
docker exec -it web sh
# now inside the container: check its filesystem, environment, and network view
env | grep -i path
cat /etc/resolv.conf
Intervyuga olib boriladigan intizom: exit kod uchun docker ps -a, muvaffaqiyatsizlik chiqishi uchun docker logs, ishlab turganining ichiga qarash uchun docker exec -it — aynan shu tartibda. Bu "container ishga tushmayapti" holatini yelka qisishdan uch buyruqli diagnostikaga aylantiradi.
Dockerfile bilan image qurish
FROMkeyingi har bir ko'rsatma ustiga quriladigan bazaviy image’ni belgilaydi;WORKDIRkeyingi ko'rsatmalar uchun image ichidagi ishchi katalogni belgilaydi va agar u mavjud bo'lmasa, uni yaratadi;COPYfaylni build kontekstidan (host’dan) image ichiga nusxalaydi;CMDshu image’dan container ishga tushganda bajariladigan standart buyruqni belgilaydi. Unidocker runpaytida almashtirish mumkin.
mkdir -p ~/lab/docker-demo && cd ~/lab/docker-demo
printf 'print("hello from a container")\n' > app.py
cat <<'EOF' > Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]
EOF
docker build -t demo-app:1.0 .
docker run --rm demo-app:1.0
docker build -t demo-app:1.0 . oxiridagi . — build konteksti: mazmuni build jarayoniga yuboriladigan va COPY/ADD ko'rsatmalari uchun mavjud bo'ladigan katalog. Katta yoki keraksiz build konteksti (tasodifan kiritilgan .git katalogi, node_modules yoki log fayllar) har bir build’ni sezilarli darajada sekinlashtiradi. Xuddi .gitignore kabi ishlaydigan .dockerignore fayli bunday yo'llarni build boshlanishidan oldin kontekstdan chiqarib tashlaydi.
Volume’lar: ma’lumotlarni container umridan tashqari saqlash
Container’ning yoziladigan qatlami container o'chirilgan zahoti yo'qoladi. Ilova container fayl tizimi ichida yozgan har qanday narsa, agar u volume da saqlanmasa, u bilan birga yo'qoladi.
docker volume create demo-data
docker run --rm -v demo-data:/data alpine sh -c 'echo "persisted" > /data/note.txt'
docker run --rm -v demo-data:/data alpine cat /data/note.txt
Yuqoridagi har bir docker run — butunlay alohida, qisqa umrli container. Ikkinchisi birinchisi haqida hech narsa bilmagan holda ishga tushadi. Shunga qaramay, birinchisi yozgan faylni ikkinchisi o'qiy oladi, chunki ikkalasi ham bir xil demo-data volume’ini mount qilgan. Docker uni har qanday alohida container’ning hayot siklidan mustaqil ravishda saqlaydi va boshqaradi.
Bind mount Docker boshqaradigan volume o'rniga to'g'ridan-to'g'ri host katalogiga ishora qilib, xuddi shunday saqlashga erishadi:
mkdir -p ~/lab/docker-demo/data
docker run --rm -v "$HOME/lab/docker-demo/data:/data" alpine \
sh -c 'echo "written from inside the container" > /data/note.txt'
cat ~/lab/docker-demo/data/note.txt
Production’da ilova ma’lumotlari uchun nomlangan volume’lar yaxshiroq standart tanlov — Docker ularning joylashuvi va hayot siklini boshqaradi va ular turli hostlarda bir xil ishlaydi. Bind mount’lar esa ishlab chiqishda eng foydali. U yerda maqsad aynan hostda faylni tahrirlab, o'zgarishni ishlab turgan container ichida darhol ko'rish (masalan, dev container’ga mount qilingan manba kodi).
Podman: daemonsiz muqobil
Podman Docker bilan deyarli bir xil buyruqlar to'plamini amalga oshiradi, lekin tubdan farq qiladigan arxitektura bilan: unda uzoq ishlaydigan daemon yo'q. Har bir podman chaqiruvi — o'zining mustaqil jarayoni. Eng muhimi, Podman Namespaces va cgroups maqolasida ko'rib chiqilgan aynan o'sha user namespace UID moslashtirishidan foydalanib, container’larni standart holatda rootless ishga tushira oladi.
docker: docker (client) ──socket──> dockerd (persistent daemon, root)
podman: podman (standalone process, rootless by default) ──directly──> container
O'rnatish va asosiy foydalanish
Ko'pchilik Docker buyruqlari docker o'rniga podman qo'yilsa, o'zgarishsiz ishlaydi:
podman run -d --name web -p 8080:80 nginx:alpine
podman ps
podman logs web
podman stop web && podman rm web
Docker va Podman
| Docker | Podman | |
|---|---|---|
| Fon daemon’i | Kerak (dockerd) |
Kerak emas |
| Standart holatda rootless | Yo'q — rootless rejimni alohida sozlash talab qilinadi | Ha |
| Buyruq sintaksisi | docker <command> |
Deyarli bir xil, podman <command> |
| systemd bilan integratsiya | Qo'shimcha vositalar talab qilinadi | Tabiiy, podman generate systemd orqali |
| Compose fayllarini qo'llab-quvvatlash | Tabiiy (docker compose) |
podman-compose yoki Kubernetes YAML hosil qilish orqali |
Standart rootless holat kichik implementatsiya tafsiloti emas, balki muhim xavfsizlik farqi. Podman container’larni yaratish va boshqarish uchun imtiyozli daemon’ga muhtoj bo'lmagani sababli, rootless Podman container’i ichidagi buzib kirilgan jarayon root’ga emas, imtiyozsiz host foydalanuvchisiga borib taqaladi. Bu aynan Namespaces va cgroups maqolasida qo'lda ko'rsatilgan user namespace himoyasi. Docker daemon’i esa, aksincha, standart holatda root sifatida ishlaydi. docker guruhiga a’zolik root huquqiga teng deb hisoblanishining sababi ham aynan shu.
Intervyuda: Podman’ga nega daemon kerak emas?
Tarixan Docker daemon’i bugungi kunda root talab qilmaydigan muammolarni hal qilish uchun mavjud bo'lgan: container hayot siklini markazlashgan holda boshqarish, Docker Compose kabi vositalar uchun barqaror API berish va (dastlab) kernel’da user namespace qo'llab-quvvatlashi yo'qligini chetlab o'tish. Zamonaviy kernel’lar user namespace’larni yetarlicha yaxshi qo'llab-quvvatlaydi, shuning uchun Podman container’ni bevosita, podman jarayonining o'z bola jarayoni sifatida yarata oladi va uni nazorat qilish uchun alohida imtiyozli jarayon kerak bo'lmaydi. Buning evaziga bitta podman chaqiruvi ishga tushirgan container daemon nazorat qilganidek avtomatik nazorat qilinmaydi. podman generate systemd aynan shu bo'shliqni yopadi: u container nazoratini container’ga xos daemon o'rniga systemd ga topshiradi.
Keng tarqalgan xatolar
- Container ma’lumotlari standart holatda saqlanib qoladi deb o'ylash. Volume yoki bind mount bo'lmasa, container’ning yoziladigan qatlami ichida yozilgan hamma narsa
docker rm(yokipodman rm) uni o'chirgan zahoti yo'qoladi. - Oqibatini o'ylamasdan foydalanuvchini
dockerguruhiga qo'shish. Yuqorida ko'rib chiqilganidek, bu shunchaki "container’larni ishga tushirish imkoniyati" emas, hostga amalda root huquqini beradi. Unga cheklovsizsudoberish bilan bir xil munosabatda bo'ling. DockerfiledagiEXPOSEni portni haqiqatan ochish (publish) bilan chalkashtirish.EXPOSEfaqat hujjatlashtirish uchun. Host portini container’ga haqiqatan uzatadigan flag —docker rundagi-p.- Ishlatilmayotgan image’lar va container’larni hech qachon tozalamaslik. Vaqt o'tishi bilan eski image’lar to'planib, disk joyini jimgina egallaydi:
docker system df image’lar, container’lar va volume’lar qancha joy egallayotganini ko'rsatadi. docker image prune esa osilib qolgan (dangling — teg'siz, hech kim murojaat qilmaydigan) image’larni o'chiradi. Bu muntazam image quradigan har qanday hostda vaqti-vaqti bilan bajarishga arziydigan amal.
Mashqlar
nginx:alpineni-d -p 8080:80bilan ishga tushiring,curl localhost:8080bilan u javob berayotganini tasdiqlang, uning chiqishinidocker logsbilan tekshiring, keyindocker stopvadocker rmbilan tozalang.- Yuqorida ko'rsatilgan
Dockerfileshabloniga amal qilib, o'zingizning kichik skriptingizni — Python yoki Bash — container’ga joylang va undan image quring. - Nomlangan volume yarating, unga bitta
docker runchaqiruvidan fayl yozing va u ikkinchi, mustaqildocker runchaqiruvi tomonidan o'qilganda saqlanib qolishini tasdiqlang. - Bir xil buyruqlar ketma-ketligini avval
docker, keyinpodmanbilan ishga tushiring vadocker pshamdapodman pschiqishlarini taqqoslang. Kuzatgan farqlaringizni yozib qo'ying.
Xulosa
Docker’ning daemon’ga asoslangan modelida docker buyrug'i imtiyozli, doimo ishlab turgan fon jarayoni bilan muloqot qiladigan klient. Podman’ning daemonsiz, odatda rootless modeli esa har bir container’ni standart holatda imtiyozsiz host foydalanuvchisiga moslashtirilgan o'z jarayonlar daraxti sifatida ishga tushiradi. Ikkalasi ham deyarli bir xil buyruq sintaksisini taqdim etadi va ostida qaysi engine turganidan qat’i nazar, docker run, docker build hamda volume’lar/bind mount’lar asosiy kundalik vositalar bo'lib qoladi. Keyingi maqola, Linux CI/CD’da, xuddi shu image’lar qo'lda ishga tushirilish o'rniga pipeline ichida qanday avtomatik qurilishi va sinovdan o'tkazilishini ko'rsatadi.