رفتن به محتوا
LoopX

image و container

توی این درس یاد می‌گیری image دقیقاً چیست (قالبی ثابت از چند لایه)، اسم کامل آن چطور خوانده می‌شود (registry/repo:tag)، فرق tag و digest چیست، و از یک image چطور چند کانتینر مستقل می‌سازی. بعد چرخه‌ی عمر کانتینر را می‌بینی: ساخته شدن، اجرا، توقف موقت، متوقف شدن و حذف. دستورهای محوری: docker pull، docker images و docker ps -a.

تشبیه: قالب شیرینی و شیرینی‌ها

Section titled “تشبیه: قالب شیرینی و شیرینی‌ها”

image مثل قالب شیرینی‌پزی است: ثابت است، تغییر نمی‌کند و می‌توانی هر چند بار که بخواهی از رویش شیرینی بسازی. کانتینر هر شیرینی‌ای است که از آن قالب درآمده: یکی داغ و تازه سر میز (در حال اجرا)، یکی خوردم و فقط بشقابش مانده (متوقف‌شده)، یکی دور ریخته شده (حذف‌شده). هر کدام مستقل‌اند: اگر روی یکی خامه بریزی، بقیه خامه ندارند.

یک image پشته‌ای از لایه‌های فقط‌خواندنی است. هر کانتینر یک لایه‌ی قابل‌نوشتن مخصوص خود را بالای آن می‌گیرد.

اسم کامل یک image این‌طور خوانده می‌شود:

بخش مثال معنی
registry docker.io انباری که image آنجاست (پیش‌فرض Docker Hub)
repository library/nginx نام کاربری یا سازمان / نام image (library یعنی image رسمی)
tag alpine برچسب نسخه (پیش‌فرض: latest)
digest sha256:... هویت ثابت و تغییرناپذیر محتوای image

پس nginx:alpine یعنی docker.io/library/nginx:alpine.

مثال ۱: گرفتن image و دیدنش (docker pull، docker images)

Section titled “مثال ۱: گرفتن image و دیدنش (docker pull، docker images)”
Terminal window
docker pull nginx:alpine
docker images nginx
خروجی
alpine: Pulling from library/nginx
Digest: sha256:df221db836e1754089190208cee7eeda94f233197056426eda74a43ab1abeac2
Status: Image is up to date for nginx:alpine
docker.io/library/nginx:alpine
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
nginx:1.28.0-alpine 30f1c0d78e0a 76.9MB 22.7MB U
nginx:alpine df221db836e1 93.9MB 27.1MB
nginx:latest abe47724e466 260MB 64.6MB

دستور docker pull image را (در لایه‌ها) از registry می‌آورد. هر خط با یک شناسه‌ی کوتاه، یک لایه است؛ لایه‌هایی که از قبل روی سیستم بوده‌اند دوباره دانلود نمی‌شوند (لایه‌ها بین image ها مشترک‌اند). docker images همه‌ی image های محلی را فهرست می‌کند؛ فیلتر nginx فقط یکی از آن‌ها را نشان می‌دهد.

مثال ۲: یک image، چند اسم (tag)

Section titled “مثال ۲: یک image، چند اسم (tag)”
Terminal window
docker tag alpine:latest lx-alpine:v1
docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}' | grep -E "REPOSITORY|^alpine|^lx-alpine"
خروجی
REPOSITORY TAG IMAGE ID
alpine latest 294b683cb724
lx-alpine v1 294b683cb724

دستور docker tag فقط یک برچسب تازه روی همان image می‌گذارد؛ هیچ داده‌ای کپی نمی‌شود. دقت کن شناسه‌ی (ID) هر دو ردیف یکی است. tag شبیه اسم میانبر است، نه یک نسخه‌ی جداگانه.

Terminal window
docker image inspect alpine:latest --format 'Tag: alpine:latest{{"\n"}}Digest: {{index .RepoDigests 0}}'
خروجی
Tag: alpine:latest
Digest: alpine@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6

