Docker容器技术之Dockerfile以From scratch创建镜像
在 Dockerfile 中以 scratch 为基础镜像 (FROM scratch)
通常使用 Docker 镜像时会以一个已存在的镜像为基础,在其上进行定制,这个已存在的镜像就是基础镜像。
在 DockerFile 中必须指定基础镜像,FROM 指令就是用于指定基础镜像,因此一个 Dockerfile 中FROM 是必备的指令,并且必须是第一条指令。
Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。在 Dockerfile 中以 scratch 为基础镜像 (FROM scratch),意味着不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
1. 创建基于静态编译的 C 程序镜像
本文在 Ubuntu 20.04 下创建基于静态编译的 C 程序镜像。
Docker 版本: 20.10.7
Docker Compose 版本: 2.6.1
1) C 程序
$ cd ~/gcc
$ vim hello.c
1 | #include <stdio.h> |
#gcc 静态编译
$gcc hello.c -static -o hello
$./hello
1 | Hello World! - C |
$ll -h hello
1 | -rwxrwxr-x 1 root root 852K hello |
2) 创建 Dockerfile
$ cd ~/gcc
$ vim Dockerfile
1 | FROM scratch |
注:scratch 空镜像中没有 sh 或 bash,无法 mkdir、mv 等 shell 命令是无效的,因此需要在镜像外部把文件目录结构建立好,然后通过 ADD 或 COPY 命令拷贝到容器内。
3) 创建 hello 镜像,并运行容器
$ cd ~/gcc
#创建镜像
$ docker build -t hello:1.0 .
1 | Step 1/3 : FROM scratch |
$ docker images
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
$ docker run –rm hello:1.0
1 | Hello World!- C |
注:以上 Dockerfile 制作出来的镜像是 872kB,hello 的二进制文件是 852kB。使用 scratch 空镜像的本质是让程序只调用 host 主机的 Linux 内核部分的功能,而不依赖容器内的操作环境功能。host 主机的 Linux 内核部分对 Docker 容器是共享的,因此其 scratch 空镜像的大小可以认为近似为 0。
2. 创建基于编译的 Go 程序镜像
本文在 Ubuntu 20.04 下创建基于编译的 Go 程序镜像。
Docker 版本: 20.10.7
Docker Compose 版本: 2.6.1
1) Go 程序
$ cd ~/go
$ vim test.go
1 | package main |
#编译
$ 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
1 | Hello world - Go |
$ ll -h test
1 | -rwxrwxr-x 1 root root 1.2M test |
2) 创建 Dockerfile
$ cd ~/go
$ vim Dockerfile
1 | FROM scratch |
3) 创建 test 镜像,并运行容器
$ cd ~/go
#创建镜像
$ docker build -t test:1.0 .
1 | Step 1/3 : FROM scratch |
$ docker images
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
$ docker run –rm test:1.0
1 | Hello world - Go |
3. 创建基于 Debian rootfs 的 Linux 镜像
由于 scratch 空镜像内,没有操作系统的根文件系统(rootfs),无法运行 sh 或 bash,无法进入容器内进行交互式调试。我们可以给基于 scratch 空镜像创建的镜像里,添加一个 rootfs。
Debian:https://www.debian.org/
Docker Debain: https://docker.debian.net/
Docker Debain GitHub: https://github.com/debuerreotype/docker-debian-artifacts
本文在 Ubuntu 20.04 下创建基于 Debian rootfs 的 Linux 镜像。
Docker 版本: 20.10.7
Docker Compose 版本: 2.6.1
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
1 | FROM scratch |
3) 创建 Linux 镜像,并运行容器
$ cd ~/debian
#创建镜像
$ docker build -t debian_local:1.0 .
1 | Step 1/4 : FROM scratch |
$ docker images
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
$ docker run -itd –name debian-local-1.0 debian_local:1.0
1 | 0e6e3704225c5723349e8d1cc07fefefdf93d205cdabc6f938f3726482b0d918 |
$ docker exec -it debian-local-1.0 /bin/bash
1 | root@0e6e3704225c:/home/docker# cd / |
4. 创建基于 CentOS rootfs 的 Linux 镜像
CentOS:https://www.centos.org/
CentOS Vault Mirror: https://vault.centos.org/
本文在 CentOS 7.9 下创建基于 CentOS rootfs 的 Linux 镜像。
Docker 版本: 20.10.7
Docker Compose 版本: 2.6.1
1) 制作 CentOS 7.5 rootfs
$ mkdir -p ~/centos75/rootfs
$ cd ~/centos75
$ rpm –root /home/xxx/centos75/rootfs –initdb # 设置 rpm 操作的根目录
#下载
$ wget https://vault.centos.org/7.5.1804/os/x86_64/Packages/centos-release-7-5.1804.el7.centos.x86_64.rpm
$ sudo rpm -ivh –nodeps –root /home/xxx/centos75/rootfs –package ./centos-release-7-5.1804.el7.centos.x86_64.rpm
1 | warning: ./centos-release-7-5.1804.el7.centos.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY |
$ sudo yum –installroot=/home/xxx/centos75/rootfs install yum –nogpgcheck
1 | ... |
$ ls ./rootfs
1 | bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var |
2) 创建 Dockerfile
$ cd ~/centos75
$ vim Dockerfile
1 | FROM scratch |
3) 创建 Linux 镜像,并运行容器
$ cd ~/centos75
#创建镜像
$ sudo docker build -t centos_local:7.5 .
1 | [+] Building 10.1s (6/6) FINISHED |
$ docker images
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
$ docker run -itd –name centos-local-1.0 centos_local:7.5
1 | 3223f140df4d26e951dadf2d938fcc561f78ac7de86ca469e6c60853a03162fd |
$ docker exec -it centos-local-1.0 /bin/bash
1 | bash-4.2# cd / |