Elastic Compute Cloud (EC2)

Get started with Amazon Elastic Compute Cloud (Amazon EC2) on LocalStack

Introduction

EC2 (Elastic Compute Cloud) is a fundamental service within Amazon Web Services (AWS) that provides scalable and flexible virtual computing resources. EC2 enables users to effortlessly launch and manage virtual servers, commonly referred to as instances. Users can create diverse computing environments tailored to specific needs by encompassing a wide array of configurations, enabling users to select the desired combination of computing power, memory, storage, and networking capabilities.

LocalStack supports a mock implementation of EC2 via the Community offering while a fully-emulated implementation is available in the Pro/Team offering, allowing you to use the EC2 APIs in your local environment to create and manage your EC2 instances. The supported APIs are available on our API coverage page, which provides information on the extent of EC2’s integration with LocalStack.

Getting started

This guide is designed for users new to EC2 and assumes basic knowledge of the AWS CLI and our awslocal wrapper script.

Start your LocalStack container using your preferred method. We will demonstrate how to create an EC2 instance that runs a simple Python web server on port 8000 with the AWS CLI.

Create a Key Pair

To create a key pair, you can use the CreateKeyPair API. Run the following command to create the key pair and pipe the output to a file named key.pem:

$ awslocal ec2 create-key-pair \
    --key-name my-key \
    --query 'KeyMaterial' \
    --output text | tee key.pem

You can assign necessary permissions to the key pair file using the following command:

$ chmod 400 key.pem

Alternatively, we can import an existing keypair, for example if you have an SSH public key in your home directory under ~/.ssh/id_rsa.pub:

$ awslocal ec2 import-key-pair --key-name my-key --public-key-material file://~/.ssh/id_rsa.pub

Add rules to your security group

Currently, LocalStack only supports the default security group. You can add rules to the security group using the AuthorizeSecurityGroupIngress API. Run the following command to add a rule to allow inbound traffic on port 8000:

$ awslocal ec2 authorize-security-group-ingress \
    --group-id default \
    --protocol tcp \
    --port 8000 \
    --cidr 0.0.0.0/0

The above command will enable rules in the security group to allow incoming traffic from your local machine on port 8000 of an emulated EC2 instance.

Run an EC2 instance

You can fetch the Security Group ID using the DescribeSecurityGroups API. Run the following command to fetch the Security Group ID:

$ awslocal ec2 describe-security-groups

You should see the following output:

{
    "SecurityGroups": [
        {
            "Description": "default VPC security group",
            "GroupName": "default",
            ...
            "OwnerId": "000000000000",
            "GroupId": "sg-0372ee3c519883079",
            ...
        }
    ]
}

To start your Python Web Server in your locally emulated EC2 instance, you can use the following user script by saving it to a file named user_script.sh:

#!/bin/bash -xeu

apt update
apt install python3 -y
python3 -m http.server 8000

You can now run an EC2 instance using the RunInstances API. Run the following command to run an EC2 instance by adding the appropriate Security Group ID that we fetched in the previous step:

$ awslocal ec2 run-instances \
    --image-id ami-ff0fea8310f3 \
    --count 1 \
    --instance-type t3.nano \
    --key-name my-key \
    --security-group-ids '<SECURITY_GROUP_ID>' \
    --user-data file://./user_script.sh

Test the Python Web Server

You can now open the LocalStack logs to find the IP address of the locally emulated EC2 instance. Run the following command to open the LocalStack logs:

$ localstack logs

You should see the following output:

2023-08-16T17:18:29.702  INFO --- [   asgi_gw_0] l.s.ec2.vmmanager.docker   : Instance i-b07acefd77a3c415f will be accessible via SSH at: 127.0.0.1:12862, 172.17.0.4:22
2023-08-16T17:18:29.702  INFO --- [   asgi_gw_0] l.s.ec2.vmmanager.docker   : Instance i-b07acefd77a3c415f port mappings (container -> host): {'8000/tcp': 29043, '22/tcp': 12862}

You can now use the IP address to test the Python Web Server. Run the following command to test the Python Web Server:

$ curl 172.17.0.4:8000

You should see the following output:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
...

Connecting via SSH

You can also set up an SSH connection to the locally emulated EC2 instance using the instance IP address.

This section assumes that you have created or imported an SSH keypair named my-key (see instructions above). When running the EC2 instance, make sure to pass the --key-name parameter to the command:

$ awslocal ec2 run-instances --key-name my-key ...

Once the instance is up and running, we can use the ssh command to set up an SSH connection. Assuming the instance is available under 127.0.0.1:12862 (as per the LocalStack log output), use this command:

$ ssh -p 12862 -i key.pem root@127.0.0.1

Docker backend

LocalStack Pro supports the Docker backend, enabling the execution of emulated EC2 instances. The Docker backend employs the Docker Engine to simulate EC2 instances. All restrictions associated with containers are also applicable to EC2 instances managed by the Docker manager.

These restrictions encompass elements like root access and networking. In order for LocalStack to function seamlessly, access to the Docker socket is essential, which can be facilitated by attaching the socket file during the launch process.

Instances encompass the mounted Docker socket (/var/run/docker.sock), which facilitates scenarios involving Docker-in-Docker. This setup makes it feasible to engage in use cases that require interactions with Docker within the instances themselves.

Operations

The Docker backend supports the following operations:

