每天进步一点点:使用Go gRPC 和k8s开始你的微服务之旅
Posted Go技术大全
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每天进步一点点:使用Go gRPC 和k8s开始你的微服务之旅相关的知识,希望对你有一定的参考价值。
原文链接:https://outcrawl.com/getting-started-microservices-go-grpc-kubernetes/
This article aims to provide a simple introduction to building microservices in Go, using gRPC, and deploying them to a Kubernetes cluster. It shows how to set up two containerized, scalable services, talking to each other using request/response mechanism.
One microservice will be responsible for calculating the greatest common divisor (GCD) of two integers. That's not something that requires building a separate service, but it will serve as an introductory example just fine. The other will serve a public REST API, and depend on first service when needed.
Prerequisites
The best way to get a running Kubernetes cluster for development, at least for now, is to use Minikube.
Install Minikube, kubectl, Docker and Protocol Buffers compiler.
Run Minikube. Certain commands will require root privileges.
$ minikube start [--vm-driver=<driver>]
$ kubectl cluster-info
To speed up the development, set docker to reuse Docker daemon running inside the Minikube virtual machine.
$ eval $(minikube docker-env)
This way, you don't have to push images to a Docker registry. For running your apps on Google Container Engine you'd use Container Registry or set up a private container registry inside the cluster.
Defining communication protocol
Services will interact synchronously, which means a client calls a service and waits for the response. One of the popular frameworks, that provides this functionality, is gRPC. To use it, you need to write definitions for message types and services in Protocol Buffer's interface definition language and compile them.
Create gcd.proto
file inside pb
directory, which is located inside your project's root directory.
syntax = "proto3";package pb;message GCDRequest { uint64 a = 1; uint64 b = 2;
}message GCDResponse { uint64 result = 1;
}service GCDService { rpc Compute (GCDRequest) returns (GCDResponse) {} }
Navigate to the pb
directory and run the following command.
$ protoc -I . --go_out=plugins=grpc:. ./*.proto
Compilation should produce gcd.pb.go
file.
Greatest common divisor service
The GCD service will use auto-generated code to serve a RPC endpoint which calculates the GCD.
Create main.go
file inside gcd
directory. Make sure you import correct packages.
package mainimport ( "log"
"net"
// Change this for your own project
"github.com/tinrab/kubernetes-go-grpc-tutorial/pb"
context "golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection")
In the main
function, register a server type which will handle requests. Then start the gRPC server.
type server struct{}func main() {
lis, err := net.Listen("tcp", ":3000") if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGCDServiceServer(s, &server{})
reflection.Register(s) if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
Declare the Compute
handler function. This makes the server
type conform to the auto-generated pb.GCDServiceServer
interface.
func (s *server) Compute(ctx context.Context, r *pb.GCDRequest) (*pb.GCDResponse, error) {
a, b := r.A, r.B for b != 0 {
a, b = b, a%b
} return &pb.GCDResponse{Result: a}, nil}
Frontend API service
Frontend service uses gin web framework to serve a REST API and calls the GCD service for the actual calculation.
Create a client to communicate with the GCD service inside main
function. Because Kubernetes (v1.3+) has a built-in DNS service, you can refer to the GCD service with the name "gcd-service", defined later on.
func main() {
conn, err := grpc.Dial("gcd-service:3000", grpc.WithInsecure()) if err != nil {
log.Fatalf("Dial failed: %v", err)
}
gcdClient := pb.NewGCDServiceClient(conn)
}
After that, declare a handler for /gcd/:a/:b
endpoint which reads parameters A
and B
, and then calls the GCD service.
r := gin.Default()
r.GET("/gcd/:a/:b", func(c *gin.Context) { // Parse parameters
a, err := strconv.ParseUint(c.Param("a"), 10, 64) if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameter A"}) return
}
b, err := strconv.ParseUint(c.Param("b"), 10, 64) if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameter B"}) return
} // Call GCD service
req := &pb.GCDRequest{A: a, B: b} if res, err := gcdClient.Compute(c, req); err == nil {
c.JSON(http.StatusOK, gin.H{ "result": fmt.Sprint(res.Result),
})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
})
Run the server.
if err := r.Run(":3000"); err != nil {
log.Fatalf("Failed to run server: %v", err)
}
Building Docker images
Now that your services are ready, you need to containerize them. Create Dockerfiles, one for each service, inside your project's root directory. Dockerfiles can't be located in a subdirectory, because Docker's COPY
command can't copy files from the parent directory, which is needed in this example.
Create Dockerfile.api
.
FROM golang:1.9.1# Change this path!WORKDIR /go/src/github.com/tinrab/kubernetes-go-grpc-tutorial/apiCOPY api .COPY pb ../pbRUN go get -v ./...RUN go install -v ./...EXPOSE 3000CMD [ "api" ]
And the Dockerfile.gcd
.
FROM golang:1.9.1WORKDIR /go/src/github.com/tinrab/kubernetes-go-grpc-tutorial/gcdCOPY gcd .COPY pb ../pbRUN go get -v ./...RUN go install -v ./...EXPOSE 3000CMD [ "gcd" ]
Build both images. If you switched to Minikube's Docker daemon, they will become available inside the VM.
$ docker build -t local/gcd -f Dockerfile.gcd .
$ docker build -t local/api -f Dockerfile.api .
Deploying to Kubernetes cluster
For each service, you'll need to configure two Kubernetes objects—a deployment and a service.
Explained simply, deployments manage sets of pods to keep the "desired state" of the cluster. Pods are collections of containers. Because they are created and destroyed dynamically, services are needed to provide fixed addresses by which to access them. Which pods are targeted by which services, is determined by label selectors.
Configuration files below are fairly standard. The important parts to keep note of are the ports, names of Docker images built earlier, and labels. Also, imagePullPolicy
is set to Never
to ensure Kubernetes uses images built locally.
Create gcd.yaml
file. It declares a deployment and a service in the same file. Another practice is to separate them into gcd-deployment.yaml
and gcd-service.yaml
.
apiVersion: apps/v1beta1kind: Deploymentmetadata: name: gcd-deployment labels: app: gcdspec: selector: matchLabels: app: gcd replicas: 3 template: metadata: labels: app: gcd spec: containers: - name: gcd image: local/gcd imagePullPolicy: Never ports: - name: gcd-service containerPort: 3000---apiVersion: v1kind: Servicemetadata: name: gcd-servicespec: selector: app: gcd ports: - port: 3000 targetPort: gcd-service
Create api.yaml
file. The service type is set to NodePort
, which makes this service accessible outside of the cluster. For the GCD service, this is set to a default value of ClusterIP
, which means a "cluster-internal" IP address.
apiVersion: apps/v1beta1kind: Deploymentmetadata: name: api-deployment labels: app: apispec: selector: matchLabels: app: api replicas: 1 template: metadata: labels: app: api spec: containers: - name: api image: local/api imagePullPolicy: Never ports: - name: api-service containerPort: 3000---apiVersion: v1kind: Servicemetadata: name: api-servicespec: type: NodePort selector: app: api ports: - port: 3000 targetPort: api-service
To create these resources inside the cluster, run the following commands.
$ kubectl create -f api.yaml
$ kubectl create -f gcd.yaml
Check if all pods are running. By specifying -w
flag, you can watch for changes.
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
api-deployment-778049682-3vd0z 1/1 Running 0 3s
gcd-deployment-544390878-0zgc8 1/1 Running 0 2s
gcd-deployment-544390878-p78g0 1/1 Running 0 2s
gcd-deployment-544390878-r26nx 1/1 Running 0 2s
As set in the configuration files, API service runs on a single pod and the GCD service runs on 3.
Get the URL of the API service.
$ minikube service api-service --url
Finally, try it out.
$ curl http://192.168.99.100:32602/gcd/294/462
Conclusion
This article showed how to get a simple stateless application running on a Kubernetes cluster. Future posts will demonstrate more thorough and interesting examples of building applications with Go and Kubernetes.
Until then, learn more about concepts of Kubernetes and find out how to perform specific tasks.
Entire source code is available on GitHub.
以上是关于每天进步一点点:使用Go gRPC 和k8s开始你的微服务之旅的主要内容,如果未能解决你的问题,请参考以下文章
每天进步一点点:为Go语言代码编写CI/CD工具的Makefile