Skip to content

Commit c078935

Browse files
authored
Add a Docker on AWS Fargate example (#290)
1 parent 8a1717f commit c078935

File tree

9 files changed

+234
-0
lines changed

9 files changed

+234
-0
lines changed

aws-ts-hello-fargate/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/bin/
2+
/node_modules/

aws-ts-hello-fargate/Pulumi.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name: aws-ts-hello-fargate
2+
runtime: nodejs
3+
description: A minimal "Hello, World" container in Fargate.

aws-ts-hello-fargate/README.md

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new)
2+
3+
# Get Started with Docker on AWS Fargate
4+
5+
This example, inspired by the [Docker Getting Started Tutorial](https://docs.docker.com/get-started/), builds, deploys,
6+
and runs a simple containerized application to a private container registry, and scales out five load balanced replicas,
7+
all in just a handful of lines of Node.js code, and leveraging modern and best-in-class AWS features.
8+
9+
To do this, we use Pulumi infrastructure as code to provision an
10+
[Elastic Container Service (ECS)](https://aws.amazon.com/ecs/) cluster, build our `Dockerfile` and deploy the
11+
resulting image to a private [Elastic Container Registry (ECR)](https://aws.amazon.com/ecr/) repository, and then create
12+
a scaled-out [Fargate](https://aws.amazon.com/fargate/) service behind an
13+
[Elastic Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/) that allows traffic from the Internet
14+
on port 80. Because this example using AWS services directly, you can mix in other resources, like S3 buckets, RDS
15+
databases, and so on.
16+
17+
# Prerequisites
18+
19+
- [Node.js](https://nodejs.org/en/download/)
20+
- [Download and install the Pulumi CLI](https://pulumi.io/install)
21+
- [Connect Pulumi with your AWS account](https://pulumi.io/quickstart/aws/setup.html) (if your AWS CLI is
22+
configured, this will just work)
23+
24+
# Running the Example
25+
26+
After cloning this repo, `cd` into it and run these commands:
27+
28+
1. Create a new stack, which is an isolated deployment target for this example:
29+
30+
```bash
31+
$ pulumi stack init dev
32+
```
33+
34+
2. Set your desired AWS region:
35+
36+
```bash
37+
$ pulumi config set aws:region us-east-1 # any valid AWS region will work
38+
```
39+
40+
3. Deploy everything with a single `pulumi up` command. This will show you a preview of changes first, which
41+
includes all of the required AWS resources (clusters, services, and the like). Don't worry if it's more than
42+
you expected -- this is one of the benefits of Pulumi, it configures everything so that so you don't need to!
43+
44+
```bash
45+
$ pulumi up
46+
```
47+
48+
After being prompted and selecting "yes", your deployment will begin. It'll complete in a few minutes:
49+
50+
```
51+
Updating (dev):
52+
53+
Type Name Status
54+
+ pulumi:pulumi:Stack aws-ts-hello-fargate-dev created
55+
+ ├─ awsx:x:ecs:Cluster cluster created
56+
+ │ ├─ awsx:x:ec2:SecurityGroup cluster created
57+
+ │ │ ├─ awsx:x:ec2:EgressSecurityGroupRule cluster-egress created
58+
+ │ │ │ └─ aws:ec2:SecurityGroupRule cluster-egress created
59+
+ │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule cluster-ssh created
60+
+ │ │ │ └─ aws:ec2:SecurityGroupRule cluster-ssh created
61+
+ │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule cluster-containers created
62+
+ │ │ │ └─ aws:ec2:SecurityGroupRule cluster-containers created
63+
+ │ │ └─ aws:ec2:SecurityGroup cluster created
64+
+ │ └─ aws:ecs:Cluster cluster created
65+
+ ├─ awsx:x:elasticloadbalancingv2:ApplicationLoadBalancer net-lb created
66+
+ │ ├─ awsx:x:elasticloadbalancingv2:ApplicationTargetGroup web created
67+
+ │ │ └─ aws:elasticloadbalancingv2:TargetGroup ca84d134 created
68+
+ │ ├─ awsx:x:elasticloadbalancingv2:ApplicationListener web created
69+
+ │ │ ├─ awsx:x:ec2:IngressSecurityGroupRule web-external-0-ingress created
70+
+ │ │ │ └─ aws:ec2:SecurityGroupRule web-external-0-ingress created
71+
+ │ │ └─ aws:elasticloadbalancingv2:Listener web created
72+
+ │ └─ aws:elasticloadbalancingv2:LoadBalancer 218ffe37 created
73+
+ ├─ awsx:x:ec2:Vpc default-vpc created
74+
+ │ ├─ awsx:x:ec2:Subnet default-vpc-public-0 created
75+
+ │ ├─ awsx:x:ec2:Subnet default-vpc-public-1 created
76+
> │ ├─ aws:ec2:Subnet default-vpc-public-0 read
77+
> │ └─ aws:ec2:Subnet default-vpc-public-1 read
78+
+ ├─ awsx:x:ecs:FargateTaskDefinition app-svc created
79+
+ │ ├─ aws:ecr:Repository app-img created
80+
+ │ ├─ aws:cloudwatch:LogGroup app-svc created
81+
+ │ ├─ aws:iam:Role app-svc-task created
82+
+ │ ├─ aws:iam:Role app-svc-execution created
83+
+ │ ├─ aws:ecr:LifecyclePolicy app-img created
84+
+ │ ├─ aws:iam:RolePolicyAttachment app-svc-task-32be53a2 created
85+
+ │ ├─ aws:iam:RolePolicyAttachment app-svc-task-fd1a00e5 created
86+
+ │ ├─ aws:iam:RolePolicyAttachment app-svc-execution-9a42f520 created
87+
+ │ └─ aws:ecs:TaskDefinition app-svc created
88+
+ ├─ awsx:x:ecs:FargateService app-svc created
89+
+ │ └─ aws:ecs:Service app-svc created
90+
> └─ aws:ec2:Vpc default-vpc read
91+
92+
Outputs:
93+
url: "218ffe37-e8023b7-1429118690.us-east-1.elb.amazonaws.com"
94+
95+
Resources:
96+
+ 34 created
97+
98+
Duration: 3m30s
99+
100+
Permalink: https://app.pulumi.com/acmecorp/aws-ts-hello-fargate/dev/updates/1
101+
```
102+
103+
4. At this point, your app is running! The URL was published so it's easy to interact with:
104+
105+
```bash
106+
$ curl http://$(pulumi stack output url)
107+
<h3>Hello World!</h3>
108+
<b>Hostname:</b> ip-172-31-39-18.ec2.internal<br/>
109+
<b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
110+
```
111+
112+
For more details on how to enable Redis or advanced options, please see the instructions in the
113+
[Docker Getting Started guide](https://docs.docker.com/get-started/part6/).
114+
115+
6. Once you are done, you can destroy all of the resources, and the stack:
116+
117+
```bash
118+
$ pulumi destroy
119+
$ pulumi stack rm
120+
```

aws-ts-hello-fargate/app/Dockerfile

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Use an official Python runtime as a parent image
2+
FROM python:2.7-slim
3+
4+
# Set the working directory to /app
5+
WORKDIR /app
6+
7+
# Copy the current directory contents into the container at /app
8+
COPY . /app
9+
10+
# Install any needed packages specified in requirements.txt
11+
RUN pip install --trusted-host pypi.python.org -r requirements.txt
12+
13+
# Make port 80 available to the world outside this container
14+
EXPOSE 80
15+
16+
# Define environment variable
17+
ENV NAME World
18+
19+
# Run app.py when the container launches
20+
CMD ["python", "app.py"]

aws-ts-hello-fargate/app/app.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from flask import Flask
2+
from redis import Redis, RedisError
3+
import os
4+
import socket
5+
6+
# Connect to Redis
7+
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
8+
9+
app = Flask(__name__)
10+
11+
@app.route("/")
12+
def hello():
13+
try:
14+
visits = redis.incr("counter")
15+
except RedisError:
16+
visits = "<i>cannot connect to Redis, counter disabled</i>"
17+
18+
html = "<h3>Hello {name}!</h3>" \
19+
"<b>Hostname:</b> {hostname}<br/>" \
20+
"<b>Visits:</b> {visits}"
21+
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
22+
23+
if __name__ == "__main__":
24+
app.run(host='0.0.0.0', port=80)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Flask
2+
Redis

aws-ts-hello-fargate/index.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as awsx from "@pulumi/awsx";
2+
3+
// Step 1: Create an ECS Fargate cluster.
4+
const cluster = new awsx.ecs.Cluster("cluster");
5+
6+
// Step 2: Define the Networking for our service.
7+
const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer(
8+
"net-lb", { external: true, securityGroups: cluster.securityGroups });
9+
const web = alb.createListener("web", { port: 80, external: true });
10+
11+
// Step 3: Build and publish a Docker image to a private ECR registry.
12+
const img = awsx.ecs.Image.fromPath("app-img", "./app");
13+
14+
// Step 4: Create a Fargate service task that can scale out.
15+
const appService = new awsx.ecs.FargateService("app-svc", {
16+
cluster,
17+
taskDefinitionArgs: {
18+
container: {
19+
image: img,
20+
cpu: 102 /*10% of 1024*/,
21+
memory: 50 /*MB*/,
22+
portMappings: [ web ],
23+
},
24+
},
25+
desiredCount: 5,
26+
});
27+
28+
// Step 5: Export the Internet address for the service.
29+
export const url = web.endpoint.hostname;

aws-ts-hello-fargate/package.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "aws-typescript",
3+
"devDependencies": {
4+
"@types/node": "latest"
5+
},
6+
"dependencies": {
7+
"@pulumi/aws": "latest",
8+
"@pulumi/awsx": "^0.18.3",
9+
"@pulumi/docker": "^0.17.0",
10+
"@pulumi/pulumi": "latest"
11+
}
12+
}

aws-ts-hello-fargate/tsconfig.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"compilerOptions": {
3+
"outDir": "bin",
4+
"target": "es6",
5+
"lib": [
6+
"es6"
7+
],
8+
"module": "commonjs",
9+
"moduleResolution": "node",
10+
"sourceMap": true,
11+
"experimentalDecorators": true,
12+
"pretty": true,
13+
"noFallthroughCasesInSwitch": true,
14+
"noImplicitAny": true,
15+
"noImplicitReturns": true,
16+
"forceConsistentCasingInFileNames": true,
17+
"strictNullChecks": true
18+
},
19+
"files": [
20+
"index.ts"
21+
]
22+
}

0 commit comments

Comments
 (0)