• 创建超小的Golang docker 镜像


    原文地址:https://colobu.com/2015/10/12/create-minimal-golang-docker-images/#more

    Docker是PaaS供应商dotCloud开源的一个基于LXC 的高级容器引擎,源代码托管在 GitHub 上, 基于Go语言开发并遵从Apache 2.0协议开源。正如DockerPool在免费Docker电子书Docker —— 从入门到实践中这样提到的:

    作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。

    首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。 其次,Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。

    容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。

    Docker让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。几乎没有性能开销,可以很容易地在机器和数据中心中运行。最重要的是,他们不依赖于任何语言、框架包括系统。

    本文不会介绍Docker原理和操作,而是介绍如何使用Docker创建一个Golang应用程序的镜像,这样我们就可以在其它机器上运行这个镜像。 本文参考了很多的文章,这些文章列在了本文的底部。

    编写一个Golang服务器

    这里我在研究endless库的时候写了一个测试程序,就用它来测试一下docker镜像的创建。 endless可以允许我们在重启网络服务器的时候零时间宕机, 英语是graceful restart,我称之为无缝重启。 服务器监听4242端口,顺便使用raymond模版引擎替换golang自带的模版引擎,采用bone这个高性能的mux库。 代码如下:

    package main
    import (
     "flag"
     "log"
     "net/http"
     "os"
     "syscall"
     "github.com/aymerick/raymond"
     "github.com/fvbock/endless"
     "github.com/go-zoo/bone"
    )
    var (
     //homeTpl, _ = raymond.ParseFile("home.hbs")
     homeTpl = raymond.MustParse(`
     
       <span class="hljs-built_in" style="color: #0086b3; line-height: 26px;">test</span>
     
     
     "entry">
       


       "body">
         
       

     

     
     
    `)
    )
    func homeHandler(rw http.ResponseWriter, req *http.Request) {
     ctx := map[string]string{"greet""hello""name""world"}
     result := homeTpl.MustExec(ctx)
     rw.Write([]byte(result))
    }
    func varHandler(rw http.ResponseWriter, req *http.Request) {
     varr := bone.GetValue(req, "var")
     test := bone.GetValue(req, "test")
     rw.Write([]byte(varr + " " + test))
    }
    func Handler404(rw http.ResponseWriter, req *http.Request) {
     rw.Write([]byte("These are not resources you're looking for ..."))
    }
    func restartHandler(rw http.ResponseWriter, req *http.Request) {
     syscall.Kill(syscall.Getppid(), syscall.SIGHUP)
     rw.Write([]byte("restarted"))
    }
    func main() {
     flag.Parse()
     mux := bone.New()
     // Custom 404
     mux.NotFoundFunc(Handler404)
     // Handle with any http method, Handle takes http.Handler as argument.
     mux.Handle("/index", http.HandlerFunc(homeHandler))
     mux.Handle("/index/:var/info/:test", http.HandlerFunc(varHandler))
     // Get, Post etc... takes http.HandlerFunc as argument.
     mux.Post("/home", http.HandlerFunc(homeHandler))
     mux.Get("/home/:var", http.HandlerFunc(varHandler))
     mux.GetFunc("/test/*", func(rw http.ResponseWriter, req *http.Request) {
      rw.Write([]byte(req.RequestURI))
     })
     mux.Get("/restart", http.HandlerFunc(restartHandler))
     err := endless.ListenAndServe(":4242", mux)
     if err != nil {
      log.Fatalln(err)
     }
     log.Println("Server on 4242 stopped")
     os.Exit(0)
    }
    • 1

    Golang镜像

    Docker官方提供了Golang各版本的镜像: Official Repository - golang. 它包含了Golang的编译和运行时环境。最简单的使用方法就是在你的Dockerfile文件中加入

    FROM golang:1.3-onbuild
    • 1

    这个镜像包含了多个ONBUILD触发器。你可以编译和运行你的镜像:

    $ docker build -t my-golang-app .
    $ docker run -it --rm --name my-running-app my-golang-app
    • 1

    为编译好的Golang应用创建小的镜像

    上面的Golang容器相当的大,因为它包含了Golang的编译和运行环境。 官方网站上列出了镜像的大小:

    golang:1.5.1-onbuild

    $ docker pull library/golang@sha256:f938465579d1cde302a447fef237a5a45d7e96609b97c83b9144446615ad9e72

    Total Virtual Size: 709.5 MB (709470237 bytes) Total v2 Content-Length: 247.0 MB (246986021 bytes)

    实际上我们并不需要那么多的软件,因为我们的Golang应用程序是预先编译好的,而不是在Golang容器中现场编译运行,因此我们不需要Golang的编译环境等。如果你查看golang:1.5的Dockerfile,会发现它基于buildpack-deps:jessie-scm,会安装GCC及一堆的build工具,下载Go的发布文件并安装。基本上这些对于我们来说并不需要。我们需要的是:

    一个可以运行我们编译好的Golang应用的镜像。

    我们可以从scratch镜像创建。 scratch镜像是一个空的镜像文件,特别适合创建超级小的镜像。 Dockerfile文件如下:

    FROM scratch
    ADD main /
    CMD ["/main"]
    • 1

    运行 输出如下

    # docker build -t example-scratch .
    Sending build context to Docker daemon 8.054 MB
    Step 0 : FROM scratch
     ---> 
    Step 1 : ADD main /
     ---> 4ad02fa47a7d
    Removing intermediate container d64080c4b42f
    Step 2 : CMD /main
     ---> Running in 5d9a08c3a20e
     ---> 5c29c8249678
    Removing intermediate container 5d9a08c3a20e
    Successfully built 5c29c8249678
    • 1

    这样镜像就创建成功了,查看一下:

    [root@localhost work]# docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    example-scratch     latest              5c29c8249678        3 minutes ago       8.052 MB
    • 1

    只有8M左右,非常的小。

    但是运行这个镜像,容器无法创建:

    # docker run -it -p 4242:4242 example-scratch
    no such file or directory
    Error response from daemon: Cannot start container 79bb9fb62788b4a8c1487695a3219ddf3aa85bde2bc44473838f6f4d1583a204: [8] System error: no such file or directory
    • 1

    原因是我们的main文件生成的时候依赖的一些库如libc还是动态链接的,但是scratch 镜像完全是空的,什么东西也不包含,所以生成main时候要按照下面的方式生成,使生成的main静态链接所有的库:

    CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o main .
    • 1

    然后重新生成镜像并运行:

    # docker build -t example-scratch .
    # docker run -it -p 4242:4242 example-scratch
    • 1

    容器运行成功,在浏览器中访问http://宿主IP:4242/index成功返回结果

    发布 可以方便的将刚才的镜像发布到docker.io上。 首先将刚才的镜像打tag:

    # docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    example-scratch     latest              2ea4bbfd67dc        10 minutes ago      8.01 MB
    # docker tag 2ea4bbfd67dc smallnest/example-scratch
    # docker images
    REPOSITORY                  TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    smallnest/example-scratch   latest              2ea4bbfd67dc        10 minutes ago      8.01 MB
    example-scratch             latest              2ea4bbfd67dc        10 minutes ago      8.01 MB
    • 1

    运行docker login登录,然后运行下面的命令push到docker.io上。

    docker push smallnest/example-scratch
    • 1

    访问 https://hub.docker.com/r/smallnest/example-scratch/ 可以看到刚刚push的这个镜像,这样我们就可以pull到其它机器上运行了。

    参考文档

    • https://blog.golang.org/docker
    • https://hub.docker.com/_/golang/
    • https://blog.codeship.com/building-minimal-docker-containers-for-go-applications/
    • https://medium.com/@kelseyhightower/optimizing-docker-images-for-static-binaries-b5696e26eb07
    • http://www.iron.io/blog/2015/07/an-easier-way-to-create-tiny-golang-docker-images.html
    • https://labs.ctl.io/small-docker-images-for-go-apps/
    • http://dockerpool.com/static/books/docker_practice/introduction/why.html
    • https://docs.docker.com/installation/centos/
    • http://segmentfault.com/a/1190000002766882

    原文地址:https://colobu.com/2015/10/12/create-minimal-golang-docker-images/#more

    关注 golang技术实验室 获取更多好文

    本文由 mdnice 多平台发布

  • 相关阅读:
    leetcode 50. Pow(x, n) (快速幂)
    vim插件管理器之Vundle的使用
    视频需求超平常数 10 倍,却节省 60% 的 IT 成本投入是种什么样体验?
    放大器的输入、输出电压范围的理解
    用于车载T-BOX汽车级的RA8900CE
    IO流的原理以及分类
    基于机器学习建模的 XSS 攻击防范检测
    适配与视口、分辨率、媒体查询、缩放的学习、消化
    Vue的详细教程--用Vue-cli搭建SPA项目
    自动化运维中间件架构概况
  • 原文地址:https://blog.csdn.net/qq_39787367/article/details/126247900