我的docker随笔28:基于容器的升级方案实验

本文涉及:
在容器化场合中,如何更快升级。涉及2方面:
docker镜像的设计。
升级方案。

docker镜像设计

充分利用docker镜像分层机制,减小升级的体积,减少流量消耗。

基础镜像设计

基础镜像可直接沿用官方的,也可自行制作,自行制作机动性强,可加定制内容,如nodejs依赖的node_modules目录,C++额外依赖的库(如cuda等)。
每次制作,在基础镜像基础上,而不是上一版本。因为docker是基础上一层制作的,即使版本间差别不大,但体积只增不减。

分层测试示例如下。
制作二镜像,先做基础,在此基础上制作0.1版本。先下载基础镜像,再下载0.1版本。观察下载过程日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# docker pull latelee/nodejs_test:base

base: Pulling from latelee/nodejs_test
3cfb62949d9d: Pull complete
34aecfb75a58: Pull complete
3d83db6658b4: Pull complete
c94ba0b8da75: Pull complete
d7bff1b55288: Pull complete
Digest: sha256:e17eefc913b842d944702fa008c3178e5f5cf6753b102954ee2426ff548aa6c4
Status: Downloaded newer image for latelee/nodejs_test:base

# docker pull latelee/nodejs_test:0.1

0.1: Pulling from latelee/nodejs_test
3cfb62949d9d: Already exists
34aecfb75a58: Already exists
3d83db6658b4: Already exists
c94ba0b8da75: Already exists
d7bff1b55288: Already exists
7c1d066698dd: Pull complete
Digest: sha256:f17bec3186ca681be8815adda298b217b6d0c1bcb248a0f57da4b03a8197db43
Status: Downloaded newer image for latelee/nodejs_test:0.1

第二次下载的,许多层已经存在了,无须再下载,故速度快。在磁盘上,也是重用镜像的,所以也能节省空间。

其它测试:使用大文件,在基础镜像上制作0.1版本,再在0.1版本上制作0.2(只做小修改),另再以0.2版本的文件,基于基础镜像做0.11版本,观察三者下载过程。

宿主机映射方案

一般地,容器作为运行环境,宿主机保存数据文件。如web服务即为典型者。这里反其义用之。即把文件放到镜像中,再把宿主机的运行环境映射到容器中,执行之。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
docker run -it --rm -v /home/latelee/nodejs/nodejs_test/node_modules:/home/node/node_modules \
-v /usr/local/bin/:/home/local/bin \
-v /lib/:/home/lib \
-v /usr/lib/:/home/usr/lib \
nodejsdata sh


export PATH=$PATH:/home/local/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/usr/lib

/ # node -v
v10.20.1
/ # cd /home/node/
/ # node koa_test.js
Running a koa server at localhost: 4000


docker run -it --rm -v /home/latelee/nodejs/nodejs_test/node_modules:/home/node/node_modules \
-v /usr:/usr \
-v /bin:/bin \
-v /lib/:/lib \
nodejs_test sh

尝试使用scratch制作,即只把相关的js文件拷贝到镜像中,不基于busybox等基础镜像,未挂载命令所在目录时提示:

1
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown.

挂载(如/bin/目录)后提示:

1
2
standard_init_linux.go:211: exec user process caused "no such file or directory"
failed to resize tty, using default size

似乎无法如此实施。

另,想过在容器A中使用容器B的目录,无果,或许无此应用方式。先挂载,再在容器中拷贝到宿主机,似乎可行,但多占用了存储空间,弃之。

另一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
docker run -itd --rm --name nodejsapp -p 3000:3000 -v /home/node/node_modules/:/home/node/node_modules \
-v /usr/:/usr \
-v /lib/:/lib \
registry.cn-hangzhou.aliyuncs.com/latelee/nodejsdata

提供的:
docker run -itd --rm --name nodejsapp -p 3000:3000 \
-v /mnt/data:/mnt/data \
-v /home/node/node_modules/:/home/node/node_modules \
-v /usr/:/usr \
-v /lib/:/lib \
nodejsdata

在 arm 上验证通过。前提:arm 板子安装了nodejs环境以及依赖的node_modules。将应用代码做成镜像,下载并运行之。为减小操作,直接挂载成相同目录。

升级方案

目前只有构思,未动手编码。
云主机为服务器,终端/板子为客户端,两者保持长连接,可用websocket实现。
需要升级时(如CICD触发,或手工触发),服务器发指令到客户端,客户端执行docker pull命令,判断其返回值,同时判断新镜像是否正常(如下发指令时带md5或crc,也可用docker自身的ID)。
客户端主动升级,可根据是否连网,深夜升级。

小结

本文镜像已修改,所示者非真实镜像。

一个builtroot更新node的记录。

前端所所需版本为10.15,builtroot默认是8.15。
1、下载alpine版本镜像,取node,其库为musllibc,目标板库为glibc,运行提示Not found,原由是链接器不同。失败。下载armv7版本的镜像,从中取之。体积30MB余,较大。

2、观察buitroot配置,确认node为8.15,临时改配置,更新node为10.15,编译失败,重新改为8.15,成功,猜测openssl问题。查找官方代码路径https://git.busybox.net/buildroot/tree/package/nodejs/nodejs.mk?h=2019.05,选择不同版本号,发现 201905 版本较近,从中取配置文件,替换/新加原有目录。其依赖https://git.busybox.net/buildroot/tree/package/nghttp2?h=2019.05。make时下载慢,用云主机下载,再用scp拷贝之。编译依然失败,从提示日志看,还是openssl。想到将buitroot升级到201905,然又与内核等有关联,成本大,弃之。
3、恢复用用8.15编译,暂不烧写镜像(一是仅验证,二是耗时),拷贝/usr/bin/node到目标板,拷贝其依赖库libhttp_parser、libuv、libcares三个库。结果:应用程序依赖的sqlite为/sqlite3/lib/binding/node-v64-linux-arm,但是该版本的node认为依赖的是sqlite3/lib/binding/node-v57-linux-arm/node_sqlite3.node,不兼容,失败。

4、使用官方预编译版本,地址https://nodejs.org/dist/v10.15.0/