持续构建:Alpine镜像下构建的Go语言应用的运行问题

﹥>﹥吖頭↗ 提交于 2020-01-22 09:03:29

Go语言虽然是平台无关性的语言,但是构建出来的应用由于是可执行文件,所以注定无法像Java那样“一次编译、处处运行”,因为Java应用程序的二进制字节码下的解释由JVM这一层来实现,所以能够实现一次编译之后随处运行的平台无关性。这篇文章通过Alpine下编译的二进制文件的运行方式来说明在实际使用中需要注意的一个细节。

使用Alpine构建Go应用

Go提供官方镜像用于构建Go语言的应用,官方镜像中也包括Alpine镜像。如何使用Docker镜像构建Go语言应用可参看:

问题现象

参照上述方式在Alpine镜像中构建的Go的可执行文件,如果在其他操作系统之上比如macOS,可能无法执行,比如如下错误示例:

liumiaocn:random liumiao$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.2
BuildVersion:	19C57
liumiaocn:random liumiao$
liumiaocn:random liumiao$ ls -l random 
-rwxr-xr-x  1 liumiao  staff  11818840 Jan  2 05:49 random
liumiaocn:random liumiao$ 
liumiaocn:random liumiao$ ./random 
-bash: ./random: cannot execute binary file
liumiaocn:random liumiao$ 

问题原因

原因很简单,如果使用file就可以看到提示信息,因为此应用中使用到了Alpine下的动态链接库musl

liumiaocn:random liumiao$ file random 
random: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, Go BuildID=hgTtsNIKz6CaFyd1O013/zRrsBwl7ZanojEmS5VjG/lanZ1NVq7_tlUKyh7Umk/Qt-ybeIFl1WKlkEbxAhh, not stripped
liumiaocn:random liumiao$ 

也可以使用如下方式确认此应用所关联的动态链接库

liumiaocn:random liumiao$ docker run -p 8080:8080 -v `pwd`/random:/random -it --rm alpine sh
/ # ls
bin     etc     lib     mnt     proc    root    sbin    sys     usr
dev     home    media   opt     random  run     srv     tmp     var
/ # ldd random
	/lib/ld-musl-x86_64.so.1 (0x7f5c916a9000)
	libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f5c916a9000)
/ # 

对应方法

对应方法有两种,其一就是使用所在平台进行go语言编译,其二是将此应用运行在Alpine容器中,虽然还有其他方式比如使得所在平台支持musl或者使Alpine支持libc就目前的使用状况而言,基本上都是不太适合的,因为由于这种方式支持的不全面,可能只是保证得了一时的正常动作。这里使用在Alpine容器中运行Alpine镜像编译的应用,比如:

liumiaocn:random liumiao$ docker run -p 8080:8080 -d -v `pwd`/random:/random -it --rm alpine /random -listen-address=:8080
f72dc7267ae84abccea73e813a8b433af5d6885d6c57d36707cfea4e9f9ca3f8
liumiaocn:random liumiao$

通过如下命令可确认到其能够正常动作

liumiaocn:random liumiao$ curl http://localhost:8080/metrics 2>/dev/null |grep -v '# ' |head -n 10
go_build_info{checksum="",path="github.com/prometheus/client_golang",version="(devel)"} 1
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
go_goroutines 10
go_info{version="go1.13.5"} 1
liumiaocn:random liumiao$ 

总结

实际上这个问题一般是反向才会出现的问题,之所以需要在Alpine镜像中构建go语言应用,一般的原因并不是Alpine的Go语言构建镜像过大,而是运行时Alpine由于不支持libc会导致出现在Alpine镜像中无法运行。构建出来的go语言应用加上小巧的Alpine镜像使得最终的部署镜像很小才是实际使用中对大家的一个吸引。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!