So erstellen Sie einen einfachen Microservice für Golang und gRPC und containerisieren ihn mit Docker

Hallo Habr! Ich präsentiere Ihnen die Übersetzung des Artikels „Go, gRPC and Docker“ von Mat Evans.

Es gibt viele Artikel über das Teilen von Go und Docker. Das Erstellen von Containern, die mit Kunden und untereinander interagieren können, ist sehr einfach. Das Folgende ist ein kleines Beispiel dafür, wie dies auf einer grundlegenden Ebene durchgeführt wird.

Was schaffen wir?


Wir werden sehr einfache Clients und Server erstellen, die mithilfe von gRPC miteinander interagieren. Der Server befindet sich im Docker- Container, sodass er problemlos bereitgestellt werden kann.

Angenommen, wir benötigen einen Dienst, der eine Zeichenfolge von einem Client empfängt und eine Zeichenfolge mit umgekehrter Zeichenreihenfolge zurückgibt. Senden Sie beispielsweise eine "Katze" und erhalten Sie eine "aktuelle" Antwort.

.proto-Datei


.proto- file beschreibt, welche Vorgänge unser Service ausführen und welche Daten er austauschen wird. Erstellen Sie den Proto- Ordner im Projekt und die Datei reverse.proto darin

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

Eine Funktion, die remote auf dem Server aufgerufen wird und Daten an den Client zurückgibt, wird als rpc markiert. Datenstrukturen, die zum Informationsaustausch zwischen interagierenden Knoten verwendet werden, werden als Nachricht markiert. Jedem Nachrichtenfeld muss eine Sequenznummer zugewiesen werden. In diesem Fall empfängt unsere Funktion Nachrichten vom Anforderungstyp vom Client und gibt Nachrichten vom Antworttyp zurück .
Sobald wir eine .proto- Datei erstellt haben, ist es notwendig, die .go- Datei unseres Dienstes abzurufen . Führen Sie dazu den folgenden Konsolenbefehl im Proto- Ordner aus:

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

Natürlich müssen Sie zuerst gRPC erstellen .
Durch Ausführen des obigen Befehls wird eine neue GO- Datei erstellt, die Methoden zum Erstellen des Clients, des Servers und der Nachrichten enthält, die sie austauschen. Wenn wir Godoc nennen, werden wir Folgendes sehen:

 $ 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 .... 

Kunde


Es wäre schön, wenn unser Kunde so arbeiten würde:

 reverse "this is a test" tset a si siht 

Hier ist der Code, der den gRPC- Client mithilfe von Datenstrukturen erstellt, die aus der .proto- Datei generiert wurden:

 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


Der Server verwendet dieselbe generierte GO- Datei. Es definiert jedoch nur die Serverschnittstelle, aber wir müssen die Logik selbst implementieren:

 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


Ich gehe davon aus, dass Sie wissen, was Docker ist und wofür es ist. Hier ist unser 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 

Der Assembler-Code für das Docker- Image wird hier geschrieben. Wir werden es Zeile für Zeile analysieren.

 FROM golang:1.12 

Dieser Befehl bedeutet, dass wir ein Image unserer Anwendung auf der Grundlage eines zuvor erstellten Images erstellen möchten, nämlich Golang . Dies ist ein Docker- Image mit einer bereits konfigurierten Umgebung zum Erstellen und Ausführen von in Go geschriebenen Programmen.

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

Dieser Befehl kopiert den Quellcode unserer Anwendung in den GOPATH / src- Container.

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

Dieser Befehl sammelt unsere Anwendung aus den in den Container kopierten Quellen und installiert sie im Containerordner GOPATH / bin .

 ENTRYPOINT ["/go/bin/server"] 

Dieser Befehl konfiguriert den Container so, dass er als ausführbares Programm arbeitet. Darin geben wir den Pfad zur ausführbaren Datei der Anwendung und gegebenenfalls Befehlszeilenargumente an.

 EXPOSE 5300 

Mit diesem Befehl teilen wir dem Container mit, auf welche Ports von außen zugegriffen werden soll.

Serverstart


Wir müssen den Container mit unserer Serveranwendung ausführen.
Zuerst müssen Sie das Image basierend auf den Anweisungen aus der Docker-Datei erstellen :

 $ 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 

Jetzt können wir dieses Bild in der Liste sehen:

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

Großartig! Wir haben ein Image unserer Serveranwendung, mit der Sie den Container mit dem folgenden Befehl starten können:

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

In diesem Fall ist das sogenannte Portweiterleitung . Beachten Sie, dass wir dafür sowohl die EXPOSE- Anweisung als auch das -p- Argument benötigen.

Client-Start


Die Containerisierung des Clients bringt keine großen Vorteile. Beginnen wir also auf die übliche Weise:

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

Danke fürs Lesen!

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


All Articles