AWS Open Source Blog

Containerize and deploy a gRPC application on AWS Fargate

These days, building an application distributed among processes, hosts, or even networks is much easier. This is partially due to an evolution in the protocols used to negotiate between different components of an application or service. This evolution is largely the result of the popularity of both the TCP and HTTP protocols as a means to connect services together. Many protocols have evolved while being built on top of HTTP, including XML, SOAP, and REST. Although each protocol has different features, they center around conforming to the underlying protocol (HTTP), but rely on external packages to ensure conformance.

gRPC, a high-performance, open source universal RPC framework, is an incubating Cloud Native Computing Foundation (CNCF) project and a newer protocol. The project is innovative in that it bakes in support for the external components, while acting as an envelope for sending structured messages through a distributed system. gRPC uses HTTP/2 transparently as a backend protocol and includes support for multiplexing, streaming, health checking, and authentication. gRPC also supports many different encodings, including JSON and protocol buffers (or protobuf for short).

Many companies across industries are considering gRPC because they want to standardize their communication between services developed by distributed development teams. By using gRPC, each development team can share their protocol buffer with other teams that implement their service, all without having to maintain multiple SDKs or writing additional code. gRPC and protocol buffers simplifies the implicit contract between development teams and fosters good practices for companies moving to microservices.

Containerizing a simple gRPC service: Greeter example

gRPC includes a number of examples, including a “Hello world” example called Greeter. This example is a simple client/server model service that can containerize easily. This slightly modified greeter specifies an endpoint as an environment variable and is available on GitHub.

Note: gRPC supports many programming languages. The Golang example is arbitrary.

To containerize the client and server, we need a Dockerfile for each. Use a multi-stage build to compile the code in a container image preloaded with Golang. Then, transfer the binary to a blank container image (known as a scratch image). With the server image, we also expose port 50051.

Diagram illustrating the greeter's workflow.

The greeter can communicate when run locally. Because we built a container using a Dockerfile for each, the greeter runs in containers:

Dockerfile.client

FROM golang:1.14.1 as builder
WORKDIR /app/
COPY * /app/
RUN CGO_ENABLED=0 go build -o /app/greeter_client .

FROM scratch
COPY --from=builder /app/greeter_client /greeter_client
CMD ["/greeter_client"]

Dockerfile.server

FROM golang:1.14.1 as builder
WORKDIR /app/
COPY * /app/
RUN CGO_ENABLED=0 go build -o /app/greeter_server .

FROM scratch
COPY --from=builder /app/greeter_server /greeter_server
EXPOSE 50051
CMD ["/greeter_server"]

Build the containers locally:

docker build -t greeter_client greeter_client/.
docker build -t greeter_server greeter_server/.

To test, start by running the server:

docker run -d \
    --name greeter_server \
    greeter_server

Run the client, and connect the client container to the server container using Docker links. Because we use the container name when creating a Docker link, also set the GREETER_ENDPOINT to greeter_server:50051:

docker run -itd \
    --name greeter_client \
    --link greeter_server \
    --env "GREETER_ENDPOINT=greeter_server:50051" \
    greeter_client

Verify the greeter by looking at the logs of each container:

$ docker logs greeter_client
Address: greeter_server:50051
Say Hello...
2020/04/15 17:15:56 Greeting: Hello world
$ docker logs greeter_server
2020/04/15 17:15:56 Received: world

Run the greeter on Amazon ECS using AWS Fargate

Diagram illustrating the workflow between the greeter client and greeter server.

To run the greeter on Amazon Elastic Container Service (Amazon ECS) using AWS Fargate, create an Amazon ECS Task definition for both the client and server. We can either upload the images created previously, or we can use an already-containerized version at docker.pkg.github.com/aws-samples/grpc-examples/greeter_client:v0.1.0 and docker.pkg.github.com/aws-samples/grpc-examples/greeter_server:v0.1.0.

To skip the following steps and deploy using AWS CloudFormation, choose Launch Stack. The repository also lists other methods to deploy the greeter, including using AWS Copilot.

Prerequisites

  • If we do not already have an Amazon ECS cluster, we can create one using the CLI command aws ecs create-cluster. This creates an ECS cluster named default.
  • We use the default VPC.
  • Make sure that the IAM role ecsTaskExecutionRole exists. Refer to the documentation for creating the task execution IAM role. Substitute your account ID for ${ACCOUNT_ID}.

Run the greeter server

Deploy the greeter server to Amazon ECS first. To deploy the client, we need the public IP address of the greeter server task.

Create a new Task definition using the Amazon ECS console:

  1. Select Fargate as the Launch type, and select Next step.
  2. Provide a Task Definition Name, such as greeter_server.
  3. Select a Task execution IAM role, possibly named ecsTaskExecutionRole. If you do not see a task execution role to select, create one from Creating the task execution IAM role.
  4. Provide the Task Memory and CPU (0.5 GB memory and 0.25 vCPU is sufficient).
  5. To add a container to the task, select Add container and provide the following parameters:
    • Container name: greeter_server
    • Image: docker.pkg.github.com/aws-samples/grpc-examples/greeter_server:v0.1.0 (or upload your image to Amazon Elastic Container Registry (Amazon ECR) and use that repository).
    • Memory Limits (MiB): Soft Limit, 128
    • Port mappings: Container port 50051, Protocol tcp
    • Under Storage and Logging, ensure Auto-configure CloudWatch Logs is checked.
    • Choose Add.
  6. Navigate to the bottom of the page and choose Create.

