۱۵. Docker و کانتینرها¶
کانتینر «ماشین مجازی سبک» نیست؛ فرایندی روی همان هسته است. دو خطر اصلی: داده (حذف volume) و امنیت (دسترسی root به میزبان، دور زدن فایروال). این فصل هر دو را پوشش میدهد.
راهنمای سریع¶
| فرمان | کاربرد | ریسک |
|---|---|---|
docker ps -a / docker logs / docker stats --no-stream / docker inspect |
مشاهده | بدون تغییر |
docker system df / docker compose config / compose ps |
ظرفیت و اعتبارسنجی | بدون تغییر |
docker exec -it |
ورود به کانتینر | تغییر (ممکن است داخل داده بنویسید) |
docker compose up -d / restart / pull |
استقرار | تغییر |
docker rm / compose down |
حذف کانتینر/شبکه | تغییر (داده: volume) |
docker compose down -v / volume rm / system prune -a --volumes |
حذف داده | مخرب |
وضعیت و منابع¶
docker version
docker info
docker ps
docker ps -a
docker images
docker stats --no-stream # بدون --no-stream، تا Ctrl+C ادامه میدهد
docker system df
docker system df -v | head -40
لاگ و ورود به کانتینر¶
docker logs --tail 200 CONTAINER
docker logs --since 10m -t CONTAINER
docker logs -f CONTAINER
docker exec -it CONTAINER sh
docker inspect CONTAINER
docker top CONTAINER
docker cp CONTAINER:/var/log/app.log ./app.log
docker events --since 10m
در imageهای مینیمال ممکن است bash وجود نداشته باشد؛ sh را امتحان کنید. تغییری که داخل کانتینر (با exec) میدهید با ساخت دوبارهٔ کانتینر از بین میرود؛ پیکربندی را در image/compose/volume بگذارید.
چرخه عمر¶
docker start CONTAINER
docker stop CONTAINER
docker restart CONTAINER
docker rm CONTAINER
docker pull nginx:stable
docker run --rm -it alpine:3.20 sh # کانتینر یکبارمصرف (بعد از خروج حذف میشود)
docker rm کانتینر را حذف میکند، نه لزوماً volumeهای مرتبط (volumeهای ناشناس با -v حذف میشوند: docker rm -v). قبل از حذف، وضعیت دادهها و سیاست پشتیبان را بررسی کنید.
Docker Compose¶
docker compose ps
docker compose logs -f --tail=200
docker compose config # اعتبارسنجی و نمایش پیکربندی نهایی (متغیرها جایگزین شده)
docker compose pull
docker compose up -d
docker compose up -d --build --remove-orphans
docker compose up -d --force-recreate SERVICE
docker compose exec SERVICE sh
docker compose restart SERVICE
docker compose down
docker compose down کانتینرها و شبکههای پروژه را حذف میکند؛ volumeهای نامگذاریشده معمولاً باقی میمانند مگر -v اضافه شود. down -v میتواند داده را حذف کند. compose config ممکن است رمزهای .env را نشان دهد؛ خروجی را در تیکت نچسبانید.
نمونهٔ سرویس با سیاستهای عملیاتی (نمونه):
services:
app:
image: registry.example.com/team/app:1.4.2 # tag ثابت، نه latest
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080" # فقط لوکال؛ proxy جلوی آن است
env_file: ./app.env # مجوز 600
read_only: true
tmpfs: [/tmp]
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
mem_limit: 512m
cpus: 1.0
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"]
interval: 30s
timeout: 3s
retries: 3
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }
volumes:
- app-data:/var/lib/app
volumes:
app-data:
عیبیابی شبکه و volume¶
docker network ls
docker network inspect NETWORK
docker volume ls
docker volume inspect VOLUME
docker inspect -f '{{json .NetworkSettings.Networks}}' CONTAINER
docker exec CONTAINER getent hosts other-service # نام سرویس در شبکهٔ compose
کانتینرها در یک شبکهٔ تعریفشده (user-defined / compose) با نام سرویس هم را پیدا میکنند؛ localhost داخل کانتینر خود همان کانتینر است، نه میزبان یا کانتینر دیگر.
دام امنیتی Docker: فایروال UFW را دور میزند¶
Docker برای پورتهای منتشرشده (-p 8080:80) قوانین iptables را قبل از زنجیرهٔ UFW درج میکند. نتیجه: حتی اگر ufw deny 8080 بزنید و ufw status ببندد نشان دهد، پورت از اینترنت باز است.
sudo ss -lntp | grep docker-proxy # چه پورتهایی منتشر شده؟
sudo iptables -t nat -L DOCKER -n | head
راههای کاهش (به ترتیب ترجیح):
- فقط به لوکال منتشر کنید و reverse proxy جلو بگذارید:
-p 127.0.0.1:8080:80. - قوانین خود را در زنجیرهٔ
DOCKER-USERبگذارید (Docker آن را دست نمیزند)، مثلاً فقط یک شبکه:
- هرگز
"iptables": falseرا بدون درک کامل (NAT کانتینرها از کار میافتد) فعال نکنید. - بعد از هر تغییر، از بیرون تست کنید:
nc -vz SERVER_PUBLIC_IP 8080.
لاگها و پر شدن دیسک¶
پیشفرض json-file بدون محدودیت است؛ یک کانتینر پرگو دیسک را پر میکند.
# بزرگترین لاگها
sudo du -h /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -h | tail -5
# اضطراری (فقط برای آزادکردن فضا؛ لاگ از بین میرود)
sudo truncate -s 0 /var/lib/docker/containers/ID/ID-json.log
// /etc/docker/daemon.json — برای کانتینرهای جدید (بعد از restart داکر)
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
برای کانتینرهای موجود باید دوباره ساخته شوند (up -d --force-recreate).
پاکسازی با احتیاط¶
از docker system prune -a --volumes بدون بررسی دقیق استفاده نکنید؛ میتواند imageها، منابع بدون استفاده و دادههای volume (کانتینر متوقفشدهٔ فعلی = volume «بدون استفاده») را حذف کند. ترتیب امن: docker system df ← docker image prune (فقط dangling) ← بررسی ← در صورت نیاز docker image prune -a --filter "until=720h".
بکاپ volume¶
docker run --rm -v VOLUME:/data:ro -v "$PWD":/backup alpine \
tar czf /backup/VOLUME-$(date +%F).tgz -C /data .
برای دیتابیس، بهجای کپی volume از dump خود دیتابیس استفاده کنید (پشتیبانگیری).
امنیت¶
- گروه
docker= root. هر عضو میتواند باdocker run -v /:/hostکل میزبان را تغییر دهد (کاربران). /var/run/docker.sockرا داخل کانتینر mount نکنید (معادل root روی میزبان).- اجرا با کاربر غیر root (
USERدر Dockerfile)،cap_drop: [ALL]،no-new-privileges،read_only. - tag ثابت یا digest (
image@sha256:...)؛ ازlatestدر production اجتناب کنید. - image ها را اسکن کنید (
trivy image IMG/docker scout cves IMG) و منبع آنها را بشناسید. - secretها را در Git، Dockerfile یا
docker inspect(متغیرهای محیطی) نگذارید؛ از secret manager یا فایل با مجوز محدود استفاده کنید. - روی RHEL،
podmanجایگزین بدون daemon و rootless است (podman ps،podman compose).
خطاهای رایج¶
| نشانه | علت احتمالی | روش بررسی |
|---|---|---|
Exit code 137 |
OOM یا SIGKILL |
docker inspect -f '{{.State.OOMKilled}}' C ، dmesg -T \| grep -i kill |
Exit code 139 |
segfault | لاگ، نسخهٔ image |
Exit code 143 |
SIGTERM (توقف عادی) |
معمولاً بیخطر |
Exit code 125/126/127 |
خطای docker / مجوز اجرا / فرمان پیدا نشد | docker logs ، ENTRYPOINT |
port is already allocated |
پورت میزبان اشغال | sudo ss -lntp \| grep :PORT |
| کانتینر مدام restart میشود | کرش در شروع / healthcheck | docker logs --tail 100 ، inspect |
permission denied روی volume |
UID داخل کانتینر ≠ مالک پوشه | ls -ln ، --user ، chown |
دیسک /var/lib/docker پر |
لاگ/image/volume | docker system df -v ، سقف لاگ |
بعد از down داده رفت |
-v یا volume ناشناس |
بکاپ؛ volume نامگذاریشده |
| کانتینر به سرویس دیگر وصل نمیشود | شبکهٔ متفاوت / localhost |
docker network inspect ، نام سرویس |
نکات عملیاتی¶
- نسخه و tag تصویر را ثابت و قابل ردیابی انتخاب کنید؛ در محیط تولیدی از
latestاجتناب کنید. - secretها را در Git یا Dockerfile قرار ندهید؛ از secret manager یا فایل محیطی با مجوز محدود استفاده کنید.
- healthcheck، محدودیت منابع، سیاست restart، سقف لاگ و پشتیبانگیری از volumeها را متناسب با سرویس تنظیم کنید.
- ارتقای image:
pull←up -d← تأیید ← نگهداشتن tag قبلی برای rollback.
خلاصهٔ اجرایی¶
- پورتها را روی
127.0.0.1منتشر کنید؛ UFW کانتینر را نمیبندد. - سقف لاگ، tag ثابت، بدون root، بدون docker.sock.
down -vوprune --volumes= حذف داده؛ اول بکاپ.- گروه docker را مثل root مدیریت کنید.