gin+docker+阿里云
原理:多阶段构建打包,创建最小go容器。
- build阶段
- 打ca证书阶段(涉及到认证的话)
- 生产阶段pull构建最小镜像
0、gin+docker例子
源代码在github
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ tree -L 3
.
├── Dockerfile
└── gin-docker
├── go.mod
└── main.go
main.go源文件
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/hello/:name", func(c *gin.Context) {
name := c.Param("name")
c.JSON(http.StatusOK, map[string]interface{}{"hello":name})
})
router.Run(":8090")
}
go.mod
这里,我们是通过go mod方式(注意,这里的编译得到的可执行文件是根据 go.mod
的gin-docker
, 也就是go mod init xxx
xxx
就是编译后的可执行文件名字。和CMD ["./gin-docker"]
有关)
go mod init gin-docker
1、查看现有镜像
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
golang 1.12 be63d15101cb 3 weeks ago 814MB
alpine latest b7b28af77ffe 4 weeks ago 5.58MB
hello-world latest fce289e99eb9 7 months ago 1.84kB
2、build镜像阶段
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker build -t liangjf/gin-docker .
Sending build context to Docker daemon 4.608kB
Step 1/10 : FROM golang:1.12 AS build
---> be63d15101cb
Step 2/10 : RUN mkdir -p /go/src/gin-docker
---> Running in d6cd180ecdfb
Removing intermediate container d6cd180ecdfb
---> 0139ad863e9a
Step 3/10 : WORKDIR /go/src/gin-docker
---> Running in 65d0f96f77d0
Removing intermediate container 65d0f96f77d0
---> bbe0cec53344
Step 4/10 : ADD ./gin-docker/ .
---> 0862fa8ac5b0
Step 5/10 : ENV GOPATH=/go
---> Running in d3335ccdf88b
Removing intermediate container d3335ccdf88b
---> b525c72ea408
Step 6/10 : ENV GOROOT=/usr/local/go
---> Running in 84a8e987f425
Removing intermediate container 84a8e987f425
---> d3b6712de72f
Step 7/10 : ENV GO111MODULE=on
---> Running in 7b0468547725
Removing intermediate container 7b0468547725
---> c8857429bcda
Step 8/10 : ENV GOPROXY=http://mirrors.aliyun.com/goproxy/
---> Running in ffaee7a06531
Removing intermediate container ffaee7a06531
---> 456a29b54162
Step 9/10 : RUN go build
---> Running in 5c0fecb6db2e
go: finding github.com/gin-gonic/gin v1.4.0
go: downloading github.com/gin-gonic/gin v1.4.0
go: extracting github.com/gin-gonic/gin v1.4.0
go: finding github.com/mattn/go-isatty v0.0.7
go: finding github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go: finding github.com/ugorji/go v1.1.4
go: finding github.com/modern-go/reflect2 v1.0.1
go: finding github.com/golang/protobuf v1.3.1
go: finding github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
go: finding gopkg.in/yaml.v2 v2.2.2
go: finding golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
go: finding github.com/json-iterator/go v1.1.6
go: finding gopkg.in/go-playground/validator.v8 v8.18.2
go: finding gopkg.in/go-playground/assert.v1 v1.2.1
go: finding github.com/stretchr/testify v1.3.0
go: finding golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
go: finding gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
go: finding golang.org/x/text v0.3.0
go: finding golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
go: finding golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
go: finding github.com/davecgh/go-spew v1.1.0
go: finding github.com/stretchr/objx v0.1.0
go: finding github.com/pmezard/go-difflib v1.0.0
go: downloading github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
go: downloading github.com/ugorji/go v1.1.4
go: downloading gopkg.in/go-playground/validator.v8 v8.18.2
go: downloading gopkg.in/yaml.v2 v2.2.2
go: downloading github.com/mattn/go-isatty v0.0.7
go: downloading github.com/golang/protobuf v1.3.1
go: extracting github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
go: extracting github.com/mattn/go-isatty v0.0.7
go: downloading golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
go: extracting gopkg.in/go-playground/validator.v8 v8.18.2
go: extracting gopkg.in/yaml.v2 v2.2.2
go: extracting github.com/ugorji/go v1.1.4
go: extracting github.com/golang/protobuf v1.3.1
go: extracting golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
Removing intermediate container 5c0fecb6db2e
---> ca9e7fe17ac3
Step 10/10 : CMD ["./gin-docker"]
---> Running in 7c2408ab80d8
Removing intermediate container 7c2408ab80d8
---> 95161086d0c9
Successfully built 95161086d0c9
Successfully tagged liangjf/gin-docker:latest
3、查看是否得到build后的镜像
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
liangjf/gin-docker latest 95161086d0c9 2 minutes ago 864MB
golang 1.12 be63d15101cb 3 weeks ago 814MB
alpine latest b7b28af77ffe 4 weeks ago 5.58MB
hello-world latest fce289e99eb9 7 months ago 1.84kB
看到构建出一个liangjf/gin-docker的测试镜像了,有864MB。。。如果在生产环境各种push,pull,那对网络和流量的消耗时巨大的。
因此上面的镜像只是第一阶段,build出一个编译好可执行文件的镜像。下面会进入第二阶段,把第一阶段build出的镜像的可执行文件和配置文件等拉取到测试/生产环境的镜像中 (一般是最小的linux镜像,比如alpine,只有5.58MB)
##4、测试下build的镜像o不ok
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker run --rm -ti -p 8080:8090 liangjf/gin-docker
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /hello/:name --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8090
可以看到镜像已经可以运行。虽然现在的镜像很大,864MB
5、http请求测试镜像
新起一个终端,通过crul构造请求:
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ curl http://127.0.0.1:8080/hello/liangjf
{"hello":"liangjf"}
看到gin web响应了,上面也看到打印了信息。
[GIN] 2019/08/10 - 14:51:02 | 200 | 95.75µs | 172.17.0.1 | GET /hello/liangjf
6、测试/生产阶段拉取build阶段的“成果”
上阶段的镜像是可以进行部署使用的,但是体积太大了。每次在k8s上启动这个容器时需要拉取这么大的镜像?太浪费时间和带宽了。
下面来构建一个生产环境使用的镜像,在Dockerfile中最后面增加下面两行代码:
COPY --from=build /go/src/gin-docker/gin-docker .
CMD ["./gin-docker"]
7、build最新的Dockerfile
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker build -t liangjf/gin-docker .
Sending build context to Docker daemon 4.608kB
Step 1/12 : FROM golang:1.12 AS build
---> be63d15101cb
Step 2/12 : RUN mkdir -p /go/src/gin-docker
---> Running in bd3e25e62734
Removing intermediate container bd3e25e62734
---> 16ad329c2afb
Step 3/12 : WORKDIR /go/src/gin-docker
---> Running in 5ebd35bb3449
Removing intermediate container 5ebd35bb3449
---> 7b5d502fff87
Step 4/12 : ADD ./gin-docker/ .
---> d58fc2bace39
Step 5/12 : ENV GOPATH=/go
---> Running in 722bdbbae369
Removing intermediate container 722bdbbae369
---> 44e76e77a758
Step 6/12 : ENV GOROOT=/usr/local/go
---> Running in 0aa7aecd3db7
Removing intermediate container 0aa7aecd3db7
---> 388f738220e7
Step 7/12 : ENV GO111MODULE=on
---> Running in 81de4a3233fb
Removing intermediate container 81de4a3233fb
---> 9bdcb88b9caa
Step 8/12 : ENV GOPROXY=http://mirrors.aliyun.com/goproxy/
---> Running in ffbc01db71a3
Removing intermediate container ffbc01db71a3
---> db3bcab4e9fb
Step 9/12 : RUN go build
---> Running in 6deceb5900b8
go: finding github.com/gin-gonic/gin v1.4.0
go: downloading github.com/gin-gonic/gin v1.4.0
go: extracting github.com/gin-gonic/gin v1.4.0
go: finding github.com/mattn/go-isatty v0.0.7
go: finding github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go: finding github.com/ugorji/go v1.1.4
go: finding github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
go: finding gopkg.in/yaml.v2 v2.2.2
go: finding github.com/json-iterator/go v1.1.6
go: finding golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
go: finding gopkg.in/go-playground/validator.v8 v8.18.2
go: finding gopkg.in/go-playground/assert.v1 v1.2.1
go: finding github.com/golang/protobuf v1.3.1
go: finding github.com/stretchr/testify v1.3.0
go: finding github.com/modern-go/reflect2 v1.0.1
go: finding golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
go: finding golang.org/x/text v0.3.0
go: finding gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
go: finding github.com/davecgh/go-spew v1.1.0
go: finding github.com/pmezard/go-difflib v1.0.0
go: finding github.com/stretchr/objx v0.1.0
go: finding golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
go: finding golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
go: downloading gopkg.in/yaml.v2 v2.2.2
go: downloading github.com/ugorji/go v1.1.4
go: downloading github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
go: downloading gopkg.in/go-playground/validator.v8 v8.18.2
go: downloading github.com/mattn/go-isatty v0.0.7
go: downloading github.com/golang/protobuf v1.3.1
go: extracting gopkg.in/yaml.v2 v2.2.2
go: extracting github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
go: extracting gopkg.in/go-playground/validator.v8 v8.18.2
go: extracting github.com/mattn/go-isatty v0.0.7
go: downloading golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
go: extracting github.com/ugorji/go v1.1.4
go: extracting github.com/golang/protobuf v1.3.1
go: extracting golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
Removing intermediate container 6deceb5900b8
---> 748da1f1fd2b
Step 10/12 : FROM alpine as prod
---> b7b28af77ffe
Step 11/12 : COPY --from=build /go/src/gin-docker/gin-docker .
---> Using cache
---> 580ac3d54e43
Step 12/12 : CMD ["./gin-docker"]
---> Using cache
---> 70a3ce82b123
Successfully built 70a3ce82b123
Successfully tagged liangjf/gin-docker:latest
8、查看第二阶段的镜像大小
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
liangjf/gin-docker latest 70a3ce82b123 9 seconds ago 22.6MB
golang 1.12 be63d15101cb 3 weeks ago 814MB
alpine latest b7b28af77ffe 4 weeks ago 5.58MB
hello-world latest fce289e99eb9 7 months ago 1.84kB
这次只有22.6MB了,少了几十倍啊啊啊。
9、运行最新镜像
报错:
standard_init_linux.go:207: exec user process caused “no such file or directory”
这是因为阶段一 go build
的时候,依赖了一些动态库,而不是静态编译。所以在scratch
中由于动态库不匹配而报错
解决办法,在阶段一 go build
的时候,通过静态编译来编译:
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo .
10、使用最新的Dockerfile build镜像
sudo docker build -t liangjf/gin-docker .
11、运行+测试
运行镜像:
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker run --rm -ti -p 8080:8090 liangjf/gin-docker
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /hello/:name --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8090
[GIN] 2019/08/10 - 15:32:54 | 200 | 115.546µs | 172.17.0.1 | GET /hello/dage
crul构造请求测试:
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ curl http://127.0.0.1:8080/hello/dage
{"hello":"dage"}
到这个步骤,这个镜像就可以推送到仓库,其他人使用,过着k8s pull使用了。
12、push到阿里云镜像仓库
地址:https://cr.console.aliyun.com/cn-hangzhou/instances/repositories
阿里云的镜像服务挺好用的,关键时目前是免费的,而且还有镜像加速器。
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
liangjf/gin-docker latest f364f8077e67 12 minutes ago 17MB
golang 1.12 be63d15101cb 3 weeks ago 814MB
alpine latest b7b28af77ffe 4 weeks ago 5.58MB
登录阿里云镜像仓库:
liangjf@blue:~/ljf_homebianxie/code/go_home/project/gin-docker$ sudo docker login --username=404748279@qq.com registry.cn-shenzhen.aliyuncs.com
Password:
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker tag f364f8077e67 registry.cn-shenzhen.aliyuncs.com/liangjf_top/liangjf_all:f364f8077e67
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker push registry.cn-shenzhen.aliyuncs.com/liangjf_top/liangjf_all:f364f8077e67
The push refers to repository [registry.cn-shenzhen.aliyuncs.com/liangjf_top/liangjf_all]
998e51a16e48: Pushed
f364f8077e67: digest: sha256:0a9a2d4fv834n7802985t70578ac1bf21051v911fdad969c689qdd9ab83c523d3 size: 528
这样就代表push到阿里云仓库了,去看看镜像大小。7.474MB,已经很小了。。。
13、pull自己的阿里云镜像仓库到本地
sudo docker pull registry.cn-shenzhen.aliyuncs.com/liangjf_top/liangjf_all:f364f8077e67
14、测试pull之前push到阿里云的镜像
运行:
liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ sudo docker run -p 8080:8090 registry.cn-shenzhen.aliyuncs.com/liangjf_top/liangjf_all:f364f8077e67
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /hello/:name --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8090
[GIN] 2019/08/10 - 16:00:34 | 200 | 206.615µs | 172.17.0.1 | GET /hello/test-pull-aliyun
测试: liangjf@blue:~/ljf_home/code/go_home/project/gin-docker$ curl http://127.0.0.1:8080/hello/test-pull-aliyun {“hello”:”test-pull-aliyun”}
15、总结
从二阶段build镜像,构建自己的轻量级go docker,到push到阿里云镜像仓库,到pull阿里云自己制作的镜像,到运行。一条龙跑通了。其余的就是根据业务和应用来进行对应的依赖打包, 也就是Dockerfile的编写。