使用scratch制作自定义最小镜像

针对如何制作尽量小的镜像问题,笔者在这里介绍一种用” 空镜像 “制作自定义最小镜像的方案。

1. 从石头缝里蹦出来的基础镜像?

这里请大家思考一个问题:docker hub 上的那些基础镜像,如 nginx、alpine、ubuntu,都是怎么来的?

事实上这些镜像都来源与一个空镜像,scratch。关于 scratch 的官方解释如下:

This image is most useful in the context of building base images (such as debian and busybox) or super minimal images (that contain only a single binary and whatever it requires, such as hello-world).

 

As of Docker 1.5.0 (specifically, docker/docker#8827), FROM scratch is a no-op in the Dockerfile, and will not create an extra layer in your image (so a previously 2-layer image will be a 1-layer image instead).

官方在这里给出了测试用的hello-world镜像 Dockerfile:

FROM scratc
COPY hello /
CMD ["/hello"]

该 Dockerfile 制作出来的镜像只有 1.84k。

使用 scratch 空镜像的本质是让你的程序只调用 host 主机的 Linux 内核部分的功能,而不依赖容器内的操作环境功能。

由于 host 主机的 Linux 内核部分对 Docker 容器是共享的,因此其 scratch 空镜像的大小可以认为近似为 0。

2. 以 Go 语言为例做一个最小镜像

做最小镜像的关键在于你要完全了解你的程序需要什么,而针对不同的语言,需要到的背景知识和技巧也不同。

这里笔者用较为熟悉的 Go 语言为例子做一个最小镜像,程序如下:

package main

import (
        "fmt"
)

func main() {
        fmt.Println("Hello")
}

2.1. 编译中技巧

如果希望你程序编译出来最小,并保证能够在 scratch 空镜像内运行,则需要在编译中用一些技巧:

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' hello.go

下面笔者将一一解析这条命令中的各个部分含义。

2.1.1. GOOS=linux GOARCH=amd64 含义

确保编译出来的程序可以运行在你的容器运行环境。笔者的容器运行环境为 amd64 linux 环境。

2.1.2 CGO_ENABLED=0 含义

该参数是确保你用到的 C 函数库包含到你的 Go run-time 中,程序运行时以静态方式内部调用

否则由于 scratch 空镜像内没有 C 函数库,Go 程序动态调用时会出错。

PS:Go 语言调用 C 函数库出错的现象也会出现在 alpine 中,这是因为 alpine 的 C 函数库是精简版的。

2.1.3. -ldflags ‘-w -s’ 含义

这部分参数是精简掉 Debug 信息,而让编译出来的程序更小。

-w 是精简掉DWARF

-s 是精简掉 debug symbol

2.2. Dockerfile 书写技巧

Dockerfile 的书写也需要一定的技巧,这里先给出笔者的 Dockerfile:

FROM scratch

LABEL authors="LEO"

ADD hello /

CMD [ "/hello" ]

2.2.1. RUN 命令失效

由于 scratch 空镜像中没有 sh 或 bash,想 mkdir、mv 等 shell 命令是无效的。

因此请在镜像外部把文件结构建立好,然后通过 ADD 或 COPY 命令拷贝到容器内。

2.2.2. 单层一次到位原则

尽管在使用 scratch 空镜像时无法使用 RUN,不大会违反这一原则,但笔者认为大家有必要知道这一原则。

Dokcer 建立镜像时会保留每一层的状态,这就导致当我们在 Dockerfile 写下下列命令时:

RUN mv /here/{1M文件} /there/{1M文件}

制作出来的镜像会大出 1M,这是因为/here 下的文件并没有因为 mv 命令而消失,它永远保存在 RUN 命令之前的那一层中了,RUN 命令这一层只是把它藏了起来。

2.3. 制作镜像查看成果

在包含 Dockerfile 和 hello 程序的位置执行下面的命令制作镜像:

docker build -t hello:latest .

执行下面的命令验证容器是否能正常运行:

docker run hello:latest

现在看一下我们镜像的大小和可执行程序的大小:

$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
hello                   latest              116f651aa3b5        6 seconds ago       356.2 kB

$ ll hello
-rwxr-xr-x 1 ... ... ... 348K Nov 12 18:51 hello

可以看到我们最终得到的镜像的大小就接近与可执行文件的大小。

3. 总结

使用 scratch 空镜像的优点:

  • 真正意义上的最小镜像,镜像大小约等于执行文件大小
  • 安全稳定,只需要关注维护程序本身和 Linux 内核安全更新即可
  • 最高效的资源利用,容器内没有任何多余程序或服务占用资源
  • 制作镜像方便快捷,由于 scratch 空镜像不需要 load 或 pull 就能使用,流水线上制作镜像更加方便快捷

缺点:

  • 由于没有 sh 或 bash,无法进入容器内进行交互式调试

PS:该问题实际可以通过构建自定义基础镜像解决。但笔者个人认为生产环境应该通过日志分析问题,而不是进入到容器内进行调试。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用 `scratch` 基础镜像制作自定义最小的JDK 8环境的 Dockerfile 示例: ```dockerfile # 使用 scratch 基础镜像 FROM scratch # 复制 JDK 8 的压缩包到镜像中 COPY jdk-8u291-linux-x64.tar.gz / # 解压 JDK 8 压缩包 RUN tar -xzf jdk-8u291-linux-x64.tar.gz && \ rm jdk-8u291-linux-x64.tar.gz # 设置环境变量 ENV JAVA_HOME=/jdk1.8.0_291 ENV PATH=$PATH:$JAVA_HOME/bin # 容器启动时运行的命令 CMD ["java", "-version"] ``` 解释每一条指令的作用: - `FROM scratch`:使用 `scratch` 基础镜像作为起点,表示不从任何镜像中继承文件系统,因此需要手动安装 JDK。 - `COPY jdk-8u291-linux-x64.tar.gz /`:将本地的 JDK 8 压缩包复制到镜像中的根目录。 - `RUN tar -xzf jdk-8u291-linux-x64.tar.gz && rm jdk-8u291-linux-x64.tar.gz`:解压 JDK 8 压缩包,并删除压缩包。 - `ENV JAVA_HOME=/jdk1.8.0_291`:设置 `JAVA_HOME` 环境变量,指向 JDK 的安装目录。 - `ENV PATH=$PATH:$JAVA_HOME/bin`:将 `JAVA_HOME/bin` 目录添加到系统的 `PATH` 环境变量中,这样在后面的命令中就可以直接使用 `java` 命令了。 - `CMD ["java", "-version"]`:容器启动时默认运行的命令,输出 Java 版本信息。 这个 Dockerfile 的作用是将 JDK 8 安装到镜像中,并设置环境变量。最后启动容器时会输出 Java 版本信息,用于验证 JDK 是否安装成功。 使用以下命令构建镜像: ``` docker build -t myjdk8:latest . ``` 然后可以运行容器来验证: ``` docker run --rm myjdk8:latest ``` 输出如下信息表示 JDK 已经安装成功: ``` java version "1.8.0_291" Java(TM) SE Runtime Environment (build 1.8.0_291-b10) Java HotSpot(TM) 64-Bit Server VM (build 25.291-b10, mixed mode) ``` 这样就完成了一个自定义最小的 JDK 8 环境的 Docker 镜像
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值