使用Docker构建容器能够极大的降低运维成本,提高部署效率,同时非常方便对服务的平行扩展。然而在构建容器镜像过程中的,存在着一个难以避免的问题,就是如果使用常见的发行版本作为程序运行的基础环境,那么即使一个服务本身的运行文件非常小,最终构建的镜像也可能会有会在运行环境的镜像的基础上变得更大,动不动就是数百M的体积。

以最常用于微服务开发的golang为例,golang的二进制程序可以一次开发跨平台编译,到处运行,因此其本身的程序自洽性其实非常完善,也很少会依赖复杂的外部环境,因此常用的发行版本镜像硕大的体积其实很没必要。因此,alpine成为了一个非常好的选择。最终alpine也不负众望,将最终的镜像体积减小到了10M+左右,已经压缩了非常大的空间了,结果还算理想。

然而,能不能更小呢?

docker自带的scratch镜像给了我一个思路:没有任何镜像会比空镜像更小。那使用scratch制作出来的镜像也必然是最小的。那么scratch是不是一个好的选择呢?scratch镜像是docker自带的空镜像,我也曾见过数篇文章推荐使用其作为golang的运行镜像,然而,在最终实践的时候,遇到的bug却会让人倍感疑惑。

# 1. 使用官方的golang镜像构建运行的容器

golang程序非常简单:

package main

import "fmt"

func main(){

fmt.Println("你好,世界!")

}

Dockerfile也不难:

FROM harbor.corp.sdo.com/library/golang:alpine as build

MAINTAINER fanxiaoqiang <fan_xq@live.com>

ADD . /data/

WORKDIR /data/

RUN export GO111MODULE=on

RUN export GOSUMDB=off

RUN unset GOPATH

RUN go env -w GOPROXY=https://goproxy.cn

RUN go build -o server helloworld.go

# deploy-image

FROM scratch

#FROM alpine

COPY --from=build /data/server /data/server

#COPY --from=build /data/entrypoint.sh /data/entrypoint.sh

WORKDIR /data/

EXPOSE 9090

CMD ["/data/server"]

构建,运行!

docker build -t hello .

&& docker run

-p 9090:9090

hello

容器成功的输出了helloword,最终的镜像大小只有2.068MB,效果非常理想。然后scratch是否真的能够满足golang容器化的所有需求呢?下面我们继续看。

# 2. 使用scratch构建稍复杂的golang的运行容器

这一次我们构建一个golang实现的http服务器。在开始之前,首先强调一下scratch是一个空镜像,意味这Docker内部不存在任何环境和依赖库。机智的读者可能已经想到我想说什么,接下来开始进行实验。

golang程序:

package main

import (

"log"

"net/http"

"os"

"os/signal"

"time"

)

func main() {

server := http.Server{

Addr:        ":9090",

ReadTimeout: 10 * time.Second,

}

//log.Println("start running")

log.Println("start running")

server.ListenAndServe()

//合建chan

c
:= make(chan os.Signal)

//监听指定信号 ctrl+c kill

signal.Notify(c, os.Interrupt, os.Kill)

//阻塞直到有信号传入

//阻塞直至有信号传入

s
:= <-c

log.Println("exit!", s)

}

Docker文件第一节相同,这里就不放了。

现在让我们尝试 运行,

docker build -t hello .

&& docker run

-p 9090:9090

hello

构建镜像的过程依旧轻松愉快:

Building image...

Preparing build context archive...

[==================================================>]9/9
files

Done

Sending build context to Docker daemon...

[==================================================>]
2.859kB

Done

Step 1/14 : FROM
harbor.corp.sdo.com/library/golang:alpine as build

---> dda4232b2bd5

Step 2/14 : MAINTAINER fanxiaoqiang
<fan_xq@live.com>

---> Using cache

---> 546d5bcb606b

Step 3/14 : ADD . /data/

---> d6bcfc3f9976

Step 4/14 : WORKDIR /data/

---> Running in 4a8f0fa4c9c4

Removing intermediate container
4a8f0fa4c9c4

---> 6f6092bc91a8

Step 5/14 : RUN export GO111MODULE=on

---> Running in 44a83bb9c9a9

Removing intermediate container
44a83bb9c9a9

---> ea199d64e9d9

Step 6/14 : RUN export GOSUMDB=off

---> Running in df368787ddd7

Removing intermediate container
df368787ddd7

---> c338c09c4980

Step 7/14 : RUN unset GOPATH

---> Running in c6016dd29cd8

Removing intermediate container
c6016dd29cd8

---> 8f7004cb8ed5

Step 8/14 : RUN go env -w
GOPROXY=https://goproxy.cn

---> Running in 237a89c7a644

