docker构建golang分布式带依赖库项目镜像

  • 用了golang一阵子,然后自己琢磨着尝试写了个分布式的游戏服务器。
    突然想到要把它部署到docker上,网上查看了别人的一些经验,发现大部分都只提到简单的将单个golang文件main.go添加到docker上,然后运行后完事了没了。
    然后,遗留了一些问题没有我没理解,如,docker里依赖的第三方库找不到如何解决,分布式多个子服务器的Dockerfile如何解决等。于是和同行们一些简单交流和翻看了一些官方文档,得到了一个大体方案。

叙述三个问题:
一,在Docker运行golangTCP服务监听,并成功访问
二,减小镜像包体
三,依赖的第三方代码和分布式构建镜像

假设项目内有两个子服务,client和server,则有项目结构:
在这里插入图片描述
client/main.go:

package main

import(
	"os"
	"net"
	"fmt"
)

//模拟客户端
func main() {
    
    
	//要从docker里访问宿主机器的端口,那么主机地址应该是局域网内分配的地址,
	//这里我的机器的分配地址是192.168.0.3
	//不能是127.0.0.1或0.0.0.0
	addr := "192.168.0.3:80"
	tcpaddr, _ := net.ResolveTCPAddr("tcp4", addr)

	_, err := net.DialTCP("tcp4", nil, tcpaddr)
	if err == nil {
    
    
		fmt.Println(addr+"请求成功!")
	}else{
    
    
		fmt.Println(addr+"请求失败!")
	}
	os.Exit(0)                                                                                                                                                                                                                                                            
}


server/main.go

package main

import(
	"net"
	"fmt"
)

func main()  {
    
    
	addr,_:=net.ResolveTCPAddr("tcp4", ":80")
	ls,_:=net.ListenTCP("tcp", addr)
	for{
    
    
		conn, err := ls.Accept()
		if err != nil{
    
    
			fmt.Println(err)
			continue
		}
		go handle(conn)
	}
}

func handle(conn net.Conn){
    
    
	defer conn.Close()
	fmt.Println("监听到一个TCP请求连接!")
}

一,在Docker运行golangTCP服务监听,并成功访问
从简入手,首先单个server/main.go文件在docker上运行:
有server/Dockerfile :

# 使用最新版 golang 作为基础镜像
FROM golang:latest
#设置工作目录,没有则自动新建
WORKDIR /go/src/app/
#拷贝代码到当前
COPY main.go .
#golang镜像中的go编译命令
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .
#运行服务
CMD ["./server"]  

在命令行终端cd到server/目录下,执行编译命令

docker build -t go_server .

编译正常过程log:

Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM golang:latest
 ---> 7ced090ee82e
Step 2/5 : WORKDIR /go/src/app/
 ---> Using cache
 ---> f1d241ee87fe
Step 3/5 : COPY main.go .
 ---> bb3313eea376
Step 4/5 : RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .
 ---> Running in 95b2c25f1782
Removing intermediate container 95b2c25f1782
 ---> 29295a4bf0ac
Step 5/5 : CMD ["./server"]
 ---> Running in 1a074496c162
Removing intermediate container 1a074496c162
 ---> 8999925901e6
Successfully built 8999925901e6
Successfully tagged go_server:latest

执行查看镜像命令:

docker images

结果看到了生成的go_server镜像。
在这里插入图片描述
启动go_server为容器:
将docker里go_server容器80端口暴露到宿主机器的80端口。

docker run -p 80:80 go_server

新开一个命令终端,cd到clinet/下直接运行client/main.go代码请求docker里的服务器。
在这里插入图片描述
docker go_server这边则看到log
在这里插入图片描述
成功!

二,减小镜像包体
以上一节内容可以看到,镜像包有700+m,很大,所以要减小镜像包的大小,方案就是基于golang镜像用alpine来构建。
官网参考:https://docs.docker.com/develop/develop-images/multistage-build/
server/Dockerfile文件改为:

# 使用最新版 golang 作为基础镜像
FROM golang:latest AS builder
#设置工作目录,没有则自动新建
WORKDIR /go/src/app/
#拷贝代码到当前
COPY main.go .
#golang镜像中的go编译命令
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .

