玩转 Docker基础系列28:从零开始 - Dockerfile中使用scratch基础镜像(FROM scratch)
最编程
2024-07-24 08:48:47
...
通常使用 Docker 镜像时会以一个已存在的镜像为基础,在其上进行定制,这个已存在的镜像就是基础镜像。
在 DockerFile 中必须指定基础镜像,FROM 指令就是用于指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。
Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。在 Dockerfile 中以 scratch 为基础镜像 (FROM scratch),意味着不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
1. 创建基于静态编译的 C 程序镜像
1) C 程序
$ cd ~/gcc
$ vim hello.c
#include <stdio.h> int main() { puts("Hello World!- C"); return 0; }
# gcc 静态编译
$ gcc hello.c -static -o hello
$ ./hello
Hello World! - C
$ ll -h hello
-rwxrwxr-x 1 root root 852K hello
2) 创建 Dockerfile
$ cd ~/gcc
$ vim Dockerfile
FROM scratch
COPY hello /
CMD ["/hello"]
注:scratch 空镜像中没有 sh 或 bash,无法 mkdir、mv 等 shell 命令是无效的,因此需要在镜像外部把文件目录结构建立好,然后通过 ADD 或 COPY 命令拷贝到容器内。
3) 创建 hello 镜像,并运行容器
$ cd ~/gcc
# 创建镜像
$ docker build -t hello:1.0 .
Step 1/3 : FROM scratch ---> Step 2/3 : COPY hello / ---> bb893abeef08 Step 3/3 : CMD ["/hello"] ---> Running in c31e62693472 Removing intermediate container c31e62693472 ---> cebea71dcbe0 Successfully built cebea71dcbe0 Successfully tagged hello:1.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 1.0 cebea71dcbe0 38 seconds ago 872kB
$ docker run --rm hello:1.0
Hello World!- C
注:以上 Dockerfile 制作出来的镜像是 872kB,hello 的二进制文件是 852kB。使用 scratch 空镜像的本质是让程序只调用 host 主机的 Linux 内核部分的功能,而不依赖容器内的操作环境功能。host 主机的 Linux 内核部分对 Docker 容器是共享的,因此其 scratch 空镜像的大小可以认为近似为 0。
2. 创建基于编译的 Go 程序镜像
1) Go 程序
$ cd ~/go
$ vim test.go
package main import "fmt" func main() { fmt.Println("Hello world - Go") }
# 编译
$ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' test.go
参数说明:
GOOS=linux GOARCH=amd64 表示确保编译出来的程序可以运行在 amd64 linux 环境;
CGO_ENABLED=0 表示确保用到的 C 函数库包含到 Go run-time 中,程序运行时以静态方式内部调用。否则,由于 scratch 空镜像内没有 C 函数库,Go 程序动态调用时会出错;
-ldflags '-w -s' 表示排除 Debug 信息,让编译出来的程序更小。-w 是排除 DWARF,-s 是排除 debug symbol;
注:Go 语言调用 C 函数库出错的现象也会出现在 alpine 中,这是因为 alpine 的 C 函数库是精简版的。
$ ./test
Hello world - Go
$ ll -h test
-rwxrwxr-x 1 root root 1.2M test
2) 创建 Dockerfile
$ cd ~/go
$ vim Dockerfile
FROM scratch
COPY test /
CMD ["/test"]
3) 创建 test 镜像,并运行容器
$ cd ~/go
# 创建镜像
$ docker build -t test:1.0 .
Step 1/3 : FROM scratch ---> Step 2/3 : COPY test / ---> cd67f4bfb544 Step 3/3 : CMD ["/test"] ---> Running in c3cf81ea01e4 Removing intermediate container c3cf81ea01e4 ---> 535665c081c8 Successfully built 535665c081c8 Successfully tagged test:1.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 1.0 535665c081c8 29 seconds ago 1.18MB
$ docker run --rm test:1.0
Hello world - Go
3. 创建基于 Debian rootfs 的 Linux 镜像
由于 scratch 空镜像内,没有操作系统的根文件系统(rootfs),无法运行 sh 或 bash,无法进入容器内进行交互式调试。我们可以给基于 scratch 空镜像创建的镜像里,添加一个 rootfs。
Docker Debain: https://docker.debian.net/
Docker Debain GitHub: https://github.com/debuerreotype/docker-debian-artifacts
1)下载 rootfs
这里选用了 https://docker.debian.net/ 页面上的 debian:bookworm-20230227,amd64 链接跳转到页面 https://github.com/debuerreotype/docker-debian-artifacts/tree/fe5738569aad49a97cf73183a8a6b2732fe57840/bookworm。
下载 rootfs.tar.xz 文件到 ~/debian 目录下,文件大小 29.66MB。
2) 创建 Dockerfile
$ cd ~/debian
$ vim Dockerfile
FROM scratch Add rootfs.tar.xz / WORKDIR /home/docker CMD /bin/bash
3) 创建 Linux 镜像,并运行容器
$ cd ~/debian
# 创建镜像
$ docker build -t debian_local:1.0 .
Step 1/4 : FROM scratch ---> Step 2/4 : Add rootfs.tar.xz / ---> 806b049c2199 Step 3/4 : WORKDIR /home/docker ---> Running in e4c0defe9fd3 Removing intermediate container e4c0defe9fd3 ---> a2bea1387c68 Step 4/4 : CMD /bin/bash ---> Running in ea20508cb334 Removing intermediate container ea20508cb334 ---> fd4fa7caba5d Successfully built fd4fa7caba5d Successfully tagged debian_local:1.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
debian_local 1.0 fd4fa7caba5d 38 seconds ago 116MB
$ docker run -itd --name debian-local-1.0 debian_local:1.0
0e6e3704225c5723349e8d1cc07fefefdf93d205cdabc6f938f3726482b0d918
$ docker exec -it debian-local-1.0 /bin/bash
root@0e6e3704225c:/home/docker# cd / root@0e6e3704225c:/# ls bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var root@0e6e3704225c:/# cat /etc/issue Debian GNU/Linux bookworm/sid \n \l
原文地址:https://www.cnblogs.com/tkuang/p/17219527.html