Set Up Grpc Web Server With AWS
Tags: aws
Published: 2021-05-01 12:20:57

# Intro
I was asked on one of my last posts about gRPC, how does one host a gRPC web server. I thought it would be a good opportunity to learn what the AWS customer experience is, currently, for setting up a simple gRPC web server. If you are advanced at gRPC and AWS already, you could probably set it up just using this post: https://aws.amazon.com/blogs/aws/new-application-load-balancer-support-for-end-to-end-http-2-and-grpc/, which is what I am using to do this. But if you need some guidance on how to use these components, like I did. The following may be helpful to you.

# Setting up an EC2 instance
My first step is setting up an EC2 instance, which will run our gRPC web server. From the AWS console, 
1. select EC2.
2. select "Instances"
3. select "Launch Instances"
4. Next, you can configure your EC2 instance. In case you are unfamiliar, here is what I did:
    1. Select Amazon Linux 2 AMI on 64-bit (x86)
    2. Choose t2.micro (Because it's free)
    3. Click review and launch and then, on the next page launch
5. Make sure that you add TCP on port 50051 as an open inbound port for your security group.
    
# Set up an ALB for my EC2 instance
Technically, you already have a web server you could host your gRPC web application on, but I wanted to try setting up a load balancer as well. 
    
1. Click "Load Balancers" on the left
2. Click "Create Load Balancer"
3. Click "Application Load Balancer"
    1. Enter any name (I chose "test-alb")
    2. Under Listeners, choose HTTPS and port 50051
    3. Under Availability Zones, select two AZs but make sure the one that your EC2 instance was created in is one of them. For me that was `us-west-2d`, but you can check on the EC2 console where yours is.
    4. Click Configure Security Settings

## Creating a certificate
This was one of the toughest parts for me, since I didn't actually have a real certificate to use. Luckily, you can create a self-signed certificate and use it here just fine, but it requires a little extra work. **Note this is not a best practice, but it will work for this little demo.** First, on my computer I ran[^1]:
```
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout example.key -out example.crt -subj "/CN=example.com" -addext "subjectAltName-DNS:example.com,DNS:www.example.net,IP:10.0.0.1"
```
[^1]: This actually didn't work for me on my macOS machine because the openssl typically in the path on macOS is actually LibreSSL. You should instead `brew install openssl` and use that binary's absolute path.

Then I tried to use this certificate in various ways, by adding it in the Load Balancer creation UX with both ACM and IAM, and also adding it into ACM through the ACM UX and then trying to choose it here. Unfortunately, they all didn't work for me. Instead what did work for me was importing it into IAM and choosing it here. **This is not a best practice, but it's what we're doing here for convenience.**

```
aws iam upload-server-certificate --server-certificate-name test-cert --certificate-body file://example.crt --private-key file://example.key
```

Then, I was able to find this certificate in the Load Balancer creation UX.

## Load Balancer Creation Continued
1. For Security groups, I just chose the security group my EC2 instance belonged in.
2. For Configure Routing, I created a new target group (test-target-group)
    1. Choose Instance for target type
    2. Protocol: HTTPS
    3. Port 50051
    4. Protocol Version: gRPC
3. Register targets: I chose my EC2 instance here.
4. Click review and then create.

# Running a gRPC server on the instance
Next, we want to start some gRPC server on port 50051 so we can do an end to end test.

I'm starting by running rustup so I can create a simple tonic server.
```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
cargo new server
cd server
```

Next, I open up `Cargo.toml` and added `tonic` and `tonic-health`. e.g.
```
[dependencies]
tonic = "0.4"
tonic-health = "0.3.1"
tokio = { version = "1.5.0", features = ["full"] }

```
Then `cargo build`. This failed for me because it was missing `cc`, which is strange because it looks like `glibc` is actually installed. But, I just ran `sudo yum groupinstall "Development Tools"` which installs a collection of tools, and it worked after that.

Once that's done, open up `src/main.rs` and put in
```
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
    health_reporter
        .set_service_status("".to_string(), tonic_health::ServingStatus::Serving)
        .await;

    let addr = "0.0.0.0:50051".parse().unwrap();

    tonic::transport::Server::builder()
        .add_service(health_service)
        .serve(addr)
        .await?;

    Ok(())
}
```

Start your server with `cargo run`

# Testing a request to the Load Balancer
If everything is set up correctly, you should now be able to use [`evans`](https://github.com/ktr0731/evans) to access your web server at the load balancer url or even the url for your ec2 instance directly e.g.
```
evans --host <your url> --proto health.proto -p 50051
```

You can get the health.proto file here:
https://github.com/hyperium/tonic/blob/master/tonic-health/proto/health.proto

# Conclusion
Hopefully, this will help other people who are interested in setting up a gRPC web server on AWS. There doesn't seem to be a lot of documentation out there on this specifically, so I hope that this article will be useful.