Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions docs/examples/deployment/ec2-docker.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
..
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.


.. _ec2-docker-deployment:

==========================
AWS EC2 + Docker
==========================

Deploy a Burr application as a Docker container on an AWS EC2 instance, provisioned with Terraform.

See the full example and step-by-step guide:

* Source: `GitHub <https://github.com/apache/burr/tree/main/examples/deployment/aws/ec2-docker>`_
* ``examples/deployment/aws/ec2-docker/README.md``

-----------
Quick Start
-----------

.. code-block:: bash

cd examples/deployment/aws/ec2-docker
docker build -t burr-ec2-example .
docker run -p 8000:8000 burr-ec2-example
curl -X POST http://localhost:8000/run -H "Content-Type: application/json" -d '{"number": 5}'

-------------
Deploy to AWS
-------------

.. code-block:: bash

cd terraform
terraform init
terraform apply -var-file=dev.tfvars

--------------
Related Guides
--------------

* :ref:`S3 Tracking on AWS <s3-tracking-aws>`
* :ref:`AWS Deployment overview <aws-deployment-example>`
1 change: 1 addition & 0 deletions docs/examples/deployment/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ have an example that would add to this guide. We have created a variety of issue
infrastructure
monitoring
aws
ec2-docker
29 changes: 29 additions & 0 deletions examples/deployment/aws/ec2-docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ ./app/

EXPOSE 8000

CMD ["python", "-m", "app.server"]
160 changes: 160 additions & 0 deletions examples/deployment/aws/ec2-docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->

# Deploy Burr on AWS EC2 with Docker

Deploy a containerized Burr application on an AWS EC2 instance, provisioned with Terraform.

## Overview

This example deploys:
- A FastAPI server wrapping a simple Burr counter application
- Running inside a Docker container on an EC2 instance
- Infrastructure provisioned via Terraform (VPC default, security group, EC2)

## Prerequisites

- AWS CLI configured with credentials
- Terraform >= 1.5 installed
- Docker installed (for local testing)
- An EC2 key pair created in your target region

## Quick Start (Local)

Build and run locally without any AWS resources:

```bash
cd examples/deployment/aws/ec2-docker

docker build -t burr-ec2-example .
docker run -p 8000:8000 burr-ec2-example
```

Test the endpoints:

```bash
# Health check
curl http://localhost:8000/health

# Run the counter app (counts up to 5)
curl -X POST http://localhost:8000/run \
-H "Content-Type: application/json" \
-d '{"number": 5}'
```

Alternatively, use Docker Compose:

```bash
docker compose up --build
```

## Deploy to AWS

1. Navigate to the Terraform directory:

```bash
cd terraform
```

2. Copy `dev.tfvars` and set your values:

```bash
cp dev.tfvars my.tfvars
```

Edit `my.tfvars` and set:
- `key_name` — your EC2 key pair name
- `allowed_cidr` — your IP address in CIDR notation (e.g. `203.0.113.50/32`)

3. Initialize and apply:

```bash
terraform init
terraform apply -var-file=my.tfvars
```

4. Wait approximately 2-3 minutes for the instance to boot, install Docker, and start the container.

5. Access the application:

```bash
# Get the URL
terraform output app_url

# Test it
curl http://<public_ip>:8000/health
curl -X POST http://<public_ip>:8000/run -H "Content-Type: application/json" -d '{"number": 3}'
```

## How it Works

- Terraform provisions a security group and EC2 instance
- The `user_data.sh` script runs on first boot:
- Installs Docker and Docker Compose
- Clones the repository
- Builds the Docker image
- Runs the container with `--restart=unless-stopped`
- The app exposes port 8000 (configurable via `app_port` variable)

> **Note:** The user_data script clones from `apache/burr` main branch. This path only
> works once this example is merged to main. For pre-merge testing on EC2, edit
> `terraform/modules/compute/templates/user_data.sh.tpl` to point at your fork and branch.

## Verify the Deployment

```bash
# Health check
curl http://<public_ip>:8000/health
# Expected: {"status": "ok"}

# Run counter
curl -X POST http://<public_ip>:8000/run \
-H "Content-Type: application/json" \
-d '{"number": 3}'
# Expected: {"counter": 3, "counter_limit": 3}
```

## Teardown

```bash
terraform destroy -var-file=my.tfvars
```

## Security

- Ingress rules use `var.allowed_cidr` — you must set this to your IP. Do not use `0.0.0.0/0`.
- The Burr tracking UI is **not exposed** in this example. If you add it, place it behind
authentication (e.g. ALB with Cognito) or restrict access to a VPN/private subnet.
- For production, use HTTPS via ACM + Application Load Balancer.
- Do not commit `.tfstate` files — they are gitignored.

## Troubleshooting

| Problem | Solution |
|---------|----------|
| Instance not reachable | Check that `allowed_cidr` matches your current IP |
| App not starting | SSH in and check `docker logs burr-app` |
| Terraform state conflicts | Do not share `.tfstate` between developers |

## Production Notes

- For persistent state across container restarts, add a volume mount or use
[S3 tracking](https://burr.apache.org/concepts/aws-tracking/)
- Consider an Auto Scaling Group for high availability
- Use a private subnet + NAT gateway for instances that don't need direct public access
16 changes: 16 additions & 0 deletions examples/deployment/aws/ec2-docker/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
63 changes: 63 additions & 0 deletions examples/deployment/aws/ec2-docker/app/counter_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.

"""
Simple counting application for the EC2 + Docker deployment example.

Reuses the same counter pattern from the Lambda example to demonstrate
deploying a Burr application as a long-running HTTP service.
"""

import time

import burr.core
from burr.core import Application, Result, State, default, expr
from burr.core.action import action
from burr.core.graph import GraphBuilder


@action(reads=["counter"], writes=["counter"])
def counter(state: State) -> State:
result = {"counter": state["counter"] + 1}
time.sleep(0.5) # simulate a longer-running computation
return state.update(**result)


graph = (
GraphBuilder()
.with_actions(counter=counter, result=Result("counter"))
.with_transitions(
("counter", "counter", expr("counter < counter_limit")),
("counter", "result", default),
)
.build()
)


def application(count_up_to: int = 10) -> Application:
"""Create and return a Burr counter application.

:param count_up_to: The target counter value.
:return: A configured Burr Application instance.
"""
return (
burr.core.ApplicationBuilder()
.with_graph(graph)
.with_state(**{"counter": 0, "counter_limit": count_up_to})
.with_entrypoint("counter")
.build()
)
53 changes: 53 additions & 0 deletions examples/deployment/aws/ec2-docker/app/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.

"""HTTP server for the Burr counter application.

Exposes:
- POST /run -- run the counter app with a given target number
- GET /health -- health check endpoint for load balancers and monitoring
"""

import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel

from app.counter_app import application

app = FastAPI(title="Burr EC2 Docker Example")


class RunRequest(BaseModel):
number: int = 5


@app.post("/run")
def run_counter(req: RunRequest):
"""Run the counter application up to the requested number."""
burr_app = application(req.number)
_, _, state = burr_app.run(halt_after=["result"])
return state.serialize()


@app.get("/health")
def health():
"""Health check endpoint."""
return {"status": "ok"}


if __name__ == "__main__":
uvicorn.run("app.server:app", host="0.0.0.0", port=8000)
Loading
Loading