Tarkibga o'tish

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 ps va docker logs bilan container’ni ishga tushirish, tekshirish va to'xtatish;
  • minimal Dockerfile yozish 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.

docker (client) ──unix socket──> dockerd (daemon, runs as root) ──> containers

Ubuntu’da Docker o'rnatish

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"

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

docker run --rm hello-world

--rm container tugagach uni avtomatik o'chiradi. Bu bir martalik sinovlar uchun foydali, chunki usiz to'xtatilgan container’lar diskda cheksiz to'planib boraveradi.

docker run -it --rm ubuntu:24.04 bash
  • -i interaktiv kiritish uchun stdin’ni ochiq saqlaydi;
  • -t pseudo-terminal ajratadi, shunda shell prompt’i va qatorni tahrirlash odatdagidek ishlaydi.

Container’larni boshqarish

docker ps
docker ps -a

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.

docker stop web
docker rm web

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

docker run -d --name web -p 8080:80 nginx:alpine
curl -s http://localhost:8080 | head -n 5
  • -d container’ni ajratilgan (detached) holda, fonda ishga tushiradi;
  • -p 8080:80 hostdagi 8080 portni container ichidagi 80 portga moslashtiradi — hostning 8080 portiga keladigan trafik container’ning 80 portiga uzatiladi.
docker logs web
docker stop web && docker rm web

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:

docker run -d --name api myapp:latest
docker ps
CONTAINER ID   IMAGE          STATUS   NAMES

Bo'sh docker ps container allaqachon yo'qligini anglatadi. docker ps -a uni exit status’i bilan ko'rsatadi va bu birinchi haqiqiy ishora:

docker ps -a
CONTAINER ID   IMAGE          STATUS                       NAMES
b2f19c4d7e88   myapp:latest   Exited (1) 3 seconds ago     api

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:

docker logs api
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

FROM python:3.12-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]
  • FROM keyingi har bir ko'rsatma ustiga quriladigan bazaviy image’ni belgilaydi;
  • WORKDIR keyingi ko'rsatmalar uchun image ichidagi ishchi katalogni belgilaydi va agar u mavjud bo'lmasa, uni yaratadi;
  • COPY faylni build kontekstidan (host’dan) image ichiga nusxalaydi;
  • CMD shu image’dan container ishga tushganda bajariladigan standart buyruqni belgilaydi. Uni docker run paytida 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
hello from a container

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
persisted

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

sudo apt install podman
podman run --rm hello-world

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
podman build -t demo-app:1.0 .
podman run --rm demo-app:1.0

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 (yoki podman rm) uni o'chirgan zahoti yo'qoladi.
  • Oqibatini o'ylamasdan foydalanuvchini docker guruhiga qo'shish. Yuqorida ko'rib chiqilganidek, bu shunchaki "container’larni ishga tushirish imkoniyati" emas, hostga amalda root huquqini beradi. Unga cheklovsiz sudo berish bilan bir xil munosabatda bo'ling.
  • Dockerfile dagi EXPOSE ni portni haqiqatan ochish (publish) bilan chalkashtirish. EXPOSE faqat hujjatlashtirish uchun. Host portini container’ga haqiqatan uzatadigan flag — docker run dagi -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
docker image prune

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

  1. nginx:alpine ni -d -p 8080:80 bilan ishga tushiring, curl localhost:8080 bilan u javob berayotganini tasdiqlang, uning chiqishini docker logs bilan tekshiring, keyin docker stop va docker rm bilan tozalang.
  2. Yuqorida ko'rsatilgan Dockerfile shabloniga amal qilib, o'zingizning kichik skriptingizni — Python yoki Bash — container’ga joylang va undan image quring.
  3. Nomlangan volume yarating, unga bitta docker run chaqiruvidan fayl yozing va u ikkinchi, mustaqil docker run chaqiruvi tomonidan o'qilganda saqlanib qolishini tasdiqlang.
  4. Bir xil buyruqlar ketma-ketligini avval docker, keyin podman bilan ishga tushiring va docker ps hamda podman ps chiqishlarini 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.

Manbalar