Create a new service using the Amazon ECS console:

  1. Select Fargate as the Launch type.
  2. Select the Task Definition Family and Revision from the previous step.
  3. Provide a Service name, such as greeter_server.
  4. Set the Number of tasks to 1.
  5. Choose Next.
  6. For Cluster VPC, provide the VPC ID of the default VPC.
  7. For Subnets, select at least one subnet from the list.
  8. Create a new security group.
    • Choose Edit next to Security groups.
    • For Inbound rules for security group, change the Type to Custom TCP and set the Port range to 50051. (Note: This is a bad security posture and only permissible for this demo. Your application should have more strict security rules.)
    • Choose Save.
  9. Ensure that Auto-assign public IP is set to Enabled.
  10. For Service discovery, select Enable service discovery integration. The default settings name the service greeter_server.local.
  11. Choose Next step.
  12. Choose Next step to skip auto scaling.
  13. Choose Create Service to launch the greeter server.

Run the greeter client

Create a new Task definition using the Amazon ECS console.

  1. Select Fargate as the Launch type and click Next step.
  2. Provide a Task definition name like greeter_client.
  3. Select a Task execution IAM role, possibly named ecsTaskExecutionRole. If you do not see a task execution role to select, create one from Creating the task execution IAM role.
  4. Provide the Task Memory and CPU (0.5 GB memory and 0.25 vCPU is sufficient).
  5. To add a container to the task, click Add container and provide the following parameters:
    • Container name: greeter_client
    • Image: docker.pkg.github.com/aws-samples/grpc-examples/greeter_client:v0.1.0 (or upload your previous image to Amazon ECR and use that repository).
    • Memory Limits (MiB): Soft Limit, 128
    • Port mappings: Container port 50051, Protocol tcp
    • For Environment variables, add Key as GREETER_ENDPOINT and Value as greeter_server.local:50051. (Note: We’re using service discovery for the client to find the server. If you previously changed the service discovery namespace or service name, use that value here.)
    • For Storage and Logging, ensure Auto-configure CloudWatch Logs is checked.
    • Choose Add.
  6. Navigate to the bottom of the page and choose Create.

Run a new Task using the Amazon ECS console:

  1. Choose Switch to launch type and select Fargate as the Launch type.
  2. Select the Task Definition Family and Revision from the previous step.
  3. Provide a Service name, such as greeter_client.
  4. Set the Number of tasks to 1.
  5. Under Cluster VPC, provide the VPC ID of the default VPC.
  6. Under Subnets, select at least one subnet from the list.
  7. Use the default security group.
    • Choose Edit next to Security groups.
    • Check next to the default security group.
    • Choose Save.
  8. Ensure that Auto-assign public IP is set to Enabled.
  9. Select Run Task to launch the greeter client.

Verify the client and server

  1. Open the Amazon CloudWatch Logs console.
  2. Find the /ecs/greeter_client and /ecs/greeter_server log groups.
  3. For each, click on the most recent log stream and view the logs.

Your logs should be similar to:

greeter_client:

Address: greeter_server.local:50051
Say Hello...
2020/04/15 20:00:21 Greeting: Hello world

greeter_server:

2020/04/15 20:00:20 Received: world

Cleanup

  1. Navigate to the Amazon ECS console and select the default cluster.
  2. Check next to the greeter_server service and choose Delete. In the modal window:
    • Check to delete the service discovery service.
    • Enter the confirmation phrase.
    • Choose Delete to confirm.
  3. Check next to the greeter_client service and choose Delete. In the modal window:
    • Check to delete the service discovery service.
    • Enter the confirmation phrase.
    • Choose Delete to confirm.
  4. Either keep the task definitions and repositories for reference, or delete them.
    • From the Amazon ECS console, choose Task Definitions.
    • Choose the greeter_client task definition, then check next to each revision, choose Actions, Deregister. Confirm by choosing Deregister.
    • Choose the greeter_server task definition, then check next to each revision, choose Actions, Deregister. Confirm by choosing Deregister.
    • From the Amazon ECR console, choose Repositories.
    • Select the greeter_client repository, then Delete. Confirm by choosing Delete.
    • Select the greeter_server repository, then Delete. Confirm by choosing Delete.

What’s next?

According to the preceding logs, the client successfully “said hello” to the server, and the server responded. The two services are communicating over gRPC, whether running locally or in AWS Fargate.

To expand the greeter, consider adding another service (for example, that says “goodbye”) or running the client in a Lambda function.

TAGS:
Theo Salvo

Theo Salvo

Theo is a Specialist Solutions Architect for AWS App Mesh and AWS Cloud Map. In his role, he defines the AWS engagement strategy and go-to-market plan for AWS App Mesh and AWS Cloud Map. He is a leader in the containers technical field community and drives education of container-related specialty topics such as networking, security, and modern applications. You can find Theo on Twitter @TheodoreJSalvo