tag قابل‌جابه‌جایی است: سازنده‌ی image می‌تواند فردا alpine:latest را به محتوای جدیدی منتقل کند. digest (sha256:...) اثر انگشت محتواست و فقط به همان محتوا اشاره می‌کند. اگر تکرارپذیری مطلق می‌خواهی، alpine@sha256:... را بنویس.

⚡ بررسی سریع

کدام یک تضمین می‌کند همیشه دقیقاً همان محتوا را می‌گیری؟

مثال ۴: از یک image سه کانتینر (docker ps)

Section titled “مثال ۴: از یک image سه کانتینر (docker ps)”
Terminal window
for i in 1 2 3; do docker run -d --name lx-img-$i -p 830$i:80 nginx:alpine >/dev/null; done
sleep 2
docker ps --filter name=lx-img --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}' | sed 's/, \[::\][^ ]*//'
for i in 1 2 3; do curl -s -o /dev/null -w "پورت 830$i: HTTP %{http_code}\n" localhost:830$i; done
خروجی
NAMES IMAGE PORTS
lx-img-3 nginx:alpine 0.0.0.0:8303->80/tcp
lx-img-2 nginx:alpine 0.0.0.0:8302->80/tcp
lx-img-1 nginx:alpine 0.0.0.0:8301->80/tcp
پورت 8301: HTTP 200
پورت 8302: HTTP 200
پورت 8303: HTTP 200

سه کانتینر مستقل از یک image، روی سه پورت. هر کدام نام، شناسه، شبکه و فایل‌های مخصوص خودش را دارد. حالا ثابت کنیم مستقل‌اند: داخل یکی فایل می‌سازیم و در بقیه نیست:

Terminal window
docker exec lx-img-1 sh -c 'echo hello > /tmp/mine.txt'
for i in 1 2 3; do echo "lx-img-$i: $(docker exec lx-img-$i sh -c 'ls /tmp/mine.txt 2>&1')"; done
خروجی
lx-img-1: /tmp/mine.txt
lx-img-2: ls: /tmp/mine.txt: No such file or directory
lx-img-3: ls: /tmp/mine.txt: No such file or directory

مثال ۵: چرخه‌ی عمر کانتینر

Section titled “مثال ۵: چرخه‌ی عمر کانتینر”

حالا یک کانتینر را قدم‌به‌قدم از ساخته شدن تا حذف ببینیم. وضعیت را در هر گام با docker ps -a می‌خوانیم:

Terminal window
st() { docker ps -a --filter name=lx-life --format '{{.Names}}: {{.Status}}'; }
docker create --name lx-life alpine sleep 300 >/dev/null; echo "1) create"; st
docker start lx-life >/dev/null; echo "2) start"; st
docker pause lx-life >/dev/null; echo "3) pause"; st
docker unpause lx-life >/dev/null; echo "4) unpause"; st
docker stop -t 1 lx-life >/dev/null; echo "5) stop"; st
docker rm lx-life >/dev/null; echo "6) rm"; st; echo "(دیگر چیزی نیست)"
خروجی
1) create
lx-life: Created
2) start
lx-life: Up Less than a second
3) pause
lx-life: Up Less than a second (Paused)
4) unpause
lx-life: Up Less than a second
5) stop
lx-life: Exited (137) Less than a second ago
6) rm
(دیگر چیزی نیست)
وضعیت‌های اصلی کانتینر: Created ← Running ← Paused/Exited ← Removed.

دقت کن مرحله‌ی ۵ با Exited (137) تمام شد، نه 0: کد ۱۳۷ یعنی ۱۲۸ + ۹، یعنی پروسه با سیگنال شماره‌ی ۹ (SIGKILL) بسته شد. docker stop اول سیگنال TERM می‌فرستد و بعد از مهلت (اینجا فقط یک ثانیه با -t 1) اگر پروسه تمام نشد، با KILL می‌بندد؛ sleep به‌عنوان پروسه‌ی اصلی TERM را نادیده می‌گیرد. (درباره‌ی سیگنال‌ها در درس CMD و ENTRYPOINT بیشتر می‌گوییم.)