#基于builder构建go_server
FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/app/ .
#运行服务
CMD ["./server"]  

结果看到新的go_server只有8.97m,运行正常!
在这里插入图片描述
三,依赖的第三方代码和分布式构建镜像
首先打开/server/main.go代码文件,
添加一行代码,对第三方库protobuf的引用:
_“github.com/golang/protobuf/proto

package main

import(
	"net"
	"fmt"
	_"github.com/golang/protobuf/proto"
)
...

进行docker build命令,发现会报错,找不到protobuf库。这也合理,毕竟构建这个镜像没有添加第三方库的代码。
在这里插入图片描述
如果要结合docker引用第三方库,那么就要将第三方代码也复制到docker构建的镜像里。
所以这里用到了一个golang依赖包管理工具dep:https://github.com/golang/dep
这个dep的作用主要是将自己依赖GOPATH的第三方库copy到自己的目录下,仅仅供自己项目使用,不再去GOPATH/GOROOT查找第三方库,可避免同一台电脑有两个项目时依赖GOPATH上同一个库的不同版本时要来回切换设置GOPATH。
如此一来,一个项目下有完自己所依赖的代码,那么就可以将整个项目COPY到docker构建的镜像里运行了。
PS:Dockerfile仅仅能识别出宿主机器的Dockerfile根目录下的文件和文件夹,无法识别宿主机器Dockerfile根目录外的文件和文件夹,所以要置放第三方库到Dockerfile目录下才能COPY到镜像里,而dep正好能做到这一点。

安装:
MacOS:

$ brew install dep
$ brew upgrade dep

Debian:

$ sudo apt-get install go-dep

Linux :

$ curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh

Win:下载后放到gobin下,
https://bin.equinox.io/a/59wHzG494MG/github-com-golang-dep-cmd-dep-windows-amd64.zip
或:

go get -u github.com/golang/dep/cmd/dep

输入dep,显示如下安装成功!
在这里插入图片描述
来到项目server和client文件夹的上一级根目录,执行

dep init

或者联网慢的话

deb init -v

在这里插入图片描述
然后看到多出了三项文件夹/文件,vendor就是存放当前引用库代码的文件夹。之后项目引用的第三方库都会优先在vendor文件夹下寻找。client微服和server微服的依赖的第三方库都在里面。
命令dep status可以查看到它们的版本:
在这里插入图片描述
所以有思路:
1,现在client和server文件夹的上一级根目录下执行dep init,将所有子服务器代码依赖的第三方库统一置于项目下。当前目录新建Dockerfile,基于golang镜像构建一个goang_dep镜像,仅仅包含所有依赖的第三方库,作为子服务器的基类镜像。
2,进入到子服务器文件夹根目录,如server/下,修改Dockerfile为基于步骤1构建的golang_dep镜像构建go_server镜像,那么go_server依赖的第三方库将在golang_dep里找到。

以server举例,开始,在clinet和server文件夹的上一级目录新建Dockerfile,

在这里插入图片描述
内容:

#基础包独立出来,主要包括了第三方库
#后面的clinet和server将基于此包构建
FROM golang:latest AS golang_dep
#/go/src为golang的默认GOPATH
WORKDIR /go/src
#将dep相关内容COPY到/go/src
COPY ./vendor ./vendor
COPY Gopkg.lock .
COPY Gopkg.toml .

项目根目录下执行:

docker build -t goalng_dep .

得到golang_dep镜像:
在这里插入图片描述
然后进入到server文件夹,打开server/Dockerfile修改为如下,基于上个golang_dep镜像构建:

#继承自golang_dep构建,
FROM golang_dep AS server_base
WORKDIR /go/src/server
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .

FROM alpine:latest AS go_server
RUN apk --no-cache add ca-certificates
WORKDIR /app/server/
COPY --from=server_base /go/src/ .
CMD ["./server/server"] 

server文件夹下执行:

docker build -t go_server .

不再报错找不到protobuf库,得到go_server镜像,运行没问题:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u012740992/article/details/91841021