|
| 1 | +# Amazon EKS Deployment Example |
| 2 | + |
| 3 | +## Introduction |
| 4 | + |
| 5 | +This is an example that demonstrates how to deploy a Python application to Amazon EKS. |
| 6 | +The example deploys a weather forecaster application that runs as a containerized service in Amazon EKS with an Application Load Balancer. The application is built with FastAPI and provides two weather endpoints: |
| 7 | + |
| 8 | +1. `/weather` - A standard endpoint that returns weather information based on the provided prompt |
| 9 | +2. `/weather-streaming` - A streaming endpoint that delivers weather information in real-time as it's being generated |
| 10 | + |
| 11 | +## Prerequisites |
| 12 | + |
| 13 | +- [AWS CLI](https://aws.amazon.com/cli/) installed and configured |
| 14 | +- [eksctl](https://eksctl.io/installation/) (v0.208.x or later) installed |
| 15 | +- [Helm](https://helm.sh/) (v3 or later) installed |
| 16 | +- [kubectl](https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html) installed |
| 17 | +- Either: |
| 18 | + - [Podman](https://podman.io/) installed and running |
| 19 | + - (or) [Docker](https://www.docker.com/) installed and running |
| 20 | +- Amazon Bedrock Anthropic Claude 3.7 model enabled in your AWS environment |
| 21 | + You'll need to enable model access in the Amazon Bedrock console following the [AWS documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html) |
| 22 | + |
| 23 | +## Project Structure |
| 24 | + |
| 25 | +- `chart/` - Contains the Helm chart |
| 26 | + - `values.yaml` - Helm chart default values |
| 27 | +- `docker/` - Contains the Dockerfile and application code for the container: |
| 28 | + - `Dockerfile` - Docker image definition |
| 29 | + - `app/` - Application code |
| 30 | + - `requirements.txt` - Python dependencies for the container & local development |
| 31 | + |
| 32 | +## Create EKS Auto Mode cluster |
| 33 | + |
| 34 | +Set environment variables |
| 35 | +```bash |
| 36 | +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) |
| 37 | +export AWS_REGION=us-east-1 |
| 38 | +export CLUSTER_NAME=eks-strands-agents-demo |
| 39 | +``` |
| 40 | + |
| 41 | +Create EKS Auto Mode cluster |
| 42 | +```bash |
| 43 | +eksctl create cluster --name $CLUSTER_NAME --enable-auto-mode |
| 44 | +``` |
| 45 | +Configure kubeconfig context |
| 46 | +```bash |
| 47 | +aws eks update-kubeconfig --name $CLUSTER_NAME |
| 48 | +``` |
| 49 | + |
| 50 | +## Building and Pushing Docker Image to ECR |
| 51 | + |
| 52 | +Follow these steps to build the Docker image and push it to Amazon ECR: |
| 53 | + |
| 54 | +1. Authenticate to Amazon ECR: |
| 55 | +```bash |
| 56 | +aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com |
| 57 | +``` |
| 58 | + |
| 59 | +2. Create the ECR repository if it doesn't exist: |
| 60 | +```bash |
| 61 | +aws ecr create-repository --repository-name strands-agents-weather --region ${AWS_REGION} |
| 62 | +``` |
| 63 | + |
| 64 | +3. Build the Docker image: |
| 65 | +```bash |
| 66 | +docker build --platform linux/amd64 -t strands-agents-weather:latest docker/ |
| 67 | +``` |
| 68 | + |
| 69 | +4. Tag the image for ECR: |
| 70 | +```bash |
| 71 | +docker tag strands-agents-weather:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/strands-agents-weather:latest |
| 72 | +``` |
| 73 | + |
| 74 | +5. Push the image to ECR: |
| 75 | +```bash |
| 76 | +docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/strands-agents-weather:latest |
| 77 | +``` |
| 78 | + |
| 79 | +## Configure EKS Pod Identity to access Amazon Bedrock |
| 80 | + |
| 81 | +Create an IAM policy to allow InvokeModel & InvokeModelWithResponseStream to all Amazon Bedrock models |
| 82 | +```bash |
| 83 | +cat > bedrock-policy.json << EOF |
| 84 | +{ |
| 85 | + "Version": "2012-10-17", |
| 86 | + "Statement": [ |
| 87 | + { |
| 88 | + "Effect": "Allow", |
| 89 | + "Action": [ |
| 90 | + "bedrock:InvokeModel", |
| 91 | + "bedrock:InvokeModelWithResponseStream" |
| 92 | + ], |
| 93 | + "Resource": "*" |
| 94 | + } |
| 95 | + ] |
| 96 | +} |
| 97 | +EOF |
| 98 | + |
| 99 | +aws iam create-policy \ |
| 100 | + --policy-name strands-agents-weather-bedrock-policy \ |
| 101 | + --policy-document file://bedrock-policy.json |
| 102 | +rm -f bedrock-policy.json |
| 103 | +``` |
| 104 | + |
| 105 | +Create an EKS Pod Identity association |
| 106 | +```bash |
| 107 | +eksctl create podidentityassociation --cluster $CLUSTER_NAME \ |
| 108 | + --namespace default \ |
| 109 | + --service-account-name strands-agents-weather \ |
| 110 | + --permission-policy-arns arn:aws:iam::$AWS_ACCOUNT_ID:policy/strands-agents-weather-bedrock-policy \ |
| 111 | + --role-name eks-strands-agents-weather |
| 112 | +``` |
| 113 | + |
| 114 | +## Deploy strands-agents-weather application |
| 115 | + |
| 116 | +Deploy the helm chart with the image from ECR |
| 117 | +```bash |
| 118 | +helm install strands-agents-weather ./chart \ |
| 119 | + --set image.repository=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/strands-agents-weather --set image.tag=latest |
| 120 | +``` |
| 121 | + |
| 122 | +Wait for Deployment to be available (Pods Running) |
| 123 | +```bash |
| 124 | +kubectl wait --for=condition=available deployments strands-agents-weather --all |
| 125 | +``` |
| 126 | + |
| 127 | +## Test the Agent |
| 128 | + |
| 129 | +Using kubernetes port-forward |
| 130 | +``` |
| 131 | +kubectl --namespace default port-forward service/strands-agents-weather 8080:80 & |
| 132 | +``` |
| 133 | + |
| 134 | +Call the weather service |
| 135 | +``` |
| 136 | +curl -X POST \ |
| 137 | + http://localhost:8080/weather \ |
| 138 | + -H 'Content-Type: application/json' \ |
| 139 | + -d '{"prompt": "What is the weather in Seattle?"}' |
| 140 | +``` |
| 141 | + |
| 142 | +Call the weather streaming endpoint |
| 143 | +``` |
| 144 | +curl -X POST \ |
| 145 | + http://localhost:8080/weather-streaming \ |
| 146 | + -H 'Content-Type: application/json' \ |
| 147 | + -d '{"prompt": "What is the weather in New York in Celsius?"}' |
| 148 | +``` |
| 149 | + |
| 150 | +## Expose Agent through Application Load Balancer |
| 151 | + |
| 152 | +[Create an IngressClass to configure an Application Load Balancer](https://docs.aws.amazon.com/eks/latest/userguide/auto-configure-alb.html) |
| 153 | +```bash |
| 154 | +cat <<EOF | kubectl apply -f - |
| 155 | +apiVersion: eks.amazonaws.com/v1 |
| 156 | +kind: IngressClassParams |
| 157 | +metadata: |
| 158 | + name: alb |
| 159 | +spec: |
| 160 | + scheme: internet-facing |
| 161 | +EOF |
| 162 | +``` |
| 163 | + |
| 164 | +```bash |
| 165 | +cat <<EOF | kubectl apply -f - |
| 166 | +apiVersion: networking.k8s.io/v1 |
| 167 | +kind: IngressClass |
| 168 | +metadata: |
| 169 | + name: alb |
| 170 | + annotations: |
| 171 | + ingressclass.kubernetes.io/is-default-class: "true" |
| 172 | +spec: |
| 173 | + controller: eks.amazonaws.com/alb |
| 174 | + parameters: |
| 175 | + apiGroup: eks.amazonaws.com |
| 176 | + kind: IngressClassParams |
| 177 | + name: alb |
| 178 | +EOF |
| 179 | +``` |
| 180 | + |
| 181 | +Update helm deployment to create Ingress using the IngressClass created |
| 182 | +```bash |
| 183 | +helm upgrade strands-agents-weather ./chart \ |
| 184 | + --set image.repository=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/strands-agents-weather --set image.tag=latest \ |
| 185 | + --set ingress.enabled=true \ |
| 186 | + --set ingress.className=alb |
| 187 | +``` |
| 188 | + |
| 189 | +Get the ALB URL |
| 190 | +```bash |
| 191 | +export ALB_URL=$(kubectl get ingress strands-agents-weather -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') |
| 192 | +echo "The shared ALB is available at: http://$ALB_URL" |
| 193 | +``` |
| 194 | + |
| 195 | +Wait for ALB to be active |
| 196 | +```bash |
| 197 | +aws elbv2 wait load-balancer-available --load-balancer-arns $(aws elbv2 describe-load-balancers --query 'LoadBalancers[?DNSName==`'"$ALB_URL"'`].LoadBalancerArn' --output text) |
| 198 | +``` |
| 199 | + |
| 200 | +Call the weather service Application Load Balancer endpoint |
| 201 | +```bash |
| 202 | +curl -X POST \ |
| 203 | + http://$ALB_URL/weather \ |
| 204 | + -H 'Content-Type: application/json' \ |
| 205 | + -d '{"prompt": "What is the weather in Portland?"}' |
| 206 | +``` |
| 207 | + |
| 208 | +## Configure High Availability and Resiliency |
| 209 | + |
| 210 | +- Increase replicas to 3 |
| 211 | +- [Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/): Spread workload across multi-az |
| 212 | +- [Pod Disruption Budgets](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#pod-disruption-budgets): Tolerate minAvailable of 1 |
| 213 | + |
| 214 | +```bash |
| 215 | +helm upgrade strands-agents-weather ./chart -f - <<EOF |
| 216 | +image: |
| 217 | + repository: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/strands-agents-weather |
| 218 | + tag: latest |
| 219 | +
|
| 220 | +ingress: |
| 221 | + enabled: true |
| 222 | + className: alb |
| 223 | +
|
| 224 | +replicaCount: 3 |
| 225 | +
|
| 226 | +topologySpreadConstraints: |
| 227 | + - maxSkew: 1 |
| 228 | + minDomains: 3 |
| 229 | + topologyKey: topology.kubernetes.io/zone |
| 230 | + whenUnsatisfiable: DoNotSchedule |
| 231 | + labelSelector: |
| 232 | + matchLabels: |
| 233 | + app.kubernetes.io/name: strands-agents-weather |
| 234 | + - maxSkew: 1 |
| 235 | + topologyKey: kubernetes.io/hostname |
| 236 | + whenUnsatisfiable: ScheduleAnyway |
| 237 | + labelSelector: |
| 238 | + matchLabels: |
| 239 | + app.kubernetes.io/instance: strands-agents-weather |
| 240 | + |
| 241 | +podDisruptionBudget: |
| 242 | + enabled: true |
| 243 | + minAvailable: 1 |
| 244 | +EOF |
| 245 | +``` |
| 246 | + |
| 247 | +## Cleanup |
| 248 | + |
| 249 | +Uninstall helm chart |
| 250 | +```bash |
| 251 | +helm uninstall strands-agents-weather |
| 252 | +``` |
| 253 | + |
| 254 | +Delete EKS Auto Mode cluster |
| 255 | +```bash |
| 256 | +eksctl delete cluster --name $CLUSTER_NAME --wait |
| 257 | +``` |
| 258 | + |
| 259 | +Delete IAM policy |
| 260 | +```bash |
| 261 | +aws iam delete-policy --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/strands-agents-weather-bedrock-policy |
| 262 | +``` |
0 commit comments