feat: add terraform infra as code for AWS fargate (#1987)

This commit is contained in:
dan sweeting
2021-02-23 17:10:33 +00:00
committed by GitHub
parent 347f49b2d4
commit d341ef72aa
10 changed files with 287 additions and 0 deletions
+2
View File
@@ -17,3 +17,5 @@ success-*.png
desktop.ini
twitch.json
terraform/terraform.tfstate
terraform/terraform.tfstate.backup
+32
View File
@@ -0,0 +1,32 @@
# Terraform for AWS Fargate
Here's some configurable terraform to get you up and running with the streetmerchant docker image in AWS ECS Fargate.
Running on cloud infrastructure, your mileage may vary, and you'll need to integrate with one of the chat notifications rather than having your local browser navigate to a url for you.
The author's findings were that it worked ok; running the container from within EU-West-2 region was suficient to get a timely alert for PS5 stock on amazon, and follow the link to a successful basket add.
Dependencies:
- Terraform 14
##Getting started
There's an example tfvars file to start you off; rename this with your own preferences. Anything you can set in the `dotenv` file you'll need to set in terraform.tfvars to get the env vars into your fargate container.
Authenticate yourself with your own AWS account as with any aws commandline tool.
If you wish, add a specific section to your aws credentials file and set that profile name in `terraform.tfvars`.
Then you can:
```shell
cd ./terraform
terraform init
terraform plan
terraform apply
```
## What's included
- container, running streetmerchant, with your chosen config
- cloud metrics and a dashboard tracking 'out of stock' and 'error' responses from your configured stores
+40
View File
@@ -0,0 +1,40 @@
{
"widgets": [
{
"type": "metric",
"x": 0,
"y": 0,
"width": 18,
"height": 12,
"properties": {
"metrics": ${out_of_stock},
"view": "timeSeries",
"stacked": false,
"region": "${region}",
"start": "-PT1H",
"end": "P0D",
"stat": "Sum",
"period": 300,
"title": "out of stock"
}
},
{
"type": "metric",
"x": 0,
"y": 0,
"width": 18,
"height": 12,
"properties": {
"metrics": ${error},
"view": "timeSeries",
"stacked": false,
"region": "${region}",
"start": "-PT1H",
"end": "P0D",
"stat": "Sum",
"period": 300,
"title": "error"
}
}
]
}
+14
View File
@@ -0,0 +1,14 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
provider "aws" {
region = var.region
shared_credentials_file = var.credential_file
profile = var.credential_profile
}
+42
View File
@@ -0,0 +1,42 @@
resource "aws_ecs_cluster" "main" {
name = "${var.app_name}-ecs-cluster"
}
resource "aws_ecs_service" "main" {
name = "${var.app_name}-ecs-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.main.id
desired_count = 1
network_configuration {
subnets = [
aws_subnet.aws-subnet.id
]
assign_public_ip = true
}
launch_type = "FARGATE"
}
data "aws_iam_role" "ecs_task_execution_role" {
name = "ecsTaskExecutionRole"
}
locals {
container_env = [for k, v in var.streetmerchant_env : { name: k, value: v}]
}
resource "aws_ecs_task_definition" "main" {
container_definitions = templatefile("taskdef.json.template", {
"name": var.app_name
"awslogs-group": aws_cloudwatch_log_group.main.name
"region": var.region
"cpu": var.cpu
"memory": parseint(var.memory,10)
"environment": jsonencode(local.container_env)
})
family = var.app_name
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = var.cpu
memory = var.memory
execution_role_arn = data.aws_iam_role.ecs_task_execution_role.arn
}
+51
View File
@@ -0,0 +1,51 @@
resource "aws_cloudwatch_log_group" "main" {
name = var.app_name
retention_in_days = 3
}
locals {
stores = split(",",var.streetmerchant_env["STORES"])
metrics = {
out_of_stock = [for store in local.stores : ["${var.app_name}-out-of-stock", store]]
error = [for store in local.stores : ["${var.app_name}-error", store]]
}
}
resource "aws_cloudwatch_log_metric_filter" "out_of_stock" {
for_each = toset(local.stores)
log_group_name = aws_cloudwatch_log_group.main.name
name = "${each.key}-out-of-stock"
pattern = "${each.key} \"OUT OF STOCK\""
metric_transformation {
name = each.key
namespace = "${var.app_name}-out-of-stock"
value = 1
default_value = 0
}
}
resource "aws_cloudwatch_log_metric_filter" "error" {
for_each = toset(local.stores)
log_group_name = aws_cloudwatch_log_group.main.name
name = "${each.key}-error"
pattern = "${each.key} \"ERROR\""
metric_transformation {
name = each.key
namespace = "${var.app_name}-error"
value = 1
default_value = 0
}
}
resource "aws_cloudwatch_dashboard" "main" {
dashboard_name = "${var.app_name}-dashboard"
dashboard_body = templatefile("dashboard.json.template", {
out_of_stock = jsonencode(local.metrics.out_of_stock)
error = jsonencode(local.metrics.error)
region = var.region
})
}
+40
View File
@@ -0,0 +1,40 @@
resource "aws_vpc" "main" {
enable_dns_support = true
cidr_block = "10.0.0.0/16"
tags = {
app = "ps5"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
app = "ps5"
}
}
resource "aws_subnet" "aws-subnet" {
vpc_id = aws_vpc.main.id
cidr_block = aws_vpc.main.cidr_block
map_public_ip_on_launch = true
tags = {
app = "ps5"
}
}
resource "aws_route_table" "main" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
app = "ps5"
}
}
resource "aws_main_route_table_association" "main" {
route_table_id = aws_route_table.main.id
vpc_id = aws_vpc.main.id
}
+18
View File
@@ -0,0 +1,18 @@
[
{
"name": "${name}-task",
"image": "ghcr.io/jef/streetmerchant:latest",
"cpu": ${cpu},
"memory": ${memory},
"essential": true,
"environment": ${environment},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "${awslogs-group}",
"awslogs-region": "${region}",
"awslogs-stream-prefix": "ecs"
}
}
}
]
+8
View File
@@ -0,0 +1,8 @@
credential_profile = "ps5"
streetmerchant_env = {
"STORES" = "amazon-uk,game,argos,box,currys,johnlewis,shopto,smythstoys,very,amazon-it,amazon-nl"
"SHOW_ONLY_SERIES" = "sonyps5c,sonyps5de"
"SLACK_TOKEN" = "your slack api token"
"SLACK_CHANNEL" = "your slack channel name"
}
+40
View File
@@ -0,0 +1,40 @@
variable "credential_file" {
type = string
description = "your aws credentials file"
default = "~/.aws/credentials"
}
variable "credential_profile" {
type = string
description = "the section in ~/.aws/credentials with your desired aws_access_key_id and aws_secret_access_key values"
default = "default"
}
variable "region" {
type = string
description = "aws region"
default = "eu-west-2"
}
variable "app_name" {
type = string
default = "streetmerchant"
}
variable "memory" {
type = string
default = "2048"
description = "ecs task memory"
}
variable "cpu" {
type = number
default = 1024
description = "ecs task cpu"
}
variable "streetmerchant_env" {
type = map
description = "name/value pairs for .env values"
default = {}
}