Build Context یا زمینهی ساخت، مجموعهای از فایلهایی است که توی یک PATH یا URL مشخص قرار دارن.
این فایلها در طول فرآیند Build به Daemon Docker فرستاده میشن تا در فایلسیستم مرتبط با اون Image مورد استفاده قرار بگیرن.
بیایید با دستور استفاده شده برای ساختن Docker Image شروع کنیم:
$ docker build [OPTIONS] PATH | URL | -
فرض کنیم من در پوشه /Users/luc/src/github.com/lucj/genx هستم که حاوی کد اصلی برنامهی genx است (یک برنامهی ساده به زبان Go که دادههای ساختگی تولید میکنه).
وقتی Dockerfile در فولدرِ Root پروژه قرار داره معمولاً از دستورهایی مثل دستور زیر برای ساختن Image استفاده میکنیم:
$ docker image build -t genx:1.0 .
توی این حالت، زمینهی ساخت یا Build Context همون محتوای فولدر فعلی هست.
در واقع نقطهای که به عنوان آخرین عنصر این دستور اومده نشوندهندهی فولدر فعلی هست و Image از روی اطلاعات موجود روی این فولدر ساخته میشه.
استفاده از URL
این هم مشابه پروژهی genX در GitLab مدیریت میشه! بنابراین این امکان وجود داره که یک Image محلی بسازیم در حالی که به یک مخزن در GitLab اشاره داشته باشه:
$ docker image build -t genx:1.0 git@gitlab.com:lucj/genx.git
در این حالت، زمینهی ساخت مجموعهای از پروندههای موجود در gitlab.com/lucj/genx است.
در اصل، زمینهی ساخت یا Build Context حداقل شامل کد برنامه میشه که باید در فایلسیستمِ Image کپی بشه.
اما اصولا حاوی موارد دیگری هم هست که ما ممکنه در ساختن Image به اونها احتیاجی نداشته باشیم.
آیا زمینهی ساخت نیاز به فیلتر دارد؟
بله، در واقع بهتره که زمینهی ساخت فقط شامل پروندهها و پوشههایی باشه که واقعا به اونها احتیاج دارین.
توی پروژهای که کد اصلی توسط Git اداره میشه، ما از فایل .gitignore استفاده میکنیم تا مطمئن بشیم که دادههای خصوصی به صورت محلی نگهداری میشن و به GitHub / GitLab / BitBucket و غیره ارسال نمیشن.
این مورد در مرحلهی ساخت یک Image داکر هم اعمال میشه.
چون Daemon از یک فایل .dockerignore برای فیلتر کردن پروندهها و پوشههایی که در زمینهی ساخت یا Build Context نباید در نظر گرفته بشن، استفاده میکنه.
…
اگر از .dockerignore استفاده نکنیم چی میشه؟
در این صورت شما کلی چیزایی که بهشون احتیاج ندارین رو به Docker Daemon ارسال میکنین و اونها در فایلسیستم Image هم کپی خواهند شد.
فایلهای حجیم در زمینهی ساخت
بیایید Dockerfile زیر را در نظر بگیریم.
این فایل از یک Image پایهی nginx: 1.14.0 استفاده میکنه و محتوای پوشه فعلی (index.html ، css ، js ، img) رو در محل پیشفرض ارائه شده توسط NginX به آدرس (/usr/share/nginx/html) کپی میکنه.
FROM nginx:1.14.0
COPY . /usr/share/nginx/html/
محتوای پوشهی فعلی به صورت زیر خواهد بود:
$ tree -ah
.
├── [ 48] Dockerfile
├── [ 64] css
├── [ 64] images
├── [ 39] index.html
├── [ 64] js
└── [1.8G] ubuntu-18.04.1-desktop-amd64.iso
دینگدینگ:
آیا متوجه حضور فایل ISO نصب اوبونتو، توی این پوشه شدین؟
خب این فایل اشتباهی اینجا اومده!
در واقع موقع ساخت Image این فایل حجیم به Daemon فرستاده شده و بر روی Image کپی شده… مسلما ما این رو نمیخواستیم!
توجه:
همچین مثالی در واقعیت وجود نداره!
این مثال رو آوردیم که جنبهی فان قضیه یکم بالا بره و بتونیم بهتر منظورمون رو برسونیم!
خب حالا برای اینکه ما بخوایم همچین فایلهایی بارگذاری نشه باید فایل .dockerignore رو بسازیم و اسم این فایل رو بهش اضافه کنیم.
ما حتی میتونیم *.iso رو اضافه کنیم که مطمئن باشیم هیچ فایل ISOای به Daemon فرستاده نمیشه.
الان میتونیم مطمئن باشیم هیچ فایل حجیم ISOای توی فولدر فعلی وجود نخواهد داشت.
اما در مورد تاریخچهی پروژههای Git چطور؟ این فایلها هم، فایلهای بسیار حجیمی میتونن باشن.
Git history
بیاین فایلهای ISO رو حذف کنیم و مدیریت پروژه رو با استفاده از Git شروع کنیم:
$ git init
$ tree -a
.
├── .git
│ ├── HEAD
│ ├── branches
│ ├── config
│ ├── description
│ ├── hooks
│ │ ├── applypatch-msg.sample
│ │ ├── commit-msg.sample
│ │ ├── fsmonitor-watchman.sample
│ │ ├── post-update.sample
│ │ ├── pre-applypatch.sample
│ │ ├── pre-commit.sample
│ │ ├── pre-push.sample
│ │ ├── pre-rebase.sample
│ │ ├── pre-receive.sample
│ │ ├── prepare-commit-msg.sample
│ │ └── update.sample
│ ├── info
│ │ └── exclude
│ ├── objects
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ └── tags
├── Dockerfile
├── css
├── images
├── index.html
└── js
حالا Image رو ایجاد میکنیم:
$ docker image build -t www:1.0 .
Sending build context to Docker daemon 40.96kB
Step 1/2 : FROM nginx:1.14.0
— -> 86898218889a
Step 2/2 : COPY . /usr/share/nginx/html/
— -> 973188e5d7a3
Successfully built 973188e5d7a3
Successfully tagged www:1.0
و چک میکنیم ببینیم چه چیزایی داخلش هست:
$ docker run -ti www:1.0 bash
root@5d91b258bdc3:/# cd /usr/share/nginx/html/
root@5d91b258bdc3:/usr/share/nginx/html# ls
50x.html Dockerfile css images index.html js
root@5d91b258bdc3:/usr/share/nginx/html# find .git/
.git/
.git/description
.git/config
.git/refs
.git/refs/tags
.git/refs/heads
.git/hooks
.git/hooks/applypatch-msg.sample
.git/hooks/pre-push.sample
.git/hooks/pre-rebase.sample
.git/hooks/prepare-commit-msg.sample
.git/hooks/post-update.sample
.git/hooks/pre-applypatch.sample
.git/hooks/update.sample
.git/hooks/fsmonitor-watchman.sample
.git/hooks/pre-commit.sample
.git/hooks/commit-msg.sample
.git/hooks/pre-receive.sample
.git/objects
.git/objects/pack
.git/objects/info
.git/HEAD
.git/branches
.git/info
.git/info/exclude
root@5d91b258bdc3:/usr/share/nginx/html#
پوشه .git، که شامل تاریخچه نسخههای پروژه ها هست و میتونه یک پوشه بزرگ باشه، اینجا قرار داره.
آیا به تمامِ تاریخچهی Git در Image احتیاج داریم؟ فکر نمیکنم.
پس باید یک .dockerignore ایجاد کنیم و .git را بهش اضافه کنیم.
اعتبارنامهها
بیاین تصور کنیم میخوایم روی یک پروژهی Node.js کار کنیم که احتیاج داریم اون رو به یک پایگاه دادهی خارجی MongoDB متصل کنیم.
وقتی برنامه بر روی Swarm یا کلاستر Kubernetes مستقر شده باشه، پیشنهاد اینه که جریان اتصال از طریق Secret انجام بشه.
اگر دوست دارین در مورد Secretهای داکر بیشتر بدونین، خوندن این مقاله رو بهتون پیشنهاد میکنیم:
From env variables to Docker secrets
اما در طول فرآیند استقرار، باید اعتبارنامهها رو به منظور تست برنامه، توی پوشهمون داشته باشیم.
خب حالا سوال اینجاست…
تکلیف پوشههای اعتبارنامه توی پروژهی ما چیه؟
خیلی زشته که این پوشهها رو توی پروژمون داشته باشیم! ولی حقیقت اینه که اونا دارن به ما کمک میکنن.
پس حداقل باید مطمئن بشیم این پوشهها جاهای مختلف تکرار نشده باشن.
$ tree
.
├── .git
├── .gitignore
├── Dockerfile
├── app.js
├── creds
│ ├── mongo-preprod <-- mongodb://user:pass@prep.db.com:27017/mydb
│ ├── mongo-prod <-- mongodb://user:pass@prod.db.com:27017/mydb
│ └── mongo-test <-- mongodb://user:pass@test.db.com:27017/mydb
├── node_modules
│ ├── ...
└── package.json
من فایل .gitignore زیر رو دارم. اما چون پسوردم به GitHub نمیره پس مشکلی هم وجود نداره.
creds
node_modules
اما من هیچ فایل .dockerignoreای ندارم. در نتیجه فایلهای اعتبارنامهی من به Image ارسال میشن.
# Building the image
$ docker build -t myapp:1.0 .
# Checking what's inside the image's filesystem
$ docker run -ti myapp:1.0 sh
/app # ls
Dockerfile app.js creds node_modules package-lock.json package.json
/app # find creds/
creds/
creds/mongo-test
creds/mongo-prod
creds/mongo-preprod
/app # cat creds/mongo-prod
mongodb://user:pass@prod.db.com:27017/mydb
/app #
ما باید یک فایل .dockerignore بسازیم و بعد از اون اسم پوشههایی که نمیخوایم توی Image قرار داده بشن (اینجا منظورم همون فایلهای اعتبارنامه یا فایلهای creds هست) رو بهش اضافه کنیم.
خلاصه
نمونه های ساده (و اغراق آمیز!) که توی این مقاله مثال زدیم، نشون دادن که فیلتر کردن محتوای زمینهی ساخت یا Build Context با استفاده از پرونده .dockerignore بسیار ساده و البته بسیار مهم هست.
آیا همیشه مطمئن هستید که Build Context شما به درستی مدیریت و ایجاد شده؟