Deploying a gRPC service in ECS with the EC2 launch type
Tags: grpc, aws, ecs, go
Published: 2020-11-01 16:32:21

The AWS team recently launched end-to-end support for HTTP/2 on Application Load Balancers, as [announced on their blog](https://aws.amazon.com/about-aws/whats-new/2020/10/application-load-balancers-enable-grpc-workloads-end-to-end-http-2-support/). This is great news if you want to upgrade your stack to use modern tooling like [gRPC](https://grpc.io/). The AWS team has also [published a post](https://aws.amazon.com/blogs/opensource/containerize-and-deploy-a-grpc-application-on-aws-fargate/) on how to setup and deploy a gRPC application on ECS, using the **Fargate** launch type. This post aims to explain how to achieve the same using the **EC2** launch type.

## The Problem 
Normally, you might directly go ahead and create a service from the ECS console. This will automatically create a target group for you. But unfortunately, the automatic target group creation does not seem to support gRPC (at least as of Nov 1st 2020). Hence, this blog post.

## The tl;dr Solution

So instead, we have to follow this flow:

    1. Create Target Group first, with the gRPC protocol
    2. Setup the routing in your ALB to let the traffic flow into your target group
    3. Create Service using this newly created Target Group

# The step-by-step Solution

Note: In this post, I will be using golang for the service and client. However, it should be applicable no matter what language you are using for your gRPC service.

I will be using a modified version of the [Greeter service example from the grpc-go](https://github.com/grpc/grpc-go/tree/master/examples/helloworld) repository. Complete version of the modified code can be found on my Github: https://github.com/chaitan94/grpc-on-ecs

```sh
git clone https://github.com/chaitan94/grpc-on-ecs.git
cd grpc-on-ecs
```

To follow-along with the step-by-step solution, clone the repo, and follow these steps:

## Step 1: Build the service as a docker image
### Generating the stubs
```sh
cd greeter_server

# We need to generate the gRPC stubs.
# Before doing that, make sure you have these installed first:
#  - protoc
#  - protoc-gen-go
#  - protoc-gen-go-grpc
protoc --go_out=./gen --go_opt=paths=source_relative \
    --go-grpc_out=./gen --go-grpc_opt=paths=source_relative \
    -I../helloworld helloworld.proto

# Building the server as a docker image
docker build -t greeter_server .
```

**(Optional) Step 1.5: Testing locally**
Run the server image locally:
```sh
docker run -it --name greeter_server -p 50051:50051 greeter_server
```
While the server is running, in a separate terminal, run the client:
```sh
cd client
go run main.go
```
If you can see the message `Greeting: Hello world`, you ran everything successfully! :)

## Step 2: Deploy the server image to ECR
1. Create an ECR repository, say with the name 'greeter_server'
1. Push your image to ECR (modify accordingly to match your AWS region and ECR repo tag)
```sh
$(aws ecr get-login --no-include-email --region ap-south-1)
docker tag greeter_server 123123123123.dkr.ecr.ap-south-1.amazonaws.com/greeter_server:latest
docker push 123123123123.dkr.ecr.ap-south-1.amazonaws.com/greeter_server:latest
```

## Step 3: Create a new Task Definition
1. Select **EC2** as the Launch type, and select **Next step**.
1. Provide a **Task Definition Name**, such as greeter_server.
1. Provide the **Task Memory** and **CPU** (0.5 GB memory and 0.25 vCPU is sufficient).
1. To add a container to the task, select **Add container** and provide the following parameters:
    - Container name: greeter_server
    - Image: 123123123123.dkr.ecr.ap-south-1.amazonaws.com/greeter_server:latest
    - Memory Limits (MiB): Soft Limit, 128
    - Port mappings: Host port: 0, Container port: 50051, Protocol: tcp
    - Choose **Add**.
1. Navigate to the bottom of the page and choose **Create**.

## Step 4: Setting up the target group and load balancer
As explained above, we need to create target group manually, otherwise your ECS service won't work over the gRPC protocol.
1. Create Target Group
    1. Go to EC2 console -> Target groups.
    1. Click **Create Target Group**.
    1. Provide a **Target Group Name**, such as greeter-server.
    1. Leave **Protocol** and **Port** to the default values, i.e., "HTTP" and "80".
    1. Set **Protocol version** to "gRPC". (Note: this is different from the protocol we selected in the previous step)
    1. In health checks section, set **Health check path** to "/helloworld.Greeter/".
    1. Click **Next**.
    1. In the next step, no need to register any targets as ECS would do that for us. Proceed to click "Create target group".
1. Setup your load balancer to receive traffic and forward it to your newly created target group.
    1. Go to your ALB's **Listeners** tab.
    1. Assuming you are using a HTTPS listener, Click **View/edit rules**, for the listener "HTTPS : 443". (Note: HTTP listener is not supported by AWS ALB for forwarding gRPC traffic right now.)
    1. Click the plus icon to add a new rule.
    1. Click **Insert rule** at an appropriate location.
    1. Add a path condition with path value = "/helloworld.Greeter*" and an action to forward to the target group "greeter-server".
    1. Click **Save**.

## Step 5: Creating the Service
1. If you don't have a cluster yet, create one.
1. In your cluster, under the **Services** tab, click **Create**.
1. Select **EC2** as the Launch type.
1. Select the **Task Definition Family** and **Revision** from the previous step.
1. Provide a Service name, such as greeter_server.
1. Set the Number of tasks to 1.
1. Choose Next step.
1. Create a new security group. (or select an existing one, if you already have one. Make sure 50051 port is allowed in the inbound rules.
    - 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.
1. In the Load Balancing section, select your **Application Load Balancer**
1. Ensure that "greeter_service:0:50051" is selected, and Click **Add to Load Balancer**
1. In **Production listener port**, Select "443:HTTPS".
1. Under **Target group name**, select the target group created in the previous step. i.e, "greeter-server"
1. Choose **Next step**.
1. Choose **Next step** again to skip auto scaling.
1. Choose **Create Service** to launch the service.

## Step 6: Verify your deployment
You can verify that your service is running properly by connecting to it using the client.

```
cd client
GREETER_ENDPOINT=mydomain.com:443 INSECURE=0 go run main.go
```
If you can see the message `Greeting: Hello world`, you ran everything successfully! :)

Note: Here I am assuming you have your domain called mydomain.com mapped to your ALB. You can also use your ALB's hostname directly. If you do not use HTTPS, you can ignore the INSECURE flag while running.

And that's it. I hope this post helped you in any way!

Feel free to [DM me on twitter](https://twitter.com/chaitan94) if you have any feedback or questions :)