forked from pulumi/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Mitch/aws py wordpress fargate rds (pulumi#893)
- Loading branch information
1 parent
88e52a4
commit 12b04c1
Showing
8 changed files
with
563 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
*.pyc | ||
venv/ | ||
.vscode/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name: wordpress-ecs-rds | ||
runtime: | ||
name: python | ||
options: | ||
virtualenv: venv | ||
description: Deploys WordPress in ECS Fargate with RDS backend. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# WordPress Site in AWS Fargate with RDS DB Backend | ||
|
||
This example serves a WordPress site in AWS ECS Fargate using an RDS MySQL Backend. | ||
|
||
It leverages the following Pulumi concepts/constructs: | ||
|
||
- [Component Resources](https://www.pulumi.com/docs/intro/concepts/programming-model/#components): Allows one to create custom resources that encapsulate one's best practices. In this example, component resource is used to define a "VPC" custom resource, a "Backend" custom resource that sets up the RDS DB, and a "Frontend" resource that sets up the ECS cluster and load balancer and tasks. | ||
- [Other Providers](https://www.pulumi.com/docs/reference/pkg/): Beyond the providers for the various clouds and Kubernetes, etc, Pulumi allows one to create and manage non-cloud resources. In this case, the program uses the Random provider to create a random password if necessary. | ||
|
||
This sample uses the following AWS products (and related Pulumi providers): | ||
|
||
- [Amazon VPC](https://aws.amazon.com/vpc): Used to set up a new virtual network in which the system is deployed. | ||
- [Amazon RDS](https://aws.amazon.com/rds): A managed DB service used to provide the MySQL backend for WordPress. | ||
- [Amazon ECS Fargate](https://aws.amazon.com/fargate): A container service used to run the WordPress frontend. | ||
|
||
## Getting Started | ||
|
||
There are no required configuration parameters for this project since the code will use defaults or generate values as needed - see the beginning of `__main__.py` to see the defaults. | ||
However, you can override these defaults by using `pulumi config` to set the following values (e.g. `pulumi config set service_name my-wp-demo`). | ||
|
||
- `service_name` - This is used as a prefix for resources created by the Pulumi program. | ||
- `db_name` - The name of the MySQL DB created in RDS. | ||
- `db_user` - The user created with access to the MySQL DB. | ||
- `db_password` - The password for the DB user. Be sure to use `--secret` if creating this config value (e.g. `pulumi config set db_password --secret`). | ||
|
||
## Deploying and running the program | ||
|
||
Note: some values in this example will be different from run to run. | ||
|
||
1. Create a new stack: | ||
|
||
```bash | ||
$ pulumi stack init lamp-test | ||
``` | ||
|
||
1. Set the AWS region: | ||
|
||
```bash | ||
$ pulumi config set aws:region us-west-2 | ||
``` | ||
|
||
1. Create a Python virtualenv, activate it, and install dependencies: | ||
|
||
This installs the dependent packages [needed](https://www.pulumi.com/docs/intro/concepts/how-pulumi-works/) for our Pulumi program. | ||
|
||
```bash | ||
$ python3 -m venv venv | ||
$ source venv/bin/activate | ||
$ pip3 install -r requirements.txt | ||
``` | ||
|
||
1. Run `pulumi up` to preview and deploy changes. After the preview is shown you will be | ||
prompted if you want to continue or not. Note: If you set the `db_password` in the configuration as described above, you will not see the `RandomPassword` resource below. | ||
|
||
```bash | ||
$ pulumi up | ||
+ pulumi:pulumi:Stack lamp-rds-wordpress-testing create | ||
+ ├─ custom:resource:VPC wp-example-net create | ||
+ │ ├─ aws:ec2:Vpc wp-example-net-vpc create | ||
+ pulumi:pulumi:Stack lamp-rds-wordpress-testing create. | ||
+ pulumi:pulumi:Stack lamp-rds-wordpress-testing create | ||
+ │ ├─ aws:ec2:Subnet wp-example-net-subnet-us-west-2a create | ||
+ │ ├─ aws:ec2:Subnet wp-example-net-subnet-us-west-2b create | ||
+ │ ├─ aws:ec2:SecurityGroup wp-example-net-rds-sg create | ||
+ │ ├─ aws:ec2:SecurityGroup wp-example-net-fe-sg create | ||
+ │ ├─ aws:ec2:RouteTableAssociation vpc-route-table-assoc-us-west-2a create | ||
+ │ └─ aws:ec2:RouteTableAssociation vpc-route-table-assoc-us-west-2b create | ||
+ ├─ random:index:RandomPassword db_password create | ||
+ ├─ custom:resource:Backend wp-example-be create | ||
+ │ ├─ aws:rds:SubnetGroup wp-example-be-sng create | ||
+ │ └─ aws:rds:Instance wp-example-be-rds create | ||
+ └─ custom:resource:Frontend wp-example-fe create | ||
+ ├─ aws:ecs:Cluster wp-example-fe-ecs create | ||
+ ├─ aws:iam:Role wp-example-fe-task-role create | ||
+ ├─ aws:lb:TargetGroup wp-example-fe-app-tg create | ||
+ ├─ aws:iam:RolePolicyAttachment wp-example-fe-task-policy create | ||
+ ├─ aws:lb:LoadBalancer wp-example-fe-alb create | ||
+ ├─ aws:lb:Listener wp-example-fe-listener create | ||
+ └─ aws:ecs:Service wp-example-fe-app-svc create | ||
``` | ||
|
||
1. The program outputs the following values: | ||
|
||
- `DB Endpoint`: This is the RDS DB endpoint. By default, the DB is deployed to disallow public access. This can be overriden in the resource declaration for the backend. | ||
- `DB Password`: This is managed as a secret. To see the value, you can use `pulumi stack output --show-secrets` | ||
- `DB User Name`: The user name for access the DB. | ||
- `ECS Cluster Name`: The name of the ECS cluster created by the stack. | ||
- `Web Service URL`: This is a link to the load balancer fronting the WordPress container. Note: It may take a few minutes for AWS to complete deploying the service and so you may see a 503 error initially. | ||
|
||
1. To clean up resources, run `pulumi destroy` and answer the confirmation question at the prompt. | ||
|
||
## Troubleshooting | ||
|
||
### 503 Error for the Web Service | ||
|
||
AWS can take a few minutes to complete deploying the WordPress container and connect the load balancer to the service. So you may see a 503 error for a few minutes right after launching the stack. You can see the status of the service by looking at the cluster in AWS. | ||
|
||
## Deployment Speed | ||
|
||
Since the stack creates an RDS instance, ECS cluster, load balancer, ECS service, as well as other elements, the stack can take about 4-5 minutes to launch and become ready. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
""" | ||
Deploys: | ||
- Network: VPC, Subnets, Security Groups | ||
- DB Backend: MySQL RDS | ||
- FrontEnd: WordPress in Fargate | ||
""" | ||
|
||
import pulumi | ||
import pulumi_random as random | ||
import network | ||
import backend | ||
import frontend | ||
|
||
# Get config data | ||
config = pulumi.Config() | ||
service_name = config.get('service_name') or 'wp-example' | ||
db_name=config.get('db_name') or 'lampdb' | ||
db_user=config.get('db_user') or 'admin' | ||
|
||
# Get secretified password from config and protect it going forward, or create one using the 'random' provider. | ||
db_password=config.get_secret('db_password') | ||
if not db_password: | ||
password=random.RandomPassword('db_password', | ||
length=16, | ||
special=True, | ||
override_special='_%@', | ||
) | ||
# Pulumi knows this provider is used to create a password and thus automatically protects it going forward. | ||
db_password=password.result | ||
|
||
# Create an AWS VPC and subnets, etc | ||
network=network.Vpc(f'{service_name}-net', network.VpcArgs()) | ||
subnet_ids=[] | ||
for subnet in network.subnets: | ||
subnet_ids.append(subnet.id) | ||
|
||
# Create a backend DB instance | ||
be=backend.Db(f'{service_name}-be', backend.DbArgs( | ||
db_name=db_name, | ||
db_user=db_user, | ||
db_password=db_password, | ||
# publicly_accessible=True, # Uncomment this to override for testing | ||
subnet_ids=subnet_ids, | ||
security_group_ids=[network.rds_security_group.id] | ||
)) | ||
|
||
fe=frontend.WebService(f'{service_name}-fe', frontend.WebServiceArgs( | ||
db_host=be.db.address, | ||
db_port='3306', | ||
db_name=be.db.name, | ||
db_user=be.db.username, | ||
db_password=be.db.password, | ||
vpc_id=network.vpc.id, | ||
subnet_ids=subnet_ids, | ||
security_group_ids=[network.fe_security_group.id] | ||
)) | ||
|
||
web_url=pulumi.Output.concat('https://', fe.alb.dns_name) | ||
pulumi.export('Web Service URL', web_url) | ||
pulumi.export('ECS Cluster Name', fe.cluster.name) | ||
|
||
pulumi.export('DB Endpoint', be.db.address) | ||
pulumi.export('DB User Name', be.db.username) | ||
pulumi.export('DB Password', be.db.password) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from pulumi import ComponentResource, ResourceOptions | ||
from pulumi_aws import rds | ||
|
||
|
||
class DbArgs: | ||
|
||
def __init__(self, | ||
db_name=None, | ||
db_user=None, | ||
db_password=None, | ||
subnet_ids=None, | ||
security_group_ids=None, | ||
allocated_storage=20, | ||
engine='mysql', | ||
engine_version='5.7', | ||
instance_class='db.t2.micro', | ||
storage_type='gp2', | ||
skip_final_snapshot=True, | ||
publicly_accessible=False): | ||
|
||
self.db_name = db_name | ||
self.db_user = db_user | ||
self.db_password = db_password | ||
self.subnet_ids = subnet_ids | ||
self.security_group_ids = security_group_ids | ||
self.allocated_storage = allocated_storage | ||
self.engine = engine | ||
self.engine_version = engine_version | ||
self.instance_class = instance_class | ||
self.storage_type = storage_type | ||
self.skip_final_snapshot = skip_final_snapshot | ||
self.publicly_accessible = publicly_accessible | ||
|
||
|
||
class Db(ComponentResource): | ||
|
||
def __init__(self, | ||
name: str, | ||
args: DbArgs, | ||
opts: ResourceOptions = None): | ||
|
||
super().__init__('custom:resource:Backend', name, {}, opts) | ||
|
||
# Create RDS subnet group to put RDS instance on. | ||
subnet_group_name = f'{name}-sng' | ||
rds_subnet_group = rds.SubnetGroup(subnet_group_name, | ||
subnet_ids=args.subnet_ids, | ||
tags={ | ||
'Name': subnet_group_name | ||
}, | ||
opts=ResourceOptions(parent=self) | ||
) | ||
|
||
rds_name = f'{name}-rds' | ||
self.db = rds.Instance(rds_name, | ||
name=args.db_name, | ||
allocated_storage=args.allocated_storage, | ||
engine=args.engine, | ||
engine_version=args.engine_version, | ||
instance_class=args.instance_class, | ||
storage_type=args.storage_type, | ||
db_subnet_group_name=rds_subnet_group.id, | ||
username=args.db_user, | ||
password=args.db_password, | ||
vpc_security_group_ids=args.security_group_ids, | ||
skip_final_snapshot=args.skip_final_snapshot, | ||
publicly_accessible=args.publicly_accessible, | ||
opts=ResourceOptions(parent=self) | ||
) | ||
|
||
self.register_outputs({}) |
Oops, something went wrong.