docker run در واقع docker create + docker start است. docker pause پروسه را فریز می‌کند (بدون بستنش) و docker stop به آن اجازه می‌دهد تمیز تمام شود.

یک image در واقع چند چیز است: یک manifest (فهرست لایه‌ها)، یک config (دستور اجرا، متغیرهای محیطی، …)، و خود لایه‌ها که هر کدام یک فایل tar فشرده است. هر کدام با هش sha256 محتوایش شناسایی می‌شود (content-addressable): اگر دو image لایه‌ی یکسان داشته باشند، فقط یک نسخه روی دیسک ذخیره می‌شود. همین باعث می‌شود pull دومِ چیزی که لایه‌های مشترک دارد سریع باشد.

Terminal window
docker image inspect nginx:alpine --format 'تعداد لایه‌ها: {{len .RootFS.Layers}}{{"\n"}}معماری: {{.Os}}/{{.Architecture}}{{"\n"}}دستور پیش‌فرض: {{.Config.Cmd}}'
خروجی
تعداد لایه‌ها: 8
معماری: linux/arm64
دستور پیش‌فرض: [nginx -g daemon off;]

وقتی کانتینر می‌سازی، داکر هیچ لایه‌ای را کپی نمی‌کند؛ فقط یک لایه‌ی نازک قابل‌نوشتن روی آن‌ها می‌گذارد (copy-on-write). به همین دلیل ساختن ۱۰۰ کانتینر از یک image تقریباً فضای اضافه نمی‌گیرد (با docker ps -s اندازه‌ی لایه‌ی نوشتنی هر کانتینر را می‌بینی).

image کانتینر
قابل‌تغییر خیر (فقط‌خواندنی) بله (لایه‌ی قابل‌نوشتن)
تعداد یک نسخه هر چند تا از یک image
فهرست docker images docker ps / docker ps -a
حذف docker rmi docker rm
وضعیت یعنی دستور رسیدن
Created ساخته شده، شروع نشده docker create
Up در حال اجرا docker start / docker run
Up (Paused) فریز شده docker pause
Exited (N) تمام شده با کد N (0 یعنی موفق) پایان پروسه یا docker stop
Removed حذف شده docker rm

۱) حذف کانتینر در حال اجرا

Section titled “۱) حذف کانتینر در حال اجرا”
Terminal window
docker run -d --name lx-life alpine sleep 300 >/dev/null
docker rm lx-life
docker rm -f lx-life
خروجی
Error response from daemon: cannot remove container "lx-life": container is running: stop the container before removing or force remove
lx-life

بدون -f (force)، داکر نمی‌گذارد کانتینر در حال اجرا را حذف کنی. راه‌حل: اول docker stop lx-life (یا docker rm -f اگر مطمئنی).

Terminal window
docker pull alpine:no-such-tag-xyz
خروجی
Error response from daemon: failed to resolve reference "docker.io/library/alpine:no-such-tag-xyz": docker.io/library/alpine:no-such-tag-xyz: not found

tag وجود ندارد (manifest unknown). راه‌حل: tag های موجود را در صفحه‌ی image در Docker Hub ببین؛ غلط املایی یا نسخه‌ی حذف‌شده معمولاً علت است.

۳) «latest همیشه آخرین نسخه است»

Section titled “۳) «latest همیشه آخرین نسخه است»”

latest فقط یک tag معمولی است که سازنده بهش اشاره می‌دهد؛ ممکن است قدیمی باشد یا با نسخه‌ی پایدار فرق کند. راه‌حل: در پروژه‌های جدی نسخه‌ی مشخص (مثل nginx:1.28-alpine) یا digest را بنویس.

۴) تعجب از «ایمیج حذف نمی‌شود»

Section titled “۴) تعجب از «ایمیج حذف نمی‌شود»”

تا کانتینری (حتی متوقف‌شده) از یک image ساخته شده باشد، docker rmi آن را حذف نمی‌کند. اول کانتینرها را docker rm کن.

✎ تمرینآسان

