ملاحظة perev. : مؤلف هذه المادة مهندس معماري في باركليز وعشاق المصادر المفتوحة البريطاني إيان ميل. تهدف إلى إنشاء صورة Docker مناسبة (مع ثنائي "نائم") ، والتي لا تحتاج إلى تنزيلها ، ولكن ببساطة نسخها عبر النسخ واللصق. من خلال التجربة والخطأ والتجريب مع رمز المجمع ، يصل إلى الهدف من خلال إعداد صورة بحجم أقل من كيلوبايت.
ها هو (مشفر في base64)H4sICIa2A1sCA2IA7Vrrbts2FFYL7M9+7QUGGNyfDYhtkuJFFLAhWZOhBYJmaLMOWBAEFC+xVlkyJLpYEBjdY+0l+k6jfGvqtkEWp2qD8TMg8vAqnsNzDg9lQhhmEjHDhY4zgWJBBUQJ5ZnCGAubMUQMyhJqoRRMJxYbo7Q2CedYxlQO/myqMroeEEHICIngApspxohEKI4h5DHmGEUQQw7jqAejDjBtnKz9q2w7zubi7gkugazVKHdGuWltQArkWDMCdoCqSpufg/QSPK4aV8pxW+nL96uxzMu39G+NqRe5PeekGj13Oi9BamXRmCtl1dS9X2jqel147C7W+aOJKd8dZ04dlcqsSw7KVyA9Ab/uHT/+cTht6mFRKVkMmywv0yv0mnxbMc8sSP8Apzvg0ViDtJwWxQ54Mpbny5W9qIrp2DSrmt+r+mVenu/ny+UelK6+mFR56VYtjsqfp3mxHupQZqZYdp/NGeo850x99r9j7QloyWEz8kvpK//47vuymvzQ29vf79m8MKnIaIa8bUmwRdByw6TKREIoIzE3xBrjrY7MGDUilomQ3GrNrFaIKqSZ4lkvL3tD12sn/IQCrI10xtcC7C1kH9I+xseQpYilRAwoZ5AI9IcfWFfqpRfzK1M3eeUZDRAfQDGAfc/jHTDKG1fVXiInlzcfctnwLPP9Vszs9VXvUzFy5jlZV5WzTbtN3cWkZWkhL/yS2gXm1p7lumkl24wkpv51FbYcU0EZy7SV0ucEZowkiCjvLbAVikCaGUqhyjT0c0Lj/YrElmmSWANOZ7MooHPwRCiLRaJEzBXKFGTCy49lUHNKjEigVdD6H4uTzPj9wzDCSawU0TQT2ujhjVwjgZzSj/n/eX7D/xPm/T8N/v/Ll/+Lg2fPnxw93eL85xFvyB9Rn4TzXwdAAxiMYLD/t9f/7eM/xDja1P+YBf3vKP7L2+PnttsA/IfjcQiE7nkgdH18Ey4O7pjdH7ygmX0p9n8eFA5aG3pb+0/eP/9jzFmw/13AdTBHK3/OPx7/Ic4X8qecQ9K244QG/98JXh8c/vLwwYM1/TD6KWqpv6LdOb37gT67URKterTpVxu1V9PXq3lW1d8skn++9Y83f4cDeEBAQMBnwliWuTWNu8l33G38/3X3fzGk79wFQ4S4Lwr+vwOcXIJHy4ANkLv4L4APcJ6ZSXUsz+efh1xaSOf3VxstHS6+H/nSu4s6wOns9OugxrdG7WXV5K6qc9NEn0n/ESab+s9o0P+O7v9ce1WzVNI7uAiczYI6BgQEBNwD/AvqV/+XACoAAA==
كيف جئت لهذا؟
أظهر زميل مرة صورة Docker التي استخدمها لاختبار مجموعات Kubernetes. لم يفعل شيئًا: ركض تحته وانتظر منك لقتله.
"انظروا ، إنها تستغرق 700 كيلوبايت فقط! تنزيل سريع حقًا! "
ثم شعرت بالفضول لمعرفة الحد الأدنى من صورة Docker التي يمكنني إنشاؤها. كنت أرغب في الحصول على واحد يمكن ترميزه في base64 وإرساله حرفياً في أي مكان مع نسخ ولصق بسيط. نظرًا لأن صورة Docker هي مجرد ملف tar وملف tar هو مجرد ملف ، يجب أن يعمل كل شيء.
ثنائي صغير
بادئ ذي بدء ، كنت بحاجة إلى ثنائي لينكس صغير جدًا لا يفعل شيئًا. يتطلب الأمر القليل من السحر - وهنا مقالتان رائعتان وغنيتان بالمعلومات وقابلة للقراءة حول إنشاء ملفات تنفيذية صغيرة:
لم أكن بحاجة إلى "Hello World" ، بل إلى برنامج ينام فقط ويعمل على x86_64. لقد بدأت بمثال من المقال الأول:
SECTION .data msg: db "Hi World",10 len: equ $-msg SECTION .text global _start _start: mov edx,len mov ecx,msg mov ebx,1 mov eax,4 int 0x80 mov ebx,0 mov eax,1 int 0x80
الجري:
nasm -f elf64 hw.asm -o hw.o ld hw.o -o hw strip -s hw
اتضح ثنائي
504 بايت .
ولكن مع ذلك ، ليس من الضروري استخدام "Hello World" ... أولاً ، اكتشفت أن أقسام البيانات. أو
.data
غير ضرورية ولا يلزم تحميل البيانات. بالإضافة إلى ذلك ، النصف العلوي من القسم
_start
يتعامل مع إخراج النص. ونتيجة لذلك ، حاولت الرمز التالي:
global _start _start: mov ebx,0 mov eax,1 int 0x80
وقد جمعت في وقت مبكر
352 بايت .
لكن هذه ليست النتيجة المرجوة ، لأن البرنامج ينهي عمله ببساطة ، ونحن بحاجة إليه للنوم. نتيجة لأبحاث إضافية ، اتضح أن الأمر
mov eax
يملأ سجل المعالج برقم استدعاء نظام Linux المقابل ، ويقوم
int 0x80
بإجراء المكالمة نفسها. يتم وصف هذا بمزيد من التفصيل
هنا .
وهنا وجدت القائمة المطلوبة. Syscall 1 هو
exit
، وما نحتاجه هو
syscall 29:pause
. تحول البرنامج التالي:
global _start _start: mov eax, 29 int 0x80
لقد أنقذنا 8 بايتات أخرى: أنتج التجميع نتيجة
344 بايت ، والآن هو ثنائي مناسب لنا ، والذي لا يفعل شيئًا ويتوقع إشارة.
حفر في ست عشري
لقد حان الوقت للحصول على المنشار والتعامل مع ثنائي ... لهذا ، استخدمت
السداسي ، وهو في الأساس ملف ثنائي للملفات الثنائية مع القدرة على تحرير السداسيات مباشرة. بعد تجارب مطولة ، حصلت على ما يلي:

