Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,7 @@ datasets/
# ChaosToolkit files
journal.json
chaostoolkit.log

# Terraform
.terraform*
terraform.tfstate*
63 changes: 63 additions & 0 deletions chaos-engineering/embracing-chaos-week/0-networking/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
terraform {
required_version = ">= 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.37"
}
}
}

provider "aws" {
default_tags {
tags = {
Project = "EmbracingChaosWeek"
}
}
}

data "aws_availability_zones" "available" {}

locals {
name = "embracing-chaos"
vpc_cidr = "10.0.0.0/16"
num_azs = 2
azs = slice(data.aws_availability_zones.available.names, 0, local.num_azs)
}

#############################################################
# Create the VPC #
#############################################################
module "vpc" {
source = "terraform-aws-modules/vpc/aws"

name = local.name
cidr = local.vpc_cidr

azs = local.azs
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)]
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 4)]

single_nat_gateway = true
enable_nat_gateway = true
}

#############################################################
# Save VPC information in SSM parameters #
#############################################################
resource "aws_ssm_parameter" "vpc_id" {
name = "/vpc/id"
value = module.vpc.vpc_id
type = "String"
}
resource "aws_ssm_parameter" "public_subnets" {
name = "/vpc/public_subnets"
value = join(",", module.vpc.public_subnets)
type = "StringList"
}
resource "aws_ssm_parameter" "private_subnets" {
name = "/vpc/private_subnets"
value = join(",", module.vpc.private_subnets)
type = "StringList"
}
15 changes: 15 additions & 0 deletions chaos-engineering/embracing-chaos-week/1-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.11-alpine

RUN pip3 install -U \
uvicorn \
requests \
starlette \
structlog

WORKDIR /app

COPY app.py .

EXPOSE 8000

CMD ["uvicorn", "--port", "8000", "--host", "0.0.0.0", "app:app"]
18 changes: 18 additions & 0 deletions chaos-engineering/embracing-chaos-week/1-service/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route


async def homepage(request):
html = """<html>
<body> <h1>Let's do some chaos!</h1> </body>
</html>"""
return Response(html, media_type="text/html")


app = Starlette(
debug=True,
routes=[
Route("/", homepage),
],
)
25 changes: 25 additions & 0 deletions chaos-engineering/embracing-chaos-week/1-service/build-and-push.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/sh -e

APP_VERSION=v1
APP_NAME=hello-chaos

echo "Creating ECR repository ${APP_NAME}"
aws ecr create-repository \
--repository-name "$APP_NAME" \
--output text 2>/dev/null || true

echo "Building container image"
docker build -t $APP_NAME:$APP_VERSION --platform linux/amd64 .

export AWS_DEFAULT_REGION="us-east-1"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
export ECR_REPO_NAME=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$APP_NAME

echo "Upload $APP_NAME container image to ECR"
docker tag $APP_NAME:$APP_VERSION $ECR_REPO_NAME:$APP_VERSION

aws ecr get-login-password --region $AWS_DEFAULT_REGION \
| docker login --username AWS \
--password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com

docker push $ECR_REPO_NAME:$APP_VERSION
170 changes: 170 additions & 0 deletions chaos-engineering/embracing-chaos-week/1-service/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
terraform {
required_version = ">= 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.37"
}
}
}

provider "aws" {
default_tags {
tags = {
Project = "EmbracingChaosWeek"
}
}
}

data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

#############################################################
# Systems Manager Parameters #
#############################################################
data "aws_ssm_parameter" "vpc_id" {
name = "/vpc/id"
}
data "aws_ssm_parameter" "public_subnets" {
name = "/vpc/public_subnets"
}
data "aws_ssm_parameter" "private_subnets" {
name = "/vpc/private_subnets"
}

locals {
name = "embracing-chaos"
application_name = "hello-chaos"
container_port = 8000
container_image = "${local.application_name}:v1"
cpu = 256
memory = 512
account_id = data.aws_caller_identity.current.account_id
region = data.aws_region.current.name
base_registry = "${local.account_id}.dkr.ecr.${local.region}.amazonaws.com"
}

