⬆️ ⬇️

How to create a simple microservice on Golang and gRPC and containerize it using Docker

Hello, Habr! I present to you the translation of the article “Go, gRPC and Docker” by Mat Evans.



There are many articles about sharing Go and Docker. Creating containers that can interact with customers and among themselves is very easy. The following is a small example of how this is done at a basic level.



What do we create?



We will create very simple client and server, interacting with each other using gRPC . The server will be located inside the Docker container so that it can be easily deployed.



Suppose we need a service that receives a string from a client and returns a string with a reverse character order. For example, send a “cat” and get a “current” in response.

')

.proto file



.proto- file describes what operations our service will carry out and what data it will exchange. Create the proto folder in the project, and the reverse.proto file in it



syntax = "proto3"; package reverse; service Reverse { rpc Do(Request) returns (Response) {} } message Request { string message = 1; } message Response { string message = 1; } 


A function that is called remotely on the server and returns data to the client is marked as rpc . Data structures used to exchange information between interacting nodes are marked as message . Each message field must be assigned a sequence number. In this case, our function receives Request messages from the client and returns Response messages.

Once we have created a .proto file, it is necessary to get the .go file of our service. To do this, run the following console command in the proto folder:



 $ protoc -I . reverse.proto --go_out=plugins=grpc:. 


Of course, you first need to build gRPC .

Executing the above command will create a new .go file containing methods for creating the client, server, and messages that they exchange. If we call godoc , we will see the following:



 $ godoc . PACKAGE DOCUMENTATION package reverse import "." Package reverse is a generated protocol buffer package. It is generated from these files: reverse.proto It has these top-level messages: Request Response .... 


Customer



It would be nice if our client worked like this:



 reverse "this is a test" tset a si siht 


Here is the code that creates the gRPC client using the data structures generated from the .proto file:



 package main import ( "context" "fmt" "os" pb "github.com/matzhouse/go-grpc/proto" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" ) func main() { opts := []grpc.DialOption{ grpc.WithInsecure(), } args := os.Args conn, err := grpc.Dial("127.0.0.1:5300", opts...) if err != nil { grpclog.Fatalf("fail to dial: %v", err) } defer conn.Close() client := pb.NewReverseClient(conn) request := &pb.Request{ Message: args[1], } response, err := client.Do(context.Background(), request) if err != nil { grpclog.Fatalf("fail to dial: %v", err) } fmt.Println(response.Message) } 




Server



The server will use the same generated .go file. However, it only defines the server interface, but we have to implement the logic on our own:



 package main import ( "net" pb "github.com/matzhouse/go-grpc/proto" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" ) func main() { listener, err := net.Listen("tcp", ":5300") if err != nil { grpclog.Fatalf("failed to listen: %v", err) } opts := []grpc.ServerOption{} grpcServer := grpc.NewServer(opts...) pb.RegisterReverseServer(grpcServer, &server{}) grpcServer.Serve(listener) } type server struct{} func (s *server) Do(c context.Context, request *pb.Request) (response *pb.Response, err error) { n := 0 // reate an array of runes to safely reverse a string. rune := make([]rune, len(request.Message)) for _, r := range request.Message { rune[n] = r n++ } // Reverse using runes. rune = rune[0:n] for i := 0; i < n/2; i++ { rune[i], rune[n-1-i] = rune[n-1-i], rune[i] } output := string(rune) response = &pb.Response{ Message: output, } return response, nil } 




Docker



I assume that you know what Docker is and what it is for. Here is our Dockerfile :



 FROM golang:1.12 ADD . /go/src/github.com/matzhouse/go-grpc/server RUN go install github.com/matzhouse/go-grpc/server ENTRYPOINT ["/go/bin/server"] EXPOSE 5300 


The assembly code for the Docker image is written here. We will analyze it line by line.



 FROM golang:1.12 


This command means that we want to create an image of our application on the basis of a previously created image, namely golang . This is a Docker image with an already configured environment for building and running programs written in Go .



 ADD . /go/src/github.com/matzhouse/go-grpc/server 


This command copies the source code of our application into the GOPATH / src container.



 RUN go install github.com/matzhouse/go-grpc/server 


This command collects our application from the sources copied to the container and installs it in the GOPATH / bin container folder .



 ENTRYPOINT ["/go/bin/server"] 


This command configures the container to work as an executable program. In it, we indicate the path to the application executable and, if necessary, command line arguments.



 EXPOSE 5300 


With this command, we tell the container which ports should be accessible from the outside.



Server start



We need to start the container with our server application.

First you need to build the image based on the instructions from the Dockerfile :



 $ sudo docker build -t matzhouse/grpc-server . Sending build context to Docker daemon 31.76 MB Step 1/5 : FROM golang ---> a0c61f0b0796 Step 2/5 : ADD . /go/src/github.com/matzhouse/go-grpc ---> 9508be6501c1 Removing intermediate container 94dc6e3a9a20 Step 3/5 : RUN go install github.com/matzhouse/go-grpc/server ---> Running in f3e0b993a420 ---> f7a0370b7f7d Removing intermediate container f3e0b993a420 Step 4/5 : ENTRYPOINT /go/bin/server ---> Running in 9c9619e45df4 ---> fb34dfe1c0ea Removing intermediate container 9c9619e45df4 Step 5/5 : EXPOSE 5300 ---> Running in 0403390af135 ---> 008e09b9aebd Removing intermediate container 0403390af135 Successfully built 008e09b9aebd 


Now we can see this image in the list:



 $ docker images REPOSITORY TAG IMAGE ID ... matzhouse/grpc-server latest 008e09b9aebd ... 


Fine! We have an image of our server application, with which you can launch its container using the following command:



 $ docker run -it -p 5300:5300 matzhouse/grpc-server 


In this case, the so-called port forwarding . Note that for it we need both the EXPOSE statement and the -p argument.



Client launch



Containerization of the client will not give big advantages, so let's start it in the usual way:



 $ go build -o reverse $ ./reverse "this is a test" tset a si siht 


Thanks for reading!

Source: https://habr.com/ru/post/461279/



All Articles