Use AWS Graviton processors on AWS Fargate with Copilot
Tags: aws, docker, arm, ecs
Published: 2023-11-17 23:01:18

[AWS Copilot CLI](https://aws.github.io/copilot-cli/) is an open source command line interface for running containers on AWS App Runner, Amazon Elastic Container Service (ECS), and AWS Fargate. 

You can use Copilot to run containers on Fargate’s serverless compute with Graviton2 processors and benefit from improved price performance. 

Graviton is not the default for Copilot, but read on to find out how to change the architecture to Arm.

# Before you begin

You will need Docker and Copilot on your local computer to follow the instructions below.

Your local computer can be Linux or macOS.

There are plenty of places to find Docker installation instructions. One option is the [Docker install guide](https://learn.arm.com/install-guides/docker/).

You will also need an AWS account. 

To create an account, go to https://aws.amazon.com and click on **Create an AWS Account** in the top right corner. Follow the instructions to register. See the [Creating an AWS account documentation](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-creating.html) for full instructions.

Make sure to configure your access key ID and secret access key, which are used to sign programmatic requests that you make to AWS. Refer to [AWS Credentials](https://learn.arm.com/install-guides/aws_access_keys/) for a quick summary of how to run `aws configure`. The install guide also covers how to install the AWS CLI. Make a note of the AWS region you set with `aws configure` so you can see the resources created by Copilot in the AWS console.

If you are using Docker on Linux you will need to install QEMU to build container images for both the `arm64` and the `amd64` architectures.

```console
sudo apt-get install qemu-user-static
```

Docker Desktop for macOS includes the ability to build for multiple architectures, so you don't need to do anything extra.

To install Copilot on Arm Linux:

```console
sudo curl -Lo /usr/local/bin/copilot https://github.com/aws/copilot-cli/releases/latest/download/copilot-linux-arm64 \
   && sudo chmod +x /usr/local/bin/copilot \
   && copilot --help
```

To install Copilot on macOS:

```console
curl -Lo copilot https://github.com/aws/copilot-cli/releases/latest/download/copilot-darwin && chmod +x copilot && sudo mv copilot /usr/local/bin/copilot && copilot --help
```

# Create an example application

To try Copilot on Graviton2 processors, you can use the same Go application used in [Multi-architecture Kubernetes clusters on Amazon EKS](https://dev.to/aws-builders/multi-architecture-kubernetes-clusters-on-amazon-eks-2nol).

Use an editor to create the 3 files below.

Create a file named `hello.go` with the contents:

```go
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"net/http"
	"runtime"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello from CPU PLATFORM:%s/%s",
		runtime.GOOS, runtime.GOARCH)
}

func main() {
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
```

Next, create a file named `go.mod` with the following two lines:

```go
module example.com/arm
go 1.21
```

Create a third file named `Dockerfile` with the contents:

```dockerfile
#
# Build: 1st stage
#
FROM golang:1.21-alpine as builder
ARG TARCH
WORKDIR /app
COPY go.mod .
COPY hello.go .
RUN go build -o /hello && \
    apk add --update --no-cache file && \
    file /hello

#
# Release: 2nd stage
#
FROM alpine
WORKDIR /
COPY --from=builder /hello /hello
RUN apk add --update --no-cache file
CMD [ "/hello" ]
```

The application listens on port 8080 and prints the architecture of the machine.

# Deploy the application with Copilot

You can deploy the application with a single command:

```console
copilot init --app go-arch               \
  --name api                             \
  --type 'Load Balanced Web Service'     \
  --dockerfile './Dockerfile'            \
  --env test                             \
  --port 8080                            \
  --deploy
```

The default architecture is `amd64`. The `copilot` command builds the container on your local machine for `amd64`, pushes it to the container registry, and creates everything needed to run on AWS Fargate. 

You can also specify an existing container image using `--image` instead of `--dockerfile`. Make sure the image is a multi-architecture image supporting both `arm64` and `amd64`.

While you are waiting for the command to complete you can look in your AWS account and see the resources created in AWS S3, CloudFormation, and ECS.

When the `copilot` command completes, the URL of the load balancer is printed.

Visit the URL in your browser and see the printed message.

To access the application from the command line, run `curl` with the URL. 

For example:

```console
curl -w '\n' http://go-arc-Publi-UvaFr7DQF5ud-988490958.us-west-2.elb.amazonaws.com
```

The output is:

```output
Hello from CPU PLATFORM:linux/amd64
```

You can also check running status using:

```console
copilot svc status
```

The output is similar to:

```output
Found only one deployed service api in environment test
Task Summary

  Running   ██████████  1/1 desired tasks are running
  Health    ██████████  1/1 passes HTTP health checks
            ██████████  1/1 passes container health checks

Tasks

  ID        Status      Revision    Started At     Cont. Health  HTTP Health
  --        ------      --------    ----------     ------------  -----------
  7779652d  RUNNING     1           7 minutes ago  HEALTHY       HEALTHY
```

# Migrate to Graviton

To move from `amd64` to `arm64` edit the file `copilot/api/manifest.yml` and change the `platform` entry from `linux/x86_64` to `linux/arm64`.

If you don't have a `platform` entry, add one after the existing entries. Either way, you should have this line in your `manifest.yml` file:

```yaml
platform: linux/arm64
```

You are now ready to run on Graviton2.

Save the file and redeploy the application using:

```console
copilot svc deploy 
```

Copilot rebuilds the container image for the `arm64` architecture, pushes the image to the container registry and deploys the new image.

If you look in your AWS console and visit the task configuration in ECS, you will see the task now shows `ARM64` as the architecture.

Visit the URL again using a browser or the same `curl` command and the message prints `arm64`.

```output
Hello from CPU PLATFORM:linux/arm64
```

# Open a shell in the container

If you need to troubleshoot a container, you can use Copilot to connect.

```console
copilot svc exec
```

Answer yes to install the Session Manager plugin, and you have a shell into the running container. 

```output
Starting session with SessionId: ecs-execute-command-0f1f212e5ff00ec05
/ # 
```

You can make changes to the container and do any needed investigation. Copilot is an easy way to connect to running containers. 

# Summary 

You have deployed a containerized application on Fargate running AWS Graviton2 processors using Copilot. The Copilot CLI makes it much easier to create the required resources and easily make changes using the `manifest.yml` file. 

There doesn't seem to be a way to specify `arm64` on the initial `copilot init` command, but it would be a useful enhancement. 

# Clean up the AWS resources

Delete the resources created by Copilot by running:

```console
copilot app delete
```