#############################################################
# Create application load balancer #
#############################################################
module "alb_sg" {
source = "terraform-aws-modules/security-group/aws"

name = "${local.name}-service"
description = "Service security group"
vpc_id = data.aws_ssm_parameter.vpc_id.value

ingress_rules = ["http-80-tcp"]
ingress_cidr_blocks = ["0.0.0.0/0"]

egress_rules = ["all-all"]
egress_cidr_blocks = ["0.0.0.0/0"]

tags = {
Application = local.application_name
Role = "main-alb-secgroup"
}
}

module "alb" {
source = "terraform-aws-modules/alb/aws"

name = local.name

load_balancer_type = "application"

vpc_id = data.aws_ssm_parameter.vpc_id.value
subnets = split(",", data.aws_ssm_parameter.public_subnets.value)
security_groups = [module.alb_sg.security_group_id]
create_security_group = false

http_tcp_listeners = [
{
port = 80
protocol = "HTTP"
target_group_index = 0
},
]

target_groups = [
{
name = "${local.name}-app"
backend_protocol = "HTTP"
backend_port = local.container_port
target_type = "ip"
},
]

tags = {
Application = local.application_name
Role = "main-alb"
}
}

#############################################################
# Hello-chaos service definition #
#############################################################
module "ecs" {
source = "terraform-aws-modules/ecs/aws"

cluster_name = "${local.name}-cluster"

services = {
app = {
cpu = local.cpu
memory = local.memory
desired_count = 1
launch_type = "FARGATE"
subnet_ids = split(",", data.aws_ssm_parameter.private_subnets.value)

container_definitions = {
app = {
essential = true
image = "${local.base_registry}/${local.container_image}"
port_mappings = [
{
containerPort = local.container_port
hostPort = local.container_port
protocol = "tcp"
},
]
}
}

load_balancer = {
service = {
target_group_arn = element(module.alb.target_group_arns, 0)
container_name = "app"
container_port = local.container_port
}
}

security_group_rules = {
alb_ingress_80 = {
type = "ingress"
description = "Allow from LB to service port"
from_port = local.container_port
to_port = local.container_port
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress_vpc_cidr = {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
}

tags = {
Application = local.application_name
}
}

output "app_url" {
value = "http://${module.alb.lb_dns_name}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
title: "ALB security group swap"
description: |
Verify application security configuration.
Swapping ALB (Application Load Balancer) security groups with a dummy group
should stop requests from reaching the target application.

contributions:
security: "high"
reliability: "low"
scalability: "low"

configuration:
stress_duration: "3m"
app_url: "http://localhost:8000"
alb_name: "dummy"
alb_arn_suffix: "dummy"
dummy_alb_secgroup: "dummy"
original_alb_secgroup: "dummy"

method:
- type: action
name: "alb-secgroup-swap"
provider:
type: python
module: chaosaws.elbv2.actions
func: set_security_groups
arguments:
load_balancer_names:
- "${alb_name}"
security_group_ids:
- "${dummy_alb_secgroup}"

- type: action
name: "stress-endpoint-with-simulated-traffic"
provider:
type: python
module: chaosk6.actions
func: stress_endpoint
arguments:
endpoint: "${app_url}"
vus: 2
duration: ${stress_duration}

steady-state-hypothesis:
title: "Ningx web server is available"
probes:
- type: probe
name: "should-have-no-successful-requests"
tolerance: 0
provider:
type: python
module: chaosaws.cloudwatch.probes
func: get_metric_statistics
arguments:
namespace: "AWS/ApplicationELB"
metric_name: HTTPCode_Target_2XX_Count
dimensions:
- Name: "LoadBalancer"
Value: "${alb_arn_suffix}"
statistic: "Sum"
unit: "Count"
duration: 60

rollbacks:
- type: action
name: "alb-secgroup-reset"
provider:
type: python
module: chaosaws.elbv2.actions
func: set_security_groups
arguments:
load_balancer_names:
- "${alb_name}"
security_group_ids:
- "${original_alb_secgroup}"
Loading