docker从零构建go应用并部署到K8s集群
本文详细介绍使用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,原因如下:
在默认的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完成。如果出现部署成功但无法访问的情况,可能是防火墙的原因,请注意解决。