راه های زیادی هست که یه ایمیج جدید بسازی (یه لایه روی یه ایمیج دیگه بکشی)، یکی از اون راه ها که کارت رو ساده میکنه داکر فایل هست.
برای اینکه یه ایمیج بسازی دستور `docker build` رو داریم. این دستور یه Dockerfile میخواد و یه context. منظور از context محلی هست که فایل هات روش قرار داره. مقدار context میتونه یه PATH (آدرس local) یا یه URL (یه ریپوزیتوری روی git) باشه.
نکته context اینه که تو وقتی میگی که داکرفایل فلان جا (یا همین جایی که الان ترمینال توشه) هست و PATH هم فلانه، کل اون دایرکتوری با زیر دایرکتوری هاش به عنوان context در نظر گرفته میشه. توی مثال زیر context اون تیکه اخر کامند هست.
docker build -f /media/home/node.js.developers.kh/Dockerfile -t imageName:version1 /media/home/node.js.developers.kh/projects/p1
نکات مهم:
مثلا توی این تصویر به خوبی میتونی ببینی که من test.md رو با این فرض که /. به دایرکتوری mmm اشاره داره توی workdir کپی کردم.
همونطوری که توی تصویر مشخصه تو میتونی با فلگ f بگی Dockerfile کجاست.
docker build -f /path/to/a/docker-file .
فلگ t هم برای مشخص کردن اسم ایمیج، ریپوزیتوریش و ورژنش استفاده میشه.
docker build -t 9109679196/myapp .
خوبی داکرفایل اینه که قبل از اینکه اجرا بشه daemon داکر یه سری validation روش میره و اگه اروری بود اونو بر میگردونه. همون طوری که توی تصویر بالا به وضوح مشخصه کاری که داکر میکنه اینه که به ازای هر instruction، اگه لازم باشه یه ایمیج میسازه و تغییرات رو توش commit میکنه.
پس دستور `RUN cd /app` هیچ تاثیری روی instruction های بعدی نداره. مثلا من خودم میومدم یه همچین چیزی مینوشتم:
# ... WORKDIR /usr/src/app RUN cd /usr/share/nginx/html RUN echo "<h1>HI</h1>" > index.html # ...
و انتظار داشتم که فایل index.html توی مسیر /usr/share/nginx/html/ ایجاد بشه. و خب طبیعتا ایجاد نمیشه و این فایل توی مسیر WORKDIR ایجاد میشه.
استفاده از cache
داکر برای سرعت دادن به فرآیند build ایمیج میاد از ایمیج های intermediate ای که دفعه قبلی ساخته بود استفاده میکنه ولی این در صورتی اتفاق میفته که شما توی instruction ها دست نبرده باشی. به عبارت ساده تر تا اونجایی از داکرفایلت که تغییر نکرده از cache استفاده میکنه وگرنه از نو ایمیج های intermediate رو میسازه.
این ابزار بهت اجازه میده که build رو خیلی راحت تر انجام بدی. برای اطلاعات بیشتر این ریپوزیتوری گیت هاب شون.
فرمت نوشتن instruction های Dockerfile اینه که اول خود دستور میاد و بعدش آرگومان هایی که دریافت میکنه.
# comment INSTRUCTION arguments
قرارداد اینه که instruction ها رو UPPERCASE بنویسیم ولی در کل داکر case-sensitive نیست.
هر داکرفایلی باید با `FROM` شروع بشه. این دستور ایمیج parent رو مشخص میکنه. البته یه سری instruction میتونه قبلش بیاد:
ARG NODE_VERSION FROM node:${NODE_VERSION}-alpine
و وقتی میخوای اجراش بکنی اینجوری میتونی اون آرگومان رو به داکرفایل پاس بدی:
docker build -e NODE_VERSION=14.15.0 .
در ضمن شما میتونی برای build گرفتن ایمیج چند تا stage داشته باشی و به هر ایمیج یه اسم مشخص بدی:
FROM alpine:latest as builder RUN apk --no-cache add build-base FROM builder as build1 COPY source1.cpp source.cpp RUN g++ -o /binary source.cpp FROM builder as build2 COPY source2.cpp source.cpp RUN g++ -o /binary source.cpp
برای مطالعه بیشتر در مورد اینکه چجوری چند تا stage داشته باشی این داکیومنت رو بخون.
برای کپی کردن فایل توی ایمیجی که میخوای بسازی باید اون فایل کنار Dockerfile ای که نوشتی باشه. مثلا:
COPY file.zip /usr/src/app
برای اجرای دستور روی ایمیجی که میخوای بسازی توی Dockerfile میتونی اینجوری کار بکنی:
RUN apt-get update
برای اینکه یه دستوری رو به ایمیج ساخته شده معرفی بکنی که همیشه اونو اجرا بکنه، و جتی اگه بنا به هر دلیلی اون دستور رو نتونست بالا نگه داره (یعنی در حال اجرا نگهش داره) بیاد کانتینر رو پاک کنه و دوباره کانتینر بسازه.
CMD [ "npm", "start" ]
این داستان کاهش دادن زمان build شده رو این طوری ببینید که دارید اون بخشی رو که کمتر تغییر میکنه تو یه stage جدا build میکنید و همین طور که به سمت انتهای Dockerfile میریم بخش هایی که تغییر زیاد میکنن رو بنویسیم. مثلا:
مثلا این یه Dockerfile برای یه پروژه NestJS ای:
FROM node:14.15.0 AS builder ENV NODE_ENV build ENV APP_PORT=3000 WORKDIR /usr/src/app # If you have troubles with node-gyp use should install these dependencies # ref: https://gist.github.com/nzvtrk/cba2970b1df9091b520811e521d9bd44 # RUN apk add g++ make python COPY package*.json ./ RUN npm ci COPY . ./ COPY .* ./ RUN npm run build && npm prune --production FROM node:14.15.0 ENV NODE_ENV production WORKDIR /usr/src/app COPY --from=builder /usr/src/app/node_modules ./ COPY --from=builder /usr/src/app/package*.json ./ COPY --from=builder /usr/src/app/dist/ ./ ENTRYPOINT [ "npm", "run" ] EXPOSE ${APP_PORT} CMD [ "start:prod" ]
https://github.com/nodejsdeveloperskh/project-schema/tree/nestjs-mongoose
داکیومنت خود داکر برای داکر فایل
رفرنس (برای COPY)