Docker基础知识 (28) - 在 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
            #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 程序镜像

    本文在 Ubuntu 20.04 下创建基于编译的 Go 程序镜像。

    Docker 版本: 20.10.7
    Docker Compose 版本: 2.6.1

 

    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。

    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
            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

 

 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

            warning: ./centos-release-7-5.1804.el7.centos.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
            Preparing...                          ################################# [100%]
            Updating / installing...
            1:centos-release-7-5.1804.el7.cento################################# [100%]
            warning: %post(centos-release-7-5.1804.el7.centos.x86_64) scriptlet failed, exit status 127


        $ sudo yum --installroot=/home/xxx/centos75/rootfs install yum --nogpgcheck

            ...

        $ ls ./rootfs

            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

            FROM scratch
            Add ./rootfs /
            WORKDIR /home/docker
            CMD /bin/bash    


     3) 创建 Linux 镜像,并运行容器

        $ cd ~/centos75

        # 创建镜像
        $ sudo docker build -t centos_local:7.5 .

            [+] Building 10.1s (6/6) FINISHED
            => [internal] load build definition from Dockerfile                                                                                     0.0s
            => => transferring dockerfile: 101B                                                            0.0s
            => [internal] load .dockerignore                                                               0.0s
            => => transferring context: 2B                                                                 0.0s
            => [internal] load build context                                                               4.4s
            => => transferring context: 453.26MB                                                           4.4s
            => [1/2] ADD ./rootfs /                                                                        2.2s
            => [2/2] WORKDIR /home/docker                                                                  0.3s
            => exporting to image                                                                          3.1s
            => => exporting layers                                                                         3.1s
            => => writing image sha256:72826446cccfd5363073ca1923c59cabf843278aa82d20ad720ac2d1cf458333    0.0s
            => => naming to docker.io/library/centos_local:7.5                                             0.0s

           
        $ docker images

            REPOSITORY          TAG      IMAGE ID       CREATED          SIZE
            centos_local        7.5      72826446cccf   4 minutes ago   452MB

        $ docker run -itd --name centos-local-1.0 centos_local:7.5

            3223f140df4d26e951dadf2d938fcc561f78ac7de86ca469e6c60853a03162fd

        $ docker exec -it centos-local-1.0 /bin/bash

            bash-4.2# cd /
            bash-4.2# ls
            bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

            bash-4.2# cat /etc/centos-release
            CentOS Linux release 7.5.1804 (Core)

 

 

posted @ 2023-03-15 18:14  垄山小站  阅读(2146)  评论(0编辑  收藏  举报