本文记录制作一个镜像的过程,先构建可运行静态程序的镜像,以此为基础,构建一个golang语言编写的web服务器,可获取容器的主机、内核版本等信息。该镜像可用于 k8s 和 KubeEdge 群集测试。
环境说明 安装docker,登陆到dockerhub。 安装golang编译器,用于编译源码。 安装 qemu,用于在 x86 平台上运行 arm 版本容器。如无此需求,可忽略。
1 sudo apt install qemu-user-static
基于manifest制作镜像,适用于 x86 和 arm 平台。 注意,这里说的 x86,实际是64位系统,应该称为amd64,说 x86 仅是习惯而已,非错误。但 arm 平台,是指 32 位系统,因笔者暂无 64 位系统,后续再完善。
镜像设计 如下:
1 2 3 4 5 6 7 latelee/busybox 这是对外使用的镜像名称,根据不同平台自动匹配下载 latelee/busybox-arm latelee/busybox-amd64 latelee/webgin latelee/webgin-arm latelee/webgin-amd64
基础镜像 官方busybox支持众多平台,但默认的版本没有一些依赖文件。但glibc版本有。 下面从实践角度描述如何制作。
制作x86平台基础镜像 下载:
制作如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 运行: docker run -itd --name busybox busybox 创建目录: docker exec -it busybox mkdir -p /lib/x86_64-linux-gnu /lib64 拷贝运行库、链接器: docker cp -a /lib/x86_64-linux-gnu/libpthread.so.0 busybox:/lib/x86_64-linux-gnu docker cp -a /lib/x86_64-linux-gnu/libpthread-2.23.so busybox:/lib/x86_64-linux-gnu docker cp -a /lib/x86_64-linux-gnu/libc-2.23.so busybox:/lib/x86_64-linux-gnu docker cp -a /lib/x86_64-linux-gnu/libc.so.6 busybox:/lib/x86_64-linux-gnu docker cp -a /lib64/ld-linux-x86-64.so.2 busybox:/lib64/ docker cp -a /lib/x86_64-linux-gnu/ld-2.23.so busybox:/lib/x86_64-linux-gnu/ 保存为镜像 docker commit busybox latelee/busybox-amd64 测试(预期结果有上述文件输出) docker run -it --rm latelee/busybox-amd64 ls -lh /lib/x86_64-linux-gnu /lib64
提交:
1 docker push latelee/busybox-amd64
制作arm平台基础镜像 在一块安装了 docker 的 arm 板子上执行:
注:该命令与上述完全相同,因其系统不同,dockerhub自动匹配到合适的并下载。在真实机器上是为了确保镜像的可靠性。
制作如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 运行: docker run -itd --name busybox busybox 创建目录: docker exec -it busybox mkdir -p /usr/lib/ /lib 拷贝运行库、链接器: docker cp /lib/ld-2.25.so busybox:/lib/ docker cp /lib/ld-linux-armhf.so.3 busybox:/lib/ docker cp /usr/lib/libpthread-2.25.so busybox:/usr/lib docker cp /usr/lib/libpthread.so.0 busybox:/usr/lib docker cp /usr/lib/libc.so.6 busybox:/usr/lib/ docker cp /usr/lib/libc-2.25.so busybox:/usr/lib/ 保存为镜像 docker commit busybox latelee/busybox-arm 测试(预期结果有上述文件输出) docker run -it --rm latelee/busybox-arm ls -lh /usr/lib/ /lib
提交:
1 docker push latelee/busybox-arm
使用glibc版本 直接使用busybox:glibc版本制作,无法额外拷贝文件。在 x86 上执行:
1 2 docker pull busybox:glibc docker tag busybox:glibc latelee/busybox-amd64
在 arm 上执行:
1 2 docker pull busybox:glibc docker tag busybox:glibc latelee/busybox-arm
注:笔者使用前面小节的方法,glibc版本可能后续更新。
在 x86 上运行 arm 版本容器 有时不方便在 arm 板子上运行,则可以在 x86 上模拟之。 挂载 qemu-arm-static 文件:
1 docker run -it --rm -v /usr/bin/qemu-arm-static:/usr/bin/qemu-arm-static latelee/busybox-arm ls -lh /usr/lib/ /lib
另一方法,运行 qemu-user-static 容器,再运行 arm 容器:
1 2 docker run --rm --privileged multiarch/qemu-user-static --reset -p yes docker run -it --rm latelee/busybox-arm ls -lh /usr/lib/ /lib
多平台支持 技术要点:开启docker实验功能,预先提交不同平台的镜像到dockerhub上,创建manifest,推送。
1 2 3 4 5 6 7 8 9 10 11 12 export DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create latelee/busybox latelee/busybox-amd64 latelee/busybox-arm docker manifest annotate latelee/busybox latelee/busybox-amd64 --os linux --arch amd64 docker manifest annotate latelee/busybox latelee/busybox-arm --os linux --arch arm 查看: docker manifest inspect latelee/busybox 推送: docker manifest push latelee/busybox
webgin webgin 是指用 gin 框架编写的 web 服务,开放80端口,可输出主机信息。其构建方式与上述类似,不再赘述。webgin.go源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package main import ( "fmt" "runtime" "os" "time" "github.com/gin-gonic/gin" "net/http" ) // uname /* #include <stdio.h> #include <sys/utsname.h> char* GetName() { arch := fmt.Sprintf("arch: %s os: %s hostname: %s\r\n", runtime.GOARCH, runtime.GOOS, hostname) struct utsname myname; static char buffer[128] = {0}; uname(&myname); snprintf(buffer, 128, "uname: %s %s %s %s %s\r\n", myname.sysname, myname.nodename, myname.release, myname.version, myname.machine); return buffer; } */ import "C" var version = "v1.0" func myIndex (c *gin.Context) { uname := C.GetName() name := C.GoString(uname) hostname, _ := os.Hostname() arch := "arch: " + runtime.GOARCH + " os: " + runtime.GOOS + " hostname: " + hostname + "\r\n"; timeStr := "Now: " + time.Now().Format("2006-01-02 15:04:05") + "\r\n" c.String(http.StatusOK, "Hello World " + version + "\r\n" + arch + name + timeStr) } func main(){ router := gin.Default() router.GET("/", myIndex) fmt.Println("gin server start...") router.Run(":80") }
构建脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #!/bin/sh export GOARCH=amd64 export GOOS="linux" export GOARM= export CGO_ENABLED=1 export CC=gcc GO111MODULE=off go build strip webgin docker build -t latelee/webgin-amd64 . -f Dockerfile export GOARCH=arm export GOOS="linux" export GOARM=7 export CGO_ENABLED=1 export CC=arm-linux-gnueabihf-gcc GO111MODULE=off go build arm-linux-gnueabihf-strip webgin docker build -t latelee/webgin-arm . -f Dockerfile.arm
dockerfile:
1 2 3 4 5 6 7 8 9 From latelee/busybox-amd64 LABEL maintainer="Late Lee" COPY webgin / EXPOSE 80 CMD ["/webgin"]
运行:
1 docker run -it --name webgin --rm -p 80:80 latelee/webgin
测试:
1 2 3 4 5 # curl localhost:80 Hello World v1.0 arch: amd64 os: linux hostname: 60acfd65857a uname: Linux 60acfd65857a 4.4.0-174-generic #204-Ubuntu SMP Wed Jan 29 06:41:01 UTC 2020 x86_64 Now: 2020-03-26 23:10:36
依赖文件确认 1 2 3 4 5 6 7 8 9 缺少链接器: / # ./webgin sh: ./webgin: not found 其它: / # /webgin /webgin: error while loading shared libraries: libpthread.so.0: cannot open shared object file: No such file or directory / # /webgin /webgin: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory
附 如果官方 dockerhub 速度慢,可选用阿里云容器镜像服务。其企业版本已于2020年3月中旬商业化,个人版不太清楚。 登陆阿里云仓库:sudo docker login --username=li@latelee.org registry.cn-hangzhou.aliyuncs.com
。 已完成版本:
1 2 registry.cn-hangzhou.aliyuncs.com/latelee/webgin 版本:v1.0 v1.1 v1.2 registry.cn-hangzhou.aliyuncs.com/latelee/busybox