توی این درس یاد میگیری image دقیقاً چیست (قالبی ثابت از چند لایه)، اسم کامل آن چطور خوانده میشود (registry/repo:tag)، فرق tag و digest چیست، و از یک image چطور چند کانتینر مستقل میسازی. بعد چرخهی عمر کانتینر را میبینی: ساخته شدن، اجرا، توقف موقت، متوقف شدن و حذف. دستورهای محوری: docker pull، docker images و docker ps -a.
تشبیه: قالب شیرینی و شیرینیها
Section titled “تشبیه: قالب شیرینی و شیرینیها”image مثل قالب شیرینیپزی است: ثابت است، تغییر نمیکند و میتوانی هر چند بار که بخواهی از رویش شیرینی بسازی. کانتینر هر شیرینیای است که از آن قالب درآمده: یکی داغ و تازه سر میز (در حال اجرا)، یکی خوردم و فقط بشقابش مانده (متوقفشده)، یکی دور ریخته شده (حذفشده). هر کدام مستقلاند: اگر روی یکی خامه بریزی، بقیه خامه ندارند.
image از چه ساخته شده؟
Section titled “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.
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: گرفتن image و دیدنش (docker pull، docker images)
Section titled “مثال ۱: گرفتن image و دیدنش (docker pull، docker images)”docker pull nginx:alpinedocker images nginxalpine: Pulling from library/nginxDigest: sha256:df221db836e1754089190208cee7eeda94f233197056426eda74a43ab1abeac2Status: Image is up to date for nginx:alpinedocker.io/library/nginx:alpineIMAGE ID DISK USAGE CONTENT SIZE EXTRAnginx:1.28.0-alpine 30f1c0d78e0a 76.9MB 22.7MB Unginx:alpine df221db836e1 93.9MB 27.1MBnginx:latest abe47724e466 260MB 64.6MBدستور docker pull image را (در لایهها) از registry میآورد. هر خط با یک شناسهی کوتاه، یک لایه است؛ لایههایی که از قبل روی سیستم بودهاند دوباره دانلود نمیشوند (لایهها بین image ها مشترکاند). docker images همهی image های محلی را فهرست میکند؛ فیلتر nginx فقط یکی از آنها را نشان میدهد.
مثال ۲: یک image، چند اسم (tag)
Section titled “مثال ۲: یک image، چند اسم (tag)”docker tag alpine:latest lx-alpine:v1docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}' | grep -E "REPOSITORY|^alpine|^lx-alpine"REPOSITORY TAG IMAGE IDalpine latest 294b683cb724lx-alpine v1 294b683cb724دستور docker tag فقط یک برچسب تازه روی همان image میگذارد؛ هیچ دادهای کپی نمیشود. دقت کن شناسهی (ID) هر دو ردیف یکی است. tag شبیه اسم میانبر است، نه یک نسخهی جداگانه.
مثال ۳: tag در برابر digest
Section titled “مثال ۳: tag در برابر digest”docker image inspect alpine:latest --format 'Tag: alpine:latest{{"\n"}}Digest: {{index .RepoDigests 0}}'Tag: alpine:latestDigest: alpine@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6tag قابلجابهجایی است: سازندهی image میتواند فردا alpine:latest را به محتوای جدیدی منتقل کند. digest (sha256:...) اثر انگشت محتواست و فقط به همان محتوا اشاره میکند. اگر تکرارپذیری مطلق میخواهی، alpine@sha256:... را بنویس.
کدام یک تضمین میکند همیشه دقیقاً همان محتوا را میگیری؟
tag قابل جابهجایی است؛ digest اثر انگشت ثابت محتواست.
مثال ۴: از یک image سه کانتینر (docker ps)
Section titled “مثال ۴: از یک image سه کانتینر (docker ps)”for i in 1 2 3; do docker run -d --name lx-img-$i -p 830$i:80 nginx:alpine >/dev/null; donesleep 2docker 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; doneNAMES IMAGE PORTSlx-img-3 nginx:alpine 0.0.0.0:8303->80/tcplx-img-2 nginx:alpine 0.0.0.0:8302->80/tcplx-img-1 nginx:alpine 0.0.0.0:8301->80/tcpپورت 8301: HTTP 200پورت 8302: HTTP 200پورت 8303: HTTP 200سه کانتینر مستقل از یک image، روی سه پورت. هر کدام نام، شناسه، شبکه و فایلهای مخصوص خودش را دارد. حالا ثابت کنیم مستقلاند: داخل یکی فایل میسازیم و در بقیه نیست:
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')"; donelx-img-1: /tmp/mine.txtlx-img-2: ls: /tmp/mine.txt: No such file or directorylx-img-3: ls: /tmp/mine.txt: No such file or directoryمثال ۵: چرخهی عمر کانتینر
Section titled “مثال ۵: چرخهی عمر کانتینر”حالا یک کانتینر را قدمبهقدم از ساخته شدن تا حذف ببینیم. وضعیت را در هر گام با docker ps -a میخوانیم:
st() { docker ps -a --filter name=lx-life --format '{{.Names}}: {{.Status}}'; }docker create --name lx-life alpine sleep 300 >/dev/null; echo "1) create"; stdocker start lx-life >/dev/null; echo "2) start"; stdocker pause lx-life >/dev/null; echo "3) pause"; stdocker unpause lx-life >/dev/null; echo "4) unpause"; stdocker stop -t 1 lx-life >/dev/null; echo "5) stop"; stdocker rm lx-life >/dev/null; echo "6) rm"; st; echo "(دیگر چیزی نیست)"1) createlx-life: Created2) startlx-life: Up Less than a second3) pauselx-life: Up Less than a second (Paused)4) unpauselx-life: Up Less than a second5) stoplx-life: Exited (137) Less than a second ago6) rm(دیگر چیزی نیست)دقت کن مرحلهی ۵ با 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 در عمق
Section titled “پشت پرده: image در عمق”یک image در واقع چند چیز است: یک manifest (فهرست لایهها)، یک config (دستور اجرا، متغیرهای محیطی، …)، و خود لایهها که هر کدام یک فایل tar فشرده است. هر کدام با هش sha256 محتوایش شناسایی میشود (content-addressable): اگر دو image لایهی یکسان داشته باشند، فقط یک نسخه روی دیسک ذخیره میشود. همین باعث میشود pull دومِ چیزی که لایههای مشترک دارد سریع باشد.
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 اندازهی لایهی نوشتنی هر کانتینر را میبینی).
جدولهای مرجع
Section titled “جدولهای مرجع”image در برابر کانتینر
Section titled “image در برابر کانتینر”| image | کانتینر | |
|---|---|---|
| قابلتغییر | خیر (فقطخواندنی) | بله (لایهی قابلنوشتن) |
| تعداد | یک نسخه | هر چند تا از یک image |
| فهرست | docker images |
docker ps / docker ps -a |
| حذف | docker rmi |
docker rm |
وضعیتهای کانتینر
Section titled “وضعیتهای کانتینر”| وضعیت | یعنی | دستور رسیدن |
|---|---|---|
| Created | ساخته شده، شروع نشده | docker create |
| Up | در حال اجرا | docker start / docker run |
| Up (Paused) | فریز شده | docker pause |
| Exited (N) | تمام شده با کد N (0 یعنی موفق) |
پایان پروسه یا docker stop |
| Removed | حذف شده | docker rm |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) حذف کانتینر در حال اجرا
Section titled “۱) حذف کانتینر در حال اجرا”docker run -d --name lx-life alpine sleep 300 >/dev/nulldocker rm lx-lifedocker rm -f lx-lifeError response from daemon: cannot remove container "lx-life": container is running: stop the container before removing or force removelx-lifeبدون -f (force)، داکر نمیگذارد کانتینر در حال اجرا را حذف کنی. راهحل: اول docker stop lx-life (یا docker rm -f اگر مطمئنی).
۲) tag ناموجود
Section titled “۲) tag ناموجود”docker pull alpine:no-such-tag-xyzError response from daemon: failed to resolve reference "docker.io/library/alpine:no-such-tag-xyz": docker.io/library/alpine:no-such-tag-xyz: not foundtag وجود ندارد (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 بیاور).
دیدن جواب
docker pull alpinedocker images alpineUsing default tag: latestlatest: Pulling from library/alpineDigest: sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6Status: Image is up to date for alpine:latestdocker.io/library/alpine:latestIMAGE ID DISK USAGE CONTENT SIZE EXTRAalpine:latest 294b683cb724 13.6MB 4.28MBاز image alpine دو کانتینر lx-t1 و lx-t2 بساز که هر دو sleep 120 اجرا کنند. فهرستشان کن، بعد هر دو را (بهصورت force) حذف کن و ثابت کن حذف شدند.
دیدن جواب
docker run -d --name lx-t1 alpine sleep 120 >/dev/nulldocker run -d --name lx-t2 alpine sleep 120 >/dev/nulldocker ps --filter name=lx-t --format '{{.Names}}: {{.Status}}'docker rm -f lx-t1 lx-t2docker ps -a --filter name=lx-t -q | wc -l | tr -d ' 'lx-t2: Up Less than a secondlx-t1: Up 1 secondlx-t1lx-t20ثابت کن tag و digest دو چیز متفاوتاند: یک tag جدید روی alpine بگذار، نشان بده شناسه (ID) یکی است. بعد ثابت کن حذف یک tag (docker rmi tag) خود image را پاک نمیکند تا وقتی tag دیگری به آن اشاره میکند.
دیدن جواب
docker tag alpine:latest lx-alpine:v2docker images --format '{{.Repository}}:{{.Tag}} {{.ID}}' | grep -E "^(alpine:latest|lx-alpine:v2)"docker rmi lx-alpine:v2docker images --format '{{.Repository}}:{{.Tag}}' | grep -c "^alpine:latest"lx-alpine:v2 294b683cb724alpine:latest 294b683cb724Untagged: lx-alpine:v21با docker rmi lx-alpine:v2 فقط برچسب حذف شد («Untagged»)؛ image با اسم alpine:latest سر جایش است (شمارنده 1).
آزمونک
Section titled “آزمونک”docker run در واقع ترکیب کدام دو دستور است؟
docker run کانتینر را میسازد (create) و شروع میکند (start)؛ اگر image نباشد، pull هم میکند.
تفاوت tag و digest؟
برای تکرارپذیری مطلق از image@sha256:... استفاده کن.
docker tag alpine:latest lx-alpine:v1 چه میکند؟
ID هر دو یکی است؛ دادهای کپی نمیشود.
چرا docker rm روی کانتینر در حال اجرا خطا میدهد؟
daemon از حذف تصادفی کانتینر در حال اجرا جلوگیری میکند.
ساختن ۱۰۰ کانتینر از یک image چرا فضای زیادی نمیگیرد؟
copy-on-write: لایههای image کپی نمیشوند.
جمعبندی
Section titled “جمعبندی”- image قالب فقطخواندنی از چند لایه است؛ کانتینر نمونهی اجراشده با یک لایهی نازک قابلنوشتن.
- اسم کامل:
registry/repo:tag؛tagبرچسب قابلجابهجایی است وdigestاثر انگشت ثابت. docker pullimage را (لایهبهلایه) میآورد؛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) |