... ها هو:

يفعل هذا الرمز نفس الشيء ، ولكن لاحظ عدد الأسطر والمسافات المتبقية. في سياق عملي ، استرشدت
بمثل هذه الوثيقة ، ولكن بشكل عام كان مسارًا للتجربة والخطأ.
لذا ، انخفض الحجم إلى
136 بايت .
أقل من 100 بايت؟
كنت أرغب في معرفة ما إذا كان يمكنني المضي قدمًا. بعد قراءة
هذا ، افترضت أنه سيكون من الممكن الوصول إلى 45 بايت ، ولكن للأسف! - لا. الحيل الموصوفة هناك مصممة فقط للثنائيات 32 بت ، لكنها لم تمر.
أفضل ما تمكّنت به هو أخذ
هذا الإصدار 64 بت من البرنامج وإنشائه في استدعاء النظام الخاص بي:
BITS 64 org 0x400000 ehdr: ; Elf64_Ehdr db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident times 8 db 0 dw 2 ; e_type dw 0x3e ; e_machine dd 1 ; e_version dq _start ; e_entry dq phdr - $$ ; e_phoff dq 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsize dw 1 ; e_phnum dw 0 ; e_shentsize dw 0 ; e_shnum dw 0 ; e_shstrndx ehdrsize equ $ - ehdr phdr: ; Elf64_Phdr dd 1 ; p_type dd 5 ; p_flags dq 0 ; p_offset dq $$ ; p_vaddr dq $$ ; p_paddr dq filesize ; p_filesz dq filesize ; p_memsz dq 0x1000 ; p_align phdrsize equ $ - phdr _start: mov eax, 29 int 0x80 filesize equ $ - $$
الصورة الناتجة
127 بايت . حول هذا ، توقفت عن محاولة تقليل الحجم ، لكنني أقبل المقترحات.
عامل ميناء صغير
الآن بعد أن أصبح هناك ثنائي يقوم بالانتظار المستمر ، يبقى وضعه في صورة Docker.
لحفظ كل بايت ممكن ، قمت بإنشاء ثنائي باسم ملف من بايت واحد -
t
- ووضعه في ملف
Dockerfile
، وإنشاء صورة فارغة تقريبًا:
FROM scratch ADD t /t
لاحظ أنه لا يوجد
CMD
في ملف
Dockerfile
، لأن هذا سيزيد من حجم الصورة. للبدء ، تحتاج إلى تمرير الأمر من خلال الوسائط
docker run
.
بعد ذلك ، أنشأ
docker save
ملف tar ، ثم تم ضغطه بأقصى ضغط gzip. والنتيجة هي ملف صورة Docker محمول بحجم أقل من 1000 بايت:
$ docker build -tt . $ docker save t | gzip -9 - | wc -c 976
حاولت أيضًا تقليل حجم ملف tar عن طريق تجربة ملف Docker البيان ، ولكن دون جدوى: نظرًا لخصائص تنسيق tar وخوارزمية ضغط gzip ، أدت هذه التغييرات فقط إلى نمو gzip النهائي. جربت خوارزميات ضغط أخرى ، ولكن تبين أن gzip هو الأفضل لهذا الملف الصغير.
ملاحظة من المترجم
اقرأ أيضا في مدونتنا: