zoukankan      html  css  js  c++  java
  • 创建超小的Golang docker 镜像

    原文: http://colobu.com/2015/10/12/create-minimal-golang-docker-images/

    本文对于创建超小的镜像非常有用

     

    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"
        "github.com/aymerick/raymond"
        "github.com/fvbock/endless"
        "github.com/go-zoo/bone"
        "log"
        "net/http"
        "os"
        "syscall"
    )
    
    var (
        //homeTpl, _ = raymond.ParseFile("home.hbs")
        homeTpl = raymond.MustParse(`<html>
    <head>
    <title>test</title>
    </head>
    </body>
    <div class="entry">
    <h1></h1>
    <div class="body">
    
    </div>
    </div>
    </body>
    </html>
    `)
    )
    
    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)
    }

    Golang镜像

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

    FROM golang:1.3-onbuild

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

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


    上面的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"]
    # 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

    这样镜像就创建成功了,查看一下:运行
    输出如下

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

    只有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

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

    CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

    注意:其实主要的是使用 CGO_ENABLED=0 ,关闭cgo

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

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

    容器运行成功,在浏览器中访问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

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

    docker push smallnest/example-scratch

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

    参考文档

      1. https://blog.golang.org/docker
      2. https://hub.docker.com/_/golang/
      3. https://blog.codeship.com/building-minimal-docker-containers-for-go-applications/
      4. https://medium.com/@kelseyhightower/optimizing-docker-images-for-static-binaries-b5696e26eb07
      5. http://www.iron.io/blog/2015/07/an-easier-way-to-create-tiny-golang-docker-images.html
      6. https://labs.ctl.io/small-docker-images-for-go-apps/
      7. http://dockerpool.com/static/books/docker_practice/introduction/why.html
      8. https://docs.docker.com/installation/centos/
      9. http://segmentfault.com/a/1190000002766882
  • 相关阅读:
    vue中的 computed 和 watch 的区别
    mysql8.0 初始化数据库及表名大小写问题
    sql server alwayson 调整数据文件路径
    zabbix 自定义监控 SQL Server
    mysql 创建用户及授权
    mysql 设置从库只读模式
    mysql8.0 主从复制安装及配置
    centos8.0安装mysql8.0
    centos8替换阿里数据源
    npm publish 报错 【you or one of your dependencies are requesting a package version that is forbidden by your security policy】
  • 原文地址:https://www.cnblogs.com/davygeek/p/6524317.html
Copyright © 2011-2022 走看看