grpc-server-go provides a production-ready gRPC server for Golang in a Docker container.
This Dockerfile implements a base container for microservices implemented in Go.
The container does the following:
- It includes a main.go for a gRPC server that imports any code that you put into
service/main.go
. - Reflection is enabled in the resulting gRPC service.
- It includes a complete health check to allow for zero-downtime updates.
- It runs a grpcui.
- It picks up additional JSON-based health checks and runs them, validating if the RPC call generates the expected result. More below.
A typical grpc-server-go Dockerfile will contain two stages:
- The first stage is based on grpc-server-go to provide
an environment for compiling your Go code; this environment contains the grpc server as
cmd/main.go
- The second stage produces a light weight production-ready container with a gRPC server.
Example:
FROM knipknap/grpc-server-go:latest as build-env
WORKDIR /app
COPY proto/service.proto proto/
COPY proto/options.proto proto/
COPY service/main.go service/
RUN make build
FROM golang:1.13-alpine
# Make sure to include all these COPY commands, they are required.
COPY --from=build-env /app/start /app/entrypoint.sh /app/healthcheck.sh ./
COPY --from=build-env /app/healthchecks /app/
COPY --from=build-env /bin/grpc_health_probe /bin
COPY --from=build-env /go/bin/grpcui /usr/local/bin/grpcui
COPY --from=build-env /go/bin/grpcurl /usr/local/bin/grpcurl
COPY --from=build-env /usr/bin/find /usr/bin
RUN adduser -S -u 10001 user
USER user
ENV GRPC_PORT=8181
ENV GRPCUI_PORT=8080
HEALTHCHECK --interval=30s --timeout=2s --start-period=20s CMD ./healthcheck.sh -addr=:$GRPC_PORT
CMD ["./entrypoint.sh"]
GRPC_PORT
: The port of the gRPC server, by default 8181GRPCUI_PORT
: The port of the gRPC user interface, by default 8080DEBUG
: To change the zap logger from Production to Development
Add your code as follows:
- Make sure that your container adds a
/app/service/main.go
. The package could be named "service" - Place your .proto files in the
/app/proto/
folder. The container includes a Makefile that will compile them during the build stage (see "make build" in the example above) - Make sure that your main package includes a RegisterService function with the following signature:
package main
import (
"yourmodule.com/path/to/proto"
"go.uber.org/zap"
"google.golang.org/grpc"
)
func RegisterService(grpcServer *grpc.Server, logger *zap.SugaredLogger) {
// You should register your gRPC service here like this:
proto.RegisterServiceServer(grpcServer, NewYourService(logger))
}
type YourService struct {
logger *zap.SugaredLogger
}
func NewYourService(logger *zap.SugaredLogger) proto.ServiceServer {
return &YourService{
logger: logger,
}
}
In addition to implementing the standard health check protocol, this container also provide a simple mechanism for more powerful health checks based on grpcurl.
The container will execute the gRPC calls every time when Docker requests a status from the healthcheck.
To use this mechanism, simply put JSON files under /app/healthchecks/
, with the following
structure:
healthchecks/
├── check1
│ └── my.host.Service
│ └── MyMethod
│ ├── input.json
│ └── output.json
└── check2
└── my.host.Service2
├── MyMethod1
│ ├── input.json
│ └── output.json
└── MySuperMethod
├── input.json
└── output.json
In this example, the healthcheck would call my.host.Service/MyMethod, passing the input from input.json as parameters. The return value has to match the contents output.json exactly, otherwise the check is considered failed.
my.host.Service2/MyMethod1 and MySuperMethod are both called and checked in the same manner.