Removing intermediate container
237a89c7a644

---> 5b5b9b8efb43

Step 9/14 : RUN go build -o server
http_server.go

---> Running in 27a5afb6b775

Removing intermediate container
27a5afb6b775

---> 8e0771380586

Step 10/14 : FROM scratch

--->

Step 11/14 : COPY --from=build /data/server
/data/server

---> 76dc69f34774

Step 12/14 : WORKDIR /data/

---> Running in 8550a1a7b8ee

Removing intermediate container
8550a1a7b8ee

---> 269d3ee7bb29

Step 13/14 : EXPOSE 9090

---> Running in 2a3f21f67f90

Removing intermediate container
2a3f21f67f90

---> 79640d9e743a

Step 14/14 : CMD ["/data/server"]

---> Running in 39581ed1d208

Removing intermediate container
39581ed1d208

---> e30b2238a606

Successfully built e30b2238a606

Successfully tagged hello:latest

Existing container found:
8b31d39f149117566da56be2796418089c47509018857427559600f1ba7c7982, removing...

Creating container...

Container Id:
20d38a265fe3496b5a4b6c3742740c6c517b7d449250ab0be246688973212079

Container name: '/vibrant_hodgkin'

Attaching to container
'/vibrant_hodgkin'...

Starting container '/vibrant_hodgkin'

'<unknown> Dockerfile: Dockerfile'
has been deployed successfully.

然而查看容器的日志输出:

standard_init_linux.go:211: exec user
process caused "no such file or directory"

???

是我的二进制程序没有编译成功吗?其实不是。从构建日志中,我们可以清楚的看到,程序其实是编译成功的,也成功的COPY到了最终的运行镜像中,然后启动的时候就是出错了。所以首先我们就排除了代码和Dockerfile的问题。

此处我曾经疑惑了很久,因为容器运行报上述错误是让人非常摸不着头脑的。我尝试用搜索引擎进行搜索,确实搜到了结果:

docker启动报错:standard_init_linux.go:211: exec user process caused "no such
file or directory"

如题所示,根据自己构建的镜像启动docker容器,直接退出,查看容器日志报错信息,没有任何别的信息。网上搜索这个问题,发现很多人都遇到过,解决办法也各不相同,最后发现一篇文章。受到启发,我的项目是java项目,通过ENTRYPOINT命令启动脚本docker-entrypoint.sh来构建一个在后台运行的服务。而我的docker-entrypoint.sh是在windows下编辑的,自然fileformat是dos,这里需要修改为unix,修改办法也很简答,无需再在linux下操作,我们一般机器上安装了git工具,自带了git bash命令行工具,进入git bash,找到该文件docker-entrypoint.sh,然后使用vi编辑,修改fileformat=unix,如下所示。

————————————————

原文链接:https://blog.csdn.net/feinifi/java/article/details/102910715

然而这个问题却与我们无关,因为我们根本没有用到entrypoint的功能。

现在我们回到本节开始的地方:scratch是一个空镜像,意味这Docker内部不存在任何环境和依赖库。这就意味着即使是最常见的依赖,在scratch中也是不存在的,那么我们检查一下helloworld和httpserver两个二进制文件的依赖,看看是不是能看出一些端倪。

$ go build http_server.go

$ ldd http_server

linux-vdso.so.1 (0x00007fffc4eaf000)

libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0
(0x00007fecea090000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fece9c90000)

/lib64/ld-linux-x86-64.so.2 (0x00007fecea400000)

真相大白,即使golang宣传了二进制文件不需要依赖任何外部文件,但是即使是程序运行最基础的libc,scratch也是不包含的。这直接导致了编译完成的httpserver无法运行,但是容器的报错却是找不到文件,报错让人摸不着头脑,希望这篇文章能提供一些小小的帮助。

理论上来说,如果在scratch中添加需要的动态库,最终是可以让程序正常运行的,但这违背了简化开发流程的原则,同时会在代码中增加不必要的负担。因此,常见的golang程序使用alpine作为最终的运行环境的基础镜像已经是一个非常折衷和合适的方案,不建议再去scratch上折腾。

