基于空镜像scratch创建一个新的Docker镜像
我们在使用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
- 三、【示例】基于scratch镜像构建一个大小为 0 的镜像
- 四、【示例】基于 scratch 构建一个 hello 镜像
- 五、补充1
- 六、【示例】基于 scratch 空镜像,使用 hello.go 创建一个新的Docker镜像
- 七、docker-library 的 hello-world
- 八、补充2
一、关于 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之后, 文件变小了, 仍然可以执行, 这就就节省了很多空间。
posted on 2019-12-03 09:40 morgan363 阅读(2415) 评论(0) 编辑 收藏 举报