本文详细介绍使用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 可以看到应用成功部署了

 

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

测试访问:

运行netcat:

./netcat ip:30009 

 

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

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