我们在使用Dockerfile构建docker镜像时,一种方式是使用官方预先配置好的容器镜像。优点是我们不用从头开始构建,节省了很多工作量,但付出的代价是需要下载很大的镜像包。

比如我机器上docker images返回的这些基于nginx的镜像,每个都超过了100MB,而一个简单的Centos的镜像近200MB,如果安装了相关的软件,占用空间会更大。

[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 540a289bab6c 5 weeks ago 126MB
centos 6.9 2199b8eb8390 8 months ago 195MB

如果我们的需求是在构建一个符合我们实际业务需求的Docker镜像的前提下,确保镜像尺寸尽可能的小,应该怎么做呢?

思路是使用空镜像scratch。

scratch 的 Docker 官方镜像地址:https://hub.docker.com/_/scratch

一、关于 Docker 的 scratch 镜像 介绍

scratch 的 Docker 官方镜像描述:https://hub.docker.com/_/scratch?tab=description

FROM scratch

官方说明:该镜像是一个空的镜像,可以用于构建基础镜像(例如 Debian、Busybox)或超小镜像,可以说是真正的从零开始构建属于自己的镜像。要知道,一个官方的ubuntu镜像有60MB+,CentOS镜像有70MB+。

可以把一个可执行文件扔进来直接执行。

二、查看 空镜像 scratch

可以看到,scratch 是一个官方提供的镜像。

既然,能搜索到 scratch 镜像,那么我们 pull 下来看看:

[root@docker scratch]# docker pull scratch
Using default tag: latest
Error response from daemon: 'scratch' is a reserved name

结果:输出了一个错误的响应。

跟据错误提示,我们知道  scratch 是一个保留名称。

备注: scratch 是一个 search 得到,但是 pull 不了的特殊镜像。

三、【示例】基于scratch镜像构建一个大小为 0 的镜像

既然 scratch 不能被拉取,如何做到 docker image ls 看到一个 0 字节的镜像。

官方给出了下面的方法:

[root@docker scratch]# tar cv --files-from /dev/null | docker import - scratch
sha256:8036fcc96c9a2f7ba1c42bab84996a882afeccf598c7fcab902f1374cd26e234
[root@docker scratch]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
scratch latest 8036fcc96c9a 3 seconds ago 0B

四、【示例】基于 scratch 构建一个 hello 镜像

创建一个 scratch 的目录:

[root@docker ~]# mkdir /opt/scratch
[root@docker ~]# cd /opt/scratch/
[root@docker scratch]#

创建 dockerfile 文件,内容如下:

[root@docker scratch]# vi dockerfile
[root@docker scratch]# cat dockerfile
FROM scratch
ADD hello /
CMD ["/hello"]

构建 docker 镜像

[root@docker02 scratch]# docker build -t hello .
Sending build context to Docker daemon 5.632kB
Step 1/3 : FROM scratch
--->
Step 2/3 : ADD hello /
---> b34f73261173
Step 3/3 : CMD ["/hello"]
---> Running in 4b7912df3e9f
Removing intermediate container 4b7912df3e9f
---> 9766933b0a7c
Successfully built 9766933b0a7c
Successfully tagged hello:latest
[root@docker02 scratch]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello latest 9766933b0a7c 5 seconds ago 1.9kB
nginx latest 540a289bab6c 5 weeks ago 126MB
centos 6.9 2199b8eb8390 8 months ago 195MB

创建并启动容器:

[root@docker scratch]# docker run --rm -it hello:latest
Welcome to Beijing!

hello 的是使用C语言编译之后的二进制程序

[root@docker scratch]# cat hello.c
//#include <unistd.h>
#include <sys/syscall.h>
#ifndef DOCKER_IMAGE
#define DOCKER_IMAGE "hello-world"
#endif
#ifndef DOCKER_GREETING
#define DOCKER_GREETING "Welcome to Beijing!"
#endif const char message[] =
DOCKER_GREETING "\n"; void _start() {
//write(1, message, sizeof(message) - 1);
syscall(SYS_write, 1, message, sizeof(message) - 1); //_exit(0);
syscall(SYS_exit, 0);
}

然后编译 hello.c

[root@docker scratch]# gcc -o hello -static -nostartfiles hello.c
[root@docker scratch]# ls
dockerfile hello hello.c

执行测试:

[root@docker02 scratch]# ./hello
Welcome to Beijing!

编译出错的故障处理

[root@docker scratch]# gcc -o hello -static -nostartfiles hello.c
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status 解决办法:
[root@docker scratch]# yum install glibc-static gcc -y

五、补充1

Docker 是go语言写的,C语言不行,跑的话会报错。

编写 hello.c

[root@docker hello]# vi hello.c
[root@docker hello]# cat hello.c
#include <stdio.h> main() {
printf("hello world\n");
}

