欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

docker从零构建go应用并部署到K8s集群

程序员文章站 2022-07-14 09:27:39
...

    本文详细介绍使用docker构建一个简单的go服务器镜像,并将容器实例部署到K8s集群中。

    使用的demo是一个go网络聊天室。具有客户端和服务器端。

编写go程序    

服务器端代码如下:

//chatroom.go
package main

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

//一个聊天服务器demo
func main(){
	ip := os.Args[1]
	listener,err := net.Listen("tcp",ip)
	if err!=nil{
		fmt.Println("listener start fail!")
	}

	go broadcaster()

	for{
		conn,err := listener.Accept()
		if err!=nil{
			fmt.Println("listener accept fail!")
			continue
		}
		go handleConn(conn)
	}
}

//广播器
type client chan <- string //对外发送消息的通道
var(
	entering = make(chan client)	//新客户进入 用来传递该用户的chan string
	leaving = make(chan client)		//新用户离开
	messages = make(chan string)//客户消息广播
)
func broadcaster(){
	clients := make(map[client]bool)	//所有连接的客户端	每个cli是一个chan string,就是handleConn里的ch
	for {
		select {
		case msg := <-messages:
			//把所有接收的消息广播给所有客户
			for cli := range clients {
				cli <- msg
			}
		case cli := <-entering:
			clients[cli] = true
		case cli := <-leaving:
			delete(clients,cli)
			close(cli)
		}
	}
}

//每个客户端维持的连接
func handleConn(conn net.Conn){
	ch := make(chan string)		//创建客户端ch 用于从select接收消息并传给remote
	go clientWriter(conn,ch)	//发送至客户端

	who := conn.RemoteAddr().String()
	ch <- "Hello your IP="+who+"\n Welcome come to zzp chat room!"
	entering<-ch	//ch注册到clients
	ch <- "in put you name:"
	input := bufio.NewScanner(conn)	//接收remote消息
	if input.Scan(){
		who = input.Text()
	}
	messages<-who + " has arrived!"	//通知其他client
	for input.Scan(){
		str := input.Text()
		if str=="\n"{
			continue
		}
		messages <- who + ": "+str
	}

	leaving <- ch	//注销
	messages <- who + " has left!"
	conn.Close()
}
//发送消息到客户端
func clientWriter(conn net.Conn,ch <-chan string){
	for msg:=range  ch{
		fmt.Fprintln(conn,msg)
	}
}

客户端代码如下:

//netcat.go
package main

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

func main(){
	ip := os.Args[1]
	conn,err := net.Dial("tcp",ip)
	if err!= nil {
		fmt.Println(err)
	}
	defer conn.Close()
	go mustCopy(os.Stdout,conn)	//接收程序
	mustCopy(conn, os.Stdin)	//输入程序
	conn.Close()
}
func mustCopy(dst io.Writer, src io.Reader){
	if _,err := io.Copy(dst,src); err!=nil{
		fmt.Println(err)
	}
}

go程序可以直接在系统中运行。执行 go build chatroom.go,可以生成 chatroom的可执行文件。

服务器运行方式:

./chatroom localhost:12048

服务器即可在本机的12048端口等待接收客户端。

客户端运行方式:

./netcat localhost:12048 

客户端即可与上文的服务器相连。 

构建服务器docker镜像

    这里有一个坑,一开始想着直接将 chatroom 的可执行文件放入镜像当中,但是发现执行不了,因为我的是MAC系统,编译后的可执行文件无法在镜像的Linux中执行。因此,需要将编译这一过程也放在docker中,这就需要用到docker的交付模式。

    我们先新建一个文件夹chatroomserver,在里面新建Dockerfile文件。同时将chatroom.go放在chatroomserver/goProject/chatroom内。文件结构如下。这里我们多了一个go.mod,里面只有一行:module chatroom 。用于go模块构建,不再赘述,具体功能请自行百度。如果不加,go是无法编译的。

chatroomserver/
--Dockerfile
--goProject/chatroom/
  --chatroom.go
  --go.mod

Dockerfile内容如下:

FROM golang:alpine AS build-env
ENV GO111MODULE=on
ENV CGO_ENABLED 0
ADD .  /go/src/app
RUN  apk --update add git tzdata
WORKDIR /go/src/app
RUN go build -v -o /go/src/app/app goProject/chatroom/chatroom.go

FROM ubuntu
COPY --from=build-env /go/src/app/app /app/server
COPY server.sh /app/
WORKDIR /app
EXPOSE 10248
CMD ["0.0.0.0:10248"]
ENTRYPOINT ["./server"]

    分为两段,第一段为编译go文件,第二段为构建可执行镜像环境。

    在chatroomserver下,执行构建镜像:

docker build -t chatroomserver:v1 .

然后即可运行镜像:

docker run -it -p 0.0.0.0:10248:10248 chatroomserver:v1  //默认监听10248

docker run -it -p 0.0.0.0:10248:10248 chatroomserver:v1 0.0.0.0:yourport  //也可以自己设置监听port

这里也有值得注意的地方:docker内的服务器监听ip需要设置为0.0.0.0,原因如下:

从Docker容器中访问宿主机网络

在默认的bridge模式下,docker0网络的默认网关即是宿主机。在Linux下,docker0网络通常会分配一个172.17.0.0/16的网段,其网关通常为172.17.0.1;macOS下的网段则为192.168.65.0/24,网关为192.168.65.1。在容器中使用该IP地址即可访问宿主机上的各种服务。

需要注意的是,这种情况下,经由docker0网桥而来的流量不经过宿主机的本地回环,因此需要将宿主机上的应用(MySQL,Redis等)配置为监听0.0.0.0。

上传到docker仓库

    构建完成后的镜像还只是本地镜像,需要上传到我们自己的仓库中。

docker tag chatroomserver:v1 yourresposity/chatroomserver:v1  //将image重新命名,增加用户名作为前缀

docker push yourresposity/chatroomserver //上传入仓库

K8s部署

  k8s我们使用一个deployment,一个service来进行应用部署

#chatroomserver.yaml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: chatroomserver
spec:
  selector:
    matchLabels:
      app: chatroomserver
  replicas: 1 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: chatroomserver
    spec:
      containers:
      - name: chatroomserver
        image: 997595461/chatroomserver
        ports:
        - containerPort: 10248
          protocol: TCP
#chatroomserverservice.yaml 
apiVersion: v1
kind: Service
metadata:
  name: chatroomserver
  labels:
    app: chatroomserver
spec:
  type: NodePort
  ports:
  - port: 10248
    targetPort: 10248
    nodePort: 30009
    protocol: TCP
  selector:
    app: chatroomserver

注意,nodeport需要30000起步~ 

执行命令

kubectl apply -f chatroomserverservice.yaml  

kubectl apply -f chatroomserver.yaml

通过 kubectl get pods 可以看到应用成功部署了

docker从零构建go应用并部署到K8s集群 

kubectl get service 可以看到service也成功部署了

docker从零构建go应用并部署到K8s集群

测试访问:

运行netcat:

./netcat ip:30009 

 docker从零构建go应用并部署到K8s集群

可以看到,成功进入了聊天室,demo完成。如果出现部署成功但无法访问的情况,可能是防火墙的原因,请注意解决。