OperationNotes
CreateImageUtilizes Docker commit to capture a snapshot of a running instance into a new AMI
DescribeImagesRetrieves a list of Docker images available for use within LocalStack
DescribeInstancesProvides information about both ‘mock’ instances and Docker-backed instances. Docker-backed instances are marked with the resource tag ec2_vm_manager:docker
RunInstancesInitiates the start of a container
StopInstancesInitiates the pause of a container
StartInstancesInitiates the resumption of a paused container
TerminateInstancesInitiates the termination of a container

Base Images

LocalStack utilizes a specific naming convention for recognition and management of its associated containers and images. Containers are designated with the name format localstack-ec2.<InstanceId>, while images are tagged using the format localstack-ec2/<AmiName>:<AmiId>.

Within the Docker backend, Docker images following the aforementioned naming pattern are treated as Amazon Machine Images (AMIs). For instance, you can associate the Ubuntu Focal image as ami-000001 using the command below:

$ docker tag ubuntu:focal localstack-ec2/ubuntu-focal-ami:ami-000001

Such Docker-backed AMIs bear the resource tag ec2_vm_manager:docker and can be listed using the subsequent command:

$ awslocal ec2 describe-images --filters Name=tag:ec2_vm_manager,Values=docker

Configuration

You can also use the EC2_DOCKER_FLAGS LocalStack configuration variable to convey supplementary flags to Docker during the initiation of containerized instances. This allows for adjustments such as commencing the container in privileged mode using --privileged or specifying an alternate CPU platform with --platform, and more. Keep in mind that these modifications apply to all instances launched within the LocalStack session.

Networking

Network addresses for Dockerized instances are allocated by the Docker daemon. These addresses are printed in the logs while the instance is being initialized.

2022-03-21T14:46:49.540  INFO  Instance i-1d6327abf04e31be6 will be accessible via SSH at: 127.0.0.1:55705

To be able to access the instance at additional ports from the host system, you can modify the default security group and incorporate the needed ingress ports.

The system supports up to 32 ingress ports. This constraint is in place to prevent the host from exhausting available ports.

$ awslocal ec2 authorize-security-group-ingress \
  --group-id default \
  --protocol tcp \
  --port 8080
$ awslocal ec2 describe-security-groups \
  --group-names default

The port mapping details are provided in the logs during the instance initialization process.

2022-12-20T19:43:44.544  INFO  Instance i-1d6327abf04e31be6 port mappings (container -> host): {'8080/tcp': 51747, '22/tcp': 55705}

Attaching EBS Block Devices to EC2 Instances

A common use case is to attach an EBS block device to an EC2 instance, which can then be used to create a custom filesystem for additional storage. This section illustrates how this functionality can be achieved with EC2 Docker instances in LocalStack.

First, we create a user data script init.sh which creates an ext3 file system on the block device /ebs-dev/sda1 and mounts it under /ebs-mounted:

$ cat > init.sh <<EOF
#!/bin/bash
set -eo
mkdir -p /ebs-mounted
mkfs -t ext3 /ebs-dev/sda1
mount -o loop /ebs-dev/sda1 /ebs-mounted
touch /ebs-mounted/my-test-file
EOF

We can then start an EC2 instance, specifying a block device mapping under the device name /ebs-dev/sda1, and pointing to our init.sh user data script:

$ awslocal ec2 run-instances --image-id ami-ff0fea8310f3 --count 1 --instance-type t3.nano \
    --block-device-mapping '{"DeviceName":"/ebs-dev/sda1","Ebs":{"VolumeSize":10}}' \
    --user-data file://init.sh

Please note that, whereas real AWS uses GB for volume sizes, we use MB as the unit for VolumeSize in the command above (to avoid creating huge files locally). Also, by default block device images are limited to 1GB in size, but this can be customized by setting the EC2_EBS_MAX_VOLUME_SIZE config variable (defaults to 1000).

Once the instance is successfully started and initialized, we can first determine the container ID via docker ps, and then list the contents of the mounted filesystem /ebs-mounted, which should contain our test file named my-test-file:

$ docker ps
CONTAINER ID   IMAGE                  PORTS           NAMES
5c60cf72d84a   ...:ami-ff0fea8310f3   19419->22/tcp   localstack-ec2...
$ docker exec 5c60cf72d84a ls /ebs-mounted
my-test-file

Resource Browser

The LocalStack Web Application provides a Resource Browser for managing EC2 instances. You can access the Resource Browser by opening the LocalStack Web Application in your browser, navigating to the Resources section, and then clicking on EC2 under the Compute section.

EC2 Resource Browser

The Resource Browser allows you to perform the following actions:

  • Create Instance: Create a new EC2 instance by clicking the Launch Instance button and specifying the AMI ID, instance type, and other parameters.
  • View Instance: View the details of an EC2 instance by clicking on the Instance ID.
  • Terminate Instance: Terminate an EC2 instance by selecting the Instance ID, and clicking on the ACTIONS button followed by clicking on Terminate Selected.
  • Start Instance: Start a stopped EC2 instance by selecting the Instance ID, and clicking on the ACTIONS button followed by clicking on Start Selected.
  • Stop Instance: Stop a running EC2 instance by selecting the Instance ID, and clicking on the ACTIONS button followed by clicking on Stop Selected.

Last modified November 8, 2023: Remove EC2 local daemon (#897) (6e1fda823)