编译 hello.c

[root@docker hello]# gcc hello.c -o hello
[root@docker hello]# ls
hello hello.c

编写dockerfile

[root@docker hello]# vi dockerfile
[root@docker hello]# cat dockerfile
FROM scratch
ADD hello /
CMD ["/hello"]
[root@docker hello]# ls
dockerfile hello hello.c

构建镜像:

[root@docker hello]# docker build -t helloworld .
Sending build context to Docker daemon 12.29kB
Step 1/3 : FROM scratch
--->
Step 2/3 : ADD hello /
---> 592aa1a2b194
Step 3/3 : CMD ["/hello"]
---> Running in 426ff56ca7b8
Removing intermediate container 426ff56ca7b8
---> b9cb3eabf90f
Successfully built b9cb3eabf90f
Successfully tagged helloworld:latest

[root@docker hello]# docker image ls helloworld
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld latest b9cb3eabf90f 10 seconds ago 8.44kB

创建并启动容器

[root@docker hello]# docker run --rm helloworld
standard_init_linux.go:211: exec user process caused "no such file or directory"

结果报错了。

备注:当然,可以对上面这个hello.c 的文件可以进行改写,参照 示例:基于 scratch 构建一个 hello 镜像

当然,使用 ubuntu 镜像作为基础镜像,来构建新的 hello 镜像,这是行得通的。

[root@docker hello]# vi dockerfile
[root@docker hello]# cat dockerfile
FROM ubuntu
ADD hello /
CMD ["/hello"] [root@docker hello]# docker build -t hello:v1 .
Sending build context to Docker daemon 12.29kB
Step 1/3 : FROM ubuntu
latest: Pulling from library/ubuntu
7ddbc47eeb70: Pull complete
c1bbdc448b72: Pull complete
8c3b70e39044: Pull complete
45d437916d57: Pull complete
Digest: sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d
Status: Downloaded newer image for ubuntu:latest
---> 775349758637
Step 2/3 : ADD hello /
---> 09485e1c9ec0
Step 3/3 : CMD ["/hello"]
---> Running in 661d260c2d06
Removing intermediate container 661d260c2d06
---> f382742c6c29
Successfully built f382742c6c29
Successfully tagged hello:v1 [root@docker hello]# docker image ls hello:v1
REPOSITORY TAG IMAGE ID CREATED SIZE
hello v1 f382742c6c29 9 seconds ago 64.2MB [root@docker hello]# docker run --rm hello:v1
hello world

六、【示例】基于 scratch 空镜像,使用 hello.go 创建一个新的Docker镜像

创建 hello.go

[root@docker opt]# mkdir hello-go
[root@docker opt]# cd hello-go/
[root@docker hello-go]# vi hello.go
[root@docker hello-go]# cat hello.go
package main
import "fmt" func main(){
fmt.Printf("Hello World\n")
}

安装 go 环境

[root@docker hello-go]# yum install go -y
[root@docker hello-go]# go version
go version go1.13.3 linux/amd64

编译 hello.go 文件

[root@docker hello-go]# go build hello.go
[root@docker hello-go]# ls
hello
hello.go

创建 dockerfile

[root@docker hello-go]# vi dockerfile
[root@docker hello-go]# cat dockerfile
FROM scratch
ADD hello /
CMD ["/hello"]
[root@docker hello-go]# ls
dockerfile hello hello.go

构建镜像

[root@docker hello-go]# docker build -t hello-go .
Sending build context to Docker daemon 2.029MB
Step 1/3 : FROM scratch
--->
Step 2/3 : ADD hello /
---> 05500e381032
Step 3/3 : CMD ["/hello"]
---> Running in 82474b89e112
Removing intermediate container 82474b89e112
---> b330800756c5
Successfully built b330800756c5
Successfully tagged hello-go:latest
[root@docker hello-go]# docker image ls hello-go
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-go latest b330800756c5 8 seconds ago 2.03MB

创建并启动容器

[root@docker hello-go]# docker run --rm hello-go
Hello World [root@docker hello-go]# docker image history hello-go
IMAGE CREATED CREATED BY SIZE COMMENT
b330800756c5 27 seconds ago /bin/sh -c #(nop) CMD ["/hello"] 0B
05500e381032 27 seconds ago /bin/sh -c #(nop) ADD file:cb1502c2540a3aa37… 2.03MB

