我的docker随笔28:基于容器的升级方案实验
Posted 李迟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的docker随笔28:基于容器的升级方案实验相关的知识,希望对你有一定的参考价值。
本文涉及:
在容器化场合中,如何更快升级。涉及2方面:
docker镜像的设计。
升级方案。
docker镜像设计
充分利用docker镜像分层机制,减小升级的体积,减少流量消耗。
基础镜像设计
基础镜像可直接沿用官方的,也可自行制作,自行制作机动性强,可加定制内容,如nodejs依赖的node_modules目录,C++额外依赖的库(如cuda等)。
每次制作,在基础镜像基础上,而不是上一版本。因为docker是基础上一层制作的,即使版本间差别不大,但体积也只增不减。
分层测试示例如下。
制作二镜像,先做基础,在此基础上制作0.1版本。先下载基础镜像,再下载0.1版本。观察下载过程日志。
# 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服务即为典型者。这里反其义用之。即把文件放到镜像中,再把宿主机的运行环境映射到容器中,执行之。
示例:
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等基础镜像,未挂载命令所在目录时提示:
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/目录)后提示:
standard_init_linux.go:211: exec user process caused "no such file or directory"
failed to resize tty, using default size
似乎无法如此实施。此问题尚未研究深。
另,想过在容器A中使用容器B的目录,无果,或许无此应用方式。先挂载,再在容器中拷贝到宿主机,似乎可行,但多占用了存储空间,弃之。
另一个示例:
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触发,或手工触发,或发post请求),服务器收到升级事件,发指令到客户端,客户端执行docker pull
命令,判断其返回值,同时判断新镜像是否正常(如下发指令时带md5或crc,也可用docker自身的ID),一切正常启动,监控些许时间后(如30秒~1分钟),容器未退出,方为正常。
客户端主动升级,需判断是否连网,可选择深夜升级,或业务非繁忙时段。
冷更新
制作随系统启动的脚本,在脚本中先执行docker pull
,如果已是最新版本,会输出docker ID值,并带有Image is up to date for
字样,可判断之。否则,会拉取镜像,此时,需先停止容器并删除容器,再启动(是否直接重启容器即可实现,此刻未研究)。此方式,需要重启设备,如果启动时间长,则服务停止时间长。在时间间隔允许情况下,可采用此法,方便。
小结
本文镜像已修改,所示者非真实镜像。
附
一个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/
。本文涉及:
在容器化场合中,如何更快升级。涉及2方面:
docker镜像的设计。
升级方案。
docker镜像设计
充分利用docker镜像分层机制,减小升级的体积,减少流量消耗。
基础镜像设计
基础镜像可直接沿用官方的,也可自行制作,自行制作机动性强,可加定制内容,如nodejs依赖的node_modules目录,C++额外依赖的库(如cuda等)。
每次制作,在基础镜像基础上,而不是上一版本。因为docker是基础上一层制作的,即使版本间差别不大,但体积也只增不减。
分层测试示例如下。
制作二镜像,先做基础,在此基础上制作0.1版本。先下载基础镜像,再下载0.1版本。观察下载过程日志。
# 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服务即为典型者。这里反其义用之。即把文件放到镜像中,再把宿主机的运行环境映射到容器中,执行之。
示例:
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等基础镜像,未挂载命令所在目录时提示:
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/目录)后提示:
standard_init_linux.go:211: exec user process caused "no such file or directory"
failed to resize tty, using default size
似乎无法如此实施。此问题尚未研究深。
另,想过在容器A中使用容器B的目录,无果,或许无此应用方式。先挂载,再在容器中拷贝到宿主机,似乎可行,但多占用了存储空间,弃之。
另一个示例:
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触发,或手工触发,或发post请求),服务器收到升级事件,发指令到客户端,客户端执行docker pull
命令,判断其返回值,同时判断新镜像是否正常(如下发指令时带md5或crc,也可用docker自身的ID),一切正常启动,监控些许时间后(如30秒~1分钟),容器未退出,方为正常。
客户端主动升级,需判断是否连网,可选择深夜升级,或业务非繁忙时段。
冷更新
制作随系统启动的脚本,在脚本中先执行docker pull
,如果已是最新版本,会输出docker ID值,并带有Image is up to date for
字样,可判断之。否则,会拉取镜像,此时,需先停止容器并删除容器,再启动(是否直接重启容器即可实现,此刻未研究)。此方式,需要重启设备,如果启动时间长,则服务停止时间长。在时间间隔允许情况下,可采用此法,方便。
小结
本文镜像已修改,所示者非真实镜像。
附
一个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/
。
以上是关于我的docker随笔28:基于容器的升级方案实验的主要内容,如果未能解决你的问题,请参考以下文章