最小的Docker映像-小于1000字节

注意事项 佩雷夫 :该材料的作者是巴克莱银行的建筑师,也是英国开源热爱者Ian Miell的建筑师。 它旨在制作一个方便的Docker映像(带有“ sleeping”二进制文件),不需要下载,而只需通过复制和粘贴进行复制。 通过对汇编代码的尝试,错误和试验,他通过准备小于一千字节大小的图像来达到目标​​。



在这里(以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==

我怎么来的?


一位同事曾经展示过他用来测试Kubernetes集群的Docker镜像。 他什么也没做:他只是跑了下来,等着你杀死他。

“看,只需要700千字节! 真正快速的下载!”

然后我开始好奇我可以创建什么最小的Docker映像。 我想获得一个可以在base64中编码的代码,并通过简单的复制和粘贴将其发送到任何地方。 由于Docker映像只是一个tar文件,而tar文件只是一个文件,因此一切正常。

小二进制


首先,我需要一个很小的Linux二进制文件,什么也不做。 这需要一点魔力-这是有关创建小型可执行文件的两篇精彩,内容丰富且易读的文章:


我不需要“ 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.text节是不必要的,并且不需要加载数据。 另外, _start部分的上半部分处理文本输出。 结果,我尝试了以下代码:

 global _start _start: mov ebx,0 mov eax,1 int 0x80 

并且它最早编译为352个字节

但这并不是理想的结果,因为该程序只是结束了工作,我们需要它使a休眠。 作为进一步研究的结果,事实证明mov eax命令用相应的Linux系统调用号填充处理器寄存器,而int 0x80进行调用。 这将在此处更详细地描述。

在这里,我找到了所需的列表。 Syscall 1是exit ,我们需要的是syscall 29:pause 。 结果是以下程序:

 global _start _start: mov eax, 29 int 0x80 

我们还保存了8个字节:编译产生的结果为344个字节 ,现在它对我们来说是一个合适的二进制文件,不执行任何操作并期望发出信号。

挖六角


现在是时候用电锯处理二进制文件了。为此,我使用了hexer ,它本质上是vim,用于二进制文件,可以直接编辑hexes。 经过长时间的实验,我从中得到了:



...这里是:



该代码执行相同的操作,但请注意还剩下多少行和空格。 在我的工作过程中,我受到这样一份文件的指导,但总的来说,这是一条反复试验的道路。

因此,大小已减少到136个字节

少于100个字节?


我想知道我是否可以走得更远。 读完这篇文章后 ,我认为可以达到45个字节,但是! -不 此处描述的技巧仅适用于32位二进制文​​件,但不适用于64位二进制文​​件。

我所管理的最好的办法是采用该程序的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 

请注意, Dockerfile没有CMD ,因为这会增加映像的大小。 首先,您需要将命令通过参数传递给docker run

接下来, docker save创建了一个tar文件,然后使用最大gzip压缩对其进行了压缩。 结果是一个可移植的Docker映像文件,其大小小于1000个字节:

 $ docker build -tt . $ docker save t | gzip -9 - | wc -c 976 

我还尝试通过试验Docker清单文件来减小tar文件的大小,但徒劳无功:由于tar格式和gzip压缩算法的特殊性,此类更改仅导致最终gzip的增长。 我尝试了其他压缩算法,但事实证明gzip最适合此小文件。

译者的PS


另请参阅我们的博客:

Source: https://habr.com/ru/post/zh-CN413959/


All Articles