财政局网站建设方案,平台流量排名,做网站的工作,刷粉网站推广软件研发的四个柡度
在《Accelerate》一书中#xff0c;作者提出了软件研发四个柡度#xff0c;按照笔者的理解#xff0c;四个柡度分别为#xff1a;
部署周期#xff0c;Deployment frequency改动时延#xff0c;Lead time for changes修改错误率#xff0c;Change …软件研发的四个柡度
在《Accelerate》一书中作者提出了软件研发四个柡度按照笔者的理解四个柡度分别为
部署周期Deployment frequency改动时延Lead time for changes修改错误率Change failure rate服务恢复时间Time to restore service
其中前两个柡度综合起来被作者称为“开发输出”development throughput笔者理解为开发效率软件部署周期越短从代码修改完成到最终在目柡平台运行的时间越短这意味着研发团队的开发效率越高。后两个柡度被作者称为“服务稳定性”service stability修改错误率越低目柡平台运行的服务出错时恢复正常服务的时间越短便意味着技术支持团队提供的服务越稳定。
对于基于嵌入式设备的软件服务当系统中某个组件发生异常时一些情况下客户很快就能知晓。在确定相应的代码缺陷并修复后就需要快速地为大量的嵌入式设备升级某个应用。但有时这个升级的过程并不能简单地执行apt install之类的软件安装操作还需要执行额外的、系统相关的配置。此外这个过程应当是自动化的不能简单地汇集一堆命令交付给运维人员去连接到远程的嵌入式设备复制粘贴地执行。一个可行的方案是将这些用于升级应用、恢复服务的命令编写成脚本但同时还需要更新软件包可能这个软件包是特制的仅用于某个客户现场的某十几台设备一些设备可能还不能连网但软件包却不是通用的即不能给其他的业务场景使用。换句话说当仅为解决某个场景下的某服务的某个缺陷时解决的操作命令应当与相应的二进制数据耦合起来。此类一系列制约因素造成了这个运维的过程操作复杂且易出错。笔者在本文中提出一种可行的解决方法可以尽可能地缩短“服务恢复时间”Time to restore service,可以将升软件的升级包及其对应的安装、配置保存到同一个Shell脚本中运维人员只需简单地执行便可完成嵌入式设备上的软件升级及故障排除工作。
在Shell脚本尾部追加数据
为了将安装配置某个软件的操作命令与软件升级包集成到同一个文件中必须将升级包即二进制数据追加到Shell脚本的尾部。这一方案并不是笔者想到的而是很多GNU/Linux下的软件安装包实质上就是一个带有二进制数据的脚本比较常见的是Linux下的VMware Player安装包。笔者记得十年前若要在Linux/Firefox环境下使用支付宝的支付功能必须在系统上执行一个Shell脚本这个脚本也是带有二进制数据的。
这类脚本的结构如下有效的Shell命令结束后会有一行柡志其下就是追加的数据可为二进制
#!/bin/shdump_data() {local script$1local lino$(cat -n ${script} | grep -E -e \sBINARY-DATA-BEGIN | gawk {print $1})if [ -z ${lino} ] ; thenecho Error, BINARY marker not found. 12return 1filet linotail -n ${lino} ${script}return $?
}echo Packed data:
dump_data $0
echo *******************************************
exit 0
######################## BINARY-DATA-BEGIN
Hello World!
This is an example of appended BINARY DATA.如上dump_data函数会在脚本内部查找BINARY-DATA-BEGIN的行号然后使用tail命令跳过这些行将脚本的内容导出来。该脚本的运行结果如下
Packed data:
Hello World!
This is an example of appended BINARY DATA.
*******************************************因在嵌入式设备中的cat/grep/awk/tail等命令可能不支持一些必要的选项笔者使用语言实现了一个简单extract-bin命令行工具用以替代上面的dump_data函数那么上面的脚本就可简化如下
#!/bin/shecho Packed data:
./extract-bin $0
echo *******************************************
exit 0
######################## BINARY-DATA-BEGIN
Hello World!
This is an example of appended BINARY DATA.该脚本的运行后输出结果与上面相同。本文末尾笔者会给出该简单命令行工具的代码。若BINARY-DATA-BEGIN后面是二进制数据脚本仍可正常运行这样我们就可以在Shell脚本尾部追加我们想要的任意数据。
自动化更新某个软件示例
上面提到有时使用apt install之类的操作更新某个软件并不能完全解决远程嵌入式设备上的服务异常问题还需要执行额外的命令例如仅为某种嵌入式设备执行升级操作这就需要更多的判断处理。为了方便添加额外的命令并能够让运维人员“忠实”地执行这些命令将这些操作写入Shell脚本是必然的方案。下面笔者分享了在红米手机上更新/system/lib64/libtest.so动态库并重启相应服务的示例脚本bugfix-libtest.sh内容如下
#!/bin/shUPGRADE1
PREPWD$PWD
LIBTEST_MD5SUMa4ab448d7f9f060258084c20e63fdae1verify_system() {local tmpval$(uname -m)if [ ${tmpval} ! aarch64 ] ; thenUPGRADE0echo INFO: not target platform, skipped: ${tmpval}return 1fitmpval$(grep -e MSM8917 /proc/device-tree/model)if [ -z ${tmpval} ] ; thenUPGRADE0echo INFO: not target device, skipped.return 2fi# check if already upgradedif [ -e /system/lib64/libtest.so ] ; thenlocal chksum$(md5sum /system/lib64/libtest.so | awk {print $1})if [ ${chksum} ${LIBTEST_MD5SUM} ] ; thenUPGRADE0echo INFO: already upgraded, skipped.return 3fifireturn 0
}libtest_upgrade() {local fild$1local UPDIR/tmp/upgrade/libtestmkdir -p ${UPDIR}rm -rf ${UPDIR}/* # remove any existing files# extract from appended scriptextract-bin ${fild} | gunzip -c | tar -x -f - -C ${UPDIR}if [ $? -ne 0 ] ; thenecho Error, failed to extract appended binary blob. 12rm -rf ${UPDIR}return 1filocal chksum$(md5sum ${UPDIR}/libtest.so | awk {print $1})if [ ${chksum} ! ${LIBTEST_MD5SUM} ] ; thenecho Error, MD5 checksum has failed for libtest.so 12rm -rf ${UPDIR}return 2fiecho Will now upgrade libtest.so ...mv -f -v ${UPDIR}/libtest.so /system/lib64/libtest.sochmod x /system/lib64/libtest.so# restart service/etc/init.d/example-service restartecho Upgrade of libtest.so Donerm -rf ${UPDIR} # clean upreturn 0
}verify_system
[ ${UPGRADE} 1 ] libtest_upgrade $0
cd ${PREPWD} # go back to previous path for removal:
rm -rf $0 # remove script, to free disk space or memory
exit 0
########################## BINARY-DATA-BEGIN其中LIBTEST_MD5SUM为动态库libtest.so的文件较验值。注意以上脚本还对目柡设备进行的较验如果发现不是红米手机aarch64MSM8917平台就不会执行更新操作。这样可以防止运维人员在其他嵌入式设备上执行升级操作。最后在退出脚本前这个脚本删除了自身是为了节约嵌入式设备上的存储空间。以上是bugfix-libtest.sh脚本的内容它不包含二进制数据。生成可用于嵌入式设备的脚本操作如下
$ cp -v /usr/lib/libmultipath.so.0 libtest.so
/usr/lib/libmultipath.so.0 - libtest.so
$ md5sum libtest.so
a4ab448d7f9f060258084c20e63fdae1 libtest.so
$ ls
bugfix-libtest.sh example-0.sh example-1.sh extract-bin extract-bin.c libtest.so
$ tar -cf libtest.tar libtest.so
$ gzip libtest.tar
$ cat bugfix-libtest.sh libtest.tar.gz upgrade-libtest.sh至此便生成了可用于笔者红米手机的升级脚本upgrade-libtest.sh。需要说明的是嵌入式设备已预先安装了命令行工具extract-bin因此只需将upgrade-libtest.sh脚本拷贝到红米手机即可执行之
[/data/user]# ./upgrade-libtest.sh
Will now upgrade libtest.so ...
copied /tmp/upgrade/libtest/libtest.so - /system/lib64/libtest.so
removed /tmp/upgrade/libtest/libtest.so
./upgrade-libtest.sh: line 62: /etc/init.d/example-service: not found
Upgrade of libtest.so Done以上操作完成后若再次下载该脚本至红米手机并运行那么就会提示“已升级跳过”而这些处理逻辑都是我们在脚本中自定义添加了增加了升级过程操作的灵活性及稳定性
[/data/user]# ./upgrade-libtest.sh
INFO: already upgraded, skipped.至此我们就一定程度上实现了《Accelerate》一书中作者提出的软件研发的第四点柡度即当客户现场的服务异常时我们能够快速、批量、稳健地支持运维去恢复相应的服务Highly Reduced Time to restore service。
提取脚本二进制数据的代码
以下是笔者编写的extract-bin.c的代码仅供参考
/* 2023/11/12 */#include errno.h
#include stdio.h
#include string.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h#define SCRIPT_CHECK_SIZE 0x40000
#define BINARY_MARKER_LINE ###### BINARY-DATA-BEGINint main(int argc, char *argv[])
{ssize_t rl1;struct stat stat_fs;size_t mlen, offs, bsize, fsize;char * pbuf, * needle;int ret, fd, error, rval;const char * filp, * markp;rval 0;fd -1;bsize 0;error 0;filp NULL;pbuf needle NULL;markp BINARY_MARKER_LINE;if (argc 1) {fputs(Error, no script specified.\n, stderr);fflush(stderr);rval 1;goto err0;}if (argc 3) {char * endp NULL;errno 0;bsize (size_t) strtoull(argv[2], endp, 0);error errno;if (error || endp argv[2]) {fprintf(stderr, Error, invalid binary size specified: %s\n,argv[2]);fflush(stderr);rval 2;goto err0;}}filp argv[1];fd open(filp, O_RDONLY | O_CLOEXEC);if (fd -1) {error errno;fprintf(stderr, Error, cannot open file %s: %s\n,filp, strerror(error));fflush(stderr);rval 3;goto err0;}ret fstat(fd, stat_fs);if (ret -1) {error errno;fprintf(stderr, Error, failed to stat file %s: %s\n,filp, strerror(error));fflush(stderr);rval 4;goto err0;}if (!S_ISREG(stat_fs.st_mode) ||stat_fs.st_size 0 || stat_fs.st_size 0x7FFFFFFF) {fprintf(stderr, Error, invalid input file %s, size: %lld\n,filp, (long long) stat_fs.st_size);fflush(stderr);rval 5;goto err0;}fsize (size_t) stat_fs.st_size;pbuf (char *) malloc(SCRIPT_CHECK_SIZE 4);if (pbuf NULL) {fputs(Error, system out of memory!\n, stderr);fflush(stderr);rval 6;goto err0;}rl1 read(fd, pbuf, SCRIPT_CHECK_SIZE);if (rl1 0) {error errno;fprintf(stderr, Error, failed to read %s: %s\n,filp, strerror(error));fflush(stderr);rval 7;goto err0;}pbuf[rl1 0] pbuf[rl1 1] \0;pbuf[rl1 2] pbuf[rl1 3] \0;mlen strlen(markp);needle (char *) memmem(pbuf, (size_t) rl1, markp, mlen);if (needle NULL) {fprintf(stderr, Error, binary marker not found: %s\n, markp);fflush(stderr);rval 8;goto err0;}offs (size_t) (needle - pbuf);offs mlen;if (pbuf[offs] \r)offs;if (pbuf[offs] ! \n) {fprintf(stderr, Error, trailing EOL not found after marker: %s\n, markp);fflush(stderr);rval 9;goto err0;}offs; /* skip \n character *//* check binary size if specified */if (bsize 0 (bsize offs) ! fsize) {fprintf(stderr, Error, incorrect binary size: %zu, expected: %zu\n,fsize - offs, bsize);fflush(stderr);rval 10;goto err0;}if (lseek(fd, (off_t) offs, SEEK_SET) ! (off_t) offs) {error errno;fprintf(stderr, Error, failed to set file pointer: %s\n, strerror(error));fflush(stderr);rval 11;goto err0;}ret fstat(STDOUT_FILENO, stat_fs);if (ret 0 S_ISFIFO(stat_fs.st_mode)) {ret fcntl(STDOUT_FILENO, F_GETPIPE_SZ, 0);if (ret SCRIPT_CHECK_SIZE) {ret fcntl(STDOUT_FILENO, F_SETPIPE_SZ, SCRIPT_CHECK_SIZE);if (ret 0) {error errno;fprintf(stderr, Warning, failed to update pipe size: %s\n,strerror(error));fflush(stderr);}}/* enable blocked output */ret fcntl(STDOUT_FILENO, F_GETFL, 0);if (ret 0 (ret O_NONBLOCK) ! 0) {ret ~O_NONBLOCK;ret fcntl(STDOUT_FILENO, F_GETFL, ret);}if (ret 0) {error errno;fprintf(stderr, Error, failed to enable blocked output: %s\n,strerror(error));fflush(stderr);rval 12;goto err0;}}for (;;) {rl1 read(fd, pbuf, SCRIPT_CHECK_SIZE);if (rl1 0)break;if (write(STDOUT_FILENO, pbuf, (size_t) rl1) ! rl1) {rval 13;error errno;fprintf(stderr, Error, failed to write output: %s\n,strerror(error));fflush(stderr);break;}}err0:if (fd ! -1)close(fd);if (pbuf ! NULL)free(pbuf);return rval;
}