في التعليقات على مقالتي في Docker: نصيحة سيئة ، كان هناك العديد من الطلبات لشرح السبب في أن Dockerfile الموصوفة فيه أمر فظيع للغاية.
ملخص السلسلة السابقة : اثنين من المطورين في الموعد النهائي الثابت يشكلون Dockerfile. في هذه العملية ، يأتي إليهم إيجور إيفانوفيتش. Dockerfile الناتج سيء جدًا لدرجة أن منظمة العفو الدولية على وشك نوبة قلبية.

الآن دعنا نتعرف على الخطأ في ملف Dockerfile هذا.
لقد مر أسبوع.
يجتمع Dev Petya في غرفة الطعام مع فنجان من القهوة مع Ops Igor Ivanovich.
P: ايجور ايفانوفيتش ، هل أنت مشغول جدا؟ أرغب في معرفة أين وصلنا.
AI: هذا جيد ، فأنت لا تقابل كثيرًا المطورين المهتمين بالعملية.
للبدء ، دعونا نتفق على بعض الأشياء:
- أيديولوجية عامل الميناء: حاوية واحدة - عملية واحدة.
- أصغر الحاوية ، كان ذلك أفضل.
- كلما تم تخزين ذاكرة التخزين المؤقت ، كان ذلك أفضل.
P: ولماذا يجب أن يكون هناك عملية واحدة في حاوية واحدة؟
AI: Docker ، عند بدء تشغيل الحاوية ، تراقب حالة العملية باستخدام معرف المنتج 1. إذا ماتت العملية ، يحاول Docker إعادة تشغيل الحاوية. افترض أن لديك العديد من التطبيقات التي تعمل في الحاوية الخاصة بك أو أن التطبيق الرئيسي لم يتم تشغيله باستخدام معرف المنتج 1. إذا ماتت العملية ، فلن يعلم Docker بذلك.
إذا لم تكن هناك أسئلة أخرى ، فقم بإظهار Dockerfile.
وأظهرت بيتيا:
FROM ubuntu:latest # COPY ./ /app WORKDIR /app # RUN apt-get update # RUN apt-get upgrade # RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor # bundler RUN gem install bundler # nodejs RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash - RUN apt-get install -y nodejs # RUN bundle install --without development test --path vendor/bundle # RUN rm -rf /usr/local/bundle/cache/*.gem RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN rake assets:precompile # , , . CMD ["/app/init.sh"]
AI: أوه ، دعونا فرزها بالترتيب. لنبدأ بالسطر الأول:
FROM ubuntu:latest
كنت تأخذ latest
علامة. يؤدي استخدام latest
علامة إلى عواقب غير متوقعة. تخيل أن برنامج صيانة الصور يبني نسخة جديدة من الصورة مع قائمة مختلفة من البرامج ، هذه الصورة تحصل على أحدث علامة. وفي أحسن الأحوال ، تتوقف الحاوية عن التجميع ، وفي أسوأ الأحوال ، تصطاد الأخطاء التي لم تكن موجودة من قبل.
يمكنك التقاط صورة مع نظام تشغيل كامل مع الكثير من البرامج غير الضرورية ، والتي تضخيم الحاوية. والمزيد من البرامج ، والمزيد من الثقوب ونقاط الضعف.
بالإضافة إلى ذلك ، كلما كانت الصورة أكبر ، زادت مساحة المساحة على المضيف وفي السجل (هل تخزن الصور في مكان ما)؟
P: نعم ، بالطبع ، لدينا سجل ، وقمت بإعداده.
II: إذن ، ما الذي أتحدث عنه؟ .. أوه نعم ، وحدات التخزين ... يتزايد حجم شبكة الاتصال أيضًا. بالنسبة إلى صورة واحدة ، يكون هذا غير مرئي ، ولكن عندما يكون هناك تجميع مستمر واختبارات ونشر ، يكون واضحًا. وإذا لم يكن لديك وضع الله على AWS ، فستتلقى أيضًا حساب مساحة.
لذلك ، تحتاج إلى اختيار الصورة الأكثر ملاءمة ، مع الإصدار الدقيق والحد الأدنى من البرنامج. على سبيل المثال ، خذ: FROM ruby:2.5.5-stretch
P: أوه ، أنا أرى. وكيف وأين ترى الصور المتاحة؟ كيف نفهم أي واحد أحتاج؟
AI: عادة ، يتم التقاط الصور من dockhub ، لا تخلط مع pornhub :). عادة ما يكون هناك العديد من التجميعات لصورة:
جبال الألب : يتم تجميع الصور على صورة لينكس في أضيق الحدود ، فقط 5 ميغابايت. ناقص: تم تصميمه من خلال تطبيقه الخاص لـ libc ، الحزم القياسية فيه لا تعمل. سيستغرق العثور على الحزمة الصحيحة وتثبيتها الكثير من الوقت.
خدش : الصورة الأساسية ، لا تستخدم لبناء صور أخرى. الغرض منه هو فقط تشغيل البيانات الثنائية المعدة. مثالي لتشغيل التطبيقات الثنائية ، والتي تشمل كل ما تحتاجه ، مثل تطبيقات go.
بناءً على أي نظام تشغيل مثل Ubuntu أو Debian. حسنا ، هنا ، لا أعتقد أن هناك حاجة لشرح ذلك.
الثاني: الآن نحن بحاجة إلى وضع كل ما هو إضافي. حزم وتنظيف المخابئ. وعلى الفور يمكنك رمي apt-get الترقية . خلاف ذلك ، مع كل مجموعة ، على الرغم من العلامة الثابتة للصورة الأساسية ، سيتم الحصول على صور مختلفة. تحديث الحزم في صورة ما هو مهمة المشرف ، ويرافقه تغيير العلامة.
P: نعم ، لقد حاولت القيام بذلك ، اتضح مثل هذا:
WORKDIR /app COPY ./ /app RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle RUN rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
الثاني: ليس سيئًا ، ولكن هناك أيضًا شيء يجب العمل عليه. انظر ، هذا هو الأمر:
RUN rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
... لا يحذف البيانات من الصورة النهائية ، لكنه يخلق طبقة إضافية فقط بدون هذه البيانات. صحيح جدا:
RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
لكن هذا ليس كل شيء. ماذا لديك هناك يا روبي؟ ثم ليس من الضروري نسخ المشروع بأكمله في البداية. مجرد نسخ Gemfile و Gemfile.lock.
مع هذا النهج ، لن يتم تنفيذ تثبيت حزمة لكل تغيير في المصدر ، ولكن فقط إذا تم تغيير Gemfile أو Gemfile.lock.
تعمل نفس الأساليب مع لغات أخرى باستخدام مدير التبعية ، مثل npm و pip و composer وغيرها بناءً على الملف مع قائمة التبعيات.
وأخيراً ، تذكر ، في البداية ، تحدثت عن أيديولوجية دوكر "حاوية واحدة - عملية واحدة"؟ هذا يعني أن المشرف غير مطلوب. أيضا ، لا تقم بتثبيت systemd ، لنفس الأسباب. في الواقع ، دوكر نفسه هو المشرف. وعندما تحاول تشغيل العديد من العمليات فيه ، فإن الأمر يشبه إطلاق العديد من التطبيقات في عملية مشرف واحد.
أثناء التجميع ، ستقوم بإنشاء صورة واحدة ، ثم تقوم بتشغيل العدد المطلوب من الحاويات بحيث يكون لكل عملية عملية واحدة.
ولكن أكثر على ذلك في وقت لاحق.
P: يبدو أن نفهم. انظر ماذا يحدث:
FROM ruby:2.5.5-stretch WORKDIR /app COPY Gemfile* /app RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY . /app RUN rake assets:precompile CMD ["bundle”, “exec”, “passenger”, “start"]
هل سيتم إعادة تعريف الشياطين عندما تبدأ الحاوية؟
AI: نعم ، هذا صحيح. بالمناسبة ، يمكنك استخدام كل من CMD و ENTRYPOINT. وفهم ما هو الفرق ، وهذا هو واجبك. يوجد مقال جيد حول هذا الموضوع في حبري.
هيا. تقوم بتنزيل الملف لتثبيت العقدة ، ولكن لا يوجد ما يضمن أنه سيكون لديه ما تحتاجه. من الضروري إضافة التحقق من الصحة. على سبيل المثال ، مثل هذا:
RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \ && echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c - \ && bash setup_9.x \ && rm -rf setup_9.x \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
باستخدام المجموع الاختباري ، يمكنك التحقق من قيامك بتنزيل الملف الصحيح.
P: لكن إذا تغير الملف ، فلن يعمل التجميع.
الثاني: نعم ، وغريب بما فيه الكفاية ، وهذا هو أيضا زائد. سوف تكتشف أن الملف قد تغير ، ويمكنك أن ترى ما الذي تغير هناك. إنهم لا يعرفون أبدًا ، كما يقولون ، نصًا يزيل كل ما يصل إليه ، أو يفعل مستترًا.
P: شكرا لك. اتضح أن Dockerfile النهائي سيبدو كما يلي:
FROM ruby:2.5.5-stretch WORKDIR /app COPY Gemfile* /app RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \ && echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c - \ && bash setup_9.x \ && rm -rf setup_9.x \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY . /app RUN rake assets:precompile CMD ["bundle”, “exec”, “passenger”, “start"]
P: ايجور ايفانوفيتش ، شكرا للمساعدة. لقد حان الوقت لأجري ، ولدي 10 إلتزامات أخرى لأقوم بها اليوم.
أيغور إيفانوفيتش ، الذي أوقف زميله المتسرع بإلقاء نظرة ، يأخذ رشفة من القهوة القوية. بعد التفكير لبضع ثوان حول اتفاقية مستوى الخدمة (99.9٪) ورمز بدون أخطاء ، يسأل سؤالاً.
AI: وأين يمكنك الاحتفاظ بسجلات؟
P: بالطبع ، في الإنتاج. بالمناسبة ، نعم ، كيف يمكننا الوصول إليهم دون ssh؟
II: إذا تركتها في الملفات ، فقد تم بالفعل اختراع حل لك. يسمح لك الأمر docker exec بتنفيذ أي أمر في حاوية. على سبيل المثال ، يمكنك جعل القط للسجلات. وباستخدام المفتاح -it وتشغيل bash (إذا كان مثبتًا في الحاوية) ، فستحصل على وصول تفاعلي إلى الحاوية.
ولكن لا يجب عليك تخزين السجلات في الملفات. كحد أدنى ، هذا يؤدي إلى نمو غير المنضبط للحاوية ، ولكن لا أحد تدوير السجلات. جميع السجلات تحتاج إلى طرح في stdout. هناك يمكنك رؤيتهم بالفعل باستخدام أمر سجلات عامل ميناء .
P: إيغور إيفانوفيتش ، ولكن هل يمكن وضع السجلات في الدليل المُثبت ، على العقدة المادية ، كبيانات مستخدم؟
AI: من الجيد أنك لم تنسَ إزالة البيانات التي تم تنزيلها إلى قرص العقدة. مع السجلات ، هذا ممكن أيضًا ، تذكر فقط إعداد التدوير.
كل ما يمكنك تشغيله.
P: ايجور ايفانوفيتش ، ولكن تقديم المشورة ماذا تقرأ؟
AI: أولاً ، اقرأ توصيات مطوري Docker ، بالكاد يعرف أي شخص Docker أفضل منهم.
وإذا كنت تريد أن تمر بهذه الممارسة ، فاذهب بشكل مكثف . بعد كل شيء ، نظرية دون ممارسة ميتة.