إنشاء حاوية Docker الدنيا لتطبيقات Go

مرحبا يا هبر! أود أن ألفت انتباهكم إلى ترجمة المقال من قبل مؤسس Meetspaceapp Nick Gauthier service "Building Minimal Docker Container for Go Applications" .

10 دقائق للقراءة

هناك العديد من الحاويات الرسمية والمدعومة من المجتمع لمختلف لغات البرمجة (بما في ذلك Go). ولكن هذه الحاويات يمكن أن تكون كبيرة جدا. لنقم أولاً بمقارنة الطرق القياسية لبناء الحاوية لتطبيقات Go ، وبعد ذلك سأريك كيفية إنشاء تطبيقات Go صغيرة الحجم للغاية في حاويات

الجزء 1: "تطبيقنا"


للاختبار ، نحن بحاجة إلى بعض التطبيقات الصغيرة. دعونا شوكة google.com وإخراج حجم HTML.

package main import ( "fmt" "io/ioutil" "net/http" "os" ) func main() { resp, err := http.Get("https://google.com") check(err) body, err := ioutil.ReadAll(resp.Body) check(err) fmt.Println(len(body)) } func check(err error) { if err != nil { fmt.Println(err) os.Exit(1) } } 

إذا بدأنا ، سنحصل على بعض الأرقام فقط. حصلت على حوالي 17 كيلو. قررت عمداً استخدام طبقة المقابس الآمنة ، لكنني سأشرح السبب لاحقًا.

الجزء 2: الإرساء


باستخدام صورة Go الرسمية ، نكتب Dockerfile "onbuild":

 FROM golang:onbuild 

تفترض صورة "Onbuild" أن مشروعك بهيكل قياسي وسيخلق تطبيق Go قياسي. إذا كنت بحاجة إلى مزيد من المرونة ، يمكنك استخدام صورة Go القياسية وتجميعها بنفسك:

 FROM golang:latest RUN mkdir /app ADD . /app/ WORKDIR /app RUN go build -o main . CMD ["/app/main"] 

سيكون من الجميل هنا إنشاء Makefile أو أي شيء آخر من هذا القبيل تستخدمه لبناء التطبيق. يمكننا تحميل بعض الموارد من CDN أو استيرادها من مشروع آخر ، أو ربما نريد إجراء اختبارات في الحاوية ...
كما ترون ، فإن Go dockerization بسيطة ومباشرة ، خاصة عندما تفكر في أننا لا نستخدم الخدمات والمنافذ التي نحتاج للاتصال بها. لكن هناك عيبًا خطيرًا واحدًا في الصور الرسمية - فهي كبيرة جدًا. لنرى:

 REPOSITORY SIZE TAG IMAGE ID CREATED VIRTUAL SIZE example-onbuild latest 9dfb1bbac2b8 19 minutes ago 520.7MB example-golang latest 02e19291523e 19 minutes ago 520.7MB golang onbuild 3be7ee2ec1ae 9 days ago 514.9MB golang 1.4.2 121a93c90463 9 days ago 514.9MB golang latest 121a93c90463 9 days ago 514.9MB 

الصورة الأساسية تصل إلى 514.9 ميغابايت ، ويضيف تطبيقنا 5.8 ميغابايت أخرى. كيف يتطلب تطبيقنا المترجم 515 ميجابايت من التبعيات؟
الحقيقة هي أن تطبيقنا تم تجميعه داخل الحاوية. هذا يعني أن الحاوية تحتاج إلى تثبيت Go. لذلك ، فهو يحتاج إلى تبعيات Go ، بالإضافة إلى مدير الحزم ونظام تشغيل كامل حقًا. في الواقع ، إذا نظرت إلى Dockerfile for golang: 1.4 ، فهي تأتي مع Debian Jessie ، وتقوم بتثبيت مترجم GCC وأدوات البناء وتنزيلات Go وتثبيتها. وبالتالي ، نحصل على خادم دبيان بأكمله ومجموعة أدوات Go لتشغيل تطبيقنا الصغير. ما الذي يمكن عمله حيال ذلك؟

الجزء 3: ترجمة!


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

 go build -o main . docker build -t example-scratch -f Dockerfile.scratch . 

و Dockerfile.scratch بسيط:

 FROM scratch ADD main / CMD ["/main"] 