Docker scratch 无法正常运行golang二进制程序的问题的更多相关文章

  1. 如何使用Docker部署一个Go Web应用程序

    熟悉Docker如何提升你在构建.测试并部署Go Web应用程序的方式,并且理解如何使用Semaphore来持续部署. 简介 大多数情况下Go应用程序被编译成单个二进制文件,web应用程序则会包括模版 ...

  2. 使用 Elastic Stack 来监控和调优 Golang 应用程序

    Golang 因为其语法简单,上手快且方便部署正被越来越多的开发者所青睐,一个 Golang 程序开发好了之后,势必要关心其运行情况,今天在这里就给大家介绍一下如果使用 Elastic Stack 来 ...

  3. CentOS7.5 使用二进制程序部署Kubernetes1.12.2(三)

    一.安装方式介绍 1.yum 安装 目前CentOS官方已经把Kubernetes源放入到自己的默认 extras 仓库里面,使用 yum 安装,好处是简单,坏处也很明显,需要官方更新 yum 源才能 ...

  4. golang获取程序运行路径

    golang获取程序运行路径: /* 获取程序运行路径 */ func getCurrentDirectory() string { dir, err := filepath.Abs(filepath ...

  5. golang windows程序获取管理员权限(UAC ) via gocn

    golang windows程序获取管理员权限(UAC ) 在windows上执行有关系统设置命令的时候需要管理员权限才能操作,比如修改网卡的禁用.启用状态.双击执行是不能正确执行命令的,只有右键以管 ...

  6. 安装mariadb二进制程序

    author:JevonWei 版权声明:原创作品 下载mariadb软件包 https://downloads.mariadb.org/mariadb/5.5.57/ 一.创建用户和准备数据目录 1 ...

  7. golang 二进制转十进制实现方式

    golang 二进制转十进制实现方式 直接上代码 package main import ( "fmt" "math" ) func StringToIntAr ...

  8. golang二进制bit位的常用操作

    golang作为一热门的兼顾性能 效率的热门语言,相信很多人都知道,在编程语言排行榜上一直都是很亮眼,作为一门强类型语言,二进制位的操作肯定是避免不了的,数据的最小的单位也就是位,尤其是网络中封包.拆 ...

  9. 使用afl-dyninst fuzz无源码的二进制程序

    转:http://ele7enxxh.com/Use-AFL-dyninst-To-Fuzz-Blackbox-Binaries.html 使用afl-dyninst fuzz无源码的二进制程序 通常 ...

随机推荐

  1. CodeForces - 1245 C - Constanze's Machine

    Codeforces Round #597 (Div. 2) Constanze is the smartest girl in her village but she has bad eyesigh ...

  2. 动态SQL各个标签作用以及注意事项详解

    创建com.mybatis包,包含:UserMapper.xml和mybatis-config.xml UserMapper.xml代码: <?xml version="1.0&quo ...

  3. 清北学堂—2020.3NOIP数学精讲营—Day 1 morning 重点笔记

    qbxt Day 1 morning 重点笔记 --2020.3.8 济南 主讲:钟皓曦 1 正数%负数==正数 负数%正数==负数 负数%负数==负数 a%b的答案的符号取决于a的符号. 2 快速幂 ...

  4. OSG程序设计之更新回调

    更新回调(Update Callback)涉及到一个类:osg::NodeCallback.这个类重载了函数调用操作符.当回调动作发生时,将会执行这一操作符的内容. 如果节点绑定了更新回调函数,那么在 ...

  5. CSS颜色及文本字体

    CSS颜色及文本字体 CSS颜色表示法 CSS文本设置 CSS边框属性 背景属性 元素溢出 CSS颜色及文本字体 CSS颜色表示法 颜色名表示,比如:red 红色,yellow黄色,pick粉色 16 ...

  6. Maxim实时时钟芯片设计指南5413-二进制编码十进制(BCD)格式实时时钟中的状态机逻辑

    网上DS12C887的文章涉及到时间的存储格式使用的都是二进制代码,究竟使用BCD码该如何操作?正好美信官网上有一篇文章.美信官网不稳定,先贴到这里,有时间再翻译. 原文链接 State Machin ...

  7. QTableWidget自定义委托

    QTableWidget单元格使用自定义的lineEdit控件导致不能选中 使用自定义委托解决 1.自定义委托 class LineEditDelegate : public QItemDelegat ...

  8. [zoj3813]Alternating Sum 公式化简,线段树

    题意:给一个长度不超过100000的原串S(只包含数字0-9),令T为将S重复若干次首尾连接后得到的新串,有两种操作:(1)修改原串S某个位置的值(2)给定L,R,询问T中L<=i<=j& ...

  9. TP5整合的导出Excel中没有图片和包含图片两种方法

    之前做了个项目需要导出Excel文件 ,我在网上查了许多资料,最后终于搞定了 ,现在把代码贴到下面 先导入库文件:将文件phpoffice放在根目录的vendor下.获取文件点击:链接:https:/ ...

  10. Atcoder Beginner Contest 167

    赛场实况: 训练反思: A题签到不说了,B题第一眼没看清楚数据范围,写了一堆然后仔细一看1e12果断不能暴力..立马换了一个写法,连交2发wa(细节啊细节!!),C题看了半天英语没看懂说了什么,拿翻译 ...