image alpine را بگیر و ثابت کن روی سیستمت هست (فقط ردیف alpine را از docker images بیاور).

دیدن جواب
Terminal window
docker pull alpine
docker images alpine
خروجی
Using default tag: latest
latest: Pulling from library/alpine
Digest: sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
Status: Image is up to date for alpine:latest
docker.io/library/alpine:latest
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
alpine:latest 294b683cb724 13.6MB 4.28MB
✎ تمرینمتوسط

از image alpine دو کانتینر lx-t1 و lx-t2 بساز که هر دو sleep 120 اجرا کنند. فهرستشان کن، بعد هر دو را (به‌صورت force) حذف کن و ثابت کن حذف شدند.

دیدن جواب
Terminal window
docker run -d --name lx-t1 alpine sleep 120 >/dev/null
docker run -d --name lx-t2 alpine sleep 120 >/dev/null
docker ps --filter name=lx-t --format '{{.Names}}: {{.Status}}'
docker rm -f lx-t1 lx-t2
docker ps -a --filter name=lx-t -q | wc -l | tr -d ' '
خروجی
lx-t2: Up Less than a second
lx-t1: Up 1 second
lx-t1
lx-t2
0
✎ تمرینسخت

ثابت کن tag و digest دو چیز متفاوت‌اند: یک tag جدید روی alpine بگذار، نشان بده شناسه (ID) یکی است. بعد ثابت کن حذف یک tag (docker rmi tag) خود image را پاک نمی‌کند تا وقتی tag دیگری به آن اشاره می‌کند.

دیدن جواب
Terminal window
docker tag alpine:latest lx-alpine:v2
docker images --format '{{.Repository}}:{{.Tag}} {{.ID}}' | grep -E "^(alpine:latest|lx-alpine:v2)"
docker rmi lx-alpine:v2
docker images --format '{{.Repository}}:{{.Tag}}' | grep -c "^alpine:latest"
خروجی
lx-alpine:v2 294b683cb724
alpine:latest 294b683cb724
Untagged: lx-alpine:v2
1

با docker rmi lx-alpine:v2 فقط برچسب حذف شد («Untagged»)؛ image با اسم alpine:latest سر جایش است (شمارنده 1).

؟ آزمونک
  1. docker run در واقع ترکیب کدام دو دستور است؟

  2. تفاوت tag و digest؟

  3. docker tag alpine:latest lx-alpine:v1 چه می‌کند؟

  4. چرا docker rm روی کانتینر در حال اجرا خطا می‌دهد؟

  5. ساختن ۱۰۰ کانتینر از یک image چرا فضای زیادی نمی‌گیرد؟

  • image قالب فقط‌خواندنی از چند لایه است؛ کانتینر نمونه‌ی اجراشده با یک لایه‌ی نازک قابل‌نوشتن.
  • اسم کامل: registry/repo:tag؛ tag برچسب قابل‌جابه‌جایی است و digest اثر انگشت ثابت.
  • docker pull image را (لایه‌به‌لایه) می‌آورد؛ docker images فهرستشان می‌کند؛ docker tag فقط اسم تازه می‌دهد.
  • از یک image هر چند کانتینر مستقل می‌شود ساخت؛ docker ps -a همه‌شان را (حتی متوقف‌شده) نشان می‌دهد.
  • چرخه‌ی عمر: created ← running ← paused/exited ← removed؛ docker run = create + start.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
docker pull nginx:alpineگرفتن image با tag مشخص
docker imagesفهرست image های محلی
docker tag SRC NEWاسم (tag) تازه برای یک image
docker image inspect IMGجزئیات image (لایه‌ها، digest، دستور)
docker create --name N IMGساخت کانتینر بدون شروع
docker start N / docker stop Nشروع و توقف
docker pause N / docker unpause Nفریز و ادامه
docker psکانتینرهای در حال اجرا
docker ps -aهمه‌ی کانتینرها
docker rm N / docker rm -f Nحذف کانتینر (force برای در حال اجرا)
docker rmi IMGحذف image (یا فقط یک tag)