最近在观察项目CI运行情况时,偶尔会发现一两个镜像虽然构建成功,但是容器无法运行。原因是退出代码问题。本文地址:https://shanyue.tech/post/exi...thrownewErrorandPromise.reject下面是两段代码,第一段是抛出异常,第二段是Promise.reject,这两段代码会打印出一条异常信息如下,那么这两者有什么区别呢?functionerror(){thrownewError('hello,error')}error()///Users/shanyue/Documents/note/demo.js:2//thrownewError('hello,world')//^////Error:hello,world//aterror(/Users/shanyue/Documents/note/demo.js:2:9)//atObject.(/Users/shanyue/Documents/note/demo.js:5:1)//在Module._compile(internal/modules/cjs/loader.js:701:30)asyncfunctionerror(){returnnewError('hello,error')}error()//(node:60356)UnhandledPromiseRejectionWarning:E??rror:hello,world//aterror(/Users/shanyue/Documents/note/demo.js:2:9)//atObject.<匿名>(/Users/shanyue/Documents/注意/demo.js:5:1)//在Module._compile(internal/modules/cjs/loader.js:701:30)//在Object.Module._extensions..js(internal/modules/cjs/loader.js:712:10)从一个比较底层的角度来看,两者的退出码不同,那么退出码是什么呢?exitcodeexitcode表示一个进程的返回码,由系统调用exit_group触发,在POSIX中,0表示正常返回码,1-255表示异常返回码,但一般错误码为1。这里是a表附录E.特殊含义的退出代码所以,在node的应用中,经常会有process.exit(1)来表示意外异常中断。现在看一个cat的异常及其退出码和系统调用$catacat:a:Nosuchfileordirectory#-e表示只显示write和exit_group的系统调用$strace-ewrite,exit_groupcatawrite(2,"cat:",5cat:)=5write(2,"a",1a)=1write(2,":没有那个文件或目录",27:没有那个文件或目录)=27write(2,"\n",1)=1exit_group(1)=?+++exitedwith1+++可以看出它的exitcode是1,错误信息写入stderr(标准错误的fd是2)如何查看退出码其实从上面的内容可以看出,可以使用strace来判断进程的退出码。但是这样确实很不方便,尤其是在shell编程环境下。有一种简单的方法可以通过echo$?$catacat:a:没有那个文件或目录$echo$?1查看上面两个测试用例的退出码,我们会发现thrownewError()的退出码为1,而Promise.reject()为0。node中Dockerfile的注意事项node中的异常和退出码说完了,该说说和Dockerfile的关联了。使用Dockerfile构建镜像时,如果RUN进程返回非零返回码,会导致构建失败。在Node中的错误处理中,我们倾向于将所有的异常都交给async/await处理,当出现异常时,此时exitcode为0,不会导致镜像构建失败。如下所示FROMnode:alpineRUNnode-e"Promise.reject('hello,world')"$dockerbuild-tdemo.SendingbuildcontexttoDockerdaemon14.85kBStep1/2:FROMnode:alpine--->d97a436daee9Step2/2:RUNnode-e"Promise.reject('hello,world')"--->Runningin0281c660ab82(node:1)UnhandledPromiseRejectionWarning:hello,world(node:1)UnhandledPromiseRejectionWarning:未处理的承诺拒绝。这个错误要么是在没有catch块的情况下在异步函数内部抛出,要么是因为拒绝了一个没有用.catch()处理的承诺。(rejectionid:1)(node:1)[DEP0018]DeprecationWarning:未处理的承诺拒绝已弃用。将来,未处理的承诺拒绝将以非零退出代码终止Node.js进程。删除中间容器0281c660ab82--->2146545654d2Successfullybuilt2146545654d2Successfullytaggeddemo:latest而在编译时能发现的问题,绝不要在运行时放开。所以在构建镜像需要执行node脚本时,需要手动指定exitcode为1进行异常处理:process.exit(1)。runScript().catch(()=>{process.exit(1)})
欢迎关注我的公众号山月行,记录我的技术成长,欢迎交流