Skip to content

Commit

Permalink
Add a Docker on AWS Fargate example (pulumi#290)
Browse files Browse the repository at this point in the history
  • Loading branch information
joeduffy committed Apr 30, 2019
1 parent 8a1717f commit c078935
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 0 deletions.
2 changes: 2 additions & 0 deletions aws-ts-hello-fargate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin/
/node_modules/
3 changes: 3 additions & 0 deletions aws-ts-hello-fargate/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: aws-ts-hello-fargate
runtime: nodejs
description: A minimal "Hello, World" container in Fargate.
120 changes: 120 additions & 0 deletions aws-ts-hello-fargate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new)

# Get Started with Docker on AWS Fargate

This example, inspired by the [Docker Getting Started Tutorial](https://docs.docker.com/get-started/), builds, deploys,
and runs a simple containerized application to a private container registry, and scales out five load balanced replicas,
all in just a handful of lines of Node.js code, and leveraging modern and best-in-class AWS features.

To do this, we use Pulumi infrastructure as code to provision an
[Elastic Container Service (ECS)](https://aws.amazon.com/ecs/) cluster, build our `Dockerfile` and deploy the
resulting image to a private [Elastic Container Registry (ECR)](https://aws.amazon.com/ecr/) repository, and then create
a scaled-out [Fargate](https://aws.amazon.com/fargate/) service behind an
[Elastic Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/) that allows traffic from the Internet
on port 80. Because this example using AWS services directly, you can mix in other resources, like S3 buckets, RDS
databases, and so on.

# Prerequisites

- [Node.js](https://nodejs.org/en/download/)
- [Download and install the Pulumi CLI](https://pulumi.io/install)
- [Connect Pulumi with your AWS account](https://pulumi.io/quickstart/aws/setup.html) (if your AWS CLI is
configured, this will just work)

# Running the Example

After cloning this repo, `cd` into it and run these commands:

1. Create a new stack, which is an isolated deployment target for this example:

```bash
$ pulumi stack init dev
```

2. Set your desired AWS region:

```bash
$ pulumi config set aws:region us-east-1 # any valid AWS region will work
```

3. Deploy everything with a single `pulumi up` command. This will show you a preview of changes first, which
includes all of the required AWS resources (clusters, services, and the like). Don't worry if it's more than
you expected -- this is one of the benefits of Pulumi, it configures everything so that so you don't need to!

```bash
$ pulumi up
```

After being prompted and selecting "yes", your deployment will begin. It'll complete in a few minutes:

```
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack aws-ts-hello-fargate-dev created
+ ├─ awsx:x:ecs:Cluster cluster created
+ │ ├─ awsx:x:ec2:SecurityGroup cluster created
+ │ │ ├─ awsx:x:ec2:EgressSecurityGroupRule cluster-egress created
+ │ │ │ └─ aws:ec2:SecurityGroupRule cluster-egress created
+ │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule cluster-ssh created
+ │ │ │ └─ aws:ec2:SecurityGroupRule cluster-ssh created
+ │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule cluster-containers created
+ │ │ │ └─ aws:ec2:SecurityGroupRule cluster-containers created
+ │ │ └─ aws:ec2:SecurityGroup cluster created
+ │ └─ aws:ecs:Cluster cluster created
+ ├─ awsx:x:elasticloadbalancingv2:ApplicationLoadBalancer net-lb created
+ │ ├─ awsx:x:elasticloadbalancingv2:ApplicationTargetGroup web created
+ │ │ └─ aws:elasticloadbalancingv2:TargetGroup ca84d134 created
+ │ ├─ awsx:x:elasticloadbalancingv2:ApplicationListener web created
+ │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule web-external-0-ingress created
+ │ │ │ └─ aws:ec2:SecurityGroupRule web-external-0-ingress created
+ │ │ └─ aws:elasticloadbalancingv2:Listener web created
+ │ └─ aws:elasticloadbalancingv2:LoadBalancer 218ffe37 created
+ ├─ awsx:x:ec2:Vpc default-vpc created
+ │ ├─ awsx:x:ec2:Subnet default-vpc-public-0 created
+ │ ├─ awsx:x:ec2:Subnet default-vpc-public-1 created
> │ ├─ aws:ec2:Subnet default-vpc-public-0 read
> │ └─ aws:ec2:Subnet default-vpc-public-1 read
+ ├─ awsx:x:ecs:FargateTaskDefinition app-svc created
+ │ ├─ aws:ecr:Repository app-img created
+ │ ├─ aws:cloudwatch:LogGroup app-svc created
+ │ ├─ aws:iam:Role app-svc-task created
+ │ ├─ aws:iam:Role app-svc-execution created
+ │ ├─ aws:ecr:LifecyclePolicy app-img created
+ │ ├─ aws:iam:RolePolicyAttachment app-svc-task-32be53a2 created
+ │ ├─ aws:iam:RolePolicyAttachment app-svc-task-fd1a00e5 created
+ │ ├─ aws:iam:RolePolicyAttachment app-svc-execution-9a42f520 created
+ │ └─ aws:ecs:TaskDefinition app-svc created
+ ├─ awsx:x:ecs:FargateService app-svc created
+ │ └─ aws:ecs:Service app-svc created
> └─ aws:ec2:Vpc default-vpc read
Outputs:
url: "218ffe37-e8023b7-1429118690.us-east-1.elb.amazonaws.com"
Resources:
+ 34 created
Duration: 3m30s
Permalink: https://app.pulumi.com/acmecorp/aws-ts-hello-fargate/dev/updates/1
```

4. At this point, your app is running! The URL was published so it's easy to interact with:

```bash
$ curl http:https://$(pulumi stack output url)
<h3>Hello World!</h3>
<b>Hostname:</b> ip-172-31-39-18.ec2.internal<br/>
<b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
```

For more details on how to enable Redis or advanced options, please see the instructions in the
[Docker Getting Started guide](https://docs.docker.com/get-started/part6/).

6. Once you are done, you can destroy all of the resources, and the stack:

```bash
$ pulumi destroy
$ pulumi stack rm
```
20 changes: 20 additions & 0 deletions aws-ts-hello-fargate/app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]
24 changes: 24 additions & 0 deletions aws-ts-hello-fargate/app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"

html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
2 changes: 2 additions & 0 deletions aws-ts-hello-fargate/app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Flask
Redis
29 changes: 29 additions & 0 deletions aws-ts-hello-fargate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as awsx from "@pulumi/awsx";

// Step 1: Create an ECS Fargate cluster.
const cluster = new awsx.ecs.Cluster("cluster");

// Step 2: Define the Networking for our service.
const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer(
"net-lb", { external: true, securityGroups: cluster.securityGroups });
const web = alb.createListener("web", { port: 80, external: true });

// Step 3: Build and publish a Docker image to a private ECR registry.
const img = awsx.ecs.Image.fromPath("app-img", "./app");

// Step 4: Create a Fargate service task that can scale out.
const appService = new awsx.ecs.FargateService("app-svc", {
cluster,
taskDefinitionArgs: {
container: {
image: img,
cpu: 102 /*10% of 1024*/,
memory: 50 /*MB*/,
portMappings: [ web ],
},
},
desiredCount: 5,
});

// Step 5: Export the Internet address for the service.
export const url = web.endpoint.hostname;
12 changes: 12 additions & 0 deletions aws-ts-hello-fargate/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "aws-typescript",
"devDependencies": {
"@types/node": "latest"
},
"dependencies": {
"@pulumi/aws": "latest",
"@pulumi/awsx": "^0.18.3",
"@pulumi/docker": "^0.17.0",
"@pulumi/pulumi": "latest"
}
}
22 changes: 22 additions & 0 deletions aws-ts-hello-fargate/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"outDir": "bin",
"target": "es6",
"lib": [
"es6"
],
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true,
"strictNullChecks": true
},
"files": [
"index.ts"
]
}

0 comments on commit c078935

Please sign in to comment.