原文: http://colobu.com/2015/10/12/create-minimal-golang-docker-images/html
本文對於建立超小的鏡像很是有用linux
Docker是PaaS供應商dotCloud開源的一個基於LXC 的高級容器引擎,源代碼託管在 GitHub 上, 基於Go語言開發並聽從Apache 2.0協議開源。正如DockerPool在免費Docker電子書Docker —— 從入門到實踐中這樣提到的:git
做爲一種新興的虛擬化方式,Docker 跟傳統的虛擬化方式相比具備衆多的優點。github
首先,Docker 容器的啓動能夠在秒級實現,這相比傳統的虛擬機方式要快得多。 其次,Docker 對系統資源的利用率很高,一臺主機上能夠同時運行數千個 Docker 容器。golang
容器除了運行其中應用外,基本不消耗額外的系統資源,使得應用的性能很高,同時系統的開銷儘可能小。傳統虛擬機方式運行 10 個不一樣的應用就要起 10 個虛擬機,而Docker 只須要啓動 10 個隔離的應用便可。docker
Docker讓開發者能夠打包他們的應用以及依賴包到一個可移植的容器中,而後發佈到任何流行的 Linux 機器上,也能夠實現虛擬化。容器是徹底使用沙箱機制,相互之間不會有任何接口(相似 iPhone 的 app)。幾乎沒有性能開銷,能夠很容易地在機器和數據中心中運行。最重要的是,他們不依賴於任何語言、框架包括系統。segmentfault
本文不會介紹Docker原理和操做,而是介紹如何使用Docker建立一個Golang應用程序的鏡像,這樣咱們就能夠在其它機器上運行這個鏡像。
本文參考了不少的文章,這些文章列在了本文的底部。centos
這裏我在研究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) }
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: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到其它機器上運行了。