Hola Habr! Les presento la traducción del artículo
"Go, gRPC and Docker" de Mat Evans.
Hay muchos artículos sobre cómo compartir Go y Docker. Crear contenedores que puedan interactuar con los clientes y entre ellos es muy fácil. El siguiente es un pequeño ejemplo de cómo se hace esto en un nivel básico.
¿Qué creamos?
Crearemos un cliente y servidor muy simples, interactuando entre nosotros usando
gRPC . El servidor estará ubicado dentro del contenedor
Docker para que pueda implementarse fácilmente.
Supongamos que necesitamos un servicio que recibe una cadena de un cliente y devuelve una cadena con un orden inverso de caracteres. Por ejemplo, envíe un "gato" y obtenga una "corriente" en respuesta.
archivo .proto
El archivo
.proto describe qué operaciones llevará a cabo nuestro servicio y qué datos intercambiará. Cree la carpeta
proto en el proyecto y el archivo
reverse.proto en ella.
syntax = "proto3"; package reverse; service Reverse { rpc Do(Request) returns (Response) {} } message Request { string message = 1; } message Response { string message = 1; }
Una función que se llama de forma remota en el servidor y devuelve datos al cliente se marca como
rpc . Las estructuras de datos utilizadas para intercambiar información entre nodos interactuantes se marcan como
mensaje . A cada campo de mensaje se le debe asignar un número de secuencia. En este caso, nuestra función recibe mensajes de
solicitud del cliente y devuelve mensajes de
respuesta .
Una vez que hemos creado un archivo
.proto , es necesario obtener el archivo
.go de nuestro servicio. Para hacer esto, ejecute el siguiente comando de consola en la carpeta
proto :
$ protoc -I . reverse.proto --go_out=plugins=grpc:.
Por supuesto, primero necesitas
construir gRPC .
La ejecución del comando anterior creará un nuevo archivo
.go que contiene métodos para crear el cliente, el servidor y los mensajes que intercambian. Si llamamos a
godoc , veremos lo siguiente:
$ 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 ....
Cliente
Sería bueno si nuestro cliente trabajara así:
reverse "this is a test" tset a si siht
Aquí está el código que crea el cliente
gRPC utilizando estructuras de datos generadas a partir del archivo
.proto :
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) }
Servidor
El servidor usará el mismo archivo
.go generado. Sin embargo, solo define la interfaz del servidor, pero tenemos que implementar la lógica por nuestra cuenta:
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
Docker
Supongo que sabes qué
es Docker y para qué sirve. Aquí está nuestro
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
El código de ensamblaje para la imagen de
Docker se escribe aquí. Lo analizaremos línea por línea.
FROM golang:1.12
Este comando significa que queremos crear una imagen de nuestra aplicación sobre la base de una imagen creada previamente, a saber,
golang . Esta es una imagen de
Docker con un entorno ya configurado para crear y ejecutar programas escritos en
Go .
ADD . /go/src/github.com/matzhouse/go-grpc/server
Este comando copia el código fuente de nuestra aplicación en el
contenedor GOPATH / src .
RUN go install github.com/matzhouse/go-grpc/server
Este comando recopila nuestra aplicación de las fuentes copiadas en el contenedor y la instala en la
carpeta GOPATH / bin container.
ENTRYPOINT ["/go/bin/server"]
Este comando configura el contenedor para que funcione como un programa ejecutable. En él, indicamos la ruta al ejecutable de la aplicación y, si es necesario, los argumentos de la línea de comandos.
EXPOSE 5300
Con este comando, le decimos al contenedor qué puertos deben ser accesibles desde el exterior.
Inicio del servidor
Necesitamos ejecutar el contenedor con nuestra aplicación de servidor.
Primero debe crear la imagen según las instrucciones del
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
Ahora podemos ver esta imagen en la lista:
$ docker images REPOSITORY TAG IMAGE ID ... matzhouse/grpc-server latest 008e09b9aebd ...
Genial Tenemos una imagen de nuestra aplicación de servidor, con la que puede iniciar su contenedor utilizando el siguiente comando:
$ docker run -it -p 5300:5300 matzhouse/grpc-server
En este caso, el llamado
reenvío de puertos . Tenga en cuenta que para ello necesitamos tanto la instrucción
EXPOSE como el argumento
-p .
Lanzamiento del cliente
La contenedorización del cliente no dará grandes ventajas, así que comencemos de la manera habitual:
$ go build -o reverse $ ./reverse "this is a test" tset a si siht
Gracias por leer!