ما هو الصفر؟ الصفر هو صورة فارغة خاصة في عامل ميناء. حجمها هو 0B:

 REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE example-scratch latest ca1ad50c9256 About a minute ago 5.60MB scratch latest 511136ea3c5a 22 months ago 0B 

ونتيجة لذلك ، فإن حاوياتنا تشغل 5.6 ميغابايت فقط. ! ممتاز ولكن هناك مشكلة واحدة:

 $ docker run -it example-scratch no such file or directory 

ماذا يعني هذا؟ استغرق الأمر مني بعض الوقت لأدرك أن برنامج Go binary الخاص بنا كان يبحث عن مكتبات على نظام التشغيل الذي كان يعمل عليه. قمنا بتجميع طلبنا ، لكنه لا يزال مرتبطًا ديناميكيًا بالمكتبات التي يجب إطلاقها (أي لجميع مكتبات C). لسوء الحظ ، الصفر فارغ ، لذلك لا توجد مكتبات أو مسارات تحميل. نحتاج إلى تعديل البرنامج النصي للبناء لتجميع تطبيقنا بشكل ثابت مع جميع المكتبات المدمجة:

 CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . 

نقوم بتعطيل cgo ، والذي يعطينا ثنائي ثابت. نحدد أيضًا Linux على أنه نظام التشغيل (في حالة قيام شخص ما ببنائه على Mac أو Windows). تعني العلامة -a إعادة بناء جميع الحزم التي نستخدمها ، والتي ستعيد بناء جميع الواردات مع تعطيل cgo. الآن لدينا ثنائي ثابت. لنركض:

 $ docker run -it example-scratch Get https://google.com: x509: failed to load system roots and no roots provided 

ما هذا؟ لهذا السبب قررت استخدام SSL في مثالنا. هذا "غير قادر" على مثل هذه السيناريوهات الشائعة: لاستكمال طلبات SSL ، نحتاج إلى شهادات SSL الجذر. إذن كيف نضيفهم إلى حاوياتنا؟
اعتمادًا على نظام التشغيل ، قد تكون الشهادات في أماكن مختلفة. بالنسبة إلى العديد من توزيعات Linux ، هذا هو /etc/ssl/certs/ca-certificates.crt . لذا ، أولاً ، سنقوم بنسخ ca-certification.crt من جهاز الكمبيوتر الخاص بنا (أو جهاز Linux ظاهري أو من مزود شهادات عبر الإنترنت) إلى مستودعنا. ثم نضيف ADD إلى Dockerfile الخاص بنا لنقل هذا الملف إلى حيث يتوقع Go ذلك:

 FROM scratch ADD ca-certificates.crt /etc/ssl/certs/ ADD main / CMD ["/main"] 

الآن فقط قم بإعادة إنشاء صورتنا وإطلاقها. إنه يعمل! دعونا نرى حجم تطبيقنا الآن:

 REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE example-scratch latest ca1ad50c9256 About a minute ago 6.12MB example-onbuild latest 9dfb1bbac2b8 19 minutes ago 520.7MB example-golang latest 02e19291523e 19 minutes ago 520.7MB golang onbuild 3be7ee2ec1ae 9 days ago 514.9MB golang 1.4.2 121a93c90463 9 days ago 514.9MB golang latest 121a93c90463 9 days ago 514.9MB scratch latest 511136ea3c5a 22 months ago 0B 

لقد أضفنا أكثر قليلاً من نصف ميغابايت (ومعظمها يأتي من ملف ثابت ، وليس من شهادات الجذر). لدينا حاوية صغيرة جدًا - سيكون من المريح جدًا نقلها بين السجلات.

استنتاج


كان هدفنا هو تقليل حجم الحاوية لتطبيق Go. خصوصية Go هي أنه يمكن إنشاء ملف ثنائي مرتبط بشكل ثابت يحتوي بالكامل على التطبيق. يمكن للغات الأخرى أن تفعل ذلك أيضًا ، ولكن ليس بأي حال. يعتمد تطبيق تقنية مماثلة لتقليل حجم الحاوية بلغات أخرى على الحد الأدنى لمتطلباتها. على سبيل المثال ، يمكن تجميع تطبيق Java أو JVM خارج الحاوية ثم تضمينه في حاوية تحتوي فقط على JVM (وتبعياتها). ولكن حتى مع ذلك ، سيكون أقل من حاوية مع JDK.

Source: https://habr.com/ru/post/ar460535/


All Articles