SimpleTimeService POC - Complete Guide
Prerequisites
Python 3.8+ installed
Docker installed
Docker Hub account
GitHub account
AWS account with CLI access
Terraform installed
TASK 1: Create the Microservice
Step 1: Create Project Structure
bash
mkdir simpletimeservice-poc
cd simpletimeservice-poc
mkdir app
mkdir terraform
Step 2: Create the Python Application
Create app/app.py :
python
from flask import Flask, request, jsonify
import datetime
import os
app = Flask(__name__)
@app.route('/')
def get_time_and_ip():
# Get current timestamp
current_time = datetime.datetime.now().isoformat()
# Get visitor IP address
# Check for forwarded IP first (for load balancers)
visitor_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
if visitor_ip:
visitor_ip = visitor_ip.split(',')[0].strip()
response = {
"timestamp": current_time,
"ip": visitor_ip
}
return jsonify(response)
if __name__ == '__main__':
port = int(os.environ.get('PORT', 8080))
app.run(host='0.0.0.0', port=port)
Create app/requirements.txt :
Flask==2.3.3
Step 3: Create Dockerfile
Create app/Dockerfile :
dockerfile
FROM python:3.11-slim
# Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Set working directory
WORKDIR /app
# Copy requirements first for better caching
COPY requirements.txt .
# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY app.py .
# Change ownership to non-root user
RUN chown -R appuser:appuser /app
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8080
# Run the application
CMD ["python", "app.py"]
Step 4: Test Locally
bash
cd app
python app.py
# Open another terminal and test:
curl http://localhost:8080
Step 5: Build and Test Docker Image
bash
cd app
docker build -t simpletimeservice:latest .
docker run -p 8080:8080 simpletimeservice:latest
# Test: curl http://localhost:8080
Step 6: Push to Docker Hub
bash
# Login to Docker Hub
docker login
# Tag your image (replace 'yourusername' with your Docker Hub username)
docker tag simpletimeservice:latest yourusername/simpletimeservice:latest
# Push to Docker Hub
docker push yourusername/simpletimeservice:latest
Step 7: Create README for App
Create app/README.md :
markdown
# SimpleTimeService
A simple microservice that returns current timestamp and visitor IP.
## Local Development
```bash
pip install -r requirements.txt
python app.py
Docker
bash
# Build
docker build -t simpletimeservice .
# Run
docker run -p 8080:8080 simpletimeservice
API
GET / - Returns JSON with timestamp and visitor IP
## TASK 2: Create AWS Infrastructure with Terraform
### Step 8: Create Terraform Files
Create `terraform/variables.tf`:
```hcl
variable "aws_region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "project_name" {
description = "Name of the project"
type = string
default = "simpletimeservice"
}
variable "container_image" {
description = "Docker image for the application"
type = string
default = "yourusername/simpletimeservice:latest"
}
variable "container_port" {
description = "Port the container listens on"
type = number
default = 8080
}
Create terraform/terraform.tfvars :
hcl
aws_region = "us-east-1"
project_name = "simpletimeservice"
container_image = "yourusername/simpletimeservice:latest"
container_port = 8080
Create terraform/main.tf :
hcl
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# Data sources
data "aws_availability_zones" "available" {
state = "available"
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
}
}
# Public Subnets
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-subnet-${count.index + 1}"
}
}
# Private Subnets
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-private-subnet-${count.index + 1}"
}
}
# NAT Gateways
resource "aws_eip" "nat" {
count = 2
domain = "vpc"
depends_on = [aws_internet_gateway.main]
tags = {
Name = "${var.project_name}-nat-eip-${count.index + 1}"
}
}
resource "aws_nat_gateway" "main" {
count = 2
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.project_name}-nat-gw-${count.index + 1}"
}
}
# Route Tables
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.project_name}-public-rt"
}
}
resource "aws_route_table" "private" {
count = 2
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.project_name}-private-rt-${count.index + 1}"
}
}
# Route Table Associations
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = 2
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
# Security Groups
resource "aws_security_group" "alb" {
name_prefix = "${var.project_name}-alb-"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-alb-sg"
}
}
resource "aws_security_group" "ecs_tasks" {
name_prefix = "${var.project_name}-ecs-tasks-"
vpc_id = aws_vpc.main.id
ingress {
from_port = var.container_port
to_port = var.container_port
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-ecs-tasks-sg"
}
}
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = var.project_name
setting {
name = "containerInsights"
value = "enabled"
}
tags = {
Name = var.project_name
}
}
# ECS Task Definition
resource "aws_ecs_task_definition" "app" {
family = var.project_name
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([
{
name = var.project_name
image = var.container_image
portMappings = [
{
containerPort = var.container_port
protocol = "tcp"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.ecs.name
awslogs-region = var.aws_region
awslogs-stream-prefix = "ecs"
}
}
}
])
tags = {
Name = var.project_name
}
}
# CloudWatch Log Group
resource "aws_cloudwatch_log_group" "ecs" {
name = "/ecs/${var.project_name}"
retention_in_days = 7
tags = {
Name = var.project_name
}
}
# IAM Role for ECS Task Execution
resource "aws_iam_role" "ecs_task_execution_role" {
name = "${var.project_name}-ecs-task-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" {
role = aws_iam_role.ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
# Application Load Balancer
resource "aws_lb" "main" {
name = var.project_name
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
tags = {
Name = var.project_name
}
}
resource "aws_lb_target_group" "app" {
name = var.project_name
port = var.container_port
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "ip"
health_check {
enabled = true
healthy_threshold = 2
interval = 30
matcher = "200"
path = "/"
port = "traffic-port"
protocol = "HTTP"
timeout = 5
unhealthy_threshold = 2
}
tags = {
Name = var.project_name
}
}
resource "aws_lb_listener" "app" {
load_balancer_arn = aws_lb.main.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
}
# ECS Service
resource "aws_ecs_service" "app" {
name = var.project_name
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
security_groups = [aws_security_group.ecs_tasks.id]
subnets = aws_subnet.private[*].id
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = var.project_name
container_port = var.container_port
}
depends_on = [aws_lb_listener.app]
tags = {
Name = var.project_name
}
}
Create terraform/outputs.tf :
hcl
output "load_balancer_dns" {
description = "DNS name of the load balancer"
value = aws_lb.main.dns_name
}
output "load_balancer_url" {
description = "URL of the application"
value = "http://${aws_lb.main.dns_name}"
}
Step 9: Create Terraform README
Create terraform/README.md :
markdown
# SimpleTimeService Infrastructure
This Terraform configuration creates AWS infrastructure to host the SimpleTimeService c
## Prerequisites
- AWS CLI configured with appropriate credentials
- Terraform installed
## AWS Authentication
Set up AWS credentials using one of these methods:
### Option 1: AWS CLI
```bash
aws configure
Option 2: Environment Variables
bash
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_DEFAULT_REGION="us-east-1"
Option 3: IAM Role (for EC2)
If running on EC2, attach an appropriate IAM role.
Deployment
1. Update terraform.tfvars with your Docker image name
2. Initialize Terraform:
bash
terraform init
3. Plan the deployment:
bash
terraform plan
4. Apply the configuration:
bash
terraform apply
Resources Created
VPC with public and private subnets
ECS Fargate cluster
Application Load Balancer
Security groups
IAM roles
Cleanup
bash
terraform destroy
### Step 10: Create Main Project README
Create root `README.md`:
```markdown
# SimpleTimeService POC
A complete microservice solution with Docker containerization and AWS
infrastructure.
## Project Structure
.
├── app/ # Application code and Dockerfile
│ ├── app.py
│ ├── requirements.txt
│ ├── Dockerfile
│ └── README.md
├── terraform/ # Infrastructure as Code
│ ├── main.tf
│ ├── variables.tf
│ ├── terraform.tfvars
│ ├── outputs.tf
│ └── README.md
└── README.md
## Quick Start
### 1. Clone Repository
```bash
git clone <your-repo-url>
cd simpletimeservice-poc
2. Build and Test Application
bash
cd app
docker build -t simpletimeservice .
docker run -p 8080:8080 simpletimeservice
Test: curl http://localhost:8080
3. Deploy Infrastructure
bash
cd terraform
# Update terraform.tfvars with your Docker image
terraform init
terraform plan
terraform apply
API Response
json
{
"timestamp": "2024-01-15T10:30:45.123456",
"ip": "192.168.1.100"
}
Features
✅ Non-root container user
✅ Minimal Docker image
✅ AWS ECS Fargate deployment
✅ Load balancer with health checks
✅ Private subnet deployment
✅ Infrastructure as Code
Security
Application runs as non-root user in container
ECS tasks deployed in private subnets
Security groups restrict access appropriately
No secrets committed to repository
### Step 11: Deploy and Test
```bash
# 1. Initialize Terraform
cd terraform
terraform init
# 2. Update terraform.tfvars with your Docker image name
# Edit: container_image = "yourusername/simpletimeservice:latest"
# 3. Plan deployment
terraform plan
# 4. Apply (this will take 5-10 minutes)
terraform apply
# 5. Test the deployed service
# Use the load_balancer_url from the output
curl http://your-load-balancer-dns-name
Step 12: Push to GitHub
bash
# Initialize git repository
git init
git add .
git commit -m "Initial commit: SimpleTimeService POC"
# Create repository on GitHub, then:
git remote add origin https://github.com/yourusername/simpletimeservice-poc.git
git push -u origin main
Important Notes
Security Considerations
Never commit AWS credentials to git
Use IAM roles when possible
Application runs as non-root user
ECS tasks are in private subnets only
Cost Optimization
Use terraform destroy when done testing
ECS Fargate charges by CPU/memory usage
NAT Gateways have hourly charges
Troubleshooting
Check ECS service logs in CloudWatch
Verify Docker image is publicly accessible
Ensure AWS credentials are properly configured
Check security group rules if connectivity issues occur
Testing Checklist
Application builds successfully with docker build
Container runs with docker run
Application responds correctly to curl
Terraform plan executes without errors
Terraform apply creates all resources
Load balancer URL returns correct JSON response
Application shows visitor's IP correctly
This POC demonstrates containerization, infrastructure as code, and cloud deployment best practices.