七、docker-library 的 hello-world

官方:https://github.com/docker-library/hello-world/blob/master/hello.c

//#include <unistd.h>
#include <sys/syscall.h> #ifndef DOCKER_IMAGE
#define DOCKER_IMAGE "hello-world"
#endif #ifndef DOCKER_GREETING
#define DOCKER_GREETING "Hello from Docker!"
#endif #ifndef DOCKER_ARCH
#define DOCKER_ARCH "amd64"
#endif const char message[] =
"\n"
DOCKER_GREETING "\n"
"This message shows that your installation appears to be working correctly.\n"
"\n"
"To generate this message, Docker took the following steps:\n"
" 1. The Docker client contacted the Docker daemon.\n"
" 2. The Docker daemon pulled the \"" DOCKER_IMAGE "\" image from the Docker Hub.\n"
" (" DOCKER_ARCH ")\n"
" 3. The Docker daemon created a new container from that image which runs the\n"
" executable that produces the output you are currently reading.\n"
" 4. The Docker daemon streamed that output to the Docker client, which sent it\n"
" to your terminal.\n"
"\n"
"To try something more ambitious, you can run an Ubuntu container with:\n"
" $ docker run -it ubuntu bash\n"
"\n"
"Share images, automate workflows, and more with a free Docker ID:\n"
" https://hub.docker.com/\n"
"\n"
"For more examples and ideas, visit:\n"
" https://docs.docker.com/get-started/\n"
"\n"; void _start() {
//write(1, message, sizeof(message) - 1);
syscall(SYS_write, 1, message, sizeof(message) - 1); //_exit(0);
syscall(SYS_exit, 0);
}

八、补充2

  • gcc -D可以定义宏,起到替换、条件编译的功能;即hello.c中定义了一个宏,我可以在gcc编译时使用-D替换该宏。就好像我docker镜像定义了一些变量,但是docker run仍可以-e传递变量,覆盖原有的变量
  • gcc -static指定强制使用静态库,
  • -O 对程序进行优化编译、链接。采用这个选项,整个源代码会在编译、链接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是编译、链接的速度就相应地要慢一些,而且对执行文件的调试会产生一定的影响,造成一些执行效果与对应源文件代码不一致等一些令人“困惑”的情况。因此,一般在编译输出软件发行版时使用此选项。
  • -Os 使用了所有-O2的优化选项,但又不缩减代码尺寸的方法 https://www.cnblogs.com/luolizhi/p/5737091.html
  • -nostartfiles 连接的使用不使用标准系统库。只有你指定的库才能够传递给连接器。不链接系统标准启动文件,而标准库文件仍然正常使用
  • -fno-asynchronous-unwind-tables 用来不生成CFI指令
  • -o 输出文件名
  • stribe 给文件脱裤子。具体就是从特定文件中剥掉一些符号信息和调试信息。 在strip之后, 文件变小了, 仍然可以执行, 这就就节省了很多空间。

最新文章

  1. AT&amp;T Assembly for Linux and Mac (sys_exit)
  2. [Android-2A] -仿IOS微信滑动删除_SwipeListview左滑删除例子
  3. 【转】SQL Server 查询表的记录数(3种方法,推荐第一种)
  4. hdu 4123 树形DP+RMQ
  5. 分享一个BUG
  6. overflow属性及其在ios下卡顿问题解决
  7. MySQL 索引 总结
  8. Eclipse安装Git插件以及通过Git导入华为软件开发云项目
  9. CountDownLatch使用场景
  10. Envoy 源码分析--network
  11. 设计在canal中的运用,看到随手记下
  12. scrapy 的log功能
  13. yii2记录
  14. 6. ASP.NET MVC 5.0 中的HTML Helper【HTML 帮助类】
  15. 【PMP】项目浮动的三种时间
  16. Visual Studio 2015新添加宏
  17. springMVC学习(注解实现依赖注入)
  18. siganl tappII的应用及MATLAB调用
  19. 如何让mysql按照两个或多个字段排序
  20. Google推荐的15条HTML 5代码军规----来看看你知道几个,我一个都不知道。。。

热门文章

  1. P1164 小A点菜(动态规划背包问题)
  2. 10 router
  3. The relationship between Sonarcube coverage and code branch
  4. 记一次奇怪的cookie丢失
  5. 第一课、python基础学习笔记
  6. adb无线连接android手机进行调式,无需获得root权限
  7. 第9课 - const 和 volatile分析
  8. Typora基础使用
  9. web网站——理论01